145 lines
4.2 KiB
Vue
145 lines
4.2 KiB
Vue
|
<script setup lang="ts">
|
||
|
import type { mastodon } from 'masto'
|
||
|
|
||
|
definePageMeta({
|
||
|
middleware: 'auth',
|
||
|
})
|
||
|
|
||
|
const { t } = useI18n()
|
||
|
|
||
|
const client = useMastoClient()
|
||
|
|
||
|
const paginator = client.v1.lists.list()
|
||
|
|
||
|
useHydratedHead({
|
||
|
title: () => t('nav.lists'),
|
||
|
})
|
||
|
|
||
|
const paginatorRef = ref()
|
||
|
const inputRef = ref<HTMLInputElement>()
|
||
|
const actionError = ref<string | undefined>(undefined)
|
||
|
const busy = ref<boolean>(false)
|
||
|
const createText = ref('')
|
||
|
const enableSubmit = computed(() => createText.value.length > 0)
|
||
|
|
||
|
async function createList() {
|
||
|
if (busy.value || !enableSubmit.value)
|
||
|
return
|
||
|
|
||
|
busy.value = true
|
||
|
actionError.value = undefined
|
||
|
await nextTick()
|
||
|
try {
|
||
|
const newEntry = await client.v1.lists.create({
|
||
|
title: createText.value,
|
||
|
})
|
||
|
paginatorRef.value?.createEntry(newEntry)
|
||
|
createText.value = ''
|
||
|
}
|
||
|
catch (err) {
|
||
|
console.error(err)
|
||
|
actionError.value = (err as Error).message
|
||
|
nextTick(() => {
|
||
|
inputRef.value?.focus()
|
||
|
})
|
||
|
}
|
||
|
finally {
|
||
|
busy.value = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function clearError(focusBtn: boolean) {
|
||
|
actionError.value = undefined
|
||
|
if (focusBtn) {
|
||
|
nextTick(() => {
|
||
|
inputRef.value?.focus()
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function updateEntry(list: mastodon.v1.List) {
|
||
|
paginatorRef.value?.updateEntry(list)
|
||
|
}
|
||
|
function removeEntry(id: string) {
|
||
|
paginatorRef.value?.removeEntry(id)
|
||
|
}
|
||
|
|
||
|
onDeactivated(() => clearError(false))
|
||
|
</script>
|
||
|
|
||
|
<template>
|
||
|
<CommonPaginator ref="paginatorRef" :paginator="paginator">
|
||
|
<template #default="{ item }">
|
||
|
<ListEntry
|
||
|
:model-value="item"
|
||
|
@update:model-value="updateEntry"
|
||
|
@list-removed="removeEntry"
|
||
|
/>
|
||
|
</template>
|
||
|
<template #done>
|
||
|
<form
|
||
|
border="t base"
|
||
|
p-4 w-full
|
||
|
flex="~ wrap" relative gap-3
|
||
|
:aria-describedby="actionError ? 'create-list-error' : undefined"
|
||
|
:class="actionError ? 'border border-base border-rounded rounded-be-is-0 rounded-be-ie-0 border-b-unset border-$c-danger-active' : null"
|
||
|
@submit.prevent="createList"
|
||
|
>
|
||
|
<div
|
||
|
bg-base border="~ base" flex-1 h10 ps-1 pe-4 rounded-2 w-full flex="~ row"
|
||
|
items-center relative focus-within:box-shadow-outline gap-3
|
||
|
>
|
||
|
<input
|
||
|
ref="inputRef"
|
||
|
v-model="createText"
|
||
|
bg-transparent
|
||
|
outline="focus:none"
|
||
|
px-4
|
||
|
pb="1px"
|
||
|
flex-1
|
||
|
placeholder-text-secondary
|
||
|
:placeholder="$t('list.list_title_placeholder')"
|
||
|
@keypress.enter="createList"
|
||
|
>
|
||
|
</div>
|
||
|
<div flex="~ col" gap-y-4 gap-x-2 sm="~ justify-between flex-row">
|
||
|
<button flex="~ row" gap-x-2 items-center btn-solid :disabled="!enableSubmit || busy">
|
||
|
<span v-if="busy" aria-hidden="true" block animate animate-spin preserve-3d class="rtl-flip">
|
||
|
<span block i-ri:loader-2-fill aria-hidden="true" />
|
||
|
</span>
|
||
|
<span v-else aria-hidden="true" block i-material-symbols:playlist-add-rounded class="rtl-flip" />
|
||
|
{{ $t('list.create') }}
|
||
|
</button>
|
||
|
</div>
|
||
|
</form>
|
||
|
<CommonErrorMessage
|
||
|
v-if="actionError"
|
||
|
id="create-list-error"
|
||
|
described-by="create-list-failed"
|
||
|
class="rounded-bs-is-0 rounded-bs-ie-0 border-t-dashed m-b-2"
|
||
|
>
|
||
|
<header id="create-list-failed" flex justify-between>
|
||
|
<div flex items-center gap-x-2 font-bold>
|
||
|
<div aria-hidden="true" i-ri:error-warning-fill />
|
||
|
<p>{{ $t('list.error') }}</p>
|
||
|
</div>
|
||
|
<CommonTooltip placement="bottom" :content="$t('list.clear_error')">
|
||
|
<button
|
||
|
flex rounded-4 p1 hover:bg-active cursor-pointer transition-100 :aria-label="$t('list.clear_error')"
|
||
|
@click="clearError(true)"
|
||
|
>
|
||
|
<span aria-hidden="true" w="1.75em" h="1.75em" i-ri:close-line />
|
||
|
</button>
|
||
|
</CommonTooltip>
|
||
|
</header>
|
||
|
<ol ps-2 sm:ps-1>
|
||
|
<li flex="~ col sm:row" gap-y-1 sm:gap-x-2>
|
||
|
<strong sr-only>{{ $t('list.error_prefix') }}</strong>
|
||
|
<span>{{ actionError }}</span>
|
||
|
</li>
|
||
|
</ol>
|
||
|
</CommonErrorMessage>
|
||
|
</template>
|
||
|
</CommonPaginator>
|
||
|
</template>
|