1import * as React from 'react'; 2import { captureImageData } from './WebCameraUtils'; 3const qrWorkerMethod = ({ data, width, height }) => { 4 // eslint-disable-next-line no-undef 5 const decoded = self.jsQR(data, width, height, { 6 inversionAttempts: 'attemptBoth', 7 }); 8 let parsed; 9 try { 10 parsed = JSON.parse(decoded); 11 } 12 catch { 13 parsed = decoded; 14 } 15 if (parsed?.data) { 16 const nativeEvent = { 17 type: 'qr', 18 data: parsed.data, 19 cornerPoints: [], 20 bounds: { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } }, 21 }; 22 if (parsed.location) { 23 nativeEvent.cornerPoints = [ 24 parsed.location.topLeftCorner, 25 parsed.location.bottomLeftCorner, 26 parsed.location.topRightCorner, 27 parsed.location.bottomRightCorner, 28 ]; 29 } 30 return nativeEvent; 31 } 32 return parsed; 33}; 34const createWorkerAsyncFunction = (fn, deps) => { 35 const stringifiedFn = [ 36 `self.func = ${fn.toString()};`, 37 'self.onmessage = (e) => {', 38 ' const result = self.func(e.data);', 39 ' self.postMessage(result);', 40 '};', 41 ]; 42 if (deps.length > 0) { 43 stringifiedFn.unshift(`importScripts(${deps.map((dep) => `'${dep}'`).join(', ')});`); 44 } 45 const blob = new Blob(stringifiedFn, { type: 'text/javascript' }); 46 const worker = new Worker(URL.createObjectURL(blob)); 47 // First-In First-Out queue of promises 48 const promises = []; 49 worker.onmessage = (e) => promises.shift()?.resolve(e.data); 50 return (data) => { 51 return new Promise((resolve, reject) => { 52 promises.push({ resolve, reject }); 53 worker.postMessage(data); 54 }); 55 }; 56}; 57const decode = createWorkerAsyncFunction(qrWorkerMethod, [ 58 'https://cdn.jsdelivr.net/npm/[email protected]/dist/jsQR.min.js', 59]); 60export function useWebQRScanner(video, { isEnabled, captureOptions, interval, onScanned, onError, }) { 61 const isRunning = React.useRef(false); 62 const timeout = React.useRef(undefined); 63 async function scanAsync() { 64 // If interval is 0 then only scan once. 65 if (!isRunning.current || !onScanned) { 66 stop(); 67 return; 68 } 69 try { 70 const data = captureImageData(video.current, captureOptions); 71 if (data) { 72 const nativeEvent = await decode(data); 73 if (nativeEvent?.data) { 74 onScanned({ 75 nativeEvent, 76 }); 77 } 78 } 79 } 80 catch (error) { 81 if (onError) { 82 onError({ nativeEvent: error }); 83 } 84 } 85 finally { 86 // If interval is 0 then only scan once. 87 if (interval === 0) { 88 stop(); 89 return; 90 } 91 const intervalToUse = !interval || interval < 0 ? 16 : interval; 92 // @ts-ignore: Type 'Timeout' is not assignable to type 'number' 93 timeout.current = setTimeout(() => { 94 scanAsync(); 95 }, intervalToUse); 96 } 97 } 98 function stop() { 99 isRunning.current = false; 100 clearTimeout(timeout.current); 101 } 102 React.useEffect(() => { 103 if (isEnabled) { 104 isRunning.current = true; 105 scanAsync(); 106 } 107 return () => { 108 if (isEnabled) { 109 stop(); 110 } 111 }; 112 }, [isEnabled]); 113} 114//# sourceMappingURL=useWebQRScanner.js.map