/* 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;