diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index aeb16c9b0f..c0a0f9d661 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -87,6 +87,11 @@ export type Paging offsetMode?: boolean; pageEl?: HTMLElement; + + /** + * 変換関数 + */ + transform?: (source: Omit[]) => MisskeyEntity[]; }; type MisskeyEntityMap = Map; @@ -169,6 +174,11 @@ const visibility = useDocumentVisibility(); const isPausingUpdateByExecutingQueue = ref(false); const denyMoveTransition = ref(false); +/** + * 変換関数 + */ +const transform = computed(() => props.pagination.transform ?? ((source: MisskeyEntity[]) => source)); + //#region scrolling const checkFn = props.pagination.reversed ? isBottomVisible : isTopVisible; const checkTop = (tolerance?: number) => { @@ -298,7 +308,9 @@ async function init(): Promise { await os.api(props.pagination.endpoint, { ...params, limit: props.pagination.limit ?? 10, - }).then(res => { + }).then(_res => { + const res = transform.value(_res); + for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 3) item._shouldInsertAd_ = true; @@ -378,7 +390,9 @@ const fetchMore = async (): Promise => { } : { untilId: Array.from(items.value.keys()).at(-1), }), - }).then(res => { + }).then(_res => { + const res = transform.value(_res); + for (let i = 0; i < res.length; i++) { const item = res[i]; if (i === 10) item._shouldInsertAd_ = true; @@ -425,15 +439,15 @@ const fetchMoreAhead = async (): Promise => { } : { sinceId: Array.from(items.value.keys()).at(-1), }), - }).then(res => { - if (res.length === 0) { - items.value = concatMapWithArray(items.value, res); + }).then(_res => { + if (_res.length === 0) { more.value = false; } else { + const res = transform.value(_res); items.value = concatMapWithArray(items.value, res); more.value = true; } - offset.value += res.length; + offset.value += _res.length; moreFetching.value = false; }, err => { moreFetching.value = false; @@ -484,9 +498,12 @@ watch([active, visibility], () => { /** * 最新のものとして1つだけアイテムを追加する * ストリーミングから降ってきたアイテムはこれで追加する - * @param item アイテム + * @param item アイテム(transform前) */ -const prepend = (item: MisskeyEntity): void => { +const prepend = (_item: MisskeyEntity): void => { + const item = transform.value([_item])[0]; + if (!item) return; + if (items.value.size === 0) { items.value.set(item.id, item); fetching.value = false; @@ -570,8 +587,12 @@ function prependQueue(newItem: MisskeyEntity) { /* * アイテムを末尾に追加する(使うの?) + * @param item アイテム(transform前) */ -const appendItem = (item: MisskeyEntity): void => { +const appendItem = (_item: MisskeyEntity): void => { + const item = transform.value([_item])[0]; + if (!item) return; + items.value.set(item.id, item); }; diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index a02dc67973..2adea4ae38 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -26,6 +26,9 @@ import { $i } from '@/account'; import { defaultStore } from '@/store'; import { i18n } from '@/i18n'; import { instance } from '@/instance'; +import { noteManager } from '@/scripts/entity-manager'; +import { Note } from 'misskey-js/built/entities'; +import { MisskeyEntity } from '@/types/date-separated-list'; const props = defineProps<{ src: string; @@ -62,6 +65,16 @@ const prepend = note => { } }; +const transform = (notes: Note[]): MisskeyEntity[] => { + return notes.map(note => { + noteManager.set(note); + return { + id: note.id, + createdAt: note.createdAt, + }; + }); +}; + let endpoint; let query; let connection; @@ -165,6 +178,7 @@ const pagination = { endpoint: endpoint, limit: 10, params: query, + transform: transform, }; onUnmounted(() => { diff --git a/packages/frontend/src/scripts/entity-manager.ts b/packages/frontend/src/scripts/entity-manager.ts index 461cb6bf44..9481ddbd0b 100644 --- a/packages/frontend/src/scripts/entity-manager.ts +++ b/packages/frontend/src/scripts/entity-manager.ts @@ -53,8 +53,9 @@ export class NoteManager { private notesSource: Map; /** - * ソースからuser, renote, replyを取得したComputedRef + * ソースからuser, renote, replyを取得したComputedRefのキャッシュを保持しておく * nullは削除済みであることを表す + * キャプチャが0になったら削除される */ private notesComputed: Map; private updatedAt: Map; @@ -81,7 +82,7 @@ export class NoteManager { }); } - public set(_note: Note): CachedNote { + public set(_note: Note): void { const note: Note = { ..._note }; userLiteManager.set(note.user); // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -98,7 +99,6 @@ export class NoteManager { this.notesSource.set(note.id, ref(note)); } this.updatedAt.set(note.id, Date.now()); - return this.get(note.id)!; } public get(id: string): CachedNote { @@ -139,7 +139,10 @@ export class NoteManager { } } return api('notes/show', { noteId: id }) - .then(fetchedNote => this.set(fetchedNote)) + .then(fetchedNote => { + this.set(fetchedNote); + return this.get(id)!; + }) .catch(() => { // エラーが発生した場合はとりあえず削除されたものとして扱う const cached = this.notesSource.get(id); @@ -161,6 +164,8 @@ export class NoteManager { if (!note || !note.value) { this.connection?.send('un', { id }); this.captureing.delete(id); + this.notesComputed.delete(id); + this.updatedAt.delete(id); return; } @@ -219,6 +224,8 @@ export class NoteManager { break; } } + + this.updatedAt.set(id, Date.now()); } private capture(id: string, markRead = true): void { @@ -252,6 +259,9 @@ export class NoteManager { } this.captureing.delete(id); + + // キャプチャが終わったらcomputedキャッシュも消してしまう + this.notesComputed.delete(id); } /**