From 1983de65288427e3e155dc85ef62b834e8260653 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Sun, 17 Mar 2024 06:30:19 -0600 Subject: [PATCH] Consolidate recordings view into one (#10496) --- web/src/pages/Events.tsx | 21 +- web/src/views/events/RecordingView.tsx | 346 ++++++------------------- 2 files changed, 78 insertions(+), 289 deletions(-) diff --git a/web/src/pages/Events.tsx b/web/src/pages/Events.tsx index 8035190e5..522027687 100644 --- a/web/src/pages/Events.tsx +++ b/web/src/pages/Events.tsx @@ -11,13 +11,9 @@ import { ReviewSummary, } from "@/types/review"; import EventView from "@/views/events/EventView"; -import { - DesktopRecordingView, - MobileRecordingView, -} from "@/views/events/RecordingView"; +import { RecordingView } from "@/views/events/RecordingView"; import axios from "axios"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { isMobile } from "react-device-detect"; import useSWR from "swr"; export default function Events() { @@ -319,21 +315,8 @@ export default function Events() { } if (selectedReviewData) { - if (isMobile) { - return ( - - ); - } - return ( - ("config"); const navigate = useNavigate(); const contentRef = useRef(null); @@ -207,15 +208,49 @@ export function DesktopRecordingView({ Back + {isMobile && ( + + + + + + { + setPlaybackStart(currentTime); + setMainCamera(cam); + }} + > + {allCameras.map((cam) => ( + + {cam.replaceAll("_", " ")} + + ))} + + + + )} -
+
-
- {allCameras.map((cam) => { - if (cam !== mainCamera) { - return ( -
- { - previewRefs.current[cam] = controller; - controller.scrubToTimestamp(startTime); - }} - onClick={() => onSelectCamera(cam)} - /> -
- ); - } - return null; - })} -
+ {isDesktop && ( +
+ {allCameras.map((cam) => { + if (cam !== mainCamera) { + return ( +
+ { + previewRefs.current[cam] = controller; + controller.scrubToTimestamp(startTime); + }} + onClick={() => onSelectCamera(cam)} + /> +
+ ); + } + return null; + })} +
+ )}
-
+
{severity != "significant_motion" ? ( ); } - -type MobileRecordingViewProps = { - startCamera: string; - startTime: number; - severity: ReviewSeverity; - reviewItems: ReviewSegment[]; - relevantPreviews?: Preview[]; - allCameras: string[]; -}; -export function MobileRecordingView({ - startCamera, - startTime, - severity, - reviewItems, - relevantPreviews, - allCameras, -}: MobileRecordingViewProps) { - const { data: config } = useSWR("config"); - const navigate = useNavigate(); - const contentRef = useRef(null); - - // controller state - - const controllerRef = useRef(undefined); - const [playbackCamera, setPlaybackCamera] = useState(startCamera); - const [playbackStart, setPlaybackStart] = useState(startTime); - - const grow = useMemo(() => { - if (!config) { - return "aspect-video"; - } - - const aspectRatio = - config.cameras[playbackCamera].detect.width / - config.cameras[playbackCamera].detect.height; - if (aspectRatio > 2) { - return "aspect-wide"; - } else { - return "aspect-video"; - } - }, [config, playbackCamera]); - - // timeline time - - const timeRange = useMemo(() => getChunkedTimeDay(startTime), [startTime]); - const [selectedRangeIdx, setSelectedRangeIdx] = useState( - timeRange.ranges.findIndex((chunk) => { - return chunk.start <= startTime && chunk.end >= startTime; - }), - ); - const currentTimeRange = useMemo( - () => timeRange.ranges[selectedRangeIdx], - [selectedRangeIdx, timeRange], - ); - - const mainCameraReviewItems = useMemo( - () => reviewItems.filter((cam) => cam.camera == playbackCamera), - [reviewItems, playbackCamera], - ); - - // handle clip change - - const onClipEnded = useCallback(() => { - if (!controllerRef.current) { - return; - } - - if (selectedRangeIdx < timeRange.ranges.length - 1) { - setSelectedRangeIdx(selectedRangeIdx + 1); - } - }, [selectedRangeIdx, timeRange]); - - // scrubbing and timeline state - - const [scrubbing, setScrubbing] = useState(false); - const [currentTime, setCurrentTime] = useState(startTime); - const [playerTime, setPlayerTime] = useState(startTime); - - const updateSelectedSegment = useCallback( - (currentTime: number, updateStartTime: boolean) => { - const index = timeRange.ranges.findIndex( - (seg) => seg.start <= currentTime && seg.end >= currentTime, - ); - - if (index != -1) { - if (updateStartTime) { - setPlaybackStart(currentTime); - } - - setSelectedRangeIdx(index); - } - }, - [timeRange], - ); - - useEffect(() => { - if (scrubbing) { - if ( - currentTime > currentTimeRange.end + 60 || - currentTime < currentTimeRange.start - 60 - ) { - updateSelectedSegment(currentTime, false); - return; - } - - controllerRef.current?.scrubToTimestamp(currentTime); - } - }, [ - currentTime, - scrubbing, - timeRange, - currentTimeRange, - updateSelectedSegment, - ]); - - useEffect(() => { - if (!scrubbing) { - if (Math.abs(currentTime - playerTime) > 10) { - if ( - currentTimeRange.start <= currentTime && - currentTimeRange.end >= currentTime - ) { - controllerRef.current?.seekToTimestamp(currentTime, true); - } else { - updateSelectedSegment(currentTime, true); - } - } - } - // we only want to seek when current time doesn't match the player update time - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [currentTime, scrubbing]); - - // motion timeline data - - const { data: motionData } = useSWR( - severity == "significant_motion" - ? [ - "review/activity/motion", - { - before: timeRange.end, - after: timeRange.start, - scale: SEGMENT_DURATION / 2, - cameras: playbackCamera, - }, - ] - : null, - ); - - return ( -
-
- - - - - - - { - setPlaybackStart(currentTime); - setPlaybackCamera(cam); - }} - > - {allCameras.map((cam) => ( - - {cam.replaceAll("_", " ")} - - ))} - - - -
- -
- { - controllerRef.current = controller; - }} - onTimestampUpdate={(timestamp) => { - setPlayerTime(timestamp); - setCurrentTime(timestamp); - }} - onClipEnded={onClipEnded} - isScrubbing={scrubbing} - /> -
- -
- {severity != "significant_motion" ? ( - setScrubbing(scrubbing)} - /> - ) : ( - setScrubbing(scrubbing)} - /> - )} -
-
- ); -}