From 1102a4fe32d3638f234a130f7ad669ea208d30b9 Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Sat, 28 Feb 2026 17:19:57 -0600 Subject: [PATCH] add filters --- web/public/locales/en/views/system.json | 4 ++ web/src/components/ws/WsMessageFeed.tsx | 59 +++++++++++++++++++++++-- 2 files changed, 59 insertions(+), 4 deletions(-) diff --git a/web/public/locales/en/views/system.json b/web/public/locales/en/views/system.json index 826efd1f3..faaff31c9 100644 --- a/web/public/locales/en/views/system.json +++ b/web/public/locales/en/views/system.json @@ -23,6 +23,10 @@ "all": "All topics", "topics": "Topics", "events": "Events", + "reviews": "Reviews", + "classification": "Classification", + "face_recognition": "Face Recognition", + "lpr": "LPR", "camera_activity": "Camera activity", "system": "System", "camera": "Camera", diff --git a/web/src/components/ws/WsMessageFeed.tsx b/web/src/components/ws/WsMessageFeed.tsx index b7c79fe45..0da86a108 100644 --- a/web/src/components/ws/WsMessageFeed.tsx +++ b/web/src/components/ws/WsMessageFeed.tsx @@ -19,15 +19,31 @@ import FilterSwitch from "@/components/filter/FilterSwitch"; import { isMobile } from "react-device-detect"; import { isReplayCamera } from "@/utils/cameraUtil"; -type TopicCategory = "events" | "camera_activity" | "system"; +type TopicCategory = + | "events" + | "camera_activity" + | "system" + | "reviews" + | "classification" + | "face_recognition" + | "lpr"; + const ALL_TOPIC_CATEGORIES: TopicCategory[] = [ "events", + "reviews", + "classification", + "face_recognition", + "lpr", "camera_activity", "system", ]; const PRESET_TOPICS: Record> = { - events: new Set(["events", "reviews", "tracked_object_update", "triggers"]), + events: new Set(["events", "triggers"]), + reviews: new Set(["reviews"]), + classification: new Set(["tracked_object_update"]), + face_recognition: new Set(["tracked_object_update"]), + lpr: new Set(["tracked_object_update"]), camera_activity: new Set(["camera_activity", "audio_detections"]), system: new Set([ "stats", @@ -39,6 +55,13 @@ const PRESET_TOPICS: Record> = { ]), }; +// Maps tracked_object_update payload type to TopicCategory +const TRACKED_UPDATE_TYPE_MAP: Record = { + classification: "classification", + face: "face_recognition", + lpr: "lpr", +}; + // camera_activity preset also matches topics with camera prefix patterns const CAMERA_ACTIVITY_TOPIC_PATTERNS = [ "/motion", @@ -51,12 +74,40 @@ const CAMERA_ACTIVITY_TOPIC_PATTERNS = [ ]; function matchesCategories( - topic: string, + msg: WsFeedMessage, categories: TopicCategory[] | undefined, ): boolean { // undefined means all topics if (!categories) return true; + const { topic, payload } = msg; + + // Handle tracked_object_update with payload-based sub-categories + if (topic === "tracked_object_update") { + // payload might be a JSON string or a parsed object + let data: unknown = payload; + if (typeof data === "string") { + try { + data = JSON.parse(data); + } catch { + // not valid JSON, fall through + } + } + + const updateType = + data && typeof data === "object" && "type" in data + ? (data as { type: string }).type + : undefined; + + if (updateType && updateType in TRACKED_UPDATE_TYPE_MAP) { + const mappedCategory = TRACKED_UPDATE_TYPE_MAP[updateType]; + return categories.includes(mappedCategory); + } + + // tracked_object_update with other types (e.g. "description") falls under "events" + return categories.includes("events"); + } + for (const cat of categories) { const topicSet = PRESET_TOPICS[cat]; if (topicSet.has(topic)) return true; @@ -123,7 +174,7 @@ export default function WsMessageFeed({ const filteredMessages = useMemo(() => { return messages.filter((msg: WsFeedMessage) => { - if (!matchesCategories(msg.topic, selectedTopics)) return false; + if (!matchesCategories(msg, selectedTopics)) return false; return true; }); }, [messages, selectedTopics]);