mirror of
https://github.com/yusufipk/OpenFrame.git
synced 2026-09-11 09:36:08 +00:00
Merge pull request #75 from yusufipk/claude/openframe-mouse-overlay-bug-66603b
fix(player): re-arm the cursor idle timer when playback changes
This commit is contained in:
@@ -216,7 +216,6 @@ export function VideoPageContent({
|
||||
setActiveVersionId,
|
||||
});
|
||||
|
||||
// Cursor idle detection: hide overlay when cursor idle for 3s while playing
|
||||
// Memoize version selection handler to prevent recreating on each render
|
||||
const handleVersionSelect = useCallback(
|
||||
(versionId: string) => {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
|
||||
/** How long the cursor has to sit still over the player before it and the overlay hide. */
|
||||
export const CURSOR_IDLE_DELAY_MS = 1000;
|
||||
|
||||
/**
|
||||
* Tracks whether the cursor has rested over the player long enough to hide it
|
||||
* and the play/pause overlay. Playback can start without the cursor moving (a
|
||||
* click, a key, the resume after a scrub), so the countdown is re-armed on
|
||||
* every playback change. Only pointer activity wakes the cursor: a pause/play
|
||||
* pair the element emits on its own (rebuffering, a source switch) leaves the
|
||||
* idle state alone, so the chrome does not flash back for a second.
|
||||
*/
|
||||
export function useCursorIdle(isPlaying: boolean) {
|
||||
const [cursorIdle, setCursorIdle] = useState(false);
|
||||
const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const isCursorOverPlayerRef = useRef(false);
|
||||
|
||||
// Restart the countdown. It only runs while the cursor is over the player
|
||||
// and playback is running; otherwise nothing is pending.
|
||||
const armTimer = useCallback(() => {
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
timerRef.current = null;
|
||||
|
||||
if (!isCursorOverPlayerRef.current || !isPlaying) return;
|
||||
|
||||
timerRef.current = setTimeout(() => {
|
||||
setCursorIdle(true);
|
||||
}, CURSOR_IDLE_DELAY_MS);
|
||||
}, [isPlaying]);
|
||||
|
||||
const handleVideoMouseMove = useCallback(() => {
|
||||
isCursorOverPlayerRef.current = true;
|
||||
setCursorIdle(false);
|
||||
armTimer();
|
||||
}, [armTimer]);
|
||||
|
||||
const handleVideoMouseLeave = useCallback(() => {
|
||||
isCursorOverPlayerRef.current = false;
|
||||
setCursorIdle(false);
|
||||
armTimer();
|
||||
}, [armTimer]);
|
||||
|
||||
useEffect(() => {
|
||||
armTimer();
|
||||
return () => {
|
||||
if (timerRef.current) clearTimeout(timerRef.current);
|
||||
};
|
||||
}, [armTimer]);
|
||||
|
||||
return { cursorIdle, handleVideoMouseMove, handleVideoMouseLeave };
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import {
|
||||
resolveSkipAmount as resolveSkipAmountFor,
|
||||
timeFromClientX as timeFromClientXWithin,
|
||||
} from '@/components/video-page/hooks/video-player-utils';
|
||||
import { useCursorIdle } from '@/components/video-page/hooks/use-cursor-idle';
|
||||
|
||||
interface UseVideoPlayerParams {
|
||||
activeVersion: Version | undefined;
|
||||
@@ -114,8 +115,7 @@ export function useVideoPlayer({
|
||||
const previousVersionKeyRef = useRef<string | null>(null);
|
||||
const [isBunnyPortraitSource, setIsBunnyPortraitSource] = useState(false);
|
||||
const [bunnyPortraitFrameWidth, setBunnyPortraitFrameWidth] = useState<number>(0);
|
||||
const [cursorIdle, setCursorIdle] = useState(false);
|
||||
const cursorIdleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const { cursorIdle, handleVideoMouseMove, handleVideoMouseLeave } = useCursorIdle(isPlaying);
|
||||
const bunnyRetryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const bunnyFrameCallbackIdRef = useRef<number | null>(null);
|
||||
const bunnyFrameSampleRef = useRef<{ mediaTime: number; presentedFrames: number } | null>(null);
|
||||
@@ -212,30 +212,6 @@ export function useVideoPlayer({
|
||||
return () => observer.disconnect();
|
||||
}, [activeVersionId, bunnyViewportRef]);
|
||||
|
||||
const handleVideoMouseMove = useCallback(() => {
|
||||
setCursorIdle(false);
|
||||
if (cursorIdleTimerRef.current) clearTimeout(cursorIdleTimerRef.current);
|
||||
|
||||
const shouldHideControls = isFullscreenMode;
|
||||
|
||||
if (isPlaying || shouldHideControls) {
|
||||
cursorIdleTimerRef.current = setTimeout(() => {
|
||||
setCursorIdle(true);
|
||||
}, 1000);
|
||||
}
|
||||
}, [isFullscreenMode, isPlaying]);
|
||||
|
||||
const handleVideoMouseLeave = useCallback(() => {
|
||||
if (cursorIdleTimerRef.current) clearTimeout(cursorIdleTimerRef.current);
|
||||
setCursorIdle(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (cursorIdleTimerRef.current) clearTimeout(cursorIdleTimerRef.current);
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (isApiLoaded) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user