viettel-ekyc-sdk
Version:
269 lines • 12.7 kB
JavaScript
/* eslint-disable react-hooks/exhaustive-deps */
import React, { useCallback, useEffect, useState } from 'react';
import { Image, StyleSheet, TouchableOpacity, View } from 'react-native';
import { RNCamera } from 'react-native-camera';
import { useCamera } from 'react-native-camera-hooks';
import { ActionFaceFrame } from '../components/actionFrame';
import { BackgroundFrontCamera } from '../components/BackgroundCamera';
import { Loadding } from '../components/Loadding';
import { RequireFrame } from '../components/RequireFrame';
import { WarningFaceFrame } from '../components/WarningFrame';
import { SUCCESS } from '../constants/api';
import { circleCameraBg, toggleCameraImage } from '../constants/assetsRequire';
import { getCircleInitPosition, requireFaceActionListConstant, requireFrameListConstant, } from '../constants/camera';
import { stylesCamera } from '../constants/style';
import { checkFaceSanityPost, checkLivenessPost } from '../utils/api';
import { getBase64FromUri } from '../utils/utils';
const circleCameraPosition = getCircleInitPosition(circleCameraBg, {
x: 100,
y: 155,
r: 265,
});
const Sound = require('react-native-sound');
Sound.setCategory('Playback');
export const FaceComponent = ({ initialCamera, faceLivenessConfiguration, afterTakePictureAction, warningFrame, loadingComponent, enableFaceDetect, enableCheckSanity, enableCheckTempering, }) => {
const [{ cameraRef, type, ratio, autoFocus, autoFocusPoint }, { toggleFacing, takePicture },] = useCamera(initialCamera);
const initFaceImages = [];
const [selectedIndex, setSelectedIndex] = useState(0);
const [faceImages, setFaceImages] = useState(initFaceImages);
let initRequireFrameList = [];
const [requireFrameList, setRequireFrameList] = useState(initRequireFrameList);
// const [enableFD, setEnableFD] = useState(
// enableFaceDetect !== undefined ? enableFaceDetect : true
// )
let enableFD = enableFaceDetect !== undefined ? enableFaceDetect : true;
const [errorMessage, setErrorMessage] = useState('');
let [isLoading, setIsLoading] = useState(false);
/**
* Get require item list match with require from props
* @returns RequireItem|undefined []
*/
const getRequireFrameList = useCallback(() => {
if (faceLivenessConfiguration) {
return faceLivenessConfiguration.requireList.map((item) => {
return requireFrameListConstant.find((requireItem) => requireItem.action === item.toLowerCase().trim());
});
}
return [];
}, [faceLivenessConfiguration]);
/**
* Get require frame list match with require from props
* Set sound with first require list
*/
useEffect(() => {
var _a;
const requireFrames = getRequireFrameList();
if (requireFrames.length > 0) {
setRequireFrameList(requireFrames);
let whoosh = new Sound((_a = requireFrames[0]) === null || _a === void 0 ? void 0 : _a.sound, (error) => {
if (!error) {
whoosh.play(() => { });
}
else {
console.log('Error sound');
}
});
}
}, [getRequireFrameList]);
/**
* Promise after get all image match require list
*/
useEffect(() => {
if ((faceLivenessConfiguration &&
faceImages.length === requireFrameList.length &&
requireFrameList.length > 0) ||
(!faceLivenessConfiguration && faceImages.length === 1)) {
enableFD = false;
setIsLoading(true);
afterTakePictureAction({ images: faceImages, errorMessage: errorMessage });
setIsLoading(false);
}
}, [faceLivenessConfiguration, faceImages, requireFrameList.length]);
/**
* Sound when next require
*/
useEffect(() => {
var _a;
if (selectedIndex < requireFrameList.length) {
let whoosh = new Sound((_a = requireFrameList[selectedIndex]) === null || _a === void 0 ? void 0 : _a.sound, (error) => {
if (!error) {
whoosh.play(() => { });
}
});
}
}, [requireFrameList, selectedIndex]);
const processImage = async (imageUri) => {
var _a, _b, _c, _d, _e, _f, _g, _h;
const imageUriBase64 = await getBase64FromUri(imageUri);
let message = '';
if (enableCheckTempering && enableCheckSanity) {
const [livenessResponse, sanityResponse] = await Promise.all([
checkLivenessPost(imageUriBase64),
checkFaceSanityPost(imageUriBase64),
]);
if (((_a = sanityResponse.data) === null || _a === void 0 ? void 0 : _a.code) !== SUCCESS) {
message = ((_b = sanityResponse.data) === null || _b === void 0 ? void 0 : _b.message) || '';
}
else {
message = '';
if (((_c = livenessResponse.data) === null || _c === void 0 ? void 0 : _c.code) === SUCCESS &&
((_d = livenessResponse.data) === null || _d === void 0 ? void 0 : _d.verify_result)) {
setFaceImages([...faceImages, imageUri]);
if (faceLivenessConfiguration !== undefined) {
setSelectedIndex(selectedIndex + 1);
}
}
}
}
else if (enableCheckSanity) {
const sanityResponse = await checkFaceSanityPost(imageUriBase64);
if (((_e = sanityResponse.data) === null || _e === void 0 ? void 0 : _e.code) !== SUCCESS) {
message = ((_f = sanityResponse.data) === null || _f === void 0 ? void 0 : _f.message) || '';
}
else {
message = '';
setFaceImages([...faceImages, imageUri]);
if (faceLivenessConfiguration !== undefined) {
setSelectedIndex(selectedIndex + 1);
}
}
}
else if (enableCheckTempering) {
const livenessResponse = await checkLivenessPost(imageUriBase64);
if (((_g = livenessResponse.data) === null || _g === void 0 ? void 0 : _g.code) === SUCCESS &&
((_h = livenessResponse.data) === null || _h === void 0 ? void 0 : _h.verify_result)) {
message = '';
setFaceImages([...faceImages, imageUri]);
if (faceLivenessConfiguration !== undefined) {
setSelectedIndex(selectedIndex + 1);
}
}
else {
message = 'Không phải ảnh selfie hoặc không có khuôn mặt';
}
}
else {
setFaceImages([...faceImages, imageUri]);
if (faceLivenessConfiguration !== undefined) {
setSelectedIndex(selectedIndex + 1);
}
}
if (!faceLivenessConfiguration && message !== '') {
afterTakePictureAction({
images: faceImages,
errorMessage: message,
});
}
};
/**
* When detected face, if match with require and config => add to faceImages
* @param faceArray : Array
* @returns
*/
const onFacesDetected = async (faceArray) => {
var _a;
if (faceArray.length > 0) {
const face = faceArray[0];
if (faceLivenessConfiguration &&
face.bottomMouthPosition &&
(face.rightEyePosition || face.leftEyePosition)) {
let yawAngle = requireFaceActionListConstant.portrait;
let pitchAngle = requireFaceActionListConstant.portrait;
let status;
if (face.yawAngle !== undefined) {
if (face.yawAngle >= faceLivenessConfiguration.yawAngle) {
yawAngle = requireFaceActionListConstant.right;
}
else if (face.yawAngle <= -faceLivenessConfiguration.yawAngle) {
yawAngle = requireFaceActionListConstant.left;
}
}
if (face.inclinationAngle !== undefined) {
if (face.inclinationAngle >= faceLivenessConfiguration.pitchAngle) {
pitchAngle = requireFaceActionListConstant.down;
}
else if (face.inclinationAngle <= -faceLivenessConfiguration.pitchAngle) {
pitchAngle = requireFaceActionListConstant.up;
}
}
if (face.smilingProbability !== undefined &&
face.smilingProbability >=
faceLivenessConfiguration.smilingProbability) {
status = requireFaceActionListConstant.smile;
}
else if (pitchAngle === requireFaceActionListConstant.portrait) {
status = yawAngle;
}
else if (yawAngle === requireFaceActionListConstant.portrait) {
status = pitchAngle;
}
console.log('status: ', status);
if (status !== undefined &&
((_a = requireFrameList[selectedIndex]) === null || _a === void 0 ? void 0 : _a.action) === status) {
const picture = await takePicture();
if (picture === null || picture === void 0 ? void 0 : picture.uri) {
await processImage(picture === null || picture === void 0 ? void 0 : picture.uri);
}
}
}
}
};
const onCapture = async () => {
setIsLoading(true);
const picture = await takePicture();
await processImage(picture === null || picture === void 0 ? void 0 : picture.uri);
setIsLoading(false);
};
return (React.createElement(View, { style: styles.container },
React.createElement(BackgroundFrontCamera, null),
React.createElement(View, { style: styles.cameraPreview },
React.createElement(RNCamera, { ref: cameraRef, autoFocusPointOfInterest: autoFocusPoint.normalized, type: type, ratio: ratio, style: styles.cameraMain, captureAudio: false, autoFocus: autoFocus === 1 || autoFocus === 'on' ? 'on' : 'off', faceDetectionMode: RNCamera.Constants.FaceDetection.Mode.fast, faceDetectionClassifications: RNCamera.Constants.FaceDetection.Classifications.all, faceDetectionLandmarks: RNCamera.Constants.FaceDetection.Landmarks.all, onFacesDetected: async (faceArray) => {
if (enableFD) {
enableFD = false;
await onFacesDetected(faceArray.faces);
if (selectedIndex < requireFrameList.length) {
enableFD = true;
}
}
} })),
React.createElement(View, { style: stylesCamera.mainContainer },
React.createElement(View, { style: stylesCamera.warningAndActionContainer },
warningFrame || React.createElement(WarningFaceFrame, null),
faceLivenessConfiguration ? (React.createElement(RequireFrame, { requireList: requireFrameList, selectedIndex: selectedIndex })) : (React.createElement(ActionFaceFrame, { capture: async () => {
await onCapture();
} })))),
React.createElement(TouchableOpacity, { testID: 'button', onPress: toggleFacing, style: styles.toggleFacing },
React.createElement(Image, { source: toggleCameraImage, style: styles.toggleCameraImage, resizeMode: 'contain' })),
isLoading ? (loadingComponent !== undefined ? (loadingComponent()) : (React.createElement(Loadding, null))) : (React.createElement(View, null))));
};
const styles = StyleSheet.create({
container: {
flex: 1,
position: 'relative',
},
cameraPreview: {
height: circleCameraPosition.r * 2,
width: circleCameraPosition.r * 2,
position: 'absolute',
top: circleCameraPosition.y,
left: circleCameraPosition.x,
right: circleCameraPosition.x,
overflow: 'hidden',
borderRadius: circleCameraPosition.r,
zIndex: 10,
},
cameraMain: {
height: circleCameraPosition.r * 2,
width: circleCameraPosition.r * 2,
},
toggleFacing: {
width: 50,
height: 50,
position: 'absolute',
top: 30,
right: 20,
zIndex: 10,
},
toggleCameraImage: { width: 50, height: 50 },
});
//# sourceMappingURL=FaceComponent.js.map