@trustchex/react-native-sdk 1.381.0 → 1.464.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/android/src/main/java/com/trustchex/reactnativesdk/TrustchexSDKModule.kt +2 -8
- package/android/src/main/java/com/trustchex/reactnativesdk/camera/TrustchexCameraView.kt +60 -13
- package/android/src/main/java/com/trustchex/reactnativesdk/mlkit/MLKitModule.kt +1 -1
- package/ios/Camera/TrustchexCameraView.swift +10 -13
- package/ios/MLKit/MLKitModule.swift +1 -1
- package/lib/module/Screens/Debug/BarcodeTestScreen.js +308 -0
- package/lib/module/Screens/Debug/MRZTestScreen.js +105 -13
- package/lib/module/Screens/Debug/NFCScanTestScreen.js +635 -0
- package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +49 -32
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +22 -4
- package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -0
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +126 -27
- package/lib/module/Screens/Dynamic/VerbalConsentScreen.js +1079 -0
- package/lib/module/Screens/Dynamic/VideoCallScreen.js +678 -0
- package/lib/module/Screens/Static/OTPVerificationScreen.js +6 -0
- package/lib/module/Screens/Static/QrCodeScanningScreen.js +7 -1
- package/lib/module/Screens/Static/ResultScreen.js +154 -34
- package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +59 -51
- package/lib/module/Shared/Animations/recording.json +1 -0
- package/lib/module/Shared/Animations/video-call.json +1 -0
- package/lib/module/Shared/Components/DebugNavigationPanel.js +231 -67
- package/lib/module/Shared/Components/EIDScanner.js +213 -112
- package/lib/module/Shared/Components/IdentityDocumentCamera.flows.js +5 -3
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +77 -39
- package/lib/module/Shared/Components/IdentityDocumentCamera.utils.js +13 -4
- package/lib/module/Shared/Components/NavigationManager.js +39 -19
- package/lib/module/Shared/Contexts/AppContext.js +1 -0
- package/lib/module/Shared/EIDReader/aesSecureMessagingWrapper.js +51 -0
- package/lib/module/Shared/EIDReader/apduLevelPACECapable.js +3 -0
- package/lib/module/Shared/EIDReader/bacKey.js +16 -2
- package/lib/module/Shared/EIDReader/eidReader.js +354 -13
- package/lib/module/Shared/EIDReader/eidService.js +25 -1
- package/lib/module/Shared/EIDReader/nfcManagerCardService.js +4 -7
- package/lib/module/Shared/EIDReader/paceInfo.js +85 -0
- package/lib/module/Shared/EIDReader/paceKeySpec.js +51 -0
- package/lib/module/Shared/EIDReader/protocol/paceAPDUSender.js +100 -0
- package/lib/module/Shared/EIDReader/protocol/paceProtocol.js +655 -0
- package/lib/module/Shared/EIDReader/protocol/paceResult.js +37 -0
- package/lib/module/Shared/EIDReader/secureMessagingWrapper.js +27 -4
- package/lib/module/Shared/EIDReader/smartcards/commandAPDU.js +2 -1
- package/lib/module/Shared/EIDReader/tlv/tlv.helpers.js +1 -1
- package/lib/module/Shared/EIDReader/tlv/tlv.utils.js +6 -3
- package/lib/module/Shared/EIDReader/utils/aesCrypto.utils.js +189 -0
- package/lib/module/Shared/Libs/SignalingClient.js +128 -0
- package/lib/module/Shared/Libs/analytics.utils.js +8 -0
- package/lib/module/Shared/Libs/contains.js +1 -40
- package/lib/module/Shared/Libs/country-display.utils.js +34 -0
- package/lib/module/Shared/Libs/deeplink.utils.js +9 -1
- package/lib/module/Shared/Libs/demo.utils.js +8 -0
- package/lib/module/Shared/Libs/http-client.js +9 -0
- package/lib/module/Shared/Libs/mrz.utils.js +3 -2
- package/lib/module/Shared/Libs/promise.utils.js +16 -2
- package/lib/module/Shared/Libs/status-bar.utils.js +23 -0
- package/lib/module/Shared/Services/DataUploadService.js +294 -0
- package/lib/module/Shared/Services/VideoSessionService.js +156 -0
- package/lib/module/Shared/Services/WebRTCService.js +510 -0
- package/lib/module/Shared/Types/analytics.types.js +4 -0
- package/lib/module/Translation/Resources/en.js +61 -2
- package/lib/module/Translation/Resources/tr.js +61 -2
- package/lib/module/Trustchex.js +64 -20
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/BarcodeTestScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Debug/MRZTestScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Debug/NFCScanTestScreen.d.ts.map +1 -0
- 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/Dynamic/VerbalConsentScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Dynamic/VerbalConsentScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Dynamic/VideoCallScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/QrCodeScanningScreen.d.ts.map +1 -1
- 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.map +1 -1
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.flows.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts +5 -0
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +1 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts +18 -0
- package/lib/typescript/src/Shared/EIDReader/aesSecureMessagingWrapper.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts +23 -0
- package/lib/typescript/src/Shared/EIDReader/apduLevelPACECapable.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts +6 -0
- package/lib/typescript/src/Shared/EIDReader/bacKey.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidReader.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/eidService.d.ts +9 -0
- package/lib/typescript/src/Shared/EIDReader/eidService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/nfcManagerCardService.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts +50 -0
- package/lib/typescript/src/Shared/EIDReader/paceInfo.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts +30 -0
- package/lib/typescript/src/Shared/EIDReader/paceKeySpec.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts +17 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceAPDUSender.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts +105 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceProtocol.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts +24 -0
- package/lib/typescript/src/Shared/EIDReader/protocol/paceResult.d.ts.map +1 -0
- package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts +15 -0
- package/lib/typescript/src/Shared/EIDReader/secureMessagingWrapper.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/smartcards/commandAPDU.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/tlv/tlv.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts +39 -0
- package/lib/typescript/src/Shared/EIDReader/utils/aesCrypto.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts +24 -0
- package/lib/typescript/src/Shared/Libs/SignalingClient.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/analytics.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/contains.d.ts +0 -7
- package/lib/typescript/src/Shared/Libs/contains.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts +2 -0
- package/lib/typescript/src/Shared/Libs/country-display.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/deeplink.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts +1 -1
- package/lib/typescript/src/Shared/Libs/http-client.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/promise.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts +9 -0
- package/lib/typescript/src/Shared/Libs/status-bar.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts +25 -0
- package/lib/typescript/src/Shared/Services/DataUploadService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts +33 -0
- package/lib/typescript/src/Shared/Services/VideoSessionService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Services/WebRTCService.d.ts +58 -0
- package/lib/typescript/src/Shared/Services/WebRTCService.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts +4 -0
- package/lib/typescript/src/Shared/Types/analytics.types.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts +13 -1
- package/lib/typescript/src/Shared/Types/identificationInfo.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +60 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +60 -1
- 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 +35 -5
- package/src/Screens/Debug/BarcodeTestScreen.tsx +317 -0
- package/src/Screens/Debug/MRZTestScreen.tsx +107 -13
- package/src/Screens/Debug/NFCScanTestScreen.tsx +692 -0
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +58 -35
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +27 -4
- package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +6 -0
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +156 -27
- package/src/Screens/Dynamic/VerbalConsentScreen.tsx +1401 -0
- package/src/Screens/Dynamic/VideoCallScreen.tsx +766 -0
- package/src/Screens/Static/OTPVerificationScreen.tsx +6 -0
- package/src/Screens/Static/QrCodeScanningScreen.tsx +7 -1
- package/src/Screens/Static/ResultScreen.tsx +235 -48
- package/src/Screens/Static/VerificationSessionCheckScreen.tsx +67 -72
- package/src/Shared/Animations/recording.json +1 -0
- package/src/Shared/Animations/video-call.json +1 -0
- package/src/Shared/Components/DebugNavigationPanel.tsx +252 -51
- package/src/Shared/Components/EIDScanner.tsx +223 -116
- package/src/Shared/Components/IdentityDocumentCamera.flows.ts +7 -4
- package/src/Shared/Components/IdentityDocumentCamera.tsx +224 -188
- package/src/Shared/Components/IdentityDocumentCamera.utils.ts +13 -4
- package/src/Shared/Components/NavigationManager.tsx +41 -19
- package/src/Shared/Contexts/AppContext.ts +2 -0
- package/src/Shared/EIDReader/aesSecureMessagingWrapper.ts +69 -0
- package/src/Shared/EIDReader/apduLevelPACECapable.ts +34 -0
- package/src/Shared/EIDReader/bacKey.ts +24 -8
- package/src/Shared/EIDReader/eidReader.ts +398 -12
- package/src/Shared/EIDReader/eidService.ts +49 -1
- package/src/Shared/EIDReader/nfcManagerCardService.ts +4 -6
- package/src/Shared/EIDReader/paceInfo.ts +159 -0
- package/src/Shared/EIDReader/paceKeySpec.ts +56 -0
- package/src/Shared/EIDReader/protocol/paceAPDUSender.ts +163 -0
- package/src/Shared/EIDReader/protocol/paceProtocol.ts +946 -0
- package/src/Shared/EIDReader/protocol/paceResult.ts +62 -0
- package/src/Shared/EIDReader/secureMessagingWrapper.ts +28 -10
- package/src/Shared/EIDReader/smartcards/commandAPDU.ts +2 -1
- package/src/Shared/EIDReader/tlv/tlv.helpers.ts +1 -1
- package/src/Shared/EIDReader/tlv/tlv.utils.ts +8 -5
- package/src/Shared/EIDReader/utils/aesCrypto.utils.ts +217 -0
- package/src/Shared/Libs/SignalingClient.ts +189 -0
- package/src/Shared/Libs/analytics.utils.ts +8 -0
- package/src/Shared/Libs/contains.ts +0 -53
- package/src/Shared/Libs/country-display.utils.ts +55 -0
- package/src/Shared/Libs/crypto.utils.ts +2 -2
- package/src/Shared/Libs/deeplink.utils.ts +12 -1
- package/src/Shared/Libs/demo.utils.ts +10 -0
- package/src/Shared/Libs/http-client.ts +19 -1
- package/src/Shared/Libs/mrz.utils.ts +3 -2
- package/src/Shared/Libs/promise.utils.ts +16 -2
- package/src/Shared/Libs/status-bar.utils.ts +21 -0
- package/src/Shared/Services/DataUploadService.ts +395 -0
- package/src/Shared/Services/VideoSessionService.ts +190 -0
- package/src/Shared/Services/WebRTCService.ts +636 -0
- package/src/Shared/Types/analytics.types.ts +4 -0
- package/src/Shared/Types/identificationInfo.ts +16 -1
- package/src/Translation/Resources/en.ts +88 -3
- package/src/Translation/Resources/tr.ts +89 -3
- package/src/Trustchex.tsx +65 -19
- package/src/version.ts +1 -1
|
@@ -10,9 +10,12 @@ import { getI18n, useTranslation } from 'react-i18next';
|
|
|
10
10
|
import StyledButton from "../../Shared/Components/StyledButton.js";
|
|
11
11
|
import NativeDeviceInfo from "../../Shared/Libs/native-device-info.utils.js";
|
|
12
12
|
import { speakWithDebounce } from "../../Shared/Libs/tts.utils.js";
|
|
13
|
+
import { useStatusBarWhiteBackground } from "../../Shared/Libs/status-bar.utils.js";
|
|
13
14
|
import { trackFunnelStep, useScreenTracking, trackConsentGiven, trackVerificationStart, trackVerificationComplete, trackError } from "../../Shared/Libs/analytics.utils.js";
|
|
15
|
+
import { useKeepAwake } from "../../Shared/Libs/native-keep-awake.utils.js";
|
|
14
16
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
15
17
|
const ContractAcceptanceScreen = () => {
|
|
18
|
+
useKeepAwake();
|
|
16
19
|
const [isEnabled, setIsEnabled] = useState(false);
|
|
17
20
|
const [isReady, setIsReady] = useState(false);
|
|
18
21
|
const appContext = useContext(AppContext);
|
|
@@ -27,6 +30,9 @@ const ContractAcceptanceScreen = () => {
|
|
|
27
30
|
|
|
28
31
|
// Track screen view and exit
|
|
29
32
|
useScreenTracking('contract_acceptance');
|
|
33
|
+
|
|
34
|
+
// Configure status bar for white background
|
|
35
|
+
useStatusBarWhiteBackground();
|
|
30
36
|
useEffect(() => {
|
|
31
37
|
const contracts = appContext.currentWorkflowStep?.data?.contracts;
|
|
32
38
|
if (!contracts) {
|
|
@@ -65,38 +71,49 @@ const ContractAcceptanceScreen = () => {
|
|
|
65
71
|
const generateHumanReadableIdentifier = useCallback(async () => {
|
|
66
72
|
return await NativeDeviceInfo.generateHumanReadableIdentifier();
|
|
67
73
|
}, []);
|
|
74
|
+
|
|
75
|
+
// Early return if workflow step data is not available (after all hooks)
|
|
76
|
+
if (!appContext.currentWorkflowStep || !appContext.currentWorkflowStep.data) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
68
79
|
return /*#__PURE__*/_jsxs(SafeAreaView, {
|
|
69
80
|
style: styles.container,
|
|
70
|
-
children: [/*#__PURE__*/_jsx(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
81
|
+
children: [/*#__PURE__*/_jsx(View, {
|
|
82
|
+
style: [styles.webViewContainer, {
|
|
83
|
+
paddingHorizontal: Math.max(insets.left, insets.right),
|
|
84
|
+
paddingTop: insets.top
|
|
85
|
+
}],
|
|
86
|
+
children: /*#__PURE__*/_jsx(WebView, {
|
|
87
|
+
source: {
|
|
88
|
+
uri: contractUrl || ''
|
|
89
|
+
},
|
|
90
|
+
javaScriptEnabled: true,
|
|
91
|
+
domStorageEnabled: false,
|
|
92
|
+
scalesPageToFit: false,
|
|
93
|
+
scrollEnabled: true,
|
|
94
|
+
style: styles.webView,
|
|
95
|
+
onError: syntheticEvent => {
|
|
96
|
+
const {
|
|
97
|
+
nativeEvent
|
|
98
|
+
} = syntheticEvent;
|
|
99
|
+
trackError('CONTRACT_WEBVIEW_ERROR', nativeEvent.description || 'Failed to load contract', 'contract_acceptance', 'medium', {
|
|
100
|
+
recoverable: true,
|
|
101
|
+
userAction: 'load_contract'
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
onLoadEnd: event => {
|
|
105
|
+
if (event.nativeEvent.url === contractUrl) {
|
|
106
|
+
setIsReady(true);
|
|
91
107
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
108
|
+
// Track contract acceptance started
|
|
109
|
+
trackVerificationStart('CONTRACT_ACCEPTANCE');
|
|
110
|
+
if (appContext.currentWorkflowStep?.data?.voiceGuidanceActive) {
|
|
111
|
+
speakWithDebounce(t('termsOfUseAndDataPrivacyScreen.footerText'));
|
|
112
|
+
}
|
|
96
113
|
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
114
|
+
},
|
|
115
|
+
onScroll: hasReachedEnd
|
|
116
|
+
})
|
|
100
117
|
}), /*#__PURE__*/_jsxs(View, {
|
|
101
118
|
style: [styles.footer, {
|
|
102
119
|
paddingBottom: insets.bottom
|
|
@@ -146,17 +163,17 @@ const ContractAcceptanceScreen = () => {
|
|
|
146
163
|
});
|
|
147
164
|
};
|
|
148
165
|
const styles = StyleSheet.create({
|
|
149
|
-
|
|
166
|
+
container: {
|
|
150
167
|
flex: 1,
|
|
151
168
|
backgroundColor: 'white'
|
|
152
169
|
},
|
|
153
|
-
|
|
154
|
-
flex: 1
|
|
170
|
+
webViewContainer: {
|
|
171
|
+
flex: 1,
|
|
172
|
+
width: '100%'
|
|
155
173
|
},
|
|
156
174
|
webView: {
|
|
157
175
|
flex: 1,
|
|
158
176
|
width: '100%',
|
|
159
|
-
height: '100%',
|
|
160
177
|
resizeMode: 'contain'
|
|
161
178
|
},
|
|
162
179
|
footer: {
|
|
@@ -9,18 +9,33 @@ import AppContext from "../../Shared/Contexts/AppContext.js";
|
|
|
9
9
|
import IdentityDocumentCamera from "../../Shared/Components/IdentityDocumentCamera.js";
|
|
10
10
|
import { useTranslation } from 'react-i18next';
|
|
11
11
|
import { trackFunnelStep, useScreenTracking, trackVerificationStart, trackVerificationComplete } from "../../Shared/Libs/analytics.utils.js";
|
|
12
|
+
import { useKeepAwake } from "../../Shared/Libs/native-keep-awake.utils.js";
|
|
12
13
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
14
|
const IdentityDocumentEIDScanningScreen = () => {
|
|
15
|
+
useKeepAwake();
|
|
14
16
|
const appContext = useContext(AppContext);
|
|
15
17
|
const navigationManagerRef = React.useRef(null);
|
|
16
18
|
const insets = useSafeAreaInsets();
|
|
17
19
|
const [idFrontSideData, setIDFrontSideData] = useState(null);
|
|
18
20
|
const [idBackSideData, setIDBackSideData] = useState(null);
|
|
19
21
|
const [passportData, setPassportData] = useState();
|
|
20
|
-
const [documentNumber, setDocumentNumber] = useState()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const [documentNumber, setDocumentNumber] = useState(() => {
|
|
23
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
24
|
+
return mrzFields?.documentNumber ?? undefined;
|
|
25
|
+
});
|
|
26
|
+
const [dateOfBirth, setDateOfBirth] = useState(() => {
|
|
27
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
28
|
+
return mrzFields?.birthDate ?? undefined;
|
|
29
|
+
});
|
|
30
|
+
const [dateOfExpiry, setDateOfExpiry] = useState(() => {
|
|
31
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
32
|
+
return mrzFields?.expirationDate ?? undefined;
|
|
33
|
+
});
|
|
34
|
+
const [documentType, setDocumentType] = useState(() => {
|
|
35
|
+
const dc = appContext.identificationInfo.scannedDocument?.mrzFields?.documentCode;
|
|
36
|
+
if (!dc) return undefined;
|
|
37
|
+
return dc === 'I' ? 'ID' : dc === 'P' ? 'PASSPORT' : 'UNKNOWN';
|
|
38
|
+
});
|
|
24
39
|
const [isNFCSupported, setIsNFCSupported] = useState(false);
|
|
25
40
|
const {
|
|
26
41
|
t
|
|
@@ -55,6 +70,9 @@ const IdentityDocumentEIDScanningScreen = () => {
|
|
|
55
70
|
setAllowedDocumentTypes(appContext.currentWorkflowStep?.data?.allowedDocumentTypes ?? null);
|
|
56
71
|
setAllowedCountries(appContext.currentWorkflowStep?.data?.allowedCountries ?? null);
|
|
57
72
|
}, [appContext.currentWorkflowStep]);
|
|
73
|
+
if (!appContext.currentWorkflowStep) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
58
76
|
return /*#__PURE__*/_jsxs(SafeAreaView, {
|
|
59
77
|
style: styles.safeAreaContainer,
|
|
60
78
|
children: [documentNumber && dateOfBirth && dateOfExpiry ? /*#__PURE__*/_jsx(View, {
|
|
@@ -8,8 +8,10 @@ import AppContext from "../../Shared/Contexts/AppContext.js";
|
|
|
8
8
|
import NavigationManager from "../../Shared/Components/NavigationManager.js";
|
|
9
9
|
import { useTranslation } from 'react-i18next';
|
|
10
10
|
import { trackFunnelStep, useScreenTracking, trackVerificationStart, trackVerificationComplete } from "../../Shared/Libs/analytics.utils.js";
|
|
11
|
+
import { useKeepAwake } from "../../Shared/Libs/native-keep-awake.utils.js";
|
|
11
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
12
13
|
const IdentityDocumentScanningScreen = () => {
|
|
14
|
+
useKeepAwake();
|
|
13
15
|
const [idFrontSideData, setIDFrontSideData] = useState(null);
|
|
14
16
|
const [idBackSideData, setIDBackSideData] = useState(null);
|
|
15
17
|
const [passportData, setPassportData] = useState();
|
|
@@ -83,6 +85,9 @@ const IdentityDocumentScanningScreen = () => {
|
|
|
83
85
|
setAllowedDocumentTypes(appContext.currentWorkflowStep?.data?.allowedDocumentTypes ?? null);
|
|
84
86
|
setAllowedCountries(appContext.currentWorkflowStep?.data?.allowedCountries ?? null);
|
|
85
87
|
}, [appContext.currentWorkflowStep]);
|
|
88
|
+
if (!appContext.currentWorkflowStep) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
86
91
|
return /*#__PURE__*/_jsxs(SafeAreaView, {
|
|
87
92
|
style: styles.container,
|
|
88
93
|
children: [/*#__PURE__*/_jsx(IdentityDocumentCamera, {
|
|
@@ -3,24 +3,25 @@
|
|
|
3
3
|
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, useRef } from 'react';
|
|
6
|
-
import { StyleSheet, Text, View, Dimensions, Vibration } from 'react-native';
|
|
6
|
+
import { Animated, Easing, StyleSheet, Text, View, Dimensions, Platform, Vibration, StatusBar } from 'react-native';
|
|
7
7
|
import { useSafeAreaInsets, SafeAreaView } from 'react-native-safe-area-context';
|
|
8
8
|
import NativeCircularProgress from "../../Shared/Components/NativeCircularProgress.js";
|
|
9
9
|
import FaceCamera from "../../Shared/Components/FaceCamera.js";
|
|
10
10
|
import NavigationManager from "../../Shared/Components/NavigationManager.js";
|
|
11
11
|
import AppContext from "../../Shared/Contexts/AppContext.js";
|
|
12
|
-
import { contains } from "../../Shared/Libs/contains.js";
|
|
13
12
|
import { useTranslation } from 'react-i18next';
|
|
14
13
|
import StyledButton from "../../Shared/Components/StyledButton.js";
|
|
15
14
|
import LottieView from 'lottie-react-native';
|
|
16
15
|
import { speak, resetLastMessage } from "../../Shared/Libs/tts.utils.js";
|
|
16
|
+
import { useStatusBarWhiteBackground } from "../../Shared/Libs/status-bar.utils.js";
|
|
17
17
|
import { trackFunnelStep, useScreenTracking, trackVerificationStart, trackVerificationComplete } from "../../Shared/Libs/analytics.utils.js";
|
|
18
|
+
import { useKeepAwake } from "../../Shared/Libs/native-keep-awake.utils.js";
|
|
18
19
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
19
20
|
const {
|
|
20
21
|
width: windowWidth,
|
|
21
22
|
height: windowHeight
|
|
22
23
|
} = Dimensions.get('window');
|
|
23
|
-
const PREVIEW_SIZE = windowWidth * 0.
|
|
24
|
+
const PREVIEW_SIZE = windowWidth * 0.95;
|
|
24
25
|
const PREVIEW_RECT = {
|
|
25
26
|
minX: (windowWidth - PREVIEW_SIZE) / 2,
|
|
26
27
|
minY: (windowHeight - PREVIEW_SIZE) / 2,
|
|
@@ -31,6 +32,7 @@ const PREVIEW_EDGE_OFFSET = 10;
|
|
|
31
32
|
const TURN_ANGLE_LIMIT = 15;
|
|
32
33
|
const LOOK_STRAIGHT_ANGLE_LIMIT = 10;
|
|
33
34
|
const LivenessDetectionScreen = () => {
|
|
35
|
+
useKeepAwake();
|
|
34
36
|
const [camera, setCamera] = useState(null);
|
|
35
37
|
const navigation = useNavigation();
|
|
36
38
|
const appContext = useContext(AppContext);
|
|
@@ -41,9 +43,32 @@ const LivenessDetectionScreen = () => {
|
|
|
41
43
|
const [isRecording, setIsRecording] = useState(false);
|
|
42
44
|
const insets = useSafeAreaInsets();
|
|
43
45
|
const referenceFaceTrackingId = useRef(null);
|
|
46
|
+
const recordingDotOpacity = useRef(new Animated.Value(1)).current;
|
|
44
47
|
|
|
45
48
|
// Track screen view and exit
|
|
46
49
|
useScreenTracking('liveness_detection');
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (isRecording) {
|
|
52
|
+
const pulse = Animated.loop(Animated.sequence([Animated.timing(recordingDotOpacity, {
|
|
53
|
+
toValue: 0.2,
|
|
54
|
+
duration: 600,
|
|
55
|
+
easing: Easing.inOut(Easing.ease),
|
|
56
|
+
useNativeDriver: true
|
|
57
|
+
}), Animated.timing(recordingDotOpacity, {
|
|
58
|
+
toValue: 1,
|
|
59
|
+
duration: 600,
|
|
60
|
+
easing: Easing.inOut(Easing.ease),
|
|
61
|
+
useNativeDriver: true
|
|
62
|
+
})]));
|
|
63
|
+
pulse.start();
|
|
64
|
+
return () => pulse.stop();
|
|
65
|
+
} else {
|
|
66
|
+
recordingDotOpacity.setValue(1);
|
|
67
|
+
}
|
|
68
|
+
}, [isRecording, recordingDotOpacity]);
|
|
69
|
+
|
|
70
|
+
// Configure status bar for white background
|
|
71
|
+
useStatusBarWhiteBackground();
|
|
47
72
|
const [initialState, setInitialState] = useState({
|
|
48
73
|
brightnessLow: false,
|
|
49
74
|
faceDetected: false,
|
|
@@ -95,7 +120,23 @@ const LivenessDetectionScreen = () => {
|
|
|
95
120
|
const [instructionList, setInstructionList] = useState([]);
|
|
96
121
|
const [hasGuideShown, setHasGuideShown] = useState(false);
|
|
97
122
|
const stoppingRecordingRef = useRef(false); // Track if we're already stopping to prevent multiple calls
|
|
98
|
-
|
|
123
|
+
|
|
124
|
+
// Configure status bar for white background
|
|
125
|
+
useStatusBarWhiteBackground();
|
|
126
|
+
|
|
127
|
+
// Update status bar when guide visibility changes
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
if (hasGuideShown) {
|
|
130
|
+
// Camera view - use light icons
|
|
131
|
+
StatusBar.setBarStyle('light-content', true);
|
|
132
|
+
} else {
|
|
133
|
+
// Guide screen with white background - use dark icons
|
|
134
|
+
StatusBar.setBarStyle('dark-content', true);
|
|
135
|
+
if (Platform.OS === 'android') {
|
|
136
|
+
StatusBar.setBackgroundColor('#ffffff', true);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}, [hasGuideShown]);
|
|
99
140
|
useEffect(() => {
|
|
100
141
|
const il = Object.keys(instructions).filter(instruction => !['START', 'FINISH'].includes(instruction) && (
|
|
101
142
|
// Look straight and blink is always included
|
|
@@ -115,7 +156,6 @@ const LivenessDetectionScreen = () => {
|
|
|
115
156
|
processComplete: false,
|
|
116
157
|
videoPath: ''
|
|
117
158
|
});
|
|
118
|
-
lastVoiceGuidanceMessage.current = '';
|
|
119
159
|
resetLastMessage();
|
|
120
160
|
}, [instructions, appContext.currentWorkflowStep?.data?.allowedLivenessInstructionTypes]);
|
|
121
161
|
const isCommandInProgress = useRef(false);
|
|
@@ -176,6 +216,13 @@ const LivenessDetectionScreen = () => {
|
|
|
176
216
|
const rightOpen = face.rightEyeOpenProbability ?? 0;
|
|
177
217
|
return leftOpen >= 0.8 && rightOpen >= 0.8;
|
|
178
218
|
};
|
|
219
|
+
const areEyesVisible = face => {
|
|
220
|
+
// Relaxed check: eyes just need to be detected (not fully closed).
|
|
221
|
+
// Smiling naturally causes squinting, so a lower threshold is used.
|
|
222
|
+
const leftOpen = face.leftEyeOpenProbability ?? 0;
|
|
223
|
+
const rightOpen = face.rightEyeOpenProbability ?? 0;
|
|
224
|
+
return leftOpen >= 0.3 && rightOpen >= 0.3;
|
|
225
|
+
};
|
|
179
226
|
const instructionReducer = (state, action) => {
|
|
180
227
|
switch (action.type) {
|
|
181
228
|
case 'RESET':
|
|
@@ -251,7 +298,6 @@ const LivenessDetectionScreen = () => {
|
|
|
251
298
|
const nextInstruction = state.instructionList[nextInstructionIndex];
|
|
252
299
|
|
|
253
300
|
// Reset TTS state when moving to any new instruction to ensure it speaks
|
|
254
|
-
lastVoiceGuidanceMessage.current = '';
|
|
255
301
|
resetLastMessage();
|
|
256
302
|
|
|
257
303
|
// Calculate progress based on actual action steps (excluding START and FINISH)
|
|
@@ -295,8 +341,7 @@ const LivenessDetectionScreen = () => {
|
|
|
295
341
|
// Let the instruction advance first, then speak the next instruction
|
|
296
342
|
|
|
297
343
|
// Only speak if message changed and is not empty
|
|
298
|
-
if (text
|
|
299
|
-
lastVoiceGuidanceMessage.current = text;
|
|
344
|
+
if (text) {
|
|
300
345
|
// Bypass interval for liveness instructions to ensure all instructions are spoken
|
|
301
346
|
speak(text, true);
|
|
302
347
|
}
|
|
@@ -369,25 +414,33 @@ const LivenessDetectionScreen = () => {
|
|
|
369
414
|
});
|
|
370
415
|
}
|
|
371
416
|
|
|
372
|
-
//
|
|
373
|
-
//
|
|
374
|
-
|
|
417
|
+
// Map the on-screen circle into frame coordinates.
|
|
418
|
+
// Camera uses FILL_CENTER / resizeAspectFill (cover): uniform scale filling
|
|
419
|
+
// the view, cropping overflow symmetrically.
|
|
420
|
+
const coverScale = Math.max(windowWidth / frameWidth, windowHeight / frameHeight);
|
|
421
|
+
const offsetX = (frameWidth - windowWidth / coverScale) / 2;
|
|
422
|
+
const offsetY = (frameHeight - windowHeight / coverScale) / 2;
|
|
375
423
|
const previewRectInFrame = {
|
|
376
|
-
minX:
|
|
377
|
-
minY:
|
|
378
|
-
width:
|
|
379
|
-
height:
|
|
380
|
-
};
|
|
381
|
-
const faceRectSmaller = {
|
|
382
|
-
width: face.bounds.width - PREVIEW_EDGE_OFFSET,
|
|
383
|
-
height: face.bounds.height - PREVIEW_EDGE_OFFSET,
|
|
384
|
-
minY: face.bounds.y + PREVIEW_EDGE_OFFSET / 2,
|
|
385
|
-
minX: face.bounds.x + PREVIEW_EDGE_OFFSET / 2
|
|
424
|
+
minX: PREVIEW_RECT.minX / coverScale + offsetX,
|
|
425
|
+
minY: PREVIEW_RECT.minY / coverScale + offsetY,
|
|
426
|
+
width: PREVIEW_RECT.width / coverScale,
|
|
427
|
+
height: PREVIEW_RECT.height / coverScale
|
|
386
428
|
};
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
429
|
+
const previewSizeInFrame = PREVIEW_RECT.width / coverScale;
|
|
430
|
+
|
|
431
|
+
// Check containment using face center + yaw-stable core radius.
|
|
432
|
+
// When the head turns (yaw), the bounding box grows wider but height stays
|
|
433
|
+
// constant — so min(width, height)/2 gives a stable size that doesn't
|
|
434
|
+
// falsely trigger an outside-circle reset during TURN_HEAD_LEFT/RIGHT.
|
|
435
|
+
const faceCenterX = face.bounds.x + face.bounds.width / 2;
|
|
436
|
+
const faceCenterY = face.bounds.y + face.bounds.height / 2;
|
|
437
|
+
const faceCoreRadius = Math.max(0, Math.min(face.bounds.width, face.bounds.height) / 2 - PREVIEW_EDGE_OFFSET / 2);
|
|
438
|
+
const circleCX = previewRectInFrame.minX + previewRectInFrame.width / 2;
|
|
439
|
+
const circleCY = previewRectInFrame.minY + previewRectInFrame.height / 2;
|
|
440
|
+
const circleR = previewSizeInFrame / 2;
|
|
441
|
+
const faceDx = faceCenterX - circleCX;
|
|
442
|
+
const faceDy = faceCenterY - circleCY;
|
|
443
|
+
const previewContainsFace = faceDx * faceDx + faceDy * faceDy <= (circleR - faceCoreRadius) * (circleR - faceCoreRadius);
|
|
391
444
|
const multipleFacesDetected = faces.length > 1;
|
|
392
445
|
if (!isImageBright) {
|
|
393
446
|
// Brightness too low - reset progress if we've started the flow
|
|
@@ -547,8 +600,9 @@ const LivenessDetectionScreen = () => {
|
|
|
547
600
|
// Ensure user is looking at camera (face direction check)
|
|
548
601
|
const isFacingCamera = instructions.SMILE.minAngle < face.yawAngle && face.yawAngle < instructions.SMILE.maxAngle && instructions.SMILE.minAngle < face.pitchAngle && face.pitchAngle < instructions.SMILE.maxAngle;
|
|
549
602
|
|
|
550
|
-
// Check if smiling with sufficient probability AND looking at camera AND eyes
|
|
551
|
-
|
|
603
|
+
// Check if smiling with sufficient probability AND looking at camera AND eyes visible
|
|
604
|
+
// Use relaxed eye check — smiling naturally causes squinting
|
|
605
|
+
if (smilingProb >= instructions.SMILE.minProbability && isFacingCamera && areEyesVisible(face)) {
|
|
552
606
|
instructions.SMILE.photo = image;
|
|
553
607
|
dispatch({
|
|
554
608
|
type: 'NEXT_INSTRUCTION',
|
|
@@ -652,6 +706,9 @@ const LivenessDetectionScreen = () => {
|
|
|
652
706
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
653
707
|
}, []); // Empty array = only run on mount/unmount
|
|
654
708
|
|
|
709
|
+
if (!appContext.currentWorkflowStep || !appContext.currentWorkflowStep.data) {
|
|
710
|
+
return null;
|
|
711
|
+
}
|
|
655
712
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
656
713
|
children: [hasGuideShown ? /*#__PURE__*/_jsxs(_Fragment, {
|
|
657
714
|
children: [/*#__PURE__*/_jsx(FaceCamera, {
|
|
@@ -691,6 +748,21 @@ const LivenessDetectionScreen = () => {
|
|
|
691
748
|
fill: "white",
|
|
692
749
|
mask: "url(#hole-mask)"
|
|
693
750
|
})]
|
|
751
|
+
}), isRecording && /*#__PURE__*/_jsx(View, {
|
|
752
|
+
style: [styles.recordingIndicatorContainer, {
|
|
753
|
+
top: PREVIEW_RECT.minY - 40
|
|
754
|
+
}],
|
|
755
|
+
children: /*#__PURE__*/_jsxs(View, {
|
|
756
|
+
style: styles.recordingIndicator,
|
|
757
|
+
children: [/*#__PURE__*/_jsx(Animated.View, {
|
|
758
|
+
style: [styles.recordingDot, {
|
|
759
|
+
opacity: recordingDotOpacity
|
|
760
|
+
}]
|
|
761
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
762
|
+
style: styles.recordingText,
|
|
763
|
+
children: "REC"
|
|
764
|
+
})]
|
|
765
|
+
})
|
|
694
766
|
}), /*#__PURE__*/_jsx(View, {
|
|
695
767
|
style: [styles.instructionsContainerBottom, {
|
|
696
768
|
top: PREVIEW_RECT.minY + PREVIEW_SIZE + 20
|
|
@@ -816,6 +888,33 @@ const styles = StyleSheet.create({
|
|
|
816
888
|
width: '100%',
|
|
817
889
|
zIndex: 1
|
|
818
890
|
},
|
|
891
|
+
recordingIndicatorContainer: {
|
|
892
|
+
position: 'absolute',
|
|
893
|
+
width: '100%',
|
|
894
|
+
alignItems: 'center',
|
|
895
|
+
zIndex: 2
|
|
896
|
+
},
|
|
897
|
+
recordingIndicator: {
|
|
898
|
+
flexDirection: 'row',
|
|
899
|
+
alignItems: 'center',
|
|
900
|
+
gap: 8,
|
|
901
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
902
|
+
paddingVertical: 6,
|
|
903
|
+
paddingHorizontal: 14,
|
|
904
|
+
borderRadius: 20
|
|
905
|
+
},
|
|
906
|
+
recordingDot: {
|
|
907
|
+
width: 10,
|
|
908
|
+
height: 10,
|
|
909
|
+
borderRadius: 5,
|
|
910
|
+
backgroundColor: '#ef4444'
|
|
911
|
+
},
|
|
912
|
+
recordingText: {
|
|
913
|
+
color: '#ffffff',
|
|
914
|
+
fontSize: 14,
|
|
915
|
+
fontWeight: '700',
|
|
916
|
+
letterSpacing: 1
|
|
917
|
+
},
|
|
819
918
|
guide: {
|
|
820
919
|
flex: 1,
|
|
821
920
|
display: 'flex',
|