From c160a0dfeb66b230bc1db2d6cbebf03bd7694f7d Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Thu, 30 Jul 2026 07:56:45 -0500 Subject: [PATCH] hide cameras with ui.review disabled from the Motion tab and the review summaries The Motion tab built its own camera list that never checked ui.review, so a hidden camera still got a preview tile, and its motion and overlap queries fell back to every allowed camera. The review and recordings summaries had the same gap: they are aggregate day counts that can't be filtered client side, so a hidden camera kept contributing to the severity tab counts and calendar indicators while its items were absent from the list. Filter the motion camera list on ui.review and query all four endpoints with the visible camera list instead of letting the backend default to all, and skip the summary queries until the config resolves so the counts don't briefly render as zero. --- web/src/pages/Events.tsx | 43 +++++++++++++++++++----------- web/src/views/events/EventView.tsx | 12 +++++++-- 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 88a210400e..bdf4bab4c4 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -84,6 +84,11 @@ export default function Events() { .map((conf) => conf.name); }, [allowedCameras, config?.cameras]); + const reviewCamerasParam = useMemo( + () => reviewCameras.join(","), + [reviewCameras], + ); + const selectedMotionSearchCamera = useMemo(() => { if (!motionSearchCamera) { return null; @@ -450,15 +455,17 @@ export default function Events() { // review summary const { data: reviewSummary, mutate: updateSummary } = useSWR( - [ - "review/summary", - { - timezone: timezone, - cameras: reviewSearchParams["cameras"] ?? null, - labels: reviewSearchParams["labels"] ?? null, - zones: reviewSearchParams["zones"] ?? null, - }, - ], + timezone + ? [ + "review/summary", + { + timezone: timezone, + cameras: reviewSearchParams["cameras"] ?? reviewCamerasParam, + labels: reviewSearchParams["labels"] ?? null, + zones: reviewSearchParams["zones"] ?? null, + }, + ] + : null, { revalidateOnFocus: true, refreshInterval: 30000, @@ -473,13 +480,17 @@ export default function Events() { // recordings summary - const { data: recordingsSummary } = useSWR([ - "recordings/summary", - { - timezone: timezone, - cameras: reviewSearchParams["cameras"] ?? null, - }, - ]); + const { data: recordingsSummary } = useSWR( + timezone + ? [ + "recordings/summary", + { + timezone: timezone, + cameras: reviewSearchParams["cameras"] ?? reviewCamerasParam, + }, + ] + : null, + ); // preview videos const previewTimes = useMemo(() => { diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index dd89e78780..50236b284a 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -1024,6 +1024,9 @@ function MotionReview({ if (!allowedCameras.includes(cam.name)) { return false; } + if (cam.ui?.review === false) { + return false; + } if (selectedCams && !selectedCams.includes(cam.name)) { return false; } @@ -1033,6 +1036,11 @@ function MotionReview({ return cameras.sort((a, b) => a.ui.order - b.ui.order); }, [config, filter, allowedCameras]); + const reviewCamerasParam = useMemo( + () => reviewCameras.map((cam) => cam.name).join(","), + [reviewCameras], + ); + const videoPlayersRef = useRef<{ [camera: string]: PreviewController }>({}); // motion data @@ -1052,7 +1060,7 @@ function MotionReview({ before: alignedBefore, after: alignedAfter, scale: segmentDuration / 2, - cameras: filter?.cameras?.join(",") ?? null, + cameras: reviewCamerasParam, }, ]); @@ -1061,7 +1069,7 @@ function MotionReview({ { before: alignedBefore, after: alignedAfter, - cameras: filter?.cameras?.join(",") ?? null, + cameras: reviewCamerasParam, }, ]);