From dd6569a1bb025f2e295c9d19d870febcde712ea1 Mon Sep 17 00:00:00 2001 From: syuilo Date: Wed, 8 Mar 2023 08:56:47 +0900 Subject: [PATCH] feat: Reaction acceptance (#10256) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * wip * wip * デフォルト設定 --- CHANGELOG.md | 3 +- locales/ja-JP.yml | 3 ++ ...8164627293-per-note-reaction-acceptance.js | 11 +++++++ .../backend/src/core/NoteCreateService.ts | 2 ++ packages/backend/src/core/ReactionService.ts | 8 +++-- .../src/core/entities/NoteEntityService.ts | 1 + packages/backend/src/misc/schema.ts | 4 +-- packages/backend/src/models/entities/Note.ts | 5 +++ packages/backend/src/models/schema/note.ts | 4 +++ .../ExportFavoritesProcessorService.ts | 1 + .../processors/ExportNotesProcessorService.ts | 3 +- .../src/server/api/endpoints/notes/create.ts | 4 ++- packages/frontend/src/components/MkNote.vue | 33 ++++++++++++++----- .../src/components/MkNoteDetailed.vue | 33 ++++++++++++++----- .../frontend/src/components/MkPostForm.vue | 27 ++++++++++++++- .../frontend/src/pages/settings/profile.vue | 12 ++++++- packages/frontend/src/store.ts | 4 +++ 17 files changed, 131 insertions(+), 27 deletions(-) create mode 100644 packages/backend/migration/1678164627293-per-note-reaction-acceptance.js diff --git a/CHANGELOG.md b/CHANGELOG.md index f2dbc2bb1f..923e8680f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,11 @@ You should also include the user name that made the change. ### Improvements - ユーザーごとにRenoteをミュートできるように +- ノートごとに絵文字リアクションを受け取るか設定できるように - enhance(client): DM作成時にメンションも含むように ### Bugfixes -- +- ## 13.9.2 (2023/03/06) diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 47e2af713b..750da3e98a 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -961,6 +961,9 @@ invitationRequiredToRegister: "現在このサーバーは招待制です。招 emailNotSupported: "このサーバーではメール配信はサポートされていません" postToTheChannel: "チャンネルに投稿" cannotBeChangedLater: "後から変更できません。" +reactionAcceptance: "リアクションの受け入れ" +likeOnly: "いいねのみ" +likeOnlyForRemote: "リモートからはいいねのみ" _achievements: earnedAt: "獲得日時" diff --git a/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js b/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js new file mode 100644 index 0000000000..f1765dd146 --- /dev/null +++ b/packages/backend/migration/1678164627293-per-note-reaction-acceptance.js @@ -0,0 +1,11 @@ +export class perNoteReactionAcceptance1678164627293 { + name = 'perNoteReactionAcceptance1678164627293' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" ADD "reactionAcceptance" character varying(64)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "note" DROP COLUMN "reactionAcceptance"`); + } +} diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 4c4261ba79..8d8535ca5b 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -125,6 +125,7 @@ type Option = { files?: DriveFile[] | null; poll?: IPoll | null; localOnly?: boolean | null; + reactionAcceptance?: Note['reactionAcceptance']; cw?: string | null; visibility?: string; visibleUsers?: MinimumUser[] | null; @@ -346,6 +347,7 @@ export class NoteCreateService implements OnApplicationShutdown { emojis, userId: user.id, localOnly: data.localOnly!, + reactionAcceptance: data.reactionAcceptance, visibility: data.visibility as any, visibleUserIds: data.visibility === 'specified' ? data.visibleUsers diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts index 9fccc14ee4..3e644018d7 100644 --- a/packages/backend/src/core/ReactionService.ts +++ b/packages/backend/src/core/ReactionService.ts @@ -99,8 +99,12 @@ export class ReactionService { throw new IdentifiableError('68e9d2d1-48bf-42c2-b90a-b20e09fd3d48', 'Note not accessible for you.'); } - // TODO: cache - reaction = await this.toDbReaction(reaction, user.host); + if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote') && (user.host != null))) { + reaction = '❤️'; + } else { + // TODO: cache + reaction = await this.toDbReaction(reaction, user.host); + } const record: NoteReaction = { id: this.idService.genId(), diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts index 4ec10df9a6..67850ad9aa 100644 --- a/packages/backend/src/core/entities/NoteEntityService.ts +++ b/packages/backend/src/core/entities/NoteEntityService.ts @@ -314,6 +314,7 @@ export class NoteEntityService implements OnModuleInit { cw: note.cw, visibility: note.visibility, localOnly: note.localOnly ?? undefined, + reactionAcceptance: note.reactionAcceptance, visibleUserIds: note.visibility === 'specified' ? note.visibleUserIds : undefined, renoteCount: note.renoteCount, repliesCount: note.repliesCount, diff --git a/packages/backend/src/misc/schema.ts b/packages/backend/src/misc/schema.ts index 0681cdb67e..9b8af6958f 100644 --- a/packages/backend/src/misc/schema.ts +++ b/packages/backend/src/misc/schema.ts @@ -95,7 +95,7 @@ export interface Schema extends OfSchema { readonly example?: any; readonly format?: string; readonly ref?: keyof typeof refs; - readonly enum?: ReadonlyArray; + readonly enum?: ReadonlyArray; readonly default?: (this['type'] extends TypeStringef ? StringDefToType : any) | null; readonly maxLength?: number; readonly minLength?: number; @@ -161,7 +161,7 @@ export type SchemaTypeDef

= p['type'] extends 'integer' ? number : p['type'] extends 'number' ? number : p['type'] extends 'string' ? ( - p['enum'] extends readonly string[] ? + p['enum'] extends readonly (string | null)[] ? p['enum'][number] : p['format'] extends 'date-time' ? string : // Dateにする?? string diff --git a/packages/backend/src/models/entities/Note.ts b/packages/backend/src/models/entities/Note.ts index 82d042f0ce..df508b4dca 100644 --- a/packages/backend/src/models/entities/Note.ts +++ b/packages/backend/src/models/entities/Note.ts @@ -87,6 +87,11 @@ export class Note { }) public localOnly: boolean; + @Column('varchar', { + length: 64, nullable: true, + }) + public reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | null; + @Column('smallint', { default: 0, }) diff --git a/packages/backend/src/models/schema/note.ts b/packages/backend/src/models/schema/note.ts index 72c0c62285..58ef425dcd 100644 --- a/packages/backend/src/models/schema/note.ts +++ b/packages/backend/src/models/schema/note.ts @@ -141,6 +141,10 @@ export const packedNoteSchema = { type: 'boolean', optional: true, nullable: false, }, + reactionAcceptance: { + type: 'string', + optional: false, nullable: true, + }, reactions: { type: 'object', optional: false, nullable: false, diff --git a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts index c65f0a97a0..e9330772b9 100644 --- a/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportFavoritesProcessorService.ts @@ -148,6 +148,7 @@ function serialize(favorite: NoteFavorite & { note: Note & { user: User } }, pol visibility: favorite.note.visibility, visibleUserIds: favorite.note.visibleUserIds, localOnly: favorite.note.localOnly, + reactionAcceptance: favorite.note.reactionAcceptance, uri: favorite.note.uri, url: favorite.note.url, user: { diff --git a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts index 3f4f16a2ec..2f74dd63cc 100644 --- a/packages/backend/src/queue/processors/ExportNotesProcessorService.ts +++ b/packages/backend/src/queue/processors/ExportNotesProcessorService.ts @@ -10,10 +10,10 @@ import { DriveService } from '@/core/DriveService.js'; import { createTemp } from '@/misc/create-temp.js'; import type { Poll } from '@/models/entities/Poll.js'; import type { Note } from '@/models/entities/Note.js'; +import { bindThis } from '@/decorators.js'; import { QueueLoggerService } from '../QueueLoggerService.js'; import type Bull from 'bull'; import type { DbUserJobData } from '../types.js'; -import { bindThis } from '@/decorators.js'; @Injectable() export class ExportNotesProcessorService { @@ -141,5 +141,6 @@ function serialize(note: Note, poll: Poll | null = null): Record { renote, cw: ps.cw, localOnly: ps.localOnly, + reactionAcceptance: ps.reactionAcceptance, visibility: ps.visibility, visibleUsers, channel, diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index bb1269562d..af81051a54 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -103,7 +103,8 @@

- +