// Single source of truth for "is export released and is the ZIP ready?". Used // by the BottomNav to surface the Export tab on demand, and by the export // page to render status. Hydrates lazily on first subscribe; reacts to the // `export-progress` / `export-available` SSE events so the nav updates live. import { writable } from 'svelte/store'; import { api } from './api'; import { onSseEvent } from './sse'; import { getToken, onClearAuth } from './auth'; export interface ExportJob { status: 'locked' | 'pending' | 'running' | 'done' | 'failed'; progress_pct: number; } export interface ExportStatusSnapshot { released: boolean; zip: ExportJob | null; html: ExportJob | null; } const empty: ExportStatusSnapshot = { released: false, zip: null, html: null }; export const exportStatus = writable(empty); let hydrated = false; let sseUnsubs: Array<() => void> = []; export async function refreshExportStatus(): Promise { if (!getToken()) return; try { const data = await api.get('/export/status'); exportStatus.set(data); } catch { // /export/status can 403 for guests on locked events; that's expected. exportStatus.set(empty); } } export function initExportStatus(): void { if (hydrated) return; hydrated = true; void refreshExportStatus(); sseUnsubs.push(onSseEvent('export-progress', () => { void refreshExportStatus(); })); sseUnsubs.push(onSseEvent('export-available', () => { void refreshExportStatus(); })); } export function teardownExportStatus(): void { for (const u of sseUnsubs) u(); sseUnsubs = []; hydrated = false; exportStatus.set(empty); } // Auto-reset on every clearAuth path (explicit logout + api.ts 401 auto-clear). // Imported for side-effect by anyone who pulls in this module — the BottomNav // does that statically, and BottomNav itself is statically imported by // +layout.svelte, so this side-effect runs at app boot, well before any API // call can 401. If a future refactor lazy-loads the BottomNav (dynamic import, // route-level code-split), move the side-effect to +layout.svelte's onMount // so the hook isn't gated on a conditional component. onClearAuth(teardownExportStatus);