From c106db89e1d54c20c6466e42dde540e0d5c5c4eb Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 17:21:16 +0900 Subject: feat: note edit --- .../src/server/api/endpoints/notes/update.ts | 88 ++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 packages/backend/src/server/api/endpoints/notes/update.ts (limited to 'packages/backend/src/server/api/endpoints/notes/update.ts') diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts new file mode 100644 index 0000000000..ccd2878d3c --- /dev/null +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -0,0 +1,88 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import ms from 'ms'; +import { Inject, Injectable } from '@nestjs/common'; +import type { UsersRepository, NotesRepository } from '@/models/_.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { NoteDeleteService } from '@/core/NoteDeleteService.js'; +import { DI } from '@/di-symbols.js'; +import { GetterService } from '@/server/api/GetterService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { MAX_NOTE_TEXT_LENGTH } from '@/const.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + tags: ['notes'], + + requireCredential: true, + requireRolePolicy: 'canEditNote', + + kind: 'write:notes', + + limit: { + duration: ms('1hour'), + max: 10, + minInterval: ms('1sec'), + }, + + errors: { + noSuchNote: { + message: 'No such note.', + code: 'NO_SUCH_NOTE', + id: 'a6584e14-6e01-4ad3-b566-851e7bf0d474', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + noteId: { type: 'string', format: 'misskey:id' }, + text: { + type: 'string', + minLength: 1, + maxLength: MAX_NOTE_TEXT_LENGTH, + nullable: false, + }, + cw: { type: 'string', nullable: true, maxLength: 100 }, + }, + required: ['noteId', 'text', 'cw'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.usersRepository) + private usersRepository: UsersRepository, + + @Inject(DI.notesRepository) + private notesRepository: NotesRepository, + + private getterService: GetterService, + private globalEventService: GlobalEventService, + ) { + super(meta, paramDef, async (ps, me) => { + const note = await this.getterService.getNote(ps.noteId).catch(err => { + if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); + throw err; + }); + + if (note.userId !== me.id) { + throw new ApiError(meta.errors.noSuchNote); + } + + await this.notesRepository.update({ id: note.id }, { + cw: ps.cw, + text: ps.text, + }); + + this.globalEventService.publishNoteStream(note.id, 'updated', { + cw: ps.cw, + text: ps.text, + }); + }); + } +} -- cgit v1.2.3-freya From 2438c047a71b7b46d3cc7304a1c78a27a6789f02 Mon Sep 17 00:00:00 2001 From: syuilo Date: Thu, 28 Sep 2023 21:06:14 +0900 Subject: enhance: 編集されたノートかどうか分かるように MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/index.d.ts | 1 + locales/ja-JP.yml | 1 + packages/backend/migration/1695901659683-note-updated-at.js | 11 +++++++++++ packages/backend/src/core/entities/NoteEntityService.ts | 1 + packages/backend/src/models/Note.ts | 5 +++++ packages/backend/src/models/json-schema/note.ts | 7 ++++++- packages/backend/src/server/api/endpoints/notes/update.ts | 1 + packages/frontend/src/components/MkNoteDetailed.vue | 3 +++ packages/frontend/src/components/MkNoteHeader.vue | 1 + packages/frontend/src/scripts/use-note-capture.ts | 1 + packages/misskey-js/etc/misskey-js.api.md | 3 ++- packages/misskey-js/src/entities.ts | 1 + 12 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 packages/backend/migration/1695901659683-note-updated-at.js (limited to 'packages/backend/src/server/api/endpoints/notes/update.ts') diff --git a/locales/index.d.ts b/locales/index.d.ts index 8c6b724623..75f867e691 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1125,6 +1125,7 @@ export interface Locale { "authenticationRequiredToContinue": string; "dateAndTime": string; "showRenotes": string; + "edited": string; "_announcement": { "forExistingUsers": string; "forExistingUsersDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index c31b4a5c27..cfa9143dde 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1122,6 +1122,7 @@ authentication: "認証" authenticationRequiredToContinue: "続けるには認証を行ってください" dateAndTime: "日時" showRenotes: "リノートを表示" +edited: "編集済み" _announcement: forExistingUsers: "既存ユーザーのみ" diff --git a/packages/backend/migration/1695901659683-note-updated-at.js b/packages/backend/migration/1695901659683-note-updated-at.js new file mode 100644 index 0000000000..d8a151a1f7 --- /dev/null +++ b/packages/backend/migration/1695901659683-note-updated-at.js @@ -0,0 +1,11 @@ +export class NoteUpdatedAt1695901659683 { + name = 'NoteUpdatedAt1695901659683' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "updatedAt" TIMESTAMP WITH TIME ZONE`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "updatedAt"`); + } +} diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index bf42e98ce0..a024286b48 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -308,6 +308,7 @@ export class NoteEntityService implements OnModuleInit { const packed: Packed<'Note'> = await awaitAll({ id: note.id, createdAt: note.createdAt.toISOString(), + updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined, userId: note.userId, user: this.userEntityService.pack(note.user ?? note.userId, me, { detail: false, diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index ed86d4549e..f396a0cd7a 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -24,6 +24,11 @@ export class MiNote { }) public createdAt: Date; + @Column('timestamp with time zone', { + default: null, + }) + public updatedAt: Date | null; + @Index() @Column({ ...id(), diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts index eb744aa109..ad0cb3c45d 100644 --- a/packages/backend/src/models/json-schema/note.ts +++ b/packages/backend/src/models/json-schema/note.ts @@ -17,6 +17,11 @@ export const packedNoteSchema = { optional: false, nullable: false, format: 'date-time', }, + updatedAt: { + type: 'string', + optional: true, nullable: true, + format: 'date-time', + }, deletedAt: { type: 'string', optional: true, nullable: true, @@ -142,7 +147,7 @@ export const packedNoteSchema = { isSensitive: { type: 'boolean', optional: true, nullable: false, - } + }, }, }, }, diff --git a/packages/backend/src/server/api/endpoints/notes/update.ts b/packages/backend/src/server/api/endpoints/notes/update.ts index ccd2878d3c..cdf7f085e0 100644 --- a/packages/backend/src/server/api/endpoints/notes/update.ts +++ b/packages/backend/src/server/api/endpoints/notes/update.ts @@ -75,6 +75,7 @@ export default class extends Endpoint { // eslint- } await this.notesRepository.update({ id: note.id }, { + updatedAt: new Date(), cw: ps.cw, text: ps.text, }); diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 06663d0477..ab8886e8ba 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -93,6 +93,9 @@ SPDX-License-Identifier: AGPL-3.0-only