@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.
- package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +8 -2
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -1
- package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -1
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +29 -15
- package/lib/module/Screens/Static/OTPVerificationScreen.js +285 -0
- package/lib/module/Screens/Static/ResultScreen.js +90 -26
- package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +48 -134
- package/lib/module/Shared/Components/DebugNavigationPanel.js +252 -0
- package/lib/module/Shared/Components/EIDScanner.js +142 -17
- package/lib/module/Shared/Components/FaceCamera.js +23 -11
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +295 -44
- package/lib/module/Shared/Components/NavigationManager.js +19 -3
- package/lib/module/Shared/Config/camera-enhancement.config.js +58 -0
- package/lib/module/Shared/Contexts/AppContext.js +1 -0
- package/lib/module/Shared/Libs/camera.utils.js +221 -1
- package/lib/module/Shared/Libs/frame-enhancement.utils.js +133 -0
- package/lib/module/Shared/Libs/mrz.utils.js +98 -1
- package/lib/module/Translation/Resources/en.js +30 -0
- package/lib/module/Translation/Resources/tr.js +30 -0
- package/lib/module/Trustchex.js +49 -39
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts +3 -0
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts +54 -0
- package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +2 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +65 -1
- package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts +25 -0
- package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +30 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +30 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +3 -3
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +6 -2
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +3 -1
- package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +3 -1
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +27 -17
- package/src/Screens/Static/OTPVerificationScreen.tsx +379 -0
- package/src/Screens/Static/ResultScreen.tsx +160 -101
- package/src/Screens/Static/VerificationSessionCheckScreen.tsx +51 -196
- package/src/Shared/Components/DebugNavigationPanel.tsx +262 -0
- package/src/Shared/Components/EIDScanner.tsx +144 -19
- package/src/Shared/Components/FaceCamera.tsx +38 -21
- package/src/Shared/Components/IdentityDocumentCamera.tsx +399 -101
- package/src/Shared/Components/NavigationManager.tsx +19 -3
- package/src/Shared/Config/camera-enhancement.config.ts +46 -0
- package/src/Shared/Contexts/AppContext.ts +3 -0
- package/src/Shared/Libs/camera.utils.ts +240 -1
- package/src/Shared/Libs/frame-enhancement.utils.ts +217 -0
- package/src/Shared/Libs/mrz.utils.ts +78 -1
- package/src/Translation/Resources/en.ts +30 -0
- package/src/Translation/Resources/tr.ts +30 -0
- package/src/Trustchex.tsx +58 -46
- 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
|
-
|
|
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(
|
|
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(
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
setHasGuideShown(true);
|
|
433
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
434
|
+
style: {
|
|
435
|
+
paddingBottom: insets.bottom
|
|
434
436
|
},
|
|
435
|
-
children:
|
|
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:
|
|
446
|
-
backgroundWidth:
|
|
453
|
+
width: 8,
|
|
454
|
+
backgroundWidth: 8,
|
|
447
455
|
fill: state.progressFill,
|
|
448
456
|
tintColor: appContext.branding.primaryColor,
|
|
449
|
-
backgroundColor: "
|
|
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:
|
|
465
|
-
cy:
|
|
466
|
-
r:
|
|
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(
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
appContext.onCompleted?.();
|
|
554
|
-
navigationManagerRef.current?.reset();
|
|
579
|
+
}), /*#__PURE__*/_jsx(View, {
|
|
580
|
+
style: {
|
|
581
|
+
paddingBottom: insets.bottom
|
|
555
582
|
},
|
|
556
|
-
children:
|
|
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
|
-
|
|
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:
|
|
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: '
|
|
698
|
+
borderBottomColor: '#D1D5DB',
|
|
657
699
|
borderBottomWidth: 1,
|
|
658
|
-
paddingVertical:
|
|
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,
|