@trustchex/react-native-sdk 1.381.0 → 1.409.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 (111) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +1 -12
  2. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
  3. package/ios/Camera/TrustchexCameraView.swift +1 -12
  4. package/ios/MLKit/MLKitModule.swift +1 -1
  5. package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
  6. package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
  7. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -29
  8. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -0
  9. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
  10. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +26 -6
  11. package/lib/module/Screens/Dynamic/VideoCallScreen.js +676 -0
  12. package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
  13. package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
  14. package/lib/module/Screens/Static/ResultScreen.js +27 -13
  15. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +51 -51
  16. package/lib/module/Shared/Animations/video-call.json +1 -0
  17. package/lib/module/Shared/Components/DebugNavigationPanel.js +180 -14
  18. package/lib/module/Shared/Components/EIDScanner.js +1 -4
  19. package/lib/module/Shared/Components/IdentityDocumentCamera.js +29 -8
  20. package/lib/module/Shared/Components/NavigationManager.js +15 -3
  21. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  22. package/lib/module/Shared/Libs/SignalingClient.js +128 -0
  23. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  24. package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
  25. package/lib/module/Shared/Libs/http-client.js +9 -0
  26. package/lib/module/Shared/Libs/promise.utils.js +16 -2
  27. package/lib/module/Shared/Libs/status-bar.utils.js +21 -0
  28. package/lib/module/Shared/Services/DataUploadService.js +294 -0
  29. package/lib/module/Shared/Services/VideoSessionService.js +156 -0
  30. package/lib/module/Shared/Services/WebRTCService.js +510 -0
  31. package/lib/module/Shared/Types/analytics.types.js +2 -0
  32. package/lib/module/Translation/Resources/en.js +20 -0
  33. package/lib/module/Translation/Resources/tr.js +20 -0
  34. package/lib/module/Trustchex.js +10 -0
  35. package/lib/module/version.js +1 -1
  36. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
  37. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
  38. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
  39. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  40. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  41. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  42. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  43. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
  44. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
  45. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  46. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  47. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  48. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  49. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  50. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  51. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  52. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  53. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
  54. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  55. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
  56. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
  57. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  58. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  59. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  60. package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
  61. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
  62. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
  63. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
  64. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
  65. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
  66. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
  67. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
  68. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
  69. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  70. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  71. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +4 -1
  72. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  73. package/lib/typescript/src/Translation/Resources/en.d.ts +20 -0
  74. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  75. package/lib/typescript/src/Translation/Resources/tr.d.ts +20 -0
  76. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  77. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  78. package/lib/typescript/src/version.d.ts +1 -1
  79. package/package.json +29 -2
  80. package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
  81. package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
  82. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +59 -33
  83. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +6 -0
  84. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
  85. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +34 -6
  86. package/src/Screens/Dynamic/VideoCallScreen.tsx +764 -0
  87. package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
  88. package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
  89. package/src/Screens/Static/ResultScreen.tsx +58 -23
  90. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +58 -72
  91. package/src/Shared/Animations/video-call.json +1 -0
  92. package/src/Shared/Components/DebugNavigationPanel.tsx +185 -9
  93. package/src/Shared/Components/EIDScanner.tsx +1 -5
  94. package/src/Shared/Components/IdentityDocumentCamera.tsx +29 -8
  95. package/src/Shared/Components/NavigationManager.tsx +14 -1
  96. package/src/Shared/Contexts/AppContext.ts +2 -0
  97. package/src/Shared/Libs/SignalingClient.ts +189 -0
  98. package/src/Shared/Libs/analytics.utils.ts +4 -0
  99. package/src/Shared/Libs/deeplink.utils.ts +12 -1
  100. package/src/Shared/Libs/http-client.ts +10 -0
  101. package/src/Shared/Libs/promise.utils.ts +16 -2
  102. package/src/Shared/Libs/status-bar.utils.ts +19 -0
  103. package/src/Shared/Services/DataUploadService.ts +395 -0
  104. package/src/Shared/Services/VideoSessionService.ts +190 -0
  105. package/src/Shared/Services/WebRTCService.ts +636 -0
  106. package/src/Shared/Types/analytics.types.ts +2 -0
  107. package/src/Shared/Types/identificationInfo.ts +5 -1
  108. package/src/Translation/Resources/en.ts +25 -0
  109. package/src/Translation/Resources/tr.ts +27 -0
  110. package/src/Trustchex.tsx +12 -2
  111. 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
 
@@ -27,6 +27,7 @@ import type {
27
27
  ScannedIdentityDocument,
28
28
  } from '../../Shared/Types/identificationInfo';
29
29
  import { runWithRetry } from '../../Shared/Libs/promise.utils';
30
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
30
31
  import NavigationManager, {
31
32
  type NavigationManagerRef,
32
33
  } from '../../Shared/Components/NavigationManager';
@@ -61,6 +62,9 @@ const ResultScreen = () => {
61
62
  // Track screen view and exit
62
63
  useScreenTracking('result_screen');
63
64
 
65
+ // Configure status bar for white background
66
+ useStatusBarWhiteBackground();
67
+
64
68
  const formatDate = useCallback((dateStr?: string | null) => {
65
69
  if (!dateStr) return '';
66
70
 
@@ -97,7 +101,7 @@ const ResultScreen = () => {
97
101
  );
98
102
 
99
103
  useEffect(() => {
100
- if (appContext.isDemoSession) {
104
+ if (appContext.isDemoSession || appContext.isTestVideoSession) {
101
105
  setShouldShowDemoData(true);
102
106
  // Generate device identifier for demo
103
107
  NativeDeviceInfo.generateHumanReadableIdentifier().then(
@@ -107,6 +111,7 @@ const ResultScreen = () => {
107
111
  }, [
108
112
  appContext.identificationInfo.identificationId,
109
113
  appContext.isDemoSession,
114
+ appContext.isTestVideoSession,
110
115
  ]);
111
116
 
112
117
  const createIdentification = useCallback(
@@ -227,8 +232,17 @@ const ResultScreen = () => {
227
232
  async (
228
233
  identificationId: string,
229
234
  scannedIdentityDocument?: ScannedIdentityDocument,
230
- livenessDetection?: LivenessDetection
235
+ livenessDetection?: LivenessDetection,
236
+ skipIfAlreadyUploaded?: boolean
231
237
  ) => {
238
+ // Skip media upload if already uploaded during video call
239
+ if (skipIfAlreadyUploaded) {
240
+ console.log(
241
+ '[ResultScreen] Media already uploaded during video call, skipping'
242
+ );
243
+ return;
244
+ }
245
+
232
246
  const uploadFileOptions: RNFS.UploadFileOptions = {
233
247
  toUrl: `${apiUrl}/identifications/${identificationId}/media`,
234
248
  method: 'POST',
@@ -432,11 +446,16 @@ const ResultScreen = () => {
432
446
  throw new Error('IdentificationId not found');
433
447
  }
434
448
 
449
+ const alreadyUploaded =
450
+ !!identificationInfo.mediaUploadedDuringVideoCall;
451
+
435
452
  const sessionKey = await runWithRetry(() =>
436
453
  getSessionKey(apiUrl, appContext.identificationInfo.sessionId)
437
454
  );
438
455
 
439
- await runWithRetry(() => createIdentification(identificationId));
456
+ if (!alreadyUploaded) {
457
+ await runWithRetry(() => createIdentification(identificationId));
458
+ }
440
459
  setProgress(20);
441
460
 
442
461
  await runWithRetry(() =>
@@ -444,29 +463,32 @@ const ResultScreen = () => {
444
463
  );
445
464
  setProgress(30);
446
465
 
447
- const scannedIdentityDocument = identificationInfo.scannedDocument;
448
- if (
449
- scannedIdentityDocument &&
450
- scannedIdentityDocument.documentType !== 'UNKNOWN'
451
- ) {
466
+ if (!alreadyUploaded) {
467
+ const scannedIdentityDocument = identificationInfo.scannedDocument;
468
+ if (
469
+ scannedIdentityDocument &&
470
+ scannedIdentityDocument.documentType !== 'UNKNOWN'
471
+ ) {
472
+ await runWithRetry(() =>
473
+ submitIdentificationDocument(
474
+ identificationId,
475
+ scannedIdentityDocument,
476
+ sessionKey
477
+ )
478
+ );
479
+ }
480
+ setProgress(40);
481
+
482
+ const livenessDetection = identificationInfo.livenessDetection;
452
483
  await runWithRetry(() =>
453
- submitIdentificationDocument(
484
+ uploadIdentificationMedia(
454
485
  identificationId,
455
486
  scannedIdentityDocument,
456
- sessionKey
487
+ livenessDetection,
488
+ false
457
489
  )
458
490
  );
459
491
  }
460
- setProgress(40);
461
-
462
- const livenessDetection = identificationInfo.livenessDetection;
463
- await runWithRetry(() =>
464
- uploadIdentificationMedia(
465
- identificationId,
466
- scannedIdentityDocument,
467
- livenessDetection
468
- )
469
- );
470
492
  setProgress(90);
471
493
 
472
494
  await runWithRetry(() =>
@@ -533,13 +555,26 @@ const ResultScreen = () => {
533
555
  );
534
556
 
535
557
  useEffect(() => {
536
- if (appContext.identificationInfo && !appContext.isDemoSession) {
558
+ if (
559
+ appContext.identificationInfo &&
560
+ !appContext.isDemoSession &&
561
+ !appContext.isTestVideoSession
562
+ ) {
537
563
  submit(appContext.identificationInfo);
538
564
  }
539
- }, [appContext.identificationInfo, appContext.isDemoSession, submit]);
565
+ }, [
566
+ appContext.identificationInfo,
567
+ appContext.isDemoSession,
568
+ appContext.isTestVideoSession,
569
+ submit,
570
+ ]);
540
571
 
541
572
  useEffect(() => {
542
- if (progress === 100 && !appContext.isDemoSession) {
573
+ if (
574
+ progress === 100 &&
575
+ !appContext.isDemoSession &&
576
+ !appContext.isTestVideoSession
577
+ ) {
543
578
  setTimeout(() => {
544
579
  appContext.onCompleted?.();
545
580
  navigationManagerRef.current?.reset();
@@ -36,7 +36,7 @@ import {
36
36
  getSimulatedDemoData,
37
37
  isDemoSession,
38
38
  } from '../../Shared/Libs/demo.utils';
39
- import { useNavigation, useFocusEffect } from '@react-navigation/native';
39
+ import { useNavigation } from '@react-navigation/native';
40
40
  import {
41
41
  SESSION_CODE_CHARSET_PATTERN,
42
42
  SESSION_CODE_VALIDATION_PATTERN,
@@ -46,16 +46,17 @@ import {
46
46
  trackFunnelStep,
47
47
  useScreenTracking,
48
48
  } from '../../Shared/Libs/analytics.utils';
49
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
49
50
 
50
51
  const VerificationSessionCheckScreen = () => {
51
52
  const [sessionCode, setSessionCode] = useState('');
52
53
  const [isCheckingSession, setIsCheckingSession] = useState(false);
53
54
  const isCheckingSessionRef = useRef(false);
55
+ const checkedSessionIdRef = useRef('');
54
56
  const appContext = useContext(AppContext);
55
57
  const { t } = useTranslation();
56
58
  const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
57
59
  const navigation = useNavigation();
58
- const initialized = useRef(false);
59
60
  const insets = useSafeAreaInsets();
60
61
  const theme = useTheme();
61
62
  const primaryColor = theme.colors.primary;
@@ -63,6 +64,9 @@ const VerificationSessionCheckScreen = () => {
63
64
  // Track screen view and exit
64
65
  useScreenTracking('verification_session_check');
65
66
 
67
+ // Configure status bar for white background
68
+ useStatusBarWhiteBackground();
69
+
66
70
  const apiUrl = useMemo(
67
71
  () => `${appContext.baseUrl}/api/app/mobile`,
68
72
  [appContext.baseUrl]
@@ -86,13 +90,7 @@ const VerificationSessionCheckScreen = () => {
86
90
  );
87
91
  return response;
88
92
  } catch (error) {
89
- if (error instanceof NotFoundError) {
90
- // User entered invalid session ID - expected user behavior, not actionable
91
- Alert.alert(
92
- t('general.error'),
93
- t('verificationSessionCheckScreen.noVerificationSessionFound')
94
- );
95
- } else {
93
+ if (!(error instanceof NotFoundError)) {
96
94
  trackError(
97
95
  'SESSION_CHECK_ERROR',
98
96
  error instanceof Error ? error.message : 'Unknown error',
@@ -101,9 +99,10 @@ const VerificationSessionCheckScreen = () => {
101
99
  { recoverable: false, userAction: 'check_session' }
102
100
  );
103
101
  }
102
+ throw error;
104
103
  }
105
104
  },
106
- [apiUrl, appContext.isDemoSession, t]
105
+ [apiUrl, appContext.isDemoSession]
107
106
  );
108
107
 
109
108
  const getSessionByCode = useCallback(
@@ -157,72 +156,59 @@ const VerificationSessionCheckScreen = () => {
157
156
  [navigation]
158
157
  );
159
158
 
160
- // Reset initialized flag when screen is focused and sessionId is empty (after reset)
161
- useFocusEffect(
162
- useCallback(() => {
163
- if (!appContext.identificationInfo.sessionId) {
164
- initialized.current = false;
165
- isCheckingSessionRef.current = false;
166
- }
167
- }, [appContext.identificationInfo.sessionId])
168
- );
159
+ const getSessionRef = useRef(getSession);
160
+ getSessionRef.current = getSession;
161
+ const navigateToOTPScreenRef = useRef(navigateToOTPScreen);
162
+ navigateToOTPScreenRef.current = navigateToOTPScreen;
163
+ const appContextRef = useRef(appContext);
164
+ appContextRef.current = appContext;
169
165
 
170
166
  useEffect(() => {
171
- // Only run this effect if sessionId exists AND we're not currently checking a session
172
- // This prevents race condition when session code is entered
173
- if (
174
- !initialized.current &&
175
- appContext.identificationInfo.sessionId &&
176
- !isCheckingSessionRef.current
177
- ) {
178
- initialized.current = true;
179
- isCheckingSessionRef.current = true;
180
- setIsCheckingSession(true);
181
- getSession(appContext.identificationInfo.sessionId)
182
- .then((session) => {
183
- if (!session) {
184
- // Session not found - clear the sessionId and reset flags to allow retry
185
- Alert.alert(
186
- t('general.error'),
187
- t('verificationSessionCheckScreen.noVerificationSessionFound')
188
- );
189
- appContext.identificationInfo.sessionId = '';
190
- if (appContext.setSessionId) {
191
- appContext.setSessionId('');
192
- }
193
- initialized.current = false;
194
- return;
195
- }
196
-
197
- appContext.workflowSteps = session.workflowSteps;
198
- if (session.branding) {
199
- appContext.branding = appContext.branding || session.branding;
167
+ const sid = appContext.identificationInfo.sessionId;
168
+ if (!sid || checkedSessionIdRef.current === sid) {
169
+ return;
170
+ }
171
+ checkedSessionIdRef.current = sid;
172
+ isCheckingSessionRef.current = true;
173
+ setIsCheckingSession(true);
174
+ getSessionRef
175
+ .current(sid)
176
+ .then((session) => {
177
+ const ctx = appContextRef.current;
178
+ ctx.workflowSteps = session.workflowSteps;
179
+ if (session.branding) {
180
+ ctx.branding = ctx.branding || session.branding;
181
+ }
182
+ if (session.sendOTP) {
183
+ navigateToOTPScreenRef.current(session.id);
184
+ } else {
185
+ if (session.identificationId) {
186
+ ctx.identificationInfo.identificationId = session.identificationId;
200
187
  }
188
+ navigationManagerRef.current?.navigateToNextStep();
189
+ }
190
+ })
191
+ .catch(() => {
192
+ Alert.alert(
193
+ t('general.error'),
194
+ t('verificationSessionCheckScreen.noVerificationSessionFound')
195
+ );
196
+ checkedSessionIdRef.current = '';
197
+ const ctx = appContextRef.current;
198
+ if (ctx.setSessionId) {
199
+ ctx.setSessionId('');
200
+ }
201
+ })
202
+ .finally(() => {
203
+ isCheckingSessionRef.current = false;
204
+ setIsCheckingSession(false);
205
+ });
206
+ // eslint-disable-next-line react-hooks/exhaustive-deps
207
+ }, [appContext.identificationInfo.sessionId]);
201
208
 
202
- if (session.sendOTP) {
203
- navigateToOTPScreen(session.id);
204
- } else {
205
- if (session.identificationId) {
206
- appContext.identificationInfo.identificationId =
207
- session.identificationId;
208
- }
209
- navigationManagerRef.current?.navigateToNextStep();
210
- }
211
- })
212
- .finally(() => {
213
- setTimeout(() => {
214
- isCheckingSessionRef.current = false;
215
- setIsCheckingSession(false);
216
- }, 1000);
217
- });
218
- }
219
- }, [
220
- appContext,
221
- appContext.identificationInfo.sessionId,
222
- getSession,
223
- navigateToOTPScreen,
224
- t,
225
- ]);
209
+ if (!appContext || !appContext.baseUrl) {
210
+ return null;
211
+ }
226
212
 
227
213
  return (
228
214
  <SafeAreaView style={styles.safeAreaContainer}>
@@ -0,0 +1 @@
1
+ {"v":"5.7.3","fr":30,"ip":0,"op":41,"w":600,"h":600,"nm":"Phone Call","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":"Call Icon","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[293.084,310.786,0],"ix":2},"a":{"a":0,"k":[1371.347,-543.458,0],"ix":1},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[28,28,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":20,"s":[25,25,100]},{"t":40,"s":[28,28,100]}],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-22.59,22.938],[0,0],[23.373,23.448],[0,0],[23.186,-22.195],[0,0],[25.1,60.946],[0,0],[22.137,23.256],[0.601,0.601],[23.496,-22.664],[0,0],[-157.804,5.059]],"o":[[0,0],[23.373,-23.448],[-0.601,-0.601],[-23.249,-22.13],[0,0],[-60.902,-25.206],[0,0],[22.213,-23.184],[0,0],[-23.009,-23.158],[0,0],[-131.678,152.765],[32.193,0.187]],"v":[[1568.371,-346.481],[1586.008,-366.813],[1586.008,-451.645],[1538.775,-488.121],[1455.803,-488.005],[1418.862,-456.878],[1284.51,-591.463],[1315.52,-628.288],[1315.656,-711.279],[1279.141,-758.453],[1195.297,-759.345],[1173.008,-739.964],[1482.685,-310.897]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[0,10.704],[64.224,0],[0,-10.704],[-10.704,0],[0,-42.816],[-10.704,0]],"o":[[0,-64.224],[-10.704,0],[0,10.704],[42.816,0],[0,10.704],[10.704,0]],"v":[[1526.061,-582.237],[1409.772,-698.526],[1390.391,-679.145],[1409.772,-659.763],[1487.298,-582.237],[1506.679,-562.856]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ind":2,"ty":"sh","ix":3,"ks":{"a":0,"k":{"i":[[10.704,0],[0,10.704],[85.597,0.085],[0,10.704],[-10.704,0],[-0.117,-106.992]],"o":[[-10.704,0],[-0.085,-85.597],[-10.704,0],[0,-10.704],[106.992,0.117],[0,10.704]],"v":[[1584.205,-562.856],[1564.824,-582.237],[1409.772,-737.289],[1390.391,-756.67],[1409.772,-776.052],[1603.587,-582.237]],"c":true},"ix":2},"nm":"Path 3","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[1,1,1,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":4,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":45,"st":-8,"bm":0},{"ddd":0,"ind":2,"ty":4,"nm":"Background Circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[300,300,0],"ix":2},"a":{"a":0,"k":[1397.949,-584.944,0],"ix":1},"s":{"a":0,"k":[27,27,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-262.123],[262.123,0],[0,262.123],[-262.123,0]],"o":[[0,262.123],[-262.123,0],[0,-262.123],[262.123,0]],"v":[[1872.564,-584.944],[1397.949,-110.329],[923.334,-584.944],[1397.949,-1059.559]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.117700554343,0.127342568192,0.138823505476,1],"ix":4},"o":{"a":0,"k":100,"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[0,0],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":45,"st":-8,"bm":0},{"ddd":0,"ind":3,"ty":4,"nm":"Small Circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":210,"ix":10},"p":{"a":0,"k":[301,301,0],"ix":2},"a":{"a":0,"k":[-7,-71,0],"ix":1},"s":{"a":0,"k":[70,70,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[426,426],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":22.03,"s":[0]},{"t":39.5470890812688,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":10.769,"s":[0]},{"t":35.7941593937688,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.125490196078,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":8,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-7,-71],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":10.7687687687688,"op":45,"st":10.7687687687688,"bm":0},{"ddd":0,"ind":4,"ty":4,"nm":"Medium Circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":119,"ix":10},"p":{"a":0,"k":[301,301,0],"ix":2},"a":{"a":0,"k":[-7,-71,0],"ix":1},"s":{"a":0,"k":[85,85,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[426,426],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":12.02,"s":[0]},{"t":29.5370790712588,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":0.759,"s":[0]},{"t":25.7841493837588,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.125490196078,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":7,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-7,-71],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":0.75875875875876,"op":45,"st":0.75875875875876,"bm":0},{"ddd":0,"ind":5,"ty":4,"nm":"Large Circle","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[301,301,0],"ix":2},"a":{"a":0,"k":[-7,-71,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6}},"ao":0,"shapes":[{"ty":"gr","it":[{"d":1,"ty":"el","s":{"a":0,"k":[426,426],"ix":2},"p":{"a":0,"k":[0,0],"ix":3},"nm":"Ellipse Path 1","mn":"ADBE Vector Shape - Ellipse","hd":false},{"ty":"tm","s":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":15.774,"s":[0]},{"t":33.2908328250125,"s":[100]}],"ix":1},"e":{"a":1,"k":[{"i":{"x":[0.667],"y":[1]},"o":{"x":[0.333],"y":[0]},"t":4.513,"s":[0]},{"t":29.5379031375125,"s":[100]}],"ix":2},"o":{"a":0,"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1","mn":"ADBE Vector Filter - Trim","hd":false},{"ty":"st","c":{"a":0,"k":[0.117647066303,0.125490196078,0.137254901961,1],"ix":3},"o":{"a":0,"k":100,"ix":4},"w":{"a":0,"k":6,"ix":5},"lc":2,"lj":2,"bm":0,"nm":"Stroke 1","mn":"ADBE Vector Graphic - Stroke","hd":false},{"ty":"tr","p":{"a":0,"k":[-7,-71],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Ellipse 1","np":3,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false}],"ip":4.51251251251251,"op":45,"st":4.51251251251251,"bm":0}],"markers":[]}
@@ -6,6 +6,7 @@ import {
6
6
  StyleSheet,
7
7
  Platform,
8
8
  Switch,
9
+ ActivityIndicator,
9
10
  } from 'react-native';
10
11
  import { useNavigation } from '@react-navigation/native';
11
12
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -13,10 +14,13 @@ import AppContext from '../Contexts/AppContext';
13
14
  import { getSimulatedDemoData } from '../Libs/demo.utils';
14
15
  import type { VerificationSession } from '../Types/verificationSession';
15
16
 
17
+ const DEV_BASE_URL = 'https://192.168.0.171:3000';
18
+
16
19
  type ScreenOption = {
17
20
  screen: string;
18
21
  label: string;
19
22
  icon: string;
23
+ isDebug?: boolean;
20
24
  hasOptions?: boolean;
21
25
  hasDocumentTypeOption?: boolean;
22
26
  setupDemo?: () => void;
@@ -43,6 +47,8 @@ const DebugNavigationPanel = () => {
43
47
  const [voiceGuidanceEnabled, setVoiceGuidanceEnabled] = useState(true);
44
48
  const [allowIdCard, setAllowIdCard] = useState(true);
45
49
  const [allowPassport, setAllowPassport] = useState(true);
50
+ const [isConnectingVideoCall, setIsConnectingVideoCall] = useState(false);
51
+ const [videoCallError, setVideoCallError] = useState<string | null>(null);
46
52
  const navigation = useNavigation();
47
53
  const appContext = useContext(AppContext);
48
54
  const insets = useSafeAreaInsets();
@@ -194,6 +200,13 @@ const DebugNavigationPanel = () => {
194
200
  screen: 'MRZTestScreen',
195
201
  label: 'MRZ Test',
196
202
  icon: ICONS.BUG_REPORT,
203
+ isDebug: true,
204
+ },
205
+ {
206
+ screen: 'BarcodeTestScreen',
207
+ label: 'Barcode Test',
208
+ icon: ICONS.BUG_REPORT,
209
+ isDebug: true,
197
210
  },
198
211
  ];
199
212
 
@@ -215,9 +228,67 @@ const DebugNavigationPanel = () => {
215
228
  [navigation]
216
229
  );
217
230
 
218
- const jumpToScreen = useCallback(() => {
219
- if (!selectedScreen) return;
231
+ const handleVideoCallTest = useCallback(async () => {
232
+ setIsConnectingVideoCall(true);
233
+ setVideoCallError(null);
234
+ try {
235
+ const fallbackUrl = (appContext.baseUrl ?? '').replace(/\/$/, '');
236
+ let response: Response;
237
+ let usedBaseUrl = DEV_BASE_URL;
238
+ try {
239
+ response = await fetch(
240
+ `${DEV_BASE_URL}/api/test/video-sessions/latest`
241
+ );
242
+ } catch {
243
+ usedBaseUrl = fallbackUrl;
244
+ response = await fetch(`${fallbackUrl}/api/test/video-sessions/latest`);
245
+ }
246
+ const text = await response.text();
247
+ let data: any;
248
+ try {
249
+ data = JSON.parse(text);
250
+ } catch {
251
+ throw new Error(
252
+ `Server returned non-JSON (${response.status}): ${text.slice(0, 120)}`
253
+ );
254
+ }
255
+ if (!response.ok || !data.success) {
256
+ throw new Error(data.message || 'No active test session found');
257
+ }
258
+ // Point SDK at the server where the test session lives.
259
+ // Do NOT call setBaseUrl (state setter) — it triggers a useMemo rebuild in
260
+ // Trustchex.tsx that resets identificationInfo to an empty object before the
261
+ // screen can render. Direct mutation is sufficient for the test flow.
262
+ appContext.baseUrl = usedBaseUrl;
263
+ appContext.isTestVideoSession = true;
264
+ appContext.identificationInfo.identificationId = data.identificationId;
265
+ appContext.identificationInfo.videoSessionId = data.videoSessionId;
266
+ appContext.identificationInfo.sessionId = data.verificationSessionId;
267
+
268
+ // Set up a mini workflow: eID scan → video call
269
+ const eidStep = {
270
+ type: 'IDENTITY_DOCUMENT_EID_SCAN' as const,
271
+ required: false,
272
+ data: { voiceGuidanceActive: false },
273
+ };
274
+ const videoCallStep = {
275
+ type: 'VIDEO_CALL' as const,
276
+ required: true,
277
+ data: {},
278
+ };
279
+ appContext.workflowSteps = [eidStep, videoCallStep];
280
+ appContext.currentWorkflowStep = eidStep;
281
+
282
+ (navigation as any).navigate('IdentityDocumentEIDScanningScreen');
283
+ setIsExpanded(false);
284
+ } catch (err: any) {
285
+ setVideoCallError(err.message || 'Failed to connect');
286
+ } finally {
287
+ setIsConnectingVideoCall(false);
288
+ }
289
+ }, [appContext, navigation]);
220
290
 
291
+ const jumpToScreen = useCallback(() => {
221
292
  // Validate document type selection for document scanning screens
222
293
  if (
223
294
  selectedScreen.hasDocumentTypeOption &&
@@ -336,16 +407,57 @@ const DebugNavigationPanel = () => {
336
407
  </View>
337
408
  </View>
338
409
  ) : (
339
- screens.map((screen) => (
410
+ <>
411
+ {screens
412
+ .filter((s) => !s.isDebug)
413
+ .map((screen) => (
414
+ <TouchableOpacity
415
+ key={screen.screen}
416
+ style={styles.screenButton}
417
+ onPress={() => selectScreen(screen)}
418
+ >
419
+ <Text style={styles.screenIcon}>{screen.icon}</Text>
420
+ <Text style={styles.screenLabel}>{screen.label}</Text>
421
+ </TouchableOpacity>
422
+ ))}
423
+ <View style={styles.sectionDivider}>
424
+ <View style={styles.sectionDividerLine} />
425
+ <Text style={styles.sectionDividerLabel}>DEBUG TOOLS</Text>
426
+ <View style={styles.sectionDividerLine} />
427
+ </View>
428
+ {screens
429
+ .filter((s) => s.isDebug)
430
+ .map((screen) => (
431
+ <TouchableOpacity
432
+ key={screen.screen}
433
+ style={styles.debugScreenButton}
434
+ onPress={() => selectScreen(screen)}
435
+ >
436
+ <Text style={styles.screenIcon}>{screen.icon}</Text>
437
+ <Text style={styles.debugScreenLabel}>{screen.label}</Text>
438
+ </TouchableOpacity>
439
+ ))}
340
440
  <TouchableOpacity
341
- key={screen.screen}
342
- style={styles.screenButton}
343
- onPress={() => selectScreen(screen)}
441
+ style={[
442
+ styles.videoCallTestButton,
443
+ isConnectingVideoCall && styles.videoCallTestButtonDisabled,
444
+ ]}
445
+ onPress={handleVideoCallTest}
446
+ disabled={isConnectingVideoCall}
344
447
  >
345
- <Text style={styles.screenIcon}>{screen.icon}</Text>
346
- <Text style={styles.screenLabel}>{screen.label}</Text>
448
+ {isConnectingVideoCall ? (
449
+ <ActivityIndicator size="small" color="#4CAF50" />
450
+ ) : (
451
+ <Text style={styles.screenIcon}>📹</Text>
452
+ )}
453
+ <Text style={styles.videoCallTestLabel}>
454
+ {isConnectingVideoCall ? 'Connecting...' : 'Video Call Test'}
455
+ </Text>
347
456
  </TouchableOpacity>
348
- ))
457
+ {videoCallError && (
458
+ <Text style={styles.videoCallTestError}>{videoCallError}</Text>
459
+ )}
460
+ </>
349
461
  )}
350
462
  </View>
351
463
  )}
@@ -518,6 +630,70 @@ const styles = StyleSheet.create({
518
630
  goButtonTextDisabled: {
519
631
  color: '#CCCCCC',
520
632
  },
633
+ sectionDivider: {
634
+ flexDirection: 'row',
635
+ alignItems: 'center',
636
+ marginTop: 4,
637
+ marginBottom: 6,
638
+ gap: 6,
639
+ },
640
+ sectionDividerLine: {
641
+ flex: 1,
642
+ height: 1,
643
+ backgroundColor: 'rgba(255, 167, 38, 0.4)',
644
+ },
645
+ sectionDividerLabel: {
646
+ color: '#FFA726',
647
+ fontSize: 9,
648
+ fontWeight: 'bold',
649
+ letterSpacing: 1,
650
+ },
651
+ debugScreenButton: {
652
+ flexDirection: 'row',
653
+ alignItems: 'center',
654
+ justifyContent: 'flex-start',
655
+ backgroundColor: 'rgba(255, 167, 38, 0.15)',
656
+ borderWidth: 1,
657
+ borderColor: 'rgba(255, 167, 38, 0.4)',
658
+ paddingHorizontal: 12,
659
+ paddingVertical: 8,
660
+ borderRadius: 4,
661
+ marginBottom: 6,
662
+ },
663
+ debugScreenLabel: {
664
+ color: '#FFA726',
665
+ fontSize: 12,
666
+ fontWeight: '500',
667
+ flex: 1,
668
+ },
669
+ videoCallTestButton: {
670
+ flexDirection: 'row',
671
+ alignItems: 'center',
672
+ justifyContent: 'flex-start',
673
+ backgroundColor: 'rgba(76, 175, 80, 0.15)',
674
+ borderWidth: 1,
675
+ borderColor: 'rgba(76, 175, 80, 0.5)',
676
+ paddingHorizontal: 12,
677
+ paddingVertical: 8,
678
+ borderRadius: 4,
679
+ marginBottom: 6,
680
+ gap: 8,
681
+ },
682
+ videoCallTestButtonDisabled: {
683
+ opacity: 0.5,
684
+ },
685
+ videoCallTestLabel: {
686
+ color: '#4CAF50',
687
+ fontSize: 12,
688
+ fontWeight: '600',
689
+ flex: 1,
690
+ },
691
+ videoCallTestError: {
692
+ color: '#FF5252',
693
+ fontSize: 10,
694
+ marginBottom: 6,
695
+ paddingHorizontal: 4,
696
+ },
521
697
  });
522
698
 
523
699
  export default DebugNavigationPanel;