diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 1d5846a2b..77c2a3f61 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -181,6 +181,7 @@ export default function Events() { return ( setSize(size + 1)} @@ -199,6 +200,7 @@ export default function Events() { loadNextPage={() => setSize(size + 1)} markItemAsReviewed={markItemAsReviewed} onSelectReview={setSelectedReviewId} + pullLatestData={updateSegments} /> ); } diff --git a/web/src/views/events/DesktopEventView.tsx b/web/src/views/events/DesktopEventView.tsx index 906cc8986..5fd186f60 100644 --- a/web/src/views/events/DesktopEventView.tsx +++ b/web/src/views/events/DesktopEventView.tsx @@ -1,3 +1,5 @@ +import { useFrigateEvents } from "@/api/ws"; +import Chip from "@/components/Chip"; import PreviewThumbnailPlayer from "@/components/player/PreviewThumbnailPlayer"; import EventReviewTimeline from "@/components/timeline/EventReviewTimeline"; import ActivityIndicator from "@/components/ui/activity-indicator"; @@ -13,7 +15,7 @@ import { FrigateConfig } from "@/types/frigateConfig"; import { ReviewSegment, ReviewSeverity } from "@/types/review"; import { formatUnixTimestampToDateTime } from "@/utils/dateUtil"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { LuCalendar, LuFilter, LuVideo } from "react-icons/lu"; +import { LuCalendar, LuFilter, LuRefreshCcw, LuVideo } from "react-icons/lu"; import { MdCircle } from "react-icons/md"; import useSWR from "swr"; @@ -26,6 +28,7 @@ type DesktopEventViewProps = { loadNextPage: () => void; markItemAsReviewed: (reviewId: string) => void; onSelectReview: (reviewId: string) => void; + pullLatestData: () => void; }; export default function DesktopEventView({ reviewPages, @@ -36,6 +39,7 @@ export default function DesktopEventView({ loadNextPage, markItemAsReviewed, onSelectReview, + pullLatestData, }: DesktopEventViewProps) { const { data: config } = useSWR("config"); const [severity, setSeverity] = useState("alert"); @@ -168,6 +172,25 @@ export default function DesktopEventView({ return data; }, [minimap]); + // new data alert + + const { payload: eventUpdate } = useFrigateEvents(); + const [hasUpdate, setHasUpdate] = useState(false); + useEffect(() => { + if (!eventUpdate) { + return; + } + + // if event is ended and was saved, update events list + if ( + eventUpdate.type == "end" && + (eventUpdate.after.has_clip || eventUpdate.after.has_snapshot) + ) { + setHasUpdate(true); + return; + } + }, [eventUpdate]); + if (!config) { return ; } @@ -225,6 +248,20 @@ export default function DesktopEventView({ + {hasUpdate && ( + + )} +
void; @@ -17,6 +17,7 @@ type MobileEventViewProps = { }; export default function MobileEventView({ reviewPages, + relevantPreviews, reachedEnd, isValidating, loadNextPage, @@ -153,34 +154,6 @@ export default function MobileEventView({ return data; }, [minimap]); - // preview videos - - const previewTimes = useMemo(() => { - if ( - !reviewPages || - reviewPages.length == 0 || - reviewPages.at(-1)!!.length == 0 - ) { - return undefined; - } - - const startDate = new Date(); - startDate.setMinutes(0, 0, 0); - - const endDate = new Date(reviewPages.at(-1)!!.at(-1)!!.end_time); - endDate.setHours(0, 0, 0, 0); - return { - start: startDate.getTime() / 1000, - end: endDate.getTime() / 1000, - }; - }, [reviewPages]); - const { data: allPreviews } = useSWR( - previewTimes - ? `preview/all/start/${previewTimes.start}/end/${previewTimes.end}` - : null, - { revalidateOnFocus: false } - ); - if (!config) { return ; } @@ -232,7 +205,7 @@ export default function MobileEventView({ {currentItems ? ( currentItems.map((value, segIdx) => { const lastRow = segIdx == reviewItems[severity].length - 1; - const relevantPreview = Object.values(allPreviews || []).find( + const relevantPreview = Object.values(relevantPreviews || []).find( (preview) => preview.camera == value.camera && preview.start < value.start_time &&