mirror of
https://github.com/blakeblackshear/frigate.git
synced 2026-02-10 05:05:26 +03:00
when minimap is hidden, only scroll timeline when needed
This commit is contained in:
parent
ca85c317e8
commit
191ebfe930
@ -11,6 +11,7 @@ import EventSegment from "./EventSegment";
|
|||||||
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
import { useTimelineUtils } from "@/hooks/use-timeline-utils";
|
||||||
import { ReviewSegment, ReviewSeverity } from "@/types/review";
|
import { ReviewSegment, ReviewSeverity } from "@/types/review";
|
||||||
import ReviewTimeline from "./ReviewTimeline";
|
import ReviewTimeline from "./ReviewTimeline";
|
||||||
|
import scrollIntoView from "scroll-into-view-if-needed";
|
||||||
|
|
||||||
export type EventReviewTimelineProps = {
|
export type EventReviewTimelineProps = {
|
||||||
segmentDuration: number;
|
segmentDuration: number;
|
||||||
@ -60,6 +61,7 @@ export function EventReviewTimeline({
|
|||||||
const [isDragging, setIsDragging] = useState(false);
|
const [isDragging, setIsDragging] = useState(false);
|
||||||
const [exportStartPosition, setExportStartPosition] = useState(0);
|
const [exportStartPosition, setExportStartPosition] = useState(0);
|
||||||
const [exportEndPosition, setExportEndPosition] = useState(0);
|
const [exportEndPosition, setExportEndPosition] = useState(0);
|
||||||
|
const [visibleThumbnails, setVisibleThumbnails] = useState<number[]>([]);
|
||||||
|
|
||||||
const internalTimelineRef = useRef<HTMLDivElement>(null);
|
const internalTimelineRef = useRef<HTMLDivElement>(null);
|
||||||
const handlebarRef = useRef<HTMLDivElement>(null);
|
const handlebarRef = useRef<HTMLDivElement>(null);
|
||||||
@ -68,6 +70,9 @@ export function EventReviewTimeline({
|
|||||||
const exportStartTimeRef = useRef<HTMLDivElement>(null);
|
const exportStartTimeRef = useRef<HTMLDivElement>(null);
|
||||||
const exportEndRef = useRef<HTMLDivElement>(null);
|
const exportEndRef = useRef<HTMLDivElement>(null);
|
||||||
const exportEndTimeRef = useRef<HTMLDivElement>(null);
|
const exportEndTimeRef = useRef<HTMLDivElement>(null);
|
||||||
|
const selectedTimelineRef = timelineRef || internalTimelineRef;
|
||||||
|
|
||||||
|
const observer = useRef<IntersectionObserver | null>(null);
|
||||||
|
|
||||||
const timelineDuration = useMemo(
|
const timelineDuration = useMemo(
|
||||||
() => timelineStart - timelineEnd,
|
() => timelineStart - timelineEnd,
|
||||||
@ -77,7 +82,7 @@ export function EventReviewTimeline({
|
|||||||
const { alignStartDateToTimeline, alignEndDateToTimeline } = useTimelineUtils(
|
const { alignStartDateToTimeline, alignEndDateToTimeline } = useTimelineUtils(
|
||||||
segmentDuration,
|
segmentDuration,
|
||||||
timelineDuration,
|
timelineDuration,
|
||||||
timelineRef || internalTimelineRef,
|
selectedTimelineRef,
|
||||||
);
|
);
|
||||||
|
|
||||||
const timelineStartAligned = useMemo(
|
const timelineStartAligned = useMemo(
|
||||||
@ -103,7 +108,7 @@ export function EventReviewTimeline({
|
|||||||
handleMouseMove: handlebarMouseMove,
|
handleMouseMove: handlebarMouseMove,
|
||||||
} = useDraggableElement({
|
} = useDraggableElement({
|
||||||
contentRef,
|
contentRef,
|
||||||
timelineRef: timelineRef || internalTimelineRef,
|
timelineRef: selectedTimelineRef,
|
||||||
draggableElementRef: handlebarRef,
|
draggableElementRef: handlebarRef,
|
||||||
segmentDuration,
|
segmentDuration,
|
||||||
showDraggableElement: showHandlebar,
|
showDraggableElement: showHandlebar,
|
||||||
@ -122,7 +127,7 @@ export function EventReviewTimeline({
|
|||||||
handleMouseMove: exportStartMouseMove,
|
handleMouseMove: exportStartMouseMove,
|
||||||
} = useDraggableElement({
|
} = useDraggableElement({
|
||||||
contentRef,
|
contentRef,
|
||||||
timelineRef: timelineRef || internalTimelineRef,
|
timelineRef: selectedTimelineRef,
|
||||||
draggableElementRef: exportStartRef,
|
draggableElementRef: exportStartRef,
|
||||||
segmentDuration,
|
segmentDuration,
|
||||||
showDraggableElement: showExportHandles,
|
showDraggableElement: showExportHandles,
|
||||||
@ -143,7 +148,7 @@ export function EventReviewTimeline({
|
|||||||
handleMouseMove: exportEndMouseMove,
|
handleMouseMove: exportEndMouseMove,
|
||||||
} = useDraggableElement({
|
} = useDraggableElement({
|
||||||
contentRef,
|
contentRef,
|
||||||
timelineRef: timelineRef || internalTimelineRef,
|
timelineRef: selectedTimelineRef,
|
||||||
draggableElementRef: exportEndRef,
|
draggableElementRef: exportEndRef,
|
||||||
segmentDuration,
|
segmentDuration,
|
||||||
showDraggableElement: showExportHandles,
|
showDraggableElement: showExportHandles,
|
||||||
@ -216,9 +221,67 @@ export function EventReviewTimeline({
|
|||||||
}
|
}
|
||||||
}, [isDragging, onHandlebarDraggingChange]);
|
}, [isDragging, onHandlebarDraggingChange]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (contentRef.current && segments) {
|
||||||
|
observer.current = new IntersectionObserver(
|
||||||
|
(entries) => {
|
||||||
|
entries.forEach((entry) => {
|
||||||
|
if (entry.isIntersecting) {
|
||||||
|
const segmentStartId =
|
||||||
|
entry.target.getAttribute("data-segment-start");
|
||||||
|
if (segmentStartId) {
|
||||||
|
setVisibleThumbnails((prevState) => [
|
||||||
|
...prevState,
|
||||||
|
parseInt(segmentStartId),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const segmentStartId =
|
||||||
|
entry.target.getAttribute("data-segment-start");
|
||||||
|
if (segmentStartId) {
|
||||||
|
setVisibleThumbnails((prevState) =>
|
||||||
|
prevState.filter(
|
||||||
|
(timestamp) => timestamp !== parseInt(segmentStartId),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
{ threshold: 0.5 },
|
||||||
|
);
|
||||||
|
|
||||||
|
const reviewItems = contentRef.current.querySelectorAll(".review-item");
|
||||||
|
reviewItems.forEach((item) => {
|
||||||
|
observer.current?.observe(item);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
observer.current?.disconnect();
|
||||||
|
};
|
||||||
|
}, [contentRef, segments]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (
|
||||||
|
selectedTimelineRef.current &&
|
||||||
|
segments &&
|
||||||
|
visibleThumbnails?.length > 0 &&
|
||||||
|
!showMinimap
|
||||||
|
) {
|
||||||
|
const element = selectedTimelineRef.current?.querySelector(
|
||||||
|
`[data-segment-id="${Math.max(...visibleThumbnails)}"]`,
|
||||||
|
);
|
||||||
|
scrollIntoView(element as HTMLDivElement, {
|
||||||
|
scrollMode: "if-needed",
|
||||||
|
behavior: "smooth",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [visibleThumbnails, selectedTimelineRef, segments, showMinimap]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReviewTimeline
|
<ReviewTimeline
|
||||||
timelineRef={timelineRef || internalTimelineRef}
|
timelineRef={selectedTimelineRef}
|
||||||
handlebarRef={handlebarRef}
|
handlebarRef={handlebarRef}
|
||||||
handlebarTimeRef={handlebarTimeRef}
|
handlebarTimeRef={handlebarTimeRef}
|
||||||
handlebarMouseMove={handlebarMouseMove}
|
handlebarMouseMove={handlebarMouseMove}
|
||||||
|
|||||||
@ -201,6 +201,7 @@ export function EventSegment({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
key={segmentKey}
|
key={segmentKey}
|
||||||
|
data-segment-id={segmentKey}
|
||||||
className={segmentClasses}
|
className={segmentClasses}
|
||||||
onClick={segmentClick}
|
onClick={segmentClick}
|
||||||
onTouchEnd={(event) => handleTouchStart(event, segmentClick)}
|
onTouchEnd={(event) => handleTouchStart(event, segmentClick)}
|
||||||
|
|||||||
@ -508,7 +508,7 @@ function DetectionReview({
|
|||||||
data-segment-start={
|
data-segment-start={
|
||||||
alignStartDateToTimeline(value.start_time) - segmentDuration
|
alignStartDateToTimeline(value.start_time) - segmentDuration
|
||||||
}
|
}
|
||||||
className={`outline outline-offset-1 rounded-lg shadow-none transition-all my-1 md:my-0 ${selected ? `outline-4 shadow-[0_0_6px_1px] outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-0 duration-500"}`}
|
className={`review-item outline outline-offset-1 rounded-lg shadow-none transition-all my-1 md:my-0 ${selected ? `outline-4 shadow-[0_0_6px_1px] outline-severity_${value.severity} shadow-severity_${value.severity}` : "outline-0 duration-500"}`}
|
||||||
>
|
>
|
||||||
<div className="aspect-video rounded-lg overflow-hidden">
|
<div className="aspect-video rounded-lg overflow-hidden">
|
||||||
<PreviewThumbnailPlayer
|
<PreviewThumbnailPlayer
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user