refactor: reactive note data を composable内で生成するように

This commit is contained in:
kakkokari-gtyih 2025-05-24 18:01:42 +09:00
parent 7081c4f30e
commit f386b8d803
3 changed files with 43 additions and 50 deletions

View File

@ -193,7 +193,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onMounted, ref, useTemplateRef, watch, provide, shallowRef, reactive } from 'vue'; import { computed, inject, onMounted, ref, useTemplateRef, provide } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js'; import { isLink } from '@@/js/is-link.js';
@ -228,7 +228,6 @@ import { $i } from '@/i.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js'; import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js';
import { noteEvents, useNoteCapture } from '@/composables/use-note-capture.js'; import { noteEvents, useNoteCapture } from '@/composables/use-note-capture.js';
import type { ReactiveNoteData } from '@/composables/use-note-capture.js';
import { deepClone } from '@/utility/clone.js'; import { deepClone } from '@/utility/clone.js';
import { useTooltip } from '@/composables/use-tooltip.js'; import { useTooltip } from '@/composables/use-tooltip.js';
import { claimAchievement } from '@/utility/achievements.js'; import { claimAchievement } from '@/utility/achievements.js';
@ -284,12 +283,10 @@ if (noteViewInterruptors.length > 0) {
const isRenote = Misskey.note.isPureRenote(note); const isRenote = Misskey.note.isPureRenote(note);
const appearNote = getAppearNote(note); const appearNote = getAppearNote(note);
const $appearNote = reactive<ReactiveNoteData>({ const { $note: $appearNote, subscribe: subscribeManuallyToNoteCapture } = useNoteCapture({
reactions: appearNote.reactions, note: appearNote,
reactionCount: appearNote.reactionCount, parentNote: note,
reactionEmojis: appearNote.reactionEmojis, mock: props.mock,
myReaction: appearNote.myReaction,
pollChoices: appearNote.poll?.choices ?? [],
}); });
const rootEl = useTemplateRef('rootEl'); const rootEl = useTemplateRef('rootEl');
@ -411,17 +408,6 @@ provide(DI.mfmEmojiReactCallback, (reaction) => {
}); });
}); });
let subscribeManuallyToNoteCapture: () => void = () => { };
if (!props.mock) {
const { subscribe } = useNoteCapture({
note: appearNote,
parentNote: note,
$note: $appearNote,
});
subscribeManuallyToNoteCapture = subscribe;
}
if (!props.mock) { if (!props.mock) {
useTooltip(renoteButton, async (showing) => { useTooltip(renoteButton, async (showing) => {
const renotes = await misskeyApi('notes/renotes', { const renotes = await misskeyApi('notes/renotes', {

View File

@ -228,7 +228,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject, onMounted, provide, reactive, ref, useTemplateRef } from 'vue'; import { computed, inject, onMounted, provide, ref, useTemplateRef } from 'vue';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { isLink } from '@@/js/is-link.js'; import { isLink } from '@@/js/is-link.js';
@ -259,7 +259,6 @@ import { $i } from '@/i.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js'; import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js';
import { noteEvents, useNoteCapture } from '@/composables/use-note-capture.js'; import { noteEvents, useNoteCapture } from '@/composables/use-note-capture.js';
import type { ReactiveNoteData } from '@/composables/use-note-capture.js';
import { deepClone } from '@/utility/clone.js'; import { deepClone } from '@/utility/clone.js';
import { useTooltip } from '@/composables/use-tooltip.js'; import { useTooltip } from '@/composables/use-tooltip.js';
import { claimAchievement } from '@/utility/achievements.js'; import { claimAchievement } from '@/utility/achievements.js';
@ -305,12 +304,9 @@ if (noteViewInterruptors.length > 0) {
const isRenote = Misskey.note.isPureRenote(note); const isRenote = Misskey.note.isPureRenote(note);
const appearNote = getAppearNote(note); const appearNote = getAppearNote(note);
const $appearNote = reactive<ReactiveNoteData>({ const { $note: $appearNote, subscribe: subscribeManuallyToNoteCapture } = useNoteCapture({
reactions: appearNote.reactions, note: appearNote,
reactionCount: appearNote.reactionCount, parentNote: note,
reactionEmojis: appearNote.reactionEmojis,
myReaction: appearNote.myReaction,
pollChoices: appearNote.poll?.choices ?? [],
}); });
const rootEl = useTemplateRef('rootEl'); const rootEl = useTemplateRef('rootEl');
@ -398,12 +394,6 @@ const reactionsPagination = computed(() => ({
}, },
})); }));
const { subscribe: subscribeManuallyToNoteCapture } = useNoteCapture({
note: appearNote,
parentNote: note,
$note: $appearNote,
});
useTooltip(renoteButton, async (showing) => { useTooltip(renoteButton, async (showing) => {
const renotes = await misskeyApi('notes/renotes', { const renotes = await misskeyApi('notes/renotes', {
noteId: appearNote.id, noteId: appearNote.id,

View File

@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { onUnmounted } from 'vue'; import { onUnmounted, reactive } from 'vue';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import { EventEmitter } from 'eventemitter3'; import { EventEmitter } from 'eventemitter3';
import type { Reactive } from 'vue'; import type { Reactive } from 'vue';
@ -188,27 +188,31 @@ export type ReactiveNoteData = {
}; };
export function useNoteCapture(props: { export function useNoteCapture(props: {
note: Pick<Misskey.entities.Note, 'id' | 'createdAt'>; note: Misskey.entities.Note;
parentNote: Misskey.entities.Note | null; parentNote: Misskey.entities.Note | null;
$note: Reactive<ReactiveNoteData>; mock?: boolean;
}): { }): {
$note: Reactive<ReactiveNoteData>;
subscribe: () => void; subscribe: () => void;
} { } {
const { note, parentNote, $note } = props; const { note, parentNote, mock } = props;
// Normalize reactions in-place const $note = reactive<ReactiveNoteData>({
for (const name of Object.keys($note.reactions)) { reactions: Object.entries(note.reactions).reduce((acc, [name, count]) => {
const normalizedName = name.replace(/^:(\w+):$/, ':$1@.:'); // Normalize reactions
if (normalizedName !== name) { const normalizedName = name.replace(/^:(\w+):$/, ':$1@.:');
const count = $note.reactions[name]; if (acc[normalizedName] == null) {
if ($note.reactions[normalizedName] == null) { acc[normalizedName] = count;
$note.reactions[normalizedName] = count;
} else { } else {
$note.reactions[normalizedName] += count; acc[normalizedName] += count;
} }
delete $note.reactions[name]; return acc;
} }, {} as Misskey.entities.Note['reactions']),
} reactionCount: note.reactionCount,
reactionEmojis: note.reactionEmojis,
myReaction: note.myReaction,
pollChoices: note.poll?.choices ?? [],
});
noteEvents.on(`reacted:${note.id}`, onReacted); noteEvents.on(`reacted:${note.id}`, onReacted);
noteEvents.on(`unreacted:${note.id}`, onUnreacted); noteEvents.on(`unreacted:${note.id}`, onUnreacted);
@ -273,10 +277,20 @@ export function useNoteCapture(props: {
} }
function subscribe() { function subscribe() {
if (mock) {
// モックモードでは購読しない
return;
}
if ($i && store.s.realtimeMode) { if ($i && store.s.realtimeMode) {
realtimeSubscribe(props); realtimeSubscribe({
note,
});
} else { } else {
pollingSubscribe(props); pollingSubscribe({
note,
$note,
});
} }
} }
@ -293,6 +307,7 @@ export function useNoteCapture(props: {
if ((Date.now() - new Date(note.createdAt).getTime()) > 1000 * 60 * 5) { // 5min if ((Date.now() - new Date(note.createdAt).getTime()) > 1000 * 60 * 5) { // 5min
// リノートで表示されているノートでもないし、投稿からある程度経過しているので自動で購読しない // リノートで表示されているノートでもないし、投稿からある程度経過しているので自動で購読しない
return { return {
$note,
subscribe: () => { subscribe: () => {
subscribe(); subscribe();
}, },
@ -302,6 +317,7 @@ export function useNoteCapture(props: {
if ((Date.now() - new Date(parentNote.createdAt).getTime()) > 1000 * 60 * 5) { // 5min if ((Date.now() - new Date(parentNote.createdAt).getTime()) > 1000 * 60 * 5) { // 5min
// リノートで表示されているノートだが、リノートされてからある程度経過しているので自動で購読しない // リノートで表示されているノートだが、リノートされてからある程度経過しているので自動で購読しない
return { return {
$note,
subscribe: () => { subscribe: () => {
subscribe(); subscribe();
}, },
@ -312,6 +328,7 @@ export function useNoteCapture(props: {
subscribe(); subscribe();
return { return {
$note,
subscribe: () => { subscribe: () => {
// すでに購読しているので何もしない // すでに購読しているので何もしない
}, },