diff --git a/web/src/components/HistoryViewer.jsx b/web/src/components/HistoryViewer.jsx new file mode 100644 index 000000000..b3207a51a --- /dev/null +++ b/web/src/components/HistoryViewer.jsx @@ -0,0 +1,117 @@ +import { Fragment, h } from 'preact'; +import { useEffect, useRef, useState } from 'preact/hooks'; +import { useApiHost, useEvents } from '../api'; +import { useSearchString } from '../hooks/useSearchString'; +import { Next } from '../icons/Next'; +import { Play } from '../icons/Play'; +import { Previous } from '../icons/Previous'; +import { HistoryHeader } from '../routes/HistoryHeader'; +import { longToDate } from '../utils/dateUtil'; +import Timeline from './Timeline'; + +export default function HistoryViewer({ camera }) { + const apiHost = useApiHost(); + const videoRef = useRef(); + + const beginningOfDay = new Date().setHours(0, 0, 0) / 1000; + const { searchString } = useSearchString(200, `camera=${camera}&after=${beginningOfDay}`); + const { data: events } = useEvents(searchString); + const [timelineEvents, setTimelineEvents] = useState(); + + const [currentEvent, setCurrentEvent] = useState(); + const [currentEventIndex, setCurrentEventIndex] = useState(); + const [timelineOffset, setTimelineOffset] = useState(0); + + useEffect(() => { + if (events) { + setTimelineEvents([...events].reverse().filter((e) => e.end_time !== undefined)); + } + }, [events]); + + const handleTimeUpdate = () => { + const timestamp = Math.round(videoRef.current.currentTime); + const offset = Math.round(timestamp); + const triggerStateChange = offset !== timelineOffset; + if (triggerStateChange) { + setTimelineOffset(offset); + } + }; + + const handleTimelineChange = (event) => { + console.log({ event }); + if (event !== undefined) { + setCurrentEvent(event); + setCurrentEventIndex(event.index); + } + }; + + const handleVideoTouch = () => { + setHideBanner(true); + }; + + const handlePlay = function () { + videoRef.current.play(); + }; + + const handlePaused = () => { + setTimelineOffset(undefined); + }; + + const handlePrevious = function () { + setCurrentEventIndex((index) => index - 1); + }; + + const handleNext = function () { + setCurrentEventIndex((index) => index + 1); + }; + + return ( + + {currentEvent && ( + +
+ + +
+
+ )} + + + +
+ + + +
+
+ ); +} diff --git a/web/src/components/Timeline.jsx b/web/src/components/Timeline.jsx index 8b47496a0..6c1311874 100644 --- a/web/src/components/Timeline.jsx +++ b/web/src/components/Timeline.jsx @@ -20,7 +20,7 @@ export default function Timeline({ events, offset, currentIndex, onChange }) { } const firstEventTime = longToDate(firstEvent.start_time); - const eventsMap = events.map((e, i) => { + const timelineEvents = events.map((e, i) => { const startTime = longToDate(e.start_time); const endTime = e.end_time ? longToDate(e.end_time) : new Date(); const seconds = Math.round(Math.abs(endTime - startTime) / 1000); @@ -35,16 +35,15 @@ export default function Timeline({ events, offset, currentIndex, onChange }) { }; }); - const recentEvent = eventsMap[eventsMap.length - 1]; - const event = { - ...recentEvent, - id: recentEvent.id, - index: eventsMap.length - 1, - startTime: recentEvent.start_time, - endTime: recentEvent.end_time, - }; - setCurrentEvent(event); - setTimeline(eventsMap); + const firstTimelineEvent = timelineEvents[0]; + setCurrentEvent({ + ...firstTimelineEvent, + id: firstTimelineEvent.id, + index: 0, + startTime: firstTimelineEvent.start_time, + endTime: firstTimelineEvent.end_time, + }); + setTimeline(timelineEvents); } }, [events, timelineOffset]); @@ -70,7 +69,7 @@ export default function Timeline({ events, offset, currentIndex, onChange }) { startTime: event.start_time, endTime: event.end_time, }); - timelineContainerRef.current.scroll({left: event.positionX - timelineOffset, behavior: "smooth"}) + timelineContainerRef.current.scroll({ left: event.positionX - timelineOffset, behavior: 'smooth' }); } }, [currentIndex]); @@ -128,7 +127,7 @@ export default function Timeline({ events, offset, currentIndex, onChange }) { const timelineLength = timelineOffset + lastEvent.positionX + lastEvent.width; return (
-
-
- {markerTime && {markerTime.toLocaleTimeString()}} +
+
+
+ {markerTime && {markerTime.toLocaleTimeString()}}
-
+
diff --git a/web/src/routes/Camera_V2.jsx b/web/src/routes/Camera_V2.jsx index 68df5b7e6..d50e04874 100644 --- a/web/src/routes/Camera_V2.jsx +++ b/web/src/routes/Camera_V2.jsx @@ -16,37 +16,19 @@ import { useSearchString } from '../hooks/useSearchString'; import { Previous } from '../icons/Previous'; import { Play } from '../icons/Play'; import { Next } from '../icons/Next'; +import HistoryViewer from '../components/HistoryViewer'; const emptyObject = Object.freeze({}); export default function Camera({ camera }) { - const apiHost = useApiHost(); - const videoRef = useRef(); - const { data: config } = useConfig(); - const beginningOfDay = new Date().setHours(0, 0, 0) / 1000; - const { searchString } = useSearchString(200, `camera=${camera}&after=${beginningOfDay}`); - const { data: events } = useEvents(searchString); - const [timelineEvents, setTimelineEvents] = useState(); - - const [hideBanner, setHideBanner] = useState(false); const [playerType, setPlayerType] = useState('live'); const cameraConfig = config?.cameras[camera]; const liveWidth = Math.round(cameraConfig.live.height * (cameraConfig.detect.width / cameraConfig.detect.height)); const [options, setOptions] = usePersistence(`${camera}-feed`, emptyObject); - const [currentEvent, setCurrentEvent] = useState(); - const [currentEventIndex, setCurrentEventIndex] = useState(); - const [timelineOffset, setTimelineOffset] = useState(0); - - useEffect(() => { - if (events) { - setTimelineEvents([...events].reverse().filter((e) => e.end_time !== undefined)); - } - }, [events]); - const handleSetOption = useCallback( (id, value) => { const newOptions = { ...options, [id]: value }; @@ -115,31 +97,12 @@ export default function Camera({ camera }) { ); break; case 'history': - if (currentEvent) { - renderPlayer = ( - - ); - } + renderPlayer = ; break; case 'debug': renderPlayer = ( -
- -
+ {optionContent}
); @@ -148,19 +111,6 @@ export default function Camera({ camera }) { break; } - const handleTimeUpdate = () => { - const timestamp = Math.round(videoRef.current.currentTime); - const offset = Math.round(timestamp); - const triggerStateChange = offset !== timelineOffset; - if (triggerStateChange) { - setTimelineOffset(offset); - } - }; - - const handleVideoTouch = () => { - setHideBanner(true); - }; - const handleTabChange = (index) => { if (index === 0) { setPlayerType('history'); @@ -171,29 +121,6 @@ export default function Camera({ camera }) { } }; - const handleTimelineChange = (event) => { - if (event !== undefined) { - setCurrentEvent(event); - setCurrentEventIndex(event.index); - } - }; - - const handlePlay = function () { - videoRef.current.play(); - }; - - const handlePaused = () => { - setTimelineOffset(undefined); - }; - - const handlePrevious = function () { - setCurrentEventIndex((index) => index - 1); - }; - - const handleNext = function () { - setCurrentEventIndex((index) => index + 1); - }; - return (
@@ -210,42 +137,7 @@ export default function Camera({ camera }) {
-
-
- {currentEvent && ( - - )} - {renderPlayer} -
- - {playerType === 'history' && ( - - - -
- - - -
-
- )} -
+
{renderPlayer}