chore: add custom page scroll track

This commit is contained in:
userquin 2023-02-10 23:37:09 +01:00
parent 3e0b2a3e4b
commit bb119d0f8d
4 changed files with 36 additions and 13 deletions

View file

@ -4,11 +4,14 @@ import { DynamicScrollerItem } from 'vue-virtual-scroller'
import type { Paginator, WsEvents, mastodon } from 'masto' import type { Paginator, WsEvents, mastodon } from 'masto'
import type { GroupedAccountLike, NotificationSlot } from '~/types' import type { GroupedAccountLike, NotificationSlot } from '~/types'
const { paginator, stream } = defineProps<{ const { path, paginator, stream } = defineProps<{
path: string
paginator: Paginator<mastodon.v1.Notification[], mastodon.v1.ListNotificationsParams> paginator: Paginator<mastodon.v1.Notification[], mastodon.v1.ListNotificationsParams>
stream?: Promise<WsEvents> stream?: Promise<WsEvents>
}>() }>()
const nuxtApp = useNuxtApp()
const virtualScroller = false // TODO: fix flickering issue with virtual scroll const virtualScroller = false // TODO: fix flickering issue with virtual scroll
const groupCapacity = Number.MAX_VALUE // No limit const groupCapacity = Number.MAX_VALUE // No limit
@ -113,6 +116,10 @@ function groupItems(items: mastodon.v1.Notification[]): NotificationSlot[] {
// Finalize remaining groups // Finalize remaining groups
processGroup() processGroup()
nextTick().then(() => {
nuxtApp.$trackScroll.restoreCustomPageScroll()
})
return results return results
} }
@ -146,6 +153,10 @@ function preprocess(items: NotificationSlot[]): NotificationSlot[] {
const { clearNotifications } = useNotifications() const { clearNotifications } = useNotifications()
const { formatNumber } = useHumanReadableNumber() const { formatNumber } = useHumanReadableNumber()
onMounted(() => {
nuxtApp.$trackScroll.registerCustomRoute(path)
})
</script> </script>
<template> <template>

View file

@ -8,5 +8,5 @@ onActivated(clearNotifications)
</script> </script>
<template> <template>
<NotificationPaginator v-bind="{ paginator, stream }" /> <NotificationPaginator v-bind="{ path: '/notification/mention', paginator, stream }" />
</template> </template>

View file

@ -8,5 +8,5 @@ onActivated(clearNotifications)
</script> </script>
<template> <template>
<NotificationPaginator v-bind="{ paginator, stream }" /> <NotificationPaginator v-bind="{ path: '/notification', paginator, stream }" />
</template> </template>

View file

@ -4,6 +4,7 @@ export default defineNuxtPlugin((nuxtApp) => {
const track = ref(false) const track = ref(false)
const { y } = useWindowScroll() const { y } = useWindowScroll()
const storage = useLocalStorage<Record<string, number>>('elk-track-scroll', {}) const storage = useLocalStorage<Record<string, number>>('elk-track-scroll', {})
const customRoutes = new Set<string>()
router.beforeEach(async () => { router.beforeEach(async () => {
track.value = false track.value = false
@ -17,7 +18,7 @@ export default defineNuxtPlugin((nuxtApp) => {
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }) window.scrollTo({ top: 0, left: 0, behavior: 'smooth' })
} }
const restoreScroll = () => { const restoreScrollCallback = (ignoreHook: boolean) => {
const path = route.fullPath const path = route.fullPath
return nextTick().then(() => { return nextTick().then(() => {
if (route.meta && route.meta?.noScrollTrack) { if (route.meta && route.meta?.noScrollTrack) {
@ -33,6 +34,12 @@ export default defineNuxtPlugin((nuxtApp) => {
} }
if (!route.meta || !route.meta?.noScrollTrack) { if (!route.meta || !route.meta?.noScrollTrack) {
const hook = ignoreHook ? undefined : customRoutes.has(route.fullPath)
if (hook) {
reject(new Error('hook detected'))
return
}
const scrollPosition = storage.value[route.fullPath] const scrollPosition = storage.value[route.fullPath]
if (scrollPosition) if (scrollPosition)
window.scrollTo(0, scrollPosition) window.scrollTo(0, scrollPosition)
@ -47,33 +54,38 @@ export default defineNuxtPlugin((nuxtApp) => {
}) })
} }
nuxtApp.hooks.hook('app:suspense:resolve', () => { const restoreScroll = () => restoreScrollCallback(false)
if (isHydrated.value) {
restoreScroll().then(() => {
track.value = true
}).catch(noop)
}
})
nuxtApp.hooks.hook('page:finish', () => { const restoreScrollHook = () => {
if (isHydrated.value) { if (isHydrated.value) {
restoreScroll().then(() => { restoreScroll().then(() => {
track.value = true track.value = true
}).catch(noop) }).catch(noop)
} }
}) }
const restoreCustomPageScroll = () => restoreScrollCallback(true)
nuxtApp.hooks.hook('app:suspense:resolve', restoreScrollHook)
nuxtApp.hooks.hook('page:finish', restoreScrollHook)
watch([track, y, () => route], ([trackEnabled, scrollPosition, r]) => { watch([track, y, () => route], ([trackEnabled, scrollPosition, r]) => {
if (trackEnabled && (!r.meta || !r.meta?.noScrollTrack)) if (trackEnabled && (!r.meta || !r.meta?.noScrollTrack))
storage.value[r.fullPath] = Math.floor(scrollPosition) storage.value[r.fullPath] = Math.floor(scrollPosition)
}, { immediate: true, flush: 'pre' }) }, { immediate: true, flush: 'pre' })
const registerCustomRoute = (path: string) => {
customRoutes.add(path)
}
return { return {
provide: { provide: {
trackScroll: reactive({ trackScroll: reactive({
forceScroll, forceScroll,
restoreScroll, restoreScroll,
track, track,
registerCustomRoute,
restoreCustomPageScroll,
}), }),
}, },
} }