mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-03-10 02:29:19 +03:00
refactor usePolygonStates to use ws topic subscription
This commit is contained in:
parent
d273ff85bc
commit
14c3e08ad3
@ -1,18 +1,70 @@
|
|||||||
import { useMemo } from "react";
|
import { useCallback, useMemo, useSyncExternalStore } from "react";
|
||||||
import { Polygon } from "@/types/canvas";
|
import { Polygon } from "@/types/canvas";
|
||||||
import { useWsState } from "@/api/ws";
|
import { subscribeWsTopic, getWsTopicValue } from "@/api/ws";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook to get enabled state for a polygon from websocket state.
|
* Hook to get enabled state for a polygon from websocket state.
|
||||||
* Memoizes the lookup function to avoid unnecessary re-renders.
|
* Subscribes to all relevant per-polygon topics so it only re-renders
|
||||||
|
* when one of those specific topics changes — not on every WS update.
|
||||||
*/
|
*/
|
||||||
export function usePolygonStates(polygons: Polygon[]) {
|
export function usePolygonStates(polygons: Polygon[]) {
|
||||||
const wsState = useWsState();
|
// Build a stable sorted list of topics we need to watch
|
||||||
|
const topics = useMemo(() => {
|
||||||
|
const set = new Set<string>();
|
||||||
|
polygons.forEach((polygon) => {
|
||||||
|
const topic =
|
||||||
|
polygon.type === "zone"
|
||||||
|
? `${polygon.camera}/zone/${polygon.name}/state`
|
||||||
|
: polygon.type === "motion_mask"
|
||||||
|
? `${polygon.camera}/motion_mask/${polygon.name}/state`
|
||||||
|
: `${polygon.camera}/object_mask/${polygon.name}/state`;
|
||||||
|
set.add(topic);
|
||||||
|
});
|
||||||
|
return Array.from(set).sort();
|
||||||
|
}, [polygons]);
|
||||||
|
|
||||||
// Create a memoized lookup map that only updates when relevant ws values change
|
// Stable key for the topic list so subscribe/getSnapshot stay in sync
|
||||||
|
const topicsKey = topics.join("\0");
|
||||||
|
|
||||||
|
// Subscribe to all topics at once — re-subscribe only when the set changes
|
||||||
|
const subscribe = useCallback(
|
||||||
|
(listener: () => void) => {
|
||||||
|
const unsubscribes = topicsKey
|
||||||
|
.split("\0")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((topic) => subscribeWsTopic(topic, listener));
|
||||||
|
return () => unsubscribes.forEach((unsub) => unsub());
|
||||||
|
},
|
||||||
|
[topicsKey],
|
||||||
|
);
|
||||||
|
|
||||||
|
// Build a snapshot string from the current values of all topics.
|
||||||
|
// useSyncExternalStore uses Object.is, so we return a primitive that
|
||||||
|
// changes only when an observed topic's value changes.
|
||||||
|
const getSnapshot = useCallback(() => {
|
||||||
|
return topicsKey
|
||||||
|
.split("\0")
|
||||||
|
.filter(Boolean)
|
||||||
|
.map((topic) => `${topic}=${getWsTopicValue(topic) ?? ""}`)
|
||||||
|
.join("\0");
|
||||||
|
}, [topicsKey]);
|
||||||
|
|
||||||
|
const snapshot = useSyncExternalStore(subscribe, getSnapshot);
|
||||||
|
|
||||||
|
// Parse the snapshot into a lookup map
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
const stateMap = new Map<string, boolean>();
|
// Build value map from snapshot
|
||||||
|
const valueMap = new Map<string, unknown>();
|
||||||
|
snapshot.split("\0").forEach((entry) => {
|
||||||
|
const eqIdx = entry.indexOf("=");
|
||||||
|
if (eqIdx > 0) {
|
||||||
|
const topic = entry.slice(0, eqIdx);
|
||||||
|
const val = entry.slice(eqIdx + 1) || undefined;
|
||||||
|
valueMap.set(topic, val);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const stateMap = new Map<string, boolean>();
|
||||||
polygons.forEach((polygon) => {
|
polygons.forEach((polygon) => {
|
||||||
const topic =
|
const topic =
|
||||||
polygon.type === "zone"
|
polygon.type === "zone"
|
||||||
@ -21,7 +73,7 @@ export function usePolygonStates(polygons: Polygon[]) {
|
|||||||
? `${polygon.camera}/motion_mask/${polygon.name}/state`
|
? `${polygon.camera}/motion_mask/${polygon.name}/state`
|
||||||
: `${polygon.camera}/object_mask/${polygon.name}/state`;
|
: `${polygon.camera}/object_mask/${polygon.name}/state`;
|
||||||
|
|
||||||
const wsValue = wsState[topic];
|
const wsValue = valueMap.get(topic);
|
||||||
const enabled =
|
const enabled =
|
||||||
wsValue === "ON"
|
wsValue === "ON"
|
||||||
? true
|
? true
|
||||||
@ -40,5 +92,5 @@ export function usePolygonStates(polygons: Polygon[]) {
|
|||||||
true
|
true
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}, [polygons, wsState]);
|
}, [polygons, snapshot]);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user