@trustchex/react-native-sdk 1.253.0 → 1.266.1

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 (88) hide show
  1. package/README.md +43 -2
  2. package/android/src/main/java/com/trustchex/reactnativesdk/DeviceBrightnessModule.kt +66 -0
  3. package/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKPackage.kt +12 -0
  4. package/ios/DeviceBrightnessModule.h +4 -0
  5. package/ios/DeviceBrightnessModule.m +27 -0
  6. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +25 -0
  7. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +19 -0
  8. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +19 -0
  9. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +18 -5
  10. package/lib/module/Screens/Static/QrCodeScanningScreen.js +10 -2
  11. package/lib/module/Screens/Static/ResultScreen.js +52 -3
  12. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +41 -2
  13. package/lib/module/Shared/Components/EIDScanner.js +63 -3
  14. package/lib/module/Shared/Components/FaceCamera.js +69 -4
  15. package/lib/module/Shared/Components/IdentityDocumentCamera.js +4 -1
  16. package/lib/module/Shared/Components/NavigationManager.js +2 -0
  17. package/lib/module/Shared/Components/QrCodeScannerCamera.js +2 -0
  18. package/lib/module/Shared/Contexts/AppContext.js +3 -1
  19. package/lib/module/Shared/Libs/analytics.utils.js +430 -0
  20. package/lib/module/Shared/Libs/camera.utils.js +58 -2
  21. package/lib/module/Shared/Libs/deeplink.utils.js +8 -0
  22. package/lib/module/Shared/Libs/http-client.js +89 -28
  23. package/lib/module/Shared/Services/AnalyticsService.js +404 -0
  24. package/lib/module/Shared/Types/analytics.types.js +111 -0
  25. package/lib/module/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.js +1 -0
  26. package/lib/module/Translation/index.js +5 -0
  27. package/lib/module/Trustchex.js +47 -4
  28. package/lib/module/index.js +3 -0
  29. package/lib/module/version.js +5 -0
  30. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  31. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  32. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  33. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  34. package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
  35. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  36. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  37. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  38. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts +7 -1
  39. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  40. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  41. package/lib/typescript/src/Shared/Components/QrCodeScannerCamera.d.ts.map +1 -1
  42. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +2 -0
  43. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  44. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts +98 -0
  45. package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -0
  46. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +19 -1
  47. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +1 -1
  48. package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
  49. package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
  50. package/lib/typescript/src/Shared/Services/AnalyticsService.d.ts +86 -0
  51. package/lib/typescript/src/Shared/Services/AnalyticsService.d.ts.map +1 -0
  52. package/lib/typescript/src/Shared/Types/analytics.types.d.ts +146 -0
  53. package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -0
  54. package/lib/typescript/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.d.ts.map +1 -1
  55. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  56. package/lib/typescript/src/Translation/index.d.ts.map +1 -1
  57. package/lib/typescript/src/Trustchex.d.ts +1 -0
  58. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  59. package/lib/typescript/src/index.d.ts +4 -0
  60. package/lib/typescript/src/index.d.ts.map +1 -1
  61. package/lib/typescript/src/version.d.ts +2 -0
  62. package/lib/typescript/src/version.d.ts.map +1 -0
  63. package/package.json +6 -2
  64. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +35 -1
  65. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +30 -0
  66. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +30 -0
  67. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +30 -4
  68. package/src/Screens/Static/QrCodeScanningScreen.tsx +12 -2
  69. package/src/Screens/Static/ResultScreen.tsx +79 -4
  70. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +65 -10
  71. package/src/Shared/Components/EIDScanner.tsx +132 -3
  72. package/src/Shared/Components/FaceCamera.tsx +77 -2
  73. package/src/Shared/Components/IdentityDocumentCamera.tsx +4 -4
  74. package/src/Shared/Components/NavigationManager.tsx +2 -0
  75. package/src/Shared/Components/QrCodeScannerCamera.tsx +2 -0
  76. package/src/Shared/Contexts/AppContext.ts +4 -0
  77. package/src/Shared/Libs/analytics.utils.ts +644 -0
  78. package/src/Shared/Libs/camera.utils.ts +74 -2
  79. package/src/Shared/Libs/deeplink.utils.ts +5 -0
  80. package/src/Shared/Libs/http-client.ts +105 -31
  81. package/src/Shared/Services/AnalyticsService.ts +470 -0
  82. package/src/Shared/Types/analytics.types.ts +179 -0
  83. package/src/Shared/VisionCameraPlugins/BarcodeScanner/hooks/useCameraPermissions.ts +1 -0
  84. package/src/Translation/Resources/tr.ts +2 -1
  85. package/src/Translation/index.ts +9 -0
  86. package/src/Trustchex.tsx +54 -2
  87. package/src/index.tsx +33 -0
  88. package/src/version.ts +3 -0
@@ -4,17 +4,19 @@ import React, { useCallback, useContext, useEffect, useMemo, useRef, useState }
4
4
  import { SafeAreaView, Text, StyleSheet, Alert, View, Image, Dimensions, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
5
5
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
6
  import AppContext from "../../Shared/Contexts/AppContext.js";
7
- import httpClient, { NotFoundError } from "../../Shared/Libs/http-client.js";
7
+ import httpClient, { BadRequestError, NotFoundError } from "../../Shared/Libs/http-client.js";
8
8
  import { useTranslation } from 'react-i18next';
9
9
  import LanguageSelector from "../../Shared/Components/LanguageSelector.js";
10
10
  import NavigationManager from "../../Shared/Components/NavigationManager.js";
11
11
  import StyledButton from "../../Shared/Components/StyledButton.js";
12
12
  import StyledTextInput from "../../Shared/Components/StyledTextInput.js";
13
+ import { analyticsService } from "../../Shared/Services/AnalyticsService.js";
13
14
  import { useTheme } from "../../Shared/Contexts/ThemeContext.js";
14
15
  import LottieView from 'lottie-react-native';
15
16
  import { getSimulatedDemoData, isDemoSession } from "../../Shared/Libs/demo.utils.js";
16
17
  import { useNavigation } from '@react-navigation/native';
17
18
  import { SESSION_CODE_CHARSET_PATTERN, SESSION_CODE_VALIDATION_PATTERN } from "../../Shared/Constants/validation.constants.js";
19
+ import { trackError, trackFunnelStep, useScreenTracking } from "../../Shared/Libs/analytics.utils.js";
18
20
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
19
21
  const VerificationSessionCheckScreen = () => {
20
22
  const [sessionCode, setSessionCode] = useState('');
@@ -33,6 +35,9 @@ const VerificationSessionCheckScreen = () => {
33
35
  const insets = useSafeAreaInsets();
34
36
  const theme = useTheme();
35
37
  const primaryColor = theme.colors.primary;
38
+
39
+ // Track screen view and exit
40
+ useScreenTracking('verification_session_check');
36
41
  const apiUrl = useMemo(() => `${appContext.baseUrl}/api/app/mobile`, [appContext.baseUrl]);
37
42
  const validateSessionCode = useCallback(value => {
38
43
  return SESSION_CODE_VALIDATION_PATTERN.test(value.toUpperCase());
@@ -43,21 +48,37 @@ const VerificationSessionCheckScreen = () => {
43
48
  return response;
44
49
  } catch (error) {
45
50
  if (error instanceof NotFoundError) {
51
+ // User entered invalid session ID - expected user behavior, not actionable
46
52
  Alert.alert(t('general.error'), t('verificationSessionCheckScreen.noVerificationSessionFound'));
53
+ } else {
54
+ trackError('SESSION_CHECK_ERROR', error instanceof Error ? error.message : 'Unknown error', 'verification_session_check', 'high', {
55
+ recoverable: false,
56
+ userAction: 'check_session'
57
+ });
47
58
  }
48
59
  }
49
60
  }, [apiUrl, appContext.isDemoSession, t]);
50
61
  const getSessionByCode = useCallback(async inputCode => {
51
62
  try {
52
63
  appContext.isDemoSession = isDemoSession(inputCode);
64
+ analyticsService.setDemoSession(appContext.isDemoSession);
53
65
  const response = await httpClient.get(`${apiUrl}/verification-sessions?sessionCode=${inputCode.toUpperCase()}`, appContext.isDemoSession ? getSimulatedDemoData('GET_SESSION_BY_CODE') : undefined);
54
66
  if (!response || !response.id) {
55
67
  throw new NotFoundError();
56
68
  }
69
+
70
+ // Track successful session start as funnel step
71
+ trackFunnelStep('Session Started', 1, 5, undefined, true);
57
72
  return response;
58
73
  } catch (error) {
59
74
  if (error instanceof NotFoundError) {
75
+ // User entered invalid session code - expected user behavior, not actionable
60
76
  Alert.alert(t('general.error'), t('verificationSessionCheckScreen.noVerificationSessionFound'));
77
+ } else {
78
+ trackError('SESSION_CODE_CHECK_ERROR', error instanceof Error ? error.message : 'Unknown error', 'verification_session_check', 'high', {
79
+ recoverable: false,
80
+ userAction: 'check_session_code'
81
+ });
61
82
  }
62
83
  }
63
84
  }, [apiUrl, appContext, t]);
@@ -67,8 +88,13 @@ const VerificationSessionCheckScreen = () => {
67
88
  return true;
68
89
  } catch (error) {
69
90
  if (error instanceof NotFoundError) {
91
+ // Session expired or invalid - expected behavior, not actionable
70
92
  Alert.alert(t('general.error'), t('verificationSessionCheckScreen.noVerificationSessionFound'));
71
93
  } else {
94
+ trackError('VERIFICATION_CODE_SEND_FAILED', 'Failed to send verification code', 'verification_session_check', 'high', {
95
+ recoverable: true,
96
+ userAction: 'send_verification_code'
97
+ });
72
98
  Alert.alert(t('general.error'), t('verificationSessionCheckScreen.cannotSendVerificationCode'));
73
99
  }
74
100
  }
@@ -84,7 +110,15 @@ const VerificationSessionCheckScreen = () => {
84
110
  return response;
85
111
  } catch (error) {
86
112
  if (error instanceof NotFoundError) {
113
+ // Session expired or invalid - expected behavior, not actionable
87
114
  Alert.alert(t('general.error'), t('verificationSessionCheckScreen.noVerificationSessionFound'));
115
+ } else if (error instanceof BadRequestError) {
116
+ // Wrong OTP code - expected user behavior, not actionable
117
+ } else {
118
+ trackError('VERIFIED_SESSION_CHECK_ERROR', error instanceof Error ? error.message : 'Unknown error', 'verification_session_check', 'high', {
119
+ recoverable: false,
120
+ userAction: 'check_verified_session'
121
+ });
88
122
  }
89
123
  }
90
124
  }, [apiUrl, appContext.isDemoSession, t]);
@@ -176,7 +210,11 @@ const VerificationSessionCheckScreen = () => {
176
210
  setIsCheckingSession(true);
177
211
  const session = await getSessionByCode(alphanumericText);
178
212
  if (session?.id) {
179
- appContext.identificationInfo.sessionId = session.id;
213
+ if (appContext.setSessionId) {
214
+ appContext.setSessionId(session.id);
215
+ } else {
216
+ appContext.identificationInfo.sessionId = session.id;
217
+ }
180
218
  } else {
181
219
  setSessionCode('');
182
220
  }
@@ -240,6 +278,7 @@ const VerificationSessionCheckScreen = () => {
240
278
  setIsCodeSent(false);
241
279
  navigationManagerRef.current?.navigateToNextStep();
242
280
  } else {
281
+ // User entered wrong OTP code - expected behavior, not actionable
243
282
  appContext.onError?.('Invalid OTP code');
244
283
  Alert.alert(t('general.error'), t('verificationSessionCheckScreen.codeError'));
245
284
  setCode('');
@@ -3,6 +3,7 @@
3
3
  import React, { useState, useEffect, useCallback } from 'react';
4
4
  import { Alert, View, Text, Image, StyleSheet } from 'react-native';
5
5
  import NFCManager from 'react-native-nfc-manager';
6
+ import DeviceInfo from 'react-native-device-info';
6
7
  import { eidReader } from "../EIDReader/eidReader.js";
7
8
  import NativeProgressBar from "./NativeProgressBar.js";
8
9
  import { useTranslation } from 'react-i18next';
@@ -11,6 +12,7 @@ import StyledButton from "./StyledButton.js";
11
12
  import LottieView from 'lottie-react-native';
12
13
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
13
14
  import { speakWithDebounce } from "../Libs/tts.utils.js";
15
+ import { trackEIDScanStart, trackEIDScanComplete, trackEIDScanFailed } from "../Libs/analytics.utils.js";
14
16
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
15
17
  const EIDScanner = ({
16
18
  documentNumber,
@@ -35,33 +37,85 @@ const EIDScanner = ({
35
37
  } = useTranslation();
36
38
  const appContext = React.useContext(AppContext);
37
39
  const [voiceGuidanceMessage, setVoiceGuidanceMessage] = useState();
40
+ const [attemptNumber, setAttemptNumber] = useState(0);
38
41
  const readNFC = useCallback(async () => {
42
+ const startTime = Date.now();
43
+ const currentAttempt = attemptNumber + 1;
44
+ setAttemptNumber(currentAttempt);
39
45
  setIsScanning(true);
40
46
  setIsScanned(false);
41
47
  setProgress(0);
48
+
49
+ // Track EID scan start using analytics helper
50
+ const docType = documentType || 'UNKNOWN';
51
+
52
+ // If device doesn't support NFC or NFC is disabled, track and abort early
53
+ if (!hasNfc) {
54
+ await trackEIDScanFailed(docType, 'device_unsupported', 'Device does not support NFC', 0, currentAttempt, DeviceInfo.getModel(), DeviceInfo.getSystemVersion()).catch(() => {});
55
+
56
+ // Stop scanning - user will see the not-supported UI
57
+ setIsScanning(false);
58
+ setProgress(0);
59
+ return;
60
+ }
61
+ if (!isEnabled) {
62
+ await trackEIDScanFailed(docType, 'not_enabled', 'NFC is disabled on device', 0, currentAttempt, DeviceInfo.getModel(), DeviceInfo.getSystemVersion()).catch(() => {});
63
+
64
+ // Stop scanning - allow the UI to prompt user to enable NFC
65
+ setIsScanning(false);
66
+ setProgress(0);
67
+ return;
68
+ }
69
+ await trackEIDScanStart(docType, hasNfc, isEnabled, currentAttempt).catch(() => {});
42
70
  try {
43
71
  if (documentNumber && dateOfBirth && dateOfExpiry) {
44
72
  const passportData = await eidReader(documentNumber, dateOfBirth, dateOfExpiry, progressValue => {
45
73
  setProgress(progressValue);
46
74
  });
47
75
  if (passportData) {
76
+ const scanDuration = Date.now() - startTime;
48
77
  setDocumentFaceImage(passportData.faceImage);
49
78
  setDocumentMRZInfo(passportData.mrz);
50
79
  setDocumentFaceImageMimeType(passportData.mimeType);
51
80
  setIsScanned(true);
81
+
82
+ // Track successful EID scan
83
+ await trackEIDScanComplete(docType, scanDuration, currentAttempt).catch(() => {});
52
84
  }
53
85
  } else {
86
+ const scanDuration = Date.now() - startTime;
87
+ // MRZ input issue — track as NFC scan failure (unknown error type for NFC-specific stream)
88
+ await trackEIDScanFailed(docType, 'unknown', 'Invalid MRZ fields', scanDuration, currentAttempt, DeviceInfo.getModel(), DeviceInfo.getSystemVersion()).catch(() => {});
54
89
  Alert.alert(t('general.error'), t('eidScannerScreen.invalidMRZFields'));
55
90
  }
56
91
  } catch (error) {
57
- console.debug(error);
58
- // Ignore error
92
+ const scanDuration = Date.now() - startTime;
93
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
94
+
95
+ // Determine error type
96
+ let errorType = 'unknown';
97
+ if (errorMessage.includes('cancelled') || errorMessage.includes('cancel')) {
98
+ errorType = 'user_cancelled';
99
+ } else if (errorMessage.includes('timeout') || errorMessage.includes('time out')) {
100
+ errorType = 'timeout';
101
+ } else if (errorMessage.includes('not supported')) {
102
+ errorType = 'device_unsupported';
103
+ } else if (errorMessage.includes('not enabled') || errorMessage.includes('disabled')) {
104
+ errorType = 'not_enabled';
105
+ } else if (errorMessage.includes('read') || errorMessage.includes('communication')) {
106
+ errorType = 'reading_error';
107
+ }
108
+
109
+ // Track EID scan failure with detailed metadata
110
+ await trackEIDScanFailed(docType, errorType, errorMessage, scanDuration, currentAttempt, DeviceInfo.getModel(), DeviceInfo.getSystemVersion()).catch(() => {});
111
+ console.debug('NFC scan error:', error);
112
+ // Ignore error - let user retry
59
113
  } finally {
60
114
  setIsScanning(false);
61
115
  setProgress(0);
62
116
  setHasGuideShown(false);
63
117
  }
64
- }, [documentNumber, dateOfBirth, dateOfExpiry, t]);
118
+ }, [documentNumber, dateOfBirth, dateOfExpiry, documentType, hasNfc, isEnabled, attemptNumber, t]);
65
119
  const getFieldsFromMRZ = useCallback(mrz => {
66
120
  return {
67
121
  documentCode: mrz.getDocumentCode(),
@@ -81,8 +135,14 @@ const EIDScanner = ({
81
135
  const deviceIsSupported = await NFCManager.isSupported();
82
136
  setHasNFC(deviceIsSupported);
83
137
  isNFCSupported && isNFCSupported(deviceIsSupported);
138
+
139
+ // NFC not supported is a device limitation - not actionable by developers
140
+
84
141
  const deviceIsEnabled = await NFCManager.isEnabled();
85
142
  setIsEnabled(deviceIsEnabled);
143
+
144
+ // NFC disabled is a user setting - they can enable it in settings
145
+ // Not tracking as it's user-controllable
86
146
  };
87
147
  checkIsSupported();
88
148
  }, [isNFCSupported, isScanning]);
@@ -3,13 +3,13 @@
3
3
  import { useIsFocused } from '@react-navigation/native';
4
4
  import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
5
5
  import React, { useEffect, useState } from 'react';
6
- import { StyleSheet, Text, View, Platform, Linking, Dimensions, ActivityIndicator } from 'react-native';
6
+ import { StyleSheet, Text, View, Platform, Linking, Dimensions, ActivityIndicator, NativeModules } from 'react-native';
7
7
  import { useCameraDevice, useCameraPermission, Camera, useFrameProcessor, useCameraFormat, runAtTargetFps, useMicrophonePermission } from 'react-native-vision-camera';
8
8
  import { runAsync } from "../Libs/worklet.utils.js";
9
9
  import { useFaceDetector } from "../VisionCameraPlugins/FaceDetector/index.js";
10
10
  import { Worklets, useSharedValue } from 'react-native-worklets-core';
11
11
  import { crop } from "../VisionCameraPlugins/Cropper/index.js";
12
- import { isFrameBright } from "../Libs/camera.utils.js";
12
+ import { isCircularRegionBright } from "../Libs/camera.utils.js";
13
13
  import { useTranslation } from 'react-i18next';
14
14
  import StyledButton from "./StyledButton.js";
15
15
  import { useTheme } from "../Contexts/ThemeContext.js";
@@ -20,7 +20,8 @@ const windowWidth = Dimensions.get('window').width;
20
20
  const windowHeight = Dimensions.get('window').height;
21
21
  const FaceCamera = ({
22
22
  onFacesDetected,
23
- onCameraInitialized
23
+ onCameraInitialized,
24
+ previewRect
24
25
  }) => {
25
26
  useKeepAwake();
26
27
  const theme = useTheme();
@@ -84,11 +85,72 @@ const FaceCamera = ({
84
85
  setIsActive(false);
85
86
  };
86
87
  }, [device, format, isFocused]);
88
+
89
+ // Set screen brightness to maximum when camera is active
90
+ useEffect(() => {
91
+ const {
92
+ DeviceBrightness
93
+ } = NativeModules;
94
+ if (!DeviceBrightness) return;
95
+ let originalBrightness = -1;
96
+ const setBrightness = async () => {
97
+ if (isActive) {
98
+ try {
99
+ originalBrightness = await DeviceBrightness.getBrightness();
100
+ await DeviceBrightness.setBrightness(1.0);
101
+ } catch (error) {
102
+ console.log('Failed to set brightness:', error);
103
+ }
104
+ }
105
+ };
106
+ setBrightness();
107
+ return () => {
108
+ if (originalBrightness >= 0) {
109
+ DeviceBrightness.setBrightness(originalBrightness).catch(() => {});
110
+ }
111
+ };
112
+ }, [isActive]);
87
113
  const handleWorklet = frame => {
88
114
  'worklet';
89
115
 
90
116
  try {
91
- const isBright = isFrameBright(frame);
117
+ // Calculate brightness based on the circular preview area if provided
118
+ // Otherwise fall back to entire frame brightness
119
+ let isBright = false;
120
+ if (previewRect) {
121
+ // Convert preview rect from screen coordinates to frame coordinates
122
+ const scaleX = frame.width / windowWidth;
123
+ const scaleY = frame.height / windowHeight;
124
+ const frameCircleRect = {
125
+ minX: Math.floor(previewRect.minX * scaleX),
126
+ minY: Math.floor(previewRect.minY * scaleY),
127
+ width: Math.floor(previewRect.width * scaleX),
128
+ height: Math.floor(previewRect.height * scaleY)
129
+ };
130
+ isBright = isCircularRegionBright(frame, frameCircleRect, 60);
131
+ } else {
132
+ // Fallback: check entire frame brightness (use legacy method)
133
+ const buffer = frame.toArrayBuffer();
134
+ const data = new Uint8Array(buffer);
135
+ const width = frame.width;
136
+ const height = frame.height;
137
+ let luminanceSum = 0;
138
+ let pixelCount = 0;
139
+ const centerX = Math.floor(width / 2);
140
+ const centerY = Math.floor(height / 2);
141
+ const halfSizeX = Math.floor(width / 2);
142
+ const halfSizeY = Math.floor(height / 2);
143
+ for (let y = centerY - halfSizeY; y < centerY + halfSizeY; y++) {
144
+ for (let x = centerX - halfSizeX; x < centerX + halfSizeX; x++) {
145
+ const index = y * width + x;
146
+ if (data[index] !== undefined) {
147
+ luminanceSum += data[index];
148
+ pixelCount++;
149
+ }
150
+ }
151
+ }
152
+ isBright = luminanceSum / pixelCount > 60;
153
+ }
92
154
  const image = crop(frame, {
93
155
  cropRegion: {
94
156
  top: 0,
@@ -139,6 +201,7 @@ const FaceCamera = ({
139
201
  });
140
202
  }
141
203
  if (!cameraPermission.hasPermission) {
204
+ // Camera permission denied by user - their choice, not actionable
142
205
  return /*#__PURE__*/_jsxs(View, {
143
206
  style: styles.permissionContainer,
144
207
  children: [/*#__PURE__*/_jsx(Text, {
@@ -154,6 +217,7 @@ const FaceCamera = ({
154
217
  });
155
218
  }
156
219
  if (!microphonePermission.hasPermission) {
220
+ // Microphone permission denied by user - their choice, not actionable
157
221
  return /*#__PURE__*/_jsxs(View, {
158
222
  style: styles.permissionContainer,
159
223
  children: [/*#__PURE__*/_jsx(Text, {
@@ -169,6 +233,7 @@ const FaceCamera = ({
169
233
  });
170
234
  }
171
235
  if (device == null) {
236
+ // No camera device - device limitation, not actionable
172
237
  return /*#__PURE__*/_jsx(View, {
173
238
  style: styles.permissionContainer,
174
239
  children: /*#__PURE__*/_jsx(Text, {
@@ -299,7 +299,10 @@ const IdentityDocumentCamera = ({
299
299
 
300
300
  // Add validation for face bounds to prevent invalid crop operations
301
301
  if (face.bounds.x < 0 || face.bounds.y < 0 || face.bounds.width <= 0 || face.bounds.height <= 0 || face.bounds.x >= width || face.bounds.y >= height) {
302
- console.warn('Invalid face bounds detected, skipping face:', face.bounds);
302
+ // console.warn(
303
+ // 'Invalid face bounds detected, skipping face:',
304
+ // face.bounds
305
+ // );
303
306
  continue;
304
307
  }
305
308
 
@@ -7,6 +7,7 @@ import { View, StyleSheet, Alert } from 'react-native';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import i18n from "../../Translation/index.js";
9
9
  import StyledButton from "./StyledButton.js";
10
+ import { analyticsService } from "../Services/AnalyticsService.js";
10
11
 
11
12
  // Simple global navigation lock
12
13
  import { jsx as _jsx } from "react/jsx-runtime";
@@ -107,6 +108,7 @@ const NavigationManager = /*#__PURE__*/forwardRef(({
107
108
  appContext.currentWorkflowStep = undefined;
108
109
  appContext.workflowSteps = undefined;
109
110
  appContext.isDemoSession = false;
111
+ analyticsService.setDemoSession(false);
110
112
  appContext.identificationInfo = {
111
113
  sessionId: '',
112
114
  identificationId: '',
@@ -131,6 +131,7 @@ const QrCodeScannerCamera = ({
131
131
  });
132
132
  }
133
133
  if (!cameraPermission.hasPermission) {
134
+ // Camera permission denied by user - their choice, not actionable
134
135
  return /*#__PURE__*/_jsxs(View, {
135
136
  style: styles.permissionContainer,
136
137
  children: [/*#__PURE__*/_jsx(Text, {
@@ -146,6 +147,7 @@ const QrCodeScannerCamera = ({
146
147
  });
147
148
  }
148
149
  if (device == null) {
150
+ // No camera device - device limitation, not actionable
149
151
  return /*#__PURE__*/_jsx(View, {
150
152
  style: styles.permissionContainer,
151
153
  children: /*#__PURE__*/_jsx(TextView, {
@@ -23,5 +23,7 @@ export default /*#__PURE__*/createContext({
23
23
  workflowSteps: [],
24
24
  currentWorkflowStep: undefined,
25
25
  onCompleted: undefined,
26
- onError: undefined
26
+ onError: undefined,
27
+ setSessionId: undefined,
28
+ setBaseUrl: undefined
27
29
  });