import { useFrigateReviews } from "@/api/ws"; import Logo from "@/components/Logo"; import { AnimatedEventThumbnail } from "@/components/image/AnimatedEventThumbnail"; import LivePlayer from "@/components/player/LivePlayer"; import { Button } from "@/components/ui/button"; import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area"; import { TooltipProvider } from "@/components/ui/tooltip"; import { usePersistence } from "@/hooks/use-persistence"; import { CameraConfig } from "@/types/frigateConfig"; import { ReviewSegment } from "@/types/review"; import { useCallback, useEffect, useMemo, useState } from "react"; import { isDesktop, isMobile, isSafari } from "react-device-detect"; import { CiGrid2H, CiGrid31 } from "react-icons/ci"; import useSWR from "swr"; type LiveDashboardViewProps = { cameras: CameraConfig[]; onSelectCamera: (camera: string) => void; }; export default function LiveDashboardView({ cameras, onSelectCamera, }: LiveDashboardViewProps) { // layout const [layout, setLayout] = usePersistence<"grid" | "list">( "live-layout", isDesktop ? "grid" : "list", ); // recent events const { payload: eventUpdate } = useFrigateReviews(); const { data: allEvents, mutate: updateEvents } = useSWR([ "review", { limit: 10, severity: "alert" }, ]); useEffect(() => { if (!eventUpdate) { return; } // if event is ended and was saved, update events list if (eventUpdate.type == "end" && eventUpdate.review.severity == "alert") { updateEvents(); return; } }, [eventUpdate, updateEvents]); const events = useMemo(() => { if (!allEvents) { return []; } const date = new Date(); date.setHours(date.getHours() - 1); const cutoff = date.getTime() / 1000; return allEvents.filter((event) => event.start_time > cutoff); }, [allEvents]); // camera live views const [windowVisible, setWindowVisible] = useState(true); const visibilityListener = useCallback(() => { setWindowVisible(document.visibilityState == "visible"); }, []); useEffect(() => { addEventListener("visibilitychange", visibilityListener); return () => { removeEventListener("visibilitychange", visibilityListener); }; }, [visibilityListener]); return (
{isMobile && (
)} {events && events.length > 0 && (
{events.map((event) => { return ; })}
)}
{cameras.map((camera) => { let grow; const aspectRatio = camera.detect.width / camera.detect.height; if (aspectRatio > 2) { grow = `${layout == "grid" ? "col-span-2" : ""} aspect-wide`; } else if (aspectRatio < 1) { grow = `${layout == "grid" ? "row-span-2 aspect-tall md:h-full" : ""} aspect-tall`; } else { grow = "aspect-video"; } return ( onSelectCamera(camera.name)} /> ); })}
); }