diff options
| author | tamaina <tamaina@hotmail.co.jp> | 2023-03-09 14:27:16 +0900 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-03-09 14:27:16 +0900 |
| commit | c75afad64aba2f4cce3f7fc37a66e9dc45488383 (patch) | |
| tree | 96dcfab25705393119b2ea60329fb3f59f77aa73 /packages/frontend/src/account.ts | |
| parent | fix: 登録メール送信時に重複確認を行う (#10231) (diff) | |
| download | misskey-c75afad64aba2f4cce3f7fc37a66e9dc45488383.tar.gz misskey-c75afad64aba2f4cce3f7fc37a66e9dc45488383.tar.bz2 misskey-c75afad64aba2f4cce3f7fc37a66e9dc45488383.zip | |
enhance: アカウント削除時のクライアントの挙動をいい感じにするなど (#10002)
* refreshAccounts
Resolve #9322
* アカウント管理画面でリストを更新するように
* Update packages/frontend/src/account.ts
Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
* :v:
* クライアント起動時は現在ログインしているアカウントのみリフレッシュする
* clean up
* なんかめっちゃ変えた
* refactor
* refactor
* fix lint
---------
Co-authored-by: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
Diffstat (limited to 'packages/frontend/src/account.ts')
| -rw-r--r-- | packages/frontend/src/account.ts | 135 |
1 files changed, 100 insertions, 35 deletions
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts index 610212b6ec..9b104391d7 100644 --- a/packages/frontend/src/account.ts +++ b/packages/frontend/src/account.ts @@ -1,4 +1,4 @@ -import { defineAsyncComponent, reactive } from 'vue'; +import { defineAsyncComponent, reactive, ref } from 'vue'; import * as misskey from 'misskey-js'; import { showSuspendedDialog } from './scripts/show-suspended-dialog'; import { i18n } from './i18n'; @@ -7,6 +7,7 @@ import { del, get, set } from '@/scripts/idb-proxy'; import { apiUrl } from '@/config'; import { waiting, api, popup, popupMenu, success, alert } from '@/os'; import { unisonReload, reloadChannel } from '@/scripts/unison-reload'; +import { MenuButton } from './types/menu'; // TODO: 他のタブと永続化されたstateを同期 @@ -26,11 +27,11 @@ export function incNotesCount() { } export async function signout() { + if (!$i) return; + waiting(); miLocalStorage.removeItem('account'); - await removeAccount($i.id); - const accounts = await getAccounts(); //#region Remove service worker registration @@ -76,15 +77,19 @@ export async function addAccount(id: Account['id'], token: Account['token']) { } } -export async function removeAccount(id: Account['id']) { +export async function removeAccount(idOrToken: Account['id']) { const accounts = await getAccounts(); - accounts.splice(accounts.findIndex(x => x.id === id), 1); + const i = accounts.findIndex(x => x.id === idOrToken || x.token === idOrToken); + if (i !== -1) accounts.splice(i, 1); - if (accounts.length > 0) await set('accounts', accounts); - else await del('accounts'); + if (accounts.length > 0) { + await set('accounts', accounts); + } else { + await del('accounts'); + } } -function fetchAccount(token: string): Promise<Account> { +function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Account> { return new Promise((done, fail) => { // Fetch user window.fetch(`${apiUrl}/i`, { @@ -96,44 +101,94 @@ function fetchAccount(token: string): Promise<Account> { 'Content-Type': 'application/json', }, }) - .then(res => res.json()) - .then(res => { - if (res.error) { - if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') { - showSuspendedDialog().then(() => { - signout(); + .then(res => new Promise<Account | { error: Record<string, any> }>((done2, fail2) => { + if (res.status >= 500 && res.status < 600) { + // サーバーエラー(5xx)の場合をrejectとする + // (認証エラーなど4xxはresolve) + return fail2(res); + } + res.json().then(done2, fail2); + })) + .then(async res => { + if (res.error) { + if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') { + // SUSPENDED + if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + await showSuspendedDialog(); + } + } else if (res.error.id === 'e5b3b9f0-2b8f-4b9f-9c1f-8c5c1b2e1b1a') { + // USER_IS_DELETED + // アカウントが削除されている + if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + await alert({ + type: 'error', + title: i18n.ts.accountDeleted, + text: i18n.ts.accountDeletedDescription, }); - } else { - alert({ + } + } else if (res.error.id === 'b0a7f5f8-dc2f-4171-b91f-de88ad238e14') { + // AUTHENTICATION_FAILED + // トークンが無効化されていたりアカウントが削除されたりしている + if (forceShowDialog || $i && (token === $i.token || id === $i.id)) { + await alert({ type: 'error', - title: i18n.ts.failedToFetchAccountInformation, - text: JSON.stringify(res.error), + title: i18n.ts.tokenRevoked, + text: i18n.ts.tokenRevokedDescription, }); } } else { - res.token = token; - done(res); + await alert({ + type: 'error', + title: i18n.ts.failedToFetchAccountInformation, + text: JSON.stringify(res.error), + }); } - }) - .catch(fail); + + // rejectかつ理由がtrueの場合、削除対象であることを示す + fail(true); + } else { + (res as Account).token = token; + done(res as Account); + } + }) + .catch(fail); }); } -export function updateAccount(accountData) { +export function updateAccount(accountData: Partial<Account>) { + if (!$i) return; for (const [key, value] of Object.entries(accountData)) { $i[key] = value; } miLocalStorage.setItem('account', JSON.stringify($i)); } -export function refreshAccount() { - return fetchAccount($i.token).then(updateAccount); +export async function refreshAccount() { + if (!$i) return; + return fetchAccount($i.token, $i.id) + .then(updateAccount, reason => { + if (reason === true) return signout(); + return; + }); } export async function login(token: Account['token'], redirect?: string) { - waiting(); + const showing = ref(true); + popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), { + success: false, + showing: showing, + }, {}, 'closed'); if (_DEV_) console.log('logging as token ', token); - const me = await fetchAccount(token); + const me = await fetchAccount(token, undefined, true) + .catch(reason => { + if (reason === true) { + // 削除対象の場合 + removeAccount(token); + } + + showing.value = false; + throw reason; + }); miLocalStorage.setItem('account', JSON.stringify(me)); document.cookie = `token=${token}; path=/; max-age=31536000`; // bull dashboardの認証とかで使う await addAccount(me.id, token); @@ -155,6 +210,8 @@ export async function openAccountMenu(opts: { active?: misskey.entities.UserDetailed['id']; onChoose?: (account: misskey.entities.UserDetailed) => void; }, ev: MouseEvent) { + if (!$i) return; + function showSigninDialog() { popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, { done: res => { @@ -175,8 +232,9 @@ export async function openAccountMenu(opts: { async function switchAccount(account: misskey.entities.UserDetailed) { const storedAccounts = await getAccounts(); - const token = storedAccounts.find(x => x.id === account.id).token; - switchAccountWithToken(token); + const found = storedAccounts.find(x => x.id === account.id); + if (found == null) return; + switchAccountWithToken(found.token); } function switchAccountWithToken(token: string) { @@ -188,7 +246,7 @@ export async function openAccountMenu(opts: { function createItem(account: misskey.entities.UserDetailed) { return { - type: 'user', + type: 'user' as const, user: account, active: opts.active != null ? opts.active === account.id : false, action: () => { @@ -201,22 +259,29 @@ export async function openAccountMenu(opts: { }; } - const accountItemPromises = storedAccounts.map(a => new Promise(res => { + const accountItemPromises = storedAccounts.map(a => new Promise<ReturnType<typeof createItem> | MenuButton>(res => { accountsPromise.then(accounts => { const account = accounts.find(x => x.id === a.id); - if (account == null) return res(null); + if (account == null) return res({ + type: 'button' as const, + text: a.id, + action: () => { + switchAccountWithToken(a.token); + }, + }); + res(createItem(account)); }); })); if (opts.withExtraOperation) { popupMenu([...[{ - type: 'link', + type: 'link' as const, text: i18n.ts.profile, to: `/@${ $i.username }`, avatar: $i, }, null, ...(opts.includeCurrentAccount ? [createItem($i)] : []), ...accountItemPromises, { - type: 'parent', + type: 'parent' as const, icon: 'ti ti-plus', text: i18n.ts.addAccount, children: [{ @@ -227,7 +292,7 @@ export async function openAccountMenu(opts: { action: () => { createAccount(); }, }], }, { - type: 'link', + type: 'link' as const, icon: 'ti ti-users', text: i18n.ts.manageAccounts, to: '/settings/accounts', |