From 3804c6e7ad8c08f87e99d7e68a7d5c0e8af68dfa Mon Sep 17 00:00:00 2001
From: syuilo <Syuilotan@yahoo.co.jp>
Date: Fri, 19 May 2023 09:43:38 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=E3=82=BB=E3=83=B3=E3=82=B7=E3=83=86?=
 =?UTF-8?q?=E3=82=A3=E3=83=96=E3=81=AA=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?=
 =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=83=AA=E3=82=A2=E3=82=AF?=
 =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E5=8F=97=E3=81=91=E5=85=A5?=
 =?UTF-8?q?=E3=82=8C=E3=81=AA=E3=81=84=E8=A8=AD=E5=AE=9A=E3=82=92=E8=BF=BD?=
 =?UTF-8?q?=E5=8A=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 CHANGELOG.md                                     |  2 ++
 locales/ja-JP.yml                                |  4 +++-
 packages/backend/src/core/ReactionService.ts     |  7 ++++++-
 packages/backend/src/models/entities/Note.ts     |  2 +-
 .../src/server/api/endpoints/notes/create.ts     |  2 +-
 packages/frontend/src/components/MkPostForm.vue  |  6 ++++--
 packages/frontend/src/pages/settings/profile.vue | 16 +++++++++-------
 packages/frontend/src/store.ts                   |  2 +-
 8 files changed, 27 insertions(+), 14 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index d9ce5fbe6c..03b1bb4e29 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,8 @@
 ### General
 - カスタム絵文字ごとにそれをリアクションとして使えるロールを設定できるように
 - カスタム絵文字ごとに連合するかどうか設定できるように
+- カスタム絵文字ごとにセンシティブフラグを設定できるように
+- センシティブなカスタム絵文字のリアクションを受け入れない設定が可能に
 - タイムラインにフォロイーの行った他人へのリプライを含めるかどうかの設定をアカウントに保存するのをやめるように
 	- 今後はAPI呼び出し時およびストリーミング接続時に設定するようになります
 
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 4067155a33..374eeba390 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -990,7 +990,9 @@ postToTheChannel: "チャンネルに投稿"
 cannotBeChangedLater: "後から変更できません。"
 reactionAcceptance: "リアクションの受け入れ"
 likeOnly: "いいねのみ"
-likeOnlyForRemote: "リモートからはいいねのみ"
+likeOnlyForRemote: "全て (リモートはいいねのみ)"
+nonSensitiveOnly: "非センシティブのみ"
+nonSensitiveOnlyForLocalLikeOnlyForRemote: "非センシティブのみ (リモートはいいねのみ)"
 rolesAssignedToMe: "自分に割り当てられたロール"
 resetPasswordConfirm: "パスワードリセットしますか?"
 sensitiveWords: "センシティブワード"
diff --git a/packages/backend/src/core/ReactionService.ts b/packages/backend/src/core/ReactionService.ts
index 27334b33e6..4b01b6af7e 100644
--- a/packages/backend/src/core/ReactionService.ts
+++ b/packages/backend/src/core/ReactionService.ts
@@ -106,7 +106,7 @@ export class ReactionService {
 
 		let reaction = _reaction ?? FALLBACK;
 
-		if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote') && (user.host != null))) {
+		if (note.reactionAcceptance === 'likeOnly' || ((note.reactionAcceptance === 'likeOnlyForRemote' || note.reactionAcceptance === 'nonSensitiveOnlyForLocalLikeOnlyForRemote') && (user.host != null))) {
 			reaction = '❤️';
 		} else if (_reaction) {
 			const custom = reaction.match(isCustomEmojiRegexp);
@@ -124,6 +124,11 @@ export class ReactionService {
 				if (emoji) {
 					if (emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.length === 0 || (await this.roleService.getUserRoles(user.id)).some(r => emoji.roleIdsThatCanBeUsedThisEmojiAsReaction.includes(r.id))) {
 						reaction = reacterHost ? `:${name}@${reacterHost}:` : `:${name}:`;
+
+						// センシティブ
+						if ((note.reactionAcceptance === 'nonSensitiveOnly') && emoji.isSensitive) {
+							reaction = FALLBACK;
+						}
 					} else {
 						// リアクションとして使う権限がない
 						reaction = FALLBACK;
diff --git a/packages/backend/src/models/entities/Note.ts b/packages/backend/src/models/entities/Note.ts
index df508b4dca..4f49a05950 100644
--- a/packages/backend/src/models/entities/Note.ts
+++ b/packages/backend/src/models/entities/Note.ts
@@ -90,7 +90,7 @@ export class Note {
 	@Column('varchar', {
 		length: 64, nullable: true,
 	})
-	public reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | null;
+	public reactionAcceptance: 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null;
 
 	@Column('smallint', {
 		default: 0,
diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts
index 3f7f2cdece..96be5ed844 100644
--- a/packages/backend/src/server/api/endpoints/notes/create.ts
+++ b/packages/backend/src/server/api/endpoints/notes/create.ts
@@ -99,7 +99,7 @@ export const paramDef = {
 		} },
 		cw: { type: 'string', nullable: true, maxLength: 100 },
 		localOnly: { type: 'boolean', default: false },
-		reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote'], default: null },
+		reactionAcceptance: { type: 'string', nullable: true, enum: [null, 'likeOnly', 'likeOnlyForRemote', 'nonSensitiveOnly', 'nonSensitiveOnlyForLocalLikeOnlyForRemote'], default: null },
 		noExtractMentions: { type: 'boolean', default: false },
 		noExtractHashtags: { type: 'boolean', default: false },
 		noExtractEmojis: { type: 'boolean', default: false },
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index c65cb7d6e5..c43d353d9c 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -31,7 +31,7 @@
 				<span v-if="!localOnly"><i class="ti ti-rocket"></i></span>
 				<span v-else><i class="ti ti-rocket-off"></i></span>
 			</button>
-			<button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" :class="['_button', $style.headerRightItem, $style.reactionAcceptance, { [$style.danger]: reactionAcceptance }]" @click="toggleReactionAcceptance">
+			<button v-click-anime v-tooltip="i18n.ts.reactionAcceptance" :class="['_button', $style.headerRightItem, $style.reactionAcceptance, { [$style.danger]: reactionAcceptance === 'likeOnly' }]" @click="toggleReactionAcceptance">
 				<span v-if="reactionAcceptance === 'likeOnly'"><i class="ti ti-heart"></i></span>
 				<span v-else-if="reactionAcceptance === 'likeOnlyForRemote'"><i class="ti ti-heart-plus"></i></span>
 				<span v-else><i class="ti ti-icons"></i></span>
@@ -484,8 +484,10 @@ async function toggleReactionAcceptance() {
 		title: i18n.ts.reactionAcceptance,
 		items: [
 			{ value: null, text: i18n.ts.all },
-			{ value: 'likeOnly' as const, text: i18n.ts.likeOnly },
 			{ value: 'likeOnlyForRemote' as const, text: i18n.ts.likeOnlyForRemote },
+			{ value: 'nonSensitiveOnly' as const, text: i18n.ts.nonSensitiveOnly },
+			{ value: 'nonSensitiveOnlyForLocalLikeOnlyForRemote' as const, text: i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote },
+			{ value: 'likeOnly' as const, text: i18n.ts.likeOnly },
 		],
 		default: reactionAcceptance,
 	});
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 35af43d789..11e891b784 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -8,21 +8,21 @@
 		<MkButton primary rounded :class="$style.bannerEdit" @click="changeBanner">{{ i18n.ts._profile.changeBanner }}</MkButton>
 	</div>
 
-	<MkInput v-model="profile.name" :max="30" manual-save>
+	<MkInput v-model="profile.name" :max="30" manualSave>
 		<template #label>{{ i18n.ts._profile.name }}</template>
 	</MkInput>
 
-	<MkTextarea v-model="profile.description" :max="500" tall manual-save>
+	<MkTextarea v-model="profile.description" :max="500" tall manualSave>
 		<template #label>{{ i18n.ts._profile.description }}</template>
 		<template #caption>{{ i18n.ts._profile.youCanIncludeHashtags }}</template>
 	</MkTextarea>
 
-	<MkInput v-model="profile.location" manual-save>
+	<MkInput v-model="profile.location" manualSave>
 		<template #label>{{ i18n.ts.location }}</template>
 		<template #prefix><i class="ti ti-map-pin"></i></template>
 	</MkInput>
 
-	<MkInput v-model="profile.birthday" type="date" manual-save>
+	<MkInput v-model="profile.birthday" type="date" manualSave>
 		<template #label>{{ i18n.ts.birthday }}</template>
 		<template #prefix><i class="ti ti-cake"></i></template>
 	</MkInput>
@@ -48,7 +48,7 @@
 				<Sortable
 					v-model="fields"
 					class="_gaps_s"
-					item-key="id"
+					itemKey="id"
 					:animation="150"
 					:handle="'.' + $style.dragItemHandle"
 					@start="e => e.item.classList.add('active')"
@@ -59,7 +59,7 @@
 							<button v-if="!fieldEditMode" class="_button" :class="$style.dragItemHandle" tabindex="-1"><i class="ti ti-menu"></i></button>
 							<button v-if="fieldEditMode" :disabled="fields.length <= 1" class="_button" :class="$style.dragItemRemove" @click="deleteField(index)"><i class="ti ti-x"></i></button>
 							<div :class="$style.dragItemForm">
-								<FormSplit :min-width="200">
+								<FormSplit :minWidth="200">
 									<MkInput v-model="element.name" small>
 										<template #label>{{ i18n.ts._profile.metadataLabel }}</template>
 									</MkInput>
@@ -88,8 +88,10 @@
 	<MkSelect v-model="reactionAcceptance">
 		<template #label>{{ i18n.ts.reactionAcceptance }}</template>
 		<option :value="null">{{ i18n.ts.all }}</option>
-		<option value="likeOnly">{{ i18n.ts.likeOnly }}</option>
 		<option value="likeOnlyForRemote">{{ i18n.ts.likeOnlyForRemote }}</option>
+		<option value="nonSensitiveOnly">{{ i18n.ts.nonSensitiveOnly }}</option>
+		<option value="nonSensitiveOnlyForLocalLikeOnlyForRemote">{{ i18n.ts.nonSensitiveOnlyForLocalLikeOnlyForRemote }}</option>
+		<option value="likeOnly">{{ i18n.ts.likeOnly }}</option>
 	</MkSelect>
 </div>
 </template>
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 0af4b5c021..aa4b98c05c 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -92,7 +92,7 @@ export const defaultStore = markRaw(new Storage('base', {
 	},
 	reactionAcceptance: {
 		where: 'account',
-		default: null as 'likeOnly' | 'likeOnlyForRemote' | null,
+		default: 'nonSensitiveOnly' as 'likeOnly' | 'likeOnlyForRemote' | 'nonSensitiveOnly' | 'nonSensitiveOnlyForLocalLikeOnlyForRemote' | null,
 	},
 	mutedWords: {
 		where: 'account',
-- 
GitLab