Spaces:
Runtime error
Runtime error
File size: 2,711 Bytes
e4d7d50 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 | /**
* ChessEcon — Real Backend WebSocket Hook
* Connects to the Python FastAPI /ws endpoint and dispatches
* all game, coaching, economy, and training events to the dashboard.
*/
import { useEffect, useRef, useCallback } from "react";
export type WSEventType =
| "game_start"
| "move"
| "coaching_request"
| "coaching_result"
| "game_end"
| "training_step"
| "economy_update"
| "pong";
export interface WSMessage {
type: WSEventType;
data: Record<string, unknown>;
}
interface UseBackendWSOptions {
url: string;
onMessage: (msg: WSMessage) => void;
onOpen?: () => void;
onClose?: () => void;
enabled?: boolean;
}
export function useBackendWS({
url,
onMessage,
onOpen,
onClose,
enabled = true,
}: UseBackendWSOptions) {
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const mountedRef = useRef(true);
const connect = useCallback(() => {
if (!enabled || !mountedRef.current) return;
if (wsRef.current?.readyState === WebSocket.OPEN) return;
try {
const ws = new WebSocket(url);
wsRef.current = ws;
ws.onopen = () => {
if (onOpen) onOpen();
// Start ping interval
const ping = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ action: "ping" }));
} else {
clearInterval(ping);
}
}, 30_000);
};
ws.onmessage = (event) => {
try {
const msg = JSON.parse(event.data) as WSMessage;
onMessage(msg);
} catch {
// ignore malformed messages
}
};
ws.onclose = () => {
if (onClose) onClose();
// Auto-reconnect after 3 seconds
if (mountedRef.current && enabled) {
reconnectTimer.current = setTimeout(connect, 3_000);
}
};
ws.onerror = () => {
ws.close();
};
} catch {
// WebSocket not available (e.g., SSR)
}
}, [url, onMessage, onOpen, onClose, enabled]);
const send = useCallback((action: string, payload: Record<string, unknown> = {}) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify({ action, ...payload }));
}
}, []);
const disconnect = useCallback(() => {
if (reconnectTimer.current) clearTimeout(reconnectTimer.current);
wsRef.current?.close();
}, []);
useEffect(() => {
mountedRef.current = true;
if (enabled) connect();
return () => {
mountedRef.current = false;
disconnect();
};
}, [connect, disconnect, enabled]);
return { send, disconnect, connect };
}
|