Browse Source
- Manages video play/pause based on hover and selection state - 300ms hover delay before playing, immediate play on selection - Pauses on mouse leave or deselection (resumes from position) - Catches AbortError from interrupted play() promiseshandoff-20260429-1057
1 changed files with 76 additions and 0 deletions
@ -0,0 +1,76 @@ |
|||
import { useRef, useEffect } from "react"; |
|||
import { useWorkflowStore } from "@/store/workflowStore"; |
|||
|
|||
/** |
|||
* Hook for managing video play/pause based on hover and selection state. |
|||
* |
|||
* Videos are paused by default. They start playing when: |
|||
* - The node is selected (immediately) |
|||
* - The node is hovered for 300ms (delayed) |
|||
* |
|||
* Videos pause when: |
|||
* - The node is deselected (if not hovered) |
|||
* - The mouse leaves the node (if not selected) |
|||
* |
|||
* @param nodeId - The node's unique ID |
|||
* @param selected - Whether the node is currently selected |
|||
* @returns A ref to attach to the video element |
|||
*/ |
|||
export function useVideoAutoplay( |
|||
nodeId: string, |
|||
selected: boolean | undefined |
|||
): React.RefObject<HTMLVideoElement | null> { |
|||
const videoRef = useRef<HTMLVideoElement | null>(null); |
|||
const hoverTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null); |
|||
const hoveredNodeId = useWorkflowStore((s) => s.hoveredNodeId); |
|||
|
|||
const isHovered = hoveredNodeId === nodeId; |
|||
|
|||
useEffect(() => { |
|||
const video = videoRef.current; |
|||
if (!video) return; |
|||
|
|||
// Clear any pending hover timeout
|
|||
const clearHoverTimeout = () => { |
|||
if (hoverTimeoutRef.current) { |
|||
clearTimeout(hoverTimeoutRef.current); |
|||
hoverTimeoutRef.current = null; |
|||
} |
|||
}; |
|||
|
|||
if (selected || isHovered) { |
|||
// Play the video
|
|||
if (selected) { |
|||
// Selected: play immediately
|
|||
clearHoverTimeout(); |
|||
video.play().catch((e) => { |
|||
if (e.name !== "AbortError") { |
|||
console.warn("Video play failed:", e); |
|||
} |
|||
}); |
|||
} else if (isHovered) { |
|||
// Hovered but not selected: play after 300ms delay
|
|||
clearHoverTimeout(); |
|||
hoverTimeoutRef.current = setTimeout(() => { |
|||
video.play().catch((e) => { |
|||
if (e.name !== "AbortError") { |
|||
console.warn("Video play failed:", e); |
|||
} |
|||
}); |
|||
}, 300); |
|||
} |
|||
} else { |
|||
// Not selected and not hovered: pause
|
|||
clearHoverTimeout(); |
|||
video.pause(); |
|||
// Note: Do NOT reset currentTime - resume from current position
|
|||
} |
|||
|
|||
// Cleanup on unmount or dependency change
|
|||
return () => { |
|||
clearHoverTimeout(); |
|||
}; |
|||
}, [isHovered, selected, nodeId]); |
|||
|
|||
return videoRef; |
|||
} |
|||
Loading…
Reference in new issue