react-native-timacare 2.0.2 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commonjs/assets/img/blink.png +0 -0
- package/lib/commonjs/assets/img/down.png +0 -0
- package/lib/commonjs/assets/img/left.png +0 -0
- package/lib/commonjs/assets/img/mouth.png +0 -0
- package/lib/commonjs/assets/img/right.png +0 -0
- package/lib/commonjs/assets/img/up.png +0 -0
- package/lib/commonjs/navigation/primary-navigator.js +1 -1
- package/lib/commonjs/navigation/primary-navigator.js.flow +1 -1
- package/lib/commonjs/navigation/primary-navigator.js.map +1 -1
- package/lib/commonjs/screens/full-submit/selfie.js +1 -1
- package/lib/commonjs/screens/full-submit/selfie.js.flow +2 -1
- package/lib/commonjs/screens/full-submit/selfie.js.map +1 -1
- package/lib/commonjs/screens/home/index.js +1 -1
- package/lib/commonjs/screens/home/index.js.flow +1 -0
- package/lib/commonjs/screens/home/index.js.map +1 -1
- package/lib/commonjs/screens/liveness/LivenessStore.js +1 -1
- package/lib/commonjs/screens/liveness/LivenessStore.js.flow +146 -0
- package/lib/commonjs/screens/liveness/LivenessStore.js.map +1 -1
- package/lib/commonjs/screens/liveness/index.js +1 -1
- package/lib/commonjs/screens/liveness/index.js.flow +4 -0
- package/lib/commonjs/screens/liveness/index.js.map +1 -1
- package/lib/commonjs/screens/liveness-v2/index.js +2 -0
- package/lib/commonjs/screens/liveness-v2/index.js.flow +598 -0
- package/lib/commonjs/screens/liveness-v2/index.js.map +1 -0
- package/lib/commonjs/screens/mrz-scanner/index.js +1 -1
- package/lib/commonjs/screens/mrz-scanner/index.js.flow +1 -0
- package/lib/commonjs/screens/mrz-scanner/index.js.map +1 -1
- package/lib/commonjs/screens/selfie/index.js +1 -1
- package/lib/commonjs/screens/selfie/index.js.flow +12 -2
- package/lib/commonjs/screens/selfie/index.js.map +1 -1
- package/lib/commonjs/services/api/api.js +1 -1
- package/lib/commonjs/services/api/api.js.flow +68 -0
- package/lib/commonjs/services/api/api.js.map +1 -1
- package/lib/module/assets/img/blink.png +0 -0
- package/lib/module/assets/img/down.png +0 -0
- package/lib/module/assets/img/left.png +0 -0
- package/lib/module/assets/img/mouth.png +0 -0
- package/lib/module/assets/img/right.png +0 -0
- package/lib/module/assets/img/up.png +0 -0
- package/lib/module/navigation/primary-navigator.js +1 -1
- package/lib/module/navigation/primary-navigator.js.map +1 -1
- package/lib/module/screens/full-submit/selfie.js +1 -1
- package/lib/module/screens/full-submit/selfie.js.map +1 -1
- package/lib/module/screens/home/index.js +1 -1
- package/lib/module/screens/home/index.js.map +1 -1
- package/lib/module/screens/liveness/LivenessStore.js +1 -1
- package/lib/module/screens/liveness/LivenessStore.js.map +1 -1
- package/lib/module/screens/liveness/index.js +1 -1
- package/lib/module/screens/liveness/index.js.map +1 -1
- package/lib/module/screens/liveness-v2/index.js +2 -0
- package/lib/module/screens/liveness-v2/index.js.map +1 -0
- package/lib/module/screens/mrz-scanner/index.js +1 -1
- package/lib/module/screens/mrz-scanner/index.js.map +1 -1
- package/lib/module/screens/selfie/index.js +1 -1
- package/lib/module/screens/selfie/index.js.map +1 -1
- package/lib/module/services/api/api.js +1 -1
- package/lib/module/services/api/api.js.map +1 -1
- package/lib/typescript/screens/full-submit/selfie.d.ts.map +1 -1
- package/lib/typescript/screens/home/index.d.ts.map +1 -1
- package/lib/typescript/screens/liveness/LivenessStore.d.ts +7 -0
- package/lib/typescript/screens/liveness/LivenessStore.d.ts.map +1 -1
- package/lib/typescript/screens/liveness/index.d.ts.map +1 -1
- package/lib/typescript/screens/liveness-v2/index.d.ts +2 -0
- package/lib/typescript/screens/liveness-v2/index.d.ts.map +1 -0
- package/lib/typescript/screens/mrz-scanner/index.d.ts.map +1 -1
- package/lib/typescript/screens/selfie/index.d.ts.map +1 -1
- package/lib/typescript/services/api/api.d.ts +64 -0
- package/lib/typescript/services/api/api.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/assets/img/blink.png +0 -0
- package/src/assets/img/down.png +0 -0
- package/src/assets/img/left.png +0 -0
- package/src/assets/img/mouth.png +0 -0
- package/src/assets/img/right.png +0 -0
- package/src/assets/img/up.png +0 -0
- package/src/navigation/primary-navigator.tsx +1 -1
- package/src/screens/full-submit/selfie.tsx +2 -1
- package/src/screens/home/index.tsx +1 -0
- package/src/screens/liveness/LivenessStore.tsx +146 -0
- package/src/screens/liveness/index.tsx +4 -0
- package/src/screens/liveness-v2/index.tsx +598 -0
- package/src/screens/mrz-scanner/index.tsx +1 -0
- package/src/screens/selfie/index.tsx +12 -2
- package/src/services/api/api.ts +68 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
//@ts-nocheck
|
|
2
|
+
import React, { useRef, useState } from 'react';
|
|
3
|
+
import { CommonActions, useNavigation } from '@react-navigation/native';
|
|
4
|
+
import { Observer, observer } from 'mobx-react-lite';
|
|
5
|
+
import {
|
|
6
|
+
ActivityIndicator,
|
|
7
|
+
Alert,
|
|
8
|
+
Dimensions,
|
|
9
|
+
Image,
|
|
10
|
+
Modal,
|
|
11
|
+
Platform,
|
|
12
|
+
StyleSheet,
|
|
13
|
+
TouchableOpacity,
|
|
14
|
+
View,
|
|
15
|
+
} from 'react-native';
|
|
16
|
+
import CircularProgress from 'react-native-circular-progress-indicator';
|
|
17
|
+
import RNFS, { CachesDirectoryPath } from 'react-native-fs';
|
|
18
|
+
import { RNFFmpeg } from 'react-native-ffmpeg';
|
|
19
|
+
import {
|
|
20
|
+
PERMISSIONS,
|
|
21
|
+
RESULTS,
|
|
22
|
+
openSettings,
|
|
23
|
+
request,
|
|
24
|
+
} from 'react-native-permissions';
|
|
25
|
+
import DeviceInfo from 'react-native-device-info';
|
|
26
|
+
import { IconBackWhite, IconEkyc } from '../../assets/icons';
|
|
27
|
+
import { MText } from '../../components/MText';
|
|
28
|
+
import { RNCamera } from 'react-native-camera';
|
|
29
|
+
import livenessStore from '../liveness/LivenessStore';
|
|
30
|
+
import { commonStyles } from '../CommonStyles';
|
|
31
|
+
import { ScreenNames } from '../../navigation';
|
|
32
|
+
import { color } from '../../theme';
|
|
33
|
+
|
|
34
|
+
const { width: windowWidth } = Dimensions.get('window');
|
|
35
|
+
|
|
36
|
+
const PREVIEW_SIZE = 320;
|
|
37
|
+
const PREVIEW_RECT = {
|
|
38
|
+
minX: (windowWidth - PREVIEW_SIZE) / 2,
|
|
39
|
+
minY: 240,
|
|
40
|
+
width: PREVIEW_SIZE,
|
|
41
|
+
height: PREVIEW_SIZE,
|
|
42
|
+
};
|
|
43
|
+
export const LivenessV2 = observer(function LivenessV2(props: any) {
|
|
44
|
+
const loan = props.route.params?.loan;
|
|
45
|
+
const navigation = useNavigation();
|
|
46
|
+
const cameraRef = useRef<any>(null);
|
|
47
|
+
const interval = useRef<any>(null);
|
|
48
|
+
const [passPermission, setPassPermission] = useState(false);
|
|
49
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
50
|
+
|
|
51
|
+
const renderHint = (key: string) => {
|
|
52
|
+
switch (key) {
|
|
53
|
+
case 'blink':
|
|
54
|
+
return 'Nhắm mắt trong 3 giây';
|
|
55
|
+
case 'right':
|
|
56
|
+
return 'Quay sang phải 3 giây';
|
|
57
|
+
case 'left':
|
|
58
|
+
return 'Quay sang trái 3 giây';
|
|
59
|
+
case 'up':
|
|
60
|
+
return 'Nhìn lên trên 3 giây';
|
|
61
|
+
case 'down':
|
|
62
|
+
return 'Nhìn xuống dưới 3 giây';
|
|
63
|
+
case 'mouth':
|
|
64
|
+
return 'Há miệng trong 3 giây';
|
|
65
|
+
default:
|
|
66
|
+
return '';
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
const renderImg = (key: string) => {
|
|
70
|
+
switch (key) {
|
|
71
|
+
case 'blink':
|
|
72
|
+
return (
|
|
73
|
+
<Image
|
|
74
|
+
source={require('../../assets/img/blink.png')}
|
|
75
|
+
style={{
|
|
76
|
+
height: 24,
|
|
77
|
+
width: 50,
|
|
78
|
+
resizeMode: 'contain',
|
|
79
|
+
marginTop: 40,
|
|
80
|
+
}}
|
|
81
|
+
/>
|
|
82
|
+
);
|
|
83
|
+
case 'right':
|
|
84
|
+
return (
|
|
85
|
+
<Image
|
|
86
|
+
source={require('../../assets/img/right.png')}
|
|
87
|
+
style={{
|
|
88
|
+
height: 24,
|
|
89
|
+
width: 50,
|
|
90
|
+
resizeMode: 'contain',
|
|
91
|
+
marginTop: 40,
|
|
92
|
+
}}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
case 'left':
|
|
96
|
+
return (
|
|
97
|
+
<Image
|
|
98
|
+
source={require('../../assets/img/left.png')}
|
|
99
|
+
style={{
|
|
100
|
+
height: 24,
|
|
101
|
+
width: 50,
|
|
102
|
+
resizeMode: 'contain',
|
|
103
|
+
marginTop: 40,
|
|
104
|
+
}}
|
|
105
|
+
/>
|
|
106
|
+
);
|
|
107
|
+
case 'up':
|
|
108
|
+
return (
|
|
109
|
+
<Image
|
|
110
|
+
source={require('../../assets/img/up.png')}
|
|
111
|
+
style={{
|
|
112
|
+
height: 24,
|
|
113
|
+
width: 50,
|
|
114
|
+
resizeMode: 'contain',
|
|
115
|
+
marginTop: 40,
|
|
116
|
+
}}
|
|
117
|
+
/>
|
|
118
|
+
);
|
|
119
|
+
case 'down':
|
|
120
|
+
return (
|
|
121
|
+
<Image
|
|
122
|
+
source={require('../../assets/img/down.png')}
|
|
123
|
+
style={{
|
|
124
|
+
height: 24,
|
|
125
|
+
width: 50,
|
|
126
|
+
resizeMode: 'contain',
|
|
127
|
+
marginTop: 40,
|
|
128
|
+
}}
|
|
129
|
+
/>
|
|
130
|
+
);
|
|
131
|
+
case 'mouth':
|
|
132
|
+
return (
|
|
133
|
+
<Image
|
|
134
|
+
source={require('../../assets/img/mouth.png')}
|
|
135
|
+
style={{
|
|
136
|
+
height: 24,
|
|
137
|
+
width: 50,
|
|
138
|
+
resizeMode: 'contain',
|
|
139
|
+
marginTop: 40,
|
|
140
|
+
}}
|
|
141
|
+
/>
|
|
142
|
+
);
|
|
143
|
+
default:
|
|
144
|
+
return <></>;
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const requestPermissions = () => {
|
|
149
|
+
request(
|
|
150
|
+
Platform.OS === 'ios'
|
|
151
|
+
? PERMISSIONS.IOS.CAMERA
|
|
152
|
+
: PERMISSIONS.ANDROID.CAMERA
|
|
153
|
+
).then((result) => {
|
|
154
|
+
switch (result) {
|
|
155
|
+
case RESULTS.UNAVAILABLE:
|
|
156
|
+
console.log(
|
|
157
|
+
'This feature is not available (on this device / in this context)'
|
|
158
|
+
);
|
|
159
|
+
Alert.alert(
|
|
160
|
+
'Thông báo',
|
|
161
|
+
'Máy ảnh trên thiết bị không tương thích.\nVui lòng kiểm tra lại thiết bị!',
|
|
162
|
+
[{ text: 'Đồng ý' }]
|
|
163
|
+
);
|
|
164
|
+
break;
|
|
165
|
+
case RESULTS.DENIED:
|
|
166
|
+
console.log(
|
|
167
|
+
'The permission has not been requested / is denied but requestable'
|
|
168
|
+
);
|
|
169
|
+
Alert.alert(
|
|
170
|
+
'Thông báo',
|
|
171
|
+
'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
|
|
172
|
+
[
|
|
173
|
+
{
|
|
174
|
+
text: 'Đồng ý',
|
|
175
|
+
onPress: () => {
|
|
176
|
+
if (Platform.OS === 'ios') {
|
|
177
|
+
openSettings();
|
|
178
|
+
} else {
|
|
179
|
+
requestPermissions();
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
]
|
|
184
|
+
);
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case RESULTS.GRANTED:
|
|
188
|
+
console.log('The permission is granted');
|
|
189
|
+
setPassPermission(true);
|
|
190
|
+
break;
|
|
191
|
+
case RESULTS.BLOCKED:
|
|
192
|
+
console.log('The permission is denied and not requestable anymore');
|
|
193
|
+
Alert.alert(
|
|
194
|
+
'Thông báo',
|
|
195
|
+
'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
|
|
196
|
+
[
|
|
197
|
+
{
|
|
198
|
+
text: 'Đồng ý',
|
|
199
|
+
onPress: () => {
|
|
200
|
+
if (Platform.OS === 'ios') {
|
|
201
|
+
openSettings();
|
|
202
|
+
} else {
|
|
203
|
+
openSettings();
|
|
204
|
+
}
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
]
|
|
208
|
+
);
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
let second = 3;
|
|
215
|
+
|
|
216
|
+
const start = () => {
|
|
217
|
+
requestPermissions();
|
|
218
|
+
livenessStore.getListAction(() => {
|
|
219
|
+
if (passPermission) {
|
|
220
|
+
interval.current = setInterval(() => {
|
|
221
|
+
second--;
|
|
222
|
+
if (second === 0) {
|
|
223
|
+
clearInterval(interval.current);
|
|
224
|
+
startLiveness();
|
|
225
|
+
}
|
|
226
|
+
}, 1000);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
useEffect(() => {
|
|
232
|
+
start();
|
|
233
|
+
}, [passPermission]);
|
|
234
|
+
|
|
235
|
+
const uploadLiveness = async (template: any) => {
|
|
236
|
+
try {
|
|
237
|
+
const listImage = [];
|
|
238
|
+
for (let i = 1; i <= 3; i++) {
|
|
239
|
+
const file = 'file://' + template + `_img_${('000' + i).slice(-4)}.jpg`;
|
|
240
|
+
const imgBase64 = await RNFS.readFile(file, 'base64');
|
|
241
|
+
listImage.push(imgBase64);
|
|
242
|
+
}
|
|
243
|
+
await livenessStore.verifyLiveness(
|
|
244
|
+
loan?.id,
|
|
245
|
+
listImage,
|
|
246
|
+
() => {
|
|
247
|
+
setTimeout(() => {
|
|
248
|
+
startLiveness();
|
|
249
|
+
}, 2000);
|
|
250
|
+
setIsLoading(false);
|
|
251
|
+
},
|
|
252
|
+
() => {
|
|
253
|
+
setTimeout(() => {
|
|
254
|
+
startLiveness();
|
|
255
|
+
}, 2000);
|
|
256
|
+
setIsLoading(false);
|
|
257
|
+
}
|
|
258
|
+
);
|
|
259
|
+
} catch (error) {
|
|
260
|
+
setIsLoading(false);
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
const captureFromVideo = async (uri: any) => {
|
|
265
|
+
try {
|
|
266
|
+
const time = new Date().getTime();
|
|
267
|
+
const template = CachesDirectoryPath + '/' + time;
|
|
268
|
+
const output = template + '_img_%04d.jpg';
|
|
269
|
+
RNFFmpeg.execute(`-i ${uri} -r 2 ${output}`)
|
|
270
|
+
.then((result) => {
|
|
271
|
+
console.log(`FFmpeg process exited with rc=${result}.`);
|
|
272
|
+
if (result === 0) {
|
|
273
|
+
uploadLiveness(template);
|
|
274
|
+
console.log('FFmpeg template', template);
|
|
275
|
+
} else {
|
|
276
|
+
setIsLoading(false);
|
|
277
|
+
Alert.alert(
|
|
278
|
+
'Thông báo',
|
|
279
|
+
'Quá trình xử lý hình ảnh bị lỗi. Vui lòng thử lại.',
|
|
280
|
+
[
|
|
281
|
+
{
|
|
282
|
+
text: 'Đồng ý',
|
|
283
|
+
onPress: () => {
|
|
284
|
+
startLiveness;
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
]
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
})
|
|
291
|
+
.catch((err) => {
|
|
292
|
+
console.log(err);
|
|
293
|
+
});
|
|
294
|
+
} catch (error) {
|
|
295
|
+
setIsLoading(false);
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const startLiveness = async () => {
|
|
300
|
+
livenessStore.ekycSuccess = false;
|
|
301
|
+
if (livenessStore.listAction.length === 0) {
|
|
302
|
+
livenessStore.ekycSuccess = true;
|
|
303
|
+
livenessStore.saveLiveness(loan.id);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (cameraRef) {
|
|
307
|
+
const videoFile =
|
|
308
|
+
CachesDirectoryPath + '/' + new Date().getTime() + '.mp4';
|
|
309
|
+
const options = {
|
|
310
|
+
quality: RNCamera.Constants.VideoQuality['720p'],
|
|
311
|
+
maxDuration: 3,
|
|
312
|
+
path: videoFile,
|
|
313
|
+
/** iOS only */
|
|
314
|
+
codec: 'H264',
|
|
315
|
+
fps: 30,
|
|
316
|
+
};
|
|
317
|
+
const data = await cameraRef.current.recordAsync(options);
|
|
318
|
+
if (data.uri) {
|
|
319
|
+
setIsLoading(true);
|
|
320
|
+
captureFromVideo(data.uri);
|
|
321
|
+
} else {
|
|
322
|
+
Alert.alert('Thông báo', 'Quá trình xác thực thất bại', [
|
|
323
|
+
{
|
|
324
|
+
text: 'Thử lại',
|
|
325
|
+
onPress: () => {
|
|
326
|
+
startLiveness();
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
return (
|
|
334
|
+
<View style={styles.container}>
|
|
335
|
+
<TouchableOpacity
|
|
336
|
+
onPress={() => {
|
|
337
|
+
navigation.goBack();
|
|
338
|
+
}}
|
|
339
|
+
style={{
|
|
340
|
+
position: 'absolute',
|
|
341
|
+
left: 16,
|
|
342
|
+
top: DeviceInfo.hasNotch() ? 70 : 16,
|
|
343
|
+
width: 40,
|
|
344
|
+
height: 40,
|
|
345
|
+
zIndex: 1000,
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
<IconBackWhite />
|
|
349
|
+
</TouchableOpacity>
|
|
350
|
+
<View style={styles.topOverlay}>
|
|
351
|
+
<MText
|
|
352
|
+
style={{
|
|
353
|
+
color: 'white',
|
|
354
|
+
fontWeight: '600',
|
|
355
|
+
fontSize: 16,
|
|
356
|
+
}}
|
|
357
|
+
>
|
|
358
|
+
Xác thực khuôn mặt
|
|
359
|
+
</MText>
|
|
360
|
+
<Observer>
|
|
361
|
+
{() => (
|
|
362
|
+
<>
|
|
363
|
+
{isLoading ? (
|
|
364
|
+
<View style={{ marginTop: 24 }}>
|
|
365
|
+
<MText
|
|
366
|
+
style={{
|
|
367
|
+
textAlign: 'center',
|
|
368
|
+
fontSize: 16,
|
|
369
|
+
color: 'white',
|
|
370
|
+
}}
|
|
371
|
+
>
|
|
372
|
+
Đang xác thực
|
|
373
|
+
</MText>
|
|
374
|
+
<ActivityIndicator
|
|
375
|
+
size={'large'}
|
|
376
|
+
color={'white'}
|
|
377
|
+
style={{ marginTop: 8 }}
|
|
378
|
+
/>
|
|
379
|
+
</View>
|
|
380
|
+
) : (
|
|
381
|
+
<></>
|
|
382
|
+
)}
|
|
383
|
+
</>
|
|
384
|
+
)}
|
|
385
|
+
</Observer>
|
|
386
|
+
</View>
|
|
387
|
+
|
|
388
|
+
<View style={styles.preview}>
|
|
389
|
+
<RNCamera
|
|
390
|
+
playSoundOnCapture={false}
|
|
391
|
+
ref={cameraRef}
|
|
392
|
+
style={StyleSheet.absoluteFill}
|
|
393
|
+
captureAudio={false}
|
|
394
|
+
type={RNCamera.Constants.Type.front}
|
|
395
|
+
androidCameraPermissionOptions={{
|
|
396
|
+
title: 'Permission to use camera',
|
|
397
|
+
message: 'We need your permission to use your camera',
|
|
398
|
+
buttonPositive: 'Ok',
|
|
399
|
+
buttonNegative: 'Cancel',
|
|
400
|
+
}}
|
|
401
|
+
androidRecordAudioPermissionOptions={{
|
|
402
|
+
title: 'Permission to use audio recording',
|
|
403
|
+
message: 'We need your permission to use your audio',
|
|
404
|
+
buttonPositive: 'Ok',
|
|
405
|
+
buttonNegative: 'Cancel',
|
|
406
|
+
}}
|
|
407
|
+
autoFocus="on"
|
|
408
|
+
>
|
|
409
|
+
<View
|
|
410
|
+
style={{
|
|
411
|
+
height: '100%',
|
|
412
|
+
width: '100%',
|
|
413
|
+
justifyContent: 'center',
|
|
414
|
+
alignItems: 'center',
|
|
415
|
+
}}
|
|
416
|
+
>
|
|
417
|
+
{renderImg(livenessStore.hintAction)}
|
|
418
|
+
<MText
|
|
419
|
+
style={{
|
|
420
|
+
color: 'white',
|
|
421
|
+
fontSize: 16,
|
|
422
|
+
marginTop: 8,
|
|
423
|
+
fontWeight: '400',
|
|
424
|
+
}}
|
|
425
|
+
>
|
|
426
|
+
{renderHint(livenessStore.hintAction)}
|
|
427
|
+
</MText>
|
|
428
|
+
</View>
|
|
429
|
+
</RNCamera>
|
|
430
|
+
</View>
|
|
431
|
+
|
|
432
|
+
<View style={styles.circularProgress}>
|
|
433
|
+
<CircularProgress
|
|
434
|
+
dashedStrokeConfig={{
|
|
435
|
+
count: 100,
|
|
436
|
+
width: 7,
|
|
437
|
+
}}
|
|
438
|
+
value={
|
|
439
|
+
livenessStore.listAction.length === 3
|
|
440
|
+
? 0
|
|
441
|
+
: livenessStore.listAction.length === 2
|
|
442
|
+
? 100 / 3
|
|
443
|
+
: livenessStore.listAction.length === 1
|
|
444
|
+
? (2 / 3) * 100
|
|
445
|
+
: 100
|
|
446
|
+
}
|
|
447
|
+
radius={PREVIEW_SIZE / 2}
|
|
448
|
+
inActiveStrokeOpacity={0.5}
|
|
449
|
+
activeStrokeWidth={13}
|
|
450
|
+
inActiveStrokeWidth={13}
|
|
451
|
+
showProgressValue={false}
|
|
452
|
+
activeStrokeColor="#F05123"
|
|
453
|
+
inActiveStrokeColor="#F05123"
|
|
454
|
+
duration={800}
|
|
455
|
+
/>
|
|
456
|
+
</View>
|
|
457
|
+
<Observer>
|
|
458
|
+
{() => (
|
|
459
|
+
<View style={styles.bottomOverlay}>
|
|
460
|
+
<MText style={styles.instructionText}>
|
|
461
|
+
Vui lòng điều chỉnh khuôn mặt nằm gọn trong vòng tròn ở trên
|
|
462
|
+
</MText>
|
|
463
|
+
</View>
|
|
464
|
+
)}
|
|
465
|
+
</Observer>
|
|
466
|
+
|
|
467
|
+
<Modal
|
|
468
|
+
visible={livenessStore.ekycSuccess}
|
|
469
|
+
animationType={'fade'}
|
|
470
|
+
transparent
|
|
471
|
+
>
|
|
472
|
+
<View
|
|
473
|
+
style={[
|
|
474
|
+
commonStyles.alignCenter,
|
|
475
|
+
commonStyles.justifyCenter,
|
|
476
|
+
{ backgroundColor: 'transparent', height: '100%' },
|
|
477
|
+
]}
|
|
478
|
+
>
|
|
479
|
+
<View
|
|
480
|
+
style={[
|
|
481
|
+
commonStyles.alignCenter,
|
|
482
|
+
commonStyles.justifyCenter,
|
|
483
|
+
{
|
|
484
|
+
backgroundColor: 'white',
|
|
485
|
+
borderRadius: 8,
|
|
486
|
+
padding: 16,
|
|
487
|
+
marginHorizontal: 16,
|
|
488
|
+
},
|
|
489
|
+
]}
|
|
490
|
+
>
|
|
491
|
+
<IconEkyc />
|
|
492
|
+
<MText
|
|
493
|
+
style={[
|
|
494
|
+
commonStyles.textNormalBold,
|
|
495
|
+
{ marginTop: 16, textAlign: 'center' },
|
|
496
|
+
]}
|
|
497
|
+
>
|
|
498
|
+
Chúc mừng bạn đã thực hiện xác thực khách hàng{'\n'}thành công.
|
|
499
|
+
</MText>
|
|
500
|
+
<TouchableOpacity
|
|
501
|
+
style={{
|
|
502
|
+
marginTop: 16,
|
|
503
|
+
backgroundColor: color.primary,
|
|
504
|
+
height: 40,
|
|
505
|
+
alignItems: 'center',
|
|
506
|
+
borderRadius: 30,
|
|
507
|
+
width: 150,
|
|
508
|
+
justifyContent: 'center',
|
|
509
|
+
}}
|
|
510
|
+
onPress={() => {
|
|
511
|
+
livenessStore.ekycSuccess = false;
|
|
512
|
+
navigation.dispatch(
|
|
513
|
+
CommonActions.reset({
|
|
514
|
+
index: 1,
|
|
515
|
+
routes: [{ name: ScreenNames.Main }],
|
|
516
|
+
})
|
|
517
|
+
);
|
|
518
|
+
}}
|
|
519
|
+
>
|
|
520
|
+
<MText style={[commonStyles.textNormalBold, { color: 'white' }]}>
|
|
521
|
+
Đồng ý
|
|
522
|
+
</MText>
|
|
523
|
+
</TouchableOpacity>
|
|
524
|
+
</View>
|
|
525
|
+
</View>
|
|
526
|
+
</Modal>
|
|
527
|
+
</View>
|
|
528
|
+
);
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
const styles = StyleSheet.create({
|
|
532
|
+
instructionText: {
|
|
533
|
+
color: 'white',
|
|
534
|
+
fontSize: 12,
|
|
535
|
+
textAlign: 'center',
|
|
536
|
+
paddingHorizontal: 50,
|
|
537
|
+
marginTop: 16,
|
|
538
|
+
},
|
|
539
|
+
bottomOverlay: {
|
|
540
|
+
marginTop: 24,
|
|
541
|
+
},
|
|
542
|
+
maskedView: {
|
|
543
|
+
flex: 1,
|
|
544
|
+
flexDirection: 'row',
|
|
545
|
+
height: '100%',
|
|
546
|
+
},
|
|
547
|
+
maskWrapper: {
|
|
548
|
+
backgroundColor: 'transparent',
|
|
549
|
+
flex: 1,
|
|
550
|
+
justifyContent: 'center',
|
|
551
|
+
alignItems: 'center',
|
|
552
|
+
},
|
|
553
|
+
mask: {
|
|
554
|
+
borderRadius: PREVIEW_SIZE / 2,
|
|
555
|
+
height: PREVIEW_SIZE - 10,
|
|
556
|
+
width: PREVIEW_SIZE - 10,
|
|
557
|
+
marginTop: PREVIEW_RECT.minY,
|
|
558
|
+
alignSelf: 'center',
|
|
559
|
+
},
|
|
560
|
+
container: {
|
|
561
|
+
flex: 1,
|
|
562
|
+
flexDirection: 'column',
|
|
563
|
+
backgroundColor: '#333333',
|
|
564
|
+
},
|
|
565
|
+
topOverlay: {
|
|
566
|
+
position: 'absolute',
|
|
567
|
+
top: DeviceInfo.hasNotch() ? 70 : 16,
|
|
568
|
+
right: 0,
|
|
569
|
+
left: 0,
|
|
570
|
+
justifyContent: 'center',
|
|
571
|
+
alignItems: 'center',
|
|
572
|
+
},
|
|
573
|
+
captureFrame: {
|
|
574
|
+
height: Dimensions.get('window').width - 50,
|
|
575
|
+
marginHorizontal: 16,
|
|
576
|
+
marginTop: '50%',
|
|
577
|
+
borderRadius: 50,
|
|
578
|
+
},
|
|
579
|
+
preview: {
|
|
580
|
+
width: PREVIEW_SIZE - 29,
|
|
581
|
+
height: PREVIEW_SIZE - 28,
|
|
582
|
+
marginTop: PREVIEW_RECT.minY + 14,
|
|
583
|
+
marginLeft: PREVIEW_RECT.minX + 14,
|
|
584
|
+
borderRadius: PREVIEW_SIZE / 2,
|
|
585
|
+
overflow: 'hidden',
|
|
586
|
+
},
|
|
587
|
+
circularProgress: {
|
|
588
|
+
width: PREVIEW_SIZE,
|
|
589
|
+
height: PREVIEW_SIZE,
|
|
590
|
+
marginTop: PREVIEW_RECT.minY,
|
|
591
|
+
marginLeft: PREVIEW_RECT.minX,
|
|
592
|
+
zIndex: 100,
|
|
593
|
+
position: 'absolute',
|
|
594
|
+
top: 0,
|
|
595
|
+
left: 0,
|
|
596
|
+
right: 0,
|
|
597
|
+
},
|
|
598
|
+
});
|
|
@@ -26,6 +26,7 @@ import MButton from '../../components/MButton';
|
|
|
26
26
|
import { IconBackWhite } from '../../assets/icons';
|
|
27
27
|
import { commonStyles } from '../CommonStyles';
|
|
28
28
|
import nfcStore from './store';
|
|
29
|
+
import { ScreenNames } from '../../navigation';
|
|
29
30
|
|
|
30
31
|
const { NFCReader } = NativeModules;
|
|
31
32
|
|
|
@@ -33,6 +33,7 @@ import selfieStore from './SelfieStore';
|
|
|
33
33
|
import nationalIDStore from '../nationalID/Store';
|
|
34
34
|
import Loading from '../../components/Loading';
|
|
35
35
|
import LinearGradient from 'react-native-linear-gradient';
|
|
36
|
+
import livenessStore from '../liveness/LivenessStore';
|
|
36
37
|
|
|
37
38
|
const logoView: ImageStyle = {
|
|
38
39
|
position: 'absolute',
|
|
@@ -72,8 +73,17 @@ export const Selfie = observer(function Selfie(props: any) {
|
|
|
72
73
|
|
|
73
74
|
selfieStore.checkSelfieImage(formSelfie, loan.id, () => {
|
|
74
75
|
selfieStore.uploadImage(form, () => {
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
livenessStore.getConfigLiveness((data) => {
|
|
77
|
+
if (data === 1) {
|
|
78
|
+
navigation.dispatch(
|
|
79
|
+
StackActions.push(ScreenNames.LiveNess, { loan })
|
|
80
|
+
);
|
|
81
|
+
} else {
|
|
82
|
+
navigation.dispatch(
|
|
83
|
+
StackActions.push(ScreenNames.LivenessV2, { loan })
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
77
87
|
});
|
|
78
88
|
});
|
|
79
89
|
};
|
package/src/services/api/api.ts
CHANGED
|
@@ -2375,4 +2375,72 @@ export class Api {
|
|
|
2375
2375
|
}
|
|
2376
2376
|
}
|
|
2377
2377
|
|
|
2378
|
+
async verifyLiveness(body: any) {
|
|
2379
|
+
// make the api call
|
|
2380
|
+
const response: ApiResponse<any> = await this.apisauce.post(`api/v1.0/timacare/livenessv2`, body)
|
|
2381
|
+
myLog(response)
|
|
2382
|
+
// the typical ways to die when calling an api
|
|
2383
|
+
if (!response.ok) {
|
|
2384
|
+
const problem = getGeneralApiProblem(response)
|
|
2385
|
+
if (problem) return problem
|
|
2386
|
+
}
|
|
2387
|
+
// transform the data into the format we are expecting
|
|
2388
|
+
try {
|
|
2389
|
+
return { kind: "ok", data: response.data }
|
|
2390
|
+
} catch {
|
|
2391
|
+
return { kind: "bad-data" }
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
async saveImages(body: any) {
|
|
2396
|
+
// make the api call
|
|
2397
|
+
const response: ApiResponse<any> = await this.apisauce.post(`api/v1.0/timacare/save_image_liveness`, body)
|
|
2398
|
+
myLog(response)
|
|
2399
|
+
// the typical ways to die when calling an api
|
|
2400
|
+
if (!response.ok) {
|
|
2401
|
+
const problem = getGeneralApiProblem(response)
|
|
2402
|
+
if (problem) return problem
|
|
2403
|
+
}
|
|
2404
|
+
// transform the data into the format we are expecting
|
|
2405
|
+
try {
|
|
2406
|
+
return { kind: "ok", data: response.data }
|
|
2407
|
+
} catch {
|
|
2408
|
+
return { kind: "bad-data" }
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
async getActionLiveness() {
|
|
2413
|
+
// make the api call
|
|
2414
|
+
const response: ApiResponse<any> = await this.apisauce.get(`api/v1.0/timacare/get_action_liveness`)
|
|
2415
|
+
myLog(response)
|
|
2416
|
+
// the typical ways to die when calling an api
|
|
2417
|
+
if (!response.ok) {
|
|
2418
|
+
const problem = getGeneralApiProblem(response)
|
|
2419
|
+
if (problem) return problem
|
|
2420
|
+
}
|
|
2421
|
+
// transform the data into the format we are expecting
|
|
2422
|
+
try {
|
|
2423
|
+
return { kind: "ok", data: response.data }
|
|
2424
|
+
} catch {
|
|
2425
|
+
return { kind: "bad-data" }
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
|
|
2429
|
+
async getConfigLiveness() {
|
|
2430
|
+
// make the api call
|
|
2431
|
+
const response: ApiResponse<any> = await this.apisauce.get(`api/v1.0/timacare/get_config_liveness`)
|
|
2432
|
+
myLog(response)
|
|
2433
|
+
// the typical ways to die when calling an api
|
|
2434
|
+
if (!response.ok) {
|
|
2435
|
+
const problem = getGeneralApiProblem(response)
|
|
2436
|
+
if (problem) return problem
|
|
2437
|
+
}
|
|
2438
|
+
// transform the data into the format we are expecting
|
|
2439
|
+
try {
|
|
2440
|
+
return { kind: "ok", data: response.data }
|
|
2441
|
+
} catch {
|
|
2442
|
+
return { kind: "bad-data" }
|
|
2443
|
+
}
|
|
2444
|
+
}
|
|
2445
|
+
|
|
2378
2446
|
}
|