fix wrong segments when changing filters in motion only mode

This commit is contained in:
Josh Hawkins 2024-03-31 20:52:01 -05:00
parent 7e5eb82882
commit 0a71e7fb88
3 changed files with 170 additions and 113 deletions

View File

@ -4,6 +4,7 @@ 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"; import { isDesktop } from "react-device-detect";
import { useMotionSegmentUtils } from "@/hooks/use-motion-segment-utils";
export type MotionReviewTimelineProps = { export type MotionReviewTimelineProps = {
segmentDuration: number; segmentDuration: number;
@ -75,14 +76,37 @@ export function MotionReviewTimeline({
[timelineStart, alignStartDateToTimeline, segmentDuration], [timelineStart, alignStartDateToTimeline, segmentDuration],
); );
const { getMotionSegmentValue } = useMotionSegmentUtils(
segmentDuration,
motion_events,
);
// Generate segments for the timeline // Generate segments for the timeline
const generateSegments = useCallback(() => { const generateSegments = useCallback(() => {
const segmentCount = Math.ceil(timelineDuration / segmentDuration); const segments = [];
let segmentTime = timelineStartAligned;
return Array.from({ length: segmentCount }, (_, index) => { while (segmentTime >= timelineStartAligned - timelineDuration) {
const segmentTime = timelineStartAligned - index * segmentDuration; const motionStart = segmentTime;
const motionEnd = motionStart + segmentDuration;
return ( const segmentMotion =
getMotionSegmentValue(motionStart) > 0 ||
getMotionSegmentValue(motionStart + segmentDuration / 2) > 0;
const overlappingReviewItems = events.some(
(item) =>
(item.start_time >= motionStart && item.start_time < motionEnd) ||
(item.end_time > motionStart && item.end_time <= motionEnd) ||
(item.start_time <= motionStart && item.end_time >= motionEnd),
);
if ((!segmentMotion || overlappingReviewItems) && motionOnly) {
// exclude segment if necessary when in motion only mode
segmentTime -= segmentDuration;
continue;
}
segments.push(
<MotionSegment <MotionSegment
key={segmentTime} key={segmentTime}
events={events} events={events}
@ -96,9 +120,11 @@ export function MotionReviewTimeline({
minimapEndTime={minimapEndTime} minimapEndTime={minimapEndTime}
setHandlebarTime={setHandlebarTime} setHandlebarTime={setHandlebarTime}
dense={dense} dense={dense}
/> />,
); );
}); segmentTime -= segmentDuration;
}
return segments;
// we know that these deps are correct // we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [

View File

@ -30,7 +30,7 @@ export type ReviewTimelineProps = {
setExportEndTime?: React.Dispatch<React.SetStateAction<number>>; setExportEndTime?: React.Dispatch<React.SetStateAction<number>>;
timelineCollapsed?: boolean; timelineCollapsed?: boolean;
dense: boolean; dense: boolean;
children: ReactNode; children: ReactNode[];
}; };
export function ReviewTimeline({ export function ReviewTimeline({
@ -113,6 +113,7 @@ export function ReviewTimeline({
setIsDragging: setIsDraggingHandlebar, setIsDragging: setIsDraggingHandlebar,
draggableElementTimeRef: handlebarTimeRef, draggableElementTimeRef: handlebarTimeRef,
dense, dense,
timelineSegments: children,
}); });
const { const {
@ -136,6 +137,7 @@ export function ReviewTimeline({
draggableElementTimeRef: exportStartTimeRef, draggableElementTimeRef: exportStartTimeRef,
setDraggableElementPosition: setExportStartPosition, setDraggableElementPosition: setExportStartPosition,
dense, dense,
timelineSegments: children,
}); });
const { const {
@ -159,6 +161,7 @@ export function ReviewTimeline({
draggableElementTimeRef: exportEndTimeRef, draggableElementTimeRef: exportEndTimeRef,
setDraggableElementPosition: setExportEndPosition, setDraggableElementPosition: setExportEndPosition,
dense, dense,
timelineSegments: children,
}); });
const handleHandlebar = useCallback( const handleHandlebar = useCallback(
@ -321,6 +324,8 @@ export function ReviewTimeline({
<div className="absolute bottom-0 inset-x-0 z-20 w-full h-[30px] bg-gradient-to-t from-secondary to-transparent pointer-events-none"></div> <div className="absolute bottom-0 inset-x-0 z-20 w-full h-[30px] bg-gradient-to-t from-secondary to-transparent pointer-events-none"></div>
{children} {children}
</div> </div>
{children.length > 0 && (
<>
{showHandlebar && ( {showHandlebar && (
<div <div
className={`absolute left-0 top-0 ${isDraggingHandlebar && isIOS ? "" : "z-20"} w-full`} className={`absolute left-0 top-0 ${isDraggingHandlebar && isIOS ? "" : "z-20"} w-full`}
@ -436,6 +441,8 @@ export function ReviewTimeline({
</div> </div>
</> </>
)} )}
</>
)}
</div> </div>
); );
} }

View File

@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from "react"; import { ReactNode, useCallback, useEffect, useMemo, useState } from "react";
import { isMobile } from "react-device-detect"; import { isMobile } from "react-device-detect";
import scrollIntoView from "scroll-into-view-if-needed"; import scrollIntoView from "scroll-into-view-if-needed";
import { useTimelineUtils } from "./use-timeline-utils"; import { useTimelineUtils } from "./use-timeline-utils";
@ -23,6 +23,7 @@ type DraggableElementProps = {
setIsDragging: React.Dispatch<React.SetStateAction<boolean>>; setIsDragging: React.Dispatch<React.SetStateAction<boolean>>;
setDraggableElementPosition?: React.Dispatch<React.SetStateAction<number>>; setDraggableElementPosition?: React.Dispatch<React.SetStateAction<number>>;
dense: boolean; dense: boolean;
timelineSegments: ReactNode[];
}; };
function useDraggableElement({ function useDraggableElement({
@ -45,6 +46,7 @@ function useDraggableElement({
setIsDragging, setIsDragging,
setDraggableElementPosition, setDraggableElementPosition,
dense, dense,
timelineSegments,
}: DraggableElementProps) { }: DraggableElementProps) {
const [clientYPosition, setClientYPosition] = useState<number | null>(null); const [clientYPosition, setClientYPosition] = useState<number | null>(null);
const [initialClickAdjustment, setInitialClickAdjustment] = useState(0); const [initialClickAdjustment, setInitialClickAdjustment] = useState(0);
@ -213,10 +215,10 @@ function useDraggableElement({
); );
useEffect(() => { useEffect(() => {
if (timelineRef.current) { if (timelineRef.current && timelineSegments.length) {
setSegments(Array.from(timelineRef.current.querySelectorAll(".segment"))); setSegments(Array.from(timelineRef.current.querySelectorAll(".segment")));
} }
}, [timelineRef, segmentDuration, timelineDuration, timelineCollapsed]); }, [timelineRef, timelineCollapsed, timelineSegments]);
useEffect(() => { useEffect(() => {
let animationFrameId: number | null = null; let animationFrameId: number | null = null;
@ -426,7 +428,13 @@ function useDraggableElement({
]); ]);
useEffect(() => { useEffect(() => {
if (timelineRef.current && draggableElementTime && timelineCollapsed) { if (
timelineRef.current &&
draggableElementTime &&
timelineCollapsed &&
timelineSegments &&
segments
) {
setFullTimelineHeight(timelineRef.current.scrollHeight); setFullTimelineHeight(timelineRef.current.scrollHeight);
const alignedSegmentTime = alignStartDateToTimeline(draggableElementTime); const alignedSegmentTime = alignStartDateToTimeline(draggableElementTime);
@ -452,14 +460,30 @@ function useDraggableElement({
if (setDraggableElementTime) { if (setDraggableElementTime) {
setDraggableElementTime(searchTime); setDraggableElementTime(searchTime);
} }
break; return;
}
}
}
if (!segmentElement) {
// segment still not found, just start at the beginning of the timeline or at now()
if (segments?.length) {
const searchTime = parseInt(
segments[0].getAttribute("data-segment-id") || "0",
10,
);
if (setDraggableElementTime) {
setDraggableElementTime(searchTime);
}
} else {
if (setDraggableElementTime) {
setDraggableElementTime(timelineStartAligned);
} }
} }
} }
} }
// we know that these deps are correct // we know that these deps are correct
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [timelineCollapsed]); }, [timelineCollapsed, segments]);
useEffect(() => { useEffect(() => {
if (timelineRef.current && segments) { if (timelineRef.current && segments) {