frigate/web/src/components/timeline/EventSegment.tsx

289 lines
7.8 KiB
TypeScript
Raw Normal View History

2024-02-20 07:54:08 +03:00
import { useEventUtils } from "@/hooks/use-event-utils";
import { useSegmentUtils } from "@/hooks/use-segment-utils";
import { Event } from "@/types/event";
import { useMemo } from "react";
type EventSegmentProps = {
events: Event[];
segmentTime: number;
segmentDuration: number;
timestampSpread: number;
showMinimap: boolean;
minimapStartTime?: number;
minimapEndTime?: number;
severityType: string;
};
2024-02-20 18:32:53 +03:00
type MinimapSegmentProps = {
isFirstSegmentInMinimap: boolean;
isLastSegmentInMinimap: boolean;
alignedMinimapStartTime: number;
alignedMinimapEndTime: number;
};
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,
}: MinimapSegmentProps) {
return (
<>
{isFirstSegmentInMinimap && (
<div className="absolute inset-0 -bottom-5 w-full flex items-center justify-center text-xs text-primary font-medium z-20 text-center text-[9px]">
{new Date(alignedMinimapStartTime).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
month: "short",
day: "2-digit",
})}
</div>
)}
{isLastSegmentInMinimap && (
<div className="absolute inset-0 -top-1 w-full flex items-center justify-center text-xs text-primary font-medium z-20 text-center text-[9px]">
{new Date(alignedMinimapEndTime).toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
month: "short",
day: "2-digit",
})}
</div>
)}
</>
);
}
function Tick({
isFirstSegmentInMinimap,
isLastSegmentInMinimap,
timestamp,
timestampSpread,
}: TickSegmentProps) {
return (
<div className="w-5 h-2 flex justify-left items-end">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
<div
className={`h-0.5 ${
timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0
? "w-4 bg-gray-400"
: "w-2 bg-gray-600"
}`}
></div>
)}
</div>
);
}
function Timestamp({
isFirstSegmentInMinimap,
isLastSegmentInMinimap,
timestamp,
timestampSpread,
segmentKey,
}: TimestampSegmentProps) {
return (
<div className="w-10 h-2 flex justify-left items-top z-10">
{!isFirstSegmentInMinimap && !isLastSegmentInMinimap && (
<div
key={`${segmentKey}_timestamp`}
className="text-[8px] text-gray-400"
>
{timestamp.getMinutes() % timestampSpread === 0 &&
timestamp.getSeconds() === 0 &&
timestamp.toLocaleTimeString([], {
hour: "2-digit",
minute: "2-digit",
})}
</div>
)}
</div>
);
}
2024-02-20 07:54:08 +03:00
export function EventSegment({
events,
segmentTime,
segmentDuration,
timestampSpread,
showMinimap,
minimapStartTime,
minimapEndTime,
severityType,
}: EventSegmentProps) {
const { isStartOfEvent, isEndOfEvent } = useEventUtils(
events,
segmentDuration
);
const {
getSeverity,
getReviewed,
displaySeverityType,
shouldShowRoundedCorners,
} = useSegmentUtils(segmentDuration, events, severityType);
const { alignDateToTimeline } = useEventUtils(events, segmentDuration);
const severity = useMemo(
() => getSeverity(segmentTime),
[getSeverity, segmentTime]
);
const reviewed = useMemo(
() => getReviewed(segmentTime),
[getReviewed, segmentTime]
);
const showRoundedCorners = useMemo(
() => shouldShowRoundedCorners(segmentTime),
[shouldShowRoundedCorners, segmentTime]
);
const timestamp = useMemo(() => new Date(segmentTime), [segmentTime]);
const segmentKey = useMemo(
() => Math.floor(segmentTime / 1000),
[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 segmentClasses = `flex flex-row ${
showMinimap
? isInMinimapRange
2024-02-20 19:41:48 +03:00
? "bg-card"
2024-02-20 07:54:08 +03:00
: isLastSegmentInMinimap
? ""
2024-02-20 19:41:48 +03:00
: "opacity-70"
2024-02-20 07:54:08 +03:00
: ""
} ${
isFirstSegmentInMinimap || isLastSegmentInMinimap
? "relative h-2 border-b border-gray-500"
: ""
}`;
2024-02-20 18:32:53 +03:00
const severityColors: { [key: number]: string } = {
1: reviewed
2024-02-20 19:41:48 +03:00
? "from-severity_motion-dimmed/30 to-severity_motion/30"
: "from-severity_motion-dimmed to-severity_motion",
2024-02-20 18:32:53 +03:00
2: reviewed
2024-02-20 19:41:48 +03:00
? "from-severity_detection-dimmed/30 to-severity_detection/30"
: "from-severity_detection-dimmed to-severity_detection",
3: reviewed
? "from-severity_alert-dimmed/30 to-severity_alert/30"
: "from-severity_alert-dimmed to-severity_alert",
2024-02-20 18:32:53 +03:00
};
2024-02-20 07:54:08 +03:00
return (
<div key={segmentKey} className={segmentClasses}>
2024-02-20 18:32:53 +03:00
<MinimapBounds
isFirstSegmentInMinimap={isFirstSegmentInMinimap}
isLastSegmentInMinimap={isLastSegmentInMinimap}
alignedMinimapStartTime={alignedMinimapStartTime}
alignedMinimapEndTime={alignedMinimapEndTime}
/>
2024-02-20 07:54:08 +03:00
2024-02-20 18:32:53 +03:00
<Tick
isFirstSegmentInMinimap={isFirstSegmentInMinimap}
isLastSegmentInMinimap={isLastSegmentInMinimap}
timestamp={timestamp}
timestampSpread={timestampSpread}
/>
<Timestamp
isFirstSegmentInMinimap={isFirstSegmentInMinimap}
isLastSegmentInMinimap={isLastSegmentInMinimap}
timestamp={timestamp}
timestampSpread={timestampSpread}
segmentKey={segmentKey}
/>
2024-02-20 07:54:08 +03:00
{severity == displaySeverityType && (
<div className="mr-3 w-2 h-2 flex justify-left items-end">
<div
key={`${segmentKey}_primary_data`}
className={`
w-full h-2 bg-gradient-to-r
${
2024-02-20 08:34:07 +03:00
showRoundedCorners && isStartOfEvent(segmentTime)
2024-02-20 07:54:08 +03:00
? "rounded-bl-full rounded-br-full"
: ""
}
${
2024-02-20 08:34:07 +03:00
showRoundedCorners && isEndOfEvent(segmentTime)
2024-02-20 07:54:08 +03:00
? "rounded-tl-full rounded-tr-full"
: ""
}
2024-02-20 18:32:53 +03:00
${severityColors[severity]}
2024-02-20 07:54:08 +03:00
`}
></div>
</div>
)}
{severity != displaySeverityType && (
<div className="h-2 flex flex-grow justify-end items-end">
<div
key={`${segmentKey}_secondary_data`}
className={`
w-1 h-2 bg-gradient-to-r
${
2024-02-20 08:34:07 +03:00
showRoundedCorners && isStartOfEvent(segmentTime)
2024-02-20 07:54:08 +03:00
? "rounded-bl-full rounded-br-full"
: ""
}
${
2024-02-20 08:34:07 +03:00
showRoundedCorners && isEndOfEvent(segmentTime)
2024-02-20 07:54:08 +03:00
? "rounded-tl-full rounded-tr-full"
: ""
}
2024-02-20 18:32:53 +03:00
${severityColors[severity]}
2024-02-20 07:54:08 +03:00
`}
></div>
</div>
)}
</div>
);
}
export default EventSegment;