react-native-timacare 3.3.63 → 3.3.65

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.
Files changed (81) hide show
  1. package/lib/commonjs/components/AlertLoan.js +1 -1
  2. package/lib/commonjs/components/AlertLoan.js.flow +11 -2
  3. package/lib/commonjs/components/AlertLoan.js.map +1 -1
  4. package/lib/commonjs/components/ItemTimaOne.js +1 -1
  5. package/lib/commonjs/components/ItemTimaOne.js.flow +3 -0
  6. package/lib/commonjs/components/ItemTimaOne.js.map +1 -1
  7. package/lib/commonjs/navigation/primary-navigator.js +1 -1
  8. package/lib/commonjs/navigation/primary-navigator.js.flow +9 -0
  9. package/lib/commonjs/navigation/primary-navigator.js.map +1 -1
  10. package/lib/commonjs/screens/full-submit/selfie.js +1 -1
  11. package/lib/commonjs/screens/full-submit/selfie.js.flow +257 -114
  12. package/lib/commonjs/screens/full-submit/selfie.js.map +1 -1
  13. package/lib/commonjs/screens/home/DigitalTopupItem.js +1 -1
  14. package/lib/commonjs/screens/home/DigitalTopupItem.js.flow +2 -11
  15. package/lib/commonjs/screens/home/DigitalTopupItem.js.map +1 -1
  16. package/lib/commonjs/screens/home/ItemMotoBikeLoan.js +1 -1
  17. package/lib/commonjs/screens/home/ItemMotoBikeLoan.js.flow +2 -7
  18. package/lib/commonjs/screens/home/ItemMotoBikeLoan.js.map +1 -1
  19. package/lib/commonjs/screens/home/index.js +1 -1
  20. package/lib/commonjs/screens/home/index.js.flow +1 -1
  21. package/lib/commonjs/screens/home/index.js.map +1 -1
  22. package/lib/commonjs/screens/selfie/SelfieCamera.js +2 -0
  23. package/lib/commonjs/screens/selfie/SelfieCamera.js.flow +302 -0
  24. package/lib/commonjs/screens/selfie/SelfieCamera.js.map +1 -0
  25. package/lib/commonjs/screens/selfie/index.js +1 -1
  26. package/lib/commonjs/screens/selfie/index.js.flow +295 -165
  27. package/lib/commonjs/screens/selfie/index.js.map +1 -1
  28. package/lib/commonjs/screens/v3/ReviewInfoLoanBuyCar.js +2 -0
  29. package/lib/commonjs/screens/v3/ReviewInfoLoanBuyCar.js.flow +521 -0
  30. package/lib/commonjs/screens/v3/ReviewInfoLoanBuyCar.js.map +1 -0
  31. package/lib/commonjs/services/api/api.js +1 -1
  32. package/lib/commonjs/services/api/api.js.flow +41 -7
  33. package/lib/commonjs/services/api/api.js.map +1 -1
  34. package/lib/module/components/AlertLoan.js +1 -1
  35. package/lib/module/components/AlertLoan.js.map +1 -1
  36. package/lib/module/components/ItemTimaOne.js +1 -1
  37. package/lib/module/components/ItemTimaOne.js.map +1 -1
  38. package/lib/module/navigation/primary-navigator.js +1 -1
  39. package/lib/module/navigation/primary-navigator.js.map +1 -1
  40. package/lib/module/screens/full-submit/selfie.js +1 -1
  41. package/lib/module/screens/full-submit/selfie.js.map +1 -1
  42. package/lib/module/screens/home/DigitalTopupItem.js +1 -1
  43. package/lib/module/screens/home/DigitalTopupItem.js.map +1 -1
  44. package/lib/module/screens/home/ItemMotoBikeLoan.js +1 -1
  45. package/lib/module/screens/home/ItemMotoBikeLoan.js.map +1 -1
  46. package/lib/module/screens/home/index.js +1 -1
  47. package/lib/module/screens/home/index.js.map +1 -1
  48. package/lib/module/screens/selfie/SelfieCamera.js +2 -0
  49. package/lib/module/screens/selfie/SelfieCamera.js.map +1 -0
  50. package/lib/module/screens/selfie/index.js +1 -1
  51. package/lib/module/screens/selfie/index.js.map +1 -1
  52. package/lib/module/screens/v3/ReviewInfoLoanBuyCar.js +2 -0
  53. package/lib/module/screens/v3/ReviewInfoLoanBuyCar.js.map +1 -0
  54. package/lib/module/services/api/api.js +1 -1
  55. package/lib/module/services/api/api.js.map +1 -1
  56. package/lib/typescript/components/AlertLoan.d.ts.map +1 -1
  57. package/lib/typescript/components/ItemTimaOne.d.ts.map +1 -1
  58. package/lib/typescript/navigation/primary-navigator.d.ts +2 -0
  59. package/lib/typescript/navigation/primary-navigator.d.ts.map +1 -1
  60. package/lib/typescript/screens/full-submit/selfie.d.ts.map +1 -1
  61. package/lib/typescript/screens/home/DigitalTopupItem.d.ts.map +1 -1
  62. package/lib/typescript/screens/home/ItemMotoBikeLoan.d.ts.map +1 -1
  63. package/lib/typescript/screens/selfie/SelfieCamera.d.ts +2 -0
  64. package/lib/typescript/screens/selfie/SelfieCamera.d.ts.map +1 -0
  65. package/lib/typescript/screens/selfie/index.d.ts.map +1 -1
  66. package/lib/typescript/screens/v3/ReviewInfoLoanBuyCar.d.ts +2 -0
  67. package/lib/typescript/screens/v3/ReviewInfoLoanBuyCar.d.ts.map +1 -0
  68. package/lib/typescript/services/api/api.d.ts +32 -0
  69. package/lib/typescript/services/api/api.d.ts.map +1 -1
  70. package/package.json +1 -1
  71. package/src/components/AlertLoan.tsx +11 -2
  72. package/src/components/ItemTimaOne.tsx +3 -0
  73. package/src/navigation/primary-navigator.tsx +9 -0
  74. package/src/screens/full-submit/selfie.tsx +257 -114
  75. package/src/screens/home/DigitalTopupItem.tsx +2 -11
  76. package/src/screens/home/ItemMotoBikeLoan.tsx +2 -7
  77. package/src/screens/home/index.tsx +1 -1
  78. package/src/screens/selfie/SelfieCamera.tsx +302 -0
  79. package/src/screens/selfie/index.tsx +295 -165
  80. package/src/screens/v3/ReviewInfoLoanBuyCar.tsx +521 -0
  81. package/src/services/api/api.ts +41 -7
@@ -0,0 +1,302 @@
1
+ //@ts-nocheck
2
+ import React, { useEffect, useRef, useState } from 'react';
3
+ import {
4
+ ActivityIndicator,
5
+ Dimensions,
6
+ Platform,
7
+ StyleSheet,
8
+ View,
9
+ } from 'react-native';
10
+ import { useNavigation, useIsFocused } from '@react-navigation/native';
11
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
12
+ import {
13
+ request,
14
+ PERMISSIONS,
15
+ RESULTS,
16
+ openSettings,
17
+ } from 'react-native-permissions';
18
+ import {
19
+ Camera as VisionCamera,
20
+ useCameraDevice,
21
+ } from 'react-native-vision-camera';
22
+ import { Svg, Ellipse, Path } from 'react-native-svg';
23
+ import RNFS from 'react-native-fs';
24
+ import MButton from '../../components/MButton';
25
+ import { MText } from '../../components/MText';
26
+ import { commonStyles } from '../CommonStyles';
27
+ import { AlertGlobal } from '../../components/ModalNotification/AlertGlobal';
28
+ import { IconBack, TakePhotoSvg } from '../../assets/icons';
29
+
30
+ const ORANGE = '#F05123';
31
+ const SCREEN_WIDTH = Dimensions.get('window').width;
32
+ // Khung oval: rộng ~72% màn hình, cao gấp 1.33 lần chiều rộng (tỉ lệ khuôn mặt)
33
+ const OVAL_WIDTH = Math.round(SCREEN_WIDTH * 0.72);
34
+ const OVAL_HEIGHT = Math.round(OVAL_WIDTH * 1.33);
35
+
36
+ export default function SelfieCamera(props: any) {
37
+ const insets = useSafeAreaInsets();
38
+ const navigation = useNavigation();
39
+ const isFocused = useIsFocused();
40
+ const myCamera = useRef(null);
41
+ const takingRef = useRef(false);
42
+ const [passPermission, setPassPermission] = useState(false);
43
+ const [shouldRenderCamera, setShouldRenderCamera] = useState(false);
44
+ const [taking, setTaking] = useState(false);
45
+ // Kích thước vùng preview để vẽ mặt nạ oval đúng tâm
46
+ const [stage, setStage] = useState({ width: SCREEN_WIDTH, height: 0 });
47
+
48
+ const device = useCameraDevice('front');
49
+
50
+ const takePhoto = async () => {
51
+ if (myCamera.current == null || takingRef.current) return;
52
+ takingRef.current = true;
53
+ setTaking(true);
54
+ try {
55
+ const data = await myCamera.current.takePhoto({
56
+ qualityPrioritization: 'quality',
57
+ });
58
+ const uri = 'file://' + data.path;
59
+ navigation.goBack();
60
+ if (props.route?.params?.callback) {
61
+ props.route.params.callback(uri);
62
+ }
63
+ if (props.route?.params?.base64) {
64
+ const base64 = await RNFS.readFile(data.path, 'base64');
65
+ props.route.params.base64(base64);
66
+ }
67
+ } catch (err) {
68
+ console.log('takePhoto error', err);
69
+ takingRef.current = false;
70
+ setTaking(false);
71
+ }
72
+ };
73
+
74
+ const requestPermissions = () => {
75
+ request(
76
+ Platform.OS === 'ios'
77
+ ? PERMISSIONS.IOS.CAMERA
78
+ : PERMISSIONS.ANDROID.CAMERA
79
+ ).then((result) => {
80
+ switch (result) {
81
+ case RESULTS.UNAVAILABLE:
82
+ AlertGlobal.show({
83
+ type: 'warning',
84
+ title: 'Thông báo',
85
+ message:
86
+ '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ị!',
87
+ primaryText: 'Đồng ý',
88
+ });
89
+ break;
90
+ case RESULTS.DENIED:
91
+ AlertGlobal.show({
92
+ type: 'warning',
93
+ title: 'Thông báo',
94
+ message:
95
+ 'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
96
+ primaryText: 'Đồng ý',
97
+ onPrimaryPress: () => {
98
+ if (Platform.OS === 'ios') {
99
+ openSettings();
100
+ } else {
101
+ requestPermissions();
102
+ }
103
+ },
104
+ });
105
+ break;
106
+ case RESULTS.LIMITED:
107
+ AlertGlobal.show({
108
+ type: 'warning',
109
+ title: 'Thông báo',
110
+ message:
111
+ 'Quyền máy ảnh bị hạn chế. Vui lòng kiểm tra lại để tiếp tục!',
112
+ primaryText: 'Đồng ý',
113
+ onPrimaryPress: () => {
114
+ if (Platform.OS === 'ios') {
115
+ openSettings();
116
+ } else {
117
+ requestPermissions();
118
+ }
119
+ },
120
+ });
121
+ break;
122
+ case RESULTS.GRANTED:
123
+ setPassPermission(true);
124
+ break;
125
+ case RESULTS.BLOCKED:
126
+ AlertGlobal.show({
127
+ type: 'warning',
128
+ title: 'Thông báo',
129
+ message:
130
+ 'Bạn đã từ chối quyền máy ảnh. Vui lòng cấp quyền máy ảnh để tiếp tục!',
131
+ primaryText: 'Đồng ý',
132
+ onPrimaryPress: () => openSettings(),
133
+ });
134
+ break;
135
+ }
136
+ });
137
+ };
138
+
139
+ useEffect(() => {
140
+ requestPermissions();
141
+ }, []);
142
+
143
+ useEffect(() => {
144
+ let timeout: NodeJS.Timeout;
145
+ if (isFocused) {
146
+ // Delay mounting camera to give Android time to release the previous one
147
+ timeout = setTimeout(() => {
148
+ setShouldRenderCamera(true);
149
+ }, 500);
150
+ } else {
151
+ setShouldRenderCamera(false);
152
+ }
153
+ return () => clearTimeout(timeout);
154
+ }, [isFocused]);
155
+
156
+ // Mặt nạ: phủ trắng toàn bộ vùng preview, khoét một lỗ hình oval ở giữa
157
+ // (fillRule evenodd) -> chỉ nhìn thấy camera bên trong oval.
158
+ const renderOvalMask = () => {
159
+ const { width: w, height: h } = stage;
160
+ if (!w || !h) return null;
161
+ const cx = w / 2;
162
+ const cy = h / 2;
163
+ const rx = OVAL_WIDTH / 2;
164
+ const ry = Math.min(OVAL_HEIGHT, h - 16) / 2;
165
+ const hole = `M ${cx - rx} ${cy} a ${rx} ${ry} 0 1 0 ${
166
+ rx * 2
167
+ } 0 a ${rx} ${ry} 0 1 0 ${-rx * 2} 0 Z`;
168
+ return (
169
+ <Svg
170
+ pointerEvents="none"
171
+ style={StyleSheet.absoluteFill}
172
+ width={w}
173
+ height={h}
174
+ >
175
+ <Path
176
+ d={`M 0 0 H ${w} V ${h} H 0 Z ${hole}`}
177
+ fill="#FFFFFF"
178
+ fillRule="evenodd"
179
+ />
180
+ {/* Vòng ngoài mờ tạo chiều sâu cho khung, vòng trong nét đứt là mốc căn mặt */}
181
+ <Ellipse
182
+ cx={cx}
183
+ cy={cy}
184
+ rx={rx + 8}
185
+ ry={ry + 8}
186
+ stroke={ORANGE}
187
+ strokeWidth={1}
188
+ strokeOpacity={0.25}
189
+ fill="none"
190
+ />
191
+ <Ellipse
192
+ cx={cx}
193
+ cy={cy}
194
+ rx={rx}
195
+ ry={ry}
196
+ stroke={ORANGE}
197
+ strokeWidth={2}
198
+ strokeDasharray="6 5"
199
+ strokeLinecap="round"
200
+ fill="none"
201
+ />
202
+ </Svg>
203
+ );
204
+ };
205
+
206
+ return (
207
+ <View style={styles.container}>
208
+ <View style={[styles.header, { paddingTop: insets.top + 12 }]}>
209
+ <MButton onPress={() => navigation.goBack()} style={styles.backButton}>
210
+ <IconBack />
211
+ </MButton>
212
+ <View style={styles.titleWrap}>
213
+ <MText style={styles.headerTitle}>Xác thực khuôn mặt</MText>
214
+ </View>
215
+ </View>
216
+
217
+ <View
218
+ style={styles.stage}
219
+ onLayout={(e) => setStage(e.nativeEvent.layout)}
220
+ >
221
+ {passPermission && shouldRenderCamera && device ? (
222
+ <VisionCamera
223
+ ref={myCamera}
224
+ style={StyleSheet.absoluteFill}
225
+ device={device}
226
+ isActive={isFocused}
227
+ photo={true}
228
+ />
229
+ ) : (
230
+ <View
231
+ style={[
232
+ StyleSheet.absoluteFill,
233
+ commonStyles.alignCenter,
234
+ commonStyles.justifyCenter,
235
+ ]}
236
+ >
237
+ <ActivityIndicator color={ORANGE} size="large" />
238
+ </View>
239
+ )}
240
+ {renderOvalMask()}
241
+ </View>
242
+
243
+ <MText style={styles.instruction}>
244
+ Xin vui lòng nhìn thẳng, khuôn mặt ở trong khung hình, ánh sáng đủ rõ
245
+ nét khuôn mặt
246
+ </MText>
247
+
248
+ <View style={[styles.captureWrap, { marginBottom: insets.bottom + 24 }]}>
249
+ {/* Quầng cam nhạt quanh nút chụp: tách nút khỏi nền trắng, đúng thiết kế */}
250
+ <View style={styles.captureHalo}>
251
+ {taking ? (
252
+ <ActivityIndicator color={ORANGE} size="large" />
253
+ ) : (
254
+ <MButton onPress={takePhoto}>
255
+ <TakePhotoSvg />
256
+ </MButton>
257
+ )}
258
+ </View>
259
+ </View>
260
+ </View>
261
+ );
262
+ }
263
+
264
+ const styles = StyleSheet.create({
265
+ container: { flex: 1, backgroundColor: '#FFFFFF' },
266
+ header: {
267
+ flexDirection: 'row',
268
+ alignItems: 'center',
269
+ paddingHorizontal: 16,
270
+ paddingBottom: 8,
271
+ },
272
+ backButton: { width: 32, height: 32, justifyContent: 'center' },
273
+ titleWrap: {
274
+ flex: 1,
275
+ alignItems: 'center',
276
+ justifyContent: 'center',
277
+ marginRight: 32,
278
+ },
279
+ headerTitle: { color: '#333333', fontSize: 16, fontWeight: '600' },
280
+ stage: {
281
+ flex: 1,
282
+ overflow: 'hidden',
283
+ backgroundColor: '#FFFFFF',
284
+ },
285
+ instruction: {
286
+ textAlign: 'center',
287
+ color: '#333333',
288
+ fontSize: 14,
289
+ lineHeight: 20,
290
+ paddingHorizontal: 32,
291
+ marginTop: 16,
292
+ },
293
+ captureWrap: { alignItems: 'center', marginTop: 24 },
294
+ captureHalo: {
295
+ width: 88,
296
+ height: 88,
297
+ borderRadius: 44,
298
+ alignItems: 'center',
299
+ justifyContent: 'center',
300
+ backgroundColor: 'rgba(240, 81, 35, 0.10)',
301
+ },
302
+ });