@trustchex/react-native-sdk 1.381.0 → 1.464.0

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 (204) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
  2. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +60 -13
  3. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
  4. package/ios/Camera/TrustchexCameraView.swift +10 -13
  5. package/ios/MLKit/MLKitModule.swift +1 -1
  6. package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
  7. package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
  8. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  9. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -32
  10. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +22 -4
  11. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
  12. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +126 -27
  13. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  14. package/lib/module/Screens/Dynamic/VideoCallScreen.js +678 -0
  15. package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
  16. package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
  17. package/lib/module/Screens/Static/ResultScreen.js +154 -34
  18. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +59 -51
  19. package/lib/module/Shared/Animations/recording.json +1 -0
  20. package/lib/module/Shared/Animations/video-call.json +1 -0
  21. package/lib/module/Shared/Components/DebugNavigationPanel.js +231 -67
  22. package/lib/module/Shared/Components/EIDScanner.js +213 -112
  23. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  24. package/lib/module/Shared/Components/IdentityDocumentCamera.js +77 -39
  25. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  26. package/lib/module/Shared/Components/NavigationManager.js +39 -19
  27. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  28. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  29. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  30. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  31. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  32. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  33. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  34. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  35. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  36. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  37. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  38. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  39. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  40. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  41. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  42. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  43. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  44. package/lib/module/Shared/Libs/SignalingClient.js +128 -0
  45. package/lib/module/Shared/Libs/analytics.utils.js +8 -0
  46. package/lib/module/Shared/Libs/contains.js +1 -40
  47. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  48. package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
  49. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  50. package/lib/module/Shared/Libs/http-client.js +9 -0
  51. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  52. package/lib/module/Shared/Libs/promise.utils.js +16 -2
  53. package/lib/module/Shared/Libs/status-bar.utils.js +23 -0
  54. package/lib/module/Shared/Services/DataUploadService.js +294 -0
  55. package/lib/module/Shared/Services/VideoSessionService.js +156 -0
  56. package/lib/module/Shared/Services/WebRTCService.js +510 -0
  57. package/lib/module/Shared/Types/analytics.types.js +4 -0
  58. package/lib/module/Translation/Resources/en.js +61 -2
  59. package/lib/module/Translation/Resources/tr.js +61 -2
  60. package/lib/module/Trustchex.js +64 -20
  61. package/lib/module/version.js +1 -1
  62. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
  63. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
  64. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
  65. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  66. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  67. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  68. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  69. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  70. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  71. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  72. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  73. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
  74. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
  75. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  76. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  77. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  78. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  79. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  82. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  83. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  84. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  85. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  86. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  87. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
  88. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  89. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  90. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  91. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  92. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  93. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  94. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  95. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  96. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  97. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  99. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  100. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  101. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  102. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  103. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  104. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  105. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  106. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  107. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  108. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  109. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  110. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  111. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  112. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  113. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  114. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  115. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
  116. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
  117. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  118. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  119. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  120. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  121. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  122. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  123. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  124. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  125. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  126. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  127. package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
  128. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
  129. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
  130. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
  131. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
  132. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
  133. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
  134. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
  135. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
  136. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +4 -0
  137. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  138. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +13 -1
  139. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  140. package/lib/typescript/src/Translation/Resources/en.d.ts +60 -1
  141. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  142. package/lib/typescript/src/Translation/Resources/tr.d.ts +60 -1
  143. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  144. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  145. package/lib/typescript/src/version.d.ts +1 -1
  146. package/package.json +35 -5
  147. package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
  148. package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
  149. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  150. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +58 -35
  151. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +27 -4
  152. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
  153. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +156 -27
  154. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  155. package/src/Screens/Dynamic/VideoCallScreen.tsx +766 -0
  156. package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
  157. package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
  158. package/src/Screens/Static/ResultScreen.tsx +235 -48
  159. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +67 -72
  160. package/src/Shared/Animations/recording.json +1 -0
  161. package/src/Shared/Animations/video-call.json +1 -0
  162. package/src/Shared/Components/DebugNavigationPanel.tsx +252 -51
  163. package/src/Shared/Components/EIDScanner.tsx +223 -116
  164. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  165. package/src/Shared/Components/IdentityDocumentCamera.tsx +224 -188
  166. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  167. package/src/Shared/Components/NavigationManager.tsx +41 -19
  168. package/src/Shared/Contexts/AppContext.ts +2 -0
  169. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  170. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  171. package/src/Shared/EIDReader/bacKey.ts +24 -8
  172. package/src/Shared/EIDReader/eidReader.ts +398 -12
  173. package/src/Shared/EIDReader/eidService.ts +49 -1
  174. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  175. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  176. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  177. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  178. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  179. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  180. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  181. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  182. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  183. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  184. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  185. package/src/Shared/Libs/SignalingClient.ts +189 -0
  186. package/src/Shared/Libs/analytics.utils.ts +8 -0
  187. package/src/Shared/Libs/contains.ts +0 -53
  188. package/src/Shared/Libs/country-display.utils.ts +55 -0
  189. package/src/Shared/Libs/crypto.utils.ts +2 -2
  190. package/src/Shared/Libs/deeplink.utils.ts +12 -1
  191. package/src/Shared/Libs/demo.utils.ts +10 -0
  192. package/src/Shared/Libs/http-client.ts +19 -1
  193. package/src/Shared/Libs/mrz.utils.ts +3 -2
  194. package/src/Shared/Libs/promise.utils.ts +16 -2
  195. package/src/Shared/Libs/status-bar.utils.ts +21 -0
  196. package/src/Shared/Services/DataUploadService.ts +395 -0
  197. package/src/Shared/Services/VideoSessionService.ts +190 -0
  198. package/src/Shared/Services/WebRTCService.ts +636 -0
  199. package/src/Shared/Types/analytics.types.ts +4 -0
  200. package/src/Shared/Types/identificationInfo.ts +16 -1
  201. package/src/Translation/Resources/en.ts +88 -3
  202. package/src/Translation/Resources/tr.ts +89 -3
  203. package/src/Trustchex.tsx +65 -19
  204. package/src/version.ts +1 -1
@@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next';
11
11
  import AppContext from "../Contexts/AppContext.js";
12
12
  import StyledButton from "./StyledButton.js";
13
13
  import LottieView from 'lottie-react-native';
14
+ import { getLocalizedCountryName } from "../Libs/country-display.utils.js";
14
15
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
15
16
  import { speak, resetLastMessage } from "../Libs/tts.utils.js";
16
17
  import { trackEIDScanStart, trackEIDScanComplete, trackEIDScanFailed } from "../Libs/analytics.utils.js";
@@ -68,12 +69,34 @@ const EIDScanner = ({
68
69
  const [isScanned, setIsScanned] = React.useState(false);
69
70
  const [hasGuideShown, setHasGuideShown] = React.useState(false);
70
71
  const {
71
- t
72
+ t,
73
+ i18n
72
74
  } = useTranslation();
73
75
  const appContext = React.useContext(AppContext);
76
+ const formatGender = useCallback(code => {
77
+ switch (code?.toUpperCase()) {
78
+ case 'M':
79
+ return t('eidScannerScreen.genderMale', 'Male');
80
+ case 'F':
81
+ return t('eidScannerScreen.genderFemale', 'Female');
82
+ default:
83
+ return t('eidScannerScreen.genderUnspecified', 'Unspecified');
84
+ }
85
+ }, [t]);
86
+ const formatDocumentType = useCallback(code => {
87
+ if (!code) return '';
88
+ const upper = code.toUpperCase();
89
+ if (upper.startsWith('P')) return t('eidScannerScreen.docTypePassport', 'Passport');
90
+ if (upper.startsWith('I')) return t('eidScannerScreen.docTypeID', 'ID Card');
91
+ if (upper.startsWith('A')) return t('eidScannerScreen.docTypeResidence', 'Residence Permit');
92
+ if (upper.startsWith('V')) return t('eidScannerScreen.docTypeVisa', 'Visa');
93
+ return code;
94
+ }, [t]);
95
+ const formatNationality = useCallback(code => {
96
+ return getLocalizedCountryName(code, i18n.language);
97
+ }, [i18n.language]);
74
98
  const [voiceGuidanceMessage, setVoiceGuidanceMessage] = useState();
75
99
  const [attemptNumber, setAttemptNumber] = useState(0);
76
- const lastVoiceGuidanceMessage = useRef('');
77
100
  const readNFC = useCallback(async () => {
78
101
  const startTime = Date.now();
79
102
  const currentAttempt = attemptNumber + 1;
@@ -82,7 +105,6 @@ const EIDScanner = ({
82
105
  setIsScanned(false);
83
106
  setProgress(0);
84
107
  setProgressStage('');
85
- lastVoiceGuidanceMessage.current = '';
86
108
  resetLastMessage();
87
109
 
88
110
  // Track EID scan start using analytics helper
@@ -241,8 +263,7 @@ const EIDScanner = ({
241
263
  }
242
264
  }, [isScanning, progress, pulseAnim]);
243
265
  useEffect(() => {
244
- if (voiceGuidanceMessage && appContext.currentWorkflowStep?.data?.voiceGuidanceActive && voiceGuidanceMessage !== lastVoiceGuidanceMessage.current) {
245
- lastVoiceGuidanceMessage.current = voiceGuidanceMessage;
266
+ if (voiceGuidanceMessage && appContext.currentWorkflowStep?.data?.voiceGuidanceActive) {
246
267
  speak(voiceGuidanceMessage, true);
247
268
  }
248
269
  }, [voiceGuidanceMessage, appContext.currentWorkflowStep?.data?.voiceGuidanceActive]);
@@ -299,101 +320,119 @@ const EIDScanner = ({
299
320
  children: t('eidScannerScreen.startScanning')
300
321
  })]
301
322
  }) : /*#__PURE__*/_jsxs(_Fragment, {
302
- children: [isScanned && documentFaceImage && documentFaceImageMimeType === 'image/jpeg' && /*#__PURE__*/_jsx(Image, {
303
- source: {
304
- uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`
305
- },
306
- style: styles.faceImage
307
- }), isScanned && documentFaceImage &&
308
- // TODO Add support for jp2 images
309
- documentFaceImageMimeType === 'image/jp2' && /*#__PURE__*/_jsx(Text, {
310
- style: styles.mainText,
311
- children: t('eidScannerScreen.imageCannotBeShown')
312
- }), isScanned && !documentFaceImage && /*#__PURE__*/_jsx(Text, {
313
- style: styles.mainText,
314
- children: t('eidScannerScreen.faceImageNotFound')
315
- }), isScanned && documentMRZInfo && /*#__PURE__*/_jsxs(View, {
316
- style: styles.mrzInfo,
323
+ children: [isScanned && /*#__PURE__*/_jsxs(View, {
324
+ style: styles.resultCard,
317
325
  children: [/*#__PURE__*/_jsxs(View, {
318
- style: styles.mrzInfoItem,
319
- children: [/*#__PURE__*/_jsxs(Text, {
320
- style: styles.mrzInfoLabel,
321
- children: [t('eidScannerScreen.documentCode'), ":"]
322
- }), /*#__PURE__*/_jsx(Text, {
323
- style: styles.mrzInfoText,
324
- children: documentMRZInfo.getDocumentCode()
325
- })]
326
- }), /*#__PURE__*/_jsxs(View, {
327
- style: styles.mrzInfoItem,
328
- children: [/*#__PURE__*/_jsxs(Text, {
329
- style: styles.mrzInfoLabel,
330
- children: [t('eidScannerScreen.nationality'), ":"]
331
- }), /*#__PURE__*/_jsx(Text, {
332
- style: styles.mrzInfoText,
333
- children: documentMRZInfo.getNationality()
334
- })]
335
- }), /*#__PURE__*/_jsxs(View, {
336
- style: styles.mrzInfoItem,
337
- children: [/*#__PURE__*/_jsxs(Text, {
338
- style: styles.mrzInfoLabel,
339
- children: [t('eidScannerScreen.personalNumber'), ":"]
340
- }), /*#__PURE__*/_jsx(Text, {
341
- style: styles.mrzInfoText,
342
- children: documentMRZInfo.getOptionalData1()
343
- })]
344
- }), /*#__PURE__*/_jsxs(View, {
345
- style: styles.mrzInfoItem,
346
- children: [/*#__PURE__*/_jsxs(Text, {
347
- style: styles.mrzInfoLabel,
348
- children: [t('eidScannerScreen.documentNumber'), ":"]
349
- }), /*#__PURE__*/_jsx(Text, {
350
- style: styles.mrzInfoText,
351
- children: documentMRZInfo.getDocumentNumber()
352
- })]
353
- }), /*#__PURE__*/_jsxs(View, {
354
- style: styles.mrzInfoItem,
355
- children: [/*#__PURE__*/_jsxs(Text, {
356
- style: styles.mrzInfoLabel,
357
- children: [t('eidScannerScreen.name'), ":"]
358
- }), /*#__PURE__*/_jsx(Text, {
359
- style: styles.mrzInfoText,
360
- children: documentMRZInfo.getSecondaryIdentifier()
361
- })]
362
- }), /*#__PURE__*/_jsxs(View, {
363
- style: styles.mrzInfoItem,
364
- children: [/*#__PURE__*/_jsxs(Text, {
365
- style: styles.mrzInfoLabel,
366
- children: [t('eidScannerScreen.surname'), ":"]
367
- }), /*#__PURE__*/_jsx(Text, {
368
- style: styles.mrzInfoText,
369
- children: documentMRZInfo.getPrimaryIdentifier()
326
+ style: styles.resultHeader,
327
+ children: [documentFaceImage && (documentFaceImageMimeType === 'image/jpeg' || documentFaceImageMimeType === 'image/png') ? /*#__PURE__*/_jsx(Image, {
328
+ source: {
329
+ uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`
330
+ },
331
+ style: styles.faceImage,
332
+ resizeMode: "cover"
333
+ }) : /*#__PURE__*/_jsx(View, {
334
+ style: styles.faceImagePlaceholder,
335
+ children: /*#__PURE__*/_jsx(Text, {
336
+ style: styles.faceImagePlaceholderText,
337
+ children: "?"
338
+ })
339
+ }), documentMRZInfo && /*#__PURE__*/_jsxs(View, {
340
+ style: styles.resultHeaderInfo,
341
+ children: [/*#__PURE__*/_jsxs(Text, {
342
+ style: styles.fullName,
343
+ numberOfLines: 2,
344
+ children: [documentMRZInfo.getSecondaryIdentifier(), ' ', documentMRZInfo.getPrimaryIdentifier()]
345
+ }), /*#__PURE__*/_jsxs(Text, {
346
+ style: styles.subInfo,
347
+ children: [formatNationality(documentMRZInfo.getNationality()), " \xB7", ' ', formatDocumentType(documentMRZInfo.getDocumentCode())]
348
+ }), /*#__PURE__*/_jsxs(Text, {
349
+ style: styles.subInfo,
350
+ children: [formatGender(documentMRZInfo.getGender().getStrCode()), " \xB7", ' ', formatDate(documentMRZInfo.getDateOfBirth())]
351
+ })]
370
352
  })]
371
- }), /*#__PURE__*/_jsxs(View, {
372
- style: styles.mrzInfoItem,
373
- children: [/*#__PURE__*/_jsxs(Text, {
374
- style: styles.mrzInfoLabel,
375
- children: [t('eidScannerScreen.birthDate'), ":"]
376
- }), /*#__PURE__*/_jsx(Text, {
377
- style: styles.mrzInfoText,
378
- children: formatDate(documentMRZInfo.getDateOfBirth())
379
- })]
380
- }), /*#__PURE__*/_jsxs(View, {
381
- style: styles.mrzInfoItem,
382
- children: [/*#__PURE__*/_jsxs(Text, {
383
- style: styles.mrzInfoLabel,
384
- children: [t('eidScannerScreen.gender'), ":"]
385
- }), /*#__PURE__*/_jsx(Text, {
386
- style: styles.mrzInfoText,
387
- children: documentMRZInfo.getGender().getStrCode()
388
- })]
389
- }), /*#__PURE__*/_jsxs(View, {
390
- style: styles.mrzInfoItem,
391
- children: [/*#__PURE__*/_jsxs(Text, {
392
- style: styles.mrzInfoLabel,
393
- children: [t('eidScannerScreen.expirationDate'), ":"]
394
- }), /*#__PURE__*/_jsx(Text, {
395
- style: styles.mrzInfoText,
396
- children: formatDate(documentMRZInfo.getDateOfExpiry())
353
+ }), documentMRZInfo && /*#__PURE__*/_jsxs(View, {
354
+ style: styles.mrzInfo,
355
+ children: [/*#__PURE__*/_jsxs(View, {
356
+ style: styles.mrzInfoItem,
357
+ children: [/*#__PURE__*/_jsx(Text, {
358
+ style: styles.mrzInfoLabel,
359
+ children: t('eidScannerScreen.name')
360
+ }), /*#__PURE__*/_jsx(Text, {
361
+ style: styles.mrzInfoText,
362
+ children: documentMRZInfo.getSecondaryIdentifier()
363
+ })]
364
+ }), /*#__PURE__*/_jsxs(View, {
365
+ style: styles.mrzInfoItem,
366
+ children: [/*#__PURE__*/_jsx(Text, {
367
+ style: styles.mrzInfoLabel,
368
+ children: t('eidScannerScreen.surname')
369
+ }), /*#__PURE__*/_jsx(Text, {
370
+ style: styles.mrzInfoText,
371
+ children: documentMRZInfo.getPrimaryIdentifier()
372
+ })]
373
+ }), /*#__PURE__*/_jsxs(View, {
374
+ style: styles.mrzInfoItem,
375
+ children: [/*#__PURE__*/_jsx(Text, {
376
+ style: styles.mrzInfoLabel,
377
+ children: t('eidScannerScreen.documentCode')
378
+ }), /*#__PURE__*/_jsx(Text, {
379
+ style: styles.mrzInfoText,
380
+ children: formatDocumentType(documentMRZInfo.getDocumentCode())
381
+ })]
382
+ }), /*#__PURE__*/_jsxs(View, {
383
+ style: styles.mrzInfoItem,
384
+ children: [/*#__PURE__*/_jsx(Text, {
385
+ style: styles.mrzInfoLabel,
386
+ children: t('eidScannerScreen.documentNumber')
387
+ }), /*#__PURE__*/_jsx(Text, {
388
+ style: styles.mrzInfoText,
389
+ children: documentMRZInfo.getDocumentNumber()
390
+ })]
391
+ }), /*#__PURE__*/_jsxs(View, {
392
+ style: styles.mrzInfoItem,
393
+ children: [/*#__PURE__*/_jsx(Text, {
394
+ style: styles.mrzInfoLabel,
395
+ children: t('eidScannerScreen.personalNumber')
396
+ }), /*#__PURE__*/_jsx(Text, {
397
+ style: styles.mrzInfoText,
398
+ children: documentMRZInfo.getOptionalData1()
399
+ })]
400
+ }), /*#__PURE__*/_jsxs(View, {
401
+ style: styles.mrzInfoItem,
402
+ children: [/*#__PURE__*/_jsx(Text, {
403
+ style: styles.mrzInfoLabel,
404
+ children: t('eidScannerScreen.nationality')
405
+ }), /*#__PURE__*/_jsx(Text, {
406
+ style: styles.mrzInfoText,
407
+ children: formatNationality(documentMRZInfo.getNationality())
408
+ })]
409
+ }), /*#__PURE__*/_jsxs(View, {
410
+ style: styles.mrzInfoItem,
411
+ children: [/*#__PURE__*/_jsx(Text, {
412
+ style: styles.mrzInfoLabel,
413
+ children: t('eidScannerScreen.gender')
414
+ }), /*#__PURE__*/_jsx(Text, {
415
+ style: styles.mrzInfoText,
416
+ children: formatGender(documentMRZInfo.getGender().getStrCode())
417
+ })]
418
+ }), /*#__PURE__*/_jsxs(View, {
419
+ style: styles.mrzInfoItem,
420
+ children: [/*#__PURE__*/_jsx(Text, {
421
+ style: styles.mrzInfoLabel,
422
+ children: t('eidScannerScreen.birthDate')
423
+ }), /*#__PURE__*/_jsx(Text, {
424
+ style: styles.mrzInfoText,
425
+ children: formatDate(documentMRZInfo.getDateOfBirth())
426
+ })]
427
+ }), /*#__PURE__*/_jsxs(View, {
428
+ style: styles.mrzInfoItem,
429
+ children: [/*#__PURE__*/_jsx(Text, {
430
+ style: styles.mrzInfoLabel,
431
+ children: t('eidScannerScreen.expirationDate')
432
+ }), /*#__PURE__*/_jsx(Text, {
433
+ style: styles.mrzInfoText,
434
+ children: formatDate(documentMRZInfo.getDateOfExpiry())
435
+ })]
397
436
  })]
398
437
  })]
399
438
  }), !hasNfc && /*#__PURE__*/_jsx(Text, {
@@ -426,7 +465,14 @@ const EIDScanner = ({
426
465
  }) || documentType === 'UNKNOWN' && /*#__PURE__*/_jsx(Text, {
427
466
  style: styles.mainText,
428
467
  children: t('eidScannerScreen.placeDocumentOnNFC')
429
- })), hasNfc && isEnabled && isScanned && /*#__PURE__*/_jsx(View, {
468
+ })), hasNfc && isEnabled && !isScanning && !isScanned && /*#__PURE__*/_jsx(View, {
469
+ style: styles.buttonsContainer,
470
+ children: /*#__PURE__*/_jsx(StyledButton, {
471
+ mode: "contained",
472
+ onPress: readNFC,
473
+ children: t('eidScannerScreen.startScanning')
474
+ })
475
+ }), hasNfc && isEnabled && isScanned && /*#__PURE__*/_jsx(View, {
430
476
  style: styles.buttonsContainer,
431
477
  children: /*#__PURE__*/_jsx(StyledButton, {
432
478
  mode: "contained",
@@ -511,31 +557,86 @@ const styles = StyleSheet.create({
511
557
  color: 'black',
512
558
  textAlign: 'center'
513
559
  },
514
- faceImage: {
515
- width: 150,
516
- height: 150,
517
- borderRadius: 150,
518
- borderColor: 'lightgray',
560
+ resultCard: {
561
+ backgroundColor: '#fff',
562
+ borderRadius: 16,
563
+ padding: 16,
564
+ gap: 16,
565
+ shadowColor: '#000',
566
+ shadowOffset: {
567
+ width: 0,
568
+ height: 2
569
+ },
570
+ shadowOpacity: 0.08,
571
+ shadowRadius: 8,
572
+ elevation: 3,
519
573
  borderWidth: 1,
520
- alignSelf: 'center'
574
+ borderColor: '#f0f0f0'
575
+ },
576
+ resultHeader: {
577
+ flexDirection: 'row',
578
+ gap: 14,
579
+ alignItems: 'flex-start'
580
+ },
581
+ resultHeaderInfo: {
582
+ flex: 1,
583
+ gap: 4,
584
+ justifyContent: 'center'
585
+ },
586
+ fullName: {
587
+ fontSize: 17,
588
+ fontWeight: '700',
589
+ color: '#111',
590
+ letterSpacing: 0.2
591
+ },
592
+ subInfo: {
593
+ fontSize: 13,
594
+ color: '#666',
595
+ fontWeight: '500'
596
+ },
597
+ faceImage: {
598
+ width: 90,
599
+ height: 115,
600
+ borderRadius: 10,
601
+ overflow: 'hidden'
602
+ },
603
+ faceImagePlaceholder: {
604
+ width: 90,
605
+ height: 115,
606
+ borderRadius: 10,
607
+ backgroundColor: '#f5f5f5',
608
+ alignItems: 'center',
609
+ justifyContent: 'center'
610
+ },
611
+ faceImagePlaceholderText: {
612
+ fontSize: 32,
613
+ color: '#ccc'
521
614
  },
522
615
  mrzInfo: {
523
616
  flexDirection: 'column',
524
- width: '100%'
617
+ width: '100%',
618
+ gap: 2
525
619
  },
526
620
  mrzInfoItem: {
527
621
  flexDirection: 'row',
528
622
  justifyContent: 'space-between',
529
- borderBottomColor: 'lightgray',
530
- borderBottomWidth: 1,
531
- paddingVertical: 5
623
+ alignItems: 'center',
624
+ paddingVertical: 8,
625
+ borderBottomColor: '#f0f0f0',
626
+ borderBottomWidth: 1
532
627
  },
533
628
  mrzInfoLabel: {
534
- color: 'black',
535
- fontWeight: 'bold'
629
+ color: '#888',
630
+ fontSize: 13,
631
+ fontWeight: '500'
536
632
  },
537
633
  mrzInfoText: {
538
- color: 'black'
634
+ color: '#111',
635
+ fontSize: 13,
636
+ fontWeight: '600',
637
+ textAlign: 'right',
638
+ flexShrink: 1,
639
+ marginLeft: 8
539
640
  },
540
641
  guide: {
541
642
  flex: 1,
@@ -8,7 +8,7 @@
8
8
  * KEY DISTINGUISHING FEATURES:
9
9
  * =============================
10
10
  * PASSPORT: Has MRZ on FRONT with pattern P<XXX (e.g., P<TUR, P<USA)
11
- * ID CARD: Has NO MRZ on front; MRZ only on BACK with code 'I'
11
+ * ID CARD: Has NO MRZ on front; MRZ only on BACK with code starting with I, A, or C
12
12
  *
13
13
  * IMPORTANT: Both passports and ID cards have signature sections!
14
14
  * Signature presence is NOT a distinguishing feature between document types.
@@ -31,6 +31,8 @@
31
31
  */
32
32
 
33
33
  import { PASSPORT_MRZ_PATTERN } from "./IdentityDocumentCamera.constants.js";
34
+ import { isIDCardDocumentCode } from "./IdentityDocumentCamera.utils.js";
35
+
34
36
  /**
35
37
  * After this many retry attempts for ID_FRONT flow, proceed with face detection alone
36
38
  * even if signature text OCR fails. This prevents blocking users due to OCR limitations.
@@ -178,10 +180,10 @@ export function handleIDFrontFlow(faces, text, mrzText, mrzFields, retryCount) {
178
180
  }
179
181
 
180
182
  // ============================================================================
181
- // STEP 4: Final MRZ code check (if present, must be 'I' not 'P')
183
+ // STEP 4: Final MRZ code check (if present, must be an ID card code, not P)
182
184
  // ============================================================================
183
185
  // ID cards should NOT have MRZ on front, but if detected, verify it's not passport
184
- if (mrzFields?.documentCode && mrzFields.documentCode !== 'I') {
186
+ if (mrzFields?.documentCode && !isIDCardDocumentCode(mrzFields.documentCode)) {
185
187
  return {
186
188
  shouldProceed: false,
187
189
  reason: `MRZ shows unexpected document code: ${mrzFields.documentCode}`,
@@ -94,6 +94,7 @@ const IdentityDocumentCamera = ({
94
94
  const isHologramDetectionInProgress = useRef(false); // Prevent concurrent hologram detection calls
95
95
  const isCompletionCallbackInvoked = useRef(false); // Prevent multiple callback invocations when COMPLETED
96
96
  const isStepTransitionInProgress = useRef(false); // Prevent duplicate step transitions from rapid frames
97
+ const isCompletionAnnouncementSpoken = useRef(false); // Prevent duplicate "scan completed" voice guidance
97
98
 
98
99
  const faceDetectionErrorCount = useRef(0);
99
100
  const brightnessHistory = useRef([]);
@@ -101,7 +102,6 @@ const IdentityDocumentCamera = ({
101
102
  const faceImages = useRef([]);
102
103
  const hologramImageCountRef = useRef(0);
103
104
  const [hologramImageCount, setHologramImageCount] = useState(0);
104
- const lastVoiceGuidanceMessage = useRef('');
105
105
  const [latestHologramFaceImage, setLatestHologramFaceImage] = useState(undefined);
106
106
  const lastFacePosition = useRef(null);
107
107
  const [documentPlaneBounds, setDocumentPlaneBounds] = useState(null);
@@ -135,6 +135,7 @@ const IdentityDocumentCamera = ({
135
135
  setIsActive(true);
136
136
  isCompletionCallbackInvoked.current = false; // Reset callback flag when starting new scan
137
137
  isStepTransitionInProgress.current = false;
138
+ isCompletionAnnouncementSpoken.current = false;
138
139
  } else {
139
140
  setIsActive(false);
140
141
  faceImages.current = [];
@@ -148,9 +149,9 @@ const IdentityDocumentCamera = ({
148
149
  lastValidMRZFields.current = null;
149
150
  validMRZConsecutiveCount.current = 0;
150
151
  cachedBarcode.current = null;
151
- lastVoiceGuidanceMessage.current = '';
152
152
  isCompletionCallbackInvoked.current = false;
153
153
  isStepTransitionInProgress.current = false;
154
+ isCompletionAnnouncementSpoken.current = false;
154
155
  // Clear all captured image states from previous scan
155
156
  setCurrentFaceImage(undefined);
156
157
  setCurrentHologramImage(undefined);
@@ -163,9 +164,9 @@ const IdentityDocumentCamera = ({
163
164
  hologramImageCountRef.current = 0;
164
165
  setHologramImageCount(0);
165
166
  setLatestHologramFaceImage(undefined);
166
- lastVoiceGuidanceMessage.current = '';
167
167
  isCompletionCallbackInvoked.current = false; // Reset callback flag on unmount
168
168
  isStepTransitionInProgress.current = false;
169
+ isCompletionAnnouncementSpoken.current = false;
169
170
  // Clear all captured image states on unmount
170
171
  setCurrentFaceImage(undefined);
171
172
  setCurrentHologramImage(undefined);
@@ -176,9 +177,17 @@ const IdentityDocumentCamera = ({
176
177
  useEffect(() => {
177
178
  if (!hasGuideShown || !appContext.currentWorkflowStep?.data?.voiceGuidanceActive) return;
178
179
  const message = getStatusMessage(nextStep, status, detectedDocumentType, isBrightnessLow, isFrameBlurry, allElementsDetected, elementsOutsideScanArea, t);
179
- if (message && message !== lastVoiceGuidanceMessage.current) {
180
- lastVoiceGuidanceMessage.current = message;
181
- speak(message, true);
180
+ if (message) {
181
+ if (nextStep === 'COMPLETED') {
182
+ if (isCompletionAnnouncementSpoken.current) {
183
+ return;
184
+ }
185
+ isCompletionAnnouncementSpoken.current = true;
186
+ } else {
187
+ isCompletionAnnouncementSpoken.current = false;
188
+ }
189
+ const shouldBypassInterval = nextStep !== 'COMPLETED';
190
+ speak(message, shouldBypassInterval);
182
191
  }
183
192
  }, [appContext.currentWorkflowStep?.data?.voiceGuidanceActive, hasGuideShown, isBrightnessLow, isFrameBlurry, nextStep, status, detectedDocumentType, allElementsDetected, elementsOutsideScanArea, t]);
184
193
  useEffect(() => {
@@ -207,6 +216,20 @@ const IdentityDocumentCamera = ({
207
216
  isStepTransitionInProgress.current = false;
208
217
  }, [nextStep]);
209
218
 
219
+ // Update status bar based on guide visibility
220
+ useEffect(() => {
221
+ if (hasGuideShown) {
222
+ // Scanning camera view - use light icons on dark background
223
+ StatusBar.setBarStyle('light-content', true);
224
+ } else {
225
+ // Guide screen with white background - use dark icons
226
+ StatusBar.setBarStyle('dark-content', true);
227
+ if (Platform.OS === 'android') {
228
+ StatusBar.setBackgroundColor('#ffffff', true);
229
+ }
230
+ }
231
+ }, [hasGuideShown]);
232
+
210
233
  // Error flash animation - flash red text when wrong side detected
211
234
  useEffect(() => {
212
235
  if (status === 'INCORRECT') {
@@ -1462,6 +1485,53 @@ const IdentityDocumentCamera = ({
1462
1485
  const handleCameraError = useCallback(event => {
1463
1486
  console.error('Camera error:', event.nativeEvent.error);
1464
1487
  }, []);
1488
+
1489
+ // Show the guide immediately — the guide is pure UI and does not need
1490
+ // camera access. Permission is requested in the background (useEffect) so
1491
+ // it will usually be resolved by the time the user taps "Let's Go".
1492
+ // This avoids a white/blank screen while the async permission check runs.
1493
+ if (!hasGuideShown) {
1494
+ return /*#__PURE__*/_jsxs(View, {
1495
+ style: StyleSheet.absoluteFill,
1496
+ children: [/*#__PURE__*/_jsx(StatusBar, {
1497
+ barStyle: "dark-content",
1498
+ backgroundColor: "#ffffff",
1499
+ translucent: true
1500
+ }), /*#__PURE__*/_jsxs(SafeAreaView, {
1501
+ style: styles.guide,
1502
+ children: [/*#__PURE__*/_jsx(LottieView, {
1503
+ source: require('../../Shared/Animations/id-or-passport.json'),
1504
+ style: styles.guideAnimation,
1505
+ loop: true,
1506
+ autoPlay: true
1507
+ }), /*#__PURE__*/_jsx(TextView, {
1508
+ style: styles.guideHeader,
1509
+ children: t('identityDocumentCamera.guideHeader')
1510
+ }), /*#__PURE__*/_jsxs(View, {
1511
+ style: styles.guidePoints,
1512
+ children: [/*#__PURE__*/_jsx(TextView, {
1513
+ style: styles.guideText,
1514
+ children: t('identityDocumentCamera.guideText')
1515
+ }), /*#__PURE__*/_jsxs(TextView, {
1516
+ style: styles.guideText,
1517
+ children: ["\u2022 ", t('identityDocumentCamera.guidePoint1')]
1518
+ }), /*#__PURE__*/_jsxs(TextView, {
1519
+ style: styles.guideText,
1520
+ children: ["\u2022 ", t('identityDocumentCamera.guidePoint2')]
1521
+ }), /*#__PURE__*/_jsxs(TextView, {
1522
+ style: styles.guideText,
1523
+ children: ["\u2022 ", t('identityDocumentCamera.guidePoint3')]
1524
+ })]
1525
+ }), /*#__PURE__*/_jsx(StyledButton, {
1526
+ mode: "contained",
1527
+ onPress: () => {
1528
+ setHasGuideShown(true);
1529
+ },
1530
+ children: t('general.letsGo')
1531
+ })]
1532
+ })]
1533
+ });
1534
+ }
1465
1535
  if (!permissionsRequested) {
1466
1536
  return /*#__PURE__*/_jsxs(SafeAreaView, {
1467
1537
  style: styles.permissionContainer,
@@ -1496,39 +1566,7 @@ const IdentityDocumentCamera = ({
1496
1566
  barStyle: "light-content",
1497
1567
  backgroundColor: "transparent",
1498
1568
  translucent: true
1499
- }), !hasGuideShown ? /*#__PURE__*/_jsxs(SafeAreaView, {
1500
- style: styles.guide,
1501
- children: [/*#__PURE__*/_jsx(LottieView, {
1502
- source: require('../../Shared/Animations/id-or-passport.json'),
1503
- style: styles.guideAnimation,
1504
- loop: true,
1505
- autoPlay: true
1506
- }), /*#__PURE__*/_jsx(TextView, {
1507
- style: styles.guideHeader,
1508
- children: t('identityDocumentCamera.guideHeader')
1509
- }), /*#__PURE__*/_jsxs(View, {
1510
- style: styles.guidePoints,
1511
- children: [/*#__PURE__*/_jsx(TextView, {
1512
- style: styles.guideText,
1513
- children: t('identityDocumentCamera.guideText')
1514
- }), /*#__PURE__*/_jsxs(TextView, {
1515
- style: styles.guideText,
1516
- children: ["\u2022 ", t('identityDocumentCamera.guidePoint1')]
1517
- }), /*#__PURE__*/_jsxs(TextView, {
1518
- style: styles.guideText,
1519
- children: ["\u2022 ", t('identityDocumentCamera.guidePoint2')]
1520
- }), /*#__PURE__*/_jsxs(TextView, {
1521
- style: styles.guideText,
1522
- children: ["\u2022 ", t('identityDocumentCamera.guidePoint3')]
1523
- })]
1524
- }), /*#__PURE__*/_jsx(StyledButton, {
1525
- mode: "contained",
1526
- onPress: () => {
1527
- setHasGuideShown(true);
1528
- },
1529
- children: t('general.letsGo')
1530
- })]
1531
- }) : /*#__PURE__*/_jsxs(_Fragment, {
1569
+ }), /*#__PURE__*/_jsxs(_Fragment, {
1532
1570
  children: [/*#__PURE__*/_jsx(TrustchexCamera, {
1533
1571
  ref: cameraRef,
1534
1572
  style: StyleSheet.absoluteFill,
@@ -4,6 +4,15 @@ import { Dimensions } from 'react-native';
4
4
  import { SIGNATURE_REGEX, PASSPORT_MRZ_PATTERN } from "./IdentityDocumentCamera.constants.js";
5
5
  import { debugLog, isDebugEnabled } from "../Libs/debug.utils.js";
6
6
 
7
+ /**
8
+ * Checks if a document code represents an ID card (TD1/TD2).
9
+ * Per ICAO 9303, valid ID card codes start with 'I', 'A', or 'C'.
10
+ */
11
+ export function isIDCardDocumentCode(code) {
12
+ if (!code) return false;
13
+ return code.startsWith('I') || code.startsWith('A') || code.startsWith('C');
14
+ }
15
+
7
16
  /**
8
17
  * Frame-to-screen coordinate transform using FILL_CENTER scaling
9
18
  */
@@ -175,8 +184,8 @@ export function detectDocumentType(faces, ocrText, mrzFields, frameWidth, mrzTex
175
184
  });
176
185
  }
177
186
 
178
- // ID Back: no face + ID MRZ
179
- if (faces.length === 0 && mrzFields?.documentCode === 'I') {
187
+ // ID Back: no face + ID MRZ (codes starting with I, A, or C per ICAO 9303)
188
+ if (faces.length === 0 && isIDCardDocumentCode(mrzFields?.documentCode)) {
180
189
  return 'ID_BACK';
181
190
  }
182
191
 
@@ -205,8 +214,8 @@ export function detectDocumentType(faces, ocrText, mrzFields, frameWidth, mrzTex
205
214
  // Filter to card-sized faces only (min 5% of frame width to exclude tiny background faces)
206
215
  const cardSizedFaces = frameWidth ? faces.filter(face => face.bounds.width >= frameWidth * 0.05 && face.bounds.height >= frameWidth * 0.05) : faces;
207
216
  if (cardSizedFaces.length > 0) {
208
- // If we have MRZ code 'I', it's definitely an ID card
209
- if (mrzFields?.documentCode === 'I') {
217
+ // If we have an ID card MRZ code (I, A, or C), it's definitely an ID card
218
+ if (isIDCardDocumentCode(mrzFields?.documentCode)) {
210
219
  return 'ID_FRONT';
211
220
  }
212
221