import { useCallback, useMemo, useState } from "react"; import { Drawer, DrawerContent, DrawerTrigger } from "../ui/drawer"; import { Button } from "../ui/button"; import { FaArrowDown, FaCalendarAlt, FaCog, FaFilter } from "react-icons/fa"; import { TimeRange } from "@/types/timeline"; import { ExportContent } from "./ExportDialog"; import { ExportMode } from "@/types/filter"; import ReviewActivityCalendar from "./ReviewActivityCalendar"; import { SelectSeparator } from "../ui/select"; import { ReviewFilter, ReviewSummary } from "@/types/review"; import { getEndOfDayTimestamp } from "@/utils/dateUtil"; import { GeneralFilterContent } from "../filter/ReviewFilterGroup"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { toast } from "sonner"; import axios from "axios"; import SaveExportOverlay from "./SaveExportOverlay"; import { isMobile } from "react-device-detect"; const ATTRIBUTES = ["amazon", "face", "fedex", "license_plate", "ups"]; type DrawerMode = "none" | "select" | "export" | "calendar" | "filter"; const DRAWER_FEATURES = ["export", "calendar", "filter"] as const; export type DrawerFeatures = (typeof DRAWER_FEATURES)[number]; const DEFAULT_DRAWER_FEATURES: DrawerFeatures[] = [ "export", "calendar", "filter", ]; type MobileReviewSettingsDrawerProps = { features?: DrawerFeatures[]; camera: string; filter?: ReviewFilter; latestTime: number; currentTime: number; range?: TimeRange; mode: ExportMode; reviewSummary?: ReviewSummary; onUpdateFilter: (filter: ReviewFilter) => void; setRange: (range: TimeRange | undefined) => void; setMode: (mode: ExportMode) => void; }; export default function MobileReviewSettingsDrawer({ features = DEFAULT_DRAWER_FEATURES, camera, filter, latestTime, currentTime, range, mode, reviewSummary, onUpdateFilter, setRange, setMode, }: MobileReviewSettingsDrawerProps) { const { data: config } = useSWR("config"); const [drawerMode, setDrawerMode] = useState("none"); // exports const [name, setName] = useState(""); const onStartExport = useCallback(() => { if (!range) { toast.error("No valid time range selected", { position: "top-center" }); return; } axios .post(`export/${camera}/start/${range.after}/end/${range.before}`, { playback: "realtime", name, }) .then((response) => { if (response.status == 200) { toast.success( "Successfully started export. View the file in the /exports folder.", { position: "top-center" }, ); setName(""); setRange(undefined); setMode("none"); } }) .catch((error) => { if (error.response?.data?.message) { toast.error( `Failed to start export: ${error.response.data.message}`, { position: "top-center" }, ); } else { toast.error(`Failed to start export: ${error.message}`, { position: "top-center", }); } }); }, [camera, name, range, setRange, setName, setMode]); // filters const allLabels = useMemo(() => { if (!config) { return []; } const labels = new Set(); const cameras = filter?.cameras || Object.keys(config.cameras); cameras.forEach((camera) => { const cameraConfig = config.cameras[camera]; cameraConfig.objects.track.forEach((label) => { if (!ATTRIBUTES.includes(label)) { labels.add(label); } }); if (cameraConfig.audio.enabled_in_config) { cameraConfig.audio.listen.forEach((label) => { labels.add(label); }); } }); return [...labels].sort(); }, [config, filter]); const [currentLabels, setCurrentLabels] = useState( filter?.labels, ); if (!isMobile) { return; } let content; if (drawerMode == "select") { content = (
{features.includes("export") && ( )} {features.includes("calendar") && ( )} {features.includes("filter") && ( )}
); } else if (drawerMode == "export") { content = ( { setMode(mode); if (mode == "timeline") { setDrawerMode("none"); } }} onCancel={() => { setMode("none"); setRange(undefined); setDrawerMode("select"); }} /> ); } else if (drawerMode == "calendar") { content = (
setDrawerMode("select")} > Back
Calendar
{ onUpdateFilter({ ...filter, after: day == undefined ? undefined : day.getTime() / 1000, before: day == undefined ? undefined : getEndOfDayTimestamp(day), }); }} />
); } else if (drawerMode == "filter") { content = (
setDrawerMode("select")} > Back
Filter
onUpdateFilter({ ...filter, labels: newLabels }) } onClose={() => setDrawerMode("select")} />
); } return ( <> onStartExport()} onCancel={() => setMode("none")} /> { if (!open) { setDrawerMode("none"); } }} > {content} ); }