diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 11fc0a78dac6..6785f9485065 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -51,12 +51,12 @@ deleteAndEdit: "削除して編集" deleteAndEditConfirm: "このノートを削除してもう一度編集しますか?このノートへのリアクション、リノート、返信も全て削除されます。" limitToHome: "公開範囲をホームに制限" limitToHomeConfirm: "このノートの公開範囲を「ホーム」に変更しますか?この操作は取り消せません。" -limitToFollowers: "公開範囲をホームに制限" +limitToFollowers: "公開範囲を「フォロワー」に制限" limitToFollowersConfirm: "このノートの公開範囲を「フォロワー」に変更しますか?この操作は取り消せません。" limitToLocalOnly: "公開範囲を「ローカルのみ」に制限" limitToLocalOnlyConfirm: "このノートの公開範囲を「ローカルのみ」に変更しますか?連合先サーバーへはノートが削除されたと通知します。この操作は取り消せません。" -limitToLikeOnly: "公開範囲を「ローカルのみ」に制限" -limitToLikeOnlyConfirm: "このノートが受け付けるリアクションをハートのみに制限しますか?この操作は取り消せません。" +limitToLikeOnly: "リアクションの受け入れを「いいね」のみに制限" +limitToLikeOnlyConfirm: "このノートが受け付けるリアクションを「いいね」のみに制限しますか?この操作は取り消せません。" addToList: "リストに追加" addToAntenna: "アンテナに追加" sendMessage: "メッセージを送信" diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index c31cef36e839..0a0550ff4b60 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -34,6 +34,7 @@ import { MfmService } from './MfmService.js'; import { ModerationLogService } from './ModerationLogService.js'; import { NoteCreateService } from './NoteCreateService.js'; import { NoteDeleteService } from './NoteDeleteService.js'; +import { NoteUpdateVisibilityService } from './NoteUpdateVisibilityService.js'; import { NotePiningService } from './NotePiningService.js'; import { NoteReadService } from './NoteReadService.js'; import { NotificationService } from './NotificationService.js'; @@ -171,6 +172,7 @@ const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService } const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService }; const $NoteCreateService: Provider = { provide: 'NoteCreateService', useExisting: NoteCreateService }; const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useExisting: NoteDeleteService }; +const $NoteUpdateVisibilityService: Provider = { provide: 'NoteUpdateVisibilityService', useExisting: NoteUpdateVisibilityService }; const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting: NotePiningService }; const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService }; const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService }; @@ -310,6 +312,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ModerationLogService, NoteCreateService, NoteDeleteService, + NoteUpdateVisibilityService, NotePiningService, NoteReadService, NotificationService, @@ -445,6 +448,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ModerationLogService, $NoteCreateService, $NoteDeleteService, + $NoteUpdateVisibilityService, $NotePiningService, $NoteReadService, $NotificationService, @@ -581,6 +585,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting ModerationLogService, NoteCreateService, NoteDeleteService, + NoteUpdateVisibilityService, NotePiningService, NoteReadService, NotificationService, @@ -715,6 +720,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $ModerationLogService, $NoteCreateService, $NoteDeleteService, + $NoteUpdateVisibilityService, $NotePiningService, $NoteReadService, $NotificationService, diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 01dd133eadc4..dc238f33aea9 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -115,6 +115,9 @@ export interface NoteEventTypes { deleted: { deletedAt: Date; }; + visibilityUpdated: { + updatedAt: Date; + }; updated: { cw: string | null; text: string; diff --git a/packages/backend/src/core/NoteUpdateVisibilityService.ts b/packages/backend/src/core/NoteUpdateVisibilityService.ts index 51669b096621..e6d13141b66c 100644 --- a/packages/backend/src/core/NoteUpdateVisibilityService.ts +++ b/packages/backend/src/core/NoteUpdateVisibilityService.ts @@ -51,9 +51,9 @@ export class NoteUpdateVisibilityService { async updateVisibility(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, visibility?: string, localOnly?: boolean, reactionAcceptance?: MiNote['reactionAcceptance'], quiet = false, updater?: MiUser) { if (note.visibility === 'home' && visibility === 'public') { throw new Error('cannot change home visibility to public'); - } else if (note.visibility === 'followers' && !visibility === 'specified') { + } else if (note.visibility === 'followers' && (visibility === 'home' || visibility === 'public')) { throw new Error('cannot change followers visibility to home or public'); - } else if (note.visibility === 'specified') { + } else if (note.visibility === 'specified' && visibility) { throw new Error('cannot change specified visibility'); } @@ -88,11 +88,13 @@ export class NoteUpdateVisibilityService { this.searchService.unindexNote(note); } - await this.notesRepository.update(note.id, { - visibility?, - localOnly?, - reactionAcceptance? - }); + const to = { + visibility: visibility ?? undefined, + localOnly: localOnly ?? undefined, + reactionAcceptance: reactionAcceptance ?? undefined, + }; + + await this.notesRepository.update(note.id, to); if (updater && (note.userId !== updater.id)) { const user = await this.usersRepository.findOneByOrFail({ id: note.userId }); @@ -102,7 +104,7 @@ export class NoteUpdateVisibilityService { noteUserUsername: user.username, noteUserHost: user.host, note: note, - to: { visibility, locaOnly, reactionAcceptance } + to }); } } diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 8a003725cd5b..fbb4c74a75a9 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -268,6 +268,7 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; 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_updateVisibility from './endpoints/notes/update-visibility.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'; @@ -639,6 +640,7 @@ const $notes_clips: Provider = { provide: 'ep:notes/clips', useClass: ep___notes const $notes_conversation: Provider = { provide: 'ep:notes/conversation', useClass: ep___notes_conversation.default }; const $notes_create: Provider = { provide: 'ep:notes/create', useClass: ep___notes_create.default }; const $notes_delete: Provider = { provide: 'ep:notes/delete', useClass: ep___notes_delete.default }; +const $notes_updateVisibility: Provider = { provide: 'ep:notes/update-visibility', useClass: ep___notes_updateVisibility.default }; const $notes_favorites_create: Provider = { provide: 'ep:notes/favorites/create', useClass: ep___notes_favorites_create.default }; const $notes_favorites_delete: Provider = { provide: 'ep:notes/favorites/delete', useClass: ep___notes_favorites_delete.default }; const $notes_featured: Provider = { provide: 'ep:notes/featured', useClass: ep___notes_featured.default }; @@ -1014,6 +1016,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $notes_conversation, $notes_create, $notes_delete, + $notes_updateVisibility, $notes_favorites_create, $notes_favorites_delete, $notes_featured, @@ -1383,6 +1386,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__ $notes_conversation, $notes_create, $notes_delete, + $notes_updateVisibility, $notes_favorites_create, $notes_favorites_delete, $notes_featured, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e1c8be727e47..330e0bd94ac7 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -268,6 +268,7 @@ import * as ep___notes_clips from './endpoints/notes/clips.js'; 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_updateVisibility from './endpoints/notes/update-visibility.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'; @@ -637,6 +638,7 @@ const eps = [ ['notes/conversation', ep___notes_conversation], ['notes/create', ep___notes_create], ['notes/delete', ep___notes_delete], + ['notes/update-visibility', ep___notes_updateVisibility], ['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/update-visibility.ts b/packages/backend/src/server/api/endpoints/notes/update-visibility.ts index a800016c4c9b..0bf9d0b6cca9 100644 --- a/packages/backend/src/server/api/endpoints/notes/update-visibility.ts +++ b/packages/backend/src/server/api/endpoints/notes/update-visibility.ts @@ -45,7 +45,7 @@ export const paramDef = { type: 'object', properties: { noteId: { type: 'string', format: 'misskey:id' }, - visibility: { type: 'string', nullable: true, enum: ['public', 'home', 'followers', 'specified'] }, + visibility: { type: 'string', nullable: true, enum: [null, 'public', 'home', 'followers', 'specified'] }, localOnly: { type: 'boolean', nullable: true }, reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null }, }, @@ -73,7 +73,7 @@ export default class extends Endpoint { // eslint- } // この操作を行うのが投稿者とは限らない(例えばモデレーター)ため - await this.noteUpdateVisibilityService.updateVisibility(await this.usersRepository.findOneByOrFail({ id: note.userId }), note, false, me); + await this.noteUpdateVisibilityService.updateVisibility(await this.usersRepository.findOneByOrFail({ id: note.userId }), note, ps.visibility, ps.localOnly, ps.reactionAcceptance, false, me); }); } } diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index 578e22cdf5a8..cead4f5a556b 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -196,7 +196,7 @@ export function getNoteMenu(props: { function limitToFollowers(): void { os.confirm({ type: 'warning', - text: i18n.ts.limitToHomeConfirm, + text: i18n.ts.limitToFollowersConfirm, }).then(({ canceled }) => { if (canceled) return; @@ -463,30 +463,30 @@ export function getNoteMenu(props: { action: del, }, { type: 'divider' }, - { + appearNote.visibility === 'public' ? { icon: 'ti ti-edit', text: i18n.ts.limitToHome, danger: true, action: limitToHome, - }, - { + } : undefined, + appearNote.visibility === 'home' ? { icon: 'ti ti-edit', text: i18n.ts.limitToFollowers, danger: true, action: limitToFollowers, - }, - { + } : undefined, + !appearNote.localOnly ? { icon: 'ti ti-edit', text: i18n.ts.limitToLocalOnly, danger: true, action: limitToLocalOnly, - }, - { + } : undefined, + appearNote.reactionAcceptance !== 'likeOnly' ? { icon: 'ti ti-edit', text: i18n.ts.limitToLikeOnly, danger: true, action: limitToLikeOnly, - }, + } : undefined, ] : [] )] diff --git a/packages/misskey-js/src/streaming.types.ts b/packages/misskey-js/src/streaming.types.ts index 0ba5715d689a..d5956db5308c 100644 --- a/packages/misskey-js/src/streaming.types.ts +++ b/packages/misskey-js/src/streaming.types.ts @@ -239,6 +239,11 @@ export type NoteUpdatedEvent = { reaction: string; userId: User['id']; }; +} | { + type: 'visibilityUpdated'; + body: { + updatedAt: string; + }; } | { type: 'deleted'; body: {