forked from FoundKeyGang/FoundKey
refactor: localStorageのaccountsはindexedDBで保持するように (#7609)
* accountsストアはindexedDBで保持するように
* fix lint
* fix indexeddb available detection
* remove debugging code
* fix lint
* resolve ba756204b7 (diff-f565878e8202f0037b830c780b7c0932dc1bb5fd3d05ede14d72d10efbc3740c)
Firefoxでの動作を改善
* fix lint
* fix lint
* add changelog
This commit is contained in:
parent
60e768436e
commit
fc56b12690
9 changed files with 112 additions and 28 deletions
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- 依存関係の更新
|
- 依存関係の更新
|
||||||
|
- localStorageのaccountsはindexedDBで保持するように
|
||||||
|
|
||||||
### Bugfixes
|
### Bugfixes
|
||||||
- チャンネルを作成しているとアカウントを削除できないのを修正
|
- チャンネルを作成しているとアカウントを削除できないのを修正
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
|
import { get, set } from '@client/scripts/idb-proxy';
|
||||||
import { reactive } from 'vue';
|
import { reactive } from 'vue';
|
||||||
import { apiUrl } from '@client/config';
|
import { apiUrl } from '@client/config';
|
||||||
import { waiting } from '@client/os';
|
import { waiting } from '@client/os';
|
||||||
import { unisonReload } from '@client/scripts/unison-reload';
|
import { unisonReload, reloadChannel } from '@client/scripts/unison-reload';
|
||||||
|
|
||||||
// TODO: 他のタブと永続化されたstateを同期
|
// TODO: 他のタブと永続化されたstateを同期
|
||||||
|
|
||||||
|
@ -17,22 +18,43 @@ const data = localStorage.getItem('account');
|
||||||
// TODO: 外部からはreadonlyに
|
// TODO: 外部からはreadonlyに
|
||||||
export const $i = data ? reactive(JSON.parse(data) as Account) : null;
|
export const $i = data ? reactive(JSON.parse(data) as Account) : null;
|
||||||
|
|
||||||
export function signout() {
|
export async function signout() {
|
||||||
|
waiting();
|
||||||
localStorage.removeItem('account');
|
localStorage.removeItem('account');
|
||||||
|
|
||||||
|
//#region Remove account
|
||||||
|
const accounts = await getAccounts();
|
||||||
|
accounts.splice(accounts.findIndex(x => x.id === $i.id), 1);
|
||||||
|
set('accounts', accounts);
|
||||||
|
//#endregion
|
||||||
|
|
||||||
|
//#region Remove push notification registration
|
||||||
|
const registration = await navigator.serviceWorker.ready;
|
||||||
|
const push = await registration.pushManager.getSubscription();
|
||||||
|
if (!push) return;
|
||||||
|
await fetch(`${apiUrl}/sw/unregister`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
i: $i.token,
|
||||||
|
endpoint: push.endpoint,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
//#endregion
|
||||||
|
|
||||||
document.cookie = `igi=; path=/`;
|
document.cookie = `igi=; path=/`;
|
||||||
location.href = '/';
|
|
||||||
|
if (accounts.length > 0) login(accounts[0].token);
|
||||||
|
else unisonReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getAccounts() {
|
export async function getAccounts(): Promise<{ id: Account['id'], token: Account['token'] }[]> {
|
||||||
const accountsData = localStorage.getItem('accounts');
|
return (await get('accounts')) || [];
|
||||||
const accounts: { id: Account['id'], token: Account['token'] }[] = accountsData ? JSON.parse(accountsData) : [];
|
|
||||||
return accounts;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function addAccount(id: Account['id'], token: Account['token']) {
|
export async function addAccount(id: Account['id'], token: Account['token']) {
|
||||||
const accounts = getAccounts();
|
const accounts = await getAccounts();
|
||||||
if (!accounts.some(x => x.id === id)) {
|
if (!accounts.some(x => x.id === id)) {
|
||||||
localStorage.setItem('accounts', JSON.stringify(accounts.concat([{ id, token }])));
|
await set('accounts', accounts.concat([{ id, token }]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,7 +69,7 @@ function fetchAccount(token): Promise<Account> {
|
||||||
})
|
})
|
||||||
.then(res => {
|
.then(res => {
|
||||||
// When failed to authenticate user
|
// When failed to authenticate user
|
||||||
if (res.status >= 400 && res.status < 500) {
|
if (res.status !== 200 && res.status < 500) {
|
||||||
return signout();
|
return signout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,15 +91,22 @@ export function updateAccount(data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function refreshAccount() {
|
export function refreshAccount() {
|
||||||
fetchAccount($i.token).then(updateAccount);
|
return fetchAccount($i.token).then(updateAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login(token: Account['token']) {
|
export async function login(token: Account['token'], redirect?: string) {
|
||||||
waiting();
|
waiting();
|
||||||
if (_DEV_) console.log('logging as token ', token);
|
if (_DEV_) console.log('logging as token ', token);
|
||||||
const me = await fetchAccount(token);
|
const me = await fetchAccount(token);
|
||||||
localStorage.setItem('account', JSON.stringify(me));
|
localStorage.setItem('account', JSON.stringify(me));
|
||||||
addAccount(me.id, token);
|
await addAccount(me.id, token);
|
||||||
|
|
||||||
|
if (redirect) {
|
||||||
|
reloadChannel.postMessage('reload');
|
||||||
|
location.href = redirect;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
unisonReload();
|
unisonReload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,15 @@
|
||||||
|
|
||||||
import '@client/style.scss';
|
import '@client/style.scss';
|
||||||
|
|
||||||
|
//#region account indexedDB migration
|
||||||
|
import { set } from '@client/scripts/idb-proxy';
|
||||||
|
|
||||||
|
if (localStorage.getItem('accounts') != null) {
|
||||||
|
set('accounts', JSON.parse(localStorage.getItem('accounts')));
|
||||||
|
localStorage.removeItem('accounts');
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
|
||||||
import * as Sentry from '@sentry/browser';
|
import * as Sentry from '@sentry/browser';
|
||||||
import { Integrations } from '@sentry/tracing';
|
import { Integrations } from '@sentry/tracing';
|
||||||
import { computed, createApp, watch, markRaw } from 'vue';
|
import { computed, createApp, watch, markRaw } from 'vue';
|
||||||
|
|
|
@ -48,10 +48,10 @@ export default defineComponent({
|
||||||
title: this.$ts.accounts,
|
title: this.$ts.accounts,
|
||||||
icon: 'fas fa-users',
|
icon: 'fas fa-users',
|
||||||
},
|
},
|
||||||
storedAccounts: getAccounts().filter(x => x.id !== this.$i.id),
|
storedAccounts: getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id)),
|
||||||
accounts: null,
|
accounts: null,
|
||||||
init: () => os.api('users/show', {
|
init: async () => os.api('users/show', {
|
||||||
userIds: this.storedAccounts.map(x => x.id)
|
userIds: (await this.storedAccounts).map(x => x.id)
|
||||||
}).then(accounts => {
|
}).then(accounts => {
|
||||||
this.accounts = accounts;
|
this.accounts = accounts;
|
||||||
}),
|
}),
|
||||||
|
@ -104,8 +104,8 @@ export default defineComponent({
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
},
|
},
|
||||||
|
|
||||||
switchAccount(account: any) {
|
async switchAccount(account: any) {
|
||||||
const storedAccounts = getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
const token = storedAccounts.find(x => x.id === account.id).token;
|
const token = storedAccounts.find(x => x.id === account.id).token;
|
||||||
this.switchAccountWithToken(token);
|
this.switchAccountWithToken(token);
|
||||||
},
|
},
|
||||||
|
|
7
src/client/scripts/get-account-from-id.ts
Normal file
7
src/client/scripts/get-account-from-id.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import { get } from '@client/scripts/idb-proxy';
|
||||||
|
|
||||||
|
export async function getAccountFromId(id: string) {
|
||||||
|
const accounts = await get('accounts') as { token: string; id: string; }[];
|
||||||
|
if (!accounts) console.log('Accounts are not recorded');
|
||||||
|
return accounts.find(e => e.id === id);
|
||||||
|
}
|
38
src/client/scripts/idb-proxy.ts
Normal file
38
src/client/scripts/idb-proxy.ts
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
// FirefoxのプライベートモードなどではindexedDBが使用不可能なので、
|
||||||
|
// indexedDBが使えない環境ではlocalStorageを使う
|
||||||
|
import {
|
||||||
|
get as iget,
|
||||||
|
set as iset,
|
||||||
|
del as idel,
|
||||||
|
createStore,
|
||||||
|
} from 'idb-keyval';
|
||||||
|
|
||||||
|
const fallbackName = (key: string) => `idbfallback::${key}`;
|
||||||
|
|
||||||
|
let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true;
|
||||||
|
|
||||||
|
if (idbAvailable) {
|
||||||
|
try {
|
||||||
|
await createStore('keyval-store', 'keyval');
|
||||||
|
} catch (e) {
|
||||||
|
console.error('idb open error', e);
|
||||||
|
idbAvailable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!idbAvailable) console.error('indexedDB is unavailable. It will use localStorage.');
|
||||||
|
|
||||||
|
export async function get(key: string) {
|
||||||
|
if (idbAvailable) return iget(key);
|
||||||
|
return JSON.parse(localStorage.getItem(fallbackName(key)));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function set(key: string, val: any) {
|
||||||
|
if (idbAvailable) return iset(key, val);
|
||||||
|
return localStorage.setItem(fallbackName(key), JSON.stringify(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function del(key: string) {
|
||||||
|
if (idbAvailable) return idel(key);
|
||||||
|
return localStorage.removeItem(fallbackName(key));
|
||||||
|
}
|
|
@ -135,7 +135,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
async openAccountMenu(ev) {
|
async openAccountMenu(ev) {
|
||||||
const storedAccounts = getAccounts().filter(x => x.id !== this.$i.id);
|
const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id));
|
||||||
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
||||||
|
|
||||||
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
||||||
|
@ -195,8 +195,8 @@ export default defineComponent({
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
},
|
},
|
||||||
|
|
||||||
switchAccount(account: any) {
|
async switchAccount(account: any) {
|
||||||
const storedAccounts = getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
const token = storedAccounts.find(x => x.id === account.id).token;
|
const token = storedAccounts.find(x => x.id === account.id).token;
|
||||||
this.switchAccountWithToken(token);
|
this.switchAccountWithToken(token);
|
||||||
},
|
},
|
||||||
|
|
|
@ -101,7 +101,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
async openAccountMenu(ev) {
|
async openAccountMenu(ev) {
|
||||||
const storedAccounts = getAccounts().filter(x => x.id !== this.$i.id);
|
const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id));
|
||||||
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
||||||
|
|
||||||
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
||||||
|
@ -161,8 +161,8 @@ export default defineComponent({
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
},
|
},
|
||||||
|
|
||||||
switchAccount(account: any) {
|
async switchAccount(account: any) {
|
||||||
const storedAccounts = getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
const token = storedAccounts.find(x => x.id === account.id).token;
|
const token = storedAccounts.find(x => x.id === account.id).token;
|
||||||
this.switchAccountWithToken(token);
|
this.switchAccountWithToken(token);
|
||||||
},
|
},
|
||||||
|
|
|
@ -121,7 +121,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
async openAccountMenu(ev) {
|
async openAccountMenu(ev) {
|
||||||
const storedAccounts = getAccounts().filter(x => x.id !== this.$i.id);
|
const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== this.$i.id));
|
||||||
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
const accountsPromise = os.api('users/show', { userIds: storedAccounts.map(x => x.id) });
|
||||||
|
|
||||||
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
const accountItemPromises = storedAccounts.map(a => new Promise(res => {
|
||||||
|
@ -181,8 +181,8 @@ export default defineComponent({
|
||||||
}, 'closed');
|
}, 'closed');
|
||||||
},
|
},
|
||||||
|
|
||||||
switchAccount(account: any) {
|
async switchAccount(account: any) {
|
||||||
const storedAccounts = getAccounts();
|
const storedAccounts = await getAccounts();
|
||||||
const token = storedAccounts.find(x => x.id === account.id).token;
|
const token = storedAccounts.find(x => x.id === account.id).token;
|
||||||
this.switchAccountWithToken(token);
|
this.switchAccountWithToken(token);
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in a new issue