// Copyright 2017-2025 @polkadot/react-qr authors & contributors // SPDX-License-Identifier: Apache-2.0 import { BrowserQRCodeReader, type IScannerControls } from '@zxing/browser'; import React, { useCallback, useEffect, useMemo, useRef } from 'react'; import { styled } from './styled.js'; import { createImgSize } from './util.js'; interface Props { className?: string | undefined; delay?: number; onError?: undefined | ((error: Error) => void); onScan: (data: string) => void; size?: string | number | undefined; style?: React.CSSProperties | undefined; } const DEFAULT_DELAY = 150; const DEFAULT_ERROR = (error: Error): void => { console.error('@polkadot/react-qr:Scan', error.message); }; function Scan ({ className = '', delay = DEFAULT_DELAY, onError = DEFAULT_ERROR, onScan, size, style = {} }: Props): React.ReactElement { const videoRef = useRef(null); const controlsRef = useRef(null); const containerStyle = useMemo( () => createImgSize(size), [size] ); const _onError = useCallback( (error: Error) => onError(error), [onError] ); useEffect(() => { const codeReader = new BrowserQRCodeReader(); const startScanning = async () => { try { const videoInputDevices = await BrowserQRCodeReader.listVideoInputDevices(); const selectedDeviceId = videoInputDevices[0].deviceId; controlsRef.current = await codeReader.decodeFromVideoDevice( selectedDeviceId, videoRef.current ?? undefined, (result, error) => { if (result) { onScan(result.getText()); } if (error && !(error instanceof Error)) { _onError(new Error(error)); } } ); } catch (error) { _onError(error instanceof Error ? error : new Error('Unknown error occurred')); } }; // eslint-disable-next-line @typescript-eslint/no-misused-promises const timeoutId = setTimeout(startScanning, delay); return () => { clearTimeout(timeoutId); if (controlsRef.current) { controlsRef.current.stop(); } }; }, [onScan, _onError, delay]); return ( ); } const StyledDiv = styled.div` .ui--qr-Scan { display: inline-block; height: 100%; transform: matrix(-1, 0, 0, 1, 0, 0); width: 100%; } `; export const QrScan = React.memo(Scan);