@trustchex/react-native-sdk 1.409.0 → 1.472.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 (171) 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 +102 -4
  3. package/ios/Camera/TrustchexCameraView.swift +151 -18
  4. package/ios/Permission/PermissionModule.m +22 -0
  5. package/ios/Permission/PermissionModule.swift +67 -0
  6. package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
  7. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +1 -4
  8. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +17 -4
  9. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +125 -25
  10. package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
  11. package/lib/module/Screens/Dynamic/VideoCallScreen.js +10 -13
  12. package/lib/module/Screens/Static/ResultScreen.js +131 -22
  13. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +8 -0
  14. package/lib/module/Shared/Animations/recording.json +1 -0
  15. package/lib/module/Shared/Components/DebugNavigationPanel.js +69 -71
  16. package/lib/module/Shared/Components/EIDScanner.js +222 -115
  17. package/lib/module/Shared/Components/FaceCamera.js +14 -8
  18. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
  19. package/lib/module/Shared/Components/IdentityDocumentCamera.js +60 -42
  20. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
  21. package/lib/module/Shared/Components/NavigationManager.js +24 -16
  22. package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
  23. package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
  24. package/lib/module/Shared/EIDReader/bacKey.js +16 -2
  25. package/lib/module/Shared/EIDReader/eidReader.js +354 -13
  26. package/lib/module/Shared/EIDReader/eidService.js +25 -1
  27. package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
  28. package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
  29. package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
  30. package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
  31. package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
  32. package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
  33. package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
  34. package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
  35. package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
  36. package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
  37. package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
  38. package/lib/module/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  39. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  40. package/lib/module/Shared/Libs/contains.js +1 -40
  41. package/lib/module/Shared/Libs/country-display.utils.js +34 -0
  42. package/lib/module/Shared/Libs/demo.utils.js +8 -0
  43. package/lib/module/Shared/Libs/index.js +20 -0
  44. package/lib/module/Shared/Libs/mrz.utils.js +3 -2
  45. package/lib/module/Shared/Libs/permissions.utils.js +199 -0
  46. package/lib/module/Shared/Libs/status-bar.utils.js +4 -2
  47. package/lib/module/Shared/Types/analytics.types.js +2 -0
  48. package/lib/module/Translation/Resources/en.js +42 -2
  49. package/lib/module/Translation/Resources/tr.js +42 -2
  50. package/lib/module/Trustchex.js +54 -20
  51. package/lib/module/version.js +1 -1
  52. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
  53. package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
  54. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  55. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts +3 -0
  58. package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
  59. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -1
  60. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  61. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  62. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  64. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  65. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
  68. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
  69. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
  70. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
  72. package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
  73. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
  74. package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
  75. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
  76. package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
  77. package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
  78. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
  79. package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
  82. package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
  83. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
  84. package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
  85. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
  86. package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
  87. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
  88. package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
  89. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
  90. package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
  91. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
  92. package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
  93. package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
  94. package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
  95. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
  96. package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
  97. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  98. package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
  99. package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
  100. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
  101. package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
  102. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  103. package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
  104. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  105. package/lib/typescript/src/Shared/Libs/index.d.ts +20 -0
  106. package/lib/typescript/src/Shared/Libs/index.d.ts.map +1 -0
  107. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  108. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts +58 -0
  109. package/lib/typescript/src/Shared/Libs/permissions.utils.d.ts.map +1 -0
  110. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -1
  111. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  112. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  113. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +10 -1
  114. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  115. package/lib/typescript/src/Translation/Resources/en.d.ts +41 -1
  116. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  117. package/lib/typescript/src/Translation/Resources/tr.d.ts +41 -1
  118. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  119. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  120. package/lib/typescript/src/version.d.ts +1 -1
  121. package/package.json +7 -4
  122. package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
  123. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +1 -4
  124. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +21 -4
  125. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +165 -24
  126. package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
  127. package/src/Screens/Dynamic/VideoCallScreen.tsx +11 -20
  128. package/src/Screens/Static/ResultScreen.tsx +187 -31
  129. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +9 -0
  130. package/src/Shared/Animations/recording.json +1 -0
  131. package/src/Shared/Components/DebugNavigationPanel.tsx +73 -48
  132. package/src/Shared/Components/EIDScanner.tsx +280 -119
  133. package/src/Shared/Components/FaceCamera.tsx +19 -16
  134. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
  135. package/src/Shared/Components/IdentityDocumentCamera.tsx +206 -191
  136. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
  137. package/src/Shared/Components/NavigationManager.tsx +27 -18
  138. package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
  139. package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
  140. package/src/Shared/EIDReader/bacKey.ts +24 -8
  141. package/src/Shared/EIDReader/eidReader.ts +398 -12
  142. package/src/Shared/EIDReader/eidService.ts +49 -1
  143. package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
  144. package/src/Shared/EIDReader/paceInfo.ts +159 -0
  145. package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
  146. package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
  147. package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
  148. package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
  149. package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
  150. package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
  151. package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
  152. package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
  153. package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
  154. package/src/Shared/Libs/PERMISSIONS_MANAGER.md +268 -0
  155. package/src/Shared/Libs/analytics.utils.ts +4 -0
  156. package/src/Shared/Libs/contains.ts +0 -53
  157. package/src/Shared/Libs/country-display.utils.ts +55 -0
  158. package/src/Shared/Libs/crypto.utils.ts +2 -2
  159. package/src/Shared/Libs/demo.utils.ts +10 -0
  160. package/src/Shared/Libs/http-client.ts +12 -4
  161. package/src/Shared/Libs/index.ts +63 -0
  162. package/src/Shared/Libs/mrz.utils.ts +3 -2
  163. package/src/Shared/Libs/permissions.utils.ts +251 -0
  164. package/src/Shared/Libs/status-bar.utils.ts +4 -2
  165. package/src/Shared/Services/VideoSessionService.ts +1 -1
  166. package/src/Shared/Types/analytics.types.ts +2 -0
  167. package/src/Shared/Types/identificationInfo.ts +11 -0
  168. package/src/Translation/Resources/en.ts +64 -3
  169. package/src/Translation/Resources/tr.ts +63 -3
  170. package/src/Trustchex.tsx +53 -17
  171. package/src/version.ts +1 -1
@@ -3,6 +3,8 @@ import { Alert, View, Text, Image, StyleSheet, Animated } from 'react-native';
3
3
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
4
4
  import NFCManager from 'react-native-nfc-manager';
5
5
  import DeviceInfo from 'react-native-device-info';
6
+ import PermissionManager from '../Libs/permissions.utils';
7
+ import { debugError } from '../Libs/debug.utils';
6
8
  import { MRZInfo } from '../EIDReader/lds/icao/mrzInfo';
7
9
  import { eidReader } from '../EIDReader/eidReader';
8
10
  import NativeProgressBar from './NativeProgressBar';
@@ -11,6 +13,7 @@ import { useTranslation } from 'react-i18next';
11
13
  import AppContext from '../Contexts/AppContext';
12
14
  import StyledButton from './StyledButton';
13
15
  import LottieView from 'lottie-react-native';
16
+ import { getLocalizedCountryName } from '../Libs/country-display.utils';
14
17
  import { useKeepAwake } from '../Libs/native-keep-awake.utils';
15
18
  import { speak, resetLastMessage } from '../Libs/tts.utils';
16
19
  import {
@@ -87,8 +90,46 @@ const EIDScanner = ({
87
90
  }, []);
88
91
  const [isScanned, setIsScanned] = React.useState(false);
89
92
  const [hasGuideShown, setHasGuideShown] = React.useState(false);
90
- const { t } = useTranslation();
93
+ const { t, i18n } = useTranslation();
91
94
  const appContext = React.useContext(AppContext);
95
+
96
+ const formatGender = useCallback(
97
+ (code?: string | null) => {
98
+ switch (code?.toUpperCase()) {
99
+ case 'M':
100
+ return t('eidScannerScreen.genderMale', 'Male');
101
+ case 'F':
102
+ return t('eidScannerScreen.genderFemale', 'Female');
103
+ default:
104
+ return t('eidScannerScreen.genderUnspecified', 'Unspecified');
105
+ }
106
+ },
107
+ [t]
108
+ );
109
+
110
+ const formatDocumentType = useCallback(
111
+ (code?: string | null) => {
112
+ if (!code) return '';
113
+ const upper = code.toUpperCase();
114
+ if (upper.startsWith('P'))
115
+ return t('eidScannerScreen.docTypePassport', 'Passport');
116
+ if (upper.startsWith('I'))
117
+ return t('eidScannerScreen.docTypeID', 'ID Card');
118
+ if (upper.startsWith('A'))
119
+ return t('eidScannerScreen.docTypeResidence', 'Residence Permit');
120
+ if (upper.startsWith('V'))
121
+ return t('eidScannerScreen.docTypeVisa', 'Visa');
122
+ return code;
123
+ },
124
+ [t]
125
+ );
126
+
127
+ const formatNationality = useCallback(
128
+ (code?: string | null) => {
129
+ return getLocalizedCountryName(code, i18n.language);
130
+ },
131
+ [i18n.language]
132
+ );
92
133
  const [voiceGuidanceMessage, setVoiceGuidanceMessage] = useState<string>();
93
134
  const [attemptNumber, setAttemptNumber] = useState<number>(0);
94
135
 
@@ -103,6 +144,21 @@ const EIDScanner = ({
103
144
  setProgressStage('');
104
145
  resetLastMessage();
105
146
 
147
+ // Check NFC permissions before attempting to scan
148
+ const hasNFCPermission = await PermissionManager.requestPermission('nfc');
149
+ if (!hasNFCPermission) {
150
+ debugError('EIDScanner', 'NFC permission denied');
151
+ setIsScanning(false);
152
+ Alert.alert(
153
+ t('eidScannerScreen.error'),
154
+ t(
155
+ 'eidScannerScreen.nfcPermissionDenied',
156
+ 'NFC permission is required to scan your document'
157
+ )
158
+ );
159
+ return;
160
+ }
161
+
106
162
  // Track EID scan start using analytics helper
107
163
  const docType =
108
164
  (documentType as 'ID' | 'PASSPORT' | 'UNKNOWN') || 'UNKNOWN';
@@ -434,106 +490,126 @@ const EIDScanner = ({
434
490
  </View>
435
491
  ) : (
436
492
  <>
437
- {isScanned &&
438
- documentFaceImage &&
439
- documentFaceImageMimeType === 'image/jpeg' && (
440
- <Image
441
- source={{
442
- uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`,
443
- }}
444
- style={styles.faceImage}
445
- />
446
- )}
447
-
448
- {isScanned &&
449
- documentFaceImage &&
450
- // TODO Add support for jp2 images
451
- documentFaceImageMimeType === 'image/jp2' && (
452
- <Text style={styles.mainText}>
453
- {t('eidScannerScreen.imageCannotBeShown')}
454
- </Text>
455
- )}
456
-
457
- {isScanned && !documentFaceImage && (
458
- <Text style={styles.mainText}>
459
- {t('eidScannerScreen.faceImageNotFound')}
460
- </Text>
461
- )}
462
-
463
493
  {isScanned && documentMRZInfo && (
464
- <View style={styles.mrzInfo}>
465
- <View style={styles.mrzInfoItem}>
466
- <Text style={styles.mrzInfoLabel}>
467
- {t('eidScannerScreen.documentCode')}:
468
- </Text>
469
- <Text style={styles.mrzInfoText}>
470
- {documentMRZInfo.getDocumentCode()}
471
- </Text>
472
- </View>
473
- <View style={styles.mrzInfoItem}>
474
- <Text style={styles.mrzInfoLabel}>
475
- {t('eidScannerScreen.nationality')}:
476
- </Text>
477
- <Text style={styles.mrzInfoText}>
478
- {documentMRZInfo.getNationality()}
479
- </Text>
480
- </View>
481
- <View style={styles.mrzInfoItem}>
482
- <Text style={styles.mrzInfoLabel}>
483
- {t('eidScannerScreen.personalNumber')}:
484
- </Text>
485
- <Text style={styles.mrzInfoText}>
486
- {documentMRZInfo.getOptionalData1()}
487
- </Text>
488
- </View>
489
- <View style={styles.mrzInfoItem}>
490
- <Text style={styles.mrzInfoLabel}>
491
- {t('eidScannerScreen.documentNumber')}:
492
- </Text>
493
- <Text style={styles.mrzInfoText}>
494
- {documentMRZInfo.getDocumentNumber()}
495
- </Text>
496
- </View>
497
- <View style={styles.mrzInfoItem}>
498
- <Text style={styles.mrzInfoLabel}>
499
- {t('eidScannerScreen.name')}:
500
- </Text>
501
- <Text style={styles.mrzInfoText}>
502
- {documentMRZInfo.getSecondaryIdentifier()}
503
- </Text>
504
- </View>
505
- <View style={styles.mrzInfoItem}>
506
- <Text style={styles.mrzInfoLabel}>
507
- {t('eidScannerScreen.surname')}:
508
- </Text>
509
- <Text style={styles.mrzInfoText}>
510
- {documentMRZInfo.getPrimaryIdentifier()}
511
- </Text>
512
- </View>
513
- <View style={styles.mrzInfoItem}>
514
- <Text style={styles.mrzInfoLabel}>
515
- {t('eidScannerScreen.birthDate')}:
516
- </Text>
517
- <Text style={styles.mrzInfoText}>
518
- {formatDate(documentMRZInfo.getDateOfBirth())}
519
- </Text>
520
- </View>
521
- <View style={styles.mrzInfoItem}>
522
- <Text style={styles.mrzInfoLabel}>
523
- {t('eidScannerScreen.gender')}:
524
- </Text>
525
- <Text style={styles.mrzInfoText}>
526
- {documentMRZInfo.getGender().getStrCode()}
527
- </Text>
528
- </View>
529
- <View style={styles.mrzInfoItem}>
530
- <Text style={styles.mrzInfoLabel}>
531
- {t('eidScannerScreen.expirationDate')}:
532
- </Text>
533
- <Text style={styles.mrzInfoText}>
534
- {formatDate(documentMRZInfo.getDateOfExpiry())}
535
- </Text>
494
+ <View style={styles.idCardWrapper}>
495
+ <View style={styles.idCard}>
496
+ {/* ── Card header strip: doc type + issuing state ── */}
497
+ <View
498
+ style={[
499
+ styles.idCardStrip,
500
+ { backgroundColor: appContext.branding.primaryColor },
501
+ ]}
502
+ >
503
+ <Text style={styles.idCardStripDocType}>
504
+ {formatDocumentType(documentMRZInfo.getDocumentCode())}
505
+ </Text>
506
+ </View>
507
+
508
+ {/* ── Photo (left) + fields (right) ── */}
509
+ <View style={styles.idCardBody}>
510
+ <View style={styles.idCardPhotoContainer}>
511
+ <View style={styles.idCardPhotoFrame}>
512
+ {documentFaceImage &&
513
+ (documentFaceImageMimeType === 'image/jpeg' ||
514
+ documentFaceImageMimeType === 'image/png') ? (
515
+ <Image
516
+ source={{
517
+ uri: `data:${documentFaceImageMimeType};base64,${documentFaceImage}`,
518
+ }}
519
+ style={styles.idCardPhoto}
520
+ resizeMode="cover"
521
+ />
522
+ ) : (
523
+ <View style={styles.idCardPhotoPlaceholder}>
524
+ <Text style={styles.idCardPhotoPlaceholderText}>
525
+ ?
526
+ </Text>
527
+ </View>
528
+ )}
529
+ </View>
530
+ </View>
531
+
532
+ {/* Right column: all fields as grid */}
533
+ <View style={styles.idCardFields}>
534
+ <IDCardField
535
+ label={t('eidScannerScreen.surname')}
536
+ value={documentMRZInfo.getPrimaryIdentifier()}
537
+ />
538
+ <IDCardField
539
+ label={t('eidScannerScreen.name')}
540
+ value={documentMRZInfo.getSecondaryIdentifier()}
541
+ />
542
+ <IDCardField
543
+ label={t('eidScannerScreen.documentNumber')}
544
+ value={documentMRZInfo.getDocumentNumber()}
545
+ />
546
+ <IDCardField
547
+ label={t('eidScannerScreen.birthDate')}
548
+ value={formatDate(documentMRZInfo.getDateOfBirth())}
549
+ />
550
+ <IDCardField
551
+ label={t('eidScannerScreen.expirationDate')}
552
+ value={formatDate(documentMRZInfo.getDateOfExpiry())}
553
+ />
554
+ <IDCardField
555
+ label={t('eidScannerScreen.gender')}
556
+ value={formatGender(
557
+ documentMRZInfo.getGender().getStrCode()
558
+ )}
559
+ />
560
+ <IDCardField
561
+ label={t('eidScannerScreen.nationality')}
562
+ value={formatNationality(
563
+ documentMRZInfo.getNationality()
564
+ )}
565
+ />
566
+ <IDCardField
567
+ label={t('eidScannerScreen.issuingState')}
568
+ value={formatNationality(
569
+ documentMRZInfo.getIssuingState()
570
+ )}
571
+ />
572
+ {!!(
573
+ documentMRZInfo.getPersonalNumber() ||
574
+ documentMRZInfo.getOptionalData1()
575
+ ) && (
576
+ <IDCardField
577
+ label={t('eidScannerScreen.personalNumber')}
578
+ value={
579
+ documentMRZInfo.getPersonalNumber() ||
580
+ documentMRZInfo.getOptionalData1() ||
581
+ ''
582
+ }
583
+ />
584
+ )}
585
+ </View>
586
+ </View>
536
587
  </View>
588
+
589
+ {hasNfc && isEnabled && isScanned && (
590
+ <View style={styles.buttonsContainer}>
591
+ <StyledButton
592
+ mode="contained"
593
+ disabled={!documentFaceImage || !documentMRZInfo}
594
+ onPress={() => {
595
+ if (
596
+ onScanSuccess &&
597
+ documentMRZInfo &&
598
+ documentFaceImage &&
599
+ documentFaceImageMimeType
600
+ ) {
601
+ onScanSuccess(
602
+ getFieldsFromMRZ(documentMRZInfo),
603
+ documentFaceImage,
604
+ documentFaceImageMimeType
605
+ );
606
+ }
607
+ }}
608
+ >
609
+ {t('eidScannerScreen.approveAndContinue')}
610
+ </StyledButton>
611
+ </View>
612
+ )}
537
613
  </View>
538
614
  )}
539
615
 
@@ -589,7 +665,15 @@ const EIDScanner = ({
589
665
  </Text>
590
666
  )))}
591
667
 
592
- {hasNfc && isEnabled && isScanned && (
668
+ {hasNfc && isEnabled && !isScanning && !isScanned && (
669
+ <View style={styles.buttonsContainer}>
670
+ <StyledButton mode="contained" onPress={readNFC}>
671
+ {t('eidScannerScreen.startScanning')}
672
+ </StyledButton>
673
+ </View>
674
+ )}
675
+
676
+ {hasNfc && isEnabled && isScanned && !documentMRZInfo && (
593
677
  <View style={styles.buttonsContainer}>
594
678
  <StyledButton
595
679
  mode="contained"
@@ -688,31 +772,99 @@ const styles = StyleSheet.create({
688
772
  color: 'black',
689
773
  textAlign: 'center',
690
774
  },
691
- faceImage: {
692
- width: 150,
693
- height: 150,
694
- borderRadius: 150,
695
- borderColor: 'lightgray',
696
- borderWidth: 1,
697
- alignSelf: 'center',
775
+ // ── ID Card result layout ──────────────────────────────────────────────
776
+ idCardWrapper: {
777
+ gap: 10,
698
778
  },
699
- mrzInfo: {
700
- flexDirection: 'column',
701
- width: '100%',
779
+ idCard: {
780
+ backgroundColor: '#fff',
781
+ borderRadius: 14,
782
+ overflow: 'hidden',
783
+ shadowColor: '#000',
784
+ shadowOffset: { width: 0, height: 3 },
785
+ shadowOpacity: 0.12,
786
+ shadowRadius: 10,
787
+ elevation: 4,
788
+ borderWidth: 1,
789
+ borderColor: '#e8e8e8',
702
790
  },
703
- mrzInfoItem: {
791
+ idCardStrip: {
704
792
  flexDirection: 'row',
705
793
  justifyContent: 'space-between',
706
- borderBottomColor: 'lightgray',
707
- borderBottomWidth: 1,
708
- paddingVertical: 5,
794
+ alignItems: 'center',
795
+ paddingHorizontal: 12,
796
+ paddingVertical: 6,
709
797
  },
710
- mrzInfoLabel: {
711
- color: 'black',
712
- fontWeight: 'bold',
798
+ idCardStripDocType: {
799
+ color: '#fff',
800
+ fontSize: 12,
801
+ fontWeight: '700',
802
+ letterSpacing: 0.5,
803
+ textTransform: 'uppercase',
713
804
  },
714
- mrzInfoText: {
715
- color: 'black',
805
+ idCardStripIssuer: {
806
+ color: 'rgba(255,255,255,0.85)',
807
+ fontSize: 12,
808
+ fontWeight: '600',
809
+ letterSpacing: 0.3,
810
+ },
811
+ idCardBody: {
812
+ flexDirection: 'row',
813
+ alignItems: 'stretch',
814
+ },
815
+ idCardPhotoContainer: {
816
+ width: 110,
817
+ padding: 8,
818
+ alignSelf: 'stretch',
819
+ },
820
+ idCardPhotoFrame: {
821
+ flex: 1,
822
+ borderRadius: 10,
823
+ overflow: 'hidden',
824
+ borderWidth: 1,
825
+ borderColor: 'rgba(0,0,0,0.12)',
826
+ },
827
+ idCardPhoto: {
828
+ flex: 1,
829
+ },
830
+ idCardPhotoPlaceholder: {
831
+ flex: 1,
832
+ borderRadius: 10,
833
+ backgroundColor: '#f5f5f5',
834
+ alignItems: 'center',
835
+ justifyContent: 'center',
836
+ },
837
+ idCardPhotoPlaceholderText: {
838
+ fontSize: 30,
839
+ color: '#ccc',
840
+ },
841
+ idCardFields: {
842
+ flex: 1,
843
+ flexDirection: 'row',
844
+ flexWrap: 'wrap',
845
+ gap: 6,
846
+ alignContent: 'flex-start',
847
+ padding: 10,
848
+ },
849
+ idCardFieldItem: {
850
+ flexGrow: 1,
851
+ flexShrink: 1,
852
+ flexBasis: '45%',
853
+ minWidth: 0,
854
+ },
855
+ idCardFieldLabel: {
856
+ fontSize: 8,
857
+ fontWeight: '600',
858
+ color: '#999',
859
+ letterSpacing: 0.3,
860
+ marginBottom: 1,
861
+ },
862
+ idCardFieldValue: {
863
+ fontSize: 11,
864
+ fontWeight: '600',
865
+ color: '#111',
866
+ flexShrink: 1,
867
+ flexWrap: 'wrap',
716
868
  },
717
869
  guide: {
718
870
  flex: 1,
@@ -744,4 +896,13 @@ const styles = StyleSheet.create({
744
896
  },
745
897
  });
746
898
 
899
+ const IDCardField = ({ label, value }: { label: string; value: string }) => (
900
+ <View style={styles.idCardFieldItem}>
901
+ <Text style={styles.idCardFieldLabel} numberOfLines={1}>
902
+ {label}
903
+ </Text>
904
+ <Text style={styles.idCardFieldValue}>{value || '—'}</Text>
905
+ </View>
906
+ );
907
+
747
908
  export default EIDScanner;
@@ -1,16 +1,15 @@
1
1
  import { useIsFocused } from '@react-navigation/native';
2
2
  import { useKeepAwake } from '../Libs/native-keep-awake.utils';
3
3
  import { debugLog, debugWarn, debugError, logError } from '../Libs/debug.utils';
4
+ import PermissionManager from '../Libs/permissions.utils';
4
5
  import React, { useEffect, useState, useRef, useCallback } from 'react';
5
6
  import {
6
7
  StyleSheet,
7
8
  Text,
8
9
  View,
9
- Platform,
10
10
  Linking,
11
11
  ActivityIndicator,
12
12
  NativeModules,
13
- PermissionsAndroid,
14
13
  type NativeSyntheticEvent,
15
14
  } from 'react-native';
16
15
  import {
@@ -81,20 +80,24 @@ const FaceCamera = ({
81
80
 
82
81
  useEffect(() => {
83
82
  const requestPermissions = async () => {
84
- if (Platform.OS === 'android') {
85
- const cameraGranted = await PermissionsAndroid.request(
86
- PermissionsAndroid.PERMISSIONS.CAMERA
87
- );
88
- const audioGranted = await PermissionsAndroid.request(
89
- PermissionsAndroid.PERMISSIONS.RECORD_AUDIO
90
- );
91
- setHasPermission(
92
- cameraGranted === PermissionsAndroid.RESULTS.GRANTED &&
93
- audioGranted === PermissionsAndroid.RESULTS.GRANTED
94
- );
95
- } else {
96
- // iOS permissions handled by native camera
97
- setHasPermission(true);
83
+ try {
84
+ const permissions = await PermissionManager.requestPermissions([
85
+ 'camera',
86
+ 'microphone',
87
+ ]);
88
+
89
+ const hasCamera = permissions.get('camera') ?? false;
90
+ const hasMicrophone = permissions.get('microphone') ?? false;
91
+
92
+ debugLog('FaceCamera', 'Permissions requested:', {
93
+ camera: hasCamera,
94
+ microphone: hasMicrophone,
95
+ });
96
+
97
+ setHasPermission(hasCamera && hasMicrophone);
98
+ } catch (error) {
99
+ debugError('FaceCamera', 'Permission request failed:', error);
100
+ setHasPermission(false);
98
101
  }
99
102
  setPermissionsRequested(true);
100
103
  };
@@ -6,7 +6,7 @@
6
6
  * KEY DISTINGUISHING FEATURES:
7
7
  * =============================
8
8
  * PASSPORT: Has MRZ on FRONT with pattern P<XXX (e.g., P<TUR, P<USA)
9
- * ID CARD: Has NO MRZ on front; MRZ only on BACK with code 'I'
9
+ * ID CARD: Has NO MRZ on front; MRZ only on BACK with code starting with I, A, or C
10
10
  *
11
11
  * IMPORTANT: Both passports and ID cards have signature sections!
12
12
  * Signature presence is NOT a distinguishing feature between document types.
@@ -31,7 +31,7 @@
31
31
  import type { Face } from './IdentityDocumentCamera.types';
32
32
  import type { MRZFields } from '../Types/mrzFields';
33
33
  import { PASSPORT_MRZ_PATTERN } from './IdentityDocumentCamera.constants';
34
- import { debugLog, isDebugEnabled } from '../Libs/debug.utils';
34
+ import { isIDCardDocumentCode } from './IdentityDocumentCamera.utils';
35
35
 
36
36
  /**
37
37
  * After this many retry attempts for ID_FRONT flow, proceed with face detection alone
@@ -222,10 +222,13 @@ export function handleIDFrontFlow(
222
222
  }
223
223
 
224
224
  // ============================================================================
225
- // STEP 4: Final MRZ code check (if present, must be 'I' not 'P')
225
+ // STEP 4: Final MRZ code check (if present, must be an ID card code, not P)
226
226
  // ============================================================================
227
227
  // ID cards should NOT have MRZ on front, but if detected, verify it's not passport
228
- if (mrzFields?.documentCode && mrzFields.documentCode !== 'I') {
228
+ if (
229
+ mrzFields?.documentCode &&
230
+ !isIDCardDocumentCode(mrzFields.documentCode)
231
+ ) {
229
232
  return {
230
233
  shouldProceed: false,
231
234
  reason: `MRZ shows unexpected document code: ${mrzFields.documentCode}`,