import React from "react"; import { WsApi, type WsMessage } from "../../api/WsApi"; import { useSnackbar } from "../../hooks/contexts_provider/SnackbarProvider"; import CircleIcon from "@mui/icons-material/Circle"; import { Tooltip } from "@mui/material"; export const WSState = { Closed: "Closed", Connected: "Connected", Error: "Error", } as const; export function MatrixWS(p: { onMessage: (msg: WsMessage) => void; onStateChange?: (state: string) => void; }): React.ReactElement { const snackbar = useSnackbar(); // Keep only the latest version of onMessage const cbRef = React.useRef(p.onMessage); React.useEffect(() => { cbRef.current = p.onMessage; }, [p.onMessage]); // Keep only the latest version of onStateChange const stateCbRef = React.useRef(p.onStateChange); React.useEffect(() => { stateCbRef.current = p.onStateChange; }, [p.onStateChange]); const [state, setState] = React.useState(WSState.Closed); const wsId = React.useRef(undefined); const [connCount, setConnCount] = React.useState(0); React.useEffect(() => { const id = Math.random(); const ws = new WebSocket(WsApi.WsURL); wsId.current = id; // Open ws.onopen = () => { if (wsId.current != id) return; setState(WSState.Connected); stateCbRef.current?.(WSState.Connected); }; // Error ws.onerror = (e) => { if (wsId.current != id) return; console.error(`WS Debug error!`, e); snackbar(`WebSocket error!`); setState(WSState.Error); stateCbRef.current?.(WSState.Error); setTimeout(() => setConnCount(connCount + 1), 500); }; // Close ws.onclose = () => { if (wsId.current !== id) return; setState(WSState.Closed); stateCbRef.current?.(WSState.Closed); wsId.current = undefined; }; // Message ws.onmessage = (msg) => { if (wsId.current !== id) return; const dec = JSON.parse(msg.data); console.info("WS message", dec); cbRef.current(dec); }; return () => ws.close(); }, [connCount, snackbar]); return ( ); }