import { getToken } from './auth'; type EventHandler = (data: string) => void; let eventSource: EventSource | null = null; let lastEventTime: string | null = null; const handlers: Map = new Map(); export function onSseEvent(eventType: string, handler: EventHandler): () => void { if (!handlers.has(eventType)) { handlers.set(eventType, []); } handlers.get(eventType)!.push(handler); // Return unsubscribe function return () => { const list = handlers.get(eventType); if (list) { const idx = list.indexOf(handler); if (idx >= 0) list.splice(idx, 1); } }; } export function connectSse(): void { const token = getToken(); if (!token || eventSource) return; // EventSource doesn't support custom headers, so pass token as query param // The backend will need to accept this — or we use a polyfill / fetch-based SSE // For simplicity, use native EventSource with token in URL eventSource = new EventSource(`/api/v1/stream?token=${encodeURIComponent(token)}`); eventSource.onopen = () => { lastEventTime = new Date().toISOString(); }; eventSource.addEventListener('new-upload', (e) => dispatch('new-upload', e.data)); eventSource.addEventListener('upload-processed', (e) => dispatch('upload-processed', e.data)); eventSource.addEventListener('like-update', (e) => dispatch('like-update', e.data)); eventSource.addEventListener('new-comment', (e) => dispatch('new-comment', e.data)); eventSource.addEventListener('export-available', (e) => dispatch('export-available', e.data)); eventSource.onerror = () => { // EventSource auto-reconnects, but we track the time for delta-fetch disconnectSse(); // Reconnect after a short delay setTimeout(connectSse, 3000); }; } export function disconnectSse(): void { if (eventSource) { eventSource.close(); eventSource = null; } } export function getLastEventTime(): string | null { return lastEventTime; } export function setLastEventTime(time: string): void { lastEventTime = time; } function dispatch(eventType: string, data: string): void { lastEventTime = new Date().toISOString(); const list = handlers.get(eventType); if (list) { for (const handler of list) { handler(data); } } } // Page Visibility API integration if (typeof document !== 'undefined') { document.addEventListener('visibilitychange', () => { if (document.hidden) { disconnectSse(); } else { connectSse(); } }); }