diff --git a/locales/index.d.ts b/locales/index.d.ts
index 63878d3d47..d87fabdcf6 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -5222,6 +5222,14 @@ export interface Locale extends ILocale {
* 注意事項を理解した上でオンにします。
*/
"acknowledgeNotesAndEnable": string;
+ /**
+ * リアクションする際に確認する
+ */
+ "confirmOnReact": string;
+ /**
+ * " {emoji} " をリアクションしますか?
+ */
+ "reactAreYouSure": ParameterizedString<"emoji">;
"_accountSettings": {
/**
* コンテンツの表示にログインを必須にする
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index d78bd4ee65..7ac8fc225e 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -1301,6 +1301,8 @@ lockdown: "ロックダウン"
pleaseSelectAccount: "アカウントを選択してください"
availableRoles: "利用可能なロール"
acknowledgeNotesAndEnable: "注意事項を理解した上でオンにします。"
+confirmOnReact: "リアクションする際に確認する"
+reactAreYouSure: "\" {emoji} \" をリアクションしますか?"
_accountSettings:
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 1a8814b7cb..9511dbc4db 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -471,7 +471,16 @@ function react(): void {
}
} else {
blur();
- reactionPicker.show(reactButton.value ?? null, note.value, reaction => {
+ reactionPicker.show(reactButton.value ?? null, note.value, async (reaction) => {
+ if (defaultStore.state.confirmOnReact) {
+ const confirm = await os.confirm({
+ type: 'question',
+ text: i18n.tsx.reactAreYouSure({ emoji: reaction }),
+ });
+
+ if (confirm.canceled) return;
+ }
+
sound.playMisskeySfx('reaction');
if (props.mock) {
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 4a350388c2..f82b44a838 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -450,7 +450,16 @@ function react(): void {
}
} else {
blur();
- reactionPicker.show(reactButton.value ?? null, note.value, reaction => {
+ reactionPicker.show(reactButton.value ?? null, note.value, async (reaction) => {
+ if (defaultStore.state.confirmOnReact) {
+ const confirm = await os.confirm({
+ type: 'question',
+ text: i18n.tsx.reactAreYouSure({ emoji: reaction }),
+ });
+
+ if (confirm.canceled) return;
+ }
+
sound.playMisskeySfx('reaction');
misskeyApi('notes/reactions/create', {
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index b65038aadc..c9e015dcf4 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -90,6 +90,15 @@ async function toggleReaction() {
}
});
} else {
+ if (defaultStore.state.confirmOnReact) {
+ const confirm = await os.confirm({
+ type: 'question',
+ text: i18n.tsx.reactAreYouSure({ emoji: props.reaction }),
+ });
+
+ if (confirm.canceled) return;
+ }
+
sound.playMisskeySfx('reaction');
if (mock) {
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 1bfdfd0e76..7f1a9e5c20 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -170,6 +170,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.enableHorizontalSwipe }}
{{ i18n.ts.alwaysConfirmFollow }}
{{ i18n.ts.confirmWhenRevealingSensitiveMedia }}
+ {{ i18n.ts.confirmOnReact }}
{{ i18n.ts.whenServerDisconnected }}
@@ -319,6 +320,7 @@ const enableHorizontalSwipe = computed(defaultStore.makeGetterSetter('enableHori
const useNativeUIForVideoAudioPlayer = computed(defaultStore.makeGetterSetter('useNativeUIForVideoAudioPlayer'));
const alwaysConfirmFollow = computed(defaultStore.makeGetterSetter('alwaysConfirmFollow'));
const confirmWhenRevealingSensitiveMedia = computed(defaultStore.makeGetterSetter('confirmWhenRevealingSensitiveMedia'));
+const confirmOnReact = computed(defaultStore.makeGetterSetter('confirmOnReact'));
const contextMenu = computed(defaultStore.makeGetterSetter('contextMenu'));
watch(lang, () => {
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 1d981e897b..b9511e82ce 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -474,6 +474,10 @@ export const defaultStore = markRaw(new Storage('base', {
where: 'device',
default: true,
},
+ confirmOnReact: {
+ where: 'device',
+ default: false,
+ },
sound_masterVolume: {
where: 'device',