blakeblackshear.frigate/web/src/hooks/use-event-segment-utils.ts
Josh Hawkins f113acee33
Summary timeline (#10569)
* implement summary timeline

* implement summary timeline

* merge dev

* conditionally attach listeners only when dragging

* set up listeners with a ref
2024-03-20 20:56:15 -06:00

211 lines
6.0 KiB
TypeScript

import { useCallback, useMemo } from "react";
import { ReviewSegment } from "@/types/review";
export const useEventSegmentUtils = (
segmentDuration: number,
events: ReviewSegment[],
severityType: string,
) => {
const getSegmentStart = useCallback(
(time: number): number => {
return Math.floor(time / segmentDuration) * segmentDuration;
},
[segmentDuration],
);
const getSegmentEnd = useCallback(
(time: number | undefined): number => {
if (time) {
return (
Math.floor(time / segmentDuration) * segmentDuration + segmentDuration
);
} else {
return Date.now() / 1000 + segmentDuration;
}
},
[segmentDuration],
);
const mapSeverityToNumber = useCallback((severity: string): number => {
switch (severity) {
case "significant_motion":
return 1;
case "detection":
return 2;
case "alert":
return 3;
default:
return 0;
}
}, []);
const displaySeverityType = useMemo(
() => mapSeverityToNumber(severityType ?? ""),
[mapSeverityToNumber, severityType],
);
const getSeverity = useCallback(
(time: number, displaySeverityType: number): number[] => {
const activeEvents = events?.filter((event) => {
const segmentStart = getSegmentStart(event.start_time);
const segmentEnd = getSegmentEnd(event.end_time);
return time >= segmentStart && time < segmentEnd;
});
if (activeEvents?.length === 0) return [0];
const severityValues = activeEvents.map((event) =>
mapSeverityToNumber(event.severity),
);
const highestSeverityValue = Math.max(...severityValues);
if (severityValues.includes(displaySeverityType)) {
const otherSeverityValues = severityValues.filter(
(severity) => severity !== displaySeverityType,
);
const highestOtherSeverityValue = Math.max(...otherSeverityValues);
return [displaySeverityType, highestOtherSeverityValue];
} else {
return [highestSeverityValue];
}
},
[events, getSegmentStart, getSegmentEnd, mapSeverityToNumber],
);
const getReviewed = useCallback(
(time: number): boolean => {
return events.some((event) => {
const segmentStart = getSegmentStart(event.start_time);
const segmentEnd = getSegmentEnd(event.end_time);
return (
time >= segmentStart && time < segmentEnd && event.has_been_reviewed
);
});
},
[events, getSegmentStart, getSegmentEnd],
);
const shouldShowRoundedCorners = useCallback(
(
segmentTime: number,
): {
roundTopPrimary: boolean;
roundBottomPrimary: boolean;
roundTopSecondary: boolean;
roundBottomSecondary: boolean;
} => {
const prevSegmentTime = segmentTime - segmentDuration;
const nextSegmentTime = segmentTime + segmentDuration;
const severityEvents = events.filter((e) => e.severity === severityType);
const otherEvents = events.filter((e) => e.severity !== severityType);
const hasPrevSeverityEvent = severityEvents.some((e) => {
return (
prevSegmentTime >= getSegmentStart(e.start_time) &&
prevSegmentTime < getSegmentEnd(e.end_time)
);
});
const hasNextSeverityEvent = severityEvents.some((e) => {
return (
nextSegmentTime >= getSegmentStart(e.start_time) &&
nextSegmentTime < getSegmentEnd(e.end_time)
);
});
const hasPrevOtherEvent = otherEvents.some((e) => {
return (
prevSegmentTime >= getSegmentStart(e.start_time) &&
prevSegmentTime < getSegmentEnd(e.end_time)
);
});
const hasNextOtherEvent = otherEvents.some((e) => {
return (
nextSegmentTime >= getSegmentStart(e.start_time) &&
nextSegmentTime < getSegmentEnd(e.end_time)
);
});
const hasOverlappingSeverityEvent = severityEvents.some((e) => {
return (
segmentTime >= getSegmentStart(e.start_time) &&
segmentTime < getSegmentEnd(e.end_time)
);
});
const hasOverlappingOtherEvent = otherEvents.some((e) => {
return (
segmentTime >= getSegmentStart(e.start_time) &&
segmentTime < getSegmentEnd(e.end_time)
);
});
let roundTopPrimary = false;
let roundBottomPrimary = false;
let roundTopSecondary = false;
let roundBottomSecondary = false;
if (hasOverlappingSeverityEvent) {
roundBottomPrimary = !hasPrevSeverityEvent;
roundTopPrimary = !hasNextSeverityEvent;
}
if (hasOverlappingOtherEvent) {
roundBottomSecondary = !hasPrevOtherEvent;
roundTopSecondary = !hasNextOtherEvent;
}
return {
roundTopPrimary,
roundBottomPrimary,
roundTopSecondary,
roundBottomSecondary,
};
},
[events, getSegmentStart, getSegmentEnd, segmentDuration, severityType],
);
const getEventStart = useCallback(
(time: number): number => {
const matchingEvent = events.find((event) => {
return (
time >= getSegmentStart(event.start_time) &&
time < getSegmentEnd(event.end_time) &&
event.severity == severityType
);
});
return matchingEvent?.start_time ?? 0;
},
[events, getSegmentStart, getSegmentEnd, severityType],
);
const getEventThumbnail = useCallback(
(time: number): string => {
const matchingEvent = events.find((event) => {
return (
time >= getSegmentStart(event.start_time) &&
time < getSegmentEnd(event.end_time) &&
event.severity == severityType
);
});
return matchingEvent?.thumb_path ?? "";
},
[events, getSegmentStart, getSegmentEnd, severityType],
);
return {
getSegmentStart,
getSegmentEnd,
getSeverity,
displaySeverityType,
getReviewed,
shouldShowRoundedCorners,
getEventStart,
getEventThumbnail,
};
};