feat: finish list CRUD (#1532)

Co-authored-by: userquin <userquin@gmail.com>
closes https://github.com/elk-zone/elk/issues/372
This commit is contained in:
Evan Boehs 2023-02-02 16:02:39 -05:00 committed by GitHub
parent 809d4a6eb3
commit e58d09b6cc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 383 additions and 150 deletions

View file

@ -1,55 +0,0 @@
<script setup lang="ts">
import type { CommonRouteTabOption } from '~/components/common/CommonRouteTabs.vue'
const list = $computed(() => useRoute().params.list as string)
const server = $computed(() => useRoute().params.server as string)
const { t } = useI18n()
const tabs = $computed<CommonRouteTabOption[]>(() => [
{
to: {
name: 'list',
params: { server, list },
},
display: t('tab.list'),
icon: 'i-ri:list-unordered',
},
{
to: {
name: 'list-accounts',
params: { server, list },
},
display: t('tab.accounts'),
icon: 'i-ri:user-line',
},
],
)
const { client } = $(useMasto())
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.fetch(list), { default: () => shallowRef() }))
if (listInfo) {
useHeadFixed({
title: () => `${listInfo.title}`,
})
}
onReactivated(() => {
// Silently update data when reentering the page
// The user will see the previous content first, and any changes will be updated to the UI when the request is completed
refresh()
})
</script>
<template>
<MainContent back>
<template #title>
<span text-lg font-bold>{{ listInfo ? listInfo.title : t('nav.list') }}</span>
</template>
<template #header>
<CommonRouteTabs replace :options="tabs" />
</template>
<NuxtPage v-if="isHydrated" />
</MainContent>
</template>

View file

@ -1,35 +0,0 @@
<script setup lang="ts">
definePageMeta({
name: 'list',
})
const params = useRoute().params
const listId = $(computedEager(() => params.list as string))
const { client } = $(useMasto())
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.fetch(listId), { default: () => shallowRef() }))
const paginator = client.v1.timelines.listList(listId)
const stream = useStreaming(client => client.v1.stream.streamListTimeline(listId))
if (listInfo) {
useHeadFixed({
title: () => `${listInfo.title}`,
})
}
onReactivated(() => {
// Silently update data when reentering the page
// The user will see the previous content first, and any changes will be updated to the UI when the request is completed
refresh()
})
</script>
<template>
<MainContent back>
<template #title>
<span text-lg font-bold>{{ listInfo ? listInfo.title : $t('nav.list') }}</span>
</template>
<TimelinePaginator v-bind="{ paginator, stream }" :preprocess="reorderedTimeline" context="home" />
</MainContent>
</template>

View file

@ -1,17 +1,56 @@
<script setup lang="ts">
definePageMeta({
name: 'list',
})
import type { CommonRouteTabOption } from '~/components/common/CommonRouteTabs.vue'
const params = useRoute().params
const listId = $(computedEager(() => params.list as string))
const list = $computed(() => useRoute().params.list as string)
const server = $computed(() => useRoute().params.server as string)
const { t } = useI18n()
const route = useRoute()
const tabs = $computed<CommonRouteTabOption[]>(() => [
{
to: {
name: 'list',
params: { server, list },
},
display: t('tab.posts'),
icon: 'i-ri:list-unordered',
},
{
to: {
name: 'list-accounts',
params: { server, list },
},
display: t('tab.accounts'),
icon: 'i-ri:user-line',
},
],
)
const { client } = $(useMasto())
const { data: listInfo, refresh } = $(await useAsyncData(() => client.v1.lists.fetch(list), { default: () => shallowRef() }))
const paginator = client.v1.timelines.listList(listId)
const stream = useStreaming(client => client.v1.stream.streamListTimeline(listId))
if (listInfo) {
useHeadFixed({
title: () => `${listInfo.title} | ${route.fullPath.endsWith('/accounts') ? t('tab.accounts') : t('tab.posts')} | ${t('nav.lists')}`,
})
}
onReactivated(() => {
// Silently update data when reentering the page
// The user will see the previous content first, and any changes will be updated to the UI when the request is completed
refresh()
})
</script>
<template>
<TimelinePaginator v-bind="{ paginator, stream }" :preprocess="reorderedTimeline" context="home" />
<MainContent back>
<template #title>
<span text-lg font-bold>{{ listInfo ? listInfo.title : t('nav.list') }}</span>
</template>
<template #header>
<CommonRouteTabs replace :options="tabs" />
</template>
<NuxtPage v-if="isHydrated" />
</MainContent>
</template>

View file

@ -0,0 +1,17 @@
<script setup lang="ts">
definePageMeta({
name: 'list',
})
const params = useRoute().params
const listId = $(computedEager(() => params.list as string))
const client = useMastoClient()
const paginator = client.v1.timelines.listList(listId)
const stream = useStreaming(client => client.v1.stream.streamListTimeline(listId))
</script>
<template>
<TimelinePaginator v-bind="{ paginator, stream }" :preprocess="reorderedTimeline" context="home" />
</template>

View file

@ -1,12 +1,44 @@
<script lang="ts" setup>
import type { mastodon } from 'masto'
const { t } = useI18n()
const { client } = $(useMasto())
const client = useMastoClient()
const paginator = client.v1.lists.list()
useHeadFixed({
title: () => t('nav.lists'),
})
const paginatorRef = ref()
let busy = $ref<boolean>(false)
const createText = ref('')
const enableSubmit = computed(() => createText.value.length > 0)
async function createList() {
if (busy || !enableSubmit.value)
return
busy = true
await nextTick()
try {
const newEntry = await client.v1.lists.create({
title: createText.value,
})
paginatorRef.value.createEntry(newEntry)
createText.value = ''
}
finally {
busy = false
}
}
function updateEntry(list: mastodon.v1.List) {
paginatorRef.value.updateEntry(list)
}
function removeEntry(id: string) {
paginatorRef.value.removeEntry(id)
}
</script>
<template>
@ -18,11 +50,47 @@ useHeadFixed({
</NuxtLink>
</template>
<slot>
<CommonPaginator :paginator="paginator">
<CommonPaginator ref="paginatorRef" :paginator="paginator">
<template #default="{ item }">
<NuxtLink :to="`list/${item.id}`" block p4 hover:bg-active flex justify-between>
{{ item.title }}
</NuxtLink>
<ListEntry
:list="item"
@list-updated="updateEntry"
@list-removed="removeEntry"
/>
</template>
<template #done>
<form
border="t base"
p-4 w-full
flex="~ wrap" relative gap-3
@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
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>
</template>
</CommonPaginator>
</slot>