summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/frontend/src/account.ts390
-rw-r--r--packages/frontend/src/accounts.ts341
-rw-r--r--packages/frontend/src/aiscript/api.ts2
-rw-r--r--packages/frontend/src/boot/common.ts10
-rw-r--r--packages/frontend/src/boot/main-boot.ts24
-rw-r--r--packages/frontend/src/components/MkAnnouncementDialog.vue5
-rw-r--r--packages/frontend/src/components/MkAuthConfirm.vue9
-rw-r--r--packages/frontend/src/components/MkClipPreview.vue2
-rw-r--r--packages/frontend/src/components/MkCropperDialog.vue2
-rw-r--r--packages/frontend/src/components/MkDrive.file.vue2
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.vue2
-rw-r--r--packages/frontend/src/components/MkFollowButton.vue2
-rw-r--r--packages/frontend/src/components/MkInstanceStats.vue2
-rw-r--r--packages/frontend/src/components/MkMediaAudio.vue2
-rw-r--r--packages/frontend/src/components/MkMediaImage.vue2
-rw-r--r--packages/frontend/src/components/MkMediaVideo.vue2
-rw-r--r--packages/frontend/src/components/MkMention.vue2
-rw-r--r--packages/frontend/src/components/MkNote.vue2
-rw-r--r--packages/frontend/src/components/MkNoteDetailed.vue2
-rw-r--r--packages/frontend/src/components/MkNoteSub.vue2
-rw-r--r--packages/frontend/src/components/MkNotification.vue2
-rw-r--r--packages/frontend/src/components/MkPasswordDialog.vue2
-rw-r--r--packages/frontend/src/components/MkPostForm.vue5
-rw-r--r--packages/frontend/src/components/MkPreview.vue2
-rw-r--r--packages/frontend/src/components/MkPushNotificationAllowButton.vue3
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue2
-rw-r--r--packages/frontend/src/components/MkSignin.vue5
-rw-r--r--packages/frontend/src/components/MkSignupDialog.form.vue4
-rw-r--r--packages/frontend/src/components/MkTimeline.vue2
-rw-r--r--packages/frontend/src/components/MkTokenGenerateWindow.vue2
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.Note.vue2
-rw-r--r--packages/frontend/src/components/MkTutorialDialog.Sensitive.vue2
-rw-r--r--packages/frontend/src/components/MkUserInfo.vue2
-rw-r--r--packages/frontend/src/components/MkUserPopup.vue2
-rw-r--r--packages/frontend/src/components/MkUserSelectDialog.vue2
-rw-r--r--packages/frontend/src/components/MkUserSetupDialog.Profile.vue2
-rw-r--r--packages/frontend/src/components/global/MkAd.vue2
-rw-r--r--packages/frontend/src/components/global/MkCustomEmoji.vue2
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.vue3
-rw-r--r--packages/frontend/src/i.ts34
-rw-r--r--packages/frontend/src/local-storage.ts1
-rw-r--r--packages/frontend/src/navbar.ts2
-rw-r--r--packages/frontend/src/pages/about-misskey.vue2
-rw-r--r--packages/frontend/src/pages/about.emojis.vue2
-rw-r--r--packages/frontend/src/pages/achievements.vue2
-rw-r--r--packages/frontend/src/pages/admin-file.vue2
-rw-r--r--packages/frontend/src/pages/admin-user.vue2
-rw-r--r--packages/frontend/src/pages/announcement.vue5
-rw-r--r--packages/frontend/src/pages/announcements.vue5
-rw-r--r--packages/frontend/src/pages/auth.vue3
-rw-r--r--packages/frontend/src/pages/avatar-decoration-edit-dialog.vue2
-rw-r--r--packages/frontend/src/pages/avatar-decorations.vue2
-rw-r--r--packages/frontend/src/pages/channel.vue2
-rw-r--r--packages/frontend/src/pages/clip.vue2
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.game.vue2
-rw-r--r--packages/frontend/src/pages/emojis.emoji.vue2
-rw-r--r--packages/frontend/src/pages/flash/flash.vue2
-rw-r--r--packages/frontend/src/pages/follow-requests.vue2
-rw-r--r--packages/frontend/src/pages/gallery/post.vue2
-rw-r--r--packages/frontend/src/pages/instance-info.vue2
-rw-r--r--packages/frontend/src/pages/invite.vue2
-rw-r--r--packages/frontend/src/pages/my-lists/index.vue2
-rw-r--r--packages/frontend/src/pages/my-lists/list.vue2
-rw-r--r--packages/frontend/src/pages/note.vue2
-rw-r--r--packages/frontend/src/pages/page-editor/page-editor.vue2
-rw-r--r--packages/frontend/src/pages/page.vue2
-rw-r--r--packages/frontend/src/pages/reversi/game.board.vue2
-rw-r--r--packages/frontend/src/pages/reversi/game.setting.vue2
-rw-r--r--packages/frontend/src/pages/reversi/game.vue2
-rw-r--r--packages/frontend/src/pages/reversi/index.vue2
-rw-r--r--packages/frontend/src/pages/scratchpad.vue2
-rw-r--r--packages/frontend/src/pages/search.note.vue2
-rw-r--r--packages/frontend/src/pages/settings/2fa.qrdialog.vue2
-rw-r--r--packages/frontend/src/pages/settings/2fa.vue5
-rw-r--r--packages/frontend/src/pages/settings/account-data.vue2
-rw-r--r--packages/frontend/src/pages/settings/accounts.vue103
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.decoration.vue2
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.dialog.vue2
-rw-r--r--packages/frontend/src/pages/settings/avatar-decoration.vue2
-rw-r--r--packages/frontend/src/pages/settings/drive.vue2
-rw-r--r--packages/frontend/src/pages/settings/email.vue2
-rw-r--r--packages/frontend/src/pages/settings/index.vue3
-rw-r--r--packages/frontend/src/pages/settings/migration.vue2
-rw-r--r--packages/frontend/src/pages/settings/mute-block.instance-mute.vue2
-rw-r--r--packages/frontend/src/pages/settings/mute-block.vue2
-rw-r--r--packages/frontend/src/pages/settings/notifications.vue2
-rw-r--r--packages/frontend/src/pages/settings/other.vue3
-rw-r--r--packages/frontend/src/pages/settings/privacy.vue2
-rw-r--r--packages/frontend/src/pages/settings/profile.vue2
-rw-r--r--packages/frontend/src/pages/signup-complete.vue2
-rw-r--r--packages/frontend/src/pages/tag.vue2
-rw-r--r--packages/frontend/src/pages/theme-editor.vue2
-rw-r--r--packages/frontend/src/pages/timeline.vue2
-rw-r--r--packages/frontend/src/pages/user/achievements.vue2
-rw-r--r--packages/frontend/src/pages/user/home.vue2
-rw-r--r--packages/frontend/src/pages/user/index.vue2
-rw-r--r--packages/frontend/src/pages/welcome.setup.vue2
-rw-r--r--packages/frontend/src/pizzax.ts2
-rw-r--r--packages/frontend/src/preferences.ts2
-rw-r--r--packages/frontend/src/preferences/def.ts4
-rw-r--r--packages/frontend/src/preferences/manager.ts2
-rw-r--r--packages/frontend/src/preferences/utility.ts2
-rw-r--r--packages/frontend/src/router/definition.ts2
-rw-r--r--packages/frontend/src/signout.ts54
-rw-r--r--packages/frontend/src/store.ts4
-rw-r--r--packages/frontend/src/stream.ts2
-rw-r--r--packages/frontend/src/theme-store.ts2
-rw-r--r--packages/frontend/src/timelines.ts2
-rw-r--r--packages/frontend/src/ui/_common_/PreferenceRestore.vue2
-rw-r--r--packages/frontend/src/ui/_common_/announcements.vue2
-rw-r--r--packages/frontend/src/ui/_common_/common.ts2
-rw-r--r--packages/frontend/src/ui/_common_/common.vue2
-rw-r--r--packages/frontend/src/ui/_common_/navbar-for-mobile.vue3
-rw-r--r--packages/frontend/src/ui/_common_/navbar.vue3
-rw-r--r--packages/frontend/src/ui/_common_/sw-inject.ts3
-rw-r--r--packages/frontend/src/ui/classic.header.vue4
-rw-r--r--packages/frontend/src/ui/classic.sidebar.vue3
-rw-r--r--packages/frontend/src/ui/deck.vue2
-rw-r--r--packages/frontend/src/ui/universal.vue2
-rw-r--r--packages/frontend/src/use/use-note-capture.ts2
-rw-r--r--packages/frontend/src/utility/achievements.ts2
-rw-r--r--packages/frontend/src/utility/autogen/settings-search-index.ts7
-rw-r--r--packages/frontend/src/utility/check-permissions.ts2
-rw-r--r--packages/frontend/src/utility/get-note-menu.ts2
-rw-r--r--packages/frontend/src/utility/get-user-menu.ts2
-rw-r--r--packages/frontend/src/utility/isFfVisibleForMe.ts2
-rw-r--r--packages/frontend/src/utility/misskey-api.ts2
-rw-r--r--packages/frontend/src/utility/please-login.ts2
-rw-r--r--packages/frontend/src/utility/show-moved-dialog.ts2
-rw-r--r--packages/frontend/src/utility/upload.ts2
-rw-r--r--packages/frontend/src/widgets/WidgetActivity.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAiscript.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetAiscriptApp.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetBirthdayFollowings.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetButton.vue2
-rw-r--r--packages/frontend/src/widgets/WidgetProfile.vue2
-rw-r--r--packages/frontend/test/aiscript/api.test.ts2
137 files changed, 640 insertions, 622 deletions
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
deleted file mode 100644
index c90d4da5ec..0000000000
--- a/packages/frontend/src/account.ts
+++ /dev/null
@@ -1,390 +0,0 @@
-/*
- * SPDX-FileCopyrightText: syuilo and misskey-project
- * SPDX-License-Identifier: AGPL-3.0-only
- */
-
-import { defineAsyncComponent, reactive, ref } from 'vue';
-import * as Misskey from 'misskey-js';
-import { apiUrl } from '@@/js/config.js';
-import type { MenuItem, MenuButton } from '@/types/menu.js';
-import { defaultMemoryStorage } from '@/memory-storage';
-import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js';
-import { i18n } from '@/i18n.js';
-import { miLocalStorage } from '@/local-storage.js';
-import { del, get, set } from '@/utility/idb-proxy.js';
-import { waiting, popup, popupMenu, success, alert } from '@/os.js';
-import { misskeyApi } from '@/utility/misskey-api.js';
-import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
-
-// TODO: 他のタブと永続化されたstateを同期
-// TODO: accountsはpreferences管理にする(tokenは別管理)
-
-type Account = Misskey.entities.MeDetailed & { token: string };
-
-const accountData = miLocalStorage.getItem('account');
-
-// TODO: 外部からはreadonlyに
-export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
-
-export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
-export const iAmAdmin = $i != null && $i.isAdmin;
-
-export function signinRequired() {
- if ($i == null) throw new Error('signin required');
- return $i;
-}
-
-export let notesCount = $i == null ? 0 : $i.notesCount;
-export function incNotesCount() {
- notesCount++;
-}
-
-export async function signout() {
- if (!$i) return;
-
- defaultMemoryStorage.clear();
-
- waiting();
- document.cookie.split(';').forEach((cookie) => {
- const cookieName = cookie.split('=')[0].trim();
- if (cookieName === 'token') {
- document.cookie = `${cookieName}=; max-age=0; path=/`;
- }
- });
- miLocalStorage.removeItem('account');
- await removeAccount($i.id);
- const accounts = await getAccounts();
-
- //#region Remove service worker registration
- try {
- if (navigator.serviceWorker.controller) {
- const registration = await navigator.serviceWorker.ready;
- const push = await registration.pushManager.getSubscription();
- if (push) {
- await window.fetch(`${apiUrl}/sw/unregister`, {
- method: 'POST',
- body: JSON.stringify({
- i: $i.token,
- endpoint: push.endpoint,
- }),
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
- }
-
- if (accounts.length === 0) {
- await navigator.serviceWorker.getRegistrations()
- .then(registrations => {
- return Promise.all(registrations.map(registration => registration.unregister()));
- });
- }
- } catch (err) {}
- //#endregion
-
- if (accounts.length > 0) login(accounts[0].token);
- else unisonReload('/');
-}
-
-export async function getAccounts(): Promise<{ id: Account['id'], token: Account['token'] }[]> {
- return (await get('accounts')) || [];
-}
-
-export async function addAccount(id: Account['id'], token: Account['token']) {
- const accounts = await getAccounts();
- if (!accounts.some(x => x.id === id)) {
- await set('accounts', accounts.concat([{ id, token }]));
- }
-}
-
-export async function removeAccount(idOrToken: Account['id']) {
- const accounts = await getAccounts();
- 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');
- }
-}
-
-function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Account> {
- document.cookie = 'token=; path=/; max-age=0';
- document.cookie = `token=${token}; path=/queue; max-age=86400; SameSite=Strict; Secure`; // bull dashboardの認証とかで使う
-
- return new Promise((done, fail) => {
- window.fetch(`${apiUrl}/i`, {
- method: 'POST',
- body: JSON.stringify({
- i: token,
- }),
- headers: {
- 'Content-Type': 'application/json',
- },
- })
- .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 ('error' in res) {
- 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 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.tokenRevoked,
- text: i18n.ts.tokenRevokedDescription,
- });
- }
- } else {
- await alert({
- type: 'error',
- title: i18n.ts.failedToFetchAccountInformation,
- text: JSON.stringify(res.error),
- });
- }
-
- // rejectかつ理由がtrueの場合、削除対象であることを示す
- fail(true);
- } else {
- (res as Account).token = token;
- done(res as Account);
- }
- })
- .catch(fail);
- });
-}
-
-export function updateAccount(accountData: Account) {
- if (!$i) return;
- for (const key of Object.keys($i)) {
- delete $i[key];
- }
- for (const [key, value] of Object.entries(accountData)) {
- $i[key] = value;
- }
- miLocalStorage.setItem('account', JSON.stringify($i));
-}
-
-export function updateAccountPartial(accountData: Partial<Account>) {
- if (!$i) return;
- for (const [key, value] of Object.entries(accountData)) {
- $i[key] = value;
- }
- miLocalStorage.setItem('account', JSON.stringify($i));
-}
-
-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) {
- const showing = ref(true);
- const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), {
- success: false,
- showing: showing,
- }, {
- closed: () => dispose(),
- });
- if (_DEV_) console.log('logging as token ', 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));
- await addAccount(me.id, token);
-
- if (redirect) {
- // 他のタブは再読み込みするだけ
- reloadChannel.postMessage(null);
- // このページはredirectで指定された先に移動
- location.href = redirect;
- return;
- }
-
- unisonReload();
-}
-
-export async function openAccountMenu(opts: {
- includeCurrentAccount?: boolean;
- withExtraOperation: boolean;
- active?: Misskey.entities.UserDetailed['id'];
- onChoose?: (account: Misskey.entities.UserDetailed) => void;
-}, ev: MouseEvent) {
- if (!$i) return;
-
- async function switchAccount(account: Misskey.entities.UserDetailed) {
- const storedAccounts = await getAccounts();
- const found = storedAccounts.find(x => x.id === account.id);
- if (found == null) return;
- switchAccountWithToken(found.token);
- }
-
- function switchAccountWithToken(token: string) {
- login(token);
- }
-
- const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id));
- const accountsPromise = misskeyApi('users/show', { userIds: storedAccounts.map(x => x.id) });
-
- function createItem(account: Misskey.entities.UserDetailed) {
- return {
- type: 'user' as const,
- user: account,
- active: opts.active != null ? opts.active === account.id : false,
- action: () => {
- if (opts.onChoose) {
- opts.onChoose(account);
- } else {
- switchAccount(account);
- }
- },
- };
- }
-
- 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({
- type: 'button' as const,
- text: a.id,
- action: () => {
- switchAccountWithToken(a.token);
- },
- });
-
- res(createItem(account));
- });
- }));
-
- const menuItems: MenuItem[] = [];
-
- if (opts.withExtraOperation) {
- menuItems.push({
- type: 'link',
- text: i18n.ts.profile,
- to: `/@${$i.username}`,
- avatar: $i,
- }, {
- type: 'divider',
- });
-
- if (opts.includeCurrentAccount) {
- menuItems.push(createItem($i));
- }
-
- menuItems.push(...accountItemPromises);
-
- menuItems.push({
- type: 'parent',
- icon: 'ti ti-plus',
- text: i18n.ts.addAccount,
- children: [{
- text: i18n.ts.existingAccount,
- action: () => {
- getAccountWithSigninDialog().then(res => {
- if (res != null) {
- success();
- }
- });
- },
- }, {
- text: i18n.ts.createAccount,
- action: () => {
- getAccountWithSignupDialog().then(res => {
- if (res != null) {
- switchAccountWithToken(res.token);
- }
- });
- },
- }],
- }, {
- type: 'link',
- icon: 'ti ti-users',
- text: i18n.ts.manageAccounts,
- to: '/settings/accounts',
- });
- } else {
- if (opts.includeCurrentAccount) {
- menuItems.push(createItem($i));
- }
-
- menuItems.push(...accountItemPromises);
- }
-
- popupMenu(menuItems, ev.currentTarget ?? ev.target, {
- align: 'left',
- });
-}
-
-export function getAccountWithSigninDialog(): Promise<{ id: string, token: string } | null> {
- return new Promise((resolve) => {
- const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
- done: async (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
- await addAccount(res.id, res.i);
- resolve({ id: res.id, token: res.i });
- },
- cancelled: () => {
- resolve(null);
- },
- closed: () => {
- dispose();
- },
- });
- });
-}
-
-export function getAccountWithSignupDialog(): Promise<{ id: string, token: string } | null> {
- return new Promise((resolve) => {
- const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
- done: async (res: Misskey.entities.SignupResponse) => {
- await addAccount(res.id, res.token);
- resolve({ id: res.id, token: res.token });
- },
- cancelled: () => {
- resolve(null);
- },
- closed: () => {
- dispose();
- },
- });
- });
-}
-
-if (_DEV_) {
- (window as any).$i = $i;
-}
diff --git a/packages/frontend/src/accounts.ts b/packages/frontend/src/accounts.ts
new file mode 100644
index 0000000000..2382a8ec32
--- /dev/null
+++ b/packages/frontend/src/accounts.ts
@@ -0,0 +1,341 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { defineAsyncComponent, ref } from 'vue';
+import * as Misskey from 'misskey-js';
+import { apiUrl, host } from '@@/js/config.js';
+import type { MenuItem } from '@/types/menu.js';
+import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js';
+import { i18n } from '@/i18n.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { waiting, popup, popupMenu, success, alert } from '@/os.js';
+import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
+import { prefer } from '@/preferences.js';
+import { store } from '@/store.js';
+import { $i } from '@/i.js';
+import { signout } from '@/signout.js';
+
+// TODO: 他のタブと永続化されたstateを同期
+
+type AccountWithToken = Misskey.entities.MeDetailed & { token: string };
+
+export async function getAccounts(): Promise<{
+ host: string;
+ user: Misskey.entities.User;
+ token: string | null;
+}[]> {
+ const tokens = store.s.accountTokens;
+ const accounts = prefer.s.accounts;
+ return accounts.map(([host, user]) => ({
+ host,
+ user,
+ token: tokens[host + '/' + user.id] ?? null,
+ }));
+}
+
+async function addAccount(host: string, user: Misskey.entities.User, token: AccountWithToken['token']) {
+ if (!prefer.s.accounts.some(x => x[0] === host && x[1].id === user.id)) {
+ store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + user.id]: token });
+ prefer.commit('accounts', [...prefer.s.accounts, [host, user]]);
+ }
+}
+
+export async function removeAccount(host: string, id: AccountWithToken['id']) {
+ const tokens = JSON.parse(JSON.stringify(store.s.accountTokens));
+ delete tokens[host + '/' + id];
+ store.set('accountTokens', tokens);
+ prefer.commit('accounts', prefer.s.accounts.filter(x => x[0] !== host || x[1].id !== id));
+}
+
+const isAccountDeleted = Symbol('isAccountDeleted');
+
+function fetchAccount(token: string, id?: string, forceShowDialog?: boolean): Promise<Misskey.entities.MeDetailed> {
+ return new Promise((done, fail) => {
+ window.fetch(`${apiUrl}/i`, {
+ method: 'POST',
+ body: JSON.stringify({
+ i: token,
+ }),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ })
+ .then(res => new Promise<Misskey.entities.MeDetailed | { 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 ('error' in res) {
+ 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 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.tokenRevoked,
+ text: i18n.ts.tokenRevokedDescription,
+ });
+ }
+ } else {
+ await alert({
+ type: 'error',
+ title: i18n.ts.failedToFetchAccountInformation,
+ text: JSON.stringify(res.error),
+ });
+ }
+
+ fail(isAccountDeleted);
+ } else {
+ done(res);
+ }
+ })
+ .catch(fail);
+ });
+}
+
+export function updateCurrentAccount(accountData: Misskey.entities.MeDetailed) {
+ if (!$i) return;
+ const token = $i.token;
+ for (const key of Object.keys($i)) {
+ delete $i[key];
+ }
+ for (const [key, value] of Object.entries(accountData)) {
+ $i[key] = value;
+ }
+ prefer.commit('accounts', prefer.s.accounts.map(([host, user]) => {
+ // TODO: $iのホストも比較したいけど通常null
+ if (user.id === $i.id) {
+ return [host, $i];
+ } else {
+ return [host, user];
+ }
+ }));
+ $i.token = token;
+ miLocalStorage.setItem('account', JSON.stringify($i));
+}
+
+export function updateCurrentAccountPartial(accountData: Partial<Misskey.entities.MeDetailed>) {
+ if (!$i) return;
+ for (const [key, value] of Object.entries(accountData)) {
+ $i[key] = value;
+ }
+ prefer.commit('accounts', prefer.s.accounts.map(([host, user]) => {
+ // TODO: $iのホストも比較したいけど通常null
+ if (user.id === $i.id) {
+ const newUser = JSON.parse(JSON.stringify($i));
+ for (const [key, value] of Object.entries(accountData)) {
+ newUser[key] = value;
+ }
+ return [host, newUser];
+ }
+ return [host, user];
+ }));
+ miLocalStorage.setItem('account', JSON.stringify($i));
+}
+
+export async function refreshCurrentAccount() {
+ if (!$i) return;
+ return fetchAccount($i.token, $i.id).then(updateCurrentAccount).catch(reason => {
+ if (reason === isAccountDeleted) {
+ removeAccount(host, $i.id);
+ if (Object.keys(store.s.accountTokens).length > 0) {
+ login(Object.values(store.s.accountTokens)[0]);
+ } else {
+ signout();
+ }
+ }
+ });
+}
+
+export async function login(token: AccountWithToken['token'], redirect?: string) {
+ const showing = ref(true);
+ const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkWaitingDialog.vue')), {
+ success: false,
+ showing: showing,
+ }, {
+ closed: () => dispose(),
+ });
+
+ const me = await fetchAccount(token, undefined, true).catch(reason => {
+ showing.value = false;
+ throw reason;
+ });
+
+ miLocalStorage.setItem('account', JSON.stringify({
+ ...me,
+ token,
+ }));
+
+ await addAccount(host, me, token);
+
+ if (redirect) {
+ // 他のタブは再読み込みするだけ
+ reloadChannel.postMessage(null);
+ // このページはredirectで指定された先に移動
+ location.href = redirect;
+ return;
+ }
+
+ unisonReload();
+}
+
+export async function switchAccount(host: string, id: string) {
+ const token = store.s.accountTokens[host + '/' + id];
+ if (token) {
+ login(token);
+ } else {
+ const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
+ done: async (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
+ store.set('accountTokens', { ...store.s.accountTokens, [host + '/' + res.id]: res.i });
+ login(res.i);
+ },
+ closed: () => {
+ dispose();
+ },
+ });
+ }
+}
+
+export async function openAccountMenu(opts: {
+ includeCurrentAccount?: boolean;
+ withExtraOperation: boolean;
+ active?: Misskey.entities.User['id'];
+ onChoose?: (account: Misskey.entities.User) => void;
+}, ev: MouseEvent) {
+ if (!$i) return;
+
+ function createItem(host: string, account: Misskey.entities.User): MenuItem {
+ return {
+ type: 'user' as const,
+ user: account,
+ active: opts.active != null ? opts.active === account.id : false,
+ action: async () => {
+ if (opts.onChoose) {
+ opts.onChoose(account);
+ } else {
+ switchAccount(host, account.id);
+ }
+ },
+ };
+ }
+
+ const menuItems: MenuItem[] = [];
+
+ // TODO: $iのホストも比較したいけど通常null
+ const accountItems = (await getAccounts().then(accounts => accounts.filter(x => x.user.id !== $i.id))).map(a => createItem(a.host, a.user));
+
+ if (opts.withExtraOperation) {
+ menuItems.push({
+ type: 'link',
+ text: i18n.ts.profile,
+ to: `/@${$i.username}`,
+ avatar: $i,
+ }, {
+ type: 'divider',
+ });
+
+ if (opts.includeCurrentAccount) {
+ menuItems.push(createItem(host, $i));
+ }
+
+ menuItems.push(...accountItems);
+
+ menuItems.push({
+ type: 'parent',
+ icon: 'ti ti-plus',
+ text: i18n.ts.addAccount,
+ children: [{
+ text: i18n.ts.existingAccount,
+ action: () => {
+ getAccountWithSigninDialog().then(res => {
+ if (res != null) {
+ success();
+ }
+ });
+ },
+ }, {
+ text: i18n.ts.createAccount,
+ action: () => {
+ getAccountWithSignupDialog().then(res => {
+ if (res != null) {
+ switchAccount(host, res.id);
+ }
+ });
+ },
+ }],
+ }, {
+ type: 'link',
+ icon: 'ti ti-users',
+ text: i18n.ts.manageAccounts,
+ to: '/settings/accounts',
+ });
+ } else {
+ if (opts.includeCurrentAccount) {
+ menuItems.push(createItem(host, $i));
+ }
+
+ menuItems.push(...accountItems);
+ }
+
+ popupMenu(menuItems, ev.currentTarget ?? ev.target, {
+ align: 'left',
+ });
+}
+
+export function getAccountWithSigninDialog(): Promise<{ id: string, token: string } | null> {
+ return new Promise((resolve) => {
+ const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSigninDialog.vue')), {}, {
+ done: async (res: Misskey.entities.SigninFlowResponse & { finished: true }) => {
+ const user = await fetchAccount(res.i, res.id, true);
+ await addAccount(host, user, res.i);
+ resolve({ id: res.id, token: res.i });
+ },
+ cancelled: () => {
+ resolve(null);
+ },
+ closed: () => {
+ dispose();
+ },
+ });
+ });
+}
+
+export function getAccountWithSignupDialog(): Promise<{ id: string, token: string } | null> {
+ return new Promise((resolve) => {
+ const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSignupDialog.vue')), {}, {
+ done: async (res: Misskey.entities.SignupResponse) => {
+ const user = JSON.parse(JSON.stringify(res));
+ delete user.token;
+ await addAccount(host, user, res.token);
+ resolve({ id: res.id, token: res.token });
+ },
+ cancelled: () => {
+ resolve(null);
+ },
+ closed: () => {
+ dispose();
+ },
+ });
+ });
+}
diff --git a/packages/frontend/src/aiscript/api.ts b/packages/frontend/src/aiscript/api.ts
index 3acc1127c9..e7e396023d 100644
--- a/packages/frontend/src/aiscript/api.ts
+++ b/packages/frontend/src/aiscript/api.ts
@@ -9,7 +9,7 @@ import { url, lang } from '@@/js/config.js';
import { assertStringAndIsIn } from './common.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { miLocalStorage } from '@/local-storage.js';
import { customEmojis } from '@/custom-emojis.js';
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 122aa50ac0..73c4256c4b 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -15,7 +15,7 @@ import components from '@/components/index.js';
import { applyTheme } from '@/theme.js';
import { isDeviceDarkmode } from '@/utility/is-device-darkmode.js';
import { updateI18n, i18n } from '@/i18n.js';
-import { $i, refreshAccount, login } from '@/account.js';
+import { refreshCurrentAccount, login } from '@/accounts.js';
import { store } from '@/store.js';
import { fetchInstance, instance } from '@/instance.js';
import { deviceKind, updateDeviceKind } from '@/utility/device-kind.js';
@@ -29,6 +29,7 @@ import { fetchCustomEmojis } from '@/custom-emojis.js';
import { setupRouter } from '@/router/main.js';
import { createMainRouter } from '@/router/definition.js';
import { prefer } from '@/preferences.js';
+import { $i } from '@/i.js';
export async function common(createVue: () => App<Element>) {
console.info(`Misskey v${version}`);
@@ -38,11 +39,6 @@ export async function common(createVue: () => App<Element>) {
console.info(`vue ${vueVersion}`);
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (window as any).$i = $i;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- (window as any).$store = store;
-
window.addEventListener('error', event => {
console.error(event);
/*
@@ -244,7 +240,7 @@ export async function common(createVue: () => App<Element>) {
console.log('account cache found. refreshing...');
}
- refreshAccount();
+ refreshCurrentAccount();
}
//#endregion
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index be72eeb9e1..64e3a236e8 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -16,7 +16,7 @@ import { i18n } from '@/i18n.js';
import { alert, confirm, popup, post, toast } from '@/os.js';
import { useStream } from '@/stream.js';
import * as sound from '@/utility/sound.js';
-import { $i, signout, updateAccountPartial } from '@/account.js';
+import { $i } from '@/i.js';
import { instance } from '@/instance.js';
import { ColdDeviceStorage, store } from '@/store.js';
import { reactionPicker } from '@/utility/reaction-picker.js';
@@ -32,6 +32,8 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { deckStore } from '@/ui/deck/deck-store.js';
import { launchPlugins } from '@/plugin.js';
import { unisonReload } from '@/utility/unison-reload.js';
+import { updateCurrentAccountPartial } from '@/accounts.js';
+import { signout } from '@/signout.js';
export async function mainBoot() {
const { isClientUpdated, lastVersion } = await common(() => {
@@ -480,11 +482,11 @@ export async function mainBoot() {
// 自分の情報が更新されたとき
main.on('meUpdated', i => {
- updateAccountPartial(i);
+ updateCurrentAccountPartial(i);
});
main.on('readAllNotifications', () => {
- updateAccountPartial({
+ updateCurrentAccountPartial({
hasUnreadNotification: false,
unreadNotificationsCount: 0,
});
@@ -492,39 +494,39 @@ export async function mainBoot() {
main.on('unreadNotification', () => {
const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1;
- updateAccountPartial({
+ updateCurrentAccountPartial({
hasUnreadNotification: true,
unreadNotificationsCount,
});
});
main.on('unreadMention', () => {
- updateAccountPartial({ hasUnreadMentions: true });
+ updateCurrentAccountPartial({ hasUnreadMentions: true });
});
main.on('readAllUnreadMentions', () => {
- updateAccountPartial({ hasUnreadMentions: false });
+ updateCurrentAccountPartial({ hasUnreadMentions: false });
});
main.on('unreadSpecifiedNote', () => {
- updateAccountPartial({ hasUnreadSpecifiedNotes: true });
+ updateCurrentAccountPartial({ hasUnreadSpecifiedNotes: true });
});
main.on('readAllUnreadSpecifiedNotes', () => {
- updateAccountPartial({ hasUnreadSpecifiedNotes: false });
+ updateCurrentAccountPartial({ hasUnreadSpecifiedNotes: false });
});
main.on('readAllAntennas', () => {
- updateAccountPartial({ hasUnreadAntenna: false });
+ updateCurrentAccountPartial({ hasUnreadAntenna: false });
});
main.on('unreadAntenna', () => {
- updateAccountPartial({ hasUnreadAntenna: true });
+ updateCurrentAccountPartial({ hasUnreadAntenna: true });
sound.playMisskeySfx('antenna');
});
main.on('readAllAnnouncements', () => {
- updateAccountPartial({ hasUnreadAnnouncement: false });
+ updateCurrentAccountPartial({ hasUnreadAnnouncement: false });
});
// 個人宛てお知らせが発行されたとき
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue
index 41fd2564d8..582bb137bc 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.vue
+++ b/packages/frontend/src/components/MkAnnouncementDialog.vue
@@ -29,7 +29,8 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n.js';
-import { $i, updateAccountPartial } from '@/account.js';
+import { $i } from '@/i.js';
+import { updateCurrentAccountPartial } from '@/accounts.js';
const props = withDefaults(defineProps<{
announcement: Misskey.entities.Announcement;
@@ -51,7 +52,7 @@ async function ok() {
modal.value?.close();
misskeyApi('i/read-announcement', { announcementId: props.announcement.id });
- updateAccountPartial({
+ updateCurrentAccountPartial({
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
});
}
diff --git a/packages/frontend/src/components/MkAuthConfirm.vue b/packages/frontend/src/components/MkAuthConfirm.vue
index 090c31044e..00bf8e68d9 100644
--- a/packages/frontend/src/components/MkAuthConfirm.vue
+++ b/packages/frontend/src/components/MkAuthConfirm.vue
@@ -117,10 +117,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { ref, computed } from 'vue';
import * as Misskey from 'misskey-js';
-
import MkButton from '@/components/MkButton.vue';
-
-import { $i, getAccounts, getAccountWithSigninDialog, getAccountWithSignupDialog } from '@/account.js';
+import { $i } from '@/i.js';
+import { getAccounts, getAccountWithSigninDialog, getAccountWithSignupDialog } from '@/accounts.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { getProxiedImageUrl } from '@/utility/media-proxy.js';
@@ -158,7 +157,7 @@ async function init() {
const accounts = await getAccounts();
- const accountIdsToFetch = accounts.map(a => a.id).filter(id => !users.value.has(id));
+ const accountIdsToFetch = accounts.map(a => a.user.id).filter(id => !users.value.has(id));
if (accountIdsToFetch.length > 0) {
const usersRes = await misskeyApi('users/show', {
@@ -170,7 +169,7 @@ async function init() {
users.value.set(user.id, {
...user,
- token: accounts.find(a => a.id === user.id)!.token,
+ token: accounts.find(a => a.user.id === user.id)!.token,
});
}
}
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index 5b09ec90dd..2154c08ab3 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import * as Misskey from 'misskey-js';
import { computed } from 'vue';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import number from '@/filters/number.js';
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 3c41d597de..4a89fb30ca 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -38,7 +38,7 @@ import tinycolor from 'tinycolor2';
import { apiUrl } from '@@/js/config.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { getProxiedImageUrl } from '@/utility/media-proxy.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 733d50728e..f02a767186 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -44,7 +44,7 @@ import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import bytes from '@/filters/bytes.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';
import { deviceKind } from '@/utility/device-kind.js';
import { useRouter } from '@/router/supplier.js';
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 384682277e..c0883b1342 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -136,7 +136,7 @@ import { deviceKind } from '@/utility/device-kind.js';
import { i18n } from '@/i18n.js';
import { store } from '@/store.js';
import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { checkReactionPermissions } from '@/utility/check-reaction-permissions.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index a063854520..b62494fa20 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -44,7 +44,7 @@ import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { claimAchievement } from '@/utility/achievements.js';
import { pleaseLogin } from '@/utility/please-login.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index 70777bb89a..3113c6fad6 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -89,7 +89,7 @@ import { Chart } from 'chart.js';
import MkSelect from '@/components/MkSelect.vue';
import MkChart from '@/components/MkChart.vue';
import { useChartTooltip } from '@/use/use-chart-tooltip.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import * as os from '@/os.js';
import { misskeyApiGet } from '@/utility/misskey-api.js';
import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/components/MkMediaAudio.vue b/packages/frontend/src/components/MkMediaAudio.vue
index 33d4d269e7..096c51bbd6 100644
--- a/packages/frontend/src/components/MkMediaAudio.vue
+++ b/packages/frontend/src/components/MkMediaAudio.vue
@@ -98,7 +98,7 @@ import * as os from '@/os.js';
import bytes from '@/filters/bytes.js';
import { hms } from '@/filters/hms.js';
import MkMediaRange from '@/components/MkMediaRange.vue';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import { prefer } from '@/preferences.js';
const props = defineProps<{
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 6029b1e0b6..20ac1a917e 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -60,7 +60,7 @@ import bytes from '@/filters/bytes.js';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 74c1aefc3a..403ec61736 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -121,7 +121,7 @@ import * as os from '@/os.js';
import { exitFullscreen, requestFullscreen } from '@/utility/fullscreen.js';
import hasAudio from '@/utility/media-has-audio.js';
import MkMediaRange from '@/components/MkMediaRange.vue';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import { prefer } from '@/preferences.js';
const props = defineProps<{
diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue
index 419407955f..f2cf33eb65 100644
--- a/packages/frontend/src/components/MkMention.vue
+++ b/packages/frontend/src/components/MkMention.vue
@@ -18,7 +18,7 @@ import { toUnicode } from 'punycode.js';
import { computed } from 'vue';
import { host as localHost } from '@@/js/config.js';
import type { MkABehavior } from '@/components/global/MkA.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { getStaticImageUrl } from '@/utility/media-proxy.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index aa352d5163..a22ad346bf 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -208,7 +208,7 @@ import * as sound from '@/utility/sound.js';
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
import { reactionPicker } from '@/utility/reaction-picker.js';
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js';
import { useNoteCapture } from '@/use/use-note-capture.js';
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index b3f99b702a..dd8d3567b2 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -238,7 +238,7 @@ import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
import * as sound from '@/utility/sound.js';
import { reactionPicker } from '@/utility/reaction-picker.js';
import { extractUrlFromMfm } from '@/utility/extract-url-from-mfm.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { getNoteClipMenu, getNoteMenu, getRenoteMenu } from '@/utility/get-note-menu.js';
import { useNoteCapture } from '@/use/use-note-capture.js';
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index bb7347cd26..4fd1c210cb 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -48,7 +48,7 @@ import MkCwButton from '@/components/MkCwButton.vue';
import { notePage } from '@/filters/note.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { userPage } from '@/filters/user.js';
import { checkWordMute } from '@/utility/check-word-mute.js';
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index 5d096cf92d..b2380a5e0e 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -169,7 +169,7 @@ import { notePage } from '@/filters/note.js';
import { userPage } from '@/filters/user.js';
import { i18n } from '@/i18n.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { infoImageUrl } from '@/instance.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index 4d1787d420..24750012d0 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -44,7 +44,7 @@ import MkInput from '@/components/MkInput.vue';
import MkButton from '@/components/MkButton.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 5e379d08b7..c6958eea77 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -127,7 +127,8 @@ import { store } from '@/store.js';
import MkInfo from '@/components/MkInfo.vue';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
-import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { signinRequired, notesCount, incNotesCount } from '@/i.js';
+import { getAccounts, openAccountMenu as openAccountMenu_ } from '@/accounts.js';
import { uploadFile } from '@/utility/upload.js';
import { deepClone } from '@/utility/clone.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
@@ -837,7 +838,7 @@ async function post(ev?: MouseEvent) {
if (postAccount.value) {
const storedAccounts = await getAccounts();
- token = storedAccounts.find(x => x.id === postAccount.value?.id)?.token;
+ token = storedAccounts.find(x => x.user.id === postAccount.value?.id)?.token;
}
posting.value = true;
diff --git a/packages/frontend/src/components/MkPreview.vue b/packages/frontend/src/components/MkPreview.vue
index 6efd99d14b..d8dfbd1655 100644
--- a/packages/frontend/src/components/MkPreview.vue
+++ b/packages/frontend/src/components/MkPreview.vue
@@ -43,7 +43,7 @@ import MkTextarea from '@/components/MkTextarea.vue';
import MkRadio from '@/components/MkRadio.vue';
import * as os from '@/os.js';
import * as config from '@@/js/config.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const text = ref('');
const flag = ref(true);
diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index 780f8bc6d0..9c37eb5e72 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -42,12 +42,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<script setup lang="ts">
import { ref } from 'vue';
-import { $i, getAccounts } from '@/account.js';
+import { $i } from '@/i.js';
import MkButton from '@/components/MkButton.vue';
import { instance } from '@/instance.js';
import { apiWithDialog, promiseDialog } from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
+import { getAccounts } from '@/accounts.js';
defineProps<{
primary?: boolean;
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index d079e68cde..12a066c710 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -27,7 +27,7 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue';
import * as os from '@/os.js';
import { misskeyApi, misskeyApiGet } from '@/utility/misskey-api.js';
import { useTooltip } from '@/use/use-tooltip.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkReactionEffect from '@/components/MkReactionEffect.vue';
import { claimAchievement } from '@/utility/achievements.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 2dcc87f425..b0fbe3c490 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -67,20 +67,19 @@ SPDX-License-Identifier: AGPL-3.0-only
import { nextTick, onBeforeUnmount, ref, shallowRef, useTemplateRef } from 'vue';
import * as Misskey from 'misskey-js';
import { supported as webAuthnSupported, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
-
import type { AuthenticationPublicKeyCredential } from '@github/webauthn-json/browser-ponyfill';
import type { OpenOnRemoteOptions } from '@/utility/please-login.js';
+import type { PwResponse } from '@/components/MkSignin.password.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { showSuspendedDialog } from '@/utility/show-suspended-dialog.js';
-import { login } from '@/account.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import XInput from '@/components/MkSignin.input.vue';
import XPassword from '@/components/MkSignin.password.vue';
-import type { PwResponse } from '@/components/MkSignin.password.vue';
import XTotp from '@/components/MkSignin.totp.vue';
import XPasskey from '@/components/MkSignin.passkey.vue';
+import { login } from '@/accounts.js';
const emit = defineEmits<{
(ev: 'login', v: Misskey.entities.SigninFlowResponse & { finished: true }): void;
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index 151cd60fb2..78b6722c1e 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -85,13 +85,13 @@ import * as Misskey from 'misskey-js';
import * as config from '@@/js/config.js';
import MkButton from './MkButton.vue';
import MkInput from './MkInput.vue';
-import MkCaptcha from '@/components/MkCaptcha.vue';
import type { Captcha } from '@/components/MkCaptcha.vue';
+import MkCaptcha from '@/components/MkCaptcha.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { login } from '@/account.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
+import { login } from '@/accounts.js';
const props = withDefaults(defineProps<{
autoSet?: boolean;
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 62ae9a048a..e8b740eae2 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -25,7 +25,7 @@ import MkNotes from '@/components/MkNotes.vue';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
import { useStream } from '@/stream.js';
import * as sound from '@/utility/sound.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { instance } from '@/instance.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 31ecb15ab8..bed15031cb 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -55,7 +55,7 @@ import MkButton from './MkButton.vue';
import MkInfo from './MkInfo.vue';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js';
-import { iAmAdmin } from '@/account.js';
+import { iAmAdmin } from '@/i.js';
const props = withDefaults(defineProps<{
title?: string | null;
diff --git a/packages/frontend/src/components/MkTutorialDialog.Note.vue b/packages/frontend/src/components/MkTutorialDialog.Note.vue
index b26a01737e..59e1b096ae 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Note.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Note.vue
@@ -27,7 +27,7 @@ import * as Misskey from 'misskey-js';
import { ref, reactive } from 'vue';
import { i18n } from '@/i18n.js';
import { globalEvents } from '@/events.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkNote from '@/components/MkNote.vue';
const props = defineProps<{
diff --git a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
index f7b60fbc45..8ae6c1ceaa 100644
--- a/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
+++ b/packages/frontend/src/components/MkTutorialDialog.Sensitive.vue
@@ -31,7 +31,7 @@ import MkPostForm from '@/components/MkPostForm.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkNote from '@/components/MkNote.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const emit = defineEmits<{
(ev: 'succeeded'): void;
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index eb189b446b..cff531b2ca 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -39,7 +39,7 @@ import MkFollowButton from '@/components/MkFollowButton.vue';
import number from '@/filters/number.js';
import { userPage } from '@/filters/user.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/utility/isFfVisibleForMe.js';
import { getStaticImageUrl } from '@/utility/media-proxy.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index 8a89b253e3..7e8b1200d5 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -65,7 +65,7 @@ import { getUserMenu } from '@/utility/get-user-menu.js';
import number from '@/filters/number.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/utility/isFfVisibleForMe.js';
import { getStaticImageUrl } from '@/utility/media-proxy.js';
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index e5c6df267b..6bf3eb44dc 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -70,7 +70,7 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { instance } from '@/instance.js';
const emit = defineEmits<{
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
index 14acfd3f89..7a5e5772a4 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -39,7 +39,7 @@ import FormSlot from '@/components/form/slot.vue';
import MkInfo from '@/components/MkInfo.vue';
import { chooseFileFromPc } from '@/utility/select-file.js';
import * as os from '@/os.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index c196519c15..b55069ca25 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -47,7 +47,7 @@ import { instance } from '@/instance.js';
import MkButton from '@/components/MkButton.vue';
import { store } from '@/store.js';
import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
type Ad = (typeof instance)['ads'][number];
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index 9981092dff..20a07e9c28 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -35,7 +35,7 @@ import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import * as sound from '@/utility/sound.js';
import { i18n } from '@/i18n.js';
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
const props = defineProps<{
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index 728e37cf51..69bbd88cb6 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -50,7 +50,8 @@ import type { PageHeaderItem } from '@/types/page-header.js';
import type { PageMetadata } from '@/page.js';
import { globalEvents } from '@/events.js';
import { injectReactiveMetadata } from '@/page.js';
-import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
const props = withDefaults(defineProps<{
overridePageMetadata?: PageMetadata;
diff --git a/packages/frontend/src/i.ts b/packages/frontend/src/i.ts
new file mode 100644
index 0000000000..aa84c6aa61
--- /dev/null
+++ b/packages/frontend/src/i.ts
@@ -0,0 +1,34 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { reactive } from 'vue';
+import * as Misskey from 'misskey-js';
+import { miLocalStorage } from '@/local-storage.js';
+
+// TODO: 他のタブと永続化されたstateを同期
+
+type AccountWithToken = Misskey.entities.MeDetailed & { token: string };
+
+const accountData = miLocalStorage.getItem('account');
+
+// TODO: 外部からはreadonlyに
+export const $i = accountData ? reactive(JSON.parse(accountData) as AccountWithToken) : null;
+
+export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
+export const iAmAdmin = $i != null && $i.isAdmin;
+
+export function signinRequired() {
+ if ($i == null) throw new Error('signin required');
+ return $i;
+}
+
+export let notesCount = $i == null ? 0 : $i.notesCount;
+export function incNotesCount() {
+ notesCount++;
+}
+
+if (_DEV_) {
+ (window as any).$i = $i;
+}
diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts
index 3977edb91b..099339fbee 100644
--- a/packages/frontend/src/local-storage.ts
+++ b/packages/frontend/src/local-storage.ts
@@ -9,7 +9,6 @@ export type Keys = (
'instance' |
'instanceCachedAt' |
'account' |
- 'accounts' |
'latestDonationInfoShownAt' |
'neverShowDonationInfo' |
'neverShowLocalOnlyInfo' |
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index 837f333c9a..c0a6a370fc 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -5,7 +5,7 @@
import { computed, reactive } from 'vue';
import { clearCache } from './utility/clear-cache.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { miLocalStorage } from '@/local-storage.js';
import { openInstanceMenu, openToolsMenu } from '@/ui/_common_/common.js';
import { lookup } from '@/utility/lookup.js';
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 5395429d7f..36dac2954d 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -146,7 +146,7 @@ import { instance } from '@/instance.js';
import * as os from '@/os.js';
import { definePage } from '@/page.js';
import { claimAchievement, claimedAchievements } from '@/utility/achievements.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
const patronsWithIcon = [{
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index d7d526f3ba..b166dfd940 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -44,7 +44,7 @@ import MkInput from '@/components/MkInput.vue';
import MkFoldableSection from '@/components/MkFoldableSection.vue';
import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const customEmojiTags = getCustomEmojiTags();
const q = ref('');
diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue
index 53ce75f9bf..ca2443cc5b 100644
--- a/packages/frontend/src/pages/achievements.vue
+++ b/packages/frontend/src/pages/achievements.vue
@@ -17,7 +17,7 @@ import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
import MkAchievements from '@/components/MkAchievements.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { claimAchievement } from '@/utility/achievements.js';
let timer: number | null;
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 0af28e94fa..9274b90892 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -86,7 +86,7 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { iAmAdmin, iAmModerator } from '@/account.js';
+import { iAmAdmin, iAmModerator } from '@/i.js';
const tab = ref('overview');
const file = ref<Misskey.entities.DriveFile | null>(null);
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 2efae999ce..ff1d5e64d3 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -233,7 +233,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { acct } from '@/filters/user.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
-import { iAmAdmin, $i, iAmModerator } from '@/account.js';
+import { iAmAdmin, $i, iAmModerator } from '@/i.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
import MkPagination from '@/components/MkPagination.vue';
diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
index 977bbe0b47..6562610b12 100644
--- a/packages/frontend/src/pages/announcement.vue
+++ b/packages/frontend/src/pages/announcement.vue
@@ -55,8 +55,9 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { $i, updateAccountPartial } from '@/account.js';
+import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
+import { updateCurrentAccountPartial } from '@/accounts.js';
const props = defineProps<{
announcementId: string;
@@ -90,7 +91,7 @@ async function read(target: Misskey.entities.Announcement): Promise<void> {
target.isRead = true;
await misskeyApi('i/read-announcement', { announcementId: target.id });
if ($i) {
- updateAccountPartial({
+ updateCurrentAccountPartial({
unreadAnnouncements: $i.unreadAnnouncements.filter((a: { id: string; }) => a.id !== target.id),
});
}
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 13f28d9b35..2387ac728b 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -56,7 +56,8 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { $i, updateAccountPartial } from '@/account.js';
+import { $i } from '@/i.js';
+import { updateCurrentAccountPartial } from '@/accounts.js';
const paginationCurrent = {
endpoint: 'announcements' as const,
@@ -94,7 +95,7 @@ async function read(target) {
return a;
});
misskeyApi('i/read-announcement', { announcementId: target.id });
- updateAccountPartial({
+ updateCurrentAccountPartial({
unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== target.id),
});
}
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 8b0fde4a25..e4699379f0 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -47,9 +47,10 @@ import * as Misskey from 'misskey-js';
import XForm from './auth.form.vue';
import MkSignin from '@/components/MkSignin.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i, login } from '@/account.js';
+import { $i } from '@/i.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
+import { login } from '@/accounts.js';
const props = defineProps<{
token: string;
diff --git a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
index a3c5a36614..884429dfeb 100644
--- a/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
+++ b/packages/frontend/src/pages/avatar-decoration-edit-dialog.vue
@@ -73,7 +73,7 @@ import { i18n } from '@/i18n.js';
import MkSwitch from '@/components/MkSwitch.vue';
import MkRolePreview from '@/components/MkRolePreview.vue';
import MkTextarea from '@/components/MkTextarea.vue';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index eb1015b19e..b84b9efc1a 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { ref, computed, defineAsyncComponent } from 'vue';
import * as Misskey from 'misskey-js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index a774aa6e44..c5951f0e13 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -82,7 +82,7 @@ import MkTimeline from '@/components/MkTimeline.vue';
import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { deviceKind } from '@/utility/device-kind.js';
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 9765ebf216..590a506a55 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -36,7 +36,7 @@ import * as Misskey from 'misskey-js';
import { url } from '@@/js/config.js';
import type { MenuItem } from '@/types/menu.js';
import MkNotes from '@/components/MkNotes.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
diff --git a/packages/frontend/src/pages/drop-and-fusion.game.vue b/packages/frontend/src/pages/drop-and-fusion.game.vue
index f760aca8ae..364006e9ad 100644
--- a/packages/frontend/src/pages/drop-and-fusion.game.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.game.vue
@@ -208,7 +208,7 @@ import { claimAchievement } from '@/utility/achievements.js';
import { store } from '@/store.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import * as sound from '@/utility/sound.js';
import MkRange from '@/components/MkRange.vue';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index 35a240b9ba..bedb0b64f9 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -22,7 +22,7 @@ import { misskeyApiGet } from '@/utility/misskey-api.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const props = defineProps<{
emoji: Misskey.entities.EmojiSimple;
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 6bce6689d4..08ac913958 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -80,7 +80,7 @@ import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
import MkFolder from '@/components/MkFolder.vue';
import MkCode from '@/components/MkCode.vue';
import { prefer } from '@/preferences.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { isSupportShare } from '@/utility/navigator.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { pleaseLogin } from '@/utility/please-login.js';
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index bd48b882d2..7e496f522d 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -54,7 +54,7 @@ import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { infoImageUrl } from '@/instance.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 56ddb820cf..eb01aadbcc 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -77,7 +77,7 @@ import MkFollowButton from '@/components/MkFollowButton.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { prefer } from '@/preferences.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { isSupportShare } from '@/utility/navigator.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { useRouter } from '@/router/supplier.js';
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index eddeb4aba9..c4aed8d6df 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -148,7 +148,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import number from '@/filters/number.js';
-import { iAmModerator, iAmAdmin } from '@/account.js';
+import { iAmModerator, iAmAdmin } from '@/i.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index 8369927d85..352e1d9386 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -45,7 +45,7 @@ import type { Paging } from '@/components/MkPagination.vue';
import MkInviteCode from '@/components/MkInviteCode.vue';
import { definePage } from '@/page.js';
import { serverErrorImageUrl, instance } from '@/instance.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
const currentInviteLimit = ref<null | number>(null);
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index 0bc9b3f3c2..cc701cb16b 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -37,7 +37,7 @@ import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { userListsCache } from '@/cache.js';
import { infoImageUrl } from '@/instance.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index fdee890cfd..6481c0da0c 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -66,7 +66,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkInput from '@/components/MkInput.vue';
import { userListsCache } from '@/cache.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import MkPagination from '@/components/MkPagination.vue';
import { mainRouter } from '@/router/main.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index 6f53cba806..fb83993fee 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -65,7 +65,7 @@ import { prefer } from '@/preferences.js';
import { pleaseLogin } from '@/utility/please-login.js';
import { getAppearNote } from '@/utility/get-appear-note.js';
import { serverContext, assertServerContext } from '@/server-context.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
// contextは非ログイン状態の情報しかないためログイン時は利用できない
const CTX_NOTE = !$i && assertServerContext(serverContext, 'note') ? serverContext.note : null;
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index ed701ed3c0..e2f6084252 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -75,7 +75,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { selectFile } from '@/utility/select-file.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { mainRouter } from '@/router/main.js';
import { getPageBlockList } from '@/pages/page-editor/common.js';
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 1c288442b5..00c664d2a0 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -115,7 +115,7 @@ import MkPagePreview from '@/components/MkPagePreview.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { deepClone } from '@/utility/clone.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { isSupportShare } from '@/utility/navigator.js';
import { instance } from '@/instance.js';
import { getStaticImageUrl } from '@/utility/media-proxy.js';
diff --git a/packages/frontend/src/pages/reversi/game.board.vue b/packages/frontend/src/pages/reversi/game.board.vue
index 71dd220cfe..ef9cc242c6 100644
--- a/packages/frontend/src/pages/reversi/game.board.vue
+++ b/packages/frontend/src/pages/reversi/game.board.vue
@@ -150,7 +150,7 @@ import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { deepClone } from '@/utility/clone.js';
import { useInterval } from '@@/js/use-interval.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { url } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { misskeyApi } from '@/utility/misskey-api.js';
diff --git a/packages/frontend/src/pages/reversi/game.setting.vue b/packages/frontend/src/pages/reversi/game.setting.vue
index 03b75f89ae..2715b70b95 100644
--- a/packages/frontend/src/pages/reversi/game.setting.vue
+++ b/packages/frontend/src/pages/reversi/game.setting.vue
@@ -114,7 +114,7 @@ import { computed, watch, ref, onMounted, shallowRef, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import * as Reversi from 'misskey-reversi';
import { i18n } from '@/i18n.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { deepClone } from '@/utility/clone.js';
import MkButton from '@/components/MkButton.vue';
import MkRadios from '@/components/MkRadios.vue';
diff --git a/packages/frontend/src/pages/reversi/game.vue b/packages/frontend/src/pages/reversi/game.vue
index 053ec2aa08..662df00d9b 100644
--- a/packages/frontend/src/pages/reversi/game.vue
+++ b/packages/frontend/src/pages/reversi/game.vue
@@ -17,7 +17,7 @@ import GameBoard from './game.board.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
import { useStream } from '@/stream.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { useRouter } from '@/router/supplier.js';
import * as os from '@/os.js';
import { url } from '@@/js/config.js';
diff --git a/packages/frontend/src/pages/reversi/index.vue b/packages/frontend/src/pages/reversi/index.vue
index ff2e7e922f..d66ff8db05 100644
--- a/packages/frontend/src/pages/reversi/index.vue
+++ b/packages/frontend/src/pages/reversi/index.vue
@@ -113,7 +113,7 @@ import { useStream } from '@/stream.js';
import MkButton from '@/components/MkButton.vue';
import MkFolder from '@/components/MkFolder.vue';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkPagination from '@/components/MkPagination.vue';
import { useRouter } from '@/router/supplier.js';
import * as os from '@/os.js';
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index ed5cd23b23..b0d3b5bbd2 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -66,7 +66,7 @@ import MkTextarea from '@/components/MkTextarea.vue';
import MkCodeEditor from '@/components/MkCodeEditor.vue';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { registerAsUiLib } from '@/aiscript/ui.js';
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index bfe56e793b..4801e9bc27 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -114,7 +114,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { computed, ref, shallowRef, toRef } from 'vue';
import type * as Misskey from 'misskey-js';
import type { Paging } from '@/components/MkPagination.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { host as localHost } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 9093ffd7a9..41a2535813 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -117,7 +117,7 @@ import MkFolder from '@/components/MkFolder.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkLink from '@/components/MkLink.vue';
import { confetti } from '@/utility/confetti.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 806599e801..20d1b0fe0f 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -92,8 +92,9 @@ import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkLink from '@/components/MkLink.vue';
import * as os from '@/os.js';
-import { signinRequired, updateAccountPartial } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { i18n } from '@/i18n.js';
+import { updateCurrentAccountPartial } from '@/accounts.js';
const $i = signinRequired();
@@ -131,7 +132,7 @@ async function unregisterTOTP(): Promise<void> {
password: auth.result.password,
token: auth.result.token,
}).then(res => {
- updateAccountPartial({
+ updateCurrentAccountPartial({
twoFactorEnabled: false,
});
}).catch(error => {
diff --git a/packages/frontend/src/pages/settings/account-data.vue b/packages/frontend/src/pages/settings/account-data.vue
index ed5fe48821..14bea577a3 100644
--- a/packages/frontend/src/pages/settings/account-data.vue
+++ b/packages/frontend/src/pages/settings/account-data.vue
@@ -167,7 +167,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { selectFile } from '@/utility/select-file.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 2cf65be2d0..749ae5147f 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -4,80 +4,51 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
-<div class="">
- <FormSuspense :p="init">
- <div class="_gaps">
- <div class="_buttons">
- <MkButton primary @click="addAccount"><i class="ti ti-plus"></i> {{ i18n.ts.addAccount }}</MkButton>
- <MkButton @click="init"><i class="ti ti-refresh"></i> {{ i18n.ts.reloadAccountsList }}</MkButton>
- </div>
-
- <template v-for="[id, user] in accounts">
- <MkUserCardMini v-if="user != null" :key="user.id" :user="user" :class="$style.user" @click.prevent="menu(user, $event)"/>
- <button v-else v-panel class="_button" :class="$style.unknownUser" @click="menu(id, $event)">
- <div :class="$style.unknownUserAvatarMock"><i class="ti ti-user-question"></i></div>
- <div>
- <div :class="$style.unknownUserTitle">{{ i18n.ts.unknown }}</div>
- <div :class="$style.unknownUserSub">ID: <span class="_monospace">{{ id }}</span></div>
- </div>
- </button>
- </template>
+<SearchMarker path="/settings/accounts" :label="i18n.ts.accounts" :keywords="['accounts']" icon="ti ti-users">
+ <div class="_gaps">
+ <div class="_buttons">
+ <MkButton primary @click="addAccount"><i class="ti ti-plus"></i> {{ i18n.ts.addAccount }}</MkButton>
+ <!--<MkButton @click="refreshAllAccounts"><i class="ti ti-refresh"></i></MkButton>-->
</div>
- </FormSuspense>
-</div>
+
+ <MkUserCardMini v-for="x in accounts" :key="x[0] + x[1].id" :user="x[1]" :class="$style.user" @click.prevent="menu(x[0], x[1], $event)"/>
+ </div>
+</SearchMarker>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue';
import * as Misskey from 'misskey-js';
-import FormSuspense from '@/components/form/suspense.vue';
+import type { MenuItem } from '@/types/menu.js';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { getAccounts, removeAccount as _removeAccount, login, $i, getAccountWithSigninDialog, getAccountWithSignupDialog } from '@/account.js';
+import { $i } from '@/i.js';
+import { switchAccount, removeAccount, login, getAccountWithSigninDialog, getAccountWithSignupDialog } from '@/accounts.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import type { MenuItem } from '@/types/menu.js';
+import { prefer } from '@/preferences.js';
-const storedAccounts = ref<{ id: string, token: string }[] | null>(null);
-const accounts = ref(new Map<string, Misskey.entities.UserDetailed | null>());
+const accounts = prefer.r.accounts;
-const init = async () => {
- getAccounts().then(accounts => {
- storedAccounts.value = accounts.filter(x => x.id !== $i!.id);
-
- return misskeyApi('users/show', {
- userIds: storedAccounts.value.map(x => x.id),
- });
- }).then(response => {
- if (storedAccounts.value == null) return;
- accounts.value = new Map(storedAccounts.value.map(x => [x.id, response.find((y: Misskey.entities.UserDetailed) => y.id === x.id) ?? null]));
- });
-};
+function refreshAllAccounts() {
+ // TODO
+}
-function menu(account: Misskey.entities.UserDetailed | string, ev: MouseEvent) {
+function menu(host: string, account: Misskey.entities.UserDetailed, ev: MouseEvent) {
let menu: MenuItem[];
- if (typeof account === 'string') {
- menu = [{
- text: i18n.ts.logout,
- icon: 'ti ti-trash',
- danger: true,
- action: () => removeAccount(account),
- }];
- } else {
- menu = [{
- text: i18n.ts.switch,
- icon: 'ti ti-switch-horizontal',
- action: () => switchAccount(account.id),
- }, {
- text: i18n.ts.logout,
- icon: 'ti ti-trash',
- danger: true,
- action: () => removeAccount(account.id),
- }];
- }
+ menu = [{
+ text: i18n.ts.switch,
+ icon: 'ti ti-switch-horizontal',
+ action: () => switchAccount(host, account.id),
+ }, {
+ text: i18n.ts.logout,
+ icon: 'ti ti-trash',
+ danger: true,
+ action: () => removeAccount(host, account.id),
+ }];
os.popupMenu(menu, ev.currentTarget ?? ev.target);
}
@@ -92,16 +63,10 @@ function addAccount(ev: MouseEvent) {
}], ev.currentTarget ?? ev.target);
}
-async function removeAccount(id: string) {
- await _removeAccount(id);
- accounts.value.delete(id);
-}
-
function addExistingAccount() {
getAccountWithSigninDialog().then((res) => {
if (res != null) {
os.success();
- init();
}
});
}
@@ -109,21 +74,11 @@ function addExistingAccount() {
function createAccount() {
getAccountWithSignupDialog().then((res) => {
if (res != null) {
- switchAccountWithToken(res.token);
+ login(res.token);
}
});
}
-async function switchAccount(id: string) {
- const fetchedAccounts = await getAccounts();
- const token = fetchedAccounts.find(x => x.id === id)!.token;
- switchAccountWithToken(token);
-}
-
-function switchAccountWithToken(token: string) {
- login(token);
-}
-
const headerActions = computed(() => []);
const headerTabs = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
index 3c9914b4e2..6b3bb1b513 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<script lang="ts" setup>
import { computed } from 'vue';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 40542ad5b2..e4803eda2e 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -51,7 +51,7 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import { i18n } from '@/i18n.js';
import MkRange from '@/components/MkRange.vue';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index ba25eee175..91549e5240 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -54,7 +54,7 @@ import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import MkInfo from '@/components/MkInfo.vue';
import { definePage } from '@/page.js';
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 34941d5af0..0b25ee5e37 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -104,7 +104,7 @@ import bytes from '@/filters/bytes.js';
import MkChart from '@/components/MkChart.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { prefer } from '@/preferences.js';
import MkPreferenceContainer from '@/components/MkPreferenceContainer.vue';
import MkFeatureBanner from '@/components/MkFeatureBanner.vue';
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index 0cbda44882..10e2a000d4 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -67,7 +67,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
import MkDisableSection from '@/components/MkDisableSection.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 0579b6d14a..ba5957f06a 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -38,7 +38,7 @@ import type { SuperMenuDef } from '@/components/MkSuperMenu.vue';
import { i18n } from '@/i18n.js';
import MkInfo from '@/components/MkInfo.vue';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
-import { signout, $i } from '@/account.js';
+import { $i } from '@/i.js';
import { clearCache } from '@/utility/clear-cache.js';
import { instance } from '@/instance.js';
import { definePage, provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
@@ -47,6 +47,7 @@ import { useRouter } from '@/router/supplier.js';
import { searchIndexes } from '@/utility/autogen/settings-search-index.js';
import { enableAutoBackup, getPreferencesProfileMenu } from '@/preferences/utility.js';
import { store } from '@/store.js';
+import { signout } from '@/signout.js';
const SETTING_INDEX = searchIndexes; // TODO: lazy load
diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue
index 60386da545..260e390b51 100644
--- a/packages/frontend/src/pages/settings/migration.vue
+++ b/packages/frontend/src/pages/settings/migration.vue
@@ -68,7 +68,7 @@ import MkUserInfo from '@/components/MkUserInfo.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { unisonReload } from '@/utility/unison-reload.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/settings/mute-block.instance-mute.vue b/packages/frontend/src/pages/settings/mute-block.instance-mute.vue
index 52e1937663..1c942e715a 100644
--- a/packages/frontend/src/pages/settings/mute-block.instance-mute.vue
+++ b/packages/frontend/src/pages/settings/mute-block.instance-mute.vue
@@ -19,7 +19,7 @@ import { ref, watch } from 'vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkButton from '@/components/MkButton.vue';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index a5ab7caf99..ce762af071 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -188,7 +188,7 @@ import { definePage } from '@/page.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import * as os from '@/os.js';
import { instance, infoImageUrl } from '@/instance.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import MkInfo from '@/components/MkInfo.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 49910cdf4a..785fcdfbce 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -75,7 +75,7 @@ import FormSection from '@/components/form/section.vue';
import MkFolder from '@/components/MkFolder.vue';
import MkSwitch from '@/components/MkSwitch.vue';
import * as os from '@/os.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index 62b0f5c941..27fb743cb2 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -127,13 +127,14 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { signout, signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { reloadAsk } from '@/utility/reload-ask.js';
import FormSection from '@/components/form/section.vue';
import { prefer } from '@/preferences.js';
import MkRolePreview from '@/components/MkRolePreview.vue';
+import { signout } from '@/signout.js';
const $i = signinRequired();
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index edc750c295..b73f781b9c 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -185,7 +185,7 @@ import MkFolder from '@/components/MkFolder.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { definePage } from '@/page.js';
import FormSlot from '@/components/form/slot.vue';
import { formatDateTimeString } from '@/utility/format-time-string.js';
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index 1d85ba7834..b12ba9fe93 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -164,7 +164,7 @@ import FormSlot from '@/components/form/slot.vue';
import { selectFile } from '@/utility/select-file.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
-import { signinRequired } from '@/account.js';
+import { signinRequired } from '@/i.js';
import { langmap } from '@/utility/langmap.js';
import { definePage } from '@/page.js';
import { claimAchievement } from '@/utility/achievements.js';
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index c3f1b4b18e..e37f05f1be 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -28,10 +28,10 @@ SPDX-License-Identifier: AGPL-3.0-only
import { ref } from 'vue';
import MkButton from '@/components/MkButton.vue';
import MkAnimBg from '@/components/MkAnimBg.vue';
-import { login } from '@/account.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
+import { login } from '@/accounts.js';
const submitting = ref(false);
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 1af69d82db..868c64d06d 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -25,7 +25,7 @@ import MkNotes from '@/components/MkNotes.vue';
import MkButton from '@/components/MkButton.vue';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { store } from '@/store.js';
import * as os from '@/os.js';
import { genEmbedCode } from '@/utility/get-embed-code.js';
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index 5ee42cc1ec..45d8c96ab7 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -86,7 +86,7 @@ import MkButton from '@/components/MkButton.vue';
import MkCodeEditor from '@/components/MkCodeEditor.vue';
import MkTextarea from '@/components/MkTextarea.vue';
import MkFolder from '@/components/MkFolder.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { applyTheme } from '@/theme.js';
import * as os from '@/os.js';
import { store } from '@/store.js';
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 0ac6979b05..68f4b7a26d 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -48,7 +48,7 @@ import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { definePage } from '@/page.js';
import { antennasCache, userListsCache, favoritedChannelsCache } from '@/cache.js';
import { deviceKind } from '@/utility/device-kind.js';
diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue
index b78ac2dc17..8f13e959e1 100644
--- a/packages/frontend/src/pages/user/achievements.vue
+++ b/packages/frontend/src/pages/user/achievements.vue
@@ -14,7 +14,7 @@ import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import MkAchievements from '@/components/MkAchievements.vue';
import { claimAchievement } from '@/utility/achievements.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const props = defineProps<{
user: Misskey.entities.User;
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 6450f1e077..149481f99b 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -176,7 +176,7 @@ import number from '@/filters/number.js';
import { userPage } from '@/filters/user.js';
import * as os from '@/os.js';
import { i18n } from '@/i18n.js';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import { dateString } from '@/filters/date.js';
import { confetti } from '@/utility/confetti.js';
import { misskeyApi } from '@/utility/misskey-api.js';
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index b5127de390..16413a55cc 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -38,7 +38,7 @@ import { acct as getAcct } from '@/filters/user.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue';
import { serverContext, assertServerContext } from '@/server-context.js';
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 939ca934e8..d9e3ca9966 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -45,9 +45,9 @@ import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { login } from '@/account.js';
import { i18n } from '@/i18n.js';
import MkAnimBg from '@/components/MkAnimBg.vue';
+import { login } from '@/accounts.js';
const username = ref('');
const password = ref('');
diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts
index f55b1e93cf..3ebf2ab4e4 100644
--- a/packages/frontend/src/pizzax.ts
+++ b/packages/frontend/src/pizzax.ts
@@ -8,7 +8,7 @@
import { onUnmounted, ref, watch } from 'vue';
import { BroadcastChannel } from 'broadcast-channel';
import type { Ref } from 'vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { get, set } from '@/utility/idb-proxy.js';
import { store } from '@/store.js';
diff --git a/packages/frontend/src/preferences.ts b/packages/frontend/src/preferences.ts
index 8785acd7d1..f9e6ab2a75 100644
--- a/packages/frontend/src/preferences.ts
+++ b/packages/frontend/src/preferences.ts
@@ -9,7 +9,7 @@ import { cloudBackup } from '@/preferences/utility.js';
import { miLocalStorage } from '@/local-storage.js';
import { isSameScope, PreferencesManager } from '@/preferences/manager.js';
import { store } from '@/store.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
const TAB_ID = uuid();
diff --git a/packages/frontend/src/preferences/def.ts b/packages/frontend/src/preferences/def.ts
index eb3d6eeac4..e460359acd 100644
--- a/packages/frontend/src/preferences/def.ts
+++ b/packages/frontend/src/preferences/def.ts
@@ -32,6 +32,10 @@ export type SoundStore = {
// NOTE: デフォルト値は他の設定の状態に依存してはならない(依存していた場合、ユーザーがその設定項目単体で「初期値にリセット」した場合不具合の原因になる)
export const PREF_DEF = {
+ accounts: {
+ default: [] as [host: string, user: Misskey.entities.User][],
+ },
+
pinnedUserLists: {
accountDependent: true,
default: [] as Misskey.entities.UserList[],
diff --git a/packages/frontend/src/preferences/manager.ts b/packages/frontend/src/preferences/manager.ts
index b053cadacb..fad0226b6e 100644
--- a/packages/frontend/src/preferences/manager.ts
+++ b/packages/frontend/src/preferences/manager.ts
@@ -9,7 +9,7 @@ import { host, version } from '@@/js/config.js';
import { PREF_DEF } from './def.js';
import type { Ref, WritableComputedRef } from 'vue';
import type { MenuItem } from '@/types/menu.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
diff --git a/packages/frontend/src/preferences/utility.ts b/packages/frontend/src/preferences/utility.ts
index c37dbcf96b..bf3dfa157f 100644
--- a/packages/frontend/src/preferences/utility.ts
+++ b/packages/frontend/src/preferences/utility.ts
@@ -12,7 +12,7 @@ import { miLocalStorage } from '@/local-storage.js';
import { prefer } from '@/preferences.js';
import * as os from '@/os.js';
import { store } from '@/store.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { misskeyApi } from '@/utility/misskey-api.js';
import { unisonReload } from '@/utility/unison-reload.js';
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index 73920766d7..9a81032cc8 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -7,7 +7,7 @@ import { defineAsyncComponent } from 'vue';
import type { AsyncComponentLoader } from 'vue';
import type { IRouter, RouteDef } from '@/nirax.js';
import { Router } from '@/nirax.js';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import MkLoading from '@/pages/_loading_.vue';
import MkError from '@/pages/_error_.vue';
diff --git a/packages/frontend/src/signout.ts b/packages/frontend/src/signout.ts
new file mode 100644
index 0000000000..8e90552546
--- /dev/null
+++ b/packages/frontend/src/signout.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { defineAsyncComponent, ref } from 'vue';
+import { apiUrl, host } from '@@/js/config.js';
+import { defaultMemoryStorage } from '@/memory-storage';
+import { i18n } from '@/i18n.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { waiting, popup, popupMenu, success, alert } from '@/os.js';
+import { unisonReload, reloadChannel } from '@/utility/unison-reload.js';
+import { prefer } from '@/preferences.js';
+import { store } from '@/store.js';
+import { $i } from '@/i.js';
+
+export async function signout() {
+ if (!$i) return;
+
+ defaultMemoryStorage.clear();
+
+ waiting();
+ miLocalStorage.removeItem('account');
+
+ // TODO: preferencesも削除
+
+ //#region Remove service worker registration
+ try {
+ if (navigator.serviceWorker.controller) {
+ const registration = await navigator.serviceWorker.ready;
+ const push = await registration.pushManager.getSubscription();
+ if (push) {
+ await window.fetch(`${apiUrl}/sw/unregister`, {
+ method: 'POST',
+ body: JSON.stringify({
+ i: $i.token,
+ endpoint: push.endpoint,
+ }),
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ });
+ }
+ }
+
+ await navigator.serviceWorker.getRegistrations()
+ .then(registrations => {
+ return Promise.all(registrations.map(registration => registration.unregister()));
+ });
+ } catch (err) {}
+ //#endregion
+
+ unisonReload('/');
+}
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 9a61e63d0e..ffbd8d10ef 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -104,6 +104,10 @@ export const store = markRaw(new Storage('base', {
where: 'deviceAccount',
default: {} as Record<string, string>, // plugin id, token
},
+ accountTokens: {
+ where: 'device',
+ default: {} as Record<string, string>, // host/userId, token
+ },
enablePreferencesAutoCloudBackup: {
where: 'device',
diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts
index e194e96a7f..c97d7d4071 100644
--- a/packages/frontend/src/stream.ts
+++ b/packages/frontend/src/stream.ts
@@ -5,7 +5,7 @@
import * as Misskey from 'misskey-js';
import { markRaw } from 'vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { wsOrigin } from '@@/js/config.js';
// TODO: No WebsocketモードでStreamMockが使えそう
//import { StreamMock } from '@/utility/stream-mock.js';
diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts
index 5d09ec27f9..2ae5d8730e 100644
--- a/packages/frontend/src/theme-store.ts
+++ b/packages/frontend/src/theme-store.ts
@@ -5,7 +5,7 @@
import type { Theme } from '@/theme.js';
import { getBuiltinThemes } from '@/theme.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { prefer } from '@/preferences.js';
export function getThemes(): Theme[] {
diff --git a/packages/frontend/src/timelines.ts b/packages/frontend/src/timelines.ts
index 94eda3545e..a39ccd481d 100644
--- a/packages/frontend/src/timelines.ts
+++ b/packages/frontend/src/timelines.ts
@@ -3,7 +3,7 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { instance } from '@/instance.js';
export const basicTimelineTypes = [
diff --git a/packages/frontend/src/ui/_common_/PreferenceRestore.vue b/packages/frontend/src/ui/_common_/PreferenceRestore.vue
index c70b82cd0e..5fd9f5e44b 100644
--- a/packages/frontend/src/ui/_common_/PreferenceRestore.vue
+++ b/packages/frontend/src/ui/_common_/PreferenceRestore.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { hideRestoreBackupSuggestion, restoreFromCloudBackup } from '@/preferences/utility.js';
diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue
index d153dc8726..f9af8e1ee7 100644
--- a/packages/frontend/src/ui/_common_/announcements.vue
+++ b/packages/frontend/src/ui/_common_/announcements.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index 8e5ba8927a..819e1fa42f 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -9,7 +9,7 @@ import * as os from '@/os.js';
import { instance } from '@/instance.js';
import { host } from '@@/js/config.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
function toolsMenuItems(): MenuItem[] {
return [{
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index e218cd8c62..a39a4ee86b 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -53,7 +53,7 @@ import { popups } from '@/os.js';
import { pendingApiRequestsCount } from '@/utility/misskey-api.js';
import { uploads } from '@/utility/upload.js';
import * as sound from '@/utility/sound.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { useStream } from '@/stream.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index 698e9d8d47..2fbc9ab4b3 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -53,10 +53,11 @@ import { computed, defineAsyncComponent, toRef } from 'vue';
import { openInstanceMenu } from './common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
-import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { prefer } from '@/preferences.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
const menu = toRef(prefer.s, 'menu');
const otherMenuItemIndicated = computed(() => {
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index 234972e76d..1810ec1743 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -93,13 +93,14 @@ import { computed, defineAsyncComponent, ref, watch } from 'vue';
import { openInstanceMenu } from './common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
-import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
import { store } from '@/store.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { getHTMLElementOrNull } from '@/utility/get-dom-node-or-null.js';
import { useRouter } from '@/router/supplier.js';
import { prefer } from '@/preferences.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
const router = useRouter();
diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts
index df392c6532..ae61e497b5 100644
--- a/packages/frontend/src/ui/_common_/sw-inject.ts
+++ b/packages/frontend/src/ui/_common_/sw-inject.ts
@@ -5,10 +5,11 @@
import { post } from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i, login } from '@/account.js';
+import { $i } from '@/i.js';
import { getAccountFromId } from '@/utility/get-account-from-id.js';
import { deepClone } from '@/utility/clone.js';
import { mainRouter } from '@/router/main.js';
+import { login } from '@/accounts.js';
export function swInject() {
navigator.serviceWorker.addEventListener('message', async ev => {
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index 39b40754ff..1e8a342977 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -51,12 +51,12 @@ import { computed, defineAsyncComponent, onMounted, ref } from 'vue';
import { openInstanceMenu } from './_common_/common.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
-import { openAccountMenu as openAccountMenu_, $i } from '@/account.js';
import MkButton from '@/components/MkButton.vue';
-import { store } from '@/store.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
const WINDOW_THRESHOLD = 1400;
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 259aad7401..096ea0d4cf 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -54,7 +54,6 @@ import { openInstanceMenu } from './_common_/common.js';
// import { host } from '@@/js/config.js';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
-import { openAccountMenu as openAccountMenu_, $i } from '@/account.js';
import MkButton from '@/components/MkButton.vue';
// import { StickySidebar } from '@/utility/sticky-sidebar.js';
// import { mainRouter } from '@/router.js';
@@ -63,6 +62,8 @@ import { store } from '@/store.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { prefer } from '@/preferences.js';
+import { openAccountMenu as openAccountMenu_ } from '@/accounts.js';
+import { $i } from '@/i.js';
const WINDOW_THRESHOLD = 1400;
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 337b0dac94..a5db4031e2 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -100,7 +100,7 @@ import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { deviceKind } from '@/utility/device-kind.js';
import { prefer } from '@/preferences.js';
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index cb93b20c24..29248b2720 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -108,7 +108,7 @@ import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
import * as os from '@/os.js';
import { navbarItemDef } from '@/navbar.js';
import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { provideMetadataReceiver, provideReactiveMetadata } from '@/page.js';
import { deviceKind } from '@/utility/device-kind.js';
import { miLocalStorage } from '@/local-storage.js';
diff --git a/packages/frontend/src/use/use-note-capture.ts b/packages/frontend/src/use/use-note-capture.ts
index 0bc10e90e4..0de2dbb3c5 100644
--- a/packages/frontend/src/use/use-note-capture.ts
+++ b/packages/frontend/src/use/use-note-capture.ts
@@ -7,7 +7,7 @@ import { onUnmounted } from 'vue';
import type { Ref, ShallowRef } from 'vue';
import * as Misskey from 'misskey-js';
import { useStream } from '@/stream.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
export function useNoteCapture(props: {
rootEl: ShallowRef<HTMLElement | undefined>;
diff --git a/packages/frontend/src/utility/achievements.ts b/packages/frontend/src/utility/achievements.ts
index 3025a985ba..f6ab587ae1 100644
--- a/packages/frontend/src/utility/achievements.ts
+++ b/packages/frontend/src/utility/achievements.ts
@@ -4,7 +4,7 @@
*/
import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
export const ACHIEVEMENT_TYPES = [
'notes1',
diff --git a/packages/frontend/src/utility/autogen/settings-search-index.ts b/packages/frontend/src/utility/autogen/settings-search-index.ts
index ebc67eb58d..e768d8a5ae 100644
--- a/packages/frontend/src/utility/autogen/settings-search-index.ts
+++ b/packages/frontend/src/utility/autogen/settings-search-index.ts
@@ -815,6 +815,13 @@ export const searchIndexes: SearchIndexItem[] = [
icon: 'ti ti-sparkles',
},
{
+ id: 'zK6posor9',
+ label: i18n.ts.accounts,
+ keywords: ['accounts'],
+ path: '/settings/accounts',
+ icon: 'ti ti-users',
+ },
+ {
id: '330Q4mf8E',
children: [
{
diff --git a/packages/frontend/src/utility/check-permissions.ts b/packages/frontend/src/utility/check-permissions.ts
index ed86529d5b..2de8fd2cd1 100644
--- a/packages/frontend/src/utility/check-permissions.ts
+++ b/packages/frontend/src/utility/check-permissions.ts
@@ -4,7 +4,7 @@
*/
import { instance } from '@/instance.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
export const notesSearchAvailable = (
// FIXME: instance.policies would be null in Vitest
diff --git a/packages/frontend/src/utility/get-note-menu.ts b/packages/frontend/src/utility/get-note-menu.ts
index 74eb326c4c..d2026de0b6 100644
--- a/packages/frontend/src/utility/get-note-menu.ts
+++ b/packages/frontend/src/utility/get-note-menu.ts
@@ -9,7 +9,7 @@ import { url } from '@@/js/config.js';
import { claimAchievement } from './achievements.js';
import type { Ref, ShallowRef } from 'vue';
import type { MenuItem } from '@/types/menu.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import * as os from '@/os.js';
diff --git a/packages/frontend/src/utility/get-user-menu.ts b/packages/frontend/src/utility/get-user-menu.ts
index 1b9b0eac95..b89c7537e2 100644
--- a/packages/frontend/src/utility/get-user-menu.ts
+++ b/packages/frontend/src/utility/get-user-menu.ts
@@ -13,7 +13,7 @@ import { i18n } from '@/i18n.js';
import { copyToClipboard } from '@/utility/copy-to-clipboard.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/utility/misskey-api.js';
-import { $i, iAmModerator } from '@/account.js';
+import { $i, iAmModerator } from '@/i.js';
import { notesSearchAvailable, canSearchNonLocalNotes } from '@/utility/check-permissions.js';
import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
import { mainRouter } from '@/router/main.js';
diff --git a/packages/frontend/src/utility/isFfVisibleForMe.ts b/packages/frontend/src/utility/isFfVisibleForMe.ts
index e28e5725bc..48ef1c4e49 100644
--- a/packages/frontend/src/utility/isFfVisibleForMe.ts
+++ b/packages/frontend/src/utility/isFfVisibleForMe.ts
@@ -4,7 +4,7 @@
*/
import * as Misskey from 'misskey-js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
export function isFollowingVisibleForMe(user: Misskey.entities.UserDetailed): boolean {
if ($i && ($i.id === user.id || $i.isAdmin || $i.isModerator)) return true;
diff --git a/packages/frontend/src/utility/misskey-api.ts b/packages/frontend/src/utility/misskey-api.ts
index dc07ad477b..72ba54ade3 100644
--- a/packages/frontend/src/utility/misskey-api.ts
+++ b/packages/frontend/src/utility/misskey-api.ts
@@ -6,7 +6,7 @@
import * as Misskey from 'misskey-js';
import { ref } from 'vue';
import { apiUrl } from '@@/js/config.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
export const pendingApiRequestsCount = ref(0);
export type Endpoint = keyof Misskey.Endpoints;
diff --git a/packages/frontend/src/utility/please-login.ts b/packages/frontend/src/utility/please-login.ts
index a8a330eb6d..9253105f48 100644
--- a/packages/frontend/src/utility/please-login.ts
+++ b/packages/frontend/src/utility/please-login.ts
@@ -4,7 +4,7 @@
*/
import { defineAsyncComponent } from 'vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { instance } from '@/instance.js';
import { i18n } from '@/i18n.js';
import { popup } from '@/os.js';
diff --git a/packages/frontend/src/utility/show-moved-dialog.ts b/packages/frontend/src/utility/show-moved-dialog.ts
index 35b3ef79d8..db21b028cd 100644
--- a/packages/frontend/src/utility/show-moved-dialog.ts
+++ b/packages/frontend/src/utility/show-moved-dialog.ts
@@ -4,7 +4,7 @@
*/
import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
export function showMovedDialog() {
diff --git a/packages/frontend/src/utility/upload.ts b/packages/frontend/src/utility/upload.ts
index d105a318a7..eb3cbd3dfa 100644
--- a/packages/frontend/src/utility/upload.ts
+++ b/packages/frontend/src/utility/upload.ts
@@ -9,7 +9,7 @@ import { v4 as uuid } from 'uuid';
import { readAndCompressImage } from '@misskey-dev/browser-image-resizer';
import { apiUrl } from '@@/js/config.js';
import { getCompressionConfig } from './upload/compress-config.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { alert } from '@/os.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index d911e71ab2..db03d1406c 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -28,7 +28,7 @@ import XChart from './WidgetActivity.chart.vue';
import type { GetFormResultType } from '@/utility/form.js';
import { misskeyApiGet } from '@/utility/misskey-api.js';
import MkContainer from '@/components/MkContainer.vue';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
const name = 'activity';
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index b49041158f..c46fd81466 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -27,7 +27,7 @@ import type { GetFormResultType } from '@/utility/form.js';
import * as os from '@/os.js';
import MkContainer from '@/components/MkContainer.vue';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { i18n } from '@/i18n.js';
const name = 'aiscript';
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index fb9dea1847..429b0e0ffb 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -21,7 +21,7 @@ import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps
import type { GetFormResultType } from '@/utility/form.js';
import * as os from '@/os.js';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkAsUi from '@/components/MkAsUi.vue';
import MkContainer from '@/components/MkContainer.vue';
import { registerAsUiLib } from '@/aiscript/ui.js';
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
index 8c7507ef44..be11a26917 100644
--- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -33,7 +33,7 @@ import MkContainer from '@/components/MkContainer.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { infoImageUrl } from '@/instance.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
const name = i18n.ts._widgets.birthdayFollowings;
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index 3f0f9eb9fd..4afe735a22 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -18,7 +18,7 @@ import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps
import type { GetFormResultType } from '@/utility/form.js';
import * as os from '@/os.js';
import { aiScriptReadline, createAiScriptEnv } from '@/aiscript/api.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import MkButton from '@/components/MkButton.vue';
const name = 'button';
diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue
index c86d1c9653..3fe8378a39 100644
--- a/packages/frontend/src/widgets/WidgetProfile.vue
+++ b/packages/frontend/src/widgets/WidgetProfile.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
import { useWidgetPropsManager } from './widget.js';
import type { WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
import type { GetFormResultType } from '@/utility/form.js';
-import { $i } from '@/account.js';
+import { $i } from '@/i.js';
import { userPage } from '@/filters/user.js';
const name = 'profile';
diff --git a/packages/frontend/test/aiscript/api.test.ts b/packages/frontend/test/aiscript/api.test.ts
index a569c0fa51..36838af163 100644
--- a/packages/frontend/test/aiscript/api.test.ts
+++ b/packages/frontend/test/aiscript/api.test.ts
@@ -33,7 +33,7 @@ async function exe(script: string): Promise<values.Value[]> {
return outputs;
}
-let $iMock = vi.hoisted<Partial<typeof import('@/account.js').$i> | null >(
+let $iMock = vi.hoisted<Partial<typeof import('@/i.js').$i> | null >(
() => null
);