import { useMemo, useRef, useState } from "react"; import Heading from "@/components/ui/heading"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import ActivityIndicator from "@/components/indicators/activity-indicator"; import EventReviewTimeline from "@/components/timeline/EventReviewTimeline"; import { MotionData, ReviewData, ReviewSegment, ReviewSeverity, } from "@/types/review"; import { Button } from "@/components/ui/button"; import CameraActivityIndicator from "@/components/indicators/CameraActivityIndicator"; import MotionReviewTimeline from "@/components/timeline/MotionReviewTimeline"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "@/components/ui/select"; import BirdseyeLivePlayer from "@/components/player/BirdseyeLivePlayer"; import HlsVideoPlayer from "@/components/player/HlsVideoPlayer"; // Color data const colors = [ "background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "primary", "primary-foreground", "secondary", "secondary-foreground", "muted", "muted-foreground", "accent", "accent-foreground", "destructive", "destructive-foreground", "border", "input", "ring", ]; function ColorSwatch({ name, value }: { name: string; value: string }) { return (
{name}
); } function generateRandomMotionAudioData(): MotionData[] { const now = new Date(); const endTime = now.getTime() / 1000; const startTime = endTime - 24 * 60 * 60; // 24 hours ago const interval = 30; // 30 seconds const data = []; for ( let startTimestamp = startTime; startTimestamp < endTime; startTimestamp += interval ) { const motion = Math.floor(Math.random() * 101); // Random number between 0 and 100 const audio = Math.random() * -100; // Random negative value between -100 and 0 data.push({ start_time: startTimestamp, motion, audio, }); } return data; } const generateRandomEvent = (): ReviewSegment => { const start_time = Math.floor(Date.now() / 1000) - 10800 - Math.random() * 60 * 60; const end_time = Math.floor(start_time + Math.random() * 60 * 10); const severities: ReviewSeverity[] = [ "significant_motion", "detection", "alert", ]; const severity = severities[Math.floor(Math.random() * severities.length)]; const has_been_reviewed = Math.random() < 0.2; const id = new Date(start_time * 1000).toISOString(); // Date string as mock ID // You need to provide values for camera, thumb_path, and data const camera = "CameraXYZ"; const thumb_path = "/path/to/thumb"; const data: ReviewData = { audio: [], detections: [], objects: [], significant_motion_areas: [], zones: [], }; return { id, start_time, end_time, severity, has_been_reviewed, camera, thumb_path, data, }; }; function UIPlayground() { const { data: config } = useSWR("config"); const contentRef = useRef(null); const [mockEvents, setMockEvents] = useState([]); const [mockMotionData, setMockMotionData] = useState([]); const [handlebarTime, setHandlebarTime] = useState( Math.round((Date.now() / 1000 - 15 * 60) / 60) * 60, ); useMemo(() => { const initialEvents = Array.from({ length: 50 }, generateRandomEvent); setMockEvents(initialEvents); setMockMotionData(generateRandomMotionAudioData()); }, []); // Calculate minimap start and end times based on events const minimapStartTime = useMemo(() => { if (mockEvents && mockEvents.length > 0) { return Math.min(...mockEvents.map((event) => event.start_time)); } return Math.floor(Date.now() / 1000); // Default to current time if no events }, [mockEvents]); const minimapEndTime = useMemo(() => { if (mockEvents && mockEvents.length > 0) { return Math.max( ...mockEvents.map((event) => event.end_time ?? event.start_time), ); } return Math.floor(Date.now() / 1000); // Default to current time if no events }, [mockEvents]); const [zoomLevel, setZoomLevel] = useState(0); const [zoomSettings, setZoomSettings] = useState({ segmentDuration: 60, timestampSpread: 15, }); const videoRef = useRef(null); const possibleZoomLevels = [ { segmentDuration: 60, timestampSpread: 15 }, { segmentDuration: 30, timestampSpread: 5 }, { segmentDuration: 10, timestampSpread: 1 }, ]; function handleZoomIn() { const nextZoomLevel = Math.min( possibleZoomLevels.length - 1, zoomLevel + 1, ); setZoomLevel(nextZoomLevel); setZoomSettings(possibleZoomLevels[nextZoomLevel]); } function handleZoomOut() { const nextZoomLevel = Math.max(0, zoomLevel - 1); setZoomLevel(nextZoomLevel); setZoomSettings(possibleZoomLevels[nextZoomLevel]); } const [isDragging, setIsDragging] = useState(false); const handleDraggingChange = (dragging: boolean) => { setIsDragging(dragging); }; const [isEventsReviewTimeline, setIsEventsReviewTimeline] = useState(true); const birdseyeConfig = config?.birdseye; return ( <>
UI Playground Scrubber

Shows the 10 most recent events within the last 4 hours

{!config && }
Timeline

Handlebar timestamp: {handlebarTime} -  {new Date(handlebarTime * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", month: "short", day: "2-digit", second: "2-digit", })}

Handlebar is dragging: {isDragging ? "yes" : "no"}

Timeline type
{birdseyeConfig && ( )}

Color scheme

Colors as set by the current theme. See the{" "} shadcn theming docs {" "} for usage.

{colors.map((color, index) => ( ))}
{!isEventsReviewTimeline && ( )} {isEventsReviewTimeline && ( )}
); } export default UIPlayground;