import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react'; export type WsStatus = 'connecting' | 'connected' | 'disconnected'; interface WsContextValue { status: WsStatus; send: (msg: string) => void; subscribe: (handler: (msg: string) => void) => () => void; } const WsContext = createContext({ status: 'disconnected', send: () => {}, subscribe: () => () => {}, }); export function WsProvider({ children }: { children: React.ReactNode }) { const wsRef = useRef(null); const listeners = useRef void>>(new Set()); const [status, setStatus] = useState('connecting'); useEffect(() => { let cancelled = false; let ws: WebSocket | null = null; const connect = () => { if (cancelled) return; ws = new WebSocket(`ws://${window.location.hostname}/ws`); wsRef.current = ws; setStatus('connecting'); ws.onopen = () => { if (!cancelled) setStatus('connected'); }; ws.onmessage = (e) => { if (!cancelled) listeners.current.forEach(h => h(String(e.data))); }; ws.onclose = () => { if (!cancelled) { setStatus('disconnected'); setTimeout(connect, 3000); } }; ws.onerror = () => ws?.close(); }; connect(); return () => { cancelled = true; ws?.close(); }; }, []); const send = useCallback((msg: string) => { if (wsRef.current?.readyState === WebSocket.OPEN) wsRef.current.send(msg); }, []); const subscribe = useCallback((handler: (msg: string) => void) => { listeners.current.add(handler); return () => { listeners.current.delete(handler); }; }, []); return ( {children} ); } export const useWs = () => useContext(WsContext);