// The groups as the menu and the pages share them: the last group opened on this // device (outside a group, the tools of the menu aim at it), and the views // already read (the menu counts the documents of the current group). import { api, ApiError } from './api'; import type { GroupView, Tool } from './group'; const KEY = 'ld:group'; /** The last group opened, read straight from storage (usable before any mount). */ export function lastGroup(): string | null { try { return localStorage.getItem(KEY); } catch { return null; } } /** Where a tool leads: its page in the group, or home when no group is known. */ export function toolHref(tool: Tool, group: string | null): string { return group ? `/g/${group}/${tool}` : '/'; } class Groups { last = $state(null); views = $state>({}); private asked = new Set(); private running = new Map>(); init() { this.last = lastGroup(); } /** Read a group (a read already under way is shared); one that opens becomes the last group. */ load(slug: string): Promise { const running = this.running.get(slug); if (running) return running; this.asked.add(slug); const p: Promise = this.fetch(slug).finally(() => { if (this.running.get(slug) === p) this.running.delete(slug); }); this.running.set(slug, p); return p; } private async fetch(slug: string): Promise { try { const v = await api.get(`/groups/${slug}`); this.views[slug] = v; if (v.group.open) this.remember(slug); else if (this.last === slug) this.forget(); return v; } catch (e) { // A group that no longer shows or opens is no longer the last group; a network hiccup keeps it. if (this.last === slug && e instanceof ApiError && (e.code === 'not_found' || e.code === 'forbidden')) this.forget(); throw e; } } /** Read a group once, for the menu; errors stay silent there. */ ensure(slug: string) { if (this.views[slug] || this.asked.has(slug)) return; this.load(slug).catch(() => {}); } remember(slug: string) { this.last = slug; try { localStorage.setItem(KEY, slug); } catch { /* no storage: the menu simply leads home */ } } /** On sign-out, or when the last group no longer opens. */ forget() { this.last = null; try { localStorage.removeItem(KEY); } catch { /* no storage */ } } /** On sign-out: nothing read under the previous identity is kept. */ reset() { this.forget(); this.views = {}; this.asked.clear(); this.running.clear(); } } export const groups = new Groups();