This commit is contained in:
Louis Mazin 2026-09-18 03:50:25 +02:00
parent 18bdc192f0
commit 29d1facc83

View File

@ -14,11 +14,8 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
const TimeClockPage = () => { const TimeClockPage = () => {
const videoRef = useRef(null); const videoRef = useRef(null);
const streamRef = useRef(null);
const scanFrameRef = useRef(null);
const qrControlsRef = useRef(null); const qrControlsRef = useRef(null);
const { isAdmin } = useAuth(); const { isAdmin } = useAuth();
const [isScanning, setIsScanning] = useState(false);
const [scanMessage, setScanMessage] = useState(''); const [scanMessage, setScanMessage] = useState('');
const [scanConflict, setScanConflict] = useState(null); const [scanConflict, setScanConflict] = useState(null);
const [conflictTime, setConflictTime] = useState(new Date().toTimeString().slice(0, 5)); const [conflictTime, setConflictTime] = useState(new Date().toTimeString().slice(0, 5));
@ -163,16 +160,10 @@ const TimeClockPage = () => {
console.warn('Arrêt du lecteur QR déjà effectué.', stopError); console.warn('Arrêt du lecteur QR déjà effectué.', stopError);
} }
if (scanFrameRef.current) {
cancelAnimationFrame(scanFrameRef.current);
scanFrameRef.current = null;
}
streamRef.current?.getTracks().forEach((track) => track.stop());
streamRef.current = null;
if (videoRef.current) { if (videoRef.current) {
videoRef.current.pause();
videoRef.current.srcObject = null; videoRef.current.srcObject = null;
} }
setIsScanning(false);
}; };
const handleQrResult = async (code) => { const handleQrResult = async (code) => {
@ -211,14 +202,28 @@ const TimeClockPage = () => {
useEffect(() => () => stopScanner(), []); useEffect(() => () => stopScanner(), []);
useEffect(() => { useEffect(() => {
if (!isScannerDialogOpen || !isScanning || !videoRef.current) { if (!isScannerDialogOpen) {
return undefined; return undefined;
} }
let cancelled = false; let cancelled = false;
const reader = new BrowserQRCodeReader(); let reader = null;
const startReader = async () => { const startReader = async () => {
// Attendre que Radix ait réellement monté le DialogContent
await new Promise((resolve) => {
requestAnimationFrame(() => {
requestAnimationFrame(resolve);
});
});
if (cancelled || !videoRef.current) {
console.warn('Vidéo du scanner non disponible.');
return;
}
reader = new BrowserQRCodeReader();
try { try {
const controls = await reader.decodeFromConstraints( const controls = await reader.decodeFromConstraints(
{ {
@ -229,33 +234,33 @@ const TimeClockPage = () => {
}, },
videoRef.current, videoRef.current,
(result) => { (result) => {
if (result && !cancelled) { if (!result || cancelled) {
handleQrResult(result.getText()); return;
} }
handleQrResult(result.getText());
}, },
); );
if (cancelled) { if (cancelled) {
try {
controls.stop(); controls.stop();
} catch (stopError) { return;
console.warn(
'Arrêt du lecteur QR déjà effectué.',
stopError,
);
} }
} else {
qrControlsRef.current = controls; qrControlsRef.current = controls;
}
} catch (scannerError) { } catch (scannerError) {
if (!cancelled) { if (cancelled) {
return;
}
console.error('Erreur scanner QR:', scannerError);
setError( setError(
'Impossible d’accéder à la caméra. Vérifiez les permissions et utilisez HTTPS sur le réseau.', 'Impossible d’accéder à la caméra. Vérifiez les permissions et utilisez HTTPS sur le réseau.',
); );
console.error(scannerError);
stopScanner(); stopScanner();
} }
}
}; };
startReader(); startReader();
@ -264,15 +269,27 @@ const TimeClockPage = () => {
cancelled = true; cancelled = true;
try { try {
reader.reset(); reader?.reset();
} catch (resetError) { } catch (resetError) {
console.warn( console.warn(
'Réinitialisation du lecteur QR ignorée.', 'Réinitialisation du lecteur QR ignorée.',
resetError, resetError,
); );
} }
const controls = qrControlsRef.current;
qrControlsRef.current = null;
try {
controls?.stop();
} catch (stopError) {
console.warn(
'Arrêt du lecteur QR ignoré.',
stopError,
);
}
}; };
}, [isScannerDialogOpen, isScanning]); }, [isScannerDialogOpen]);
const startScanner = () => { const startScanner = () => {
setError(''); setError('');
@ -295,7 +312,7 @@ const TimeClockPage = () => {
return; return;
} }
setIsScanning(true); setIsScannerDialogOpen(true);
}; };
const formatTime = (timeString) => { const formatTime = (timeString) => {
@ -366,7 +383,6 @@ const TimeClockPage = () => {
} }
}} }}
> >
<DialogTrigger asChild>
<Button <Button
onClick={startScanner} onClick={startScanner}
variant="default" variant="default"
@ -374,27 +390,34 @@ const TimeClockPage = () => {
> >
{isAdmin ? 'Pointer avec un QR code' : 'Pointer'} {isAdmin ? 'Pointer avec un QR code' : 'Pointer'}
</Button> </Button>
</DialogTrigger>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-auto"> <DialogContent className="max-w-2xl">
<DialogHeader> <DialogHeader>
<DialogTitle>Aperçu de la caméra</DialogTitle> <DialogTitle>Aperçu de la caméra</DialogTitle>
</DialogHeader> </DialogHeader>
{isScanning ? (
<div className="scanner-panel"> <div className="scanner-panel">
<video ref={videoRef} className="scanner-video w-full h-auto" playsInline muted /> <video
<div className="scanner-frame" aria-hidden="true" /> ref={videoRef}
<Button onClick={stopScanner} variant="secondary" className="mt-2"> className="scanner-video h-auto w-full"
playsInline
muted
autoPlay
/>
<div
className="scanner-frame"
aria-hidden="true"
/>
<Button
onClick={() => setIsScannerDialogOpen(false)}
variant="secondary"
className="mt-2"
>
Annuler Annuler
</Button> </Button>
</div> </div>
) : (
<div className="text-center py-8">
<p>La caméra est prête à être utilisée</p>
<Button onClick={startScanner} variant="default" className="mt-4">
Démarrer le scanner
</Button>
</div>
)}
</DialogContent> </DialogContent>
</Dialog> </Dialog>