"use client"; import { useEffect } from "react"; export type WebEventMap = { hello: { ts: number }; ping: { ts: number }; "session.qr": { accountId: string; ts: number }; "session.connected": { accountId: string; phoneNumber: string | null }; "session.disconnected": { accountId: string }; "session.timeout": { accountId: string }; "groups.synced": { accountId: string; count: number }; "reminder.fired": { reminderId: string; runId: string; status: string; sent?: number; total?: number; }; "reminder.failed": { reminderId: string; error: string }; "send_test.done": { groupId: string; ok: boolean; error: string | null }; }; type Handlers = { [K in keyof WebEventMap]?: (data: WebEventMap[K]) => void }; export function useEvents(handlers: Handlers): void { useEffect(() => { const es = new EventSource("/api/events"); const wired: { type: string; fn: (e: MessageEvent) => void }[] = []; for (const type of Object.keys(handlers) as (keyof WebEventMap)[]) { const h = handlers[type]; if (!h) continue; const fn = (e: MessageEvent) => { try { (h as (data: unknown) => void)(JSON.parse(e.data)); } catch { // ignore malformed } }; es.addEventListener(type, fn); wired.push({ type, fn }); } return () => { for (const { type, fn } of wired) { es.removeEventListener(type, fn); } es.close(); }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); }