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 videoRef = useRef(null);
const streamRef = useRef(null);
const scanFrameRef = useRef(null);
const qrControlsRef = useRef(null);
const { isAdmin } = useAuth();
const [isScanning, setIsScanning] = useState(false);
const [scanMessage, setScanMessage] = useState('');
const [scanConflict, setScanConflict] = useState(null);
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);
}
if (scanFrameRef.current) {
cancelAnimationFrame(scanFrameRef.current);
scanFrameRef.current = null;
}
streamRef.current?.getTracks().forEach((track) => track.stop());
streamRef.current = null;
if (videoRef.current) {
videoRef.current.pause();
videoRef.current.srcObject = null;
}
setIsScanning(false);
};
const handleQrResult = async (code) => {
@ -211,14 +202,28 @@ const TimeClockPage = () => {
useEffect(() => () => stopScanner(), []);
useEffect(() => {
if (!isScannerDialogOpen || !isScanning || !videoRef.current) {
if (!isScannerDialogOpen) {
return undefined;
}
let cancelled = false;
const reader = new BrowserQRCodeReader();
let reader = null;
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 {
const controls = await reader.decodeFromConstraints(
{
@ -229,33 +234,33 @@ const TimeClockPage = () => {
},
videoRef.current,
(result) => {
if (result && !cancelled) {
handleQrResult(result.getText());
if (!result || cancelled) {
return;
}
handleQrResult(result.getText());
},
);
if (cancelled) {
try {
controls.stop();
} catch (stopError) {
console.warn(
'Arrêt du lecteur QR déjà effectué.',
stopError,
);
return;
}
} else {
qrControlsRef.current = controls;
}
} catch (scannerError) {
if (!cancelled) {
if (cancelled) {
return;
}
console.error('Erreur scanner QR:', scannerError);
setError(
'Impossible d’accéder à la caméra. Vérifiez les permissions et utilisez HTTPS sur le réseau.',
);
console.error(scannerError);
stopScanner();
}
}
};
startReader();
@ -264,15 +269,27 @@ const TimeClockPage = () => {
cancelled = true;
try {
reader.reset();
reader?.reset();
} catch (resetError) {
console.warn(
'Réinitialisation du lecteur QR ignorée.',
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 = () => {
setError('');
@ -295,7 +312,7 @@ const TimeClockPage = () => {
return;
}
setIsScanning(true);
setIsScannerDialogOpen(true);
};
const formatTime = (timeString) => {
@ -366,7 +383,6 @@ const TimeClockPage = () => {
}
}}
>
<DialogTrigger asChild>
<Button
onClick={startScanner}
variant="default"
@ -374,27 +390,34 @@ const TimeClockPage = () => {
>
{isAdmin ? 'Pointer avec un QR code' : 'Pointer'}
</Button>
</DialogTrigger>
<DialogContent className="max-w-2xl max-h-[90vh] overflow-auto">
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle>Aperçu de la caméra</DialogTitle>
</DialogHeader>
{isScanning ? (
<div className="scanner-panel">
<video ref={videoRef} className="scanner-video w-full h-auto" playsInline muted />
<div className="scanner-frame" aria-hidden="true" />
<Button onClick={stopScanner} variant="secondary" className="mt-2">
<video
ref={videoRef}
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
</Button>
</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>
</Dialog>