diff --git a/packages/backend/src/models/repositories/note.ts b/packages/backend/src/models/repositories/note.ts index 25b8e8083..7c976d8d8 100644 --- a/packages/backend/src/models/repositories/note.ts +++ b/packages/backend/src/models/repositories/note.ts @@ -10,6 +10,7 @@ import { Followings, Polls, Channels, + Notes, } from "../index.js"; import type { Packed } from "@/misc/schema.js"; import { nyaize } from "@/misc/nyaize.js"; @@ -95,6 +96,19 @@ async function populateMyReaction( return undefined; } +async function populateIsRenoted( + note: Note, + meId: User["id"], + _hint_?: { + myRenotes: Map; + }, +) { + return _hint_?.myRenotes + ? _hint_.myRenotes.get(note.id) ? true : undefined + : Notes.exist({ where: { renoteId: note.id, userId: meId } }) + .then(res => res ? true : undefined); +} + export const NoteRepository = db.getRepository(Note).extend({ async isVisibleForMe(note: Note, meId: User["id"] | null): Promise { // This code must always be synchronized with the checks in generateVisibilityQuery. @@ -156,6 +170,7 @@ export const NoteRepository = db.getRepository(Note).extend({ detail?: boolean; _hint_?: { myReactions: Map; + myRenotes: Map; }; }, ): Promise> { @@ -240,6 +255,7 @@ export const NoteRepository = db.getRepository(Note).extend({ ...(meId ? { myReaction: populateMyReaction(note, meId, options?._hint_), + isRenoted: populateIsRenoted(note, meId, options?._hint_) } : {}), @@ -290,6 +306,7 @@ export const NoteRepository = db.getRepository(Note).extend({ detail?: boolean; _hint_?: { myReactions: Map; + myRenotes: Map; }; }, ): Promise | undefined> { @@ -311,6 +328,7 @@ export const NoteRepository = db.getRepository(Note).extend({ const meId = me ? me.id : null; const myReactionsMap = new Map(); + const myRenotesMap = new Map(); if (meId) { const renoteIds = notes .filter((n) => n.renoteId != null) @@ -320,12 +338,22 @@ export const NoteRepository = db.getRepository(Note).extend({ userId: meId, noteId: In(targets), }); + const myRenotes = await Notes.createQueryBuilder('note') + .select('note.renoteId') + .where('note.userId = :meId', { meId }) + .andWhere('note.renoteId IN (:...targets)', { targets }) + .getMany(); for (const target of targets) { myReactionsMap.set( target, myReactions.find((reaction) => reaction.noteId === target) || null, ); + + myRenotesMap.set( + target, + !!myRenotes.find(p => p.renoteId == target), + ); } } @@ -337,6 +365,7 @@ export const NoteRepository = db.getRepository(Note).extend({ ...options, _hint_: { myReactions: myReactionsMap, + myRenotes: myRenotesMap }, }), ), diff --git a/packages/backend/src/models/repositories/notification.ts b/packages/backend/src/models/repositories/notification.ts index 1538e67d8..2f77c35ca 100644 --- a/packages/backend/src/models/repositories/notification.ts +++ b/packages/backend/src/models/repositories/notification.ts @@ -22,6 +22,7 @@ export const NotificationRepository = db.getRepository(Notification).extend({ options: { _hintForEachNotes_?: { myReactions: Map; + myRenotes: Map; }; }, ): Promise> { @@ -153,6 +154,7 @@ export const NotificationRepository = db.getRepository(Notification).extend({ .map((x) => x.note!); const noteIds = notes.map((n) => n.id); const myReactionsMap = new Map(); + const myRenotesMap = new Map(); const renoteIds = notes .filter((n) => n.renoteId != null) .map((n) => n.renoteId!); @@ -161,12 +163,22 @@ export const NotificationRepository = db.getRepository(Notification).extend({ userId: meId, noteId: In(targets), }); + const myRenotes = await Notes.createQueryBuilder('note') + .select('note.renoteId') + .where('note.userId = :meId', { meId }) + .andWhere('note.renoteId IN array[:...targets]', { targets }) + .getMany(); for (const target of targets) { myReactionsMap.set( target, myReactions.find((reaction) => reaction.noteId === target) || null, ); + + myRenotesMap.set( + target, + !!myRenotes.find(p => p.renoteId == target), + ); } await prefetchEmojis(aggregateNoteEmojis(notes)); @@ -176,6 +188,7 @@ export const NotificationRepository = db.getRepository(Notification).extend({ this.pack(x, { _hintForEachNotes_: { myReactions: myReactionsMap, + myRenotes: myRenotesMap }, }).catch((e) => null), ), diff --git a/packages/backend/src/models/schema/note.ts b/packages/backend/src/models/schema/note.ts index e17f054e8..b956e7d57 100644 --- a/packages/backend/src/models/schema/note.ts +++ b/packages/backend/src/models/schema/note.ts @@ -190,11 +190,15 @@ export const packedNoteSchema = { optional: true, nullable: false, }, - myReaction: { type: "object", optional: true, nullable: true, }, + isRenoted: { + type: "boolean", + optional: true, + nullable: true, + } }, } as const; diff --git a/packages/client/src/components/MkRenoteButton.vue b/packages/client/src/components/MkRenoteButton.vue index 621c65d4e..9ee600aea 100644 --- a/packages/client/src/components/MkRenoteButton.vue +++ b/packages/client/src/components/MkRenoteButton.vue @@ -70,14 +70,7 @@ useTooltip(buttonRef, async (showing) => { ); }); -let hasRenotedBefore = $ref(false); -os.api("notes/renotes", { - noteId: props.note.id, - userId: $i.id, - limit: 1, -}).then((res) => { - hasRenotedBefore = res.length > 0; -}); +let hasRenotedBefore = $ref(props.note.isRenoted ?? false); const renote = (viaKeyboard = false, ev?: MouseEvent) => { pleaseLogin(); diff --git a/packages/iceshrimp-js/src/entities.ts b/packages/iceshrimp-js/src/entities.ts index e4777a4ce..e47b05caa 100644 --- a/packages/iceshrimp-js/src/entities.ts +++ b/packages/iceshrimp-js/src/entities.ts @@ -151,6 +151,7 @@ export type Note = { localOnly?: boolean; channel?: Channel["id"]; myReaction?: string; + isRenoted?: boolean; reactions: Record; renoteCount: number; repliesCount: number;