From 5926a4098b0d9fa8a0f5b00a937f409752caec99 Mon Sep 17 00:00:00 2001 From: tamaina Date: Mon, 19 Jul 2021 15:50:06 +0900 Subject: [PATCH] =?UTF-8?q?accounts=E3=82=B9=E3=83=88=E3=82=A2=E3=81=AFind?= =?UTF-8?q?exedDB=E3=81=A7=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/client/account.ts | 58 +++++++++++++++++------ src/client/init.ts | 9 ++++ src/client/scripts/get-account-from-id.ts | 7 +++ src/client/scripts/idb-proxy.ts | 35 ++++++++++++++ 4 files changed, 95 insertions(+), 14 deletions(-) create mode 100644 src/client/scripts/get-account-from-id.ts create mode 100644 src/client/scripts/idb-proxy.ts diff --git a/src/client/account.ts b/src/client/account.ts index 2b860b3ddf..f033bd3959 100644 --- a/src/client/account.ts +++ b/src/client/account.ts @@ -1,7 +1,8 @@ +import { get, set } from '@client/scripts/idb-proxy'; import { reactive } from 'vue'; import { apiUrl } from '@client/config'; import { waiting } from '@client/os'; -import { unisonReload } from '@client/scripts/unison-reload'; +import { unisonReload, reloadChannel } from '@client/scripts/unison-reload'; // TODO: 他のタブと永続化されたstateを同期 @@ -17,22 +18,44 @@ const data = localStorage.getItem('account'); // TODO: 外部からはreadonlyに export const $i = data ? reactive(JSON.parse(data) as Account) : null; -export function signout() { +export async function signout() { + waiting(); 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 + await navigator.serviceWorker.ready.then(async r => { + const push = await r.pushManager.getSubscription(); + if (!push) return; + return fetch(`${apiUrl}/sw/unregister`, { + method: 'POST', + body: JSON.stringify({ + i: $i.token, + endpoint: push.endpoint, + }), + }); + }); + //#endregion + document.cookie = `igi=; path=/`; - location.href = '/'; + + if (accounts.length > 0) login(accounts[0].token); + else unisonReload(true); } -export function getAccounts() { - const accountsData = localStorage.getItem('accounts'); - const accounts: { id: Account['id'], token: Account['token'] }[] = accountsData ? JSON.parse(accountsData) : []; - return accounts; +export async function getAccounts(): Promise<{ id: Account['id'], token: Account['token'] }[]> { + return (await get('accounts')) || []; } -export function addAccount(id: Account['id'], token: Account['token']) { - const accounts = getAccounts(); +export async function addAccount(id: Account['id'], token: Account['token']) { + const accounts = await getAccounts(); 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 +70,7 @@ function fetchAccount(token): Promise { }) .then(res => { // When failed to authenticate user - if (res.status >= 400 && res.status < 500) { + if (res.status !== 200 && res.status < 500) { return signout(); } @@ -69,15 +92,22 @@ export function updateAccount(data) { } 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(); if (_DEV_) console.log('logging as token ', token); const me = await fetchAccount(token); 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(); } diff --git a/src/client/init.ts b/src/client/init.ts index d1c408bb77..c0b0675a3f 100644 --- a/src/client/init.ts +++ b/src/client/init.ts @@ -4,6 +4,15 @@ 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 { Integrations } from '@sentry/tracing'; import { computed, createApp, watch } from 'vue'; diff --git a/src/client/scripts/get-account-from-id.ts b/src/client/scripts/get-account-from-id.ts new file mode 100644 index 0000000000..065b41118c --- /dev/null +++ b/src/client/scripts/get-account-from-id.ts @@ -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); +} diff --git a/src/client/scripts/idb-proxy.ts b/src/client/scripts/idb-proxy.ts new file mode 100644 index 0000000000..e15be7cde2 --- /dev/null +++ b/src/client/scripts/idb-proxy.ts @@ -0,0 +1,35 @@ +// FirefoxのプライベートモードなどではindexedDBが使用不可能なので、 +// indexedDBが使えない環境ではlocalStorageを使う +import { + get as iget, + set as iset, + del as idel, +} from 'idb-keyval'; + +const fallbackName = (key: string) => `idbfallback::${key}`; + +let idbAvailable = typeof window !== 'undefined' ? !!window.indexedDB : true; + +if (idbAvailable) { + try { + const request = indexedDB.open('keyval-store'); + if (request.error) idbAvailable = false; + } catch (e) { + idbAvailable = false; + } +} + +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)); +}