@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
@@ -38,12 +38,15 @@ import {
38
38
  trackError,
39
39
  useScreenTracking,
40
40
  } from '../../Shared/Libs/analytics.utils';
41
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
42
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
41
43
 
42
44
  type OTPVerificationScreenParams = {
43
45
  sessionId: string;
44
46
  };
45
47
 
46
48
  const OTPVerificationScreen = () => {
49
+ useKeepAwake();
47
50
  const route =
48
51
  useRoute<RouteProp<{ params: OTPVerificationScreenParams }, 'params'>>();
49
52
  const sessionId = route.params?.sessionId;
@@ -60,6 +63,9 @@ const OTPVerificationScreen = () => {
60
63
 
61
64
  useScreenTracking('otp_verification');
62
65
 
66
+ // Configure status bar for white background
67
+ useStatusBarWhiteBackground();
68
+
63
69
  // Guard: If sessionId is not provided, show error
64
70
  if (!sessionId) {
65
71
  return (
@@ -5,11 +5,17 @@ import AppContext from '../../Shared/Contexts/AppContext';
5
5
  import QrCodeScannerCamera from '../../Shared/Components/QrCodeScannerCamera';
6
6
  import { useNavigation } from '@react-navigation/native';
7
7
  import { handleDeepLink } from '../../Shared/Libs/deeplink.utils';
8
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
9
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
8
10
 
9
11
  const QrCodeScanningScreen = () => {
12
+ useKeepAwake();
10
13
  const appContext = React.useContext(AppContext);
11
14
  const navigation = useNavigation();
12
15
 
16
+ // Configure status bar for white background
17
+ useStatusBarWhiteBackground();
18
+
13
19
  const onQrCodeScanned = (data: string) => {
14
20
  const [bUrl, sId] = handleDeepLink({ url: data });
15
21
 
@@ -26,7 +32,7 @@ const QrCodeScanningScreen = () => {
26
32
  appContext.identificationInfo.sessionId = sId;
27
33
  }
28
34
 
29
- navigation.navigate('VerificationSessionCheckScreen' as never);
35
+ navigation.goBack();
30
36
  }
31
37
  };
32
38
 
@@ -25,11 +25,14 @@ import type {
25
25
  IdentificationInfo,
26
26
  LivenessDetection,
27
27
  ScannedIdentityDocument,
28
+ VerbalConsentVideo,
28
29
  } from '../../Shared/Types/identificationInfo';
29
30
  import { runWithRetry } from '../../Shared/Libs/promise.utils';
31
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
30
32
  import NavigationManager, {
31
33
  type NavigationManagerRef,
32
34
  } from '../../Shared/Components/NavigationManager';
35
+ import { getLocalizedCountryName } from '../../Shared/Libs/country-display.utils';
33
36
  import mrzUtils from '../../Shared/Libs/mrz.utils';
34
37
  import { useTranslation } from 'react-i18next';
35
38
  import { Video as VideoCompressor } from 'react-native-compressor';
@@ -53,14 +56,18 @@ const ResultScreen = () => {
53
56
  const [isSubmitting, setIsSubmitting] = useState(false);
54
57
  const [progress, setProgress] = useState(0);
55
58
  const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
59
+ const hasSubmittedRef = React.useRef(false);
56
60
  const [shouldShowDemoData, setShouldShowDemoData] = useState(false);
57
61
  const [deviceIdentifier, setDeviceIdentifier] = useState<string>('');
58
- const { t } = useTranslation();
62
+ const { t, i18n } = useTranslation();
59
63
  const insets = useSafeAreaInsets();
60
64
 
61
65
  // Track screen view and exit
62
66
  useScreenTracking('result_screen');
63
67
 
68
+ // Configure status bar for white background
69
+ useStatusBarWhiteBackground();
70
+
64
71
  const formatDate = useCallback((dateStr?: string | null) => {
65
72
  if (!dateStr) return '';
66
73
 
@@ -72,7 +79,7 @@ const ResultScreen = () => {
72
79
  const dd = dateStr.substring(4, 6);
73
80
 
74
81
  // Assume 19xx if YY >= 50, else 20xx
75
- const year = parseInt(yy) >= 50 ? `19${yy}` : `20${yy}`;
82
+ const year = parseInt(yy, 10) >= 50 ? `19${yy}` : `20${yy}`;
76
83
  return `${dd}/${mm}/${year}`;
77
84
  }
78
85
 
@@ -91,13 +98,47 @@ const ResultScreen = () => {
91
98
  }
92
99
  }, []);
93
100
 
101
+ const formatGender = useCallback(
102
+ (code?: string | null) => {
103
+ switch (code?.toUpperCase()) {
104
+ case 'M':
105
+ return t('eidScannerScreen.genderMale');
106
+ case 'F':
107
+ return t('eidScannerScreen.genderFemale');
108
+ default:
109
+ return t('eidScannerScreen.genderUnspecified');
110
+ }
111
+ },
112
+ [t]
113
+ );
114
+
115
+ const formatDocumentType = useCallback(
116
+ (code?: string | null) => {
117
+ if (!code) return '';
118
+ const upper = code.toUpperCase();
119
+ if (upper.startsWith('P')) return t('eidScannerScreen.docTypePassport');
120
+ if (upper.startsWith('I')) return t('eidScannerScreen.docTypeID');
121
+ if (upper.startsWith('A')) return t('eidScannerScreen.docTypeResidence');
122
+ if (upper.startsWith('V')) return t('eidScannerScreen.docTypeVisa');
123
+ return code;
124
+ },
125
+ [t]
126
+ );
127
+
128
+ const formatNationality = useCallback(
129
+ (code?: string | null) => {
130
+ return getLocalizedCountryName(code, i18n.language);
131
+ },
132
+ [i18n.language]
133
+ );
134
+
94
135
  const apiUrl = useMemo(
95
136
  () => `${appContext.baseUrl}/api/app/mobile`,
96
137
  [appContext.baseUrl]
97
138
  );
98
139
 
99
140
  useEffect(() => {
100
- if (appContext.isDemoSession) {
141
+ if (appContext.isDemoSession || appContext.isTestVideoSession) {
101
142
  setShouldShowDemoData(true);
102
143
  // Generate device identifier for demo
103
144
  NativeDeviceInfo.generateHumanReadableIdentifier().then(
@@ -107,6 +148,7 @@ const ResultScreen = () => {
107
148
  }, [
108
149
  appContext.identificationInfo.identificationId,
109
150
  appContext.isDemoSession,
151
+ appContext.isTestVideoSession,
110
152
  ]);
111
153
 
112
154
  const createIdentification = useCallback(
@@ -187,22 +229,22 @@ const ResultScreen = () => {
187
229
  throw new Error('MRZ fields not found');
188
230
  }
189
231
 
232
+ // Backend payload must contain raw MRZ data only.
233
+ // Human-friendly formatting is strictly for UI rendering paths.
234
+ const rawMrzFields = scannedDocument.mrzFields;
235
+
190
236
  const identificationDocument = {
191
- type: scannedDocument.mrzFields.documentCode,
192
- name: scannedDocument.mrzFields.firstName,
193
- surname: scannedDocument.mrzFields.lastName,
194
- gender: getGenderEnumType(scannedDocument.mrzFields.sex),
195
- number: scannedDocument.mrzFields.documentNumber,
196
- country: scannedDocument.mrzFields.issuingState,
237
+ type: rawMrzFields.documentCode,
238
+ name: rawMrzFields.firstName,
239
+ surname: rawMrzFields.lastName,
240
+ gender: getGenderEnumType(rawMrzFields.sex),
241
+ number: rawMrzFields.documentNumber,
242
+ country: rawMrzFields.issuingState,
197
243
  barcodeValue: scannedDocument.barcodeValue,
198
- personalNumber:
199
- scannedDocument.mrzFields.personalNumber ||
200
- scannedDocument.mrzFields.optional1,
201
- birthDate: mrzUtils.convertMRZDateToISODate(
202
- scannedDocument.mrzFields.birthDate
203
- ),
244
+ personalNumber: rawMrzFields.personalNumber || rawMrzFields.optional1,
245
+ birthDate: mrzUtils.convertMRZDateToISODate(rawMrzFields.birthDate),
204
246
  expiryDate: mrzUtils.convertMRZDateToISODate(
205
- scannedDocument.mrzFields.expirationDate
247
+ rawMrzFields.expirationDate
206
248
  ),
207
249
  dataSource: scannedDocument.dataSource,
208
250
  mrzText: scannedDocument.mrzText,
@@ -227,8 +269,17 @@ const ResultScreen = () => {
227
269
  async (
228
270
  identificationId: string,
229
271
  scannedIdentityDocument?: ScannedIdentityDocument,
230
- livenessDetection?: LivenessDetection
272
+ livenessDetection?: LivenessDetection,
273
+ skipIfAlreadyUploaded?: boolean
231
274
  ) => {
275
+ // Skip media upload if already uploaded during video call
276
+ if (skipIfAlreadyUploaded) {
277
+ console.log(
278
+ '[ResultScreen] Media already uploaded during video call, skipping'
279
+ );
280
+ return;
281
+ }
282
+
232
283
  const uploadFileOptions: RNFS.UploadFileOptions = {
233
284
  toUrl: `${apiUrl}/identifications/${identificationId}/media`,
234
285
  method: 'POST',
@@ -287,7 +338,9 @@ const ResultScreen = () => {
287
338
  const extension =
288
339
  scannedIdentityDocument.faceImageMimeType === 'image/jp2'
289
340
  ? 'jp2'
290
- : 'jpg';
341
+ : scannedIdentityDocument.faceImageMimeType === 'image/png'
342
+ ? 'png'
343
+ : 'jpg';
291
344
  await RNFS.writeFile(
292
345
  decodeURIComponent(
293
346
  `${RNFS.TemporaryDirectoryPath}/FACE_IMAGE.${extension}`
@@ -378,10 +431,9 @@ const ResultScreen = () => {
378
431
  if (livenessVideoPath) {
379
432
  let videoFilePath: string;
380
433
  if (Platform.OS === 'ios') {
381
- await RNFS.mkdir(
382
- `${RNFS.TemporaryDirectoryPath}/${new Date().getTime()}`
383
- );
384
- videoFilePath = `${RNFS.TemporaryDirectoryPath}/${new Date().getTime()}/LIVENESS_VIDEO.mp4`;
434
+ const tmpTimestamp = new Date().getTime();
435
+ await RNFS.mkdir(`${RNFS.TemporaryDirectoryPath}/${tmpTimestamp}`);
436
+ videoFilePath = `${RNFS.TemporaryDirectoryPath}/${tmpTimestamp}/LIVENESS_VIDEO.mp4`;
385
437
  } else {
386
438
  videoFilePath = `${RNFS.TemporaryDirectoryPath}/LIVENESS_VIDEO.mp4`;
387
439
  }
@@ -414,6 +466,10 @@ const ResultScreen = () => {
414
466
  }
415
467
  }
416
468
 
469
+ if (uploadFileOptions.files.length === 0) {
470
+ return;
471
+ }
472
+
417
473
  const response = await RNFS.uploadFiles(uploadFileOptions).promise;
418
474
 
419
475
  if (![200, 201, 204].includes(response.statusCode)) {
@@ -423,6 +479,68 @@ const ResultScreen = () => {
423
479
  [apiUrl]
424
480
  );
425
481
 
482
+ const uploadVerbalConsentVideos = useCallback(
483
+ async (
484
+ identificationId: string,
485
+ verbalConsentVideos?: VerbalConsentVideo[]
486
+ ) => {
487
+ if (!verbalConsentVideos?.length) {
488
+ return;
489
+ }
490
+
491
+ for (const consentVideo of verbalConsentVideos) {
492
+ const normalizedPath = consentVideo.videoPath.replace('file://', '');
493
+ let targetPath: string;
494
+ if (Platform.OS === 'ios') {
495
+ const tmpTimestamp = new Date().getTime();
496
+ await RNFS.mkdir(`${RNFS.TemporaryDirectoryPath}/${tmpTimestamp}`);
497
+ targetPath = `${RNFS.TemporaryDirectoryPath}/${tmpTimestamp}/VERBAL_CONSENT_VIDEO.mp4`;
498
+ } else {
499
+ targetPath = `${RNFS.TemporaryDirectoryPath}/VERBAL_CONSENT_VIDEO.mp4`;
500
+ }
501
+
502
+ await RNFS.copyFile(normalizedPath, targetPath);
503
+
504
+ const compressedVideoPath = await VideoCompressor.compress(targetPath, {
505
+ compressionMethod: 'manual',
506
+ bitrate: 500000, // 500 kbps
507
+ maxSize: 1280, // HD 720p
508
+ minimumFileSizeForCompress: 0, // Always compress
509
+ });
510
+
511
+ const compressedNormalizedPath = compressedVideoPath.replace(
512
+ 'file://',
513
+ ''
514
+ );
515
+
516
+ const response = await RNFS.uploadFiles({
517
+ toUrl: `${apiUrl}/identifications/${identificationId}/media`,
518
+ method: 'POST',
519
+ headers: {
520
+ Accept: 'application/json',
521
+ },
522
+ fields: {
523
+ consentTitle: consentVideo.title,
524
+ consentText: consentVideo.text,
525
+ },
526
+ files: [
527
+ {
528
+ name: 'files',
529
+ filename: 'VERBAL_CONSENT_VIDEO.mp4',
530
+ filepath: compressedNormalizedPath,
531
+ filetype: 'video/mp4',
532
+ },
533
+ ],
534
+ }).promise;
535
+
536
+ if (![200, 201, 204].includes(response.statusCode)) {
537
+ throw new Error('Verbal consent video upload failed');
538
+ }
539
+ }
540
+ },
541
+ [apiUrl]
542
+ );
543
+
426
544
  const submit = useCallback(
427
545
  async (identificationInfo: IdentificationInfo) => {
428
546
  try {
@@ -432,11 +550,16 @@ const ResultScreen = () => {
432
550
  throw new Error('IdentificationId not found');
433
551
  }
434
552
 
553
+ const alreadyUploaded =
554
+ !!identificationInfo.mediaUploadedDuringVideoCall;
555
+
435
556
  const sessionKey = await runWithRetry(() =>
436
557
  getSessionKey(apiUrl, appContext.identificationInfo.sessionId)
437
558
  );
438
559
 
439
- await runWithRetry(() => createIdentification(identificationId));
560
+ if (!alreadyUploaded) {
561
+ await runWithRetry(() => createIdentification(identificationId));
562
+ }
440
563
  setProgress(20);
441
564
 
442
565
  await runWithRetry(() =>
@@ -444,27 +567,37 @@ const ResultScreen = () => {
444
567
  );
445
568
  setProgress(30);
446
569
 
447
- const scannedIdentityDocument = identificationInfo.scannedDocument;
448
- if (
449
- scannedIdentityDocument &&
450
- scannedIdentityDocument.documentType !== 'UNKNOWN'
451
- ) {
570
+ if (!alreadyUploaded) {
571
+ const scannedIdentityDocument = identificationInfo.scannedDocument;
572
+ if (
573
+ scannedIdentityDocument &&
574
+ scannedIdentityDocument.documentType !== 'UNKNOWN'
575
+ ) {
576
+ await runWithRetry(() =>
577
+ submitIdentificationDocument(
578
+ identificationId,
579
+ scannedIdentityDocument,
580
+ sessionKey
581
+ )
582
+ );
583
+ }
584
+ setProgress(40);
585
+
586
+ const livenessDetection = identificationInfo.livenessDetection;
452
587
  await runWithRetry(() =>
453
- submitIdentificationDocument(
588
+ uploadIdentificationMedia(
454
589
  identificationId,
455
590
  scannedIdentityDocument,
456
- sessionKey
591
+ livenessDetection,
592
+ false
457
593
  )
458
594
  );
459
595
  }
460
- setProgress(40);
461
596
 
462
- const livenessDetection = identificationInfo.livenessDetection;
463
597
  await runWithRetry(() =>
464
- uploadIdentificationMedia(
598
+ uploadVerbalConsentVideos(
465
599
  identificationId,
466
- scannedIdentityDocument,
467
- livenessDetection
600
+ identificationInfo.verbalConsentVideos
468
601
  )
469
602
  );
470
603
  setProgress(90);
@@ -524,6 +657,7 @@ const ResultScreen = () => {
524
657
  createIdentification,
525
658
  apiUrl,
526
659
  uploadIdentificationMedia,
660
+ uploadVerbalConsentVideos,
527
661
  finishIdentification,
528
662
  submitIdentificationDocument,
529
663
  submitIdentificationConsent,
@@ -533,13 +667,28 @@ const ResultScreen = () => {
533
667
  );
534
668
 
535
669
  useEffect(() => {
536
- if (appContext.identificationInfo && !appContext.isDemoSession) {
670
+ if (
671
+ appContext.identificationInfo &&
672
+ !appContext.isDemoSession &&
673
+ !appContext.isTestVideoSession &&
674
+ !hasSubmittedRef.current
675
+ ) {
676
+ hasSubmittedRef.current = true;
537
677
  submit(appContext.identificationInfo);
538
678
  }
539
- }, [appContext.identificationInfo, appContext.isDemoSession, submit]);
679
+ }, [
680
+ appContext.identificationInfo,
681
+ appContext.isDemoSession,
682
+ appContext.isTestVideoSession,
683
+ submit,
684
+ ]);
540
685
 
541
686
  useEffect(() => {
542
- if (progress === 100 && !appContext.isDemoSession) {
687
+ if (
688
+ progress === 100 &&
689
+ !appContext.isDemoSession &&
690
+ !appContext.isTestVideoSession
691
+ ) {
543
692
  setTimeout(() => {
544
693
  appContext.onCompleted?.();
545
694
  navigationManagerRef.current?.reset();
@@ -601,10 +750,10 @@ const ResultScreen = () => {
601
750
  {t('eidScannerScreen.documentCode')}:
602
751
  </Text>
603
752
  <Text style={styles.mrzInfoText}>
604
- {
753
+ {formatDocumentType(
605
754
  appContext.identificationInfo.scannedDocument
606
755
  .mrzFields?.documentCode
607
- }
756
+ )}
608
757
  </Text>
609
758
  </View>
610
759
  <View style={styles.mrzInfoItem}>
@@ -612,10 +761,12 @@ const ResultScreen = () => {
612
761
  {t('eidScannerScreen.nationality')}:
613
762
  </Text>
614
763
  <Text style={styles.mrzInfoText}>
615
- {
764
+ {formatNationality(
616
765
  appContext.identificationInfo.scannedDocument
617
- .mrzFields?.issuingState
618
- }
766
+ .mrzFields?.nationality ||
767
+ appContext.identificationInfo.scannedDocument
768
+ .mrzFields?.issuingState
769
+ )}
619
770
  </Text>
620
771
  </View>
621
772
  <View style={styles.mrzInfoItem}>
@@ -678,10 +829,10 @@ const ResultScreen = () => {
678
829
  {t('eidScannerScreen.gender')}:
679
830
  </Text>
680
831
  <Text style={styles.mrzInfoText}>
681
- {
832
+ {formatGender(
682
833
  appContext.identificationInfo.scannedDocument
683
834
  .mrzFields?.sex
684
- }
835
+ )}
685
836
  </Text>
686
837
  </View>
687
838
  <View style={styles.mrzInfoItem}>
@@ -883,6 +1034,41 @@ const ResultScreen = () => {
883
1034
  </ScrollView>
884
1035
  </>
885
1036
  )}
1037
+
1038
+ {!!appContext.identificationInfo.verbalConsentVideos
1039
+ ?.length && (
1040
+ <>
1041
+ <Text style={styles.sectionHeader}>
1042
+ {t('resultScreen.demoVerbalConsent')}
1043
+ </Text>
1044
+ {appContext.identificationInfo.verbalConsentVideos.map(
1045
+ (consentVideo, index) => (
1046
+ <View key={`${consentVideo.videoPath}-${index}`}>
1047
+ <Text style={styles.sectionText}>
1048
+ {t('resultScreen.demoVerbalConsentTitle')}:{' '}
1049
+ {consentVideo.title}
1050
+ </Text>
1051
+ <Text style={styles.sectionText}>
1052
+ {t('resultScreen.demoVerbalConsentText')}:{' '}
1053
+ {consentVideo.text}
1054
+ </Text>
1055
+ <Text style={styles.sectionText}>
1056
+ {t('resultScreen.demoVideo')}
1057
+ </Text>
1058
+ <Video
1059
+ source={{
1060
+ uri: consentVideo.videoPath,
1061
+ }}
1062
+ resizeMode="contain"
1063
+ style={styles.video}
1064
+ controls={true}
1065
+ muted={false}
1066
+ />
1067
+ </View>
1068
+ )
1069
+ )}
1070
+ </>
1071
+ )}
886
1072
  </View>
887
1073
  </ScrollView>
888
1074
  <View style={{ paddingBottom: insets.bottom }}>
@@ -973,9 +1159,10 @@ const styles = StyleSheet.create({
973
1159
  },
974
1160
  image: {
975
1161
  width: 100,
976
- height: 100,
977
- objectFit: 'contain',
978
- borderRadius: 8,
1162
+ height: 140,
1163
+ objectFit: 'cover',
1164
+ borderRadius: 12,
1165
+ overflow: 'hidden',
979
1166
  },
980
1167
  imageItem: {
981
1168
  display: 'flex',