From d64d133d7f9d741a16d374c200710787f6b04b8e Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Tue, 26 Mar 2024 01:33:42 +0800 Subject: [PATCH 01/14] backend: added /note/history endpoint --- packages/backend/src/models/index.ts | 4 +- .../src/models/repositories/note-edit.ts | 37 +++++++++++ .../backend/src/models/schema/note-edit.ts | 2 +- packages/backend/src/server/api/endpoints.ts | 2 + .../src/server/api/endpoints/notes/history.ts | 64 +++++++++++++++++++ 5 files changed, 106 insertions(+), 3 deletions(-) create mode 100644 packages/backend/src/models/repositories/note-edit.ts create mode 100644 packages/backend/src/server/api/endpoints/notes/history.ts diff --git a/packages/backend/src/models/index.ts b/packages/backend/src/models/index.ts index 5d4ff52198..c578d9d409 100644 --- a/packages/backend/src/models/index.ts +++ b/packages/backend/src/models/index.ts @@ -65,14 +65,14 @@ import { UserPending } from "./entities/user-pending.js"; import { InstanceRepository } from "./repositories/instance.js"; import { Webhook } from "./entities/webhook.js"; import { UserIp } from "./entities/user-ip.js"; -import { NoteEdit } from "./entities/note-edit.js"; import { NoteFileRepository } from "./repositories/note-file.js"; +import { NoteEditRepository } from "./repositories/note-edit.js"; export const Announcements = db.getRepository(Announcement); export const AnnouncementReads = db.getRepository(AnnouncementRead); export const Apps = AppRepository; export const Notes = NoteRepository; -export const NoteEdits = db.getRepository(NoteEdit); +export const NoteEdits = NoteEditRepository; export const NoteFiles = NoteFileRepository; export const NoteFavorites = NoteFavoriteRepository; export const NoteWatchings = db.getRepository(NoteWatching); diff --git a/packages/backend/src/models/repositories/note-edit.ts b/packages/backend/src/models/repositories/note-edit.ts new file mode 100644 index 0000000000..31a63af5b1 --- /dev/null +++ b/packages/backend/src/models/repositories/note-edit.ts @@ -0,0 +1,37 @@ +import { db } from "@/db/postgre.js"; +import { NoteEdit } from "@/models/entities/note-edit.js"; +import { awaitAll } from "@/prelude/await-all.js"; +import type { Packed } from "@/misc/schema.js"; + +export const NoteEditRepository = db.getRepository(NoteEdit).extend({ + async pack( + noteEdit: NoteEdit, + ) { + const packed: Packed<"NoteEdit"> = await awaitAll({ + id: noteEdit.id, + noteId: noteEdit.noteId, + updatedAt: noteEdit.updatedAt.toISOString(), + text: noteEdit.text, + cw: noteEdit.cw, + fileIds: noteEdit.fileIds, + }) + + return packed; + }, + async packMany( + noteEdits: NoteEdit[], + ) { + if (noteEdits.length === 0) return []; + + const promises = await Promise.allSettled( + noteEdits.map((n) => + this.pack(n) + ), + ); + + // filter out rejected promises, only keep fulfilled values + return promises.flatMap((result) => + result.status === "fulfilled" ? [result.value] : [], + ); + } +}); diff --git a/packages/backend/src/models/schema/note-edit.ts b/packages/backend/src/models/schema/note-edit.ts index e877f3f946..e02b866abd 100644 --- a/packages/backend/src/models/schema/note-edit.ts +++ b/packages/backend/src/models/schema/note-edit.ts @@ -16,7 +16,7 @@ export const packedNoteEdit = { }, note: { type: "object", - optional: false, + optional: true, nullable: false, ref: "Note", }, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 52dd3382f7..9a0de00b8b 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -240,6 +240,7 @@ import * as ep___notes_conversation from "./endpoints/notes/conversation.js"; import * as ep___notes_create from "./endpoints/notes/create.js"; import * as ep___notes_delete from "./endpoints/notes/delete.js"; import * as ep___notes_edit from "./endpoints/notes/edit.js"; +import * as ep___notes_history from "./endpoints/notes/history.js"; import * as ep___notes_favorites_create from "./endpoints/notes/favorites/create.js"; import * as ep___notes_favorites_delete from "./endpoints/notes/favorites/delete.js"; import * as ep___notes_featured from "./endpoints/notes/featured.js"; @@ -583,6 +584,7 @@ const eps = [ ["notes/create", ep___notes_create], ["notes/delete", ep___notes_delete], ["notes/edit", ep___notes_edit], + ["notes/history", ep___notes_history], ["notes/favorites/create", ep___notes_favorites_create], ["notes/favorites/delete", ep___notes_favorites_delete], ["notes/featured", ep___notes_featured], diff --git a/packages/backend/src/server/api/endpoints/notes/history.ts b/packages/backend/src/server/api/endpoints/notes/history.ts new file mode 100644 index 0000000000..e0c7be3df5 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/history.ts @@ -0,0 +1,64 @@ +import { NoteEdits } from "@/models/index.js"; +import define from "@/server/api/define.js"; +import { ApiError } from "@/server/api/error.js"; +import { getNote } from "@/server/api/common/getters.js"; +import type { NoteEdit } from "@/models/entities/note-edit.js"; + +export const meta = { + tags: ["notes"], + + requireCredential: false, + requireCredentialPrivateMode: true, + description: "Get edit history of a note", + + res: { + type: "array", + optional: false, + nullable: true, + items: { + type: "object", + optional: false, + nullable: false, + ref: "NoteEdit", + }, + }, + + errors: { + noSuchNote: { + message: "No such note.", + code: "NO_SUCH_NOTE", + id: "e1035875-9551-45ec-afa8-1ded1fcb53c8", + }, + }, +} as const; + +export const paramDef = { + type: "object", + properties: { + noteId: { + type: "string", + format: "misskey:id", + }, + limit: { type: "integer", minimum: 1, maximum: 100, default: 10 }, + offset: { type: "integer", default: 0 }, + }, + required: ["noteId"], +} as const; + +export default define(meta, paramDef, async (ps, user) => { + const note = await getNote(ps.noteId, user).catch((err) => { + if (err.id === "9725d0ce-ba28-4dde-95a7-2cbb2c15de24") + throw new ApiError(meta.errors.noSuchNote); + throw err; + }); + + const history: NoteEdit[] = await NoteEdits.find({ + where: { + noteId: note.id, + }, + take: ps.limit, + skip: ps.offset, + }); + + return await NoteEdits.packMany(history); +}); From bab704992ff71b823dee50e5c0de414b8c0eb910 Mon Sep 17 00:00:00 2001 From: Lhcfl Date: Tue, 26 Mar 2024 16:59:42 +0800 Subject: [PATCH 02/14] feat: add post history page --- locales/en-US.yml | 1 + locales/zh-CN.yml | 1 + .../src/server/api/endpoints/notes/history.ts | 3 + .../src/components/MkDateSeparatedList.vue | 2 +- packages/client/src/components/MkNote.vue | 8 +- packages/client/src/filters/note.ts | 9 +- packages/client/src/pages/note-history.vue | 123 ++++++++++++++++++ packages/client/src/router.ts | 5 + packages/client/src/scripts/get-note-menu.ts | 17 +++ packages/firefish-js/src/api.types.ts | 9 ++ packages/firefish-js/src/entities.ts | 13 +- 11 files changed, 186 insertions(+), 5 deletions(-) create mode 100644 packages/client/src/pages/note-history.vue diff --git a/locales/en-US.yml b/locales/en-US.yml index 88b44d2787..9a85183fe3 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -2232,3 +2232,4 @@ messagingUnencryptedInfo: "Chats on Firefish are not end-to-end encrypted. Don't any sensitive infomation over Firefish." autocorrectNoteLanguage: "Show a warning if the post language does not match the auto-detected result" incorrectLanguageWarning: "It looks like your post is in {detected}, but you selected {current}.\nWould you like to set the language to {detected} instead?" +noteEditHistory: "Post edit history" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index f67bf4a600..85b73d7b45 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -2060,3 +2060,4 @@ noAltTextWarning: 有些附件没有描述。您是否忘记写描述了? showNoAltTextWarning: 当您尝试发布没有描述的帖子附件时显示警告 autocorrectNoteLanguage: 当帖子语言不符合自动检测的结果的时候显示警告 incorrectLanguageWarning: "看上去您帖子使用的语言是{detected},但您选择的语言是{current}。\n要改为以{detected}发帖吗?" +noteEditHistory: "帖子编辑历史" diff --git a/packages/backend/src/server/api/endpoints/notes/history.ts b/packages/backend/src/server/api/endpoints/notes/history.ts index e0c7be3df5..fb49b4a5b9 100644 --- a/packages/backend/src/server/api/endpoints/notes/history.ts +++ b/packages/backend/src/server/api/endpoints/notes/history.ts @@ -58,6 +58,9 @@ export default define(meta, paramDef, async (ps, user) => { }, take: ps.limit, skip: ps.offset, + order: { + id: "DESC" + }, }); return await NoteEdits.packMany(history); diff --git a/packages/client/src/components/MkDateSeparatedList.vue b/packages/client/src/components/MkDateSeparatedList.vue index e40d64dd7d..c6b75a20fc 100644 --- a/packages/client/src/components/MkDateSeparatedList.vue +++ b/packages/client/src/components/MkDateSeparatedList.vue @@ -10,7 +10,7 @@ export default defineComponent({ props: { items: { type: Array as PropType< - { id: string; createdAt: string; _shouldInsertAd_: boolean }[] + { id: string; createdAt: string; _shouldInsertAd_?: boolean }[] >, required: true, }, diff --git a/packages/client/src/components/MkNote.vue b/packages/client/src/components/MkNote.vue index e13050206b..723fabbeee 100644 --- a/packages/client/src/components/MkNote.vue +++ b/packages/client/src/components/MkNote.vue @@ -154,7 +154,12 @@ {{ appearNote.channel.name }} -