import { useEventUtils } from "@/hooks/use-event-utils"; import { useSegmentUtils } from "@/hooks/use-segment-utils"; import { ReviewSegment, ReviewSeverity } from "@/types/review"; import React, { useEffect, useMemo, useRef } from "react"; type EventSegmentProps = { events: ReviewSegment[]; segmentTime: number; segmentDuration: number; timestampSpread: number; showMinimap: boolean; minimapStartTime?: number; minimapEndTime?: number; severityType: ReviewSeverity; }; type MinimapSegmentProps = { isFirstSegmentInMinimap: boolean; isLastSegmentInMinimap: boolean; alignedMinimapStartTime: number; alignedMinimapEndTime: number; firstMinimapSegmentRef: React.MutableRefObject; }; type TickSegmentProps = { isFirstSegmentInMinimap: boolean; isLastSegmentInMinimap: boolean; timestamp: Date; timestampSpread: number; }; type TimestampSegmentProps = { isFirstSegmentInMinimap: boolean; isLastSegmentInMinimap: boolean; timestamp: Date; timestampSpread: number; segmentKey: number; }; function MinimapBounds({ isFirstSegmentInMinimap, isLastSegmentInMinimap, alignedMinimapStartTime, alignedMinimapEndTime, firstMinimapSegmentRef, }: MinimapSegmentProps) { return ( <> {isFirstSegmentInMinimap && (
{new Date(alignedMinimapStartTime * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", month: "short", day: "2-digit", })}
)} {isLastSegmentInMinimap && (
{new Date(alignedMinimapEndTime * 1000).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", month: "short", day: "2-digit", })}
)} ); } function Tick({ isFirstSegmentInMinimap, isLastSegmentInMinimap, timestamp, timestampSpread, }: TickSegmentProps) { return (
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
)}
); } function Timestamp({ isFirstSegmentInMinimap, isLastSegmentInMinimap, timestamp, timestampSpread, segmentKey, }: TimestampSegmentProps) { return (
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
{timestamp.getMinutes() % timestampSpread === 0 && timestamp.getSeconds() === 0 && timestamp.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })}
)}
); } export function EventSegment({ events, segmentTime, segmentDuration, timestampSpread, showMinimap, minimapStartTime, minimapEndTime, severityType, }: EventSegmentProps) { const { getSeverity, getReviewed, displaySeverityType, shouldShowRoundedCorners, } = useSegmentUtils(segmentDuration, events, severityType); const { alignDateToTimeline } = useEventUtils(events, segmentDuration); const severity = useMemo( () => getSeverity(segmentTime, displaySeverityType), [getSeverity, segmentTime] ); const reviewed = useMemo( () => getReviewed(segmentTime), [getReviewed, segmentTime] ); const { roundTop, roundBottom } = useMemo( () => shouldShowRoundedCorners(segmentTime), [shouldShowRoundedCorners, segmentTime] ); const timestamp = useMemo(() => new Date(segmentTime * 1000), [segmentTime]); const segmentKey = useMemo(() => segmentTime, [segmentTime]); const alignedMinimapStartTime = useMemo( () => alignDateToTimeline(minimapStartTime ?? 0), [minimapStartTime, alignDateToTimeline] ); const alignedMinimapEndTime = useMemo( () => alignDateToTimeline(minimapEndTime ?? 0), [minimapEndTime, alignDateToTimeline] ); const isInMinimapRange = useMemo(() => { return ( showMinimap && minimapStartTime && minimapEndTime && segmentTime > minimapStartTime && segmentTime < minimapEndTime ); }, [showMinimap, minimapStartTime, minimapEndTime, segmentTime]); const isFirstSegmentInMinimap = useMemo(() => { return showMinimap && segmentTime === alignedMinimapStartTime; }, [showMinimap, segmentTime, alignedMinimapStartTime]); const isLastSegmentInMinimap = useMemo(() => { return showMinimap && segmentTime === alignedMinimapEndTime; }, [showMinimap, segmentTime, alignedMinimapEndTime]); const firstMinimapSegmentRef = useRef(null); let debounceTimer: ReturnType; function debounceScrollIntoView(element: HTMLElement) { clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { element.scrollIntoView({ behavior: "smooth", block: "center" }); }, 100); } useEffect(() => { // Check if the first segment is out of view const firstSegment = firstMinimapSegmentRef.current; if (firstSegment && showMinimap && isFirstSegmentInMinimap) { debounceScrollIntoView(firstSegment); } }, [showMinimap, isFirstSegmentInMinimap, events, segmentDuration]); const segmentClasses = `flex flex-row ${ showMinimap ? isInMinimapRange ? "bg-card" : isLastSegmentInMinimap ? "" : "opacity-70" : "" } ${ isFirstSegmentInMinimap || isLastSegmentInMinimap ? "relative h-2 border-b border-gray-500" : "" }`; const severityColors: { [key: number]: string } = { 1: reviewed ? "from-severity_motion-dimmed/50 to-severity_motion/50" : "from-severity_motion-dimmed to-severity_motion", 2: reviewed ? "from-severity_detection-dimmed/50 to-severity_detection/50" : "from-severity_detection-dimmed to-severity_detection", 3: reviewed ? "from-severity_alert-dimmed/50 to-severity_alert/50" : "from-severity_alert-dimmed to-severity_alert", }; return (
{severity.map((severityValue, index) => ( {severityValue === displaySeverityType && (
)} {severityValue !== displaySeverityType && (
)}
))}
); } export default EventSegment;