bb008cfaa5
- Prefs sync: GET/PUT /api/prefs store Calm Filters/Boundaries on the account. On sign-in the client adopts the account's prefs if present, else seeds them from the device; every change PUTs to the account so tuning follows you across devices. (Login side-effects run under untrack so browsing doesn't re-trigger.) - Account panel: GET /api/account (email, connected sign-in methods, saved count, active sessions); Export my data (GET /api/account/export → JSON download); Sign out everywhere (revoke all sessions); Delete account (cascades to all account data) with an inline confirm. Reachable from You → Account. Deferred to a follow-up: link/unlink a provider (OAuth link-mode) and per-session revoke. 118 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
72 lines
1.8 KiB
JavaScript
72 lines
1.8 KiB
JavaScript
// Shared, reactive auth + saved-articles state (Svelte 5 runes in a module).
|
|
import { SvelteSet } from 'svelte/reactivity';
|
|
import { getJSON, postJSON, delJSON } from './api.js';
|
|
|
|
export const auth = $state({ user: null, ready: false });
|
|
export const savedIds = new SvelteSet(); // reactive set of saved article ids
|
|
|
|
export async function refresh() {
|
|
try {
|
|
auth.user = await getJSON('/api/auth/me');
|
|
} catch {
|
|
auth.user = null;
|
|
} finally {
|
|
auth.ready = true;
|
|
}
|
|
if (auth.user) await loadSaved();
|
|
else savedIds.clear();
|
|
}
|
|
|
|
export async function loadSaved() {
|
|
try {
|
|
const ids = await getJSON('/api/saved/ids');
|
|
savedIds.clear();
|
|
for (const id of ids) savedIds.add(id);
|
|
} catch {
|
|
/* not signed in / transient — leave as-is */
|
|
}
|
|
}
|
|
|
|
// Optimistic toggle; reverts on failure.
|
|
export async function toggleSave(id) {
|
|
if (!auth.user) return false;
|
|
const willSave = !savedIds.has(id);
|
|
if (willSave) savedIds.add(id);
|
|
else savedIds.delete(id);
|
|
try {
|
|
if (willSave) await postJSON(`/api/saved/${id}`, {});
|
|
else await delJSON(`/api/saved/${id}`);
|
|
} catch {
|
|
if (willSave) savedIds.delete(id);
|
|
else savedIds.add(id);
|
|
}
|
|
return savedIds.has(id);
|
|
}
|
|
|
|
// Request a magic link. Reply is intentionally identical for any address.
|
|
export function startEmail(email) {
|
|
return postJSON('/api/auth/email/start', { email });
|
|
}
|
|
|
|
export async function verifyToken(token) {
|
|
const res = await postJSON('/api/auth/email/verify', { token });
|
|
auth.user = res.user;
|
|
await loadSaved();
|
|
return res;
|
|
}
|
|
|
|
export async function logout() {
|
|
try {
|
|
await postJSON('/api/auth/logout', {});
|
|
} finally {
|
|
clearLocal();
|
|
}
|
|
}
|
|
|
|
// Clear client auth state without a logout call (cookie already cleared server-side,
|
|
// e.g. after delete-account or sign-out-everywhere).
|
|
export function clearLocal() {
|
|
auth.user = null;
|
|
savedIds.clear();
|
|
}
|