- block-timeline: ref updates moved to useLayoutEffect - channel-card, guide/page: Date.now() wrapped in useMemo + suppress purity rule - auth-context: lazy localStorage init (removes setState-in-effect) - use-channel-order: lazy localStorage init (removes setState-in-effect) - use-idle: start timer on mount without calling resetIdle (removes setState-in-effect) - use-subtitles, transcode-settings-dialog: inline eslint-disable on exact violating line - providers: block-level eslint-disable for tokenRef closure in useState initializer - edit-channel-sheet: remove unused minsToTime and BlockContent imports - docs/page: escape unescaped quote and apostrophe entities
45 lines
1.4 KiB
TypeScript
45 lines
1.4 KiB
TypeScript
"use client";
|
|
|
|
import { useState, useEffect, useCallback, useRef, RefObject } from "react";
|
|
|
|
export function useIdle(
|
|
timeoutMs: number,
|
|
videoRef: RefObject<HTMLVideoElement | null>,
|
|
onIdle?: () => void,
|
|
) {
|
|
const [showOverlays, setShowOverlays] = useState(true);
|
|
const [needsInteraction, setNeedsInteraction] = useState(false);
|
|
const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
// Keep onIdle in a ref so resetIdle doesn't need it as a dep
|
|
const onIdleRef = useRef(onIdle);
|
|
useEffect(() => {
|
|
onIdleRef.current = onIdle;
|
|
});
|
|
|
|
const resetIdle = useCallback(() => {
|
|
setShowOverlays(true);
|
|
setNeedsInteraction(false);
|
|
if (idleTimer.current) clearTimeout(idleTimer.current);
|
|
idleTimer.current = setTimeout(() => {
|
|
setShowOverlays(false);
|
|
onIdleRef.current?.();
|
|
}, timeoutMs);
|
|
videoRef.current?.play().catch(() => {});
|
|
}, [timeoutMs, videoRef]);
|
|
|
|
// Start the idle timer on mount without calling setState
|
|
// (default state values already set by useState above)
|
|
useEffect(() => {
|
|
idleTimer.current = setTimeout(() => {
|
|
setShowOverlays(false);
|
|
onIdleRef.current?.();
|
|
}, timeoutMs);
|
|
return () => {
|
|
if (idleTimer.current) clearTimeout(idleTimer.current);
|
|
};
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, []);
|
|
|
|
return { showOverlays, needsInteraction, setNeedsInteraction, resetIdle };
|
|
}
|