mirror of
https://github.com/blakeblackshear/frigate.git
synced 2024-11-21 19:07:46 +01:00
fixes and changes (#10587)
This commit is contained in:
parent
865c26ff18
commit
4040191101
@ -43,7 +43,7 @@ export default function NewReviewData({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<div className="flex justify-center items-center md:mr-[115px] pointer-events-auto">
|
<div className="flex justify-center items-center mr-[65px] md:mr-[115px] pointer-events-auto">
|
||||||
<Button
|
<Button
|
||||||
className={`${
|
className={`${
|
||||||
hasUpdate
|
hasUpdate
|
||||||
|
@ -197,7 +197,12 @@ export function EventSegment({
|
|||||||
}, [startTimestamp]);
|
}, [startTimestamp]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={segmentKey} className={segmentClasses}>
|
<div
|
||||||
|
key={segmentKey}
|
||||||
|
className={segmentClasses}
|
||||||
|
onClick={segmentClick}
|
||||||
|
onTouchEnd={(event) => handleTouchStart(event, segmentClick)}
|
||||||
|
>
|
||||||
<MinimapBounds
|
<MinimapBounds
|
||||||
isFirstSegmentInMinimap={isFirstSegmentInMinimap}
|
isFirstSegmentInMinimap={isFirstSegmentInMinimap}
|
||||||
isLastSegmentInMinimap={isLastSegmentInMinimap}
|
isLastSegmentInMinimap={isLastSegmentInMinimap}
|
||||||
@ -219,34 +224,36 @@ export function EventSegment({
|
|||||||
{severity.map((severityValue: number, index: number) => (
|
{severity.map((severityValue: number, index: number) => (
|
||||||
<React.Fragment key={index}>
|
<React.Fragment key={index}>
|
||||||
{severityValue === displaySeverityType && (
|
{severityValue === displaySeverityType && (
|
||||||
<HoverCard openDelay={200} closeDelay={100}>
|
<div className="absolute left-1/2 transform -translate-x-1/2 w-[20px] md:w-[40px] h-2 z-10 cursor-pointer">
|
||||||
<div
|
<div className="flex flex-row justify-center w-[20px] md:w-[40px]">
|
||||||
className="absolute left-1/2 transform -translate-x-1/2 w-[8px] h-2 ml-[2px] z-10 cursor-pointer"
|
<div className="flex justify-center">
|
||||||
data-severity={severityValue}
|
<HoverCard openDelay={200} closeDelay={100}>
|
||||||
>
|
<div
|
||||||
<HoverCardTrigger asChild>
|
className="absolute left-1/2 transform -translate-x-1/2 w-[8px] h-2 ml-[2px] z-10 cursor-pointer"
|
||||||
<div
|
data-severity={severityValue}
|
||||||
key={`${segmentKey}_${index}_primary_data`}
|
>
|
||||||
className={`w-full h-2 bg-gradient-to-r ${roundBottomPrimary ? "rounded-bl-full rounded-br-full" : ""} ${roundTopPrimary ? "rounded-tl-full rounded-tr-full" : ""} ${severityColors[severityValue]}`}
|
<HoverCardTrigger asChild>
|
||||||
onClick={segmentClick}
|
<div
|
||||||
onTouchEnd={(event) =>
|
key={`${segmentKey}_${index}_primary_data`}
|
||||||
handleTouchStart(event, segmentClick)
|
className={`w-full h-2 bg-gradient-to-r ${roundBottomPrimary ? "rounded-bl-full rounded-br-full" : ""} ${roundTopPrimary ? "rounded-tl-full rounded-tr-full" : ""} ${severityColors[severityValue]}`}
|
||||||
}
|
></div>
|
||||||
></div>
|
</HoverCardTrigger>
|
||||||
</HoverCardTrigger>
|
<HoverCardPortal>
|
||||||
<HoverCardPortal>
|
<HoverCardContent
|
||||||
<HoverCardContent
|
className="rounded-2xl w-[250px] p-2"
|
||||||
className="rounded-2xl w-[250px] p-2"
|
side="left"
|
||||||
side="left"
|
>
|
||||||
>
|
<img
|
||||||
<img
|
className="rounded-lg"
|
||||||
className="rounded-lg"
|
src={`${apiHost}${eventThumbnail.replace("/media/frigate/", "")}`}
|
||||||
src={`${apiHost}${eventThumbnail.replace("/media/frigate/", "")}`}
|
/>
|
||||||
/>
|
</HoverCardContent>
|
||||||
</HoverCardContent>
|
</HoverCardPortal>
|
||||||
</HoverCardPortal>
|
</div>
|
||||||
|
</HoverCard>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</HoverCard>
|
</div>
|
||||||
)}
|
)}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
))}
|
))}
|
||||||
|
@ -11,6 +11,7 @@ import MotionSegment from "./MotionSegment";
|
|||||||
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
||||||
import { MotionData, ReviewSegment, ReviewSeverity } from "@/types/review";
|
import { MotionData, ReviewSegment, ReviewSeverity } from "@/types/review";
|
||||||
import ReviewTimeline from "./ReviewTimeline";
|
import ReviewTimeline from "./ReviewTimeline";
|
||||||
|
import { isDesktop } from "react-device-detect";
|
||||||
|
|
||||||
export type MotionReviewTimelineProps = {
|
export type MotionReviewTimelineProps = {
|
||||||
segmentDuration: number;
|
segmentDuration: number;
|
||||||
@ -218,7 +219,7 @@ export function MotionReviewTimeline({
|
|||||||
const segmentsObserver = useRef<IntersectionObserver | null>(null);
|
const segmentsObserver = useRef<IntersectionObserver | null>(null);
|
||||||
const selectedTimelineRef = timelineRef || internalTimelineRef;
|
const selectedTimelineRef = timelineRef || internalTimelineRef;
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedTimelineRef.current && segments) {
|
if (selectedTimelineRef.current && segments && isDesktop) {
|
||||||
segmentsObserver.current = new IntersectionObserver(
|
segmentsObserver.current = new IntersectionObserver(
|
||||||
(entries) => {
|
(entries) => {
|
||||||
entries.forEach((entry) => {
|
entries.forEach((entry) => {
|
||||||
|
@ -5,7 +5,7 @@ import React, { useCallback, useEffect, useMemo, useRef } from "react";
|
|||||||
import scrollIntoView from "scroll-into-view-if-needed";
|
import scrollIntoView from "scroll-into-view-if-needed";
|
||||||
import { MinimapBounds, Tick, Timestamp } from "./segment-metadata";
|
import { MinimapBounds, Tick, Timestamp } from "./segment-metadata";
|
||||||
import { useMotionSegmentUtils } from "@/hooks/use-motion-segment-utils";
|
import { useMotionSegmentUtils } from "@/hooks/use-motion-segment-utils";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isDesktop, isMobile } from "react-device-detect";
|
||||||
import useTapUtils from "@/hooks/use-tap-utils";
|
import useTapUtils from "@/hooks/use-tap-utils";
|
||||||
|
|
||||||
type MotionSegmentProps = {
|
type MotionSegmentProps = {
|
||||||
@ -155,6 +155,11 @@ export function MotionSegment({
|
|||||||
: ""
|
: ""
|
||||||
}`;
|
}`;
|
||||||
|
|
||||||
|
const animationClassesSecondHalf = `motion-segment ${secondHalfSegmentWidth > 1 ? "hidden" : ""}
|
||||||
|
zoom-in-[0.2] ${secondHalfSegmentWidth < 5 ? "duration-200" : "duration-1000"}`;
|
||||||
|
const animationClassesFirstHalf = `motion-segment ${firstHalfSegmentWidth > 1 ? "hidden" : ""}
|
||||||
|
zoom-in-[0.2] ${firstHalfSegmentWidth < 5 ? "duration-200" : "duration-1000"}`;
|
||||||
|
|
||||||
const severityColors: { [key: number]: string } = {
|
const severityColors: { [key: number]: string } = {
|
||||||
1: reviewed
|
1: reviewed
|
||||||
? "from-severity_motion-dimmed/50 to-severity_motion/50"
|
? "from-severity_motion-dimmed/50 to-severity_motion/50"
|
||||||
@ -204,7 +209,7 @@ export function MotionSegment({
|
|||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<div
|
<div
|
||||||
key={`${segmentKey}_motion_data_1`}
|
key={`${segmentKey}_motion_data_1`}
|
||||||
className={`motion-segment ${secondHalfSegmentWidth > 1 ? "hidden" : ""} zoom-in-[0.2] ${secondHalfSegmentWidth < 5 ? "duration-200" : "duration-1000"} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
|
className={`${isDesktop && animationClassesSecondHalf} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
|
||||||
style={{
|
style={{
|
||||||
width: secondHalfSegmentWidth,
|
width: secondHalfSegmentWidth,
|
||||||
}}
|
}}
|
||||||
@ -216,7 +221,7 @@ export function MotionSegment({
|
|||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<div
|
<div
|
||||||
key={`${segmentKey}_motion_data_2`}
|
key={`${segmentKey}_motion_data_2`}
|
||||||
className={`motion-segment ${firstHalfSegmentWidth > 1 ? "hidden" : ""} zoom-in-[0.2] ${firstHalfSegmentWidth < 5 ? "duration-200" : "duration-1000"} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
|
className={`${isDesktop && animationClassesFirstHalf} h-[2px] rounded-full ${severity[0] != 0 ? "bg-motion_review-dimmed" : "bg-motion_review"}`}
|
||||||
style={{
|
style={{
|
||||||
width: firstHalfSegmentWidth,
|
width: firstHalfSegmentWidth,
|
||||||
}}
|
}}
|
||||||
|
@ -14,16 +14,8 @@ export type ReviewTimelineProps = {
|
|||||||
timelineRef: RefObject<HTMLDivElement>;
|
timelineRef: RefObject<HTMLDivElement>;
|
||||||
handlebarRef: RefObject<HTMLDivElement>;
|
handlebarRef: RefObject<HTMLDivElement>;
|
||||||
handlebarTimeRef: RefObject<HTMLDivElement>;
|
handlebarTimeRef: RefObject<HTMLDivElement>;
|
||||||
handlebarMouseMove: (
|
handlebarMouseMove: (e: MouseEvent | TouchEvent) => void;
|
||||||
e:
|
handlebarMouseUp: (e: MouseEvent | TouchEvent) => void;
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => void;
|
|
||||||
handlebarMouseUp: (
|
|
||||||
e:
|
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => void;
|
|
||||||
handlebarMouseDown: (
|
handlebarMouseDown: (
|
||||||
e:
|
e:
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||||
@ -37,31 +29,15 @@ export type ReviewTimelineProps = {
|
|||||||
exportStartTimeRef: RefObject<HTMLDivElement>;
|
exportStartTimeRef: RefObject<HTMLDivElement>;
|
||||||
exportEndRef: RefObject<HTMLDivElement>;
|
exportEndRef: RefObject<HTMLDivElement>;
|
||||||
exportEndTimeRef: RefObject<HTMLDivElement>;
|
exportEndTimeRef: RefObject<HTMLDivElement>;
|
||||||
exportStartMouseMove: (
|
exportStartMouseMove: (e: MouseEvent | TouchEvent) => void;
|
||||||
e:
|
exportStartMouseUp: (e: MouseEvent | TouchEvent) => void;
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => void;
|
|
||||||
exportStartMouseUp: (
|
|
||||||
e:
|
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => void;
|
|
||||||
exportStartMouseDown: (
|
exportStartMouseDown: (
|
||||||
e:
|
e:
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
| React.TouchEvent<HTMLDivElement>,
|
||||||
) => void;
|
) => void;
|
||||||
exportEndMouseMove: (
|
exportEndMouseMove: (e: MouseEvent | TouchEvent) => void;
|
||||||
e:
|
exportEndMouseUp: (e: MouseEvent | TouchEvent) => void;
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => void;
|
|
||||||
exportEndMouseUp: (
|
|
||||||
e:
|
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => void;
|
|
||||||
exportEndMouseDown: (
|
exportEndMouseDown: (
|
||||||
e:
|
e:
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
||||||
@ -152,11 +128,7 @@ export function ReviewTimeline({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseMove = useCallback(
|
const handleMouseMove = useCallback(
|
||||||
(
|
(e: MouseEvent | TouchEvent) => {
|
||||||
e:
|
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => {
|
|
||||||
switch (draggableElementType) {
|
switch (draggableElementType) {
|
||||||
case "export_start":
|
case "export_start":
|
||||||
exportStartMouseMove(e);
|
exportStartMouseMove(e);
|
||||||
@ -181,11 +153,7 @@ export function ReviewTimeline({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseUp = useCallback(
|
const handleMouseUp = useCallback(
|
||||||
(
|
(e: MouseEvent | TouchEvent) => {
|
||||||
e:
|
|
||||||
| React.MouseEvent<HTMLDivElement, MouseEvent>
|
|
||||||
| React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => {
|
|
||||||
switch (draggableElementType) {
|
switch (draggableElementType) {
|
||||||
case "export_start":
|
case "export_start":
|
||||||
exportStartMouseUp(e);
|
exportStartMouseUp(e);
|
||||||
@ -227,13 +195,32 @@ export function ReviewTimeline({
|
|||||||
exportEndPosition,
|
exportEndPosition,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
const documentRef = useRef<Document | null>(document);
|
||||||
|
useEffect(() => {
|
||||||
|
const documentInstance = documentRef.current;
|
||||||
|
|
||||||
|
if (isDragging) {
|
||||||
|
documentInstance?.addEventListener("mousemove", handleMouseMove);
|
||||||
|
documentInstance?.addEventListener("touchmove", handleMouseMove);
|
||||||
|
documentInstance?.addEventListener("mouseup", handleMouseUp);
|
||||||
|
documentInstance?.addEventListener("touchend", handleMouseUp);
|
||||||
|
} else {
|
||||||
|
documentInstance?.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
documentInstance?.removeEventListener("touchmove", handleMouseMove);
|
||||||
|
documentInstance?.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
documentInstance?.removeEventListener("touchend", handleMouseUp);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
documentInstance?.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
documentInstance?.removeEventListener("touchmove", handleMouseMove);
|
||||||
|
documentInstance?.removeEventListener("mouseup", handleMouseUp);
|
||||||
|
documentInstance?.removeEventListener("touchend", handleMouseUp);
|
||||||
|
};
|
||||||
|
}, [handleMouseMove, handleMouseUp, isDragging]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={timelineRef}
|
ref={timelineRef}
|
||||||
onMouseMove={handleMouseMove}
|
|
||||||
onTouchMove={handleMouseMove}
|
|
||||||
onMouseUp={handleMouseUp}
|
|
||||||
onTouchEnd={handleMouseUp}
|
|
||||||
className={`relative h-full overflow-y-auto no-scrollbar select-none bg-secondary ${
|
className={`relative h-full overflow-y-auto no-scrollbar select-none bg-secondary ${
|
||||||
isDragging && (showHandlebar || showExportHandles)
|
isDragging && (showHandlebar || showExportHandles)
|
||||||
? "cursor-grabbing"
|
? "cursor-grabbing"
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useEventSegmentUtils } from "@/hooks/use-event-segment-utils";
|
import { useEventSegmentUtils } from "@/hooks/use-event-segment-utils";
|
||||||
import { ReviewSegment } from "@/types/review";
|
import { ReviewSegment, ReviewSeverity } from "@/types/review";
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
// import useTapUtils from "@/hooks/use-tap-utils";
|
// import useTapUtils from "@/hooks/use-tap-utils";
|
||||||
|
|
||||||
@ -8,6 +8,7 @@ type SummarySegmentProps = {
|
|||||||
segmentTime: number;
|
segmentTime: number;
|
||||||
segmentDuration: number;
|
segmentDuration: number;
|
||||||
segmentHeight: number;
|
segmentHeight: number;
|
||||||
|
severityType: ReviewSeverity;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SummarySegment({
|
export function SummarySegment({
|
||||||
@ -15,8 +16,8 @@ export function SummarySegment({
|
|||||||
segmentTime,
|
segmentTime,
|
||||||
segmentDuration,
|
segmentDuration,
|
||||||
segmentHeight,
|
segmentHeight,
|
||||||
|
severityType,
|
||||||
}: SummarySegmentProps) {
|
}: SummarySegmentProps) {
|
||||||
const severityType = "all";
|
|
||||||
const { getSeverity, getReviewed, displaySeverityType } =
|
const { getSeverity, getReviewed, displaySeverityType } =
|
||||||
useEventSegmentUtils(segmentDuration, events, severityType);
|
useEventSegmentUtils(segmentDuration, events, severityType);
|
||||||
|
|
||||||
@ -44,9 +45,9 @@ export function SummarySegment({
|
|||||||
className="relative w-full"
|
className="relative w-full"
|
||||||
style={{ height: segmentHeight }}
|
style={{ height: segmentHeight }}
|
||||||
>
|
>
|
||||||
{severity.map((severityValue: number, index: number) => {
|
{severity.map((severityValue: number, index: number) => (
|
||||||
return (
|
<React.Fragment key={index}>
|
||||||
<React.Fragment key={index}>
|
{severityValue === displaySeverityType && (
|
||||||
<div
|
<div
|
||||||
className="flex justify-end cursor-pointer"
|
className="flex justify-end cursor-pointer"
|
||||||
style={{ height: segmentHeight }}
|
style={{ height: segmentHeight }}
|
||||||
@ -57,9 +58,9 @@ export function SummarySegment({
|
|||||||
className={`w-[10px] ${severityColors[severityValue]}`}
|
className={`w-[10px] ${severityColors[severityValue]}`}
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
)}
|
||||||
);
|
</React.Fragment>
|
||||||
})}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from "react";
|
} from "react";
|
||||||
import { SummarySegment } from "./SummarySegment";
|
import { SummarySegment } from "./SummarySegment";
|
||||||
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
||||||
import { ReviewSegment } from "@/types/review";
|
import { ReviewSegment, ReviewSeverity } from "@/types/review";
|
||||||
import { isMobile } from "react-device-detect";
|
import { isMobile } from "react-device-detect";
|
||||||
|
|
||||||
export type SummaryTimelineProps = {
|
export type SummaryTimelineProps = {
|
||||||
@ -17,6 +17,7 @@ export type SummaryTimelineProps = {
|
|||||||
timelineEnd: number;
|
timelineEnd: number;
|
||||||
segmentDuration: number;
|
segmentDuration: number;
|
||||||
events: ReviewSegment[];
|
events: ReviewSegment[];
|
||||||
|
severityType: ReviewSeverity;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function SummaryTimeline({
|
export function SummaryTimeline({
|
||||||
@ -25,6 +26,7 @@ export function SummaryTimeline({
|
|||||||
timelineEnd,
|
timelineEnd,
|
||||||
segmentDuration,
|
segmentDuration,
|
||||||
events,
|
events,
|
||||||
|
severityType,
|
||||||
}: SummaryTimelineProps) {
|
}: SummaryTimelineProps) {
|
||||||
const summaryTimelineRef = useRef<HTMLDivElement>(null);
|
const summaryTimelineRef = useRef<HTMLDivElement>(null);
|
||||||
const visibleSectionRef = useRef<HTMLDivElement>(null);
|
const visibleSectionRef = useRef<HTMLDivElement>(null);
|
||||||
@ -35,6 +37,8 @@ export function SummaryTimeline({
|
|||||||
const [initialReviewTimelineScrollTop, setInitialReviewTimelineScrollTop] =
|
const [initialReviewTimelineScrollTop, setInitialReviewTimelineScrollTop] =
|
||||||
useState<number>(0);
|
useState<number>(0);
|
||||||
|
|
||||||
|
const observer = useRef<ResizeObserver | null>(null);
|
||||||
|
|
||||||
const { alignStartDateToTimeline } = useTimelineUtils(segmentDuration);
|
const { alignStartDateToTimeline } = useTimelineUtils(segmentDuration);
|
||||||
|
|
||||||
const timelineStartAligned = useMemo(
|
const timelineStartAligned = useMemo(
|
||||||
@ -62,6 +66,7 @@ export function SummaryTimeline({
|
|||||||
segmentDuration={segmentDuration}
|
segmentDuration={segmentDuration}
|
||||||
segmentTime={segmentTime}
|
segmentTime={segmentTime}
|
||||||
segmentHeight={segmentHeight}
|
segmentHeight={segmentHeight}
|
||||||
|
severityType={severityType}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -72,6 +77,7 @@ export function SummaryTimeline({
|
|||||||
events,
|
events,
|
||||||
reviewTimelineDuration,
|
reviewTimelineDuration,
|
||||||
segmentHeight,
|
segmentHeight,
|
||||||
|
severityType,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const segments = useMemo(
|
const segments = useMemo(
|
||||||
@ -86,34 +92,73 @@ export function SummaryTimeline({
|
|||||||
reviewTimelineDuration,
|
reviewTimelineDuration,
|
||||||
segmentHeight,
|
segmentHeight,
|
||||||
generateSegments,
|
generateSegments,
|
||||||
|
severityType,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const setVisibleSectionStyles = useCallback(() => {
|
||||||
|
if (
|
||||||
|
reviewTimelineRef.current &&
|
||||||
|
summaryTimelineRef.current &&
|
||||||
|
visibleSectionRef.current
|
||||||
|
) {
|
||||||
|
const content = reviewTimelineRef.current;
|
||||||
|
const summary = summaryTimelineRef.current;
|
||||||
|
const {
|
||||||
|
clientHeight: reviewTimelineVisibleHeight,
|
||||||
|
scrollHeight: reviewTimelineFullHeight,
|
||||||
|
scrollTop: scrolled,
|
||||||
|
} = content;
|
||||||
|
const { clientHeight: summaryTimelineVisibleHeight } = summary;
|
||||||
|
|
||||||
|
visibleSectionRef.current.style.top = `${
|
||||||
|
summaryTimelineVisibleHeight * (scrolled / reviewTimelineFullHeight)
|
||||||
|
}px`;
|
||||||
|
visibleSectionRef.current.style.height = `${
|
||||||
|
reviewTimelineVisibleHeight *
|
||||||
|
(reviewTimelineVisibleHeight / reviewTimelineFullHeight)
|
||||||
|
}px`;
|
||||||
|
}
|
||||||
|
}, [reviewTimelineRef, summaryTimelineRef, visibleSectionRef]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (reviewTimelineRef.current && summaryTimelineRef.current) {
|
if (reviewTimelineRef.current && summaryTimelineRef.current) {
|
||||||
const content = reviewTimelineRef.current;
|
const content = reviewTimelineRef.current;
|
||||||
const summary = summaryTimelineRef.current;
|
|
||||||
|
|
||||||
const handleScroll = () => {
|
const handleScroll = () => {
|
||||||
const {
|
setVisibleSectionStyles();
|
||||||
clientHeight: reviewTimelineVisibleHeight,
|
|
||||||
scrollHeight: reviewTimelineFullHeight,
|
|
||||||
scrollTop: scrolled,
|
|
||||||
} = content;
|
|
||||||
const { clientHeight: summaryTimelineVisibleHeight } = summary;
|
|
||||||
|
|
||||||
if (visibleSectionRef.current) {
|
|
||||||
visibleSectionRef.current.style.top = `${summaryTimelineVisibleHeight * (scrolled / reviewTimelineFullHeight)}px`;
|
|
||||||
visibleSectionRef.current.style.height = `${reviewTimelineVisibleHeight * (reviewTimelineVisibleHeight / reviewTimelineFullHeight)}px`;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set initial styles
|
||||||
|
setVisibleSectionStyles();
|
||||||
|
|
||||||
|
observer.current = new ResizeObserver(() => {
|
||||||
|
setVisibleSectionStyles();
|
||||||
|
if (summaryTimelineRef.current) {
|
||||||
|
const { clientHeight: summaryTimelineVisibleHeight } =
|
||||||
|
summaryTimelineRef.current;
|
||||||
|
|
||||||
|
setSegmentHeight(
|
||||||
|
summaryTimelineVisibleHeight /
|
||||||
|
(reviewTimelineDuration / segmentDuration),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
observer.current.observe(content);
|
||||||
|
|
||||||
content.addEventListener("scroll", handleScroll);
|
content.addEventListener("scroll", handleScroll);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
content.removeEventListener("scroll", handleScroll);
|
content.removeEventListener("scroll", handleScroll);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}, [reviewTimelineRef, summaryTimelineRef]);
|
}, [
|
||||||
|
reviewTimelineRef,
|
||||||
|
summaryTimelineRef,
|
||||||
|
setVisibleSectionStyles,
|
||||||
|
reviewTimelineDuration,
|
||||||
|
segmentDuration,
|
||||||
|
]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (summaryTimelineRef.current) {
|
if (summaryTimelineRef.current) {
|
||||||
|
@ -63,14 +63,12 @@ function useDraggableElement({
|
|||||||
}, [clientYPosition, timelineRef, isDragging]);
|
}, [clientYPosition, timelineRef, isDragging]);
|
||||||
|
|
||||||
const getClientYPosition = useCallback(
|
const getClientYPosition = useCallback(
|
||||||
(
|
(e: MouseEvent | TouchEvent) => {
|
||||||
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => {
|
|
||||||
let clientY;
|
let clientY;
|
||||||
if (isMobile && e.nativeEvent instanceof TouchEvent) {
|
if (isMobile && e instanceof TouchEvent) {
|
||||||
clientY = e.nativeEvent.touches[0].clientY;
|
clientY = e.touches[0].clientY;
|
||||||
} else if (e.nativeEvent instanceof MouseEvent) {
|
} else if (e instanceof MouseEvent) {
|
||||||
clientY = e.nativeEvent.clientY;
|
clientY = e.clientY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientY) {
|
if (clientY) {
|
||||||
@ -113,9 +111,7 @@ function useDraggableElement({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseUp = useCallback(
|
const handleMouseUp = useCallback(
|
||||||
(
|
(e: MouseEvent | TouchEvent) => {
|
||||||
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
if (isDragging) {
|
if (isDragging) {
|
||||||
@ -187,9 +183,7 @@ function useDraggableElement({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseMove = useCallback(
|
const handleMouseMove = useCallback(
|
||||||
(
|
(e: MouseEvent | TouchEvent) => {
|
||||||
e: React.MouseEvent<HTMLDivElement> | React.TouchEvent<HTMLDivElement>,
|
|
||||||
) => {
|
|
||||||
if (
|
if (
|
||||||
!contentRef.current ||
|
!contentRef.current ||
|
||||||
!timelineRef.current ||
|
!timelineRef.current ||
|
||||||
|
@ -405,18 +405,19 @@ function UIPlayground() {
|
|||||||
events={mockEvents} // events, including new has_been_reviewed and severity properties
|
events={mockEvents} // events, including new has_been_reviewed and severity properties
|
||||||
severityType={"alert"} // choose the severity type for the middle line - all other severity types are to the right
|
severityType={"alert"} // choose the severity type for the middle line - all other severity types are to the right
|
||||||
contentRef={contentRef} // optional content ref where previews are, can be used for observing/scrolling later
|
contentRef={contentRef} // optional content ref where previews are, can be used for observing/scrolling later
|
||||||
timelineRef={reviewTimelineRef}
|
timelineRef={reviewTimelineRef} // save a ref to this timeline to connect with the summary timeline
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isEventsReviewTimeline && (
|
{isEventsReviewTimeline && (
|
||||||
<div className="w-[10px]">
|
<div className="w-[10px]">
|
||||||
<SummaryTimeline
|
<SummaryTimeline
|
||||||
reviewTimelineRef={reviewTimelineRef}
|
reviewTimelineRef={reviewTimelineRef} // the ref to the review timeline
|
||||||
timelineStart={Math.floor(Date.now() / 1000)} // timestamp start of the timeline - the earlier time
|
timelineStart={Math.floor(Date.now() / 1000)} // timestamp start of the timeline - the earlier time
|
||||||
timelineEnd={Math.floor(Date.now() / 1000) - 6 * 60 * 60} // end of timeline - the later time
|
timelineEnd={Math.floor(Date.now() / 1000) - 6 * 60 * 60} // end of timeline - the later time
|
||||||
segmentDuration={zoomSettings.segmentDuration}
|
segmentDuration={zoomSettings.segmentDuration}
|
||||||
events={mockEvents}
|
events={mockEvents}
|
||||||
|
severityType={"alert"} // show only events of this severity on the summary timeline
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -555,6 +555,7 @@ function DetectionReview({
|
|||||||
timelineEnd={timeRange.after}
|
timelineEnd={timeRange.after}
|
||||||
segmentDuration={segmentDuration}
|
segmentDuration={segmentDuration}
|
||||||
events={reviewItems?.all ?? []}
|
events={reviewItems?.all ?? []}
|
||||||
|
severityType={severity}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user