feat: stream tags, home, and public timelines (#190)

zio/stable
Daniel Roe 2022-11-28 11:18:45 +00:00 committed by GitHub
parent d94bed686b
commit 5560fe66cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 55 additions and 16 deletions

View File

@ -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 }"

View File

@ -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

View File

@ -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,

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>