frigate/web/src/api/WsProvider.tsx
Josh Hawkins 43d97acd21
Some checks are pending
CI / AMD64 Build (push) Waiting to run
CI / ARM Build (push) Waiting to run
CI / Jetson Jetpack 6 (push) Waiting to run
CI / AMD64 Extra Build (push) Blocked by required conditions
CI / ARM Extra Build (push) Blocked by required conditions
CI / Synaptics Build (push) Blocked by required conditions
CI / Assemble and push default build (push) Blocked by required conditions
Miscellaneous fixes (#23238)
* start audio transcription post processor when enabled on any camera

* Fetch embed key whenever an error occurs in case the llama server was restarted

* mypy

* add tooltips for colored dots in settings menu

* add ability to reorder cameras from management pane

* add ability to reorder birdseye

* add reordering save text to camera management view

* Include NPU in latency performance hint

* Implement turbo for NPU on object detection

* hide order fields

* drop auto-derived field paths from camera value when unset globally

* use correct field type for export hwaccel args

* add debug replay to detail actions menu

* clarify debug replay in docs

* guard get_current_frame_time against missing camera state

* Implement debug reply from export

* Refactor debug replay to use sources for dynamic playback

* Mypy

* fix debug export replay source timestamp handling

* skip replay cameras in stats immediately

* broadcast debug replay state over ws and buffer pre-OPEN sends

- push debug replay session state over the job_state ws topic so the status bar reacts instantly to start/stop without polling
- fix child-effect-before-parent-effect race in WsProvider that silently dropped initial snapshot requests on cold load

* fix debug replay test hang

---------

Co-authored-by: Nicolas Mowen <nickmowen213@gmail.com>
2026-05-18 22:52:40 -05:00

97 lines
2.7 KiB
TypeScript

import { baseUrl } from "./baseUrl";
import { ReactNode, useCallback, useEffect, useRef } from "react";
import { WsSendContext } from "./wsContext";
import type { Update } from "./wsContext";
import { processWsMessage, resetWsStore } from "./ws";
export function WsProvider({ children }: { children: ReactNode }) {
const wsUrl = `${baseUrl.replace(/^http/, "ws")}ws`;
const wsRef = useRef<WebSocket | null>(null);
const reconnectTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const reconnectAttempt = useRef(0);
const unmounted = useRef(false);
const pendingSends = useRef<Map<string, unknown>>(new Map());
const sendJsonMessage = useCallback((msg: unknown) => {
if (wsRef.current?.readyState === WebSocket.OPEN) {
wsRef.current.send(JSON.stringify(msg));
} else if (msg && typeof msg === "object" && "topic" in msg) {
// Sends issued before the socket reaches OPEN (or during a reconnect
// window) are buffered here and flushed in onopen
pendingSends.current.set(String((msg as { topic: unknown }).topic), msg);
}
}, []);
useEffect(() => {
unmounted.current = false;
const queue = pendingSends.current;
function connect() {
if (unmounted.current) return;
const ws = new WebSocket(wsUrl);
wsRef.current = ws;
ws.onopen = () => {
reconnectAttempt.current = 0;
ws.send(
JSON.stringify({ topic: "onConnect", message: "", retain: false }),
);
for (const queued of queue.values()) {
ws.send(JSON.stringify(queued));
}
queue.clear();
};
ws.onmessage = (event: MessageEvent) => {
processWsMessage(event.data as string);
};
ws.onclose = () => {
if (unmounted.current) return;
const delay = Math.min(1000 * 2 ** reconnectAttempt.current, 30000);
reconnectAttempt.current++;
reconnectTimer.current = setTimeout(connect, delay);
};
ws.onerror = () => {
ws.close();
};
}
connect();
return () => {
unmounted.current = true;
if (reconnectTimer.current) {
clearTimeout(reconnectTimer.current);
}
const ws = wsRef.current;
if (ws) {
ws.onopen = null;
ws.onmessage = null;
ws.onclose = null;
ws.onerror = null;
ws.close();
}
queue.clear();
resetWsStore();
};
}, [wsUrl]);
const send = useCallback(
(message: Update) => {
sendJsonMessage({
topic: message.topic,
payload: message.payload,
retain: message.retain,
});
},
[sendJsonMessage],
);
return (
<WsSendContext.Provider value={send}>{children}</WsSendContext.Provider>
);
}