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 (
);
}
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;