feat: avoid reordering pagination border effects (#877)

This commit is contained in:
patak 2023-01-08 17:04:26 +01:00 committed by GitHub
parent f8692ed480
commit efe406df5b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 19 additions and 10 deletions

View file

@ -10,6 +10,7 @@ const {
keyProp = 'id', keyProp = 'id',
virtualScroller = false, virtualScroller = false,
eventType = 'update', eventType = 'update',
buffer = 10,
preprocess, preprocess,
} = defineProps<{ } = defineProps<{
paginator: Paginator<T[], O> paginator: Paginator<T[], O>
@ -17,6 +18,10 @@ const {
virtualScroller?: boolean virtualScroller?: boolean
stream?: Promise<WsEvents> stream?: Promise<WsEvents>
eventType?: 'notification' | 'update' eventType?: 'notification' | 'update'
// When preprocess is used, buffer is the number of items that will be hidden
// until the next pagination to avoid border effect between pages when reordering
// and grouping items
buffer?: number
preprocess?: (items: T[]) => any[] preprocess?: (items: T[]) => any[]
}>() }>()

View file

@ -13,12 +13,13 @@ const showHistory = (edit: mastodon.v1.StatusEdit) => {
} }
const timeAgoOptions = useTimeAgoOptions() const timeAgoOptions = useTimeAgoOptions()
// TODO: rework, this is only reversing the first page of edits
const reverseHistory = (items: mastodon.v1.StatusEdit[]) => const reverseHistory = (items: mastodon.v1.StatusEdit[]) =>
[...items].reverse() [...items].reverse()
</script> </script>
<template> <template>
<CommonPaginator :paginator="paginator" key-prop="createdAt" :preprocess="reverseHistory"> <CommonPaginator :paginator="paginator" key-prop="createdAt" :preprocess="reverseHistory" :buffer="0">
<template #default="{ items, item, index }"> <template #default="{ items, item, index }">
<CommonDropdownItem <CommonDropdownItem
px="0.5" px="0.5"

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const paginator = useMasto().v1.timelines.listHome() const paginator = useMasto().v1.timelines.listHome({ limit: 30 })
const stream = useMasto().v1.stream.streamUser() const stream = useMasto().v1.stream.streamUser()
onBeforeUnmount(() => stream?.then(s => s.disconnect())) onBeforeUnmount(() => stream?.then(s => s.disconnect()))
</script> </script>

View file

@ -4,12 +4,13 @@ import { DynamicScrollerItem } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import type { Paginator, WsEvents, mastodon } from 'masto' import type { Paginator, WsEvents, mastodon } from 'masto'
const { paginator, stream, account } = defineProps<{ const { paginator, stream, account, buffer = 10 } = defineProps<{
paginator: Paginator<mastodon.v1.Status[], mastodon.v1.ListAccountStatusesParams> paginator: Paginator<mastodon.v1.Status[], mastodon.v1.ListAccountStatusesParams>
stream?: Promise<WsEvents> stream?: Promise<WsEvents>
context?: mastodon.v2.FilterContext context?: mastodon.v2.FilterContext
account?: mastodon.v1.Account account?: mastodon.v1.Account
preprocess?: (items: mastodon.v1.Status[]) => mastodon.v1.Status[] preprocess?: (items: mastodon.v1.Status[]) => mastodon.v1.Status[]
buffer?: number
}>() }>()
const { formatNumber } = useHumanReadableNumber() const { formatNumber } = useHumanReadableNumber()
@ -21,7 +22,7 @@ const showOriginSite = $computed(() =>
</script> </script>
<template> <template>
<CommonPaginator v-bind="{ paginator, stream, preprocess }" :virtual-scroller="virtualScroller"> <CommonPaginator v-bind="{ paginator, stream, preprocess, buffer }" :virtual-scroller="virtualScroller">
<template #updater="{ number, update }"> <template #updater="{ number, update }">
<button py-4 border="b base" flex="~ col" p-3 w-full text-primary font-bold @click="update"> <button py-4 border="b base" flex="~ col" p-3 w-full text-primary font-bold @click="update">
{{ $t('timeline.show_new_items', number, { named: { v: formatNumber(number) } }) }} {{ $t('timeline.show_new_items', number, { named: { v: formatNumber(number) } }) }}

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const paginator = useMasto().v1.timelines.listPublic() const paginator = useMasto().v1.timelines.listPublic({ limit: 30 })
const stream = useMasto().v1.stream.streamPublicTimeline() const stream = useMasto().v1.stream.streamPublicTimeline()
onBeforeUnmount(() => stream.then(s => s.disconnect())) onBeforeUnmount(() => stream.then(s => s.disconnect()))
</script> </script>

View file

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
const paginator = useMasto().v1.timelines.listPublic({ local: true }) const paginator = useMasto().v1.timelines.listPublic({ limit: 30, local: true })
const stream = useMasto().v1.stream.streamCommunityTimeline() const stream = useMasto().v1.stream.streamCommunityTimeline()
onBeforeUnmount(() => stream.then(s => s.disconnect())) onBeforeUnmount(() => stream.then(s => s.disconnect()))
</script> </script>

View file

@ -6,6 +6,7 @@ export function usePaginator<T, P>(
stream?: Promise<WsEvents>, stream?: Promise<WsEvents>,
eventType: 'notification' | 'update' = 'update', eventType: 'notification' | 'update' = 'update',
preprocess: (items: T[]) => T[] = (items: T[]) => items, preprocess: (items: T[]) => T[] = (items: T[]) => items,
buffer = 10,
) { ) {
const state = ref<PaginatorState>(isMastoInitialised.value ? 'idle' : 'loading') const state = ref<PaginatorState>(isMastoInitialised.value ? 'idle' : 'loading')
const items = ref<T[]>([]) const items = ref<T[]>([])
@ -62,8 +63,10 @@ export function usePaginator<T, P>(
const result = await paginator.next() const result = await paginator.next()
if (result.value?.length) { if (result.value?.length) {
nextItems.value = preprocess(result.value) as any const preprocessedItems = preprocess([...nextItems.value, ...result.value]) as any
items.value.push(...nextItems.value) const itemsToShowCount = preprocessedItems.length - buffer
nextItems.value = preprocessedItems.slice(itemsToShowCount)
items.value.push(...preprocessedItems.slice(0, itemsToShowCount))
state.value = 'idle' state.value = 'idle'
} }
else { else {
@ -108,7 +111,6 @@ export function usePaginator<T, P>(
return { return {
items, items,
prevItems, prevItems,
nextItems,
update, update,
state, state,
error, error,

View file

@ -8,7 +8,7 @@ const { t } = useI18n()
const account = await fetchAccountByHandle(handle) const account = await fetchAccountByHandle(handle)
const paginator = useMasto().v1.accounts.listStatuses(account.id, { excludeReplies: true }) const paginator = useMasto().v1.accounts.listStatuses(account.id, { limit: 30, excludeReplies: true })
if (account) { if (account) {
useHeadFixed({ useHeadFixed({