Implement support for no recordings indicator on timeline (#18363)

* Indicate no recordings on the history timeline with gray hash marks

This commit includes a new backend API endpoint and the frontend changes needed to support this functionality

* don't show slashes for now
This commit is contained in:
Josh Hawkins
2025-05-23 09:55:48 -05:00
committed by Blake Blackshear
parent e1340443f5
commit 4ebc4f6d21
7 changed files with 121 additions and 3 deletions

View File

@@ -17,6 +17,7 @@ import {
VirtualizedMotionSegments,
VirtualizedMotionSegmentsRef,
} from "./VirtualizedMotionSegments";
import { RecordingSegment } from "@/types/record";
export type MotionReviewTimelineProps = {
segmentDuration: number;
@@ -38,6 +39,7 @@ export type MotionReviewTimelineProps = {
setExportEndTime?: React.Dispatch<React.SetStateAction<number>>;
events: ReviewSegment[];
motion_events: MotionData[];
noRecordingRanges?: RecordingSegment[];
contentRef: RefObject<HTMLDivElement>;
timelineRef?: RefObject<HTMLDivElement>;
onHandlebarDraggingChange?: (isDragging: boolean) => void;
@@ -66,6 +68,7 @@ export function MotionReviewTimeline({
setExportEndTime,
events,
motion_events,
noRecordingRanges,
contentRef,
timelineRef,
onHandlebarDraggingChange,
@@ -97,6 +100,17 @@ export function MotionReviewTimeline({
motion_events,
);
const getRecordingAvailability = useCallback(
(time: number): boolean | undefined => {
if (!noRecordingRanges?.length) return undefined;
return !noRecordingRanges.some(
(range) => time >= range.start_time && time < range.end_time,
);
},
[noRecordingRanges],
);
const segmentTimes = useMemo(() => {
const segments = [];
let segmentTime = timelineStartAligned;
@@ -206,6 +220,7 @@ export function MotionReviewTimeline({
dense={dense}
motionOnly={motionOnly}
getMotionSegmentValue={getMotionSegmentValue}
getRecordingAvailability={getRecordingAvailability}
/>
</ReviewTimeline>
);

View File

@@ -15,6 +15,7 @@ type MotionSegmentProps = {
timestampSpread: number;
firstHalfMotionValue: number;
secondHalfMotionValue: number;
hasRecording?: boolean;
motionOnly: boolean;
showMinimap: boolean;
minimapStartTime?: number;
@@ -31,6 +32,7 @@ export function MotionSegment({
timestampSpread,
firstHalfMotionValue,
secondHalfMotionValue,
hasRecording,
motionOnly,
showMinimap,
minimapStartTime,
@@ -176,6 +178,12 @@ export function MotionSegment({
segmentClasses,
severity[0] && "bg-gradient-to-r",
severity[0] && severityColorsBg[severity[0]],
// TODO: will update this for 0.17
false &&
hasRecording == false &&
firstHalfMotionValue == 0 &&
secondHalfMotionValue == 0 &&
"bg-slashes",
)}
onClick={segmentClick}
onTouchEnd={(event) => handleTouchStart(event, segmentClick)}

View File

@@ -24,6 +24,7 @@ type VirtualizedMotionSegmentsProps = {
dense: boolean;
motionOnly: boolean;
getMotionSegmentValue: (timestamp: number) => number;
getRecordingAvailability: (timestamp: number) => boolean | undefined;
};
export interface VirtualizedMotionSegmentsRef {
@@ -55,6 +56,7 @@ export const VirtualizedMotionSegments = forwardRef<
dense,
motionOnly,
getMotionSegmentValue,
getRecordingAvailability,
},
ref,
) => {
@@ -154,6 +156,8 @@ export const VirtualizedMotionSegments = forwardRef<
(item.end_time ?? segmentTime) >= motionEnd),
);
const hasRecording = getRecordingAvailability(segmentTime);
if ((!segmentMotion || overlappingReviewItems) && motionOnly) {
return null; // Skip rendering this segment in motion only mode
}
@@ -172,6 +176,7 @@ export const VirtualizedMotionSegments = forwardRef<
events={events}
firstHalfMotionValue={firstHalfMotionValue}
secondHalfMotionValue={secondHalfMotionValue}
hasRecording={hasRecording}
segmentDuration={segmentDuration}
segmentTime={segmentTime}
timestampSpread={timestampSpread}
@@ -189,6 +194,7 @@ export const VirtualizedMotionSegments = forwardRef<
[
events,
getMotionSegmentValue,
getRecordingAvailability,
motionOnly,
segmentDuration,
showMinimap,