import React from 'react'; import type { ReactElement } from 'react'; import type { ViewProps } from 'react-native'; import { DecodeHintType, BarcodeFormat, BrowserMultiFormatReader, Result, } from '@zxing/library'; import styled from '@emotion/styled'; import { Camera, CameraType } from './Camera'; import './styles.css'; interface BarCodeAcceptedFormat { acceptedFormats: string[]; } interface Props extends ViewProps { autoFocusEnabled: boolean; flashModeEnabled: boolean; acceptFormats?: BarCodeAcceptedFormat; onScanResultListener?: (event: any) => void; onErrorListener?: (event: any) => void; frameWidth: number; frameHeight: number; frameBorderColor?: string; screenWidth: number; screenHeight: number; isTouchSupported?: boolean; touchSupportedCallback?: (touchSupported: boolean) => void; } export type BarcodeScannerViewType = React.ForwardRefExoticComponent< Props & React.RefAttributes > & { takePhoto(): string; getNumberOfCameras(): number; flashStatus(): boolean; }; const delay = 350; const BarcodeScannerView = React.forwardRef( (props: Props, ref): ReactElement => { const { frameWidth, frameHeight, acceptFormats, screenHeight, flashModeEnabled, onScanResultListener, onErrorListener, isTouchSupported, touchSupportedCallback, } = props; const codeReader = React.useMemo(() => { const hints = new Map(); hints.set( DecodeHintType.POSSIBLE_FORMATS, mapToSupportedFormats(acceptFormats?.acceptedFormats || []) ); return new BrowserMultiFormatReader(hints); }, [acceptFormats?.acceptedFormats]); const camera = React.useRef(null); React.useImperativeHandle(ref, () => ({ takePhoto: () => { camera?.current?.takePhoto(); }, getNumberOfCameras: () => { return camera?.current?.getNumberOfCameras() ?? 0; }, flashStatus: () => { return camera?.current?.flashStatus() ?? false; }, })); const capture = React.useCallback(() => { // const imageSrc = webcamRef?.current?.getScreenshot(); const numberOfCameras = camera?.current?.getNumberOfCameras() ?? 0; if (numberOfCameras > 0) { const imageSrc = camera?.current?.takePhoto(); if (imageSrc) { codeReader .decodeFromImage(undefined, imageSrc) .then((result: Result) => { const event = { nativeEvent: { value: refactorResult(result), }, }; onScanResultListener?.(event); }) .catch(() => { // console.log(err); // onErrorListener?.(err); }); } } }, [codeReader, onScanResultListener]); // React.useEffect(() => { // // Turn on the flashlight if prop is defined and device has the capability // if ( // typeof torch === 'boolean' && // // @ts-ignore // navigator?.mediaDevices?.getSupportedConstraints().torch // ) { // const stream = webcamRef?.current?.video?.srcObject as MediaStream; // const track = stream?.getVideoTracks()[0] as MediaStreamTrack; // get the active track of the stream // if ( // track && // track.getCapabilities().torch && // !track.getConstraints().torch // ) { // track // .applyConstraints({ // advanced: [{ torch }], // }) // .catch((err: any) => onUpdate(err)); // } // } // }, [onUpdate]); React.useEffect(() => { const interval = setInterval(capture, delay); return () => { clearInterval(interval); }; }, [capture]); React.useEffect(() => { return () => { codeReader.reset(); }; }, [codeReader]); const FocusView = styled.div<{ frameWidth: string; frameHeight: string }>` position: absolute; width: ${(props) => props.frameWidth}; height: ${(props) => props.frameHeight}; border-radius: 10px; box-shadow: 0 0 0 99999px rgba(0, 0, 0, 0.5); `; const Border = styled.div<{ frameWidth: string; frameHeight: string }>` --b: 4px; /* thickness of the border */ --c: #ffb800; /* color of the border */ --w: 100px; /* width of border */ --r: 10px; /* radius */ position: absolute; z-index: 5; border-color: transparent; width: ${(props) => props.frameWidth}; height: ${(props) => props.frameHeight}; `; const LabelView = styled.div<{ top: string }>` background-color: #000000; display: inline-flex; align-items: center; justify-content: center; text-align: center; // top: ${(props) => props.top}; border-radius: 23px; padding: 12px 16px; border: 1px solid #585858; // min-height: 44px; background: rgba(0, 0, 0, 0.3); backdrop-filter: blur(6.5px); webkit-backdrop-filter: blur(6.5px); `; const LabelText = styled.div` font-size: 16px; font-weight: 500; color: white; text-align: center; margin: 0; `; const Container = styled.div<{ top: string }>` position: absolute; z-index: 5; align-items: center; justify-content: center; width: 100%; display: flex; top: ${(props) => props.top}; `; const FocusContainer = styled.div` // position: absolute; // z-index: 5; align-items: center; justify-content: center; width: 100%; display: flex; flex: 1; `; return ( Center barcode in scanner. ); } ); BarcodeScannerView.displayName = 'BarcodeScannerView'; export default BarcodeScannerView; const mapToSupportedFormats = (acceptFormats: string[]): BarcodeFormat[] => { const supportedFormats: BarcodeFormat[] = []; acceptFormats.forEach((format) => { switch (format) { case 'AZTEC': supportedFormats.push(BarcodeFormat.AZTEC); break; case 'CODABAR': supportedFormats.push(BarcodeFormat.CODABAR); break; case 'CODE_39': supportedFormats.push(BarcodeFormat.CODE_39); break; case 'CODE_93': supportedFormats.push(BarcodeFormat.CODE_93); break; case 'CODE_128': supportedFormats.push(BarcodeFormat.CODE_128); break; case 'EAN_8': supportedFormats.push(BarcodeFormat.EAN_8); break; case 'EAN_13': supportedFormats.push(BarcodeFormat.EAN_13); break; case 'PDF': supportedFormats.push(BarcodeFormat.PDF_417); break; case 'QR_CODE': supportedFormats.push(BarcodeFormat.QR_CODE); break; case 'UPC_A': supportedFormats.push(BarcodeFormat.UPC_A); break; case 'UPC_E': supportedFormats.push(BarcodeFormat.UPC_E); break; default: break; } }); return supportedFormats; }; const refactorResult = (result: Result): string => { const text = result.getText(); // const format = result.getBarcodeFormat(); // const rawBytes = result.getRawBytes(); // console.log({ text, length: text.length, format, rawBytes }); // if (text.length === 13 && format === 7) { // console.log('slide'); // return text.slice(1); // } return text; };