From 94d64920c47ed2aca3e5501b8830f2608cf18296 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Wed, 10 Apr 2024 09:37:26 -0600 Subject: [PATCH] Handle in progress review items --- .../player/PreviewThumbnailPlayer.tsx | 38 +++++++++++++++---- web/src/pages/Events.tsx | 16 +++++--- web/src/types/review.ts | 2 +- web/src/views/events/EventView.tsx | 22 ++++++----- web/src/views/live/LiveDashboardView.tsx | 4 +- 5 files changed, 56 insertions(+), 26 deletions(-) diff --git a/web/src/components/player/PreviewThumbnailPlayer.tsx b/web/src/components/player/PreviewThumbnailPlayer.tsx index a7a54ae60..0521bdf0b 100644 --- a/web/src/components/player/PreviewThumbnailPlayer.tsx +++ b/web/src/components/player/PreviewThumbnailPlayer.tsx @@ -21,11 +21,14 @@ import { useSwipeable } from "react-swipeable"; import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; import ImageLoadingIndicator from "../indicators/ImageLoadingIndicator"; import useContextMenu from "@/hooks/use-contextmenu"; +import ActivityIndicator from "../indicators/activity-indicator"; +import { TimeRange } from "@/types/timeline"; type PreviewPlayerProps = { review: ReviewSegment; allPreviews?: Preview[]; scrollLock?: boolean; + timeRange: TimeRange; onTimeUpdate?: (time: number | undefined) => void; setReviewed: (review: ReviewSegment) => void; onClick: (review: ReviewSegment, ctrl: boolean) => void; @@ -43,6 +46,7 @@ export default function PreviewThumbnailPlayer({ review, allPreviews, scrollLock = false, + timeRange, setReviewed, onClick, onTimeUpdate, @@ -70,8 +74,10 @@ export default function PreviewThumbnailPlayer({ }); const handleSetReviewed = useCallback(() => { - review.has_been_reviewed = true; - setReviewed(review); + if (review.end_time) { + review.has_been_reviewed = true; + setReviewed(review); + } }, [review, setReviewed]); useContextMenu(imgRef, () => { @@ -91,7 +97,7 @@ export default function PreviewThumbnailPlayer({ return false; } - if (review.end_time > preview.end) { + if ((review.end_time ?? timeRange.before) > preview.end) { multiHour = true; } @@ -108,7 +114,8 @@ export default function PreviewThumbnailPlayer({ const firstPrev = allPreviews[firstIndex]; const firstDuration = firstPrev.end - review.start_time; - const secondDuration = review.end_time - firstPrev.end; + const secondDuration = + (review.end_time ?? timeRange.before) - firstPrev.end; if (firstDuration > secondDuration) { // the first preview is longer than the second, return the first @@ -123,7 +130,7 @@ export default function PreviewThumbnailPlayer({ return undefined; } - }, [allPreviews, review]); + }, [allPreviews, review, timeRange]); // Hover Playback @@ -183,6 +190,7 @@ export default function PreviewThumbnailPlayer({
- + {review.end_time ? ( + + ) : ( +
+ +
+ )} {formattedDate}
@@ -270,6 +284,7 @@ export default function PreviewThumbnailPlayer({ type PreviewContentProps = { review: ReviewSegment; relevantPreview: Preview | undefined; + timeRange: TimeRange; setReviewed: () => void; setIgnoreClick: (ignore: boolean) => void; isPlayingBack: (ended: boolean) => void; @@ -278,6 +293,7 @@ type PreviewContentProps = { function PreviewContent({ review, relevantPreview, + timeRange, setReviewed, setIgnoreClick, isPlayingBack, @@ -300,6 +316,7 @@ function PreviewContent({ return ( review.end_time - review.start_time + PREVIEW_PADDING, + () => + (review.end_time ?? relevantPreview.end) - + review.start_time + + PREVIEW_PADDING, // we know that these deps are correct // eslint-disable-next-line react-hooks/exhaustive-deps [], @@ -551,6 +571,7 @@ function VideoPreview({ const MIN_LOAD_TIMEOUT_MS = 200; type InProgressPreviewProps = { review: ReviewSegment; + timeRange: TimeRange; setReviewed: (reviewId: string) => void; setIgnoreClick: (ignore: boolean) => void; isPlayingBack: (ended: boolean) => void; @@ -558,6 +579,7 @@ type InProgressPreviewProps = { }; function InProgressPreview({ review, + timeRange, setReviewed, setIgnoreClick, isPlayingBack, @@ -567,7 +589,7 @@ function InProgressPreview({ const sliderRef = useRef(null); const { data: previewFrames } = useSWR( `preview/${review.camera}/start/${Math.floor(review.start_time) - PREVIEW_PADDING}/end/${ - Math.ceil(review.end_time) + PREVIEW_PADDING + Math.ceil(review.end_time ?? timeRange.before) + PREVIEW_PADDING }/frames`, { revalidateOnFocus: false }, ); diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 91b8f1839..c9dac4460 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -204,7 +204,7 @@ export default function Events() { const newData = [...data]; newData.forEach((seg) => { - if (seg.severity == severity) { + if (seg.end_time && seg.severity == severity) { seg.has_been_reviewed = true; } }); @@ -214,10 +214,16 @@ export default function Events() { { revalidate: false, populateCache: true }, ); - await axios.post(`reviews/viewed`, { - ids: currentItems?.map((seg) => seg.id), - }); - reloadData(); + const itemsToMarkReviewed = currentItems + ?.filter((seg) => seg.end_time) + ?.map((seg) => seg.id); + + if (itemsToMarkReviewed.length > 0) { + await axios.post(`reviews/viewed`, { + ids: itemsToMarkReviewed, + }); + reloadData(); + } }, [reloadData, updateSegments], ); diff --git a/web/src/types/review.ts b/web/src/types/review.ts index e18d3b785..ea5bbb250 100644 --- a/web/src/types/review.ts +++ b/web/src/types/review.ts @@ -3,7 +3,7 @@ export interface ReviewSegment { camera: string; severity: ReviewSeverity; start_time: number; - end_time: number; + end_time?: number; thumb_path: string; has_been_reviewed: boolean; data: ReviewData; diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index def4aa972..66608b836 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -546,6 +546,7 @@ function DetectionReview({ - ((item.start_time >= segmentStartTime && - item.start_time < segmentEndTime) || - (item.end_time > segmentStartTime && - item.end_time <= segmentEndTime) || - (item.start_time <= segmentStartTime && - item.end_time >= segmentEndTime)) && - item.camera === cameraName, - ); + const matchingItem = reviewItems?.all.find((item) => { + const endTime = item.end_time ?? timeRange.before; + + ((item.start_time >= segmentStartTime && + item.start_time < segmentEndTime) || + (endTime > segmentStartTime && endTime <= segmentEndTime) || + (item.start_time <= segmentStartTime && + endTime >= segmentEndTime)) && + item.camera === cameraName; + }); return matchingItem ? matchingItem.severity : null; } @@ -805,6 +806,7 @@ function MotionReview({ reviewItems, motionData, currentTime, + timeRange, motionOnly, alignStartDateToTimeline, ], diff --git a/web/src/views/live/LiveDashboardView.tsx b/web/src/views/live/LiveDashboardView.tsx index c9f775da7..c8ddd1822 100644 --- a/web/src/views/live/LiveDashboardView.tsx +++ b/web/src/views/live/LiveDashboardView.tsx @@ -47,8 +47,8 @@ export default function LiveDashboardView({ } // if event is ended and was saved, update events list - if (eventUpdate.type == "end" && eventUpdate.review.severity == "alert") { - setTimeout(() => updateEvents(), 1000); + if (eventUpdate.review.severity == "alert") { + setTimeout(() => updateEvents(), eventUpdate.type == "end" ? 1000 : 6000); return; } }, [eventUpdate, updateEvents]);