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}