/* eslint @typescript-eslint/no-var-requires: "off" */ /* eslint @typescript-eslint/no-require-imports: "off" */ 'use strict'; import React, { useCallback, useMemo, useState } from 'react'; import { SafeAreaView, Image, Text, TouchableOpacity, View, StyleSheet, } from 'react-native'; import { RNCamera } from 'react-native-camera'; import { colors, fontStyles } from '../../../styles/common'; import Icon from 'react-native-vector-icons/Ionicons'; import { strings } from '../../../../locales/i18n'; import { URRegistryDecoder } from '@keystonehq/ur-decoder'; import Modal from 'react-native-modal'; import { UR } from '@ngraveio/bc-ur'; import AnalyticsV2 from '../../../util/analyticsV2'; import { SUPPORTED_UR_TYPE } from '../../../constants/qr'; const styles = StyleSheet.create({ modal: { margin: 0, }, container: { width: '100%', height: '100%', backgroundColor: colors.black, }, preview: { flex: 1, }, innerView: { flex: 1, }, closeIcon: { marginTop: 20, marginRight: 20, width: 40, alignSelf: 'flex-end', }, frame: { width: 250, height: 250, alignSelf: 'center', justifyContent: 'center', marginTop: 100, opacity: 0.5, }, text: { flex: 1, fontSize: 17, color: colors.white, textAlign: 'center', justifyContent: 'center', marginTop: 100, }, hint: { backgroundColor: colors.whiteTransparent, width: '100%', height: 120, alignItems: 'center', justifyContent: 'center', }, hintText: { width: 240, maxWidth: '80%', color: colors.black, textAlign: 'center', fontSize: 16, ...fontStyles.normal, }, bold: { ...fontStyles.bold, }, }); const frameImage = require('images/frame.png'); // eslint-disable-line import/no-commonjs interface AnimatedQRScannerProps { visible: boolean; purpose: 'sync' | 'sign'; onScanSuccess: (ur: UR) => void; onScanError: (error: string) => void; hideModal: () => void; pauseQRCode?: (x: boolean) => void; } const AnimatedQRScannerModal = (props: AnimatedQRScannerProps) => { const { visible, onScanError, purpose, onScanSuccess, hideModal, pauseQRCode, } = props; const [urDecoder, setURDecoder] = useState(new URRegistryDecoder()); const [progress, setProgress] = useState(0); let expectedURTypes: string[]; if (purpose === 'sync') { expectedURTypes = [ SUPPORTED_UR_TYPE.CRYPTO_HDKEY, SUPPORTED_UR_TYPE.CRYPTO_ACCOUNT, ]; } else { expectedURTypes = [SUPPORTED_UR_TYPE.ETH_SIGNATURE]; } const reset = useCallback(() => { setURDecoder(new URRegistryDecoder()); setProgress(0); }, []); const hintText = useMemo( () => ( {strings('connect_qr_hardware.hint_text')} {strings( purpose === 'sync' ? 'connect_qr_hardware.purpose_connect' : 'connect_qr_hardware.purpose_sign', )} ), [purpose], ); const onError = useCallback( (error) => { if (onScanError && error) { AnalyticsV2.trackEvent( AnalyticsV2.ANALYTICS_EVENTS.HARDWARE_WALLET_ERROR, { purpose, error, }, ); onScanError(error.message); } }, [purpose, onScanError], ); const onBarCodeRead = useCallback( (response) => { if (!visible) { return; } if (!response.data) { return; } try { const content = response.data; urDecoder.receivePart(content); setProgress(Math.ceil(urDecoder.getProgress() * 100)); if (urDecoder.isError()) { AnalyticsV2.trackEvent( AnalyticsV2.ANALYTICS_EVENTS.HARDWARE_WALLET_ERROR, { purpose, error: urDecoder.resultError(), }, ); onScanError(strings('transaction.unknown_qr_code')); } else if (urDecoder.isSuccess()) { const ur = urDecoder.resultUR(); if (expectedURTypes.includes(ur.type)) { onScanSuccess(ur); setProgress(0); setURDecoder(new URRegistryDecoder()); } else if (purpose === 'sync') { AnalyticsV2.trackEvent( AnalyticsV2.ANALYTICS_EVENTS.HARDWARE_WALLET_ERROR, { purpose, received_ur_type: ur.type, error: 'invalid `sync` qr code', }, ); onScanError(strings('transaction.invalid_qr_code_sync')); } else { AnalyticsV2.trackEvent( AnalyticsV2.ANALYTICS_EVENTS.HARDWARE_WALLET_ERROR, { purpose, received_ur_type: ur.type, error: 'invalid `sign` qr code', }, ); onScanError(strings('transaction.invalid_qr_code_sign')); } } } catch (e) { onScanError(strings('transaction.unknown_qr_code')); } }, [visible, urDecoder, onScanError, expectedURTypes, purpose, onScanSuccess], ); const onStatusChange = useCallback( (event) => { if (event.cameraStatus === 'NOT_AUTHORIZED') { onScanError(strings('transaction.no_camera_permission')); } }, [onScanError], ); return ( { reset(); pauseQRCode?.(false); }} onModalWillShow={() => pauseQRCode?.(true)} > {`${strings('qr_scanner.scanning')} ${ progress ? `${progress.toString()}%` : '' }`} {hintText} ); }; export default AnimatedQRScannerModal;