import { useCallback, useEffect, useRef, useState } from 'react'; /** * Camera torch / flash control. * * @param {React.MutableRefObject} streamRef * A ref holding the active MediaStream from the camera hook. */ export function useScannerFlash(streamRef) { const [flashOn, setFlashOn] = useState(false); const [flashSupported, setFlashSupported] = useState(false); const streamIdentityRef = useRef(null); const getVideoTrack = useCallback(() => { const stream = streamRef?.current; if (!stream) return null; const tracks = stream.getVideoTracks(); return tracks.length > 0 ? tracks[0] : null; }, [streamRef]); const probeFlashSupport = useCallback(() => { const track = getVideoTrack(); if (!track) { setFlashSupported(false); return; } const capabilities = track.getCapabilities?.(); setFlashSupported(Boolean(capabilities?.torch)); const handleEnded = () => { setFlashOn(false); setFlashSupported(false); }; track.addEventListener('ended', handleEnded); return () => { track.removeEventListener('ended', handleEnded); }; }, [getVideoTrack]); useEffect(() => { const check = () => { const currentStream = streamRef?.current ?? null; if (currentStream !== streamIdentityRef.current) { streamIdentityRef.current = currentStream; probeFlashSupport(); } }; const initialTimer = setTimeout(check, 0); const interval = setInterval(check, 500); return () => { clearTimeout(initialTimer); clearInterval(interval); }; }, [probeFlashSupport, streamRef]); const toggleFlash = useCallback(async () => { if (!flashSupported) return; const track = getVideoTrack(); if (!track) return; const next = !flashOn; try { await track.applyConstraints({ advanced: [{ torch: next }] }); setFlashOn(next); } catch (err) { console.warn('Torch toggle failed:', err); } }, [flashSupported, flashOn, getVideoTrack]); return { flashSupported, flashOn, toggleFlash }; }