From ab6bac1d2c0a35ba92241607b6932822c4536aaa Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 19 Mar 2024 14:56:38 -0600 Subject: [PATCH] Clean up selected data for recording (#10537) --- .../player/PreviewThumbnailPlayer.tsx | 6 +-- web/src/hooks/use-overlay-state.tsx | 2 +- web/src/pages/Events.tsx | 50 +++++-------------- web/src/types/record.ts | 8 +++ web/src/views/events/EventView.tsx | 35 ++++++++----- 5 files changed, 48 insertions(+), 53 deletions(-) diff --git a/web/src/components/player/PreviewThumbnailPlayer.tsx b/web/src/components/player/PreviewThumbnailPlayer.tsx index e3676e747..5e9a5c651 100644 --- a/web/src/components/player/PreviewThumbnailPlayer.tsx +++ b/web/src/components/player/PreviewThumbnailPlayer.tsx @@ -26,7 +26,7 @@ type PreviewPlayerProps = { scrollLock?: boolean; onTimeUpdate?: (time: number | undefined) => void; setReviewed: (review: ReviewSegment) => void; - onClick: (reviewId: string, ctrl: boolean) => void; + onClick: (review: ReviewSegment, ctrl: boolean) => void; }; type Preview = { @@ -55,7 +55,7 @@ export default function PreviewThumbnailPlayer({ const handleOnClick = useCallback( (e: React.MouseEvent) => { if (!ignoreClick) { - onClick(review.id, e.metaKey); + onClick(review, e.metaKey); } }, [ignoreClick, review, onClick], @@ -165,7 +165,7 @@ export default function PreviewThumbnailPlayer({ onMouseLeave={isMobile ? undefined : () => setIsHovered(false)} onContextMenu={(e) => { e.preventDefault(); - onClick(review.id, true); + onClick(review, true); }} onClick={handleOnClick} {...swipeHandlers} diff --git a/web/src/hooks/use-overlay-state.tsx b/web/src/hooks/use-overlay-state.tsx index 82fd0ee2d..c2f2a0f85 100644 --- a/web/src/hooks/use-overlay-state.tsx +++ b/web/src/hooks/use-overlay-state.tsx @@ -2,7 +2,7 @@ import { useCallback, useMemo } from "react"; import { useLocation, useNavigate } from "react-router-dom"; import { usePersistence } from "./use-persistence"; -export function useOverlayState( +export function useOverlayState( key: string, defaultValue: S | undefined = undefined, ): [S | undefined, (value: S, replace?: boolean) => void] { diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 522027687..3791300b9 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -4,6 +4,7 @@ import { useTimezone } from "@/hooks/use-date-utils"; import { useOverlayState } from "@/hooks/use-overlay-state"; import { FrigateConfig } from "@/types/frigateConfig"; import { Preview } from "@/types/preview"; +import { RecordingStartingPoint } from "@/types/record"; import { ReviewFilter, ReviewSegment, @@ -26,7 +27,8 @@ export default function Events() { "severity", "alert", ); - const [selectedReviewId, setSelectedReviewId] = useOverlayState("review"); + const [recording, setRecording] = + useOverlayState("recording"); const [startTime, setStartTime] = useState(); // review filter @@ -257,6 +259,10 @@ export default function Events() { // selected items const selectedReviewData = useMemo(() => { + if (!recording) { + return undefined; + } + if (!config) { return undefined; } @@ -265,50 +271,20 @@ export default function Events() { return undefined; } - if (!selectedReviewId) { - return undefined; - } - + setStartTime(recording.startTime); const allCameras = reviewFilter?.cameras ?? Object.keys(config.cameras); - if (selectedReviewId.startsWith("motion")) { - const motionData = selectedReviewId.split(","); - const motionStart = parseFloat(motionData[2]); - setStartTime(motionStart); - // format is motion,camera,start_time - return { - camera: motionData[1], - severity: "significant_motion" as ReviewSeverity, - start_time: motionStart, - allCameras: allCameras, - cameraSegments: reviews.filter((seg) => - allCameras.includes(seg.camera), - ), - }; - } - - const selectedReview = reviews.find((item) => item.id == selectedReviewId); - - if (!selectedReview) { - return undefined; - } - - // mark item as reviewed since it has been opened - if (!selectedReview?.has_been_reviewed) { - markItemAsReviewed(selectedReview); - } - return { - camera: selectedReview.camera, - severity: selectedReview.severity, - start_time: selectedReview.start_time, + camera: recording.camera, + severity: recording.severity, + start_time: recording.startTime, allCameras: allCameras, cameraSegments: reviews.filter((seg) => allCameras.includes(seg.camera)), }; // previews will not update after item is selected // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedReviewId, reviews]); + }, [recording, reviews]); if (!timezone) { return ; @@ -338,7 +314,7 @@ export default function Events() { setSeverity={setSeverity} markItemAsReviewed={markItemAsReviewed} markAllItemsAsReviewed={markAllItemsAsReviewed} - onOpenReview={setSelectedReviewId} + onOpenRecording={setRecording} pullLatestData={reloadData} updateFilter={onUpdateFilter} /> diff --git a/web/src/types/record.ts b/web/src/types/record.ts index 4522c1814..1efa8565f 100644 --- a/web/src/types/record.ts +++ b/web/src/types/record.ts @@ -1,3 +1,5 @@ +import { ReviewSeverity } from "./review"; + export type Recording = { id: string; camera: string; @@ -30,3 +32,9 @@ type RecordingSegmentActivity = { count: number; hasObjects: boolean; }; + +export type RecordingStartingPoint = { + camera: string; + startTime: number; + severity: ReviewSeverity; +}; diff --git a/web/src/views/events/EventView.tsx b/web/src/views/events/EventView.tsx index 796ee9964..32cc5283e 100644 --- a/web/src/views/events/EventView.tsx +++ b/web/src/views/events/EventView.tsx @@ -36,6 +36,7 @@ import { Button } from "@/components/ui/button"; import PreviewPlayer, { PreviewController, } from "@/components/player/PreviewPlayer"; +import { RecordingStartingPoint } from "@/types/record"; type EventViewProps = { reviews?: ReviewSegment[]; @@ -48,7 +49,7 @@ type EventViewProps = { setSeverity: (severity: ReviewSeverity) => void; markItemAsReviewed: (review: ReviewSegment) => void; markAllItemsAsReviewed: (currentItems: ReviewSegment[]) => void; - onOpenReview: (reviewId: string) => void; + onOpenRecording: (recordingInfo: RecordingStartingPoint) => void; pullLatestData: () => void; updateFilter: (filter: ReviewFilter) => void; }; @@ -63,7 +64,7 @@ export default function EventView({ setSeverity, markItemAsReviewed, markAllItemsAsReviewed, - onOpenReview, + onOpenRecording, pullLatestData, updateFilter, }: EventViewProps) { @@ -145,9 +146,9 @@ export default function EventView({ const [selectedReviews, setSelectedReviews] = useState([]); const onSelectReview = useCallback( - (reviewId: string, ctrl: boolean) => { + (review: ReviewSegment, ctrl: boolean) => { if (selectedReviews.length > 0 || ctrl) { - const index = selectedReviews.indexOf(reviewId); + const index = selectedReviews.indexOf(review.id); if (index != -1) { if (selectedReviews.length == 1) { @@ -161,14 +162,20 @@ export default function EventView({ } } else { const copy = [...selectedReviews]; - copy.push(reviewId); + copy.push(review.id); setSelectedReviews(copy); } } else { - onOpenReview(reviewId); + onOpenRecording({ + camera: review.camera, + startTime: review.start_time, + severity: review.severity, + }); + + markItemAsReviewed(review); } }, - [selectedReviews, setSelectedReviews, onOpenReview], + [selectedReviews, setSelectedReviews, onOpenRecording, markItemAsReviewed], ); const exportReview = useCallback( @@ -281,7 +288,7 @@ export default function EventView({ timeRange={timeRange} startTime={startTime} filter={filter} - onSelectReview={onSelectReview} + onOpenRecording={onOpenRecording} /> )} @@ -305,7 +312,7 @@ type DetectionReviewProps = { timeRange: { before: number; after: number }; markItemAsReviewed: (review: ReviewSegment) => void; markAllItemsAsReviewed: (currentItems: ReviewSegment[]) => void; - onSelectReview: (id: string, ctrl: boolean) => void; + onSelectReview: (review: ReviewSegment, ctrl: boolean) => void; pullLatestData: () => void; }; function DetectionReview({ @@ -552,7 +559,7 @@ type MotionReviewProps = { timeRange: { before: number; after: number }; startTime?: number; filter?: ReviewFilter; - onSelectReview: (data: string, ctrl: boolean) => void; + onOpenRecording: (data: RecordingStartingPoint) => void; }; function MotionReview({ contentRef, @@ -561,7 +568,7 @@ function MotionReview({ timeRange, startTime, filter, - onSelectReview, + onOpenRecording, }: MotionReviewProps) { const segmentDuration = 30; const { data: config } = useSWR("config"); @@ -688,7 +695,11 @@ function MotionReview({ videoPlayersRef.current[camera.name] = controller; }} onClick={() => - onSelectReview(`motion,${camera.name},${currentTime}`, false) + onOpenRecording({ + camera: camera.name, + startTime: currentTime, + severity: "significant_motion", + }) } /> );