react-native-timacare 3.3.56-alert → 3.3.58-cccd

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 (211) hide show
  1. package/lib/commonjs/assets/icons/index.js +1 -1
  2. package/lib/commonjs/assets/icons/index.js.flow +2 -0
  3. package/lib/commonjs/assets/icons/index.js.map +1 -1
  4. package/lib/commonjs/assets/icons/modal_confirm.svg +5 -5
  5. package/lib/commonjs/assets/icons/modal_notice.svg +32 -32
  6. package/lib/commonjs/assets/icons/modal_success.svg +26 -26
  7. package/lib/commonjs/assets/icons/modal_warning.svg +21 -21
  8. package/lib/commonjs/assets/icons/user_security.svg +10 -0
  9. package/lib/commonjs/assets/tts/Frame1000011947.png +0 -0
  10. package/lib/commonjs/components/AlertLoan.js +1 -1
  11. package/lib/commonjs/components/AlertLoan.js.flow +1 -1
  12. package/lib/commonjs/components/AlertLoan.js.map +1 -1
  13. package/lib/commonjs/components/AppModal.js +1 -1
  14. package/lib/commonjs/components/AppModal.js.flow +40 -2
  15. package/lib/commonjs/components/AppModal.js.map +1 -1
  16. package/lib/commonjs/components/ItemTimaOne.js +1 -1
  17. package/lib/commonjs/components/ItemTimaOne.js.flow +1 -1
  18. package/lib/commonjs/components/ItemTimaOne.js.map +1 -1
  19. package/lib/commonjs/components/ModalNotification/AlertGlobal.js +1 -1
  20. package/lib/commonjs/components/ModalNotification/AlertGlobal.js.flow +92 -33
  21. package/lib/commonjs/components/ModalNotification/AlertGlobal.js.map +1 -1
  22. package/lib/commonjs/components/ModalNotification/index.js +1 -1
  23. package/lib/commonjs/components/ModalNotification/index.js.flow +7 -0
  24. package/lib/commonjs/components/ModalNotification/index.js.map +1 -1
  25. package/lib/commonjs/components/ModalOptionLoan.js +1 -1
  26. package/lib/commonjs/components/ModalOptionLoan.js.flow +1 -1
  27. package/lib/commonjs/components/ModalOptionLoan.js.map +1 -1
  28. package/lib/commonjs/components/ModalOptionLoanDigital.js +1 -1
  29. package/lib/commonjs/components/ModalOptionLoanDigital.js.flow +1 -1
  30. package/lib/commonjs/components/ModalOptionLoanDigital.js.map +1 -1
  31. package/lib/commonjs/components/modalStack.js +2 -0
  32. package/lib/commonjs/components/modalStack.js.flow +47 -0
  33. package/lib/commonjs/components/modalStack.js.map +1 -0
  34. package/lib/commonjs/navigation/primary-navigator.js +1 -1
  35. package/lib/commonjs/navigation/primary-navigator.js.flow +7 -0
  36. package/lib/commonjs/navigation/primary-navigator.js.map +1 -1
  37. package/lib/commonjs/screens/NFC/OcrStart.js +2 -0
  38. package/lib/commonjs/screens/NFC/OcrStart.js.flow +171 -0
  39. package/lib/commonjs/screens/NFC/OcrStart.js.map +1 -0
  40. package/lib/commonjs/screens/NFC/ScanMRZ.js +2 -0
  41. package/lib/commonjs/screens/NFC/ScanMRZ.js.flow +563 -0
  42. package/lib/commonjs/screens/NFC/ScanMRZ.js.map +1 -0
  43. package/lib/commonjs/screens/camera/CCCDCameraScreen.js +1 -1
  44. package/lib/commonjs/screens/camera/CCCDCameraScreen.js.flow +203 -133
  45. package/lib/commonjs/screens/camera/CCCDCameraScreen.js.map +1 -1
  46. package/lib/commonjs/screens/camera/index.js +1 -1
  47. package/lib/commonjs/screens/camera/index.js.flow +1 -1
  48. package/lib/commonjs/screens/camera/index.js.map +1 -1
  49. package/lib/commonjs/screens/detail-loan/PaymentInfo.js +1 -1
  50. package/lib/commonjs/screens/detail-loan/PaymentInfo.js.flow +1 -1
  51. package/lib/commonjs/screens/detail-loan/PaymentInfo.js.map +1 -1
  52. package/lib/commonjs/screens/detail-loan/ShowQrCode.js +1 -1
  53. package/lib/commonjs/screens/detail-loan/ShowQrCode.js.flow +1 -1
  54. package/lib/commonjs/screens/detail-loan/ShowQrCode.js.map +1 -1
  55. package/lib/commonjs/screens/digital-lending/Step6.js +1 -1
  56. package/lib/commonjs/screens/digital-lending/Step6.js.flow +1 -1
  57. package/lib/commonjs/screens/digital-lending/Step6.js.map +1 -1
  58. package/lib/commonjs/screens/eSign/index.js +1 -1
  59. package/lib/commonjs/screens/eSign/index.js.flow +1 -1
  60. package/lib/commonjs/screens/eSign/index.js.map +1 -1
  61. package/lib/commonjs/screens/home/DigitalTopupItem.js +1 -1
  62. package/lib/commonjs/screens/home/DigitalTopupItem.js.flow +1 -1
  63. package/lib/commonjs/screens/home/DigitalTopupItem.js.map +1 -1
  64. package/lib/commonjs/screens/home/ItemMotoBikeLoan.js +1 -1
  65. package/lib/commonjs/screens/home/ItemMotoBikeLoan.js.flow +1 -1
  66. package/lib/commonjs/screens/home/ItemMotoBikeLoan.js.map +1 -1
  67. package/lib/commonjs/screens/home/index.js +1 -1
  68. package/lib/commonjs/screens/home/index.js.flow +8 -113
  69. package/lib/commonjs/screens/home/index.js.map +1 -1
  70. package/lib/commonjs/screens/nationalID/index.js +1 -1
  71. package/lib/commonjs/screens/nationalID/index.js.flow +123 -218
  72. package/lib/commonjs/screens/nationalID/index.js.map +1 -1
  73. package/lib/commonjs/screens/nationalIDBack/index.js +1 -1
  74. package/lib/commonjs/screens/nationalIDBack/index.js.flow +129 -209
  75. package/lib/commonjs/screens/nationalIDBack/index.js.map +1 -1
  76. package/lib/commonjs/screens/payment-evidence/ModalGuideline.js +1 -1
  77. package/lib/commonjs/screens/payment-evidence/ModalGuideline.js.flow +1 -1
  78. package/lib/commonjs/screens/payment-evidence/ModalGuideline.js.map +1 -1
  79. package/lib/commonjs/screens/toan-trinh-so/OCR.js +1 -1
  80. package/lib/commonjs/screens/toan-trinh-so/OCR.js.flow +270 -556
  81. package/lib/commonjs/screens/toan-trinh-so/OCR.js.map +1 -1
  82. package/lib/commonjs/screens/toan-trinh-so/RegisterCamera.js +1 -1
  83. package/lib/commonjs/screens/toan-trinh-so/RegisterCamera.js.flow +1 -1
  84. package/lib/commonjs/screens/toan-trinh-so/RegisterCamera.js.map +1 -1
  85. package/lib/commonjs/screens/toan-trinh-so/VehicleCamera.js +1 -1
  86. package/lib/commonjs/screens/toan-trinh-so/VehicleCamera.js.flow +226 -288
  87. package/lib/commonjs/screens/toan-trinh-so/VehicleCamera.js.map +1 -1
  88. package/lib/commonjs/utils/cameraCrop.js +2 -0
  89. package/lib/commonjs/utils/cameraCrop.js.flow +96 -0
  90. package/lib/commonjs/utils/cameraCrop.js.map +1 -0
  91. package/lib/module/assets/icons/index.js +1 -1
  92. package/lib/module/assets/icons/index.js.map +1 -1
  93. package/lib/module/assets/icons/modal_confirm.svg +5 -5
  94. package/lib/module/assets/icons/modal_notice.svg +32 -32
  95. package/lib/module/assets/icons/modal_success.svg +26 -26
  96. package/lib/module/assets/icons/modal_warning.svg +21 -21
  97. package/lib/module/assets/icons/user_security.svg +10 -0
  98. package/lib/module/assets/tts/Frame1000011947.png +0 -0
  99. package/lib/module/components/AlertLoan.js +1 -1
  100. package/lib/module/components/AlertLoan.js.map +1 -1
  101. package/lib/module/components/AppModal.js +1 -1
  102. package/lib/module/components/AppModal.js.map +1 -1
  103. package/lib/module/components/ItemTimaOne.js +1 -1
  104. package/lib/module/components/ItemTimaOne.js.map +1 -1
  105. package/lib/module/components/ModalNotification/AlertGlobal.js +1 -1
  106. package/lib/module/components/ModalNotification/AlertGlobal.js.map +1 -1
  107. package/lib/module/components/ModalNotification/index.js +1 -1
  108. package/lib/module/components/ModalNotification/index.js.map +1 -1
  109. package/lib/module/components/ModalOptionLoan.js +1 -1
  110. package/lib/module/components/ModalOptionLoan.js.map +1 -1
  111. package/lib/module/components/ModalOptionLoanDigital.js +1 -1
  112. package/lib/module/components/ModalOptionLoanDigital.js.map +1 -1
  113. package/lib/module/components/modalStack.js +2 -0
  114. package/lib/module/components/modalStack.js.map +1 -0
  115. package/lib/module/navigation/primary-navigator.js +1 -1
  116. package/lib/module/navigation/primary-navigator.js.map +1 -1
  117. package/lib/module/screens/NFC/OcrStart.js +2 -0
  118. package/lib/module/screens/NFC/OcrStart.js.map +1 -0
  119. package/lib/module/screens/NFC/ScanMRZ.js +2 -0
  120. package/lib/module/screens/NFC/ScanMRZ.js.map +1 -0
  121. package/lib/module/screens/camera/CCCDCameraScreen.js +1 -1
  122. package/lib/module/screens/camera/CCCDCameraScreen.js.map +1 -1
  123. package/lib/module/screens/camera/index.js +1 -1
  124. package/lib/module/screens/camera/index.js.map +1 -1
  125. package/lib/module/screens/detail-loan/PaymentInfo.js +1 -1
  126. package/lib/module/screens/detail-loan/PaymentInfo.js.map +1 -1
  127. package/lib/module/screens/detail-loan/ShowQrCode.js +1 -1
  128. package/lib/module/screens/detail-loan/ShowQrCode.js.map +1 -1
  129. package/lib/module/screens/digital-lending/Step6.js +1 -1
  130. package/lib/module/screens/digital-lending/Step6.js.map +1 -1
  131. package/lib/module/screens/eSign/index.js +1 -1
  132. package/lib/module/screens/eSign/index.js.map +1 -1
  133. package/lib/module/screens/home/DigitalTopupItem.js +1 -1
  134. package/lib/module/screens/home/DigitalTopupItem.js.map +1 -1
  135. package/lib/module/screens/home/ItemMotoBikeLoan.js +1 -1
  136. package/lib/module/screens/home/ItemMotoBikeLoan.js.map +1 -1
  137. package/lib/module/screens/home/index.js +1 -1
  138. package/lib/module/screens/home/index.js.map +1 -1
  139. package/lib/module/screens/nationalID/index.js +1 -1
  140. package/lib/module/screens/nationalID/index.js.map +1 -1
  141. package/lib/module/screens/nationalIDBack/index.js +1 -1
  142. package/lib/module/screens/nationalIDBack/index.js.map +1 -1
  143. package/lib/module/screens/payment-evidence/ModalGuideline.js +1 -1
  144. package/lib/module/screens/payment-evidence/ModalGuideline.js.map +1 -1
  145. package/lib/module/screens/toan-trinh-so/OCR.js +1 -1
  146. package/lib/module/screens/toan-trinh-so/OCR.js.map +1 -1
  147. package/lib/module/screens/toan-trinh-so/RegisterCamera.js +1 -1
  148. package/lib/module/screens/toan-trinh-so/RegisterCamera.js.map +1 -1
  149. package/lib/module/screens/toan-trinh-so/VehicleCamera.js +1 -1
  150. package/lib/module/screens/toan-trinh-so/VehicleCamera.js.map +1 -1
  151. package/lib/module/utils/cameraCrop.js +2 -0
  152. package/lib/module/utils/cameraCrop.js.map +1 -0
  153. package/lib/typescript/assets/icons/index.d.ts +2 -1
  154. package/lib/typescript/assets/icons/index.d.ts.map +1 -1
  155. package/lib/typescript/components/AppModal.d.ts +6 -1
  156. package/lib/typescript/components/AppModal.d.ts.map +1 -1
  157. package/lib/typescript/components/ModalNotification/AlertGlobal.d.ts +20 -4
  158. package/lib/typescript/components/ModalNotification/AlertGlobal.d.ts.map +1 -1
  159. package/lib/typescript/components/ModalNotification/index.d.ts +6 -1
  160. package/lib/typescript/components/ModalNotification/index.d.ts.map +1 -1
  161. package/lib/typescript/components/modalStack.d.ts +11 -0
  162. package/lib/typescript/components/modalStack.d.ts.map +1 -0
  163. package/lib/typescript/navigation/primary-navigator.d.ts +3 -0
  164. package/lib/typescript/navigation/primary-navigator.d.ts.map +1 -1
  165. package/lib/typescript/screens/NFC/OcrStart.d.ts +2 -0
  166. package/lib/typescript/screens/NFC/OcrStart.d.ts.map +1 -0
  167. package/lib/typescript/screens/NFC/ScanMRZ.d.ts +15 -0
  168. package/lib/typescript/screens/NFC/ScanMRZ.d.ts.map +1 -0
  169. package/lib/typescript/screens/camera/CCCDCameraScreen.d.ts.map +1 -1
  170. package/lib/typescript/screens/home/index.d.ts.map +1 -1
  171. package/lib/typescript/screens/nationalID/index.d.ts.map +1 -1
  172. package/lib/typescript/screens/nationalIDBack/index.d.ts.map +1 -1
  173. package/lib/typescript/screens/toan-trinh-so/OCR.d.ts.map +1 -1
  174. package/lib/typescript/screens/toan-trinh-so/VehicleCamera.d.ts.map +1 -1
  175. package/lib/typescript/utils/cameraCrop.d.ts +43 -0
  176. package/lib/typescript/utils/cameraCrop.d.ts.map +1 -0
  177. package/package.json +2 -1
  178. package/src/assets/icons/index.tsx +2 -0
  179. package/src/assets/icons/modal_confirm.svg +5 -5
  180. package/src/assets/icons/modal_notice.svg +32 -32
  181. package/src/assets/icons/modal_success.svg +26 -26
  182. package/src/assets/icons/modal_warning.svg +21 -21
  183. package/src/assets/icons/user_security.svg +10 -0
  184. package/src/assets/tts/Frame1000011947.png +0 -0
  185. package/src/components/AlertLoan.tsx +1 -1
  186. package/src/components/AppModal.tsx +40 -2
  187. package/src/components/ItemTimaOne.tsx +1 -1
  188. package/src/components/ModalNotification/AlertGlobal.tsx +92 -33
  189. package/src/components/ModalNotification/index.tsx +7 -0
  190. package/src/components/ModalOptionLoan.tsx +1 -1
  191. package/src/components/ModalOptionLoanDigital.tsx +1 -1
  192. package/src/components/modalStack.ts +47 -0
  193. package/src/navigation/primary-navigator.tsx +7 -0
  194. package/src/screens/NFC/OcrStart.tsx +171 -0
  195. package/src/screens/NFC/ScanMRZ.tsx +563 -0
  196. package/src/screens/camera/CCCDCameraScreen.tsx +203 -133
  197. package/src/screens/camera/index.tsx +1 -1
  198. package/src/screens/detail-loan/PaymentInfo.tsx +1 -1
  199. package/src/screens/detail-loan/ShowQrCode.tsx +1 -1
  200. package/src/screens/digital-lending/Step6.tsx +1 -1
  201. package/src/screens/eSign/index.tsx +1 -1
  202. package/src/screens/home/DigitalTopupItem.tsx +1 -1
  203. package/src/screens/home/ItemMotoBikeLoan.tsx +1 -1
  204. package/src/screens/home/index.tsx +8 -113
  205. package/src/screens/nationalID/index.tsx +123 -218
  206. package/src/screens/nationalIDBack/index.tsx +129 -209
  207. package/src/screens/payment-evidence/ModalGuideline.tsx +1 -1
  208. package/src/screens/toan-trinh-so/OCR.tsx +270 -556
  209. package/src/screens/toan-trinh-so/RegisterCamera.tsx +1 -1
  210. package/src/screens/toan-trinh-so/VehicleCamera.tsx +226 -288
  211. package/src/utils/cameraCrop.ts +96 -0
@@ -1,16 +1,19 @@
1
1
  //@ts-nocheck
2
- import React, { useEffect, useRef, useState } from 'react';
2
+ import React, {
3
+ useCallback,
4
+ useEffect,
5
+ useMemo,
6
+ useRef,
7
+ useState,
8
+ } from 'react';
3
9
  import { observer } from 'mobx-react-lite';
4
10
  import {
5
11
  View,
6
- NativeModules,
7
12
  TouchableOpacity,
8
13
  TextStyle,
9
14
  ViewStyle,
10
15
  ImageStyle,
11
16
  Image,
12
- ActivityIndicator,
13
- Dimensions,
14
17
  StyleSheet,
15
18
  Platform,
16
19
  } from 'react-native';
@@ -22,50 +25,66 @@ import {
22
25
  RESULTS,
23
26
  openSettings,
24
27
  } from 'react-native-permissions';
25
- import {
26
- Camera as VisionCamera,
27
- useCameraDevice,
28
- useCameraFormat,
29
- } from 'react-native-vision-camera';
30
- import Modal from 'react-native-modal';
28
+ import { Camera as VisionCamera, useCameraDevices } from 'react-native-vision-camera';
29
+ import Modal from '../../components/AppModal';
31
30
  import LinearGradient from 'react-native-linear-gradient';
32
- import {
33
- CloseIcon,
34
- IconBackWhite,
35
- TakePhotoSvg,
36
- TimaSmall,
37
- } from '../../assets/icons';
38
- import MButton from '../../components/MButton';
39
- import { commonStyles } from '../CommonStyles';
31
+ import { CloseIcon, IconBack, TakePhotoSvg, TimaSmall } from '../../assets/icons';
40
32
  import { useNavigation, useIsFocused } from '@react-navigation/native';
41
- import TextRecognition from '@react-native-ml-kit/text-recognition';
42
33
  import { ScreenNames } from '../../navigation';
43
- import { Api } from '../../services/api';
44
- import Loading from '../../components/Loading';
45
34
  import { IconUpload } from '../../assets/svgs';
46
35
  import ImagePicker from 'react-native-image-crop-picker';
47
36
  import { launchImageLibrary } from 'react-native-image-picker';
48
37
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
49
38
  import { AlertGlobal } from '../../components/ModalNotification/AlertGlobal';
39
+ import {
40
+ cropPhotoToAspect,
41
+ pickClosestFocusDevice,
42
+ } from '../../utils/cameraCrop';
50
43
 
51
- const { NFCReader } = NativeModules;
44
+ const ORANGE = '#F05123';
45
+ // Thẻ CMND/CCCD theo chuẩn ID-1: 85.6mm x 54mm
46
+ const CARD_ASPECT_RATIO = 85.6 / 54;
52
47
 
53
48
  export const OCR = observer(function OCR(props: any) {
54
49
  const insets = useSafeAreaInsets();
55
50
  const isFocused = useIsFocused();
56
- const [isLoading, setIsLoading] = useState(false);
57
51
  const navigation = useNavigation();
58
52
  const [showModal, setShowModal] = useState(false);
59
- const [type, setType] = useState('passport');
60
53
  const [taking, setTaking] = useState(false);
61
54
  const [passPermission, setPassPermission] = useState(false);
62
55
  const [shouldRenderCamera, setShouldRenderCamera] = useState(false);
56
+ const [focusPoint, setFocusPoint] = useState(null);
57
+
63
58
  const myCamera = useRef(null);
64
- const device = useCameraDevice('back');
65
- const format = useCameraFormat(device, [
66
- { photoResolution: { width: 1280, height: 720 } },
59
+ const frameSize = useRef({ width: 0, height: 0 });
60
+ const focusTimer = useRef(null);
61
+
62
+ // Chọn ống kính sau lấy nét gần nhất (macro) để chụp cận thẻ mà không mờ
63
+ const devices = useCameraDevices();
64
+ const device = useMemo(() => pickClosestFocusDevice(devices, 'back'), [
65
+ devices,
67
66
  ]);
68
67
 
68
+ const front = props?.route?.params?.front;
69
+
70
+ // Lấy nét vào một điểm (toạ độ theo hệ khung preview = camera view). Dùng cho
71
+ // chạm-để-lấy-nét và lấy nét giữa khung trước khi chụp -> tránh mờ khi gần.
72
+ const focusAt = useCallback(
73
+ async (x, y) => {
74
+ const cam = myCamera.current;
75
+ if (cam == null || !device?.supportsFocus) return;
76
+ setFocusPoint({ x, y });
77
+ if (focusTimer.current) clearTimeout(focusTimer.current);
78
+ focusTimer.current = setTimeout(() => setFocusPoint(null), 800);
79
+ try {
80
+ await cam.focus({ x, y });
81
+ } catch (e) {
82
+ // focus có thể lỗi nếu đang lấy nét dở hoặc điểm ngoài vùng -> bỏ qua
83
+ }
84
+ },
85
+ [device]
86
+ );
87
+
69
88
  const requestPermissions = () => {
70
89
  request(
71
90
  Platform.OS === 'ios'
@@ -170,139 +189,19 @@ export const OCR = observer(function OCR(props: any) {
170
189
  useEffect(() => {
171
190
  requestPermissions();
172
191
  loadIntro();
192
+ return () => {
193
+ if (focusTimer.current) clearTimeout(focusTimer.current);
194
+ };
173
195
  }, []);
174
196
 
175
- const saveDataNfc = async (body) => {
176
- try {
177
- const response = await Api.getInstance().saveNfcInfo({
178
- ...body,
179
- loanBriefId: props?.route?.params?.loan?.id,
180
- });
181
- if (response.kind === 'ok') {
182
- if (response.data?.meta?.errorCode === 200) {
183
- setTimeout(() => {
184
- navigation.push(ScreenNames.TTSSelfie, {
185
- loan: props?.route?.params?.loan,
186
- });
187
- }, 500);
188
- } else {
189
- AlertGlobal.show({
190
- type: 'warning',
191
- title: 'Thông báo',
192
- message: response?.data?.meta?.errorMessage,
193
- primaryText: 'Đóng',
194
- });
195
- }
196
- } else {
197
- AlertGlobal.show({
198
- type: 'error',
199
- title: 'Thông báo',
200
- message: 'Có lỗi xảy ra. Vui lòng thử lại sau',
201
- primaryText: 'Đóng',
202
- });
203
- }
204
- } catch (error) {
205
- console.log(error);
206
- }
207
- };
208
-
209
- const convertToDate = (input, end) => {
210
- let yearPrefix = input.substring(0, 2);
211
- let year = end
212
- ? '20' + yearPrefix
213
- : parseInt(yearPrefix) > 20
214
- ? '19' + yearPrefix
215
- : '20' + yearPrefix;
216
- let month = input.substring(2, 4);
217
- let day = input.substring(4, 6);
218
-
219
- return year + '-' + month + '-' + day;
220
- };
221
-
222
- const extractMRZInfo = (text) => {
223
- let documentNumber = '';
224
- let birthDate = '';
225
- let expirationDate = '';
226
- const regex1 = /VNM(\d{9})/;
227
- const match1 = text.match(regex1);
228
-
229
- // If a match is found, return the 9 digits, otherwise return null
230
- if (match1) {
231
- documentNumber = match1[1];
232
- }
233
-
234
- console.log('documentNumber', documentNumber);
235
-
236
- // Regular expression to match 'VNM' followed by 9 digits, then two 6-digit numbers
237
- const regex = /(\d{6}).[MF](\d{6})/;
238
-
239
- // Search for the pattern in the text
240
- const match = text.replace(/ /g, '').match(regex);
241
-
242
- // If a match is found, return the captured groups, otherwise return null
243
- if (match) {
244
- console.log({
245
- documentNumber: documentNumber,
246
- birthDate: match[1],
247
- expirationDate: match[2],
248
- });
249
- (birthDate = match[1]), (expirationDate = match[2]);
250
- } else {
251
- return null;
252
- }
253
- if (documentNumber && birthDate && expirationDate) {
254
- try {
255
- NFCReader.readNFCCard(
256
- documentNumber,
257
- convertToDate(birthDate, false),
258
- convertToDate(expirationDate, true),
259
- (error, response) => {
260
- try {
261
- const convertData = JSON.parse(response);
262
- saveDataNfc(convertData);
263
- } catch (e) {
264
- console.log(e);
265
- }
266
- }
267
- );
268
- } catch (e) {
269
- console.log(e);
270
- }
271
- }
272
- };
273
-
274
- const handleTextRecognition = async (imagePath) => {
275
- try {
276
- const result = await TextRecognition.recognize(imagePath);
277
- extractMRZInfo(result.text);
278
- } catch (error) {
279
- console.log(error);
280
- }
281
- };
282
-
283
- const scanNfc = async () => {
284
- if (myCamera.current) {
285
- const data = await myCamera.current.takePhoto({
286
- qualityPrioritization: 'speed',
287
- });
288
- await handleTextRecognition('file://' + data.path);
289
- }
290
- };
291
-
292
- const takePhoto = async () => {
293
- if (myCamera.current == null || taking) return;
294
- setTaking(true);
295
- const data = await myCamera.current.takePhoto({
296
- qualityPrioritization: 'quality',
297
- });
298
- const uri = 'file://' + data.path;
299
- setTaking(false);
300
- if (props?.route?.params?.front && !props.route?.params?.callback) {
197
+ // Điều hướng sau khi có ảnh (giữ nguyên logic gốc của OCR)
198
+ const goNextWithUri = (uri) => {
199
+ if (front && !props.route?.params?.callback) {
301
200
  navigation.push(ScreenNames.TTSNationalIDFront, {
302
201
  uri,
303
202
  loan: props?.route?.params?.loan,
304
203
  });
305
- } else if (!props?.route?.params?.front && !props.route?.params?.callback) {
204
+ } else if (!front && !props.route?.params?.callback) {
306
205
  navigation.push(ScreenNames.TTSNationalIDBack, {
307
206
  uri,
308
207
  loan: props?.route?.params?.loan,
@@ -313,229 +212,135 @@ export const OCR = observer(function OCR(props: any) {
313
212
  }
314
213
  };
315
214
 
215
+ const takePhoto = async () => {
216
+ if (myCamera.current == null || taking) return;
217
+ setTaking(true);
218
+ try {
219
+ // Lấy nét giữa khung trước khi chụp để tài liệu ở gần vẫn nét
220
+ const { width: fw, height: fh } = frameSize.current;
221
+ if (device?.supportsFocus && fw && fh) {
222
+ try {
223
+ await myCamera.current.focus({ x: fw / 2, y: fh / 2 });
224
+ } catch (e) {}
225
+ }
226
+ const data = await myCamera.current.takePhoto({
227
+ qualityPrioritization: 'quality',
228
+ });
229
+ // Cắt ảnh vừa đúng khung theo tỉ lệ thẻ (xử lý orientation trong util)
230
+ const uri = await cropPhotoToAspect(data, CARD_ASPECT_RATIO);
231
+ setTaking(false);
232
+ goNextWithUri(uri);
233
+ } catch (e) {
234
+ console.error(e);
235
+ setTaking(false);
236
+ AlertGlobal.show({
237
+ type: 'warning',
238
+ title: 'Thông báo',
239
+ message: 'Không chụp được ảnh. Vui lòng thử lại.',
240
+ primaryText: 'Đóng',
241
+ });
242
+ }
243
+ };
244
+
316
245
  const pickImageFromGallery = async () => {
317
246
  if (Platform.OS === 'android') {
318
- const result = await launchImageLibrary({
319
- mediaType: 'photo',
320
- });
247
+ const result = await launchImageLibrary({ mediaType: 'photo' });
321
248
  if (result.assets) {
322
- if (props?.route?.params?.front && !props.route?.params?.callback) {
323
- navigation.push(ScreenNames.TTSNationalIDFront, {
324
- uri: result.assets[0]?.uri,
325
- loan: props?.route?.params?.loan,
326
- });
327
- } else if (
328
- !props?.route?.params?.front &&
329
- !props.route?.params?.callback
330
- ) {
331
- navigation.push(ScreenNames.TTSNationalIDBack, {
332
- uri: result.assets[0]?.uri,
333
- loan: props?.route?.params?.loan,
334
- });
335
- } else {
336
- navigation.goBack();
337
- props.route?.params?.callback(result.assets[0]?.uri);
338
- }
249
+ goNextWithUri(result.assets[0]?.uri);
339
250
  }
340
251
  } else {
341
252
  ImagePicker.openPicker({}).then((image) => {
342
- if (props?.route?.params?.front && !props.route?.params?.callback) {
343
- navigation.push(ScreenNames.TTSNationalIDFront, {
344
- uri:
345
- Platform.OS === 'android' ? image?.path : 'file://' + image?.path,
346
- loan: props?.route?.params?.loan,
347
- });
348
- } else if (
349
- !props?.route?.params?.front &&
350
- !props.route?.params?.callback
351
- ) {
352
- navigation.push(ScreenNames.TTSNationalIDBack, {
353
- uri:
354
- Platform.OS === 'android' ? image?.path : 'file://' + image?.path,
355
- loan: props?.route?.params?.loan,
356
- });
357
- } else {
358
- navigation.goBack();
359
- props.route?.params?.callback(
360
- Platform.OS === 'android' ? image?.path : 'file://' + image?.path
361
- );
362
- }
253
+ goNextWithUri(
254
+ Platform.OS === 'android' ? image?.path : 'file://' + image?.path
255
+ );
363
256
  });
364
257
  }
365
258
  };
366
259
 
367
260
  return (
368
- <View
369
- style={{ flex: 1, backgroundColor: 'black' }}
370
- pointerEvents={'box-none'}
371
- >
372
- <View
373
- style={{
374
- alignItems: 'center',
375
- position: 'absolute',
376
- top: insets.top + 16,
377
- left: 0,
378
- right: 0,
379
- zIndex: 100,
380
- }}
381
- >
382
- <TouchableOpacity
383
- onPress={() => {
384
- navigation.navigate(ScreenNames.Main);
385
- }}
386
- style={{
387
- position: 'absolute',
388
- left: 10,
389
- top: 4,
390
- width: 24,
391
- height: 24,
392
- alignItems: 'center',
393
- justifyContent: 'center',
394
- }}
395
- >
396
- <IconBackWhite />
397
- </TouchableOpacity>
398
-
399
- <MText
400
- style={{
401
- color: 'white',
402
- fontSize: 16,
403
- textAlign: 'center',
404
- padding: 16,
405
- marginTop: 10,
406
- }}
407
- >
408
- {type === 'passport' && props?.route?.params?.front
409
- ? 'Chụp CMND/ CCCD mặt trước'
410
- : type === 'passport'
411
- ? 'Chụp CMND/ CCCD mặt sau'
412
- : 'Dùng camera để quét chuỗi ký tự mặt sau thẻ CCCD gắn chíp ở phía dưới'}
413
- </MText>
414
- </View>
415
-
416
- {passPermission && type === 'passport' && shouldRenderCamera ? (
417
- <View
418
- style={[
419
- commonStyles.alignCenter,
420
- commonStyles.fill,
421
- commonStyles.justifyCenter,
422
- {
423
- position: 'absolute',
424
- width: Dimensions.get('window').width,
425
- height: Dimensions.get('window').height,
426
- },
427
- ]}
428
- >
429
- {device ? (
430
- <View style={styles.passportCameraFrame}>
261
+ <View style={styles.container}>
262
+ {passPermission && shouldRenderCamera ? (
263
+ <>
264
+ <View style={[styles.header, { paddingTop: insets.top + 16 }]}>
265
+ <TouchableOpacity
266
+ onPress={() => navigation.navigate(ScreenNames.Main)}
267
+ hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }}
268
+ >
269
+ <IconBack />
270
+ </TouchableOpacity>
271
+ <View style={styles.titleWrap}>
272
+ <MText style={styles.headerTitle}>CMND / CCCD</MText>
273
+ </View>
274
+ </View>
275
+
276
+ <MText style={styles.instruction}>
277
+ {front ? 'Chụp CMND/ CCCD mặt trước' : 'Chụp CMND/ CCCD mặt sau'}
278
+ </MText>
279
+
280
+ {/* Khung tỉ lệ thẻ: vùng nhìn thấy của preview = vùng sẽ crop.
281
+ Chạm vào khung để lấy nét thủ công. */}
282
+ <View
283
+ style={styles.photoFrame}
284
+ onLayout={(e) => {
285
+ frameSize.current = e.nativeEvent.layout;
286
+ }}
287
+ onStartShouldSetResponder={() => true}
288
+ onResponderRelease={(e) =>
289
+ focusAt(e.nativeEvent.locationX, e.nativeEvent.locationY)
290
+ }
291
+ >
292
+ {device ? (
431
293
  <VisionCamera
432
294
  ref={myCamera}
433
295
  style={StyleSheet.absoluteFill}
434
296
  device={device}
435
- isActive={true}
297
+ isActive={isFocused}
436
298
  photo={true}
437
- format={format}
438
299
  />
439
- </View>
440
- ) : (
441
- <ActivityIndicator />
442
- )}
300
+ ) : null}
301
+ <View style={[styles.corner, styles.cornerTL]} />
302
+ <View style={[styles.corner, styles.cornerTR]} />
303
+ <View style={[styles.corner, styles.cornerBL]} />
304
+ <View style={[styles.corner, styles.cornerBR]} />
305
+ {focusPoint && (
306
+ <View
307
+ pointerEvents="none"
308
+ style={[
309
+ styles.focusRing,
310
+ { left: focusPoint.x - 32, top: focusPoint.y - 32 },
311
+ ]}
312
+ />
313
+ )}
314
+ </View>
315
+
443
316
  <TouchableOpacity
444
- onPress={() => {
445
- pickImageFromGallery();
446
- }}
447
- style={{
448
- flexDirection: 'row',
449
- alignItems: 'center',
450
- marginTop: 16,
451
- }}
317
+ style={styles.galleryButton}
318
+ onPress={pickImageFromGallery}
452
319
  >
453
320
  <IconUpload />
454
- <MText
455
- style={{
456
- color: 'white',
457
- fontSize: 12,
458
- marginLeft: 8,
459
- }}
460
- >
461
- Tải ảnh từ thư viện
462
- </MText>
321
+ <MText style={styles.galleryText}>Tải ảnh từ thư viện</MText>
463
322
  </TouchableOpacity>
464
323
 
465
- <View style={[styles.buttonContainer, { bottom: insets.bottom }]}>
466
- <MText
467
- style={{
468
- color: 'white',
469
- fontSize: 14,
470
- textAlign: 'center',
471
- padding: 16,
472
- }}
473
- >
474
- Vui lòng đặt giấy tờ nằm vừa khung hình chữ nhật, chụp đủ ánh sáng
475
- và rõ nét.
476
- </MText>
477
- <MButton
478
- onPress={() => {
479
- takePhoto();
480
- }}
481
- >
482
- <TakePhotoSvg />
483
- </MButton>
484
- </View>
485
- </View>
486
- ) : passPermission && type === 'nfc' && shouldRenderCamera ? (
487
- <>
488
- {device ? (
489
- <VisionCamera
490
- ref={myCamera}
491
- style={styles.camera}
492
- device={device}
493
- isActive={true}
494
- photo={true}
495
- format={format}
496
- />
497
- ) : null}
498
- <View style={styles.overlay}>
499
- <View style={styles.overlayTop} />
500
- <View style={styles.overlayCenter}>
501
- <View style={styles.overlayLeft} />
502
- <View style={styles.overlayRect}></View>
503
- <View style={styles.overlayRight} />
504
- </View>
505
- <View style={styles.overlayBottom} />
506
- </View>
507
- <View
508
- style={[styles.buttonContainer, { bottom: insets.bottom + 24 }]}
324
+ <View style={styles.spacer} />
325
+ <MText style={[styles.hint, { marginTop: 0, marginBottom: 20 }]}>
326
+ Đưa giấy tờ lại gần cho vừa khung, chạm vào màn hình{'\n'}để lấy nét
327
+ nếu ảnh bị mờ.
328
+ </MText>
329
+ <TouchableOpacity
330
+ style={[styles.captureButton, { marginBottom: insets.bottom + 24 }]}
331
+ onPress={takePhoto}
332
+ disabled={taking}
509
333
  >
510
- <MText
511
- style={{
512
- color: 'white',
513
- fontSize: 14,
514
- textAlign: 'center',
515
- padding: 16,
516
- }}
517
- >
518
- Vui lòng đặt chuỗi kí tự nằm vừa khung hình chữ nhật, chụp đủ ánh
519
- sáng và rõ nét.
520
- </MText>
521
- <TouchableOpacity
522
- onPress={() => {
523
- scanNfc();
524
- }}
525
- >
526
- <TakePhotoSvg />
527
- </TouchableOpacity>
528
- </View>
334
+ <TakePhotoSvg />
335
+ </TouchableOpacity>
529
336
  </>
530
- ) : (
531
- <></>
532
- )}
337
+ ) : null}
533
338
 
534
339
  <Modal
535
340
  isVisible={showModal}
536
341
  style={$modal}
537
342
  backdropTransitionInTiming={0}
538
- nimationOutTiming={200}
343
+ animationOutTiming={200}
539
344
  animationInTiming={200}
540
345
  animationIn={'slideInUp'}
541
346
  animationOut={'fadeOutDown'}
@@ -564,9 +369,7 @@ export const OCR = observer(function OCR(props: any) {
564
369
  <CloseIcon />
565
370
  </TouchableOpacity>
566
371
  <MText style={$title}>NHẬN DIỆN GIẤY TỜ TÙY THÂN</MText>
567
- <MText style={$description}>
568
- Chụp ảnh 2 mặt hoặc NFC CCCD/ CMND
569
- </MText>
372
+ <MText style={$description}>Chụp ảnh 2 mặt CCCD/ CMND</MText>
570
373
 
571
374
  <Image
572
375
  source={require('../../assets/tts/vehicleStep.png')}
@@ -574,52 +377,9 @@ export const OCR = observer(function OCR(props: any) {
574
377
  />
575
378
 
576
379
  <View style={$bg}>
577
- <View
578
- style={{
579
- flexDirection: 'column',
580
- alignItems: 'center',
581
- }}
582
- >
583
- <Image
584
- source={require('../../assets/tts/Frame1000011946.png')}
585
- style={{
586
- width: '60%',
587
- height: 70,
588
- resizeMode: 'contain',
589
- marginVertical: 16,
590
- }}
591
- />
592
- </View>
593
- <View style={{ marginTop: 10, paddingHorizontal: 24 }}>
380
+ <View style={{ marginTop: 16, paddingHorizontal: 24 }}>
594
381
  <MText style={[$title2, { textAlign: 'left' }]}>
595
- Cách 1: NFC (Áp dụng cho CCCD gắn chip)
596
- </MText>
597
- <View style={$row}>
598
- <TimaSmall />
599
- <MText style={$txt}>
600
- Đặt phần chip đồng gần lại với phần phía trên của{'\n'}mặt
601
- lưng điện thoại
602
- </MText>
603
- </View>
604
- <View style={$row}>
605
- <TimaSmall />
606
- <MText style={$txt}>
607
- Giữ nguyên cho đến khi đọc xong thông tin thẻ
608
- </MText>
609
- </View>
610
- <Image
611
- source={require('../../assets/tts/Frame1000011953.png')}
612
- style={{
613
- width: '100%',
614
- height: 70,
615
- resizeMode: 'contain',
616
- marginTop: 16,
617
- }}
618
- />
619
- </View>
620
- <View style={{ marginTop: 10, paddingHorizontal: 24 }}>
621
- <MText style={[$title2, { textAlign: 'left' }]}>
622
- Cách 2: Chụp 2 mặt
382
+ Lưu ý chụp CCCD/ CMND
623
383
  </MText>
624
384
  <View style={$row}>
625
385
  <TimaSmall />
@@ -651,82 +411,131 @@ export const OCR = observer(function OCR(props: any) {
651
411
  />
652
412
  </View>
653
413
 
654
- <View
414
+ <TouchableOpacity
415
+ onPress={() => {
416
+ setShowModal(false);
417
+ save(OCR_V1, true);
418
+ }}
655
419
  style={{
656
- flexDirection: 'row',
657
- alignItems: 'center',
658
420
  margin: 16,
421
+ height: 40,
422
+ alignItems: 'center',
423
+ justifyContent: 'center',
424
+ borderRadius: 40,
425
+ borderWidth: 1,
426
+ borderColor: '#EF592E',
659
427
  }}
660
428
  >
661
- <TouchableOpacity
662
- onPress={() => {
663
- setType('passport');
664
- setShowModal(false);
665
- save(OCR_V1, true);
666
- }}
667
- style={{
668
- flex: 1,
669
- borderWidth: 1,
670
- borderColor: '#EF592E',
671
- height: 40,
672
- alignItems: 'center',
673
- justifyContent: 'center',
674
- borderRadius: 40,
675
- // marginRight: 16,
676
- }}
677
- >
678
- <MText
679
- style={{
680
- color: '#EF592E',
681
- fontSize: 16,
682
- }}
683
- >
684
- Chụp 2 mặt
685
- </MText>
686
- </TouchableOpacity>
687
- {/* <MButton
688
- style={{
689
- flex: 1,
690
- }}
691
- onPress={() => {
692
- setType('nfc');
693
- setShowModal(false);
694
- save(OCR_V1, true);
695
- }}
696
- >
697
- <LinearGradient
698
- start={{ x: 1, y: 0 }}
699
- end={{ x: 0, y: 1 }}
700
- style={{
701
- height: 40,
702
- marginVertical: 16,
703
- borderRadius: 30,
704
- alignItems: 'center',
705
- justifyContent: 'center',
706
- }}
707
- colors={['#FF7A00', '#EF4123']}
708
- >
709
- <MText
710
- style={{
711
- fontSize: 16,
712
- color: 'white',
713
- }}
714
- >
715
- NFC
716
- </MText>
717
- </LinearGradient>
718
- </MButton> */}
719
- </View>
720
- <View style={{ height: 24 }}></View>
429
+ <MText style={{ color: '#EF592E', fontSize: 16 }}>
430
+ Chụp 2 mặt
431
+ </MText>
432
+ </TouchableOpacity>
433
+ <View style={{ height: 24 }} />
721
434
  </View>
722
435
  </View>
723
436
  </LinearGradient>
724
437
  </Modal>
725
-
726
- <Loading isLoading={isLoading} />
727
438
  </View>
728
439
  );
729
440
  });
441
+
442
+ const styles = StyleSheet.create({
443
+ container: { flex: 1, backgroundColor: '#FFFFFF' },
444
+ header: {
445
+ flexDirection: 'row',
446
+ alignItems: 'center',
447
+ paddingHorizontal: 16,
448
+ paddingTop: 8,
449
+ },
450
+ titleWrap: {
451
+ flex: 1,
452
+ alignItems: 'center',
453
+ justifyContent: 'center',
454
+ marginRight: 24,
455
+ },
456
+ headerTitle: { color: '#333333', fontSize: 16, fontWeight: '600' },
457
+ instruction: {
458
+ textAlign: 'center',
459
+ color: '#333333',
460
+ fontSize: 14,
461
+ lineHeight: 20,
462
+ marginTop: 20,
463
+ marginBottom: 28,
464
+ },
465
+ photoFrame: {
466
+ marginHorizontal: 24,
467
+ aspectRatio: CARD_ASPECT_RATIO,
468
+ borderRadius: 8,
469
+ overflow: 'hidden',
470
+ backgroundColor: '#000000',
471
+ },
472
+ corner: {
473
+ position: 'absolute',
474
+ width: 26,
475
+ height: 26,
476
+ borderColor: ORANGE,
477
+ },
478
+ focusRing: {
479
+ position: 'absolute',
480
+ width: 64,
481
+ height: 64,
482
+ borderRadius: 8,
483
+ borderWidth: 1.5,
484
+ borderColor: '#FFD54A',
485
+ },
486
+ cornerTL: {
487
+ top: 0,
488
+ left: 0,
489
+ borderTopWidth: 4,
490
+ borderLeftWidth: 4,
491
+ borderTopLeftRadius: 8,
492
+ },
493
+ cornerTR: {
494
+ top: 0,
495
+ right: 0,
496
+ borderTopWidth: 4,
497
+ borderRightWidth: 4,
498
+ borderTopRightRadius: 8,
499
+ },
500
+ cornerBL: {
501
+ bottom: 0,
502
+ left: 0,
503
+ borderBottomWidth: 4,
504
+ borderLeftWidth: 4,
505
+ borderBottomLeftRadius: 8,
506
+ },
507
+ cornerBR: {
508
+ bottom: 0,
509
+ right: 0,
510
+ borderBottomWidth: 4,
511
+ borderRightWidth: 4,
512
+ borderBottomRightRadius: 8,
513
+ },
514
+ galleryButton: {
515
+ alignSelf: 'center',
516
+ flexDirection: 'row',
517
+ alignItems: 'center',
518
+ gap: 8,
519
+ marginTop: 20,
520
+ paddingHorizontal: 14,
521
+ paddingVertical: 8,
522
+ borderRadius: 8,
523
+ borderWidth: 1,
524
+ borderColor: ORANGE,
525
+ backgroundColor: '#FFFFFF',
526
+ },
527
+ galleryText: { color: '#333333', fontSize: 13 },
528
+ hint: {
529
+ textAlign: 'center',
530
+ color: '#555555',
531
+ fontSize: 13,
532
+ lineHeight: 19,
533
+ marginTop: 48,
534
+ },
535
+ spacer: { flex: 1 },
536
+ captureButton: { alignSelf: 'center' },
537
+ });
538
+
730
539
  const $row: ViewStyle = {
731
540
  flexDirection: 'row',
732
541
  alignItems: 'center',
@@ -774,10 +583,6 @@ const $description: TextStyle = {
774
583
  textAlign: 'center',
775
584
  };
776
585
 
777
- const $flex: ViewStyle = {
778
- flex: 1,
779
- };
780
-
781
586
  const $modal: ViewStyle = {
782
587
  justifyContent: 'flex-end',
783
588
  margin: 0,
@@ -787,94 +592,3 @@ const $container: ViewStyle = {
787
592
  borderTopRightRadius: 26,
788
593
  borderTopLeftRadius: 26,
789
594
  };
790
-
791
- const styles = StyleSheet.create({
792
- container: {
793
- flex: 1,
794
- },
795
- camera: {
796
- flex: 1,
797
- },
798
- passportCameraFrame: {
799
- width: Dimensions.get('window').width,
800
- height: 260,
801
- overflow: 'hidden',
802
- backgroundColor: 'black',
803
- },
804
- buttonContainer: {
805
- flex: 0,
806
- flexDirection: 'column',
807
- justifyContent: 'center',
808
- alignItems: 'center',
809
- position: 'absolute',
810
- left: 0,
811
- right: 0,
812
- zIndex: 1000,
813
- },
814
- captureButton: {
815
- width: 56,
816
- height: 56,
817
- borderRadius: 35,
818
- backgroundColor: 'white',
819
- justifyContent: 'center',
820
- alignItems: 'center',
821
- },
822
- innerCaptureButton: {
823
- width: 60,
824
- height: 60,
825
- borderRadius: 30,
826
- backgroundColor: 'red',
827
- },
828
- overlay: {
829
- position: 'absolute',
830
- top: 0,
831
- left: 0,
832
- right: 0,
833
- bottom: 0,
834
- justifyContent: 'center',
835
- alignItems: 'center',
836
- },
837
- overlayTop: {
838
- flex: 1,
839
- backgroundColor: 'black',
840
- width: '100%',
841
- },
842
- overlayCenter: {
843
- flexDirection: 'row',
844
- },
845
- overlayLeft: {
846
- flex: 1,
847
- backgroundColor: 'black',
848
- },
849
- overlayRect: {
850
- width: '90%',
851
- height: 100,
852
- borderColor: 'white',
853
- borderWidth: 2,
854
- justifyContent: 'center',
855
- alignItems: 'center',
856
- },
857
- instructionText: {
858
- color: 'white',
859
- textAlign: 'center',
860
- paddingHorizontal: 10,
861
- fontSize: 14,
862
- position: 'absolute',
863
- top: -50,
864
- },
865
- overlayRight: {
866
- flex: 1,
867
- backgroundColor: 'black',
868
- },
869
- overlayBottom: {
870
- flex: 1,
871
- backgroundColor: 'black',
872
- width: '100%',
873
- },
874
- mrzContainer: {
875
- padding: 10,
876
- },
877
- mrzText: {
878
- fontSize: 16,
879
- },
880
- });