Fix barcode scanner on iOS Safari

iOS WebKit does not provide a reliable native Barcode Detection API, and
ZXing often failed due to strict camera constraints and video startup timing.

- Install @undecaf/barcode-detector-polyfill (ZBar WASM) on Apple devices
- Fall back through progressively looser getUserMedia constraints
- Wait for video metadata/playback before decoding frames
- Throttle native scans on iOS and tune ZXing retry intervals
- Defer scanner startup until the modal video element is mounted

Co-authored-by: Ned Halksworth <hello@nedhalksworth.com>
This commit is contained in:
Cursor Agent
2026-05-27 19:51:08 +00:00
co-authored by Ned Halksworth
parent 4e5fa5d42e
commit 64584315e5
6 changed files with 203 additions and 83 deletions
+27 -17
View File
@@ -191,24 +191,32 @@ export function BarcodeScannerModal({
window.setTimeout(() => closeButtonRef.current?.focus(), 80);
let active = true;
const video = videoRef.current;
if (!video) return undefined;
let frameId = 0;
void startBarcodeScanner(video, handleScannerResult, handleScannerError)
.then((controller) => {
if (!active) {
controller.stop();
return;
}
controllerRef.current = controller;
setScannerMode(controller.mode);
setPhase("scanning");
})
.catch((error: BarcodeScannerError) => {
if (!active) return;
setScannerError(error);
setPhase("error");
});
const startScanner = () => {
const video = videoRef.current;
if (!video || !active) return;
void startBarcodeScanner(video, handleScannerResult, handleScannerError)
.then((controller) => {
if (!active) {
controller.stop();
return;
}
controllerRef.current = controller;
setScannerMode(controller.mode);
setPhase("scanning");
})
.catch((error: BarcodeScannerError) => {
if (!active) return;
setScannerError(error);
setPhase("error");
});
};
frameId = window.requestAnimationFrame(() => {
window.requestAnimationFrame(startScanner);
});
void listBarcodeCatalog()
.then((catalog) => {
@@ -224,6 +232,7 @@ export function BarcodeScannerModal({
return () => {
active = false;
window.cancelAnimationFrame(frameId);
stopScanner();
};
}, [applyManualDefaults, handleScannerError, handleScannerResult, open, stopScanner, userId]);
@@ -335,6 +344,7 @@ export function BarcodeScannerModal({
<video
ref={videoRef}
className="aspect-[3/4] w-full bg-black object-cover sm:aspect-video"
autoPlay
muted
playsInline
aria-label="Live camera preview"