94 lines
2.3 KiB
TypeScript
94 lines
2.3 KiB
TypeScript
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<string>(WSState.Closed);
|
|
const wsId = React.useRef<number | undefined>(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 (
|
|
<Tooltip title={state}>
|
|
<CircleIcon
|
|
color={
|
|
state === WSState.Connected
|
|
? "success"
|
|
: state === WSState.Error
|
|
? "error"
|
|
: undefined
|
|
}
|
|
/>
|
|
</Tooltip>
|
|
);
|
|
}
|