import { useQueryClient } from '@tanstack/react-query'; import { createContext, useContext, useEffect, useMemo, useRef, useState, type PropsWithChildren } from 'react'; import type { DashboardEvent } from '../../../../packages/api-contracts/src/index.js'; import { createBrowserEventSource, createDashboardRealtimeClient, queryKeysForDashboardEvent, type RealtimeConnectionSnapshot, } from '../features/operations/realtime-client.js'; import { browserPollingScheduler, browserVisibilitySource, createVisibilityAwarePollingFallback } from '../features/operations/realtime-fallback.js'; import { repairRealtimeSnapshots } from './realtime-repair.js'; type EventListener = (event: DashboardEvent) => void; interface RealtimeContextValue { connection: RealtimeConnectionSnapshot; subscribe(listener: EventListener): () => void; } const RealtimeContext = createContext(null); export function DashboardRealtimeProvider({ children }: PropsWithChildren) { const queryClient = useQueryClient(); const listeners = useRef(new Set()); const [connection, setConnection] = useState({ status: 'idle', isStale: true }); useEffect(() => { const repair = () => { void repairRealtimeSnapshots(queryClient); }; const fallback = createVisibilityAwarePollingFallback({ intervalMs: 15_000, poll: repair, scheduler: browserPollingScheduler, visibility: browserVisibilitySource }); fallback.start(); if (typeof EventSource === 'undefined') { fallback.setStale(true); return () => fallback.stop(); } const client = createDashboardRealtimeClient({ sourceFactory: createBrowserEventSource, onConnected: repair, onEvent(event) { for (const queryKey of queryKeysForDashboardEvent(event)) void queryClient.invalidateQueries({ queryKey }); for (const listener of listeners.current) listener(event); }, onStateChange(snapshot) { setConnection(snapshot); fallback.setStale(snapshot.isStale); }, }); client.start(); return () => { client.stop(); fallback.stop(); }; }, [queryClient]); const value = useMemo(() => ({ connection, subscribe(listener) { listeners.current.add(listener); return () => listeners.current.delete(listener); }, }), [connection]); return {children}; } export function useDashboardRealtime(): RealtimeContextValue { return useContext(RealtimeContext) ?? { connection: { status: 'idle', isStale: true }, subscribe: () => () => undefined }; }