import { useEffect, useRef, useState } from 'react'; /** True when the document tab is visible (Page Visibility API). */ export function usePageVisible(): boolean { const [visible, setVisible] = useState( () => typeof document === 'undefined' || document.visibilityState === 'visible', ); useEffect(() => { const onVis = () => setVisible(document.visibilityState === 'visible'); document.addEventListener('visibilitychange', onVis); return () => document.removeEventListener('visibilitychange', onVis); }, []); return visible; } /** * Run `fn` on an interval; pauses while the tab is hidden. * * `fn` is stored in a ref so callers do NOT need to wrap it in `useCallback`. * The effect only re-runs when `ms`, `enabled`, or tab-visibility changes — * an unstable `fn` reference will NOT trigger extra immediate calls. */ export function useVisibleInterval(fn: () => void, ms: number, enabled = true): void { const visible = usePageVisible(); const fnRef = useRef(fn); // Keep the ref in sync with the latest fn without scheduling a new interval. fnRef.current = fn; useEffect(() => { if (!enabled || !visible) return; const id = window.setInterval(() => fnRef.current(), ms); return () => window.clearInterval(id); }, [ms, enabled, visible]); }