import React, { FC, PropsWithChildren, useCallback, useEffect, useMemo, useRef, useState, } from 'react'; import { Button, LayoutChangeEvent, PixelRatio, StyleProp, StyleSheet, Text, TouchableOpacity, useWindowDimensions, View, ViewStyle, } from 'react-native'; import {runOnJS} from 'react-native-reanimated'; import { Camera, Frame, useCameraDevices, useFrameProcessor, } from 'react-native-vision-camera'; import { boundingBoxAdjustToView, BoundingFrame, Dimensions, MRZCameraProps, MRZFrame, scanMRZ, sortFormatsByResolution, TextBlock, } from 'diva-mobile-vision-camera-mrz-scanner'; const MRZCamera: FC> = ({ enableBoundingBox, boundingBoxStyle, boundingBoxHorizontalPadding, boundingBoxVerticalPadding, style, skipButtonEnabled: photoSkipButtonEnabled, skipButton: photoSkipButton, onSkipPressed: photoSkipOnPress, skipButtonStyle: photoSkipButtonStyle, cameraProps, onData, scanSuccess, skipButtonText, cameraDirection, isActiveCamera, languages, }) => { //***************************************************************************************** // setting up the state //***************************************************************************************** // Permissions const [hasPermission, setHasPermission] = React.useState(false); // camera states const devices = useCameraDevices(); const direction: 'front' | 'back' = cameraDirection ?? 'back'; const device = devices[direction]; const camera = useRef(null); const {height: screenHeight, width: screenWidth} = useWindowDimensions(); const [isActive, setIsActive] = useState(true); const [feedbackText, setFeedbackText] = useState(''); const [ocrElements, setOcrElements] = useState([]); const [frameDimensions, setFrameDimensions] = useState(); const landscapeMode = screenWidth > screenHeight; const [pixelRatio, setPixelRatio] = React.useState(1); //***************************************************************************************** // Comp Logic //***************************************************************************************** // const xRatio = frame.width / WINDOW_WIDTH; // const yRatio = frame.height / WINDOW_HEIGHT; /* A cleanup function that is called when the component is unmounted. */ useEffect(() => { return () => { setIsActive(false); }; }, []); // which format should we use const formats = useMemo( () => device?.formats.sort(sortFormatsByResolution), [device?.formats], ); //figure our what happens if it is undefined? const [format, setFormat] = useState( formats && formats.length > 0 ? formats[0] : undefined, ); /** * Prevents sending copious amounts of scans */ const handleScan = useCallback( (data: MRZFrame, frame: Frame) => { const isRotated = !landscapeMode; setFrameDimensions( isRotated ? { width: frame.height, height: frame.width, } : { width: frame.width, height: frame.height, }, ); if ( data && data.result && data.result.blocks && data.result.blocks.length === 0 ) { setFeedbackText(''); } /* Scanning the text from the image and then setting the state of the component. */ if ( data && data.result && data.result.blocks && data.result.blocks.length > 0 && languages ) { let updatedOCRElements: BoundingFrame[] = []; data.result.blocks.forEach((block: TextBlock) => { if (block.frame.width / screenWidth < 0.8) { setFeedbackText(`${languages[0]!.HOLD_STILL}`); } else { setFeedbackText(`${languages[0]!.SCANNING}`); } updatedOCRElements.push({...block.frame}); }); let lines: string[] = []; data.result.blocks.forEach((block: TextBlock) => { lines.push(block.text); }); if (lines.length > 0 && isActive && onData) { setOcrElements(updatedOCRElements); onData(lines); } else { setOcrElements([]); } } }, [isActive, landscapeMode, onData, screenWidth, languages], ); /* Setting the format to the first format in the formats array. */ useEffect(() => { setFormat(formats && formats.length > 0 ? formats[0] : undefined); // eslint-disable-next-line react-hooks/exhaustive-deps }, [device]); /* Using the useFrameProcessor hook to process the video frames. */ const frameProcessor = useFrameProcessor( frame => { 'worklet'; if (!scanSuccess) { const ocrData = scanMRZ(frame); runOnJS(handleScan)(ocrData, frame); } }, [handleScan], ); useEffect(() => { (async () => { const status = await Camera.requestCameraPermission(); setHasPermission(status === 'authorized'); })(); }, []); /* Using the useMemo hook to create a style object. */ const boundingStyle = useMemo>( () => ({ position: 'absolute', top: 0, left: 0, width: screenWidth, height: screenHeight, }), [screenWidth, screenHeight], ); const bounds = ocrElements[ocrElements.length - 1]; //***************************************************************************************** // stylesheet //***************************************************************************************** const styles = StyleSheet.create({ fixToText: { flexDirection: 'row', justifyContent: 'space-between', }, skipButtonContainer: { position: 'absolute', bottom: screenHeight * 0.05, width: screenWidth, alignItems: 'center', justifyContent: 'center', flexDirection: 'row', }, feedbackContainer: { position: 'absolute', top: screenHeight * 0.3, width: screenWidth, alignItems: 'center', }, feedbackText: { backgroundColor: 'white', color: 'black', fontSize: 18, paddingRight: 8, paddingLeft: 8, textAlign: 'center', }, boundingBox: { borderRadius: 5, borderWidth: 3, borderColor: 'yellow', position: 'absolute', left: bounds ? bounds.x * pixelRatio : 0, top: bounds ? bounds.y * pixelRatio : 0, height: bounds ? bounds.height : 35, width: bounds ? bounds.width : 35, }, }); //***************************************************************************************** // Components //***************************************************************************************** return ( {device && hasPermission ? ( { setPixelRatio( event.nativeEvent.layout.width / PixelRatio.getPixelSizeForLayoutSize( event.nativeEvent.layout.width, ), ); }} /> ) : undefined} {/* */} {enableBoundingBox && ocrElements.length > 0 ? ( {frameDimensions && (() => { const {adjustRect} = boundingBoxAdjustToView( frameDimensions, { width: landscapeMode ? screenHeight : screenWidth, height: landscapeMode ? screenWidth : screenHeight, }, landscapeMode, boundingBoxVerticalPadding, boundingBoxHorizontalPadding, ); return ocrElements ? ocrElements.map((i, index) => { const {left, ...others} = adjustRect(i); return ( ); }) : undefined; })()} ) : null} {photoSkipButton ? ( {photoSkipButtonEnabled ? ( photoSkipButton ? ( {photoSkipButton} ) : (