@trustchex/react-native-sdk 1.334.0 → 1.354.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 (72) hide show
  1. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +8 -2
  2. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -1
  3. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -1
  4. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +29 -15
  5. package/lib/module/Screens/Static/OTPVerificationScreen.js +285 -0
  6. package/lib/module/Screens/Static/ResultScreen.js +90 -26
  7. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +48 -134
  8. package/lib/module/Shared/Components/DebugNavigationPanel.js +252 -0
  9. package/lib/module/Shared/Components/EIDScanner.js +142 -17
  10. package/lib/module/Shared/Components/FaceCamera.js +23 -11
  11. package/lib/module/Shared/Components/IdentityDocumentCamera.js +295 -44
  12. package/lib/module/Shared/Components/NavigationManager.js +19 -3
  13. package/lib/module/Shared/Config/camera-enhancement.config.js +58 -0
  14. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  15. package/lib/module/Shared/Libs/camera.utils.js +221 -1
  16. package/lib/module/Shared/Libs/frame-enhancement.utils.js +133 -0
  17. package/lib/module/Shared/Libs/mrz.utils.js +98 -1
  18. package/lib/module/Translation/Resources/en.js +30 -0
  19. package/lib/module/Translation/Resources/tr.js +30 -0
  20. package/lib/module/Trustchex.js +49 -39
  21. package/lib/module/version.js +1 -1
  22. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  23. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  24. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  25. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  26. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts +3 -0
  27. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -0
  28. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  29. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  30. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts +3 -0
  31. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -0
  32. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  33. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  34. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  35. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  36. package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts +54 -0
  37. package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts.map +1 -0
  38. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +2 -0
  39. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  40. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +65 -1
  41. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +1 -1
  42. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts +25 -0
  43. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts.map +1 -0
  44. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  45. package/lib/typescript/src/Translation/Resources/en.d.ts +30 -0
  46. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  47. package/lib/typescript/src/Translation/Resources/tr.d.ts +30 -0
  48. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  49. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  50. package/lib/typescript/src/version.d.ts +1 -1
  51. package/package.json +3 -3
  52. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +6 -2
  53. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +3 -1
  54. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +3 -1
  55. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +27 -17
  56. package/src/Screens/Static/OTPVerificationScreen.tsx +379 -0
  57. package/src/Screens/Static/ResultScreen.tsx +160 -101
  58. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +51 -196
  59. package/src/Shared/Components/DebugNavigationPanel.tsx +262 -0
  60. package/src/Shared/Components/EIDScanner.tsx +144 -19
  61. package/src/Shared/Components/FaceCamera.tsx +38 -21
  62. package/src/Shared/Components/IdentityDocumentCamera.tsx +399 -101
  63. package/src/Shared/Components/NavigationManager.tsx +19 -3
  64. package/src/Shared/Config/camera-enhancement.config.ts +46 -0
  65. package/src/Shared/Contexts/AppContext.ts +3 -0
  66. package/src/Shared/Libs/camera.utils.ts +240 -1
  67. package/src/Shared/Libs/frame-enhancement.utils.ts +217 -0
  68. package/src/Shared/Libs/mrz.utils.ts +78 -1
  69. package/src/Translation/Resources/en.ts +30 -0
  70. package/src/Translation/Resources/tr.ts +30 -0
  71. package/src/Trustchex.tsx +58 -46
  72. package/src/version.ts +1 -1
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { useState, useContext, useCallback, useEffect, useRef } from 'react';
4
4
  import { SafeAreaView, View, Text, StyleSheet } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
6
  import WebView from 'react-native-webview';
6
7
  import NavigationManager from "../../Shared/Components/NavigationManager.js";
7
8
  import AppContext from "../../Shared/Contexts/AppContext.js";
@@ -22,6 +23,7 @@ const ContractAcceptanceScreen = () => {
22
23
  const {
23
24
  t
24
25
  } = useTranslation();
26
+ const insets = useSafeAreaInsets();
25
27
 
26
28
  // Track screen view and exit
27
29
  useScreenTracking('contract_acceptance');
@@ -96,7 +98,9 @@ const ContractAcceptanceScreen = () => {
96
98
  },
97
99
  onScroll: hasReachedEnd
98
100
  }), /*#__PURE__*/_jsxs(View, {
99
- style: styles.footer,
101
+ style: [styles.footer, {
102
+ paddingBottom: insets.bottom
103
+ }],
100
104
  children: [/*#__PURE__*/_jsx(Text, {
101
105
  style: styles.footerText,
102
106
  children: t('termsOfUseAndDataPrivacyScreen.footerText')
@@ -158,7 +162,9 @@ const styles = StyleSheet.create({
158
162
  footer: {
159
163
  flex: 0,
160
164
  justifyContent: 'flex-end',
161
- padding: 10
165
+ paddingTop: 10,
166
+ paddingHorizontal: 20,
167
+ paddingBottom: 10
162
168
  },
163
169
  footerText: {
164
170
  textAlign: 'center',
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { useContext, useEffect, useState } from 'react';
4
4
  import { Alert, SafeAreaView, StyleSheet, View } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
6
  import EIDScanner from "../../Shared/Components/EIDScanner.js";
6
7
  import NavigationManager from "../../Shared/Components/NavigationManager.js";
7
8
  import AppContext from "../../Shared/Contexts/AppContext.js";
@@ -12,6 +13,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
13
  const IdentityDocumentEIDScanningScreen = () => {
13
14
  const appContext = useContext(AppContext);
14
15
  const navigationManagerRef = React.useRef(null);
16
+ const insets = useSafeAreaInsets();
15
17
  const [idFrontSideData, setIDFrontSideData] = useState(null);
16
18
  const [idBackSideData, setIDBackSideData] = useState(null);
17
19
  const [passportData, setPassportData] = useState();
@@ -131,7 +133,9 @@ const IdentityDocumentEIDScanningScreen = () => {
131
133
  },
132
134
  showDebugImages: false
133
135
  }), /*#__PURE__*/_jsx(View, {
134
- style: styles.footer,
136
+ style: [styles.footer, {
137
+ bottom: insets.bottom
138
+ }],
135
139
  children: /*#__PURE__*/_jsx(NavigationManager, {
136
140
  canSkipStep: !isNFCSupported && !!appContext.identificationInfo.scannedDocument?.faceImage && !!appContext.identificationInfo.scannedDocument?.frontImage && !!appContext.identificationInfo.scannedDocument?.mrzText,
137
141
  ref: navigationManagerRef
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { useContext, useEffect, useState } from 'react';
4
4
  import { Alert, SafeAreaView, StyleSheet, View } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
6
  import IdentityDocumentCamera from "../../Shared/Components/IdentityDocumentCamera.js";
6
7
  import AppContext from "../../Shared/Contexts/AppContext.js";
7
8
  import NavigationManager from "../../Shared/Components/NavigationManager.js";
@@ -17,6 +18,7 @@ const IdentityDocumentScanningScreen = () => {
17
18
  const {
18
19
  t
19
20
  } = useTranslation();
21
+ const insets = useSafeAreaInsets();
20
22
  const [allowedDocumentTypes, setAllowedDocumentTypes] = useState(null);
21
23
  const [allowedCountries, setAllowedCountries] = useState(null);
22
24
 
@@ -96,7 +98,9 @@ const IdentityDocumentScanningScreen = () => {
96
98
  },
97
99
  showDebugImages: false
98
100
  }), /*#__PURE__*/_jsx(View, {
99
- style: styles.footer,
101
+ style: [styles.footer, {
102
+ bottom: insets.bottom
103
+ }],
100
104
  children: /*#__PURE__*/_jsx(NavigationManager, {
101
105
  ref: navigationManagerRef
102
106
  })
@@ -4,6 +4,8 @@ import Svg, { Rect as SvgRect, Circle, Mask } from 'react-native-svg';
4
4
  import { useNavigation } from '@react-navigation/native';
5
5
  import React, { useState, useReducer, useContext, useEffect, useCallback } from 'react';
6
6
  import { Dimensions, Text, View, StyleSheet, Platform, Vibration } from 'react-native';
7
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
8
+ import { SafeAreaView } from 'react-native-safe-area-context';
7
9
  import NativeCircularProgress from "../../Shared/Components/NativeCircularProgress.js";
8
10
  import FaceCamera from "../../Shared/Components/FaceCamera.js";
9
11
  import NavigationManager from "../../Shared/Components/NavigationManager.js";
@@ -38,6 +40,7 @@ const LivenessDetectionScreen = () => {
38
40
  t
39
41
  } = useTranslation();
40
42
  const [isRecording, setIsRecording] = useState(false);
43
+ const insets = useSafeAreaInsets();
41
44
 
42
45
  // Track screen view and exit
43
46
  useScreenTracking('liveness_detection');
@@ -399,7 +402,7 @@ const LivenessDetectionScreen = () => {
399
402
  }
400
403
  }, [state.processComplete, state.videoPath, state.instructionList, appContext.identificationInfo, navigation, instructions]);
401
404
  return /*#__PURE__*/_jsxs(_Fragment, {
402
- children: [!hasGuideShown ? /*#__PURE__*/_jsxs(View, {
405
+ children: [!hasGuideShown ? /*#__PURE__*/_jsxs(SafeAreaView, {
403
406
  style: styles.guide,
404
407
  children: [/*#__PURE__*/_jsx(LottieView, {
405
408
  source: require('../../Shared/Animations/face-scan.json'),
@@ -427,12 +430,17 @@ const LivenessDetectionScreen = () => {
427
430
  style: styles.guideText,
428
431
  children: ["\u2022 ", t('livenessDetectionScreen.guidePoint4')]
429
432
  })]
430
- }), /*#__PURE__*/_jsx(StyledButton, {
431
- mode: "contained",
432
- onPress: () => {
433
- setHasGuideShown(true);
433
+ }), /*#__PURE__*/_jsx(View, {
434
+ style: {
435
+ paddingBottom: insets.bottom
434
436
  },
435
- children: t('general.letsGo')
437
+ children: /*#__PURE__*/_jsx(StyledButton, {
438
+ mode: "contained",
439
+ onPress: () => {
440
+ setHasGuideShown(true);
441
+ },
442
+ children: t('general.letsGo')
443
+ })
436
444
  })]
437
445
  }) : /*#__PURE__*/_jsxs(_Fragment, {
438
446
  children: [/*#__PURE__*/_jsx(FaceCamera, {
@@ -442,11 +450,11 @@ const LivenessDetectionScreen = () => {
442
450
  }), /*#__PURE__*/_jsx(NativeCircularProgress, {
443
451
  style: styles.circularProgress,
444
452
  size: PREVIEW_SIZE,
445
- width: 10,
446
- backgroundWidth: 5,
453
+ width: 8,
454
+ backgroundWidth: 8,
447
455
  fill: state.progressFill,
448
456
  tintColor: appContext.branding.primaryColor,
449
- backgroundColor: "white"
457
+ backgroundColor: "rgba(255, 255, 255, 0.3)"
450
458
  }), state.brightnessLow && /*#__PURE__*/_jsx(LottieView, {
451
459
  source: require('../../Shared/Animations/light.json'),
452
460
  style: styles.animation,
@@ -461,9 +469,9 @@ const LivenessDetectionScreen = () => {
461
469
  height: "100%",
462
470
  fill: "white"
463
471
  }), /*#__PURE__*/_jsx(Circle, {
464
- cx: "50%",
465
- cy: "50%",
466
- r: "25%",
472
+ cx: windowWidth / 2,
473
+ cy: windowHeight / 2,
474
+ r: PREVIEW_SIZE / 2,
467
475
  fill: "black"
468
476
  })]
469
477
  }), /*#__PURE__*/_jsx(SvgRect, {
@@ -473,7 +481,9 @@ const LivenessDetectionScreen = () => {
473
481
  mask: "url(#hole-mask)"
474
482
  })]
475
483
  }), /*#__PURE__*/_jsx(View, {
476
- style: styles.instructionsContainerTop,
484
+ style: [styles.instructionsContainerTop, {
485
+ paddingTop: insets.top
486
+ }],
477
487
  children: /*#__PURE__*/_jsx(Text, {
478
488
  style: styles.instructions,
479
489
  children: (() => {
@@ -491,14 +501,18 @@ const LivenessDetectionScreen = () => {
491
501
  })()
492
502
  })
493
503
  }), /*#__PURE__*/_jsx(View, {
494
- style: styles.instructionsContainerBottom,
504
+ style: [styles.instructionsContainerBottom, {
505
+ paddingBottom: insets.bottom
506
+ }],
495
507
  children: /*#__PURE__*/_jsx(Text, {
496
508
  style: styles.action,
497
509
  children: state.faceDetected && !state.faceTooBig && !state.multipleFacesDetected && !state.brightnessLow && instructions[state.currentInstruction]?.instruction
498
510
  })
499
511
  })]
500
512
  }), /*#__PURE__*/_jsx(View, {
501
- style: styles.footer,
513
+ style: [styles.footer, {
514
+ bottom: insets.bottom
515
+ }],
502
516
  children: /*#__PURE__*/_jsx(NavigationManager, {
503
517
  ref: navigationManagerRef
504
518
  })
@@ -0,0 +1,285 @@
1
+ "use strict";
2
+
3
+ import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';
4
+ import { SafeAreaView, Text, StyleSheet, Alert, View, Image, Dimensions, KeyboardAvoidingView, Platform, ScrollView } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
6
+ import { useRoute } from '@react-navigation/native';
7
+ import AppContext from "../../Shared/Contexts/AppContext.js";
8
+ import httpClient, { BadRequestError, NotFoundError, TooManyRequestsError } from "../../Shared/Libs/http-client.js";
9
+ import { useTranslation } from 'react-i18next';
10
+ import LanguageSelector from "../../Shared/Components/LanguageSelector.js";
11
+ import NavigationManager from "../../Shared/Components/NavigationManager.js";
12
+ import StyledButton from "../../Shared/Components/StyledButton.js";
13
+ import StyledTextInput from "../../Shared/Components/StyledTextInput.js";
14
+ import { useTheme } from "../../Shared/Contexts/ThemeContext.js";
15
+ import LottieView from 'lottie-react-native';
16
+ import { getSimulatedDemoData } from "../../Shared/Libs/demo.utils.js";
17
+ import { trackError, useScreenTracking } from "../../Shared/Libs/analytics.utils.js";
18
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
19
+ const OTPVerificationScreen = () => {
20
+ const route = useRoute();
21
+ const sessionId = route.params?.sessionId;
22
+ const [code, setCode] = useState('');
23
+ const [isSendAgainEnabled, setIsSendAgainEnabled] = useState(false);
24
+ const [isCodeGettingVerified, setIsCodeGettingVerified] = useState(false);
25
+ const appContext = useContext(AppContext);
26
+ const {
27
+ t
28
+ } = useTranslation();
29
+ const navigationManagerRef = useRef(null);
30
+ const initialized = useRef(false);
31
+ const insets = useSafeAreaInsets();
32
+ const theme = useTheme();
33
+ const primaryColor = theme.colors.primary;
34
+ useScreenTracking('otp_verification');
35
+
36
+ // Guard: If sessionId is not provided, show error
37
+ if (!sessionId) {
38
+ return /*#__PURE__*/_jsx(SafeAreaView, {
39
+ style: [styles.container, {
40
+ backgroundColor: theme.colors.background
41
+ }],
42
+ children: /*#__PURE__*/_jsx(Text, {
43
+ style: [styles.errorText, {
44
+ color: theme.colors.error
45
+ }],
46
+ children: t('general.sessionNotFound')
47
+ })
48
+ });
49
+ }
50
+ const apiUrl = `${appContext.baseUrl}/api/app/mobile`;
51
+ const sendVerificationCode = useCallback(async sessionId => {
52
+ try {
53
+ await httpClient.post(`${apiUrl}/verification-sessions/${sessionId}`, {}, appContext.isDemoSession ? getSimulatedDemoData('SEND_VERIFICATION_CODE') : undefined);
54
+ return true;
55
+ } catch (error) {
56
+ if (error instanceof NotFoundError) {
57
+ Alert.alert(t('general.error'), t('verificationSessionCheckScreen.noVerificationSessionFound'));
58
+ } else if (error instanceof TooManyRequestsError) {
59
+ return true;
60
+ } else {
61
+ trackError('VERIFICATION_CODE_SEND_FAILED', 'Failed to send verification code', 'otp_verification', 'high', {
62
+ recoverable: true,
63
+ userAction: 'send_verification_code'
64
+ });
65
+ Alert.alert(t('general.error'), t('verificationSessionCheckScreen.cannotSendVerificationCode'));
66
+ }
67
+ }
68
+ return false;
69
+ }, [apiUrl, appContext.isDemoSession, t]);
70
+ const getVerifiedSession = useCallback(async (sessionId, verificationCode) => {
71
+ try {
72
+ const response = await httpClient.post(`${apiUrl}/verification-sessions/${sessionId}`, {
73
+ code: verificationCode
74
+ }, appContext.isDemoSession ? getSimulatedDemoData('GET_VERIFIED_SESSION', {
75
+ code: verificationCode
76
+ }) : undefined);
77
+ return response;
78
+ } catch (error) {
79
+ if (error instanceof NotFoundError) {
80
+ Alert.alert(t('general.error'), t('verificationSessionCheckScreen.noVerificationSessionFound'));
81
+ } else if (error instanceof BadRequestError) {
82
+ // Wrong OTP code - expected user behavior, not actionable
83
+ } else {
84
+ trackError('VERIFIED_SESSION_CHECK_ERROR', error instanceof Error ? error.message : 'Unknown error', 'otp_verification', 'high', {
85
+ recoverable: false,
86
+ userAction: 'check_verified_session'
87
+ });
88
+ }
89
+ }
90
+ }, [apiUrl, appContext.isDemoSession, t]);
91
+ const sendOTPCode = useCallback(async sessionId => {
92
+ if (!sessionId) {
93
+ return false;
94
+ }
95
+ const isSent = await sendVerificationCode(sessionId);
96
+ if (!isSent) {
97
+ return false;
98
+ }
99
+ setIsSendAgainEnabled(false);
100
+ setTimeout(() => {
101
+ setIsSendAgainEnabled(true);
102
+ }, 3 * 60 * 1000);
103
+ return true;
104
+ }, [sendVerificationCode]);
105
+ useEffect(() => {
106
+ if (!initialized.current && sessionId) {
107
+ initialized.current = true;
108
+ sendOTPCode(sessionId);
109
+ }
110
+ }, [sessionId, sendOTPCode]);
111
+ return /*#__PURE__*/_jsx(SafeAreaView, {
112
+ style: styles.safeAreaContainer,
113
+ children: /*#__PURE__*/_jsx(KeyboardAvoidingView, {
114
+ style: styles.keyboardAvoidingView,
115
+ behavior: Platform.OS === 'ios' ? 'padding' : 'height',
116
+ keyboardVerticalOffset: Platform.OS === 'ios' ? 0 : 20,
117
+ children: /*#__PURE__*/_jsxs(ScrollView, {
118
+ style: styles.container,
119
+ contentContainerStyle: styles.scrollContainer,
120
+ keyboardShouldPersistTaps: "handled",
121
+ showsVerticalScrollIndicator: false,
122
+ children: [/*#__PURE__*/_jsxs(View, {
123
+ style: styles.mainContent,
124
+ children: [/*#__PURE__*/_jsx(LanguageSelector, {}), /*#__PURE__*/_jsx(View, {
125
+ style: styles.mainHeader,
126
+ children: appContext.branding.logoUrl ? /*#__PURE__*/_jsx(Image, {
127
+ source: {
128
+ uri: appContext.branding.logoUrl
129
+ },
130
+ style: styles.mainLogo
131
+ }) : /*#__PURE__*/_jsx(Image, {
132
+ source: require('../../Shared/Assets/trustchex-logo-black.png'),
133
+ style: styles.trustchexLogo
134
+ })
135
+ }), isCodeGettingVerified ? /*#__PURE__*/_jsx(LottieView, {
136
+ source: require('../../Shared/Animations/loading.json'),
137
+ style: styles.loadingAnimation,
138
+ autoPlay: true,
139
+ loop: true,
140
+ resizeMode: "cover"
141
+ }) : /*#__PURE__*/_jsxs(_Fragment, {
142
+ children: [/*#__PURE__*/_jsx(Text, {
143
+ style: styles.mainText,
144
+ children: t('verificationSessionCheckScreen.codeText')
145
+ }), /*#__PURE__*/_jsx(StyledTextInput, {
146
+ autoFocus: true,
147
+ placeholder: "",
148
+ borderColor: primaryColor,
149
+ focusedBorderColor: primaryColor,
150
+ inputStyle: styles.otpCodeTextInput,
151
+ keyboardType: "number-pad",
152
+ textContentType: "oneTimeCode",
153
+ autoComplete: "sms-otp",
154
+ maxLength: 6,
155
+ onChangeText: text => {
156
+ const numericText = text.replace(/[^0-9]/g, '');
157
+ if (numericText.length <= 6) {
158
+ setCode(numericText);
159
+ if (numericText.length === 6) {
160
+ (async () => {
161
+ setIsCodeGettingVerified(true);
162
+ const verifiedSession = await getVerifiedSession(sessionId, numericText);
163
+ if (verifiedSession?.identificationId) {
164
+ appContext.identificationInfo.identificationId = verifiedSession?.identificationId;
165
+ // Update workflow steps from verified session if available
166
+ if (verifiedSession.workflowSteps) {
167
+ appContext.workflowSteps = verifiedSession.workflowSteps;
168
+ }
169
+ setCode('');
170
+ navigationManagerRef.current?.navigateToNextStep();
171
+ } else {
172
+ appContext.onError?.('Invalid OTP code');
173
+ Alert.alert(t('general.error'), t('verificationSessionCheckScreen.codeError'));
174
+ setCode('');
175
+ setIsCodeGettingVerified(false);
176
+ }
177
+ })();
178
+ }
179
+ }
180
+ },
181
+ value: code
182
+ }), /*#__PURE__*/_jsx(StyledButton, {
183
+ mode: "contained",
184
+ disabled: !isSendAgainEnabled,
185
+ onPress: () => {
186
+ sendOTPCode(sessionId);
187
+ },
188
+ children: t('verificationSessionCheckScreen.sendCodeAgain')
189
+ })]
190
+ })]
191
+ }), appContext.branding.logoUrl && /*#__PURE__*/_jsxs(View, {
192
+ style: [styles.footer, {
193
+ bottom: insets.bottom + 15
194
+ }],
195
+ children: [/*#__PURE__*/_jsx(Text, {
196
+ style: styles.mainText,
197
+ children: "Powered By"
198
+ }), /*#__PURE__*/_jsx(Image, {
199
+ source: require('../../Shared/Assets/trustchex-logo-black.png'),
200
+ style: styles.poweredByLogo
201
+ })]
202
+ }), /*#__PURE__*/_jsx(NavigationManager, {
203
+ ref: navigationManagerRef
204
+ })]
205
+ })
206
+ })
207
+ });
208
+ };
209
+ const styles = StyleSheet.create({
210
+ safeAreaContainer: {
211
+ flex: 1,
212
+ backgroundColor: 'white'
213
+ },
214
+ keyboardAvoidingView: {
215
+ flex: 1
216
+ },
217
+ container: {
218
+ flex: 1
219
+ },
220
+ scrollContainer: {
221
+ flexGrow: 1,
222
+ padding: 20,
223
+ alignItems: 'center',
224
+ justifyContent: 'center'
225
+ },
226
+ loadingAnimation: {
227
+ width: '100%',
228
+ height: '100%',
229
+ maxHeight: 200
230
+ },
231
+ mainHeader: {
232
+ display: 'flex',
233
+ flexDirection: 'column',
234
+ alignItems: 'center',
235
+ justifyContent: 'center'
236
+ },
237
+ mainContent: {
238
+ flex: 1,
239
+ display: 'flex',
240
+ gap: 20,
241
+ justifyContent: 'center',
242
+ width: '100%',
243
+ maxWidth: 400
244
+ },
245
+ mainText: {
246
+ color: 'black',
247
+ textAlign: 'center'
248
+ },
249
+ mainLogo: {
250
+ width: Dimensions.get('window').width - 40,
251
+ height: Dimensions.get('window').width / 4,
252
+ maxWidth: 300,
253
+ maxHeight: 300,
254
+ resizeMode: 'contain'
255
+ },
256
+ trustchexLogo: {
257
+ width: 300,
258
+ height: 75,
259
+ resizeMode: 'contain'
260
+ },
261
+ poweredByLogo: {
262
+ width: 120,
263
+ height: 30,
264
+ resizeMode: 'contain'
265
+ },
266
+ footer: {
267
+ display: 'flex',
268
+ flexDirection: 'column',
269
+ justifyContent: 'center',
270
+ alignItems: 'center',
271
+ position: 'absolute'
272
+ },
273
+ otpCodeTextInput: {
274
+ fontWeight: '800',
275
+ fontSize: 28,
276
+ textAlign: 'center',
277
+ letterSpacing: 8
278
+ },
279
+ errorText: {
280
+ fontSize: 16,
281
+ textAlign: 'center',
282
+ padding: 20
283
+ }
284
+ });
285
+ export default OTPVerificationScreen;
@@ -2,6 +2,7 @@
2
2
 
3
3
  import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
4
4
  import { Alert, Image, Platform, SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
6
  import AppContext from "../../Shared/Contexts/AppContext.js";
6
7
  import httpClient from "../../Shared/Libs/http-client.js";
7
8
  import RNFS from 'react-native-fs';
@@ -30,9 +31,37 @@ const ResultScreen = () => {
30
31
  const {
31
32
  t
32
33
  } = useTranslation();
34
+ const insets = useSafeAreaInsets();
33
35
 
34
36
  // Track screen view and exit
35
37
  useScreenTracking('result_screen');
38
+ const formatDate = useCallback(dateStr => {
39
+ if (!dateStr) return '';
40
+ try {
41
+ // Check if it's YYMMDD format (6 digits)
42
+ if (dateStr.length === 6 && /^\d{6}$/.test(dateStr)) {
43
+ const yy = dateStr.substring(0, 2);
44
+ const mm = dateStr.substring(2, 4);
45
+ const dd = dateStr.substring(4, 6);
46
+
47
+ // Assume 19xx if YY >= 50, else 20xx
48
+ const year = parseInt(yy) >= 50 ? `19${yy}` : `20${yy}`;
49
+ return `${dd}/${mm}/${year}`;
50
+ }
51
+
52
+ // Otherwise try to parse as ISO 8601
53
+ const date = new Date(dateStr);
54
+ if (!isNaN(date.getTime())) {
55
+ const day = String(date.getDate()).padStart(2, '0');
56
+ const month = String(date.getMonth() + 1).padStart(2, '0');
57
+ const year = date.getFullYear();
58
+ return `${day}/${month}/${year}`;
59
+ }
60
+ return dateStr;
61
+ } catch {
62
+ return dateStr;
63
+ }
64
+ }, []);
36
65
  const apiUrl = useMemo(() => `${appContext.baseUrl}/api/app/mobile`, [appContext.baseUrl]);
37
66
  useEffect(() => {
38
67
  if (appContext.isDemoSession) {
@@ -414,7 +443,7 @@ const ResultScreen = () => {
414
443
  children: [t('eidScannerScreen.birthDate'), ":"]
415
444
  }), /*#__PURE__*/_jsx(Text, {
416
445
  style: styles.mrzInfoText,
417
- children: appContext.identificationInfo.scannedDocument.mrzFields?.birthDate
446
+ children: formatDate(appContext.identificationInfo.scannedDocument.mrzFields?.birthDate)
418
447
  })]
419
448
  }), /*#__PURE__*/_jsxs(View, {
420
449
  style: styles.mrzInfoItem,
@@ -432,18 +461,18 @@ const ResultScreen = () => {
432
461
  children: [t('eidScannerScreen.expirationDate'), ":"]
433
462
  }), /*#__PURE__*/_jsx(Text, {
434
463
  style: styles.mrzInfoText,
435
- children: appContext.identificationInfo.scannedDocument.mrzFields?.expirationDate
436
- })]
437
- }), /*#__PURE__*/_jsxs(View, {
438
- style: styles.mrzInfoItem,
439
- children: [/*#__PURE__*/_jsxs(Text, {
440
- style: styles.mrzInfoLabel,
441
- children: [t('eidScannerScreen.mrzText'), ":"]
442
- }), /*#__PURE__*/_jsx(Text, {
443
- style: styles.mrzInfoText,
444
- children: appContext.identificationInfo.scannedDocument.mrzText
464
+ children: formatDate(appContext.identificationInfo.scannedDocument.mrzFields?.expirationDate)
445
465
  })]
446
466
  })]
467
+ }), appContext.identificationInfo.scannedDocument.mrzText && /*#__PURE__*/_jsxs(View, {
468
+ style: styles.mrzTextContainer,
469
+ children: [/*#__PURE__*/_jsx(Text, {
470
+ style: styles.mrzTextLabel,
471
+ children: t('eidScannerScreen.mrzText')
472
+ }), /*#__PURE__*/_jsx(Text, {
473
+ style: styles.mrzTextValue,
474
+ children: appContext.identificationInfo.scannedDocument.mrzText
475
+ })]
447
476
  }), /*#__PURE__*/_jsx(Text, {
448
477
  style: styles.sectionText,
449
478
  children: t('resultScreen.demoImages')
@@ -547,13 +576,18 @@ const ResultScreen = () => {
547
576
  })]
548
577
  })]
549
578
  })
550
- }), /*#__PURE__*/_jsx(StyledButton, {
551
- mode: "contained",
552
- onPress: () => {
553
- appContext.onCompleted?.();
554
- navigationManagerRef.current?.reset();
579
+ }), /*#__PURE__*/_jsx(View, {
580
+ style: {
581
+ paddingBottom: insets.bottom
555
582
  },
556
- children: t('resultScreen.demoStartOver')
583
+ children: /*#__PURE__*/_jsx(StyledButton, {
584
+ mode: "contained",
585
+ onPress: () => {
586
+ appContext.onCompleted?.();
587
+ navigationManagerRef.current?.reset();
588
+ },
589
+ children: t('resultScreen.demoStartOver')
590
+ })
557
591
  })]
558
592
  }) : /*#__PURE__*/_jsxs(_Fragment, {
559
593
  children: [/*#__PURE__*/_jsx(LottieView, {
@@ -582,9 +616,11 @@ const styles = StyleSheet.create({
582
616
  container: {
583
617
  flex: 1,
584
618
  display: 'flex',
585
- padding: 20,
619
+ paddingVertical: 20,
620
+ paddingHorizontal: 10,
586
621
  justifyContent: 'center',
587
- gap: 20
622
+ gap: 20,
623
+ backgroundColor: 'white'
588
624
  },
589
625
  sectionAnimation: {
590
626
  width: 192,
@@ -627,18 +663,20 @@ const styles = StyleSheet.create({
627
663
  image: {
628
664
  width: 100,
629
665
  height: 100,
630
- objectFit: 'contain'
666
+ objectFit: 'contain',
667
+ borderRadius: 8
631
668
  },
632
669
  imageItem: {
633
670
  display: 'flex',
634
671
  flexDirection: 'column',
635
- gap: 10,
672
+ gap: 8,
636
673
  alignItems: 'center'
637
674
  },
638
675
  imageContainer: {
639
676
  display: 'flex',
640
677
  flexDirection: 'row',
641
- gap: 10
678
+ gap: 10,
679
+ paddingVertical: 8
642
680
  },
643
681
  imageText: {
644
682
  fontSize: 10,
@@ -648,14 +686,19 @@ const styles = StyleSheet.create({
648
686
  },
649
687
  mrzInfo: {
650
688
  flexDirection: 'column',
651
- width: '100%'
689
+ width: '100%',
690
+ borderWidth: 1,
691
+ borderColor: '#D1D5DB',
692
+ borderRadius: 8,
693
+ overflow: 'hidden'
652
694
  },
653
695
  mrzInfoItem: {
654
696
  flexDirection: 'row',
655
697
  justifyContent: 'space-between',
656
- borderBottomColor: 'lightgray',
698
+ borderBottomColor: '#D1D5DB',
657
699
  borderBottomWidth: 1,
658
- paddingVertical: 5
700
+ paddingVertical: 8,
701
+ paddingHorizontal: 12
659
702
  },
660
703
  mrzInfoLabel: {
661
704
  color: 'black',
@@ -664,7 +707,28 @@ const styles = StyleSheet.create({
664
707
  },
665
708
  mrzInfoText: {
666
709
  color: 'black',
667
- fontSize: 10
710
+ fontSize: 10,
711
+ fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace'
712
+ },
713
+ mrzTextContainer: {
714
+ padding: 12,
715
+ backgroundColor: '#F3F4F6',
716
+ borderRadius: 8,
717
+ borderWidth: 1,
718
+ borderColor: '#D1D5DB'
719
+ },
720
+ mrzTextLabel: {
721
+ fontSize: 12,
722
+ fontWeight: '600',
723
+ color: '#6B7280',
724
+ marginBottom: 8
725
+ },
726
+ mrzTextValue: {
727
+ fontFamily: Platform.OS === 'ios' ? 'Courier' : 'monospace',
728
+ fontSize: 10,
729
+ letterSpacing: 1,
730
+ lineHeight: 14,
731
+ color: '#1F2937'
668
732
  },
669
733
  trustchexLogo: {
670
734
  width: 150,