From 6c783ff04a85ad1141ad9d4bfeec71fc278f98c4 Mon Sep 17 00:00:00 2001 From: Nicolas Mowen Date: Tue, 26 Mar 2024 14:59:43 -0600 Subject: [PATCH] Add custom time selection --- web/src/components/overlay/ExportDialog.tsx | 198 +++++++++++++++++++- 1 file changed, 193 insertions(+), 5 deletions(-) diff --git a/web/src/components/overlay/ExportDialog.tsx b/web/src/components/overlay/ExportDialog.tsx index c74d39ed8..3ac04cb98 100644 --- a/web/src/components/overlay/ExportDialog.tsx +++ b/web/src/components/overlay/ExportDialog.tsx @@ -1,4 +1,4 @@ -import { useCallback, useState } from "react"; +import { useCallback, useMemo, useState } from "react"; import { Dialog, DialogClose, @@ -12,11 +12,17 @@ import { Label } from "../ui/label"; import { RadioGroup, RadioGroupItem } from "../ui/radio-group"; import { Button } from "../ui/button"; import { ExportMode } from "@/types/filter"; -import { FaArrowDown } from "react-icons/fa"; +import { FaArrowDown, FaArrowRight, FaCalendarAlt } from "react-icons/fa"; import axios from "axios"; import { toast } from "sonner"; import { Input } from "../ui/input"; import { TimeRange } from "@/types/timeline"; +import { useFormattedTimestamp } from "@/hooks/use-date-utils"; +import useSWR from "swr"; +import { FrigateConfig } from "@/types/frigateConfig"; +import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; +import ReviewActivityCalendar from "./ReviewActivityCalendar"; +import { SelectSeparator } from "../ui/select"; const EXPORT_OPTIONS = [ "1", @@ -24,8 +30,8 @@ const EXPORT_OPTIONS = [ "8", "12", "24", - "custom", "timeline", + "custom", ] as const; type ExportOption = (typeof EXPORT_OPTIONS)[number]; @@ -35,7 +41,7 @@ type ExportDialogProps = { currentTime: number; range?: TimeRange; mode: ExportMode; - setRange: (range: TimeRange) => void; + setRange: (range: TimeRange | undefined) => void; setMode: (mode: ExportMode) => void; }; export default function ExportDialog({ @@ -104,6 +110,8 @@ export default function ExportDialog({ "Successfully started export. View the file in the /exports folder.", { position: "top-center" }, ); + setName(""); + setRange(undefined); } }) .catch((error) => { @@ -118,7 +126,7 @@ export default function ExportDialog({ }); } }); - }, [camera, name, range]); + }, [camera, name, range, setRange]); return ( + {selectedOption == "custom" && ( + + )} ); } + +type CustomTimeSelectorProps = { + latestTime: number; + range?: TimeRange; + setRange: (range: TimeRange | undefined) => void; +}; +function CustomTimeSelector({ + latestTime, + range, + setRange, +}: CustomTimeSelectorProps) { + const { data: config } = useSWR("config"); + + // times + + const startTime = useMemo( + () => range?.after || latestTime - 3600, + [range, latestTime], + ); + const endTime = useMemo( + () => range?.before || latestTime, + [range, latestTime], + ); + const formattedStart = useFormattedTimestamp( + startTime, + config?.ui.time_format == "24hour" + ? "%b %-d, %H:%M:%S" + : "%b %-d, %I:%M:%S %p", + ); + const formattedEnd = useFormattedTimestamp( + endTime, + config?.ui.time_format == "24hour" + ? "%b %-d, %H:%M:%S" + : "%b %-d, %I:%M:%S %p", + ); + + const startClock = useMemo(() => { + const date = new Date(startTime * 1000); + return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}`; + }, [startTime]); + const endClock = useMemo(() => { + const date = new Date(endTime * 1000); + return `${date.getHours().toString().padStart(2, "0")}:${date.getMinutes().toString().padStart(2, "0")}:${date.getSeconds().toString().padStart(2, "0")}`; + }, [endTime]); + + // calendars + + const [startOpen, setStartOpen] = useState(false); + const [endOpen, setEndOpen] = useState(false); + + return ( +
+ + { + if (!open) { + setStartOpen(false); + } + }} + > + + + + + { + if (!day) { + return; + } + + setRange({ + before: endTime, + after: day.getTime() / 1000 + 1, + }); + }} + /> + + { + const clock = e.target.value; + const [hour, minute, second] = clock.split(":"); + const start = new Date(startTime * 1000); + start.setHours( + parseInt(hour), + parseInt(minute), + parseInt(second), + 0, + ); + setRange({ + before: endTime, + after: start.getTime() / 1000, + }); + }} + /> + + + + { + if (!open) { + setEndOpen(false); + } + }} + > + + + + + { + if (!day) { + return; + } + + setRange({ + after: startTime, + before: day.getTime() / 1000, + }); + }} + /> + + { + const clock = e.target.value; + const [hour, minute, second] = clock.split(":"); + const end = new Date(endTime * 1000); + end.setHours( + parseInt(hour), + parseInt(minute), + parseInt(second), + 0, + ); + setRange({ + before: end.getTime() / 1000, + after: startTime, + }); + }} + /> + + +
+ ); +}