From afb171267414e1f3bcdc6141052398258e3980ef Mon Sep 17 00:00:00 2001 From: Josh Hawkins <32435876+hawkeye217@users.noreply.github.com> Date: Fri, 29 Nov 2024 20:17:26 -0600 Subject: [PATCH] use virtual segments in event review timeline --- .../timeline/EventReviewTimeline.tsx | 120 ++++++++---------- .../components/timeline/ReviewTimeline.tsx | 17 ++- 2 files changed, 64 insertions(+), 73 deletions(-) diff --git a/web/src/components/timeline/EventReviewTimeline.tsx b/web/src/components/timeline/EventReviewTimeline.tsx index 9d5a4f70c..1f460b32e 100644 --- a/web/src/components/timeline/EventReviewTimeline.tsx +++ b/web/src/components/timeline/EventReviewTimeline.tsx @@ -1,9 +1,17 @@ -import { useEffect, useCallback, useMemo, useRef, RefObject } from "react"; -import EventSegment from "./EventSegment"; +import React, { + useEffect, + useMemo, + useRef, + RefObject, + useCallback, +} from "react"; import { useTimelineUtils } from "@/hooks/use-timeline-utils"; import { ReviewSegment, ReviewSeverity } from "@/types/review"; import ReviewTimeline from "./ReviewTimeline"; -import scrollIntoView from "scroll-into-view-if-needed"; +import { + VirtualizedEventSegments, + VirtualizedEventSegmentsRef, +} from "./VirtualizedEventSegments"; export type EventReviewTimelineProps = { segmentDuration: number; @@ -56,6 +64,7 @@ export function EventReviewTimeline({ }: EventReviewTimelineProps) { const internalTimelineRef = useRef(null); const selectedTimelineRef = timelineRef || internalTimelineRef; + const virtualizedSegmentsRef = useRef(null); const timelineDuration = useMemo( () => timelineStart - timelineEnd, @@ -73,79 +82,27 @@ export function EventReviewTimeline({ [timelineStart, alignStartDateToTimeline], ); - // Generate segments for the timeline - const generateSegments = useCallback(() => { + // Generate segment times for the timeline + const segmentTimes = useMemo(() => { const segmentCount = Math.ceil(timelineDuration / segmentDuration); - - return Array.from({ length: segmentCount }, (_, index) => { - const segmentTime = timelineStartAligned - index * segmentDuration; - - return ( - - ); - }); - // we know that these deps are correct - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [ - segmentDuration, - timestampSpread, - timelineStart, - timelineDuration, - showMinimap, - minimapStartTime, - minimapEndTime, - events, - ]); - - const segments = useMemo( - () => generateSegments(), - // we know that these deps are correct - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - segmentDuration, - timestampSpread, - timelineStart, - timelineDuration, - showMinimap, - minimapStartTime, - minimapEndTime, - events, - ], - ); + return Array.from( + { length: segmentCount }, + (_, index) => timelineStartAligned - index * segmentDuration, + ); + }, [timelineDuration, segmentDuration, timelineStartAligned]); useEffect(() => { if ( - selectedTimelineRef.current && - segments && visibleTimestamps && - visibleTimestamps?.length > 0 && - !showMinimap + visibleTimestamps.length > 0 && + !showMinimap && + virtualizedSegmentsRef.current ) { const alignedVisibleTimestamps = visibleTimestamps.map( alignStartDateToTimeline, ); - const element = selectedTimelineRef.current?.querySelector( - `[data-segment-id="${Math.max(...alignedVisibleTimestamps)}"]`, - ); - if (element) { - scrollIntoView(element, { - scrollMode: "if-needed", - behavior: "smooth", - }); - } + + scrollToSegment(Math.max(...alignedVisibleTimestamps), true); } // don't scroll when segments update from unreviewed -> reviewed // we know that these deps are correct @@ -155,8 +112,18 @@ export function EventReviewTimeline({ showMinimap, alignStartDateToTimeline, visibleTimestamps, + segmentDuration, ]); + const scrollToSegment = useCallback( + (segmentTime: number, ifNeeded?: boolean) => { + if (virtualizedSegmentsRef.current) { + virtualizedSegmentsRef.current.scrollToSegment(segmentTime, ifNeeded); + } + }, + [], + ); + return ( - {segments} + ); } diff --git a/web/src/components/timeline/ReviewTimeline.tsx b/web/src/components/timeline/ReviewTimeline.tsx index faa22325d..9fdd21d5e 100644 --- a/web/src/components/timeline/ReviewTimeline.tsx +++ b/web/src/components/timeline/ReviewTimeline.tsx @@ -30,7 +30,9 @@ export type ReviewTimelineProps = { setExportEndTime?: React.Dispatch>; timelineCollapsed?: boolean; dense: boolean; - children: ReactNode[]; + segments: number[]; + scrollToSegment: (segmentTime: number, ifNeeded?: boolean) => void; + children: ReactNode; }; export function ReviewTimeline({ @@ -51,6 +53,8 @@ export function ReviewTimeline({ setExportEndTime, timelineCollapsed = false, dense, + segments, + scrollToSegment, children, }: ReviewTimelineProps) { const [isDraggingHandlebar, setIsDraggingHandlebar] = useState(false); @@ -116,7 +120,8 @@ export function ReviewTimeline({ setIsDragging: setIsDraggingHandlebar, draggableElementTimeRef: handlebarTimeRef, dense, - timelineSegments: children, + segments, + scrollToSegment, }); const { @@ -140,7 +145,8 @@ export function ReviewTimeline({ draggableElementTimeRef: exportStartTimeRef, setDraggableElementPosition: setExportStartPosition, dense, - timelineSegments: children, + segments, + scrollToSegment, }); const { @@ -164,7 +170,8 @@ export function ReviewTimeline({ draggableElementTimeRef: exportEndTimeRef, setDraggableElementPosition: setExportEndPosition, dense, - timelineSegments: children, + segments, + scrollToSegment, }); const handleHandlebar = useCallback( @@ -327,7 +334,7 @@ export function ReviewTimeline({
{children} - {children.length > 0 && ( + {children && ( <> {showHandlebar && (