diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 53477e6786..f3dfc72bf4 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -1034,12 +1034,12 @@ _accountMigration: moveTo: "このアカウントを新しいアカウントへ移行" moveToLabel: "移行先のアカウント:" moveCannotBeUndone: "アカウントを移行すると、取り消すことはできません。" - moveAccountDescription: "新しいアカウントへ移行します。\n ・フォロワーが新しいアカウントを自動でフォローします(フォロワーのサーバーが対応している場合)。\n ・このアカウントからのフォローは全て解除されます。\n ・このアカウントではノートの作成などができなくなります。\n\nフォロワーの移行は自動ですが、フォローの移行は手動で行う必要があります。移行前にこのアカウントでフォローエクスポートし、移行後すぐに移行先アカウントでインポートを行なってください。" + moveAccountDescription: "新しいアカウントへ移行します。\n ・フォロワーが新しいアカウントを自動でフォローします(フォロワーのサーバーが対応している場合)。\n ・このアカウントからのフォローは全て解除されます。\n ・このアカウントではノートの作成などができなくなります。\n\nフォロワーの移行は自動ですが、フォローの移行は手動で行う必要があります。移行前にこのアカウントでフォローエクスポートし、移行後すぐに移行先アカウントでインポートを行なってください。\nリスト・ミュート・ブロックについても同様ですので、手動で移行する必要があります。\n\n(ここに記載されている仕様は同一サーバーもしくは13.12.0以降のMisskeyサーバーのものであり、他のActivityPub実装では挙動が異なる場合があります。)" moveAccountHowTo: "アカウントの移行には、まずは移行先のアカウントでこのアカウントに対しエイリアスを作成します。\nエイリアス作成後、移行先のアカウントを次のように入力してください: @username@server.example.com" startMigration: "移行する" migrationConfirm: "本当にこのアカウントを {account} に移行しますか?一度移行すると取り消せず、二度とこのアカウントを元の状態で使用できなくなります。" - postMigrationNote: "フォロー解除は、移行操作をしてから1日後に行われます。" movedAndCannotBeUndone: "\nアカウントは移行されています。\n移行を取り消すことはできません。" + postMigrationNote: "このアカウントからのフォロー解除は移行操作から24時間後に実行されます。\nこのアカウントのフォロー・フォロワー数は0になっています。フォロワーの解除はされないため、あなたのフォロワーはこのアカウントのフォロワー向け投稿を引き続き閲覧できます。" movedTo: "移行先のアカウント:" _achievements: diff --git a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts index 102dae4fb7..4eef496385 100644 --- a/packages/backend/src/server/api/endpoints/i/claim-achievement.ts +++ b/packages/backend/src/server/api/endpoints/i/claim-achievement.ts @@ -4,6 +4,7 @@ import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService export const meta = { requireCredential: true, + prohibitMoved: true, } as const; export const paramDef = { diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts index 326594901a..4dd5cbb9dd 100644 --- a/packages/backend/test/e2e/move.ts +++ b/packages/backend/test/e2e/move.ts @@ -388,6 +388,7 @@ describe('Account Move', () => { '/gallery/posts/like', '/gallery/posts/unlike', '/gallery/posts/update', + '/i/claim-achievement', '/i/move', '/i/import-blocking', '/i/import-following', diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index cb4bb8f23e..eba3c211a6 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -162,6 +162,7 @@ import { claimAchievement } from '@/scripts/achievements'; import { getNoteSummary } from '@/scripts/get-note-summary'; import { MenuItem } from '@/types/menu'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import { showMovedDialog } from '@/scripts/show-moved-dialog'; const props = defineProps<{ note: misskey.entities.Note; @@ -255,6 +256,7 @@ useTooltip(renoteButton, async (showing) => { function renote(viaKeyboard = false) { pleaseLogin(); + showMovedDialog(); let items = [] as MenuItem[]; @@ -335,6 +337,7 @@ function reply(viaKeyboard = false): void { function react(viaKeyboard = false): void { pleaseLogin(); + showMovedDialog(); if (appearNote.reactionAcceptance === 'likeOnly') { os.api('notes/reactions/create', { noteId: appearNote.id, @@ -401,6 +404,7 @@ async function clip() { function showRenoteMenu(viaKeyboard = false): void { if (!isMyRenote) return; + pleaseLogin(); os.popupMenu([{ text: i18n.ts.unrenote, icon: 'ti ti-trash', diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index b9ab366850..0d6d329d98 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -166,6 +166,7 @@ import { useTooltip } from '@/scripts/use-tooltip'; import { claimAchievement } from '@/scripts/achievements'; import { MenuItem } from '@/types/menu'; import MkRippleEffect from '@/components/MkRippleEffect.vue'; +import { showMovedDialog } from '@/scripts/show-moved-dialog'; const props = defineProps<{ note: misskey.entities.Note; @@ -248,6 +249,7 @@ useTooltip(renoteButton, async (showing) => { function renote(viaKeyboard = false) { pleaseLogin(); + showMovedDialog(); let items = [] as MenuItem[]; @@ -318,6 +320,7 @@ function renote(viaKeyboard = false) { function reply(viaKeyboard = false): void { pleaseLogin(); + showMovedDialog(); os.post({ reply: appearNote, animation: !viaKeyboard, @@ -328,6 +331,7 @@ function reply(viaKeyboard = false): void { function react(viaKeyboard = false): void { pleaseLogin(); + showMovedDialog(); if (appearNote.reactionAcceptance === 'likeOnly') { os.api('notes/reactions/create', { noteId: appearNote.id, @@ -394,6 +398,7 @@ async function clip() { function showRenoteMenu(viaKeyboard = false): void { if (!isMyRenote) return; + pleaseLogin(); os.popupMenu([{ text: i18n.ts.unrenote, icon: 'ti ti-trash', diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts index dc80eba902..67acee5aca 100644 --- a/packages/frontend/src/os.ts +++ b/packages/frontend/src/os.ts @@ -18,7 +18,7 @@ import MkPopupMenu from '@/components/MkPopupMenu.vue'; import MkContextMenu from '@/components/MkContextMenu.vue'; import { MenuItem } from '@/types/menu'; import copyToClipboard from './scripts/copy-to-clipboard'; -import { $i } from './account'; +import { showMovedDialog } from './scripts/show-moved-dialog'; export const openingWindowsCount = ref(0); @@ -579,13 +579,7 @@ export function contextMenu(items: MenuItem[] | Ref, ev: MouseEvent) } export function post(props: Record = {}): Promise { - if ($i && $i.movedTo) { - return alert({ - type: 'error', - title: i18n.ts.accountMovedShort, - text: i18n.ts.operationForbidden, - }); - } + showMovedDialog(); return new Promise((resolve, reject) => { // NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue index a8274f5601..c883efe691 100644 --- a/packages/frontend/src/pages/settings/import-export.vue +++ b/packages/frontend/src/pages/settings/import-export.vue @@ -32,7 +32,7 @@ {{ i18n.ts.export }} - + {{ i18n.ts.import }} @@ -47,7 +47,7 @@ {{ i18n.ts.export }} - + {{ i18n.ts.import }} @@ -62,7 +62,7 @@ {{ i18n.ts.export }} - + {{ i18n.ts.import }} @@ -77,7 +77,7 @@ {{ i18n.ts.export }} - + {{ i18n.ts.import }} @@ -97,6 +97,7 @@ import * as os from '@/os'; import { selectFile } from '@/scripts/select-file'; import { i18n } from '@/i18n'; import { definePageMetadata } from '@/scripts/page-metadata'; +import { $i } from '@/account'; const excludeMutingUsers = ref(false); const excludeInactiveUsers = ref(false); diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts index f1da8a76da..fbca005769 100644 --- a/packages/frontend/src/scripts/achievements.ts +++ b/packages/frontend/src/scripts/achievements.ts @@ -464,6 +464,7 @@ const claimingQueue = new Set(); export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { if ($i == null) return; + if ($i.movedTo) return; if (claimedAchievements.includes(type)) return; claimingQueue.add(type); claimedAchievements.push(type); diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts index b8fb853cc1..c101a127f3 100644 --- a/packages/frontend/src/scripts/please-login.ts +++ b/packages/frontend/src/scripts/please-login.ts @@ -17,5 +17,5 @@ export function pleaseLogin(path?: string) { }, }, 'closed'); - if (!path) throw new Error('signin required'); + throw new Error('signin required'); } diff --git a/packages/frontend/src/scripts/show-moved-dialog.ts b/packages/frontend/src/scripts/show-moved-dialog.ts new file mode 100644 index 0000000000..acb26c36e2 --- /dev/null +++ b/packages/frontend/src/scripts/show-moved-dialog.ts @@ -0,0 +1,16 @@ +import * as os from '@/os'; +import { $i } from '@/account'; +import { i18n } from '@/i18n'; + +export function showMovedDialog() { + if (!$i) return; + if (!$i.movedTo) return; + + os.alert({ + type: 'error', + title: i18n.ts.accountMovedShort, + text: i18n.ts.operationForbidden, + }); + + throw new Error('account moved'); +}