diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-06-29 15:11:25 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-06-29 15:11:25 +0900 |
| commit | f1deb89e348eb8f1a39b51e33a0ae33d59529feb (patch) | |
| tree | 2e92a7a21a1bf377719e1b125a9ac44bc14a529e /packages/frontend/src/components/MkStreamingNotesTimeline.vue | |
| parent | feat(backend): クリップ内でノートを検索できるように (diff) | |
| download | misskey-f1deb89e348eb8f1a39b51e33a0ae33d59529feb.tar.gz misskey-f1deb89e348eb8f1a39b51e33a0ae33d59529feb.tar.bz2 misskey-f1deb89e348eb8f1a39b51e33a0ae33d59529feb.zip | |
refactor(frontend): improve pagination implementation
Diffstat (limited to 'packages/frontend/src/components/MkStreamingNotesTimeline.vue')
| -rw-r--r-- | packages/frontend/src/components/MkStreamingNotesTimeline.vue | 199 |
1 files changed, 95 insertions, 104 deletions
diff --git a/packages/frontend/src/components/MkStreamingNotesTimeline.vue b/packages/frontend/src/components/MkStreamingNotesTimeline.vue index 7e72840b7b..69602f4c1c 100644 --- a/packages/frontend/src/components/MkStreamingNotesTimeline.vue +++ b/packages/frontend/src/components/MkStreamingNotesTimeline.vue @@ -56,14 +56,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide, useTemplateRef, TransitionGroup, onMounted, shallowRef, ref } from 'vue'; +import { computed, watch, onUnmounted, provide, useTemplateRef, TransitionGroup, onMounted, shallowRef, ref, markRaw } from 'vue'; import * as Misskey from 'misskey-js'; import { useInterval } from '@@/js/use-interval.js'; import { getScrollContainer, scrollToTop } from '@@/js/scroll.js'; import type { BasicTimelineType } from '@/timelines.js'; -import type { PagingCtx } from '@/composables/use-pagination.js'; import type { SoundStore } from '@/preferences/def.js'; -import { usePagination } from '@/composables/use-pagination.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import { useStream } from '@/stream.js'; import * as sound from '@/utility/sound.js'; @@ -76,6 +74,7 @@ import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { globalEvents, useGlobalEvent } from '@/events.js'; import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-separate.js'; +import { Paginator } from '@/utility/paginator.js'; const props = withDefaults(defineProps<{ src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role'; @@ -102,6 +101,97 @@ provide('inTimeline', true); provide('tl_withSensitive', computed(() => props.withSensitive)); provide('inChannel', computed(() => props.src === 'channel')); +let paginator: Paginator; + +if (props.src === 'antenna') { + paginator = markRaw(new Paginator('antennas/notes', { + computedParams: computed(() => ({ + antennaId: props.antenna, + })), + useShallowRef: true, + })); +} else if (props.src === 'home') { + paginator = markRaw(new Paginator('notes/timeline', { + computedParams: computed(() => ({ + withRenotes: props.withRenotes, + withFiles: props.onlyFiles ? true : undefined, + })), + useShallowRef: true, + })); +} else if (props.src === 'local') { + paginator = markRaw(new Paginator('notes/local-timeline', { + computedParams: computed(() => ({ + withRenotes: props.withRenotes, + withReplies: props.withReplies, + withFiles: props.onlyFiles ? true : undefined, + })), + useShallowRef: true, + })); +} else if (props.src === 'social') { + paginator = markRaw(new Paginator('notes/hybrid-timeline', { + computedParams: computed(() => ({ + withRenotes: props.withRenotes, + withReplies: props.withReplies, + withFiles: props.onlyFiles ? true : undefined, + })), + useShallowRef: true, + })); +} else if (props.src === 'global') { + paginator = markRaw(new Paginator('notes/global-timeline', { + computedParams: computed(() => ({ + withRenotes: props.withRenotes, + withFiles: props.onlyFiles ? true : undefined, + })), + useShallowRef: true, + })); +} else if (props.src === 'mentions') { + paginator = markRaw(new Paginator('notes/mentions', { + useShallowRef: true, + })); +} else if (props.src === 'directs') { + paginator = markRaw(new Paginator('notes/mentions', { + params: { + visibility: 'specified', + }, + useShallowRef: true, + })); +} else if (props.src === 'list') { + paginator = markRaw(new Paginator('notes/user-list-timeline', { + computedParams: computed(() => ({ + withRenotes: props.withRenotes, + withFiles: props.onlyFiles ? true : undefined, + listId: props.list, + })), + useShallowRef: true, + })); +} else if (props.src === 'channel') { + paginator = markRaw(new Paginator('channels/timeline', { + computedParams: computed(() => ({ + channelId: props.channel, + })), + useShallowRef: true, + })); +} else if (props.src === 'role') { + paginator = markRaw(new Paginator('roles/notes', { + computedParams: computed(() => ({ + roleId: props.role, + })), + useShallowRef: true, + })); +} else { + throw new Error('Unrecognized timeline type: ' + props.src); +} + +onMounted(() => { + paginator.init(); + + if (paginator.computedParams) { + watch(paginator.computedParams, () => { + paginator.reload(); + }, { immediate: false, deep: true }); + } +}); + function isTop() { if (scrollContainer == null) return true; if (rootEl.value == null) return true; @@ -133,17 +223,6 @@ onUnmounted(() => { } }); -type TimelineQueryType = { - antennaId?: string, - withRenotes?: boolean, - withReplies?: boolean, - withFiles?: boolean, - visibility?: string, - listId?: string, - channelId?: string, - roleId?: string -}; - let adInsertionCounter = 0; const MIN_POLLING_INTERVAL = 1000 * 10; @@ -204,7 +283,6 @@ function prepend(note: Misskey.entities.Note) { let connection: Misskey.ChannelConnection | null = null; let connection2: Misskey.ChannelConnection | null = null; -let paginationQuery: PagingCtx; const stream = store.s.realtimeMode ? useStream() : null; @@ -274,100 +352,13 @@ function disconnectChannel() { if (connection2) connection2.dispose(); } -function updatePaginationQuery() { - let endpoint: keyof Misskey.Endpoints | null; - let query: TimelineQueryType | null; - - if (props.src === 'antenna') { - endpoint = 'antennas/notes'; - query = { - antennaId: props.antenna, - }; - } else if (props.src === 'home') { - endpoint = 'notes/timeline'; - query = { - withRenotes: props.withRenotes, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'local') { - endpoint = 'notes/local-timeline'; - query = { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'social') { - endpoint = 'notes/hybrid-timeline'; - query = { - withRenotes: props.withRenotes, - withReplies: props.withReplies, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'global') { - endpoint = 'notes/global-timeline'; - query = { - withRenotes: props.withRenotes, - withFiles: props.onlyFiles ? true : undefined, - }; - } else if (props.src === 'mentions') { - endpoint = 'notes/mentions'; - query = null; - } else if (props.src === 'directs') { - endpoint = 'notes/mentions'; - query = { - visibility: 'specified', - }; - } else if (props.src === 'list') { - endpoint = 'notes/user-list-timeline'; - query = { - withRenotes: props.withRenotes, - withFiles: props.onlyFiles ? true : undefined, - listId: props.list, - }; - } else if (props.src === 'channel') { - endpoint = 'channels/timeline'; - query = { - channelId: props.channel, - }; - } else if (props.src === 'role') { - endpoint = 'roles/notes'; - query = { - roleId: props.role, - }; - } else { - throw new Error('Unrecognized timeline type: ' + props.src); - } - - paginationQuery = { - endpoint: endpoint, - limit: 10, - params: query, - }; -} - -function refreshEndpointAndChannel() { +watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], () => { if (store.s.realtimeMode) { disconnectChannel(); connectChannel(); } - - updatePaginationQuery(); -} - -// デッキのリストカラムでwithRenotesを変更した場合に自動的に更新されるようにさせる -// IDが切り替わったら切り替え先のTLを表示させたい -watch(() => [props.list, props.antenna, props.channel, props.role, props.withRenotes], refreshEndpointAndChannel); - -// withSensitiveはクライアントで完結する処理のため、単にリロードするだけでOK -watch(() => props.withSensitive, reloadTimeline); - -// 初回表示用 -refreshEndpointAndChannel(); - -const paginator = usePagination({ - ctx: paginationQuery, - useShallowRef: true, }); +watch(() => props.withSensitive, reloadTimeline); onUnmounted(() => { disconnectChannel(); |