summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components/MkStreamingNotesTimeline.vue
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-06-29 15:11:25 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2025-06-29 15:11:25 +0900
commitf1deb89e348eb8f1a39b51e33a0ae33d59529feb (patch)
tree2e92a7a21a1bf377719e1b125a9ac44bc14a529e /packages/frontend/src/components/MkStreamingNotesTimeline.vue
parentfeat(backend): クリップ内でノートを検索できるように (diff)
downloadmisskey-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.vue199
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();