diff --git a/CHANGELOG.md b/CHANGELOG.md
index ed8a220e0d..eb5ac28487 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@ You should also include the user name that made the change.
- コンディショナルロールもバッジとして表示可能に
- enhance(client): ロールをより簡単に付与できるように
- enhance(client): 一度見たノートのRenoteは省略して表示するように
+- enhance(client): 迷惑になる可能性のある投稿を行う前に警告を表示
- 一部のMFM構文をopt-outに
### Bugfixes
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 031e90215f..5c919c3032 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -947,6 +947,10 @@ selectFromPresets: "プリセットから選択"
achievements: "実績"
gotInvalidResponseError: "サーバーの応答が無効です"
gotInvalidResponseErrorDescription: "サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから再度お試しください。"
+thisPostMayBeAnnoying: "この投稿は迷惑になる可能性があります。"
+thisPostMayBeAnnoyingHome: "ホームに投稿"
+thisPostMayBeAnnoyingCancel: "やめる"
+thisPostMayBeAnnoyingIgnore: "このまま投稿"
_achievements:
earnedAt: "獲得日時"
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index da4db63406..9690353432 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -32,7 +32,7 @@
{{ cancelText ?? i18n.ts.cancel }}
- { action.callback(); close(); }">{{ action.text }}
+ { action.callback(); modal?.close(); }">{{ action.text }}
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index a06bdecaa8..f15906c1c1 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -579,6 +579,36 @@ async function post(ev?: MouseEvent) {
os.popup(MkRippleEffect, { x, y }, {}, 'end');
}
+ const annoying =
+ text.includes('$[x2') ||
+ text.includes('$[x3') ||
+ text.includes('$[x4') ||
+ text.includes('$[scale') ||
+ text.includes('$[position');
+ if (annoying) {
+ const { canceled, result } = await os.actions({
+ type: 'warning',
+ text: i18n.ts.thisPostMayBeAnnoying,
+ actions: [{
+ value: 'home',
+ text: i18n.ts.thisPostMayBeAnnoyingHome,
+ primary: true,
+ }, {
+ value: 'cancel',
+ text: i18n.ts.thisPostMayBeAnnoyingCancel,
+ }, {
+ value: 'ignore',
+ text: i18n.ts.thisPostMayBeAnnoyingIgnore,
+ }],
+ });
+
+ if (canceled) return;
+ if (result === 'cancel') return;
+ if (result === 'home') {
+ visibility = 'home';
+ }
+ }
+
let postData = {
text: text === '' ? undefined : text,
fileIds: files.length > 0 ? files.map(f => f.id) : undefined,
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 4997030669..639f4eaf17 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -186,6 +186,38 @@ export function confirm(props: {
});
}
+// TODO: const T extends ... にしたい
+// https://zenn.dev/general_link/articles/813e47b7a0eef7#const-type-parameters
+export function actions(props: {
+ type: 'error' | 'info' | 'success' | 'warning' | 'waiting' | 'question';
+ title?: string | null;
+ text?: string | null;
+ actions: T;
+}): Promise<{ canceled: true; result: undefined; } | {
+ canceled: false; result: T[number]['value'];
+}> {
+ return new Promise((resolve, reject) => {
+ popup(MkDialog, {
+ ...props,
+ actions: props.actions.map(a => ({
+ text: a.text,
+ primary: a.primary,
+ callback: () => {
+ resolve({ canceled: false, result: a.value });
+ },
+ })),
+ }, {
+ done: result => {
+ resolve(result ? result : { canceled: true });
+ },
+ }, 'closed');
+ });
+}
+
export function inputText(props: {
type?: 'text' | 'email' | 'password' | 'url';
title?: string | null;