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";
// 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 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 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;