Display WS state in favicon

This commit is contained in:
2025-12-01 08:44:30 +01:00
parent 64985bb39e
commit 7acb0cbafa
3 changed files with 39 additions and 10 deletions

View File

@@ -1,4 +1,5 @@
import Favicon from "react-favicon"; import Favicon from "react-favicon";
import { WSState } from "./MatrixWS";
// Taken from https://github.com/element-hq/element-web/blob/0577e245dac944bd85eea07b93a9762a93062f62/src/favicon.ts // Taken from https://github.com/element-hq/element-web/blob/0577e245dac944bd85eea07b93a9762a93062f62/src/favicon.ts
function getInitialFavicon(): HTMLLinkElement[] { function getInitialFavicon(): HTMLLinkElement[] {
@@ -21,6 +22,14 @@ let iconPath = getInitialFavicon()[0].getAttribute("href")!;
export function AppIconModifier(p: { export function AppIconModifier(p: {
numberUnread: number; numberUnread: number;
state: string;
}): React.ReactElement { }): React.ReactElement {
return <Favicon url={iconPath} alertCount={p.numberUnread} />; const isError = p.state === WSState.Error || p.state === WSState.Closed;
return (
<Favicon
url={iconPath}
alertFillColor={isError ? "orange" : undefined}
alertCount={isError ? "x" : p.numberUnread}
/>
);
} }

View File

@@ -90,6 +90,7 @@ function _MainMessageWidget(p: {
setRoomMgr(mgr); setRoomMgr(mgr);
}; };
const [wsState, setWsState] = React.useState("");
const handleWsEvent = (m: WsMessage) => { const handleWsEvent = (m: WsMessage) => {
// Process messages for current room // Process messages for current room
if (roomMgr?.processWsMessage(m)) { if (roomMgr?.processWsMessage(m)) {
@@ -120,11 +121,11 @@ function _MainMessageWidget(p: {
<div style={{ display: "flex", height: "100%" }}> <div style={{ display: "flex", height: "100%" }}>
{/* Websocket */} {/* Websocket */}
<div style={{ position: "absolute", right: "0px", padding: "10px" }}> <div style={{ position: "absolute", right: "0px", padding: "10px" }}>
<MatrixWS onMessage={handleWsEvent} /> <MatrixWS onMessage={handleWsEvent} onStateChange={setWsState} />
</div> </div>
{/** Application icon modifier */} {/** Application icon modifier */}
<AppIconModifier numberUnread={unreadRooms} /> <AppIconModifier numberUnread={unreadRooms} state={wsState} />
{/* Space selector */} {/* Space selector */}
<SpaceSelector {...p} selectedSpace={space} onChange={setSpace} /> <SpaceSelector {...p} selectedSpace={space} onChange={setSpace} />

View File

@@ -4,7 +4,7 @@ import { useSnackbar } from "../../hooks/contexts_provider/SnackbarProvider";
import CircleIcon from "@mui/icons-material/Circle"; import CircleIcon from "@mui/icons-material/Circle";
import { Tooltip } from "@mui/material"; import { Tooltip } from "@mui/material";
const State = { export const WSState = {
Closed: "Closed", Closed: "Closed",
Connected: "Connected", Connected: "Connected",
Error: "Error", Error: "Error",
@@ -12,6 +12,7 @@ const State = {
export function MatrixWS(p: { export function MatrixWS(p: {
onMessage: (msg: WsMessage) => void; onMessage: (msg: WsMessage) => void;
onStateChange?: (state: string) => void;
}): React.ReactElement { }): React.ReactElement {
const snackbar = useSnackbar(); const snackbar = useSnackbar();
@@ -21,7 +22,13 @@ export function MatrixWS(p: {
cbRef.current = p.onMessage; cbRef.current = p.onMessage;
}, [p.onMessage]); }, [p.onMessage]);
const [state, setState] = React.useState<string>(State.Closed); // 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 wsId = React.useRef<number | undefined>(undefined);
const [connCount, setConnCount] = React.useState(0); const [connCount, setConnCount] = React.useState(0);
@@ -30,23 +37,35 @@ export function MatrixWS(p: {
const ws = new WebSocket(WsApi.WsURL); const ws = new WebSocket(WsApi.WsURL);
wsId.current = id; wsId.current = id;
ws.onopen = () => setState(State.Connected); // Open
ws.onopen = () => {
if (wsId.current != id) return;
setState(WSState.Connected);
stateCbRef.current?.(WSState.Connected);
};
// Error
ws.onerror = (e) => { ws.onerror = (e) => {
if (wsId.current != id) return; if (wsId.current != id) return;
console.error(`WS Debug error!`, e); console.error(`WS Debug error!`, e);
snackbar(`WebSocket error!`); snackbar(`WebSocket error!`);
setState(State.Error); setState(WSState.Error);
stateCbRef.current?.(WSState.Error);
setTimeout(() => setConnCount(connCount + 1), 500); setTimeout(() => setConnCount(connCount + 1), 500);
}; };
// Close
ws.onclose = () => { ws.onclose = () => {
if (wsId.current !== id) return; if (wsId.current !== id) return;
setState(State.Closed); setState(WSState.Closed);
stateCbRef.current?.(WSState.Closed);
wsId.current = undefined; wsId.current = undefined;
}; };
// Message
ws.onmessage = (msg) => { ws.onmessage = (msg) => {
if (wsId.current !== id) return; if (wsId.current !== id) return;
@@ -62,9 +81,9 @@ export function MatrixWS(p: {
<Tooltip title={state}> <Tooltip title={state}>
<CircleIcon <CircleIcon
color={ color={
state === State.Connected state === WSState.Connected
? "success" ? "success"
: state === State.Error : state === WSState.Error
? "error" ? "error"
: undefined : undefined
} }