import { useCallback, useMemo, useRef, useState } from "react"; import Heading from "@/components/ui/heading"; import ActivityScrubber, { ScrubberItem, } from "@/components/scrubber/ActivityScrubber"; import useSWR from "swr"; import { FrigateConfig } from "@/types/frigateConfig"; import { Event } from "@/types/event"; import ActivityIndicator from "@/components/ui/activity-indicator"; import { useApiHost } from "@/api"; import TimelineScrubber from "@/components/playground/TimelineScrubber"; import EventReviewTimeline from "@/components/timeline/EventReviewTimeline"; import { ReviewData, ReviewSegment, ReviewSeverity } from "@/types/review"; import { Button } from "@/components/ui/button"; // 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 eventsToScrubberItems(events: Event[]): ScrubberItem[] { const apiHost = useApiHost(); return events.map((event: Event) => ({ id: event.id, content: `
${event.label}
`, start: new Date(event.start_time * 1000), end: event.end_time ? new Date(event.end_time * 1000) : undefined, type: "box", })); } 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 [timeline, setTimeline] = useState(undefined); const contentRef = useRef(null); const [mockEvents, setMockEvents] = useState([]); const [handlebarTime, setHandlebarTime] = useState( Math.floor(Date.now() / 1000) - 15 * 60 ); const onSelect = useCallback(({ items }: { items: string[] }) => { setTimeline(items[0]); }, []); const recentTimestamp = useMemo(() => { const now = new Date(); now.setMinutes(now.getMinutes() - 240); return now.getTime() / 1000; }, []); const { data: events } = useSWR([ "events", { limit: 10, after: recentTimestamp }, ]); useMemo(() => { const initialEvents = Array.from({ length: 50 }, generateRandomEvent); setMockEvents(initialEvents); }, []); // 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 }, [events]); 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 }, [events]); const [zoomLevel, setZoomLevel] = useState(0); const [zoomSettings, setZoomSettings] = useState({ segmentDuration: 60, timestampSpread: 15, }); 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); }; return ( <>
UI Playground Scrubber

Shows the 10 most recent events within the last 4 hours

{!config && } {config && (
{events && events.length > 0 && ( <> )}
)} {config && (
{timeline && ( <> )}
)}
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"}

Color scheme

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

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