From 0b226f7013ca4414734e53fb02528c197c1d9bbd Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Mon, 1 Apr 2024 11:01:59 +0800 Subject: [PATCH] store emojis for note_edit --- .../1711936358554-expand-note-edit.ts | 15 ++++++++++++++ packages/backend/src/misc/populate-emojis.ts | 20 ++++++++++++++++++- .../backend/src/models/entities/note-edit.ts | 7 +++++++ .../src/models/repositories/note-edit.ts | 17 +++++++++++++--- .../backend/src/models/schema/note-edit.ts | 5 +++++ .../src/remote/activitypub/models/note.ts | 1 + .../src/server/api/endpoints/notes/edit.ts | 1 + .../src/server/api/endpoints/notes/history.ts | 2 +- packages/client/src/pages/note-history.vue | 2 ++ packages/firefish-js/src/entities.ts | 4 ++++ 10 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 packages/backend/src/migration/1711936358554-expand-note-edit.ts diff --git a/packages/backend/src/migration/1711936358554-expand-note-edit.ts b/packages/backend/src/migration/1711936358554-expand-note-edit.ts new file mode 100644 index 0000000000..1f23736fd1 --- /dev/null +++ b/packages/backend/src/migration/1711936358554-expand-note-edit.ts @@ -0,0 +1,15 @@ +import type { MigrationInterface, QueryRunner } from "typeorm"; + +export class ExpandNoteEdit1711936358554 implements MigrationInterface { + async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "note_edit" ADD "emojis" character varying(128) array NOT NULL DEFAULT '{}'::varchar[] + `); + } + + async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(` + ALTER TABLE "note_edit" DROP COLUMN "emojis" + `); + } +} diff --git a/packages/backend/src/misc/populate-emojis.ts b/packages/backend/src/misc/populate-emojis.ts index 0e21d0e2ab..45e36649a1 100644 --- a/packages/backend/src/misc/populate-emojis.ts +++ b/packages/backend/src/misc/populate-emojis.ts @@ -8,6 +8,7 @@ import { decodeReaction } from "./reaction-lib.js"; import config from "@/config/index.js"; import { query } from "@/prelude/url.js"; import { redisClient } from "@/db/redis.js"; +import type { NoteEdit } from "@/models/entities/note-edit.js"; const cache = new Cache("populateEmojis", 60 * 60 * 12); @@ -110,6 +111,23 @@ export async function populateEmojis( return emojis.filter((x): x is PopulatedEmoji => x != null); } +export function aggregateNoteEditEmojis( + noteEdits: NoteEdit[], + sourceHost: string | null, +) { + let emojis: string[] = []; + for (const noteEdit of noteEdits) { + emojis = emojis.concat(noteEdit.emojis); + } + emojis = Array.from(new Set(emojis)); + return emojis + .map((e) => parseEmojiStr(e, sourceHost)) + .filter((x) => x.name != null) as { + name: string; + host: string | null; + }[]; +} + export function aggregateNoteEmojis(notes: Note[]) { let emojis: { name: string | null; host: string | null }[] = []; for (const note of notes) { @@ -145,7 +163,7 @@ export function aggregateNoteEmojis(notes: Note[]) { } /** - * 与えられた絵文字のリストをデータベースから取得し、キャッシュに追加します + * Get the given list of emojis from the database and adds them to the cache */ export async function prefetchEmojis( emojis: { name: string; host: string | null }[], diff --git a/packages/backend/src/models/entities/note-edit.ts b/packages/backend/src/models/entities/note-edit.ts index 8761e2b153..ceb423411d 100644 --- a/packages/backend/src/models/entities/note-edit.ts +++ b/packages/backend/src/models/entities/note-edit.ts @@ -50,4 +50,11 @@ export class NoteEdit { comment: "The updated date of the Note.", }) public updatedAt: Date; + + @Column("varchar", { + length: 128, + array: true, + default: "{}", + }) + public emojis: string[]; } diff --git a/packages/backend/src/models/repositories/note-edit.ts b/packages/backend/src/models/repositories/note-edit.ts index 88314eacc2..210d489712 100644 --- a/packages/backend/src/models/repositories/note-edit.ts +++ b/packages/backend/src/models/repositories/note-edit.ts @@ -1,11 +1,17 @@ import { db } from "@/db/postgre.js"; import { NoteEdit } from "@/models/entities/note-edit.js"; +import type { Note } from "@/models/entities/note.js"; import { awaitAll } from "@/prelude/await-all.js"; import type { Packed } from "@/misc/schema.js"; import { DriveFiles } from "../index.js"; +import { + aggregateNoteEditEmojis, + populateEmojis, + prefetchEmojis, +} from "@/misc/populate-emojis.js"; export const NoteEditRepository = db.getRepository(NoteEdit).extend({ - async pack(noteEdit: NoteEdit) { + async pack(noteEdit: NoteEdit, sourceNote: Note) { const packed: Packed<"NoteEdit"> = await awaitAll({ id: noteEdit.id, noteId: noteEdit.noteId, @@ -14,15 +20,20 @@ export const NoteEditRepository = db.getRepository(NoteEdit).extend({ cw: noteEdit.cw, fileIds: noteEdit.fileIds, files: DriveFiles.packMany(noteEdit.fileIds), + emojis: populateEmojis(noteEdit.emojis, sourceNote.userHost), }); return packed; }, - async packMany(noteEdits: NoteEdit[]) { + async packMany(noteEdits: NoteEdit[], sourceNote: Note) { if (noteEdits.length === 0) return []; + await prefetchEmojis( + aggregateNoteEditEmojis(noteEdits, sourceNote.userHost), + ); + const promises = await Promise.allSettled( - noteEdits.map((n) => this.pack(n)), + noteEdits.map((n) => this.pack(n, sourceNote)), ); // filter out rejected promises, only keep fulfilled values diff --git a/packages/backend/src/models/schema/note-edit.ts b/packages/backend/src/models/schema/note-edit.ts index 0ca89d96fe..478eece67c 100644 --- a/packages/backend/src/models/schema/note-edit.ts +++ b/packages/backend/src/models/schema/note-edit.ts @@ -56,5 +56,10 @@ export const packedNoteEdit = { ref: "DriveFile", }, }, + emojis: { + type: "object", + optional: true, + nullable: true, + }, }, } as const; diff --git a/packages/backend/src/remote/activitypub/models/note.ts b/packages/backend/src/remote/activitypub/models/note.ts index 0d706df5d7..3077ddb4f0 100644 --- a/packages/backend/src/remote/activitypub/models/note.ts +++ b/packages/backend/src/remote/activitypub/models/note.ts @@ -773,6 +773,7 @@ export async function updateNote(value: string | IObject, resolver?: Resolver) { cw: note.cw, fileIds: note.fileIds, updatedAt: update.updatedAt, + emojis: note.emojis, }); publishing = true; diff --git a/packages/backend/src/server/api/endpoints/notes/edit.ts b/packages/backend/src/server/api/endpoints/notes/edit.ts index c7670151e0..012f22bbd7 100644 --- a/packages/backend/src/server/api/endpoints/notes/edit.ts +++ b/packages/backend/src/server/api/endpoints/notes/edit.ts @@ -621,6 +621,7 @@ export default define(meta, paramDef, async (ps, user) => { cw: note.cw, fileIds: note.fileIds, updatedAt: new Date(), + emojis: note.emojis, }); publishing = true; diff --git a/packages/backend/src/server/api/endpoints/notes/history.ts b/packages/backend/src/server/api/endpoints/notes/history.ts index 4cde10385e..b6677743bb 100644 --- a/packages/backend/src/server/api/endpoints/notes/history.ts +++ b/packages/backend/src/server/api/endpoints/notes/history.ts @@ -63,5 +63,5 @@ export default define(meta, paramDef, async (ps, user) => { }, }); - return await NoteEdits.packMany(history); + return await NoteEdits.packMany(history, note); }); diff --git a/packages/client/src/pages/note-history.vue b/packages/client/src/pages/note-history.vue index 6ce79a116c..423df90fdb 100644 --- a/packages/client/src/pages/note-history.vue +++ b/packages/client/src/pages/note-history.vue @@ -93,6 +93,7 @@ function convertNoteEditsToNotes(noteEdits: NoteEdit[]) { cw: note.value.cw, files: note.value.files, fileIds: note.value.fileIds, + emojis: note.value.emojis, }; return [now] @@ -108,6 +109,7 @@ function convertNoteEditsToNotes(noteEdits: NoteEdit[]) { _shouldInsertAd_: false, files: noteEdit.files, fileIds: noteEdit.fileIds, + emojis: note.value.emojis.concat(noteEdit.emojis), }); }); } diff --git a/packages/firefish-js/src/entities.ts b/packages/firefish-js/src/entities.ts index a237b1a23f..ef1804ee71 100644 --- a/packages/firefish-js/src/entities.ts +++ b/packages/firefish-js/src/entities.ts @@ -186,6 +186,10 @@ export type NoteEdit = { updatedAt: string; fileIds: DriveFile["id"][]; files: DriveFile[]; + emojis: { + name: string; + url: string; + }[]; }; export type NoteReaction = {