From 82e14d71fbc5affc1f5c2a5d7e2acc96c9f40c5b Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Wed, 15 Apr 2026 09:24:36 -0500 Subject: [PATCH] Fix review page spinner not clearing when review item ends (#22886) * fix review page spinner not clearing when review item ends * use last ended review item ID instead of counter * use separate displayItems memo to overlay end_time updates without re-filtering reviewed items --- web/src/pages/Events.tsx | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index c4a1f1c6d..d3545f242 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -24,6 +24,7 @@ import { import EventView from "@/views/events/EventView"; import MotionSearchView from "@/views/motion-search/MotionSearchView"; import { RecordingView } from "@/views/recording/RecordingView"; +import { useFrigateReviews } from "@/api/ws"; import axios from "axios"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -326,6 +327,29 @@ export default function Events() { }; }, [reviews]); + // update review items in place when a review segment ends + const reviewUpdate = useFrigateReviews(); + const [endedReviews, setEndedReviews] = useState( + new Map(), + ); + + useEffect(() => { + if (reviewUpdate?.type === "end") { + updateSegments( + (data) => { + if (!data) return data; + return data.map((seg) => + seg.id === reviewUpdate.after.id ? reviewUpdate.after : seg, + ); + }, + { revalidate: false, populateCache: true }, + ); + setEndedReviews((prev) => + new Map(prev).set(reviewUpdate.after.id, reviewUpdate.after), + ); + } + }, [reviewUpdate, updateSegments]); + const currentItems = useMemo(() => { if (!reviewItems || !severity) { return null; @@ -352,6 +376,13 @@ export default function Events() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [severity, reviewFilter, showReviewed, reviewItems?.all.length]); + // overlay end_time updates onto currentItems without re-running + // the has_been_reviewed filter, so hover-reviewed items stay visible + const displayItems = useMemo(() => { + if (!currentItems || endedReviews.size === 0) return currentItems; + return currentItems.map((seg) => endedReviews.get(seg.id) ?? seg); + }, [currentItems, endedReviews]); + // review summary const { data: reviewSummary, mutate: updateSummary } = useSWR( @@ -603,7 +634,7 @@ export default function Events() { ) : (