fix perm and camera
This commit is contained in:
parent
ebf0d5b6ec
commit
bb0f242dd5
@ -150,25 +150,54 @@ const TimeClockPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const stopScanner = () => {
|
const scannerSessionRef = useRef(0);
|
||||||
const controls = qrControlsRef.current;
|
|
||||||
qrControlsRef.current = null;
|
|
||||||
|
|
||||||
|
const stopScanner = () => {
|
||||||
|
// Invalide immédiatement toute session de scan en cours
|
||||||
|
scannerSessionRef.current += 1;
|
||||||
|
|
||||||
|
// Arrêter les contrôles ZXing
|
||||||
|
if (qrControlsRef.current) {
|
||||||
try {
|
try {
|
||||||
controls?.stop();
|
qrControlsRef.current.stop();
|
||||||
} catch (stopError) {
|
} catch (error) {
|
||||||
console.warn('Arrêt du lecteur QR déjà effectué.', stopError);
|
console.warn('Impossible d’arrêter le scanner QR.', error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (videoRef.current) {
|
qrControlsRef.current = null;
|
||||||
videoRef.current.pause();
|
}
|
||||||
|
|
||||||
|
// Arrêter explicitement la caméra
|
||||||
|
if (videoRef.current?.srcObject) {
|
||||||
|
const stream = videoRef.current.srcObject;
|
||||||
|
|
||||||
|
stream.getTracks().forEach((track) => {
|
||||||
|
try {
|
||||||
|
track.stop();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Impossible d’arrêter la piste caméra.', error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
videoRef.current.srcObject = null;
|
videoRef.current.srcObject = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleQrResult = async (code) => {
|
const closeScanner = () => {
|
||||||
setScanConflict(null);
|
|
||||||
stopScanner();
|
stopScanner();
|
||||||
|
setIsScannerDialogOpen(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleQrResult = async (code, sessionId) => {
|
||||||
|
// Si cette session n’est plus active, on ignore le résultat
|
||||||
|
if (scannerSessionRef.current !== sessionId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fermer immédiatement la caméra
|
||||||
|
closeScanner();
|
||||||
|
|
||||||
|
setScanConflict(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await timeApi.scanTimeEntry(code);
|
const response = await timeApi.scanTimeEntry(code);
|
||||||
@ -176,7 +205,11 @@ const TimeClockPage = () => {
|
|||||||
await loadTimeEntries();
|
await loadTimeEntries();
|
||||||
} catch (scanError) {
|
} catch (scanError) {
|
||||||
if (scanError.code === 409 && scanError.conflict) {
|
if (scanError.code === 409 && scanError.conflict) {
|
||||||
setScanConflict({ code, type: scanError.conflict, message: scanError.message });
|
setScanConflict({
|
||||||
|
code,
|
||||||
|
type: scanError.conflict,
|
||||||
|
message: scanError.message,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
setError(scanError.message);
|
setError(scanError.message);
|
||||||
}
|
}
|
||||||
@ -185,12 +218,15 @@ const TimeClockPage = () => {
|
|||||||
|
|
||||||
const resolveScanConflict = async (resolution) => {
|
const resolveScanConflict = async (resolution) => {
|
||||||
if (!scanConflict) return;
|
if (!scanConflict) return;
|
||||||
|
|
||||||
setError('');
|
setError('');
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await timeApi.scanTimeEntry(scanConflict.code, {
|
const response = await timeApi.scanTimeEntry(scanConflict.code, {
|
||||||
resolution,
|
resolution,
|
||||||
time: conflictTime,
|
time: conflictTime,
|
||||||
});
|
});
|
||||||
|
|
||||||
setScanMessage(response.message);
|
setScanMessage(response.message);
|
||||||
setScanConflict(null);
|
setScanConflict(null);
|
||||||
await loadTimeEntries();
|
await loadTimeEntries();
|
||||||
@ -199,26 +235,37 @@ const TimeClockPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => () => stopScanner(), []);
|
// Nettoyage lors du démontage
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
stopScanner();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Gestion du scanner QR
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isScannerDialogOpen) {
|
if (!isScannerDialogOpen) {
|
||||||
return undefined;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const sessionId = scannerSessionRef.current;
|
||||||
let cancelled = false;
|
let cancelled = false;
|
||||||
let reader = null;
|
let reader = null;
|
||||||
|
|
||||||
const startReader = async () => {
|
const startReader = async () => {
|
||||||
// Attendre que Radix ait réellement monté le DialogContent
|
// Attendre que DialogContent soit réellement monté
|
||||||
await new Promise((resolve) => {
|
await new Promise((resolve) => {
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
requestAnimationFrame(resolve);
|
requestAnimationFrame(resolve);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if (cancelled || !videoRef.current) {
|
// Le dialog a pu être fermé pendant l’attente
|
||||||
console.warn('Vidéo du scanner non disponible.');
|
if (
|
||||||
|
cancelled ||
|
||||||
|
scannerSessionRef.current !== sessionId ||
|
||||||
|
!videoRef.current
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,33 +281,51 @@ const TimeClockPage = () => {
|
|||||||
},
|
},
|
||||||
videoRef.current,
|
videoRef.current,
|
||||||
(result) => {
|
(result) => {
|
||||||
if (!result || cancelled) {
|
if (
|
||||||
|
!result ||
|
||||||
|
cancelled ||
|
||||||
|
scannerSessionRef.current !== sessionId
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleQrResult(result.getText());
|
handleQrResult(result.getText(), sessionId);
|
||||||
setIsScannerDialogOpen(false);
|
}
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (cancelled) {
|
// La fermeture peut avoir eu lieu pendant l’initialisation
|
||||||
|
if (
|
||||||
|
cancelled ||
|
||||||
|
scannerSessionRef.current !== sessionId
|
||||||
|
) {
|
||||||
|
try {
|
||||||
controls.stop();
|
controls.stop();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
'Impossible d’arrêter le scanner après annulation.',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
qrControlsRef.current = controls;
|
qrControlsRef.current = controls;
|
||||||
} catch (scannerError) {
|
} catch (scannerError) {
|
||||||
if (cancelled) {
|
if (
|
||||||
|
cancelled ||
|
||||||
|
scannerSessionRef.current !== sessionId
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.error('Erreur scanner QR:', scannerError);
|
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.'
|
||||||
);
|
);
|
||||||
|
|
||||||
stopScanner();
|
closeScanner();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -269,26 +334,51 @@ const TimeClockPage = () => {
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true;
|
cancelled = true;
|
||||||
|
|
||||||
|
// Invalide cette session
|
||||||
|
if (scannerSessionRef.current === sessionId) {
|
||||||
|
scannerSessionRef.current += 1;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
reader?.reset();
|
reader?.reset();
|
||||||
} catch (resetError) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Réinitialisation du lecteur QR ignorée.',
|
'Réinitialisation du lecteur QR ignorée.',
|
||||||
resetError,
|
error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const controls = qrControlsRef.current;
|
// Arrêter les contrôles ZXing
|
||||||
qrControlsRef.current = null;
|
if (qrControlsRef.current) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
controls?.stop();
|
qrControlsRef.current.stop();
|
||||||
} catch (stopError) {
|
} catch (error) {
|
||||||
console.warn(
|
console.warn(
|
||||||
'Arrêt du lecteur QR ignoré.',
|
'Impossible d’arrêter les contrôles QR.',
|
||||||
stopError,
|
error
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qrControlsRef.current = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arrêter explicitement toutes les pistes caméra
|
||||||
|
if (videoRef.current?.srcObject) {
|
||||||
|
const stream = videoRef.current.srcObject;
|
||||||
|
|
||||||
|
stream.getTracks().forEach((track) => {
|
||||||
|
try {
|
||||||
|
track.stop();
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(
|
||||||
|
'Impossible d’arrêter la piste caméra.',
|
||||||
|
error
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
videoRef.current.srcObject = null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}, [isScannerDialogOpen]);
|
}, [isScannerDialogOpen]);
|
||||||
|
|
||||||
@ -303,7 +393,7 @@ const TimeClockPage = () => {
|
|||||||
window.location.hostname !== '192.168.1.19'
|
window.location.hostname !== '192.168.1.19'
|
||||||
) {
|
) {
|
||||||
setError(
|
setError(
|
||||||
'La caméra nécessite une connexion HTTPS sur cette adresse réseau.',
|
'La caméra nécessite une connexion HTTPS sur cette adresse réseau.'
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -313,6 +403,9 @@ const TimeClockPage = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nouvelle session de scanner
|
||||||
|
scannerSessionRef.current += 1;
|
||||||
|
|
||||||
setIsScannerDialogOpen(true);
|
setIsScannerDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -377,10 +470,10 @@ const TimeClockPage = () => {
|
|||||||
<Dialog
|
<Dialog
|
||||||
open={isScannerDialogOpen}
|
open={isScannerDialogOpen}
|
||||||
onOpenChange={(open) => {
|
onOpenChange={(open) => {
|
||||||
setIsScannerDialogOpen(open);
|
if (open) {
|
||||||
|
setIsScannerDialogOpen(true);
|
||||||
if (!open) {
|
} else {
|
||||||
stopScanner();
|
closeScanner();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -412,7 +505,7 @@ const TimeClockPage = () => {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
onClick={() => setIsScannerDialogOpen(false)}
|
onClick={closeScanner}
|
||||||
variant="secondary"
|
variant="secondary"
|
||||||
className="mt-2"
|
className="mt-2"
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user