calckey/src/client/account.ts

116 lines
2.8 KiB
TypeScript
Raw Normal View History

2021-01-27 18:24:32 +00:00
import { get, set } from 'idb-keyval';
import { reactive } from 'vue';
import { apiUrl } from '@/config';
import { waiting } from '@/os';
2021-02-14 21:05:18 +00:00
import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
// TODO: 他のタブと永続化されたstateを同期
type Account = {
id: string;
token: string;
};
const data = localStorage.getItem('account');
// TODO: 外部からはreadonlyに
export const $i = data ? reactive(JSON.parse(data) as Account) : null;
2021-01-27 18:24:32 +00:00
export async function signout() {
localStorage.removeItem('account');
2021-01-27 18:24:32 +00:00
//#region Remove account
const accounts = await getAccounts();
2021-01-27 18:49:27 +00:00
accounts.splice(accounts.findIndex(x => x.id === $i.id), 1);
2021-01-27 19:07:05 +00:00
set('accounts', accounts);
2021-01-27 18:24:32 +00:00
//#endregion
//#region Remove push notification registration
await navigator.serviceWorker.ready.then(async r => {
2021-01-27 18:49:27 +00:00
const push = await r.pushManager.getSubscription();
2021-01-27 18:24:32 +00:00
if (!push) return;
return fetch(`${apiUrl}/sw/unregister`, {
method: 'POST',
body: JSON.stringify({
i: $i.token,
endpoint: push.endpoint,
}),
});
});
//#endregion
document.cookie = `igi=; path=/`;
2021-01-27 18:49:27 +00:00
2021-01-27 18:24:32 +00:00
if (accounts.length > 0) login(accounts[0].token);
else location.href = '/';
}
2021-01-27 19:01:55 +00:00
export async function getAccounts(): Promise<{ id: Account['id'], token: Account['token'] }[]> {
return (await get('accounts')) || [];
}
2021-01-27 18:24:32 +00:00
export async function addAccount(id: Account['id'], token: Account['token']) {
const accounts = await getAccounts();
if (!accounts.some(x => x.id === id)) {
2021-01-27 18:24:32 +00:00
return set('accounts', accounts.concat([{ id, token }]));
}
}
function fetchAccount(token): Promise<Account> {
return new Promise((done, fail) => {
// Fetch user
fetch(`${apiUrl}/i`, {
method: 'POST',
body: JSON.stringify({
i: token
})
})
.then(res => {
// When failed to authenticate user
if (res.status !== 200 && res.status < 500) {
return signout();
}
// Parse response
res.json().then(i => {
i.token = token;
done(i);
});
})
.catch(fail);
});
}
export function updateAccount(data) {
for (const [key, value] of Object.entries(data)) {
$i[key] = value;
}
localStorage.setItem('account', JSON.stringify($i));
}
export function refreshAccount() {
2021-02-14 21:05:18 +00:00
return fetchAccount($i.token).then(updateAccount);
}
2021-02-14 21:05:18 +00:00
export async function login(token: Account['token'], href?: string) {
waiting();
if (_DEV_) console.log('logging as token ', token);
const me = await fetchAccount(token);
localStorage.setItem('account', JSON.stringify(me));
2021-01-27 18:24:32 +00:00
await addAccount(me.id, token);
2021-02-14 21:05:18 +00:00
if (href) {
reloadChannel.postMessage('reload');
location.href = href;
return;
}
unisonReload();
}
// このファイルに書きたくないけどここに書かないと何故かVeturが認識しない
declare module '@vue/runtime-core' {
interface ComponentCustomProperties {
$i: typeof $i;
}
}