@trustchex/react-native-sdk 1.374.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 (137) hide show
  1. package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +1 -21
  2. package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
  3. package/android/src/main/java/com/trustchex/reactnativesdk/opencv/OpenCVModule.kt +636 -301
  4. package/ios/Camera/TrustchexCameraView.swift +9 -20
  5. package/ios/MLKit/MLKitModule.swift +1 -1
  6. package/ios/OpenCV/OpenCVHelper.h +0 -7
  7. package/ios/OpenCV/OpenCVHelper.mm +0 -60
  8. package/ios/OpenCV/OpenCVModule.h +0 -4
  9. package/ios/OpenCV/OpenCVModule.mm +440 -358
  10. package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
  11. package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
  12. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -29
  13. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -0
  14. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
  15. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +26 -6
  16. package/lib/module/Screens/Dynamic/VideoCallScreen.js +676 -0
  17. package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
  18. package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
  19. package/lib/module/Screens/Static/ResultScreen.js +27 -13
  20. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +51 -51
  21. package/lib/module/Shared/Animations/video-call.json +1 -0
  22. package/lib/module/Shared/Components/DebugNavigationPanel.js +180 -14
  23. package/lib/module/Shared/Components/DebugOverlay.js +541 -0
  24. package/lib/module/Shared/Components/EIDScanner.js +1 -4
  25. package/lib/module/Shared/Components/IdentityDocumentCamera.constants.js +44 -0
  26. package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +270 -0
  27. package/lib/module/Shared/Components/IdentityDocumentCamera.js +702 -1703
  28. package/lib/module/Shared/Components/IdentityDocumentCamera.types.js +3 -0
  29. package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +273 -0
  30. package/lib/module/Shared/Components/NavigationManager.js +15 -3
  31. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  32. package/lib/module/Shared/Libs/SignalingClient.js +128 -0
  33. package/lib/module/Shared/Libs/analytics.utils.js +4 -0
  34. package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
  35. package/lib/module/Shared/Libs/http-client.js +9 -0
  36. package/lib/module/Shared/Libs/promise.utils.js +16 -2
  37. package/lib/module/Shared/Libs/status-bar.utils.js +21 -0
  38. package/lib/module/Shared/Services/DataUploadService.js +294 -0
  39. package/lib/module/Shared/Services/VideoSessionService.js +156 -0
  40. package/lib/module/Shared/Services/WebRTCService.js +510 -0
  41. package/lib/module/Shared/Types/analytics.types.js +2 -0
  42. package/lib/module/Translation/Resources/en.js +20 -0
  43. package/lib/module/Translation/Resources/tr.js +20 -0
  44. package/lib/module/Trustchex.js +10 -0
  45. package/lib/module/version.js +1 -1
  46. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
  47. package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
  48. package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
  49. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  50. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  51. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  52. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  53. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
  54. package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
  55. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
  56. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  57. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  58. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  59. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -1
  60. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts +30 -0
  61. package/lib/typescript/src/Shared/Components/DebugOverlay.d.ts.map +1 -0
  62. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  63. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts +35 -0
  64. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.constants.d.ts.map +1 -0
  65. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts +3 -56
  66. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  67. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +88 -0
  68. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -0
  69. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts +116 -0
  70. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.types.d.ts.map +1 -0
  71. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +93 -0
  72. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -0
  73. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  74. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
  75. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  76. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
  77. package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
  78. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
  79. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  80. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  81. package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
  82. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
  83. package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
  84. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
  85. package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
  86. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
  87. package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
  88. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
  89. package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
  90. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +2 -0
  91. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
  92. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +4 -1
  93. package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
  94. package/lib/typescript/src/Translation/Resources/en.d.ts +20 -0
  95. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  96. package/lib/typescript/src/Translation/Resources/tr.d.ts +20 -0
  97. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  98. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  99. package/lib/typescript/src/version.d.ts +1 -1
  100. package/package.json +29 -2
  101. package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
  102. package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
  103. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +59 -33
  104. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +6 -0
  105. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
  106. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +34 -6
  107. package/src/Screens/Dynamic/VideoCallScreen.tsx +764 -0
  108. package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
  109. package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
  110. package/src/Screens/Static/ResultScreen.tsx +58 -23
  111. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +58 -72
  112. package/src/Shared/Animations/video-call.json +1 -0
  113. package/src/Shared/Components/DebugNavigationPanel.tsx +185 -9
  114. package/src/Shared/Components/DebugOverlay.tsx +656 -0
  115. package/src/Shared/Components/EIDScanner.tsx +1 -5
  116. package/src/Shared/Components/IdentityDocumentCamera.constants.ts +44 -0
  117. package/src/Shared/Components/IdentityDocumentCamera.flows.ts +342 -0
  118. package/src/Shared/Components/IdentityDocumentCamera.tsx +1089 -2465
  119. package/src/Shared/Components/IdentityDocumentCamera.types.ts +136 -0
  120. package/src/Shared/Components/IdentityDocumentCamera.utils.ts +364 -0
  121. package/src/Shared/Components/NavigationManager.tsx +14 -1
  122. package/src/Shared/Contexts/AppContext.ts +2 -0
  123. package/src/Shared/Libs/SignalingClient.ts +189 -0
  124. package/src/Shared/Libs/analytics.utils.ts +4 -0
  125. package/src/Shared/Libs/deeplink.utils.ts +12 -1
  126. package/src/Shared/Libs/http-client.ts +10 -0
  127. package/src/Shared/Libs/promise.utils.ts +16 -2
  128. package/src/Shared/Libs/status-bar.utils.ts +19 -0
  129. package/src/Shared/Services/DataUploadService.ts +395 -0
  130. package/src/Shared/Services/VideoSessionService.ts +190 -0
  131. package/src/Shared/Services/WebRTCService.ts +636 -0
  132. package/src/Shared/Types/analytics.types.ts +2 -0
  133. package/src/Shared/Types/identificationInfo.ts +5 -1
  134. package/src/Translation/Resources/en.ts +25 -0
  135. package/src/Translation/Resources/tr.ts +27 -0
  136. package/src/Trustchex.tsx +12 -2
  137. package/src/version.ts +1 -1
@@ -7,22 +7,38 @@ import {
7
7
  StatusBar,
8
8
  TouchableOpacity,
9
9
  type NativeSyntheticEvent,
10
+ type LayoutChangeEvent,
10
11
  } from 'react-native';
11
12
  import { SafeAreaView } from 'react-native-safe-area-context';
12
13
  import {
13
14
  TrustchexCamera,
14
15
  type TrustchexCameraHandle,
15
16
  type Frame,
17
+ type NativeTextBlock,
16
18
  } from '../../Shared/Components/TrustchexCamera';
17
19
  import mrzUtils from '../../Shared/Libs/mrz.utils';
20
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
21
+
22
+ interface MrzOverlayBlock extends NativeTextBlock {
23
+ isMrz: boolean;
24
+ }
18
25
 
19
26
  const MRZTestScreen = () => {
27
+ useKeepAwake();
20
28
  const cameraRef = useRef<TrustchexCameraHandle>(null);
21
29
  const [mrzText, setMrzText] = useState<string>('Waiting for MRZ...');
22
30
  const [mrzRawText, setMrzRawText] = useState<string>('');
23
31
  const [mrzValid, setMrzValid] = useState<boolean>(false);
24
32
  const [rawHistory, setRawHistory] = useState<string[]>([]);
25
33
  const [isPaused, setIsPaused] = useState<boolean>(false);
34
+ const [overlayBlocks, setOverlayBlocks] = useState<MrzOverlayBlock[]>([]);
35
+ const [cameraLayout, setCameraLayout] = useState({ width: 1, height: 1 });
36
+ const [frameSize, setFrameSize] = useState({ width: 1080, height: 1920 });
37
+
38
+ const handleCameraLayout = useCallback((e: LayoutChangeEvent) => {
39
+ const { width, height } = e.nativeEvent.layout;
40
+ setCameraLayout({ width, height });
41
+ }, []);
26
42
 
27
43
  const handleFrame = useCallback(
28
44
  (event: NativeSyntheticEvent<{ frame: Frame }>) => {
@@ -32,6 +48,28 @@ const MRZTestScreen = () => {
32
48
 
33
49
  const frame = event.nativeEvent.frame;
34
50
 
51
+ if (frame.width && frame.height) {
52
+ setFrameSize({ width: frame.width, height: frame.height });
53
+ }
54
+
55
+ if (frame.textBlocks && frame.textBlocks.length > 0) {
56
+ const frameHeight = frame.height;
57
+ const bottomThreshold = frameHeight * 0.5;
58
+ const blocks: MrzOverlayBlock[] = frame.textBlocks.map((block) => {
59
+ const blockText = block.text || '';
60
+ return {
61
+ ...block,
62
+ isMrz:
63
+ blockText.includes('<') &&
64
+ blockText.length >= 12 &&
65
+ (block.blockFrame ? block.blockFrame.y > bottomThreshold : false),
66
+ };
67
+ });
68
+ setOverlayBlocks(blocks);
69
+ } else {
70
+ setOverlayBlocks([]);
71
+ }
72
+
35
73
  if (frame.resultText) {
36
74
  setRawHistory((prev) => {
37
75
  if (prev[0] === frame.resultText) {
@@ -86,18 +124,47 @@ const MRZTestScreen = () => {
86
124
  backgroundColor="transparent"
87
125
  translucent
88
126
  />
89
- <TrustchexCamera
90
- ref={cameraRef}
91
- style={styles.camera}
92
- cameraType="back"
93
- enableFrameProcessing={true}
94
- enableFaceDetection={false}
95
- enableTextRecognition={true}
96
- enableBarcodeScanning={false}
97
- includeBase64={false}
98
- targetFps={10}
99
- onFrameAvailable={handleFrame}
100
- />
127
+ <View style={styles.cameraWrapper} onLayout={handleCameraLayout}>
128
+ <TrustchexCamera
129
+ ref={cameraRef}
130
+ style={StyleSheet.absoluteFill}
131
+ cameraType="back"
132
+ enableFrameProcessing={true}
133
+ enableFaceDetection={false}
134
+ enableTextRecognition={true}
135
+ enableBarcodeScanning={false}
136
+ includeBase64={false}
137
+ targetFps={10}
138
+ onFrameAvailable={handleFrame}
139
+ />
140
+ {overlayBlocks.map((block, i) => {
141
+ const bf = block.blockFrame;
142
+ if (!bf) return null;
143
+ const scaleX = cameraLayout.width / frameSize.width;
144
+ const scaleY = cameraLayout.height / frameSize.height;
145
+ return (
146
+ <View
147
+ key={i}
148
+ style={[
149
+ styles.boundingBox,
150
+ block.isMrz ? styles.boundingBoxMrz : styles.boundingBoxText,
151
+ {
152
+ left: bf.x * scaleX,
153
+ top: bf.y * scaleY,
154
+ width: bf.width * scaleX,
155
+ height: bf.height * scaleY,
156
+ },
157
+ ]}
158
+ >
159
+ {block.isMrz && (
160
+ <View style={styles.blockLabel}>
161
+ <Text style={styles.blockLabelText}>MRZ</Text>
162
+ </View>
163
+ )}
164
+ </View>
165
+ );
166
+ })}
167
+ </View>
101
168
 
102
169
  <SafeAreaView style={styles.mrzPanel} edges={['bottom']}>
103
170
  <ScrollView style={styles.scrollView}>
@@ -152,8 +219,35 @@ const styles = StyleSheet.create({
152
219
  flex: 1,
153
220
  backgroundColor: '#000000',
154
221
  },
155
- camera: {
222
+ cameraWrapper: {
156
223
  flex: 2,
224
+ position: 'relative',
225
+ },
226
+ boundingBox: {
227
+ position: 'absolute',
228
+ borderWidth: 1.5,
229
+ },
230
+ boundingBoxText: {
231
+ borderColor: 'rgba(255, 255, 255, 0.25)',
232
+ },
233
+ boundingBoxMrz: {
234
+ borderColor: '#FFA500',
235
+ borderWidth: 2,
236
+ backgroundColor: 'rgba(255, 165, 0, 0.07)',
237
+ },
238
+ blockLabel: {
239
+ position: 'absolute',
240
+ top: -14,
241
+ left: 0,
242
+ backgroundColor: '#FFA500',
243
+ paddingHorizontal: 4,
244
+ paddingVertical: 1,
245
+ borderRadius: 2,
246
+ },
247
+ blockLabelText: {
248
+ color: '#000000',
249
+ fontSize: 8,
250
+ fontWeight: 'bold',
157
251
  },
158
252
  mrzPanel: {
159
253
  flex: 1,
@@ -16,6 +16,7 @@ import { getI18n, useTranslation } from 'react-i18next';
16
16
  import StyledButton from '../../Shared/Components/StyledButton';
17
17
  import NativeDeviceInfo from '../../Shared/Libs/native-device-info.utils';
18
18
  import { speakWithDebounce } from '../../Shared/Libs/tts.utils';
19
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
19
20
  import {
20
21
  trackFunnelStep,
21
22
  useScreenTracking,
@@ -24,8 +25,10 @@ import {
24
25
  trackVerificationComplete,
25
26
  trackError,
26
27
  } from '../../Shared/Libs/analytics.utils';
28
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
27
29
 
28
30
  const ContractAcceptanceScreen = () => {
31
+ useKeepAwake();
29
32
  const [isEnabled, setIsEnabled] = useState(false);
30
33
  const [isReady, setIsReady] = useState(false);
31
34
  const appContext = useContext(AppContext);
@@ -39,6 +42,9 @@ const ContractAcceptanceScreen = () => {
39
42
  // Track screen view and exit
40
43
  useScreenTracking('contract_acceptance');
41
44
 
45
+ // Configure status bar for white background
46
+ useStatusBarWhiteBackground();
47
+
42
48
  useEffect(() => {
43
49
  const contracts = appContext.currentWorkflowStep?.data?.contracts;
44
50
  if (!contracts) {
@@ -93,41 +99,58 @@ const ContractAcceptanceScreen = () => {
93
99
  return await NativeDeviceInfo.generateHumanReadableIdentifier();
94
100
  }, []);
95
101
 
102
+ // Early return if workflow step data is not available (after all hooks)
103
+ if (!appContext.currentWorkflowStep || !appContext.currentWorkflowStep.data) {
104
+ return null;
105
+ }
106
+
96
107
  return (
97
108
  <SafeAreaView style={styles.container}>
98
- <WebView
99
- source={{
100
- uri: contractUrl || '',
101
- }}
102
- javaScriptEnabled={true}
103
- domStorageEnabled={false}
104
- scalesPageToFit={false}
105
- scrollEnabled={true}
106
- style={styles.webView}
107
- onError={(syntheticEvent) => {
108
- const { nativeEvent } = syntheticEvent;
109
- trackError(
110
- 'CONTRACT_WEBVIEW_ERROR',
111
- nativeEvent.description || 'Failed to load contract',
112
- 'contract_acceptance',
113
- 'medium',
114
- { recoverable: true, userAction: 'load_contract' }
115
- );
116
- }}
117
- onLoadEnd={(event) => {
118
- if (event.nativeEvent.url === contractUrl) {
119
- setIsReady(true);
120
-
121
- // Track contract acceptance started
122
- trackVerificationStart('CONTRACT_ACCEPTANCE');
123
-
124
- if (appContext.currentWorkflowStep?.data?.voiceGuidanceActive) {
125
- speakWithDebounce(t('termsOfUseAndDataPrivacyScreen.footerText'));
109
+ <View
110
+ style={[
111
+ styles.webViewContainer,
112
+ {
113
+ paddingHorizontal: Math.max(insets.left, insets.right),
114
+ paddingTop: insets.top,
115
+ },
116
+ ]}
117
+ >
118
+ <WebView
119
+ source={{
120
+ uri: contractUrl || '',
121
+ }}
122
+ javaScriptEnabled={true}
123
+ domStorageEnabled={false}
124
+ scalesPageToFit={false}
125
+ scrollEnabled={true}
126
+ style={styles.webView}
127
+ onError={(syntheticEvent) => {
128
+ const { nativeEvent } = syntheticEvent;
129
+ trackError(
130
+ 'CONTRACT_WEBVIEW_ERROR',
131
+ nativeEvent.description || 'Failed to load contract',
132
+ 'contract_acceptance',
133
+ 'medium',
134
+ { recoverable: true, userAction: 'load_contract' }
135
+ );
136
+ }}
137
+ onLoadEnd={(event) => {
138
+ if (event.nativeEvent.url === contractUrl) {
139
+ setIsReady(true);
140
+
141
+ // Track contract acceptance started
142
+ trackVerificationStart('CONTRACT_ACCEPTANCE');
143
+
144
+ if (appContext.currentWorkflowStep?.data?.voiceGuidanceActive) {
145
+ speakWithDebounce(
146
+ t('termsOfUseAndDataPrivacyScreen.footerText')
147
+ );
148
+ }
126
149
  }
127
- }
128
- }}
129
- onScroll={hasReachedEnd}
130
- />
150
+ }}
151
+ onScroll={hasReachedEnd}
152
+ />
153
+ </View>
131
154
  <View style={[styles.footer, { paddingBottom: insets.bottom }]}>
132
155
  <Text style={styles.footerText}>
133
156
  {t('termsOfUseAndDataPrivacyScreen.footerText')}
@@ -183,10 +206,13 @@ const styles = StyleSheet.create({
183
206
  container: {
184
207
  flex: 1,
185
208
  },
209
+ webViewContainer: {
210
+ flex: 1,
211
+ width: '100%',
212
+ },
186
213
  webView: {
187
214
  flex: 1,
188
215
  width: '100%',
189
- height: '100%',
190
216
  resizeMode: 'contain',
191
217
  },
192
218
  footer: {
@@ -17,8 +17,10 @@ import {
17
17
  trackVerificationStart,
18
18
  trackVerificationComplete,
19
19
  } from '../../Shared/Libs/analytics.utils';
20
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
20
21
 
21
22
  const IdentityDocumentEIDScanningScreen = () => {
23
+ useKeepAwake();
22
24
  const appContext = useContext(AppContext);
23
25
  const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
24
26
  const insets = useSafeAreaInsets();
@@ -77,6 +79,10 @@ const IdentityDocumentEIDScanningScreen = () => {
77
79
  );
78
80
  }, [appContext.currentWorkflowStep]);
79
81
 
82
+ if (!appContext.currentWorkflowStep) {
83
+ return null;
84
+ }
85
+
80
86
  return (
81
87
  <SafeAreaView style={styles.safeAreaContainer}>
82
88
  {documentNumber && dateOfBirth && dateOfExpiry ? (
@@ -17,8 +17,10 @@ import {
17
17
  trackVerificationStart,
18
18
  trackVerificationComplete,
19
19
  } from '../../Shared/Libs/analytics.utils';
20
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
20
21
 
21
22
  const IdentityDocumentScanningScreen = () => {
23
+ useKeepAwake();
22
24
  const [idFrontSideData, setIDFrontSideData] =
23
25
  useState<DocumentScannedData | null>(null);
24
26
  const [idBackSideData, setIDBackSideData] =
@@ -145,6 +147,10 @@ const IdentityDocumentScanningScreen = () => {
145
147
  );
146
148
  }, [appContext.currentWorkflowStep]);
147
149
 
150
+ if (!appContext.currentWorkflowStep) {
151
+ return null;
152
+ }
153
+
148
154
  return (
149
155
  <SafeAreaView style={styles.container}>
150
156
  <IdentityDocumentCamera
@@ -8,7 +8,14 @@ import React, {
8
8
  useCallback,
9
9
  useRef,
10
10
  } from 'react';
11
- import { StyleSheet, Text, View, Dimensions, Vibration } from 'react-native';
11
+ import {
12
+ StyleSheet,
13
+ Text,
14
+ View,
15
+ Dimensions,
16
+ Vibration,
17
+ StatusBar,
18
+ } from 'react-native';
12
19
  import {
13
20
  useSafeAreaInsets,
14
21
  SafeAreaView,
@@ -25,12 +32,14 @@ import { useTranslation } from 'react-i18next';
25
32
  import StyledButton from '../../Shared/Components/StyledButton';
26
33
  import LottieView from 'lottie-react-native';
27
34
  import { speak, resetLastMessage } from '../../Shared/Libs/tts.utils';
35
+ import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
28
36
  import {
29
37
  trackFunnelStep,
30
38
  useScreenTracking,
31
39
  trackVerificationStart,
32
40
  trackVerificationComplete,
33
41
  } from '../../Shared/Libs/analytics.utils';
42
+ import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
34
43
 
35
44
  const { width: windowWidth, height: windowHeight } = Dimensions.get('window');
36
45
 
@@ -78,6 +87,7 @@ type PossibleActions = {
78
87
  }[keyof Actions];
79
88
 
80
89
  const LivenessDetectionScreen = () => {
90
+ useKeepAwake();
81
91
  const [camera, setCamera] = useState<TrustchexCameraHandle | null>(null);
82
92
  const navigation = useNavigation();
83
93
  const appContext = useContext(AppContext);
@@ -90,6 +100,9 @@ const LivenessDetectionScreen = () => {
90
100
  // Track screen view and exit
91
101
  useScreenTracking('liveness_detection');
92
102
 
103
+ // Configure status bar for white background
104
+ useStatusBarWhiteBackground();
105
+
93
106
  const [initialState, setInitialState] = useState<StateType>({
94
107
  brightnessLow: false,
95
108
  faceDetected: false,
@@ -142,7 +155,21 @@ const LivenessDetectionScreen = () => {
142
155
  const [instructionList, setInstructionList] = useState<string[]>([]);
143
156
  const [hasGuideShown, setHasGuideShown] = useState(false);
144
157
  const stoppingRecordingRef = useRef(false); // Track if we're already stopping to prevent multiple calls
145
- const lastVoiceGuidanceMessage = useRef<string>('');
158
+
159
+ // Configure status bar for white background
160
+ useStatusBarWhiteBackground();
161
+
162
+ // Update status bar when guide visibility changes
163
+ useEffect(() => {
164
+ if (hasGuideShown) {
165
+ // Camera view - use light icons
166
+ StatusBar.setBarStyle('light-content', true);
167
+ } else {
168
+ // Guide screen with white background - use dark icons
169
+ StatusBar.setBarStyle('dark-content', true);
170
+ StatusBar.setBackgroundColor('#ffffff', true);
171
+ }
172
+ }, [hasGuideShown]);
146
173
 
147
174
  useEffect(() => {
148
175
  const il = Object.keys(instructions)
@@ -175,7 +202,6 @@ const LivenessDetectionScreen = () => {
175
202
  processComplete: false,
176
203
  videoPath: '',
177
204
  });
178
- lastVoiceGuidanceMessage.current = '';
179
205
  resetLastMessage();
180
206
  }, [
181
207
  instructions,
@@ -350,7 +376,6 @@ const LivenessDetectionScreen = () => {
350
376
  const nextInstruction = state.instructionList[nextInstructionIndex];
351
377
 
352
378
  // Reset TTS state when moving to any new instruction to ensure it speaks
353
- lastVoiceGuidanceMessage.current = '';
354
379
  resetLastMessage();
355
380
 
356
381
  // Calculate progress based on actual action steps (excluding START and FINISH)
@@ -404,8 +429,7 @@ const LivenessDetectionScreen = () => {
404
429
  // Let the instruction advance first, then speak the next instruction
405
430
 
406
431
  // Only speak if message changed and is not empty
407
- if (text && text !== lastVoiceGuidanceMessage.current) {
408
- lastVoiceGuidanceMessage.current = text;
432
+ if (text) {
409
433
  // Bypass interval for liveness instructions to ensure all instructions are spoken
410
434
  speak(text, true);
411
435
  }
@@ -818,6 +842,10 @@ const LivenessDetectionScreen = () => {
818
842
  // eslint-disable-next-line react-hooks/exhaustive-deps
819
843
  }, []); // Empty array = only run on mount/unmount
820
844
 
845
+ if (!appContext.currentWorkflowStep || !appContext.currentWorkflowStep.data) {
846
+ return null;
847
+ }
848
+
821
849
  return (
822
850
  <>
823
851
  {hasGuideShown ? (