diff --git a/locales/en-US.yml b/locales/en-US.yml index 0143bc48e0..a20a0a1270 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1735,6 +1735,7 @@ _wordMute: muteWords: "Muted words" muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." muteWordsDescription2: "Surround keywords with slashes to use regular expressions." + hideMutedNotes: "Hide notes containing muted words" _instanceMute: instanceMuteDescription: "This will mute any notes/renotes from the listed instances, including those of users replying to a user from a muted instance." instanceMuteDescription2: "Separate with newlines" diff --git a/locales/index.d.ts b/locales/index.d.ts index 958bd066f7..0e5c193d20 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -644,7 +644,6 @@ export interface Locale { "smtpSecureInfo": string; "testEmail": string; "wordMute": string; - "hardWordMute": string; "regexpError": string; "regexpErrorDescription": string; "instanceMute": string; @@ -1866,6 +1865,7 @@ export interface Locale { "muteWords": string; "muteWordsDescription": string; "muteWordsDescription2": string; + "hideMutedNotes": string; }; "_instanceMute": { "instanceMuteDescription": string; diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8651bd92d7..11f3314853 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -641,7 +641,6 @@ smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する" smtpSecureInfo: "STARTTLS使用時はオフにします。" testEmail: "配信テスト" wordMute: "ワードミュート" -hardWordMute: "ハードワードミュート" regexpError: "正規表現エラー" regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが発生しました:" instanceMute: "サーバーミュート" @@ -1771,6 +1770,7 @@ _wordMute: muteWords: "ミュートするワード" muteWordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります。" muteWordsDescription2: "キーワードをスラッシュで囲むと正規表現になります。" + hideMutedNotes: "ミュートされた単語を含むノートを非表示にする" _instanceMute: instanceMuteDescription: "ミュートしたサーバーのユーザーへの返信を含めて、設定したサーバーの全てのノートとRenoteをミュートします。" diff --git a/packages/backend/migration/1700383825690-hard-mute.js b/packages/backend/migration/1700383825690-hard-mute.js index afd3247f5c..f920a61b17 100644 --- a/packages/backend/migration/1700383825690-hard-mute.js +++ b/packages/backend/migration/1700383825690-hard-mute.js @@ -1,11 +1,11 @@ export class HardMute1700383825690 { - name = 'HardMute1700383825690' + name = 'HardMute1700383825690' - async up(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" ADD "hardMutedWords" jsonb NOT NULL DEFAULT '[]'`); - } + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "hardMutedWords" jsonb NOT NULL DEFAULT '[]'`); + } - async down(queryRunner) { - await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "hardMutedWords"`); - } + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "hardMutedWords"`); + } } diff --git a/packages/backend/migration/1700880703631-revert-hard-mute.js b/packages/backend/migration/1700880703631-revert-hard-mute.js new file mode 100644 index 0000000000..4943decef1 --- /dev/null +++ b/packages/backend/migration/1700880703631-revert-hard-mute.js @@ -0,0 +1,35 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class RevertHardMute1700880703631 { + name = 'RevertHardMute1700880703631'; + + async up(queryRunner) { + // migrate hardMutedWords to mutedWords + await queryRunner.query(` + update "user_profile" + set "mutedWords" = ( + select jsonb_agg(elem order by ord) + from ( + select elem, ord + from ( + select elem, row_number() over () as ord + from jsonb_array_elements("mutedWords") as elem + ) as muted + union + select elem, 1000000 + row_number() over () + from jsonb_array_elements("hardMutedWords") as elem + where elem not in (select jsonb_array_elements("mutedWords")) + ) as combined + ) + where "hardMutedWords" <> '[]' + `); + await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "hardMutedWords"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "user_profile" ADD "hardMutedWords" jsonb NOT NULL DEFAULT '[]'`); + } +} diff --git a/packages/backend/src/core/entities/UserEntityService.ts b/packages/backend/src/core/entities/UserEntityService.ts index ba671b2a2d..aa78995f72 100644 --- a/packages/backend/src/core/entities/UserEntityService.ts +++ b/packages/backend/src/core/entities/UserEntityService.ts @@ -473,7 +473,6 @@ export class UserEntityService implements OnModuleInit { hasPendingReceivedFollowRequest: this.getHasPendingReceivedFollowRequest(user.id), unreadNotificationsCount: notificationsInfo?.unreadCount, mutedWords: profile!.mutedWords, - hardMutedWords: profile!.hardMutedWords, mutedInstances: profile!.mutedInstances, mutingNotificationTypes: [], // 後方互換性のため notificationRecieveConfig: profile!.notificationRecieveConfig, diff --git a/packages/backend/src/models/UserProfile.ts b/packages/backend/src/models/UserProfile.ts index 8a43b60039..96763bae72 100644 --- a/packages/backend/src/models/UserProfile.ts +++ b/packages/backend/src/models/UserProfile.ts @@ -217,11 +217,6 @@ export class MiUserProfile { }) public mutedWords: (string[] | string)[]; - @Column('jsonb', { - default: [], - }) - public hardMutedWords: (string[] | string)[]; - @Column('jsonb', { default: [], comment: 'List of instances muted by the user.', diff --git a/packages/backend/src/models/json-schema/user.ts b/packages/backend/src/models/json-schema/user.ts index f267bdf590..c63fac54bf 100644 --- a/packages/backend/src/models/json-schema/user.ts +++ b/packages/backend/src/models/json-schema/user.ts @@ -534,18 +534,6 @@ export const packedMeDetailedOnlySchema = { }, }, }, - hardMutedWords: { - type: 'array', - nullable: false, optional: false, - items: { - type: 'array', - nullable: false, optional: false, - items: { - type: 'string', - nullable: false, optional: false, - }, - }, - }, mutedInstances: { type: 'array', nullable: true, optional: false, diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts index 773a47371d..dcca6d205a 100644 --- a/packages/backend/src/server/api/endpoints/i/update.ts +++ b/packages/backend/src/server/api/endpoints/i/update.ts @@ -15,7 +15,6 @@ import type { UsersRepository, DriveFilesRepository, UserProfilesRepository, Pag import type { MiLocalUser, MiUser } from '@/models/User.js'; import { birthdaySchema, descriptionSchema, locationSchema, nameSchema } from '@/models/User.js'; import type { MiUserProfile } from '@/models/UserProfile.js'; -import { notificationTypes } from '@/types.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { langmap } from '@/misc/langmap.js'; import { Endpoint } from '@/server/api/endpoint-base.js'; @@ -124,11 +123,6 @@ export const meta = { }, } as const; -const muteWordsType = { type: 'array', items: { oneOf: [ - { type: 'array', items: { type: 'string' } }, - { type: 'string' } -] } } as const; - export const paramDef = { type: 'object', properties: { @@ -177,8 +171,12 @@ export const paramDef = { autoSensitive: { type: 'boolean' }, ffVisibility: { type: 'string', enum: ['public', 'followers', 'private'] }, pinnedPageId: { type: 'string', format: 'misskey:id', nullable: true }, - mutedWords: muteWordsType, - hardMutedWords: muteWordsType, + mutedWords: { type: 'array', items: { + oneOf: [ + { type: 'array', items: { type: 'string' } }, + { type: 'string' } + ] + } }, mutedInstances: { type: 'array', items: { type: 'string', } }, @@ -241,19 +239,15 @@ export default class extends Endpoint { // eslint- if (ps.location !== undefined) profileUpdates.location = ps.location; if (ps.birthday !== undefined) profileUpdates.birthday = ps.birthday; if (ps.ffVisibility !== undefined) profileUpdates.ffVisibility = ps.ffVisibility; - - function checkMuteWordCount(mutedWords: (string[] | string)[], limit: number) { - const length = mutedWords.length; - if (length > limit) { + if (ps.mutedWords !== undefined) { + const length = ps.mutedWords.length; + if (length > (await this.roleService.getUserPolicies(user.id)).wordMuteLimit) { throw new ApiError(meta.errors.tooManyMutedWords); } - } - function validateMuteWordRegex(mutedWords: (string[] | string)[]) { - for (const mutedWord of mutedWords) { - if (typeof mutedWord !== "string") continue; - - const regexp = mutedWord.match(/^\/(.+)\/(.*)$/); + // validate regular expression syntax + ps.mutedWords.filter(x => !Array.isArray(x)).forEach(x => { + const regexp = RegExp(/^\/(.+)\/(.*)$/).exec(x as string); if (!regexp) throw new ApiError(meta.errors.invalidRegexp); try { @@ -261,21 +255,11 @@ export default class extends Endpoint { // eslint- } catch (err) { throw new ApiError(meta.errors.invalidRegexp); } - } - } - - if (ps.mutedWords !== undefined) { - checkMuteWordCount(ps.mutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit); - validateMuteWordRegex(ps.mutedWords); + }); profileUpdates.mutedWords = ps.mutedWords; profileUpdates.enableWordMute = ps.mutedWords.length > 0; } - if (ps.hardMutedWords !== undefined) { - checkMuteWordCount(ps.hardMutedWords, (await this.roleService.getUserPolicies(user.id)).wordMuteLimit); - validateMuteWordRegex(ps.hardMutedWords); - profileUpdates.hardMutedWords = ps.hardMutedWords; - } if (ps.mutedInstances !== undefined) profileUpdates.mutedInstances = ps.mutedInstances; if (ps.notificationRecieveConfig !== undefined) profileUpdates.notificationRecieveConfig = ps.notificationRecieveConfig; if (typeof ps.isLocked === 'boolean') updates.isLocked = ps.isLocked; diff --git a/packages/backend/test/e2e/users.ts b/packages/backend/test/e2e/users.ts index d80b0c0704..7924b3382f 100644 --- a/packages/backend/test/e2e/users.ts +++ b/packages/backend/test/e2e/users.ts @@ -169,7 +169,6 @@ describe('ユーザー', () => { hasPendingReceivedFollowRequest: user.hasPendingReceivedFollowRequest, unreadAnnouncements: user.unreadAnnouncements, mutedWords: user.mutedWords, - hardMutedWords: user.hardMutedWords, mutedInstances: user.mutedInstances, mutingNotificationTypes: user.mutingNotificationTypes, notificationRecieveConfig: user.notificationRecieveConfig, diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 6349df2e30..6cb9dd7e46 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only