Allow hovering to control preview on desktop

This commit is contained in:
Nicolas Mowen 2024-03-07 07:23:39 -07:00
parent b1470682ba
commit 79a1b000e5

View File

@ -292,10 +292,12 @@ function VideoPreview({
onTimeUpdate, onTimeUpdate,
}: VideoPreviewProps) { }: VideoPreviewProps) {
const playerRef = useRef<HTMLVideoElement | null>(null); const playerRef = useRef<HTMLVideoElement | null>(null);
const sliderRef = useRef<HTMLDivElement | null>(null);
// keep track of playback state // keep track of playback state
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const [hoverTimeout, setHoverTimeout] = useState<NodeJS.Timeout>();
const playerStartTime = useMemo(() => { const playerStartTime = useMemo(() => {
if (!relevantPreview) { if (!relevantPreview) {
return 0; return 0;
@ -456,6 +458,26 @@ function VideoPreview({
}, 500); }, 500);
}, [playerRef, setIgnoreClick]); }, [playerRef, setIgnoreClick]);
const onProgressHover = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
if (!sliderRef.current) {
return;
}
const rect = sliderRef.current.getBoundingClientRect();
const positionX = event.clientX - rect.left;
const width = sliderRef.current.clientWidth;
onManualSeek([Math.round((positionX / width) * 100)]);
if (hoverTimeout) {
clearTimeout(hoverTimeout);
}
setHoverTimeout(setTimeout(() => onStopManualSeek(), 500));
},
[sliderRef, hoverTimeout, onManualSeek, onStopManualSeek, setHoverTimeout],
);
return ( return (
<div className="relative size-full aspect-video bg-black"> <div className="relative size-full aspect-video bg-black">
<video <video
@ -470,6 +492,7 @@ function VideoPreview({
<source src={relevantPreview.src} type={relevantPreview.type} /> <source src={relevantPreview.src} type={relevantPreview.type} />
</video> </video>
<Slider <Slider
ref={sliderRef}
className="absolute inset-x-0 bottom-0 z-30" className="absolute inset-x-0 bottom-0 z-30"
value={[progress]} value={[progress]}
onValueChange={onManualSeek} onValueChange={onManualSeek}
@ -477,6 +500,7 @@ function VideoPreview({
min={0} min={0}
step={1} step={1}
max={100} max={100}
onMouseMove={isMobile ? undefined : onProgressHover}
/> />
</div> </div>
); );
@ -498,12 +522,14 @@ function InProgressPreview({
onTimeUpdate, onTimeUpdate,
}: InProgressPreviewProps) { }: InProgressPreviewProps) {
const apiHost = useApiHost(); const apiHost = useApiHost();
const sliderRef = useRef<HTMLDivElement | null>(null);
const { data: previewFrames } = useSWR<string[]>( const { data: previewFrames } = useSWR<string[]>(
`preview/${review.camera}/start/${Math.floor(review.start_time) - PREVIEW_PADDING}/end/${ `preview/${review.camera}/start/${Math.floor(review.start_time) - PREVIEW_PADDING}/end/${
Math.ceil(review.end_time) + PREVIEW_PADDING Math.ceil(review.end_time) + PREVIEW_PADDING
}/frames`, }/frames`,
); );
const [manualFrame, setManualFrame] = useState(false); const [manualFrame, setManualFrame] = useState(false);
const [hoverTimeout, setHoverTimeout] = useState<NodeJS.Timeout>();
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
const handleLoad = useCallback(() => { const handleLoad = useCallback(() => {
@ -575,6 +601,34 @@ function InProgressPreview({
[setManualFrame, setIgnoreClick], [setManualFrame, setIgnoreClick],
); );
const onProgressHover = useCallback(
(event: React.MouseEvent<HTMLDivElement>) => {
if (!sliderRef.current || !previewFrames) {
return;
}
const rect = sliderRef.current.getBoundingClientRect();
const positionX = event.clientX - rect.left;
const width = sliderRef.current.clientWidth;
const progress = [Math.round((positionX / width) * previewFrames.length)];
onManualSeek(progress);
if (hoverTimeout) {
clearTimeout(hoverTimeout);
}
setHoverTimeout(setTimeout(() => onStopManualSeek(progress), 500));
},
[
sliderRef,
hoverTimeout,
previewFrames,
onManualSeek,
onStopManualSeek,
setHoverTimeout,
],
);
if (!previewFrames || previewFrames.length == 0) { if (!previewFrames || previewFrames.length == 0) {
return ( return (
<img <img
@ -592,6 +646,7 @@ function InProgressPreview({
onLoad={handleLoad} onLoad={handleLoad}
/> />
<Slider <Slider
ref={sliderRef}
className="absolute inset-x-0 bottom-0 z-30" className="absolute inset-x-0 bottom-0 z-30"
value={[key]} value={[key]}
onValueChange={onManualSeek} onValueChange={onManualSeek}
@ -599,6 +654,7 @@ function InProgressPreview({
min={0} min={0}
step={1} step={1}
max={previewFrames.length - 1} max={previewFrames.length - 1}
onMouseMove={isMobile ? undefined : onProgressHover}
/> />
</div> </div>
); );