実績を凍結する

This commit is contained in:
tamaina 2023-04-22 18:35:45 +00:00
parent 2f3c2af619
commit 23670ff0fe
10 changed files with 38 additions and 15 deletions

View File

@ -1034,12 +1034,12 @@ _accountMigration:
moveTo: "このアカウントを新しいアカウントへ移行" moveTo: "このアカウントを新しいアカウントへ移行"
moveToLabel: "移行先のアカウント:" moveToLabel: "移行先のアカウント:"
moveCannotBeUndone: "アカウントを移行すると、取り消すことはできません。" 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" moveAccountHowTo: "アカウントの移行には、まずは移行先のアカウントでこのアカウントに対しエイリアスを作成します。\nエイリアス作成後、移行先のアカウントを次のように入力してください: @username@server.example.com"
startMigration: "移行する" startMigration: "移行する"
migrationConfirm: "本当にこのアカウントを {account} に移行しますか?一度移行すると取り消せず、二度とこのアカウントを元の状態で使用できなくなります。" migrationConfirm: "本当にこのアカウントを {account} に移行しますか?一度移行すると取り消せず、二度とこのアカウントを元の状態で使用できなくなります。"
postMigrationNote: "フォロー解除は、移行操作をしてから1日後に行われます。"
movedAndCannotBeUndone: "\nアカウントは移行されています。\n移行を取り消すことはできません。" movedAndCannotBeUndone: "\nアカウントは移行されています。\n移行を取り消すことはできません。"
postMigrationNote: "このアカウントからのフォロー解除は移行操作から24時間後に実行されます。\nこのアカウントのフォロー・フォロワー数は0になっています。フォロワーの解除はされないため、あなたのフォロワーはこのアカウントのフォロワー向け投稿を引き続き閲覧できます。"
movedTo: "移行先のアカウント:" movedTo: "移行先のアカウント:"
_achievements: _achievements:

View File

@ -4,6 +4,7 @@ import { AchievementService, ACHIEVEMENT_TYPES } from '@/core/AchievementService
export const meta = { export const meta = {
requireCredential: true, requireCredential: true,
prohibitMoved: true,
} as const; } as const;
export const paramDef = { export const paramDef = {

View File

@ -388,6 +388,7 @@ describe('Account Move', () => {
'/gallery/posts/like', '/gallery/posts/like',
'/gallery/posts/unlike', '/gallery/posts/unlike',
'/gallery/posts/update', '/gallery/posts/update',
'/i/claim-achievement',
'/i/move', '/i/move',
'/i/import-blocking', '/i/import-blocking',
'/i/import-following', '/i/import-following',

View File

@ -162,6 +162,7 @@ import { claimAchievement } from '@/scripts/achievements';
import { getNoteSummary } from '@/scripts/get-note-summary'; import { getNoteSummary } from '@/scripts/get-note-summary';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
import MkRippleEffect from '@/components/MkRippleEffect.vue'; import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog';
const props = defineProps<{ const props = defineProps<{
note: misskey.entities.Note; note: misskey.entities.Note;
@ -255,6 +256,7 @@ useTooltip(renoteButton, async (showing) => {
function renote(viaKeyboard = false) { function renote(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
showMovedDialog();
let items = [] as MenuItem[]; let items = [] as MenuItem[];
@ -335,6 +337,7 @@ function reply(viaKeyboard = false): void {
function react(viaKeyboard = false): void { function react(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') { if (appearNote.reactionAcceptance === 'likeOnly') {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.id,
@ -401,6 +404,7 @@ async function clip() {
function showRenoteMenu(viaKeyboard = false): void { function showRenoteMenu(viaKeyboard = false): void {
if (!isMyRenote) return; if (!isMyRenote) return;
pleaseLogin();
os.popupMenu([{ os.popupMenu([{
text: i18n.ts.unrenote, text: i18n.ts.unrenote,
icon: 'ti ti-trash', icon: 'ti ti-trash',

View File

@ -166,6 +166,7 @@ import { useTooltip } from '@/scripts/use-tooltip';
import { claimAchievement } from '@/scripts/achievements'; import { claimAchievement } from '@/scripts/achievements';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
import MkRippleEffect from '@/components/MkRippleEffect.vue'; import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { showMovedDialog } from '@/scripts/show-moved-dialog';
const props = defineProps<{ const props = defineProps<{
note: misskey.entities.Note; note: misskey.entities.Note;
@ -248,6 +249,7 @@ useTooltip(renoteButton, async (showing) => {
function renote(viaKeyboard = false) { function renote(viaKeyboard = false) {
pleaseLogin(); pleaseLogin();
showMovedDialog();
let items = [] as MenuItem[]; let items = [] as MenuItem[];
@ -318,6 +320,7 @@ function renote(viaKeyboard = false) {
function reply(viaKeyboard = false): void { function reply(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
showMovedDialog();
os.post({ os.post({
reply: appearNote, reply: appearNote,
animation: !viaKeyboard, animation: !viaKeyboard,
@ -328,6 +331,7 @@ function reply(viaKeyboard = false): void {
function react(viaKeyboard = false): void { function react(viaKeyboard = false): void {
pleaseLogin(); pleaseLogin();
showMovedDialog();
if (appearNote.reactionAcceptance === 'likeOnly') { if (appearNote.reactionAcceptance === 'likeOnly') {
os.api('notes/reactions/create', { os.api('notes/reactions/create', {
noteId: appearNote.id, noteId: appearNote.id,
@ -394,6 +398,7 @@ async function clip() {
function showRenoteMenu(viaKeyboard = false): void { function showRenoteMenu(viaKeyboard = false): void {
if (!isMyRenote) return; if (!isMyRenote) return;
pleaseLogin();
os.popupMenu([{ os.popupMenu([{
text: i18n.ts.unrenote, text: i18n.ts.unrenote,
icon: 'ti ti-trash', icon: 'ti ti-trash',

View File

@ -18,7 +18,7 @@ import MkPopupMenu from '@/components/MkPopupMenu.vue';
import MkContextMenu from '@/components/MkContextMenu.vue'; import MkContextMenu from '@/components/MkContextMenu.vue';
import { MenuItem } from '@/types/menu'; import { MenuItem } from '@/types/menu';
import copyToClipboard from './scripts/copy-to-clipboard'; import copyToClipboard from './scripts/copy-to-clipboard';
import { $i } from './account'; import { showMovedDialog } from './scripts/show-moved-dialog';
export const openingWindowsCount = ref(0); export const openingWindowsCount = ref(0);
@ -579,13 +579,7 @@ export function contextMenu(items: MenuItem[] | Ref<MenuItem[]>, ev: MouseEvent)
} }
export function post(props: Record<string, any> = {}): Promise<void> { export function post(props: Record<string, any> = {}): Promise<void> {
if ($i && $i.movedTo) { showMovedDialog();
return alert({
type: 'error',
title: i18n.ts.accountMovedShort,
text: i18n.ts.operationForbidden,
});
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない // NOTE: MkPostFormDialogをdynamic importするとiOSでテキストエリアに自動フォーカスできない

View File

@ -32,7 +32,7 @@
<MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> <MkButton primary :class="$style.button" inline @click="exportFollowing()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</div> </div>
</MkFolder> </MkFolder>
<MkFolder> <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template> <template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template> <template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> <MkButton primary :class="$style.button" inline @click="importFollowing($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -47,7 +47,7 @@
<template #icon><i class="ti ti-download"></i></template> <template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> <MkButton primary :class="$style.button" inline @click="exportUserLists()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder> </MkFolder>
<MkFolder> <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template> <template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template> <template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> <MkButton primary :class="$style.button" inline @click="importUserLists($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -62,7 +62,7 @@
<template #icon><i class="ti ti-download"></i></template> <template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> <MkButton primary :class="$style.button" inline @click="exportMuting()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder> </MkFolder>
<MkFolder> <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template> <template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template> <template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> <MkButton primary :class="$style.button" inline @click="importMuting($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -77,7 +77,7 @@
<template #icon><i class="ti ti-download"></i></template> <template #icon><i class="ti ti-download"></i></template>
<MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton> <MkButton primary :class="$style.button" inline @click="exportBlocking()"><i class="ti ti-download"></i> {{ i18n.ts.export }}</MkButton>
</MkFolder> </MkFolder>
<MkFolder> <MkFolder v-if="$i && !$i.movedTo">
<template #label>{{ i18n.ts.import }}</template> <template #label>{{ i18n.ts.import }}</template>
<template #icon><i class="ti ti-upload"></i></template> <template #icon><i class="ti ti-upload"></i></template>
<MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton> <MkButton primary :class="$style.button" inline @click="importBlocking($event)"><i class="ti ti-upload"></i> {{ i18n.ts.import }}</MkButton>
@ -97,6 +97,7 @@ import * as os from '@/os';
import { selectFile } from '@/scripts/select-file'; import { selectFile } from '@/scripts/select-file';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
import { $i } from '@/account';
const excludeMutingUsers = ref(false); const excludeMutingUsers = ref(false);
const excludeInactiveUsers = ref(false); const excludeInactiveUsers = ref(false);

View File

@ -464,6 +464,7 @@ const claimingQueue = new Set<string>();
export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) { export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) {
if ($i == null) return; if ($i == null) return;
if ($i.movedTo) return;
if (claimedAchievements.includes(type)) return; if (claimedAchievements.includes(type)) return;
claimingQueue.add(type); claimingQueue.add(type);
claimedAchievements.push(type); claimedAchievements.push(type);

View File

@ -17,5 +17,5 @@ export function pleaseLogin(path?: string) {
}, },
}, 'closed'); }, 'closed');
if (!path) throw new Error('signin required'); throw new Error('signin required');
} }

View File

@ -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');
}