From 971f0c3c00654d8680df7652d46f932c264d3fcf Mon Sep 17 00:00:00 2001 From: trafficlunar Date: Sun, 29 Mar 2026 00:34:30 +0000 Subject: [PATCH] fix: camera problems --- src/components/submit-form/camera.tsx | 44 ++++++++++++++----- .../submit-form/switch-file-upload.tsx | 18 ++++---- 2 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/components/submit-form/camera.tsx b/src/components/submit-form/camera.tsx index 5045cd6..36780c3 100644 --- a/src/components/submit-form/camera.tsx +++ b/src/components/submit-form/camera.tsx @@ -10,11 +10,12 @@ import { useSelect } from "downshift"; interface Props { isOpen: boolean; setIsOpen: React.Dispatch>; + onCapture?: () => void; setImage?: React.Dispatch>; setQrBytesRaw?: React.Dispatch>; } -export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: Props) { +export default function Camera({ isOpen, setIsOpen, onCapture, setImage, setQrBytesRaw }: Props) { const [isVisible, setIsVisible] = useState(false); const [permissionGranted, setPermissionGranted] = useState(null); @@ -23,6 +24,7 @@ export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: P const [selectedDeviceId, setSelectedDeviceId] = useState(null); const videoRef = useRef(null); + const streamRef = useRef(null); const requestRef = useRef(null); const canvasRef = useRef(null); @@ -65,6 +67,7 @@ export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: P if (setImage) { setImage(canvas.toDataURL()); + if (onCapture) onCapture(); close(); return; } @@ -89,14 +92,34 @@ export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: P navigator.mediaDevices .getUserMedia({ video: true, audio: false }) - .then(() => setPermissionGranted(true)) + .then((stream) => { + // immediately stop this temp stream + stream.getTracks().forEach((track) => track.stop()); + setPermissionGranted(true); + }) .catch((err) => { setPermissionGranted(false); console.error("An error occurred trying to access the camera", err); }); }; + const stopCamera = () => { + if (requestRef.current) { + cancelAnimationFrame(requestRef.current); + requestRef.current = null; + } + if (videoRef.current) { + videoRef.current.pause(); + videoRef.current.srcObject = null; + } + if (streamRef.current) { + streamRef.current.getTracks().forEach((track) => track.stop()); + streamRef.current = null; + } + }; + const close = () => { + stopCamera(); setIsVisible(false); setTimeout(() => { setIsOpen(false); @@ -132,6 +155,7 @@ export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: P }) .then((stream) => { if (!stream || !videoRef.current) return; + streamRef.current = stream; videoRef.current.srcObject = stream; videoRef.current.play(); }) @@ -141,16 +165,9 @@ export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: P // cleanup return () => { - if (requestRef.current) { - cancelAnimationFrame(requestRef.current); - } - if (videoRef.current?.srcObject) { - const stream = videoRef.current.srcObject as MediaStream; - stream.getTracks().forEach((track) => track.stop()); - videoRef.current.srcObject = null; - } + stopCamera(); }; - }, [isOpen, permissionGranted, selectedDeviceId, takePicture]); + }, [isOpen, permissionGranted, selectedDeviceId]); return (
@@ -218,7 +235,10 @@ export default function Camera({ isOpen, setIsOpen, setImage, setQrBytesRaw }: P
)} -