/** * Who is signed in. An account is optional: without one, documents stay in * this browser (IndexedDB); with one, they live on the server and can be * shared. Sign-in is by magic link or the 6-digit code from the same mail. */ import { computed, reactive } from 'vue'; import { api, bootData, type User } from '../io/api'; const state = reactive({ user: bootData().user as User | null }); export function useSession() { const signedIn = computed(() => !!state.user); /** Mail a sign-in link + code. `returnTo` = where the link leads back to. */ async function requestLink(email: string, returnTo = location.pathname + location.search) { await api('POST', '/auth/magic_link', { body: { email, return_to: returnTo } }); } /** Sign in with the code. The page reloads, so everything starts signed in. */ async function verifyCode(email: string, code: string) { await api<{ redirect: string }>('POST', '/auth/code', { body: { email, code } }); location.reload(); } async function logout() { await api('DELETE', '/auth/session'); location.assign('/'); } async function setDisplayName(display_name: string) { const r = await api<{ user: User }>('PATCH', '/me', { body: { display_name } }); state.user = r.user; } return { state, signedIn, requestLink, verifyCode, logout, setDisplayName }; }