Untitled
unknown
plain_text
a year ago
15 kB
7
Indexable
import React, {useCallback, useEffect, useMemo, useState} from 'react';
import {
ActivityIndicator,
DeviceEventEmitter,
Dimensions,
Image,
Platform,
Text,
TouchableOpacity,
View,
} from 'react-native';
import ImageResizer from '@bam.tech/react-native-image-resizer';
import ImageEditor from '@react-native-community/image-editor';
import {
startLightSensor,
stopLightSensor,
} from 'react-native-ambient-light-sensor';
import {RNCamera} from 'react-native-camera';
import FastImage from 'react-native-fast-image';
import OrangeWarningIcon from '@assets/kyc/orange_warning.webp';
import {BackImage, NavigationButton} from '@components/Atom';
import BoxLockSaveKYC from '@components/Atom/BoxLockSaveKYC';
import {useLocale} from '@hooks/global/useLocale';
import useModal from '@hooks/global/useModal';
import useFirebaseRemoteConfig, {
FirebaseRemoteKycSettingsType,
} from '@hooks/local/useFirebaseRemoteConfig';
import useHelpCenter from '@hooks/local/useHelpCenter';
import useKTPUploadLog, {LogImageKTPReason} from '@hooks/local/useKTPUploadLog';
import useSegmentAnalytics from '@hooks/local/useSegmentAnalytics';
import {AppStackProps} from '@navigator/AppParamList';
import {isAndroid} from '@utils/iPhoneXHelpers';
import styles, {cropHeight, cropLeft, cropTop, cropWidth} from './style';
const windowWidth = Dimensions.get('window').width;
const windowHeight = Dimensions.get('window').height;
const KYCKTPCameraScreen = ({
navigation,
route,
}: AppStackProps<'KYCKTPCamera'>) => {
let camera: RNCamera | null = null;
const [ratio, setRatio] = React.useState<string>('16:9');
const {translate: t} = useLocale();
const {trackEvent} = useSegmentAnalytics('KYCScanKTPPage');
const {showHelpCenter} = useHelpCenter();
const [loading, setLoading] = useState<boolean>(false);
const [, showAlertModal] = useModal(
_ => undefined,
action => action.showAlertModal,
);
const [, hideModal] = useModal(
_ => undefined,
action => action.hideModal,
);
const {doLogErrorUploadKTP} = useKTPUploadLog();
const {getFirebaseRemoteConfigData} = useFirebaseRemoteConfig();
const remoteConfig = useMemo(
() =>
getFirebaseRemoteConfigData<FirebaseRemoteKycSettingsType>(
'kyc_settings',
),
[getFirebaseRemoteConfigData],
);
const onPressHelp = useCallback(() => {
showHelpCenter({});
}, [showHelpCenter]);
const [result, setResult] = React.useState<number>(0);
useEffect(() => {
if (Platform.OS === 'android') {
startLightSensor();
const subscription = DeviceEventEmitter.addListener(
'LightSensor',
(data: {lightValue: number}) => {
setResult(data.lightValue);
},
);
return () => {
stopLightSensor();
subscription?.remove();
};
}
}, []);
useEffect(() => {
navigation.setOptions({
headerTransparent: true,
headerStyle: styles.headerText,
headerRight: () => (
<NavigationButton
text={t('help')}
onPress={() => {
onPressHelp();
}}
/>
),
headerLeft: () => (
<TouchableOpacity
activeOpacity={0.9}
onPress={() => {
navigation.goBack();
}}>
<BackImage />
</TouchableOpacity>
),
});
navigation.setParams({flow: 'KYC'});
}, [navigation, onPressHelp, t]);
const prepareRatio = () => {
if (camera !== null && Platform.OS === 'android') {
camera.getSupportedRatiosAsync().then(ratios => {
const findRatio = ratios.find(item => {
return item === '16:9';
});
setRatio(findRatio ? findRatio : ratios[ratios.length - 2]);
});
}
};
const showModalError = useCallback(
({error}: {error: 'sizing' | 'resolution'}) => {
const errorMessage =
error === 'sizing'
? t('kyc_size_error')
: t('kyc_resolution_error', {
minPx: remoteConfig?.kycKtpUpload.kycImageWidthMinimum,
maxPx: remoteConfig?.kycKtpUpload.kycImageWidthMaximum,
});
setTimeout(() => {
showAlertModal({
visible: true,
title: t('kyc_general_error.title'),
subTitle: errorMessage,
primaryText: t('button.contact_us'),
image: OrangeWarningIcon,
secondaryText: t('retry'),
onSecondaryPress: hideModal,
onPrimaryPress: onPressHelp,
onBackdropPress: hideModal,
});
}, 500);
},
[
t,
remoteConfig?.kycKtpUpload.kycImageWidthMinimum,
remoteConfig?.kycKtpUpload.kycImageWidthMaximum,
showAlertModal,
hideModal,
onPressHelp,
],
);
const visibleGuideline = useCallback(() => {
if (
Platform.OS === 'android' &&
(result < (remoteConfig?.kycKtpUpload.ambientDarkTreshold ?? 51) ||
result > (remoteConfig?.kycKtpUpload.ambientBrightTreshold ?? 5000)) &&
remoteConfig?.kycKtpUpload.takePhotoButtonValidation
) {
return (
<>
<View style={styles.guidelineErrorContainer}>
<FastImage
style={styles.imageGuide}
source={require('@assets/kyc/light_guide.webp')}
/>
<Text style={styles.guidelineText}>
{result <
(remoteConfig?.kycKtpUpload.ambientDarkTreshold ?? 51) &&
t('kyc.guidelineErrorTooDark')}
{result >
(remoteConfig?.kycKtpUpload.ambientBrightTreshold ?? 5000) &&
t('kyc.guidelineErrorTooBright')}
</Text>
</View>
<View style={styles.boxContainer}>
{remoteConfig?.kycKtpUpload.showingThresholdResult && (
<Text style={styles.title}>{result}</Text>
)}
<BoxLockSaveKYC />
</View>
</>
);
}
return (
<>
<Text style={styles.text}>{t('kyc.guidelineTakeKTPPic')}</Text>
<View style={styles.boxContaineriOS}>
<BoxLockSaveKYC />
</View>
</>
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
remoteConfig?.kycKtpUpload.ambientBrightTreshold,
remoteConfig?.kycKtpUpload.ambientDarkTreshold,
remoteConfig?.kycKtpUpload.takePhotoButtonValidation,
result,
t,
]);
const takePicture = async () => {
if (camera) {
if (
((result > (remoteConfig?.kycKtpUpload.ambientBrightTreshold ?? 5000) ||
result < (remoteConfig?.kycKtpUpload.ambientDarkTreshold ?? 51)) &&
Platform.OS === 'android' &&
remoteConfig?.kycKtpUpload.takePhotoButtonValidation) ||
loading
) {
return;
}
setLoading(true);
const options = {
quality: 1,
base64: true,
orientation: RNCamera.Constants.Orientation.portrait,
fixOrientation: true,
};
try {
// Taking the picture
const heightAdjustment = Platform.OS === 'ios' ? 0 : 20;
const widthAdjustment = Platform.OS === 'ios' ? 0 : 5;
const data = await camera.takePictureAsync(options);
const widthScale = data.width / windowWidth;
const heightScale = data.height / windowHeight;
// Crop & resizing image
const imageWidth = widthScale * cropWidth - widthAdjustment;
const imageHeight = heightScale * cropHeight - heightAdjustment;
const uri = await ImageEditor.cropImage(data.uri, {
offset: {
x: widthScale * cropLeft - 50,
y: heightScale * cropTop - 100,
},
size: {
width: imageWidth + 100,
height: imageHeight + 100,
},
displaySize: {
width: widthScale * cropWidth,
height: heightScale * cropHeight,
},
});
const kycImageWidthMinimum =
remoteConfig?.kycKtpUpload?.kycImageWidthMinimum ?? 0;
const kycImageHeightMinimum =
remoteConfig?.kycKtpUpload?.kycImageHeightMinimum ?? 0;
const kycImageWidthMaximum =
remoteConfig?.kycKtpUpload?.kycImageWidthMaximum ?? 0;
const kycImageHeightMaximum =
remoteConfig?.kycKtpUpload?.kycImageHeightMaximum ?? 0;
if (
imageWidth < kycImageWidthMinimum ||
imageHeight < kycImageHeightMinimum ||
imageWidth > kycImageWidthMaximum ||
imageHeight > kycImageHeightMaximum
) {
if (
imageWidth < kycImageWidthMinimum ||
imageHeight < kycImageHeightMinimum
) {
doLogErrorUploadKTP(LogImageKTPReason.ImageResolutionUnderLimit);
} else if (
imageWidth > kycImageWidthMaximum ||
imageHeight > kycImageHeightMaximum
) {
doLogErrorUploadKTP(LogImageKTPReason.ImageResolutionOverLimit);
}
showModalError({error: 'resolution'});
setLoading(false);
return;
}
/**
* on serveral device there is unknown way that got 432 px for the height
* so we add buffer crop resize in order match with AAI requirement
*/
const kycImageBufferWidth =
remoteConfig?.kycKtpUpload?.kycImageBufferWidth || 0;
const kycImageBufferHeight =
remoteConfig?.kycKtpUpload?.kycImageBufferHeight || 0;
// Compressing image
const compressionWidth =
widthScale * cropWidth + (isAndroid() ? kycImageBufferWidth : 0);
const compressionHeight =
heightScale * cropTop + (isAndroid() ? kycImageBufferHeight : 0);
let compression = 50;
let compressedImage = await ImageResizer.createResizedImage(
uri.uri,
compressionWidth,
compressionHeight,
'JPEG',
compression,
);
/* If Image File Size is Under the Minimum Size */
if (
remoteConfig?.kycKtpUpload.kycImageMinFileSize &&
compressedImage.size < remoteConfig?.kycKtpUpload.kycImageMinFileSize
) {
for (
let _compression = compression / 10;
_compression <= 10;
_compression++
) {
compressedImage = await ImageResizer.createResizedImage(
uri.uri,
compressionWidth,
compressionHeight,
'JPEG',
_compression * 10,
);
if (
compressedImage.size >=
remoteConfig?.kycKtpUpload.kycImageMinFileSize
) {
break;
}
}
if (
compressedImage.size <
remoteConfig?.kycKtpUpload.kycImageMinFileSize
) {
doLogErrorUploadKTP(LogImageKTPReason.ImageSizeUnderLimit);
showModalError({error: 'sizing'});
setLoading(false);
return;
}
}
/* If Image File Size is Larger than Maximum */
if (
remoteConfig?.kycKtpUpload.kycImageMinFileSize &&
compressedImage.size > remoteConfig?.kycKtpUpload.kycImageMaxFileSize
) {
for (
let _compression = compression / 10;
_compression > 0;
_compression--
) {
compressedImage = await ImageResizer.createResizedImage(
uri.uri,
compressionWidth,
compressionHeight,
'JPEG',
_compression * 10,
);
if (
compressedImage.size <=
remoteConfig?.kycKtpUpload.kycImageMaxFileSize
) {
break;
}
}
if (
compressedImage.size >
remoteConfig?.kycKtpUpload.kycImageMaxFileSize
) {
doLogErrorUploadKTP(LogImageKTPReason.ImageSizeOverLimit);
showModalError({error: 'sizing'});
setLoading(false);
return;
}
}
trackEvent('KYCScanKTPTakePictureUpload');
navigation.replace('KTPConfirm', {
url: compressedImage.uri,
completion: route.params.completion,
});
} catch (error) {
console.log('error', error);
doLogErrorUploadKTP(
`${
LogImageKTPReason.Others
}, KYCKTPCamera.tsx | takePicture() | error ->', ${String(error)}`,
);
setLoading(false);
}
}
};
return (
<>
<View style={styles.container}>
<RNCamera
ref={ref => {
camera = ref;
}}
captureAudio={false}
style={styles.preview}
ratio={Platform.select({ios: undefined, android: ratio})}
type={RNCamera.Constants.Type.back}
flashMode={RNCamera.Constants.FlashMode.off}
onCameraReady={prepareRatio}
androidCameraPermissionOptions={{
title: t('kyc.accessCameraTip'),
message: t('kyc.accessGallerySubTip'),
buttonPositive: 'Ok',
buttonNegative: t('kyc.notAllow'),
}}
androidRecordAudioPermissionOptions={{
title: t('kyc.accessCameraTip'),
message: t('kyc.accessGallerySubTip'),
buttonPositive: 'Ok',
buttonNegative: t('kyc.notAllow'),
}}
/>
</View>
<Text style={styles.title}>{t('kyc.takeKTPPicture')}</Text>
<View style={styles.guideline}>{visibleGuideline()}</View>
<View style={styles.captureContainer}>
<TouchableOpacity
onPress={() => {
takePicture();
}}
style={styles.capture}>
{loading ? (
<ActivityIndicator size={'large'} style={styles.button} />
) : (
<Image
style={styles.button}
source={require('@assets/kyc/capture_button.webp')}
/>
)}
</TouchableOpacity>
</View>
<View style={styles.cropContainer}>
<View style={styles.rectangle} />
</View>
<View style={styles.overlayTop} />
<View style={styles.overlayRight} />
<View style={styles.overlayBottom} />
<View style={styles.overlayLeft} />
{/* corner */}
<View style={styles.leftTopCorner} />
<View style={styles.rightTopCorner} />
<View style={styles.rightBottomCorner} />
<View style={styles.leftBottomCorner} />
{/* triangle */}
<View style={styles.leftTopTriangle} />
<View style={styles.rightTopTriangle} />
<View style={styles.rightBottomTriangle} />
<View style={styles.leftBottomTriangle} />
</>
);
};
export default KYCKTPCameraScreen;
Editor is loading...
Leave a Comment