feat: stream tags, home, and public timelines (#190)
parent
d94bed686b
commit
5560fe66cc
|
@ -2,12 +2,13 @@
|
|||
// @ts-expect-error missing types
|
||||
import { DynamicScroller } from 'vue-virtual-scroller'
|
||||
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
|
||||
import type { Paginator } from 'masto'
|
||||
import type { Paginator, WsEvents } from 'masto'
|
||||
|
||||
const { paginator, keyProp = 'id', virtualScroller = false } = defineProps<{
|
||||
const { paginator, stream, keyProp = 'id', virtualScroller = false } = defineProps<{
|
||||
paginator: Paginator<any, any[]>
|
||||
keyProp?: string
|
||||
virtualScroller: boolean
|
||||
stream?: WsEvents
|
||||
}>()
|
||||
|
||||
defineSlots<{
|
||||
|
@ -15,14 +16,19 @@ defineSlots<{
|
|||
item: any
|
||||
active?: boolean
|
||||
}
|
||||
updater: {
|
||||
number: number
|
||||
update: () => void
|
||||
}
|
||||
loading: {}
|
||||
}>()
|
||||
|
||||
const { items, state, endAnchor, error } = usePaginator(paginator)
|
||||
const { items, prevItems, update, state, endAnchor, error } = usePaginator(paginator, stream)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<slot v-if="prevItems.length" name="updater" v-bind="{ number: prevItems.length, update }" />
|
||||
<template v-if="virtualScroller">
|
||||
<DynamicScroller
|
||||
v-slot="{ item, active }"
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
<script setup lang="ts">
|
||||
// @ts-expect-error missing types
|
||||
import { DynamicScrollerItem } from 'vue-virtual-scroller'
|
||||
import type { Paginator, Status } from 'masto'
|
||||
import type { Paginator, Status, WsEvents } from 'masto'
|
||||
|
||||
const { paginator } = defineProps<{
|
||||
const { paginator, stream } = defineProps<{
|
||||
paginator: Paginator<any, Status[]>
|
||||
stream?: WsEvents
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<CommonPaginator :paginator="paginator" virtual-scroller>
|
||||
<CommonPaginator v-bind="{ paginator, stream }" virtual-scroller>
|
||||
<template #updater="{ number, update }">
|
||||
<button py-4 border="b base" flex="~ col" p-3 w-full text-primary font-bold @click="update">
|
||||
Show {{ number }} new items
|
||||
</button>
|
||||
</template>
|
||||
<template #default="{ item, active }">
|
||||
<DynamicScrollerItem :item="item" :active="active" tag="article">
|
||||
<StatusCard
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import type { Paginator } from 'masto'
|
||||
import type { Paginator, WsEvents } from 'masto'
|
||||
import { useDeactivated } from './lifecycle'
|
||||
import type { PaginatorState } from '~/types'
|
||||
|
||||
export function usePaginator<T>(paginator: Paginator<any, T[]>) {
|
||||
export function usePaginator<T>(paginator: Paginator<any, T[]>, stream?: WsEvents) {
|
||||
const state = ref<PaginatorState>('idle')
|
||||
const items = ref<T[]>([])
|
||||
const newItems = ref<T[]>([])
|
||||
const nextItems = ref<T[]>([])
|
||||
const prevItems = ref<T[]>([])
|
||||
|
||||
const endAnchor = ref<HTMLDivElement>()
|
||||
const bound = reactive(useElementBounding(endAnchor))
|
||||
|
@ -13,6 +14,22 @@ export function usePaginator<T>(paginator: Paginator<any, T[]>) {
|
|||
const error = ref<unknown | undefined>()
|
||||
const deactivated = useDeactivated()
|
||||
|
||||
async function update() {
|
||||
items.value.unshift(...prevItems.value)
|
||||
prevItems.value = []
|
||||
}
|
||||
|
||||
stream?.on('update', (status) => {
|
||||
prevItems.value.unshift(status as any)
|
||||
})
|
||||
|
||||
// TODO: update statuses
|
||||
stream?.on('status.update', (status) => {
|
||||
const index = items.value.findIndex((s: any) => s.id === status.id)
|
||||
if (index >= 0)
|
||||
items.value[index] = status as any
|
||||
})
|
||||
|
||||
async function loadNext() {
|
||||
if (state.value !== 'idle')
|
||||
return
|
||||
|
@ -22,8 +39,8 @@ export function usePaginator<T>(paginator: Paginator<any, T[]>) {
|
|||
const result = await paginator.next()
|
||||
|
||||
if (result.value?.length) {
|
||||
newItems.value = result.value
|
||||
items.value.push(...newItems.value)
|
||||
nextItems.value = result.value
|
||||
items.value.push(...nextItems.value)
|
||||
state.value = 'idle'
|
||||
}
|
||||
else {
|
||||
|
@ -59,7 +76,9 @@ export function usePaginator<T>(paginator: Paginator<any, T[]>) {
|
|||
|
||||
return {
|
||||
items,
|
||||
newItems,
|
||||
prevItems,
|
||||
nextItems,
|
||||
update,
|
||||
state,
|
||||
error,
|
||||
endAnchor,
|
||||
|
|
|
@ -12,6 +12,8 @@ if (useRoute().path === '/signin/callback') {
|
|||
}
|
||||
|
||||
const paginator = useMasto().timelines.getHomeIterable()
|
||||
const stream = await useMasto().stream.streamUser()
|
||||
onBeforeUnmount(() => stream.disconnect())
|
||||
|
||||
const { t } = useI18n()
|
||||
useHead({
|
||||
|
@ -26,7 +28,7 @@ useHead({
|
|||
</template>
|
||||
<slot>
|
||||
<PublishWidget draft-key="home" border="b base" />
|
||||
<TimelinePaginator :paginator="paginator" />
|
||||
<TimelinePaginator v-bind="{ paginator, stream }" />
|
||||
</slot>
|
||||
</MainContent>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
const paginator = useMasto().timelines.getPublicIterable()
|
||||
const stream = await useMasto().stream.streamPublicTimeline()
|
||||
onBeforeUnmount(() => stream.disconnect())
|
||||
|
||||
useHead({
|
||||
title: 'Federated',
|
||||
|
@ -13,7 +15,7 @@ useHead({
|
|||
</template>
|
||||
|
||||
<slot>
|
||||
<TimelinePaginator :paginator="paginator" />
|
||||
<TimelinePaginator v-bind="{ paginator, stream }" />
|
||||
</slot>
|
||||
</MainContent>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
const paginator = useMasto().timelines.getPublicIterable({ local: true })
|
||||
const stream = await useMasto().stream.streamCommunityTimeline()
|
||||
onBeforeUnmount(() => stream.disconnect())
|
||||
|
||||
useHead({
|
||||
title: 'Local',
|
||||
|
@ -13,7 +15,7 @@ useHead({
|
|||
</template>
|
||||
|
||||
<slot>
|
||||
<TimelinePaginator :paginator="paginator" />
|
||||
<TimelinePaginator v-bind="{ paginator, stream }" />
|
||||
</slot>
|
||||
</MainContent>
|
||||
</template>
|
||||
|
|
|
@ -3,6 +3,8 @@ const params = useRoute().params
|
|||
const tag = $(computedEager(() => params.tag as string))
|
||||
|
||||
const paginator = useMasto().timelines.getHashtagIterable(tag)
|
||||
const stream = await useMasto().stream.streamTagTimeline(tag)
|
||||
onBeforeUnmount(() => stream.disconnect())
|
||||
|
||||
useHead({
|
||||
title: `#${tag}`,
|
||||
|
@ -16,7 +18,7 @@ useHead({
|
|||
</template>
|
||||
|
||||
<slot>
|
||||
<TimelinePaginator :paginator="paginator" />
|
||||
<TimelinePaginator v-bind="{ paginator, stream }" />
|
||||
</slot>
|
||||
</MainContent>
|
||||
</template>
|
||||
|
|
Loading…
Reference in New Issue