@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
|
@@ -16,6 +16,7 @@ import { getI18n, useTranslation } from 'react-i18next';
|
|
|
16
16
|
import StyledButton from '../../Shared/Components/StyledButton';
|
|
17
17
|
import NativeDeviceInfo from '../../Shared/Libs/native-device-info.utils';
|
|
18
18
|
import { speakWithDebounce } from '../../Shared/Libs/tts.utils';
|
|
19
|
+
import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
|
|
19
20
|
import {
|
|
20
21
|
trackFunnelStep,
|
|
21
22
|
useScreenTracking,
|
|
@@ -24,8 +25,10 @@ import {
|
|
|
24
25
|
trackVerificationComplete,
|
|
25
26
|
trackError,
|
|
26
27
|
} from '../../Shared/Libs/analytics.utils';
|
|
28
|
+
import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
|
|
27
29
|
|
|
28
30
|
const ContractAcceptanceScreen = () => {
|
|
31
|
+
useKeepAwake();
|
|
29
32
|
const [isEnabled, setIsEnabled] = useState(false);
|
|
30
33
|
const [isReady, setIsReady] = useState(false);
|
|
31
34
|
const appContext = useContext(AppContext);
|
|
@@ -39,6 +42,9 @@ const ContractAcceptanceScreen = () => {
|
|
|
39
42
|
// Track screen view and exit
|
|
40
43
|
useScreenTracking('contract_acceptance');
|
|
41
44
|
|
|
45
|
+
// Configure status bar for white background
|
|
46
|
+
useStatusBarWhiteBackground();
|
|
47
|
+
|
|
42
48
|
useEffect(() => {
|
|
43
49
|
const contracts = appContext.currentWorkflowStep?.data?.contracts;
|
|
44
50
|
if (!contracts) {
|
|
@@ -93,41 +99,58 @@ const ContractAcceptanceScreen = () => {
|
|
|
93
99
|
return await NativeDeviceInfo.generateHumanReadableIdentifier();
|
|
94
100
|
}, []);
|
|
95
101
|
|
|
102
|
+
// Early return if workflow step data is not available (after all hooks)
|
|
103
|
+
if (!appContext.currentWorkflowStep || !appContext.currentWorkflowStep.data) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
96
107
|
return (
|
|
97
108
|
<SafeAreaView style={styles.container}>
|
|
98
|
-
<
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
109
|
+
<View
|
|
110
|
+
style={[
|
|
111
|
+
styles.webViewContainer,
|
|
112
|
+
{
|
|
113
|
+
paddingHorizontal: Math.max(insets.left, insets.right),
|
|
114
|
+
paddingTop: insets.top,
|
|
115
|
+
},
|
|
116
|
+
]}
|
|
117
|
+
>
|
|
118
|
+
<WebView
|
|
119
|
+
source={{
|
|
120
|
+
uri: contractUrl || '',
|
|
121
|
+
}}
|
|
122
|
+
javaScriptEnabled={true}
|
|
123
|
+
domStorageEnabled={false}
|
|
124
|
+
scalesPageToFit={false}
|
|
125
|
+
scrollEnabled={true}
|
|
126
|
+
style={styles.webView}
|
|
127
|
+
onError={(syntheticEvent) => {
|
|
128
|
+
const { nativeEvent } = syntheticEvent;
|
|
129
|
+
trackError(
|
|
130
|
+
'CONTRACT_WEBVIEW_ERROR',
|
|
131
|
+
nativeEvent.description || 'Failed to load contract',
|
|
132
|
+
'contract_acceptance',
|
|
133
|
+
'medium',
|
|
134
|
+
{ recoverable: true, userAction: 'load_contract' }
|
|
135
|
+
);
|
|
136
|
+
}}
|
|
137
|
+
onLoadEnd={(event) => {
|
|
138
|
+
if (event.nativeEvent.url === contractUrl) {
|
|
139
|
+
setIsReady(true);
|
|
140
|
+
|
|
141
|
+
// Track contract acceptance started
|
|
142
|
+
trackVerificationStart('CONTRACT_ACCEPTANCE');
|
|
143
|
+
|
|
144
|
+
if (appContext.currentWorkflowStep?.data?.voiceGuidanceActive) {
|
|
145
|
+
speakWithDebounce(
|
|
146
|
+
t('termsOfUseAndDataPrivacyScreen.footerText')
|
|
147
|
+
);
|
|
148
|
+
}
|
|
126
149
|
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
150
|
+
}}
|
|
151
|
+
onScroll={hasReachedEnd}
|
|
152
|
+
/>
|
|
153
|
+
</View>
|
|
131
154
|
<View style={[styles.footer, { paddingBottom: insets.bottom }]}>
|
|
132
155
|
<Text style={styles.footerText}>
|
|
133
156
|
{t('termsOfUseAndDataPrivacyScreen.footerText')}
|
|
@@ -176,17 +199,17 @@ const ContractAcceptanceScreen = () => {
|
|
|
176
199
|
};
|
|
177
200
|
|
|
178
201
|
const styles = StyleSheet.create({
|
|
179
|
-
|
|
202
|
+
container: {
|
|
180
203
|
flex: 1,
|
|
181
204
|
backgroundColor: 'white',
|
|
182
205
|
},
|
|
183
|
-
|
|
206
|
+
webViewContainer: {
|
|
184
207
|
flex: 1,
|
|
208
|
+
width: '100%',
|
|
185
209
|
},
|
|
186
210
|
webView: {
|
|
187
211
|
flex: 1,
|
|
188
212
|
width: '100%',
|
|
189
|
-
height: '100%',
|
|
190
213
|
resizeMode: 'contain',
|
|
191
214
|
},
|
|
192
215
|
footer: {
|
|
@@ -17,8 +17,10 @@ import {
|
|
|
17
17
|
trackVerificationStart,
|
|
18
18
|
trackVerificationComplete,
|
|
19
19
|
} from '../../Shared/Libs/analytics.utils';
|
|
20
|
+
import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
|
|
20
21
|
|
|
21
22
|
const IdentityDocumentEIDScanningScreen = () => {
|
|
23
|
+
useKeepAwake();
|
|
22
24
|
const appContext = useContext(AppContext);
|
|
23
25
|
const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
|
|
24
26
|
const insets = useSafeAreaInsets();
|
|
@@ -28,10 +30,27 @@ const IdentityDocumentEIDScanningScreen = () => {
|
|
|
28
30
|
useState<DocumentScannedData | null>(null);
|
|
29
31
|
const [passportData, setPassportData] =
|
|
30
32
|
useState<DocumentScannedData | null>();
|
|
31
|
-
const [documentNumber, setDocumentNumber] = useState<string>(
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
const [documentNumber, setDocumentNumber] = useState<string | undefined>(
|
|
34
|
+
() => {
|
|
35
|
+
const mrzFields =
|
|
36
|
+
appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
37
|
+
return mrzFields?.documentNumber ?? undefined;
|
|
38
|
+
}
|
|
39
|
+
);
|
|
40
|
+
const [dateOfBirth, setDateOfBirth] = useState<string | undefined>(() => {
|
|
41
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
42
|
+
return mrzFields?.birthDate ?? undefined;
|
|
43
|
+
});
|
|
44
|
+
const [dateOfExpiry, setDateOfExpiry] = useState<string | undefined>(() => {
|
|
45
|
+
const mrzFields = appContext.identificationInfo.scannedDocument?.mrzFields;
|
|
46
|
+
return mrzFields?.expirationDate ?? undefined;
|
|
47
|
+
});
|
|
48
|
+
const [documentType, setDocumentType] = useState<string | undefined>(() => {
|
|
49
|
+
const dc =
|
|
50
|
+
appContext.identificationInfo.scannedDocument?.mrzFields?.documentCode;
|
|
51
|
+
if (!dc) return undefined;
|
|
52
|
+
return dc === 'I' ? 'ID' : dc === 'P' ? 'PASSPORT' : 'UNKNOWN';
|
|
53
|
+
});
|
|
35
54
|
const [isNFCSupported, setIsNFCSupported] = useState<boolean>(false);
|
|
36
55
|
const { t } = useTranslation();
|
|
37
56
|
const [allowedDocumentTypes, setAllowedDocumentTypes] = useState<
|
|
@@ -77,6 +96,10 @@ const IdentityDocumentEIDScanningScreen = () => {
|
|
|
77
96
|
);
|
|
78
97
|
}, [appContext.currentWorkflowStep]);
|
|
79
98
|
|
|
99
|
+
if (!appContext.currentWorkflowStep) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
80
103
|
return (
|
|
81
104
|
<SafeAreaView style={styles.safeAreaContainer}>
|
|
82
105
|
{documentNumber && dateOfBirth && dateOfExpiry ? (
|
|
@@ -17,8 +17,10 @@ import {
|
|
|
17
17
|
trackVerificationStart,
|
|
18
18
|
trackVerificationComplete,
|
|
19
19
|
} from '../../Shared/Libs/analytics.utils';
|
|
20
|
+
import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
|
|
20
21
|
|
|
21
22
|
const IdentityDocumentScanningScreen = () => {
|
|
23
|
+
useKeepAwake();
|
|
22
24
|
const [idFrontSideData, setIDFrontSideData] =
|
|
23
25
|
useState<DocumentScannedData | null>(null);
|
|
24
26
|
const [idBackSideData, setIDBackSideData] =
|
|
@@ -145,6 +147,10 @@ const IdentityDocumentScanningScreen = () => {
|
|
|
145
147
|
);
|
|
146
148
|
}, [appContext.currentWorkflowStep]);
|
|
147
149
|
|
|
150
|
+
if (!appContext.currentWorkflowStep) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
|
|
148
154
|
return (
|
|
149
155
|
<SafeAreaView style={styles.container}>
|
|
150
156
|
<IdentityDocumentCamera
|
|
@@ -8,7 +8,17 @@ import React, {
|
|
|
8
8
|
useCallback,
|
|
9
9
|
useRef,
|
|
10
10
|
} from 'react';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
Animated,
|
|
13
|
+
Easing,
|
|
14
|
+
StyleSheet,
|
|
15
|
+
Text,
|
|
16
|
+
View,
|
|
17
|
+
Dimensions,
|
|
18
|
+
Platform,
|
|
19
|
+
Vibration,
|
|
20
|
+
StatusBar,
|
|
21
|
+
} from 'react-native';
|
|
12
22
|
import {
|
|
13
23
|
useSafeAreaInsets,
|
|
14
24
|
SafeAreaView,
|
|
@@ -20,21 +30,23 @@ import NavigationManager, {
|
|
|
20
30
|
type NavigationManagerRef,
|
|
21
31
|
} from '../../Shared/Components/NavigationManager';
|
|
22
32
|
import AppContext from '../../Shared/Contexts/AppContext';
|
|
23
|
-
import {
|
|
33
|
+
import { type Rect } from '../../Shared/Libs/contains';
|
|
24
34
|
import { useTranslation } from 'react-i18next';
|
|
25
35
|
import StyledButton from '../../Shared/Components/StyledButton';
|
|
26
36
|
import LottieView from 'lottie-react-native';
|
|
27
37
|
import { speak, resetLastMessage } from '../../Shared/Libs/tts.utils';
|
|
38
|
+
import { useStatusBarWhiteBackground } from '../../Shared/Libs/status-bar.utils';
|
|
28
39
|
import {
|
|
29
40
|
trackFunnelStep,
|
|
30
41
|
useScreenTracking,
|
|
31
42
|
trackVerificationStart,
|
|
32
43
|
trackVerificationComplete,
|
|
33
44
|
} from '../../Shared/Libs/analytics.utils';
|
|
45
|
+
import { useKeepAwake } from '../../Shared/Libs/native-keep-awake.utils';
|
|
34
46
|
|
|
35
47
|
const { width: windowWidth, height: windowHeight } = Dimensions.get('window');
|
|
36
48
|
|
|
37
|
-
const PREVIEW_SIZE = windowWidth * 0.
|
|
49
|
+
const PREVIEW_SIZE = windowWidth * 0.95;
|
|
38
50
|
const PREVIEW_RECT: Rect = {
|
|
39
51
|
minX: (windowWidth - PREVIEW_SIZE) / 2,
|
|
40
52
|
minY: (windowHeight - PREVIEW_SIZE) / 2,
|
|
@@ -78,6 +90,7 @@ type PossibleActions = {
|
|
|
78
90
|
}[keyof Actions];
|
|
79
91
|
|
|
80
92
|
const LivenessDetectionScreen = () => {
|
|
93
|
+
useKeepAwake();
|
|
81
94
|
const [camera, setCamera] = useState<TrustchexCameraHandle | null>(null);
|
|
82
95
|
const navigation = useNavigation();
|
|
83
96
|
const appContext = useContext(AppContext);
|
|
@@ -86,10 +99,39 @@ const LivenessDetectionScreen = () => {
|
|
|
86
99
|
const [isRecording, setIsRecording] = useState(false);
|
|
87
100
|
const insets = useSafeAreaInsets();
|
|
88
101
|
const referenceFaceTrackingId = useRef<number | null>(null);
|
|
102
|
+
const recordingDotOpacity = useRef(new Animated.Value(1)).current;
|
|
89
103
|
|
|
90
104
|
// Track screen view and exit
|
|
91
105
|
useScreenTracking('liveness_detection');
|
|
92
106
|
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (isRecording) {
|
|
109
|
+
const pulse = Animated.loop(
|
|
110
|
+
Animated.sequence([
|
|
111
|
+
Animated.timing(recordingDotOpacity, {
|
|
112
|
+
toValue: 0.2,
|
|
113
|
+
duration: 600,
|
|
114
|
+
easing: Easing.inOut(Easing.ease),
|
|
115
|
+
useNativeDriver: true,
|
|
116
|
+
}),
|
|
117
|
+
Animated.timing(recordingDotOpacity, {
|
|
118
|
+
toValue: 1,
|
|
119
|
+
duration: 600,
|
|
120
|
+
easing: Easing.inOut(Easing.ease),
|
|
121
|
+
useNativeDriver: true,
|
|
122
|
+
}),
|
|
123
|
+
])
|
|
124
|
+
);
|
|
125
|
+
pulse.start();
|
|
126
|
+
return () => pulse.stop();
|
|
127
|
+
} else {
|
|
128
|
+
recordingDotOpacity.setValue(1);
|
|
129
|
+
}
|
|
130
|
+
}, [isRecording, recordingDotOpacity]);
|
|
131
|
+
|
|
132
|
+
// Configure status bar for white background
|
|
133
|
+
useStatusBarWhiteBackground();
|
|
134
|
+
|
|
93
135
|
const [initialState, setInitialState] = useState<StateType>({
|
|
94
136
|
brightnessLow: false,
|
|
95
137
|
faceDetected: false,
|
|
@@ -142,7 +184,23 @@ const LivenessDetectionScreen = () => {
|
|
|
142
184
|
const [instructionList, setInstructionList] = useState<string[]>([]);
|
|
143
185
|
const [hasGuideShown, setHasGuideShown] = useState(false);
|
|
144
186
|
const stoppingRecordingRef = useRef(false); // Track if we're already stopping to prevent multiple calls
|
|
145
|
-
|
|
187
|
+
|
|
188
|
+
// Configure status bar for white background
|
|
189
|
+
useStatusBarWhiteBackground();
|
|
190
|
+
|
|
191
|
+
// Update status bar when guide visibility changes
|
|
192
|
+
useEffect(() => {
|
|
193
|
+
if (hasGuideShown) {
|
|
194
|
+
// Camera view - use light icons
|
|
195
|
+
StatusBar.setBarStyle('light-content', true);
|
|
196
|
+
} else {
|
|
197
|
+
// Guide screen with white background - use dark icons
|
|
198
|
+
StatusBar.setBarStyle('dark-content', true);
|
|
199
|
+
if (Platform.OS === 'android') {
|
|
200
|
+
StatusBar.setBackgroundColor('#ffffff', true);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}, [hasGuideShown]);
|
|
146
204
|
|
|
147
205
|
useEffect(() => {
|
|
148
206
|
const il = Object.keys(instructions)
|
|
@@ -175,7 +233,6 @@ const LivenessDetectionScreen = () => {
|
|
|
175
233
|
processComplete: false,
|
|
176
234
|
videoPath: '',
|
|
177
235
|
});
|
|
178
|
-
lastVoiceGuidanceMessage.current = '';
|
|
179
236
|
resetLastMessage();
|
|
180
237
|
}, [
|
|
181
238
|
instructions,
|
|
@@ -263,6 +320,14 @@ const LivenessDetectionScreen = () => {
|
|
|
263
320
|
return leftOpen >= 0.8 && rightOpen >= 0.8;
|
|
264
321
|
};
|
|
265
322
|
|
|
323
|
+
const areEyesVisible = (face: Face) => {
|
|
324
|
+
// Relaxed check: eyes just need to be detected (not fully closed).
|
|
325
|
+
// Smiling naturally causes squinting, so a lower threshold is used.
|
|
326
|
+
const leftOpen = face.leftEyeOpenProbability ?? 0;
|
|
327
|
+
const rightOpen = face.rightEyeOpenProbability ?? 0;
|
|
328
|
+
return leftOpen >= 0.3 && rightOpen >= 0.3;
|
|
329
|
+
};
|
|
330
|
+
|
|
266
331
|
const instructionReducer = (
|
|
267
332
|
state: StateType,
|
|
268
333
|
action: PossibleActions
|
|
@@ -350,7 +415,6 @@ const LivenessDetectionScreen = () => {
|
|
|
350
415
|
const nextInstruction = state.instructionList[nextInstructionIndex];
|
|
351
416
|
|
|
352
417
|
// Reset TTS state when moving to any new instruction to ensure it speaks
|
|
353
|
-
lastVoiceGuidanceMessage.current = '';
|
|
354
418
|
resetLastMessage();
|
|
355
419
|
|
|
356
420
|
// Calculate progress based on actual action steps (excluding START and FINISH)
|
|
@@ -404,8 +468,7 @@ const LivenessDetectionScreen = () => {
|
|
|
404
468
|
// Let the instruction advance first, then speak the next instruction
|
|
405
469
|
|
|
406
470
|
// Only speak if message changed and is not empty
|
|
407
|
-
if (text
|
|
408
|
-
lastVoiceGuidanceMessage.current = text;
|
|
471
|
+
if (text) {
|
|
409
472
|
// Bypass interval for liveness instructions to ensure all instructions are spoken
|
|
410
473
|
speak(text, true);
|
|
411
474
|
}
|
|
@@ -502,26 +565,42 @@ const LivenessDetectionScreen = () => {
|
|
|
502
565
|
);
|
|
503
566
|
}
|
|
504
567
|
|
|
505
|
-
//
|
|
506
|
-
//
|
|
507
|
-
|
|
568
|
+
// Map the on-screen circle into frame coordinates.
|
|
569
|
+
// Camera uses FILL_CENTER / resizeAspectFill (cover): uniform scale filling
|
|
570
|
+
// the view, cropping overflow symmetrically.
|
|
571
|
+
const coverScale = Math.max(
|
|
572
|
+
windowWidth / frameWidth,
|
|
573
|
+
windowHeight / frameHeight
|
|
574
|
+
);
|
|
575
|
+
const offsetX = (frameWidth - windowWidth / coverScale) / 2;
|
|
576
|
+
const offsetY = (frameHeight - windowHeight / coverScale) / 2;
|
|
508
577
|
const previewRectInFrame: Rect = {
|
|
509
|
-
minX:
|
|
510
|
-
minY:
|
|
511
|
-
width:
|
|
512
|
-
height:
|
|
578
|
+
minX: PREVIEW_RECT.minX / coverScale + offsetX,
|
|
579
|
+
minY: PREVIEW_RECT.minY / coverScale + offsetY,
|
|
580
|
+
width: PREVIEW_RECT.width / coverScale,
|
|
581
|
+
height: PREVIEW_RECT.height / coverScale,
|
|
513
582
|
};
|
|
583
|
+
const previewSizeInFrame = PREVIEW_RECT.width / coverScale;
|
|
514
584
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
585
|
+
// Check containment using face center + yaw-stable core radius.
|
|
586
|
+
// When the head turns (yaw), the bounding box grows wider but height stays
|
|
587
|
+
// constant — so min(width, height)/2 gives a stable size that doesn't
|
|
588
|
+
// falsely trigger an outside-circle reset during TURN_HEAD_LEFT/RIGHT.
|
|
589
|
+
const faceCenterX = face.bounds.x + face.bounds.width / 2;
|
|
590
|
+
const faceCenterY = face.bounds.y + face.bounds.height / 2;
|
|
591
|
+
const faceCoreRadius = Math.max(
|
|
592
|
+
0,
|
|
593
|
+
Math.min(face.bounds.width, face.bounds.height) / 2 -
|
|
594
|
+
PREVIEW_EDGE_OFFSET / 2
|
|
595
|
+
);
|
|
596
|
+
const circleCX = previewRectInFrame.minX + previewRectInFrame.width / 2;
|
|
597
|
+
const circleCY = previewRectInFrame.minY + previewRectInFrame.height / 2;
|
|
598
|
+
const circleR = previewSizeInFrame / 2;
|
|
599
|
+
const faceDx = faceCenterX - circleCX;
|
|
600
|
+
const faceDy = faceCenterY - circleCY;
|
|
601
|
+
const previewContainsFace =
|
|
602
|
+
faceDx * faceDx + faceDy * faceDy <=
|
|
603
|
+
(circleR - faceCoreRadius) * (circleR - faceCoreRadius);
|
|
525
604
|
const multipleFacesDetected = faces.length > 1;
|
|
526
605
|
|
|
527
606
|
if (!isImageBright) {
|
|
@@ -670,11 +749,12 @@ const LivenessDetectionScreen = () => {
|
|
|
670
749
|
instructions.SMILE.minAngle < face.pitchAngle &&
|
|
671
750
|
face.pitchAngle < instructions.SMILE.maxAngle;
|
|
672
751
|
|
|
673
|
-
// Check if smiling with sufficient probability AND looking at camera AND eyes
|
|
752
|
+
// Check if smiling with sufficient probability AND looking at camera AND eyes visible
|
|
753
|
+
// Use relaxed eye check — smiling naturally causes squinting
|
|
674
754
|
if (
|
|
675
755
|
smilingProb >= instructions.SMILE.minProbability &&
|
|
676
756
|
isFacingCamera &&
|
|
677
|
-
|
|
757
|
+
areEyesVisible(face)
|
|
678
758
|
) {
|
|
679
759
|
instructions.SMILE.photo = image;
|
|
680
760
|
dispatch({ type: 'NEXT_INSTRUCTION', payload: 'SMILE' });
|
|
@@ -818,6 +898,10 @@ const LivenessDetectionScreen = () => {
|
|
|
818
898
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
819
899
|
}, []); // Empty array = only run on mount/unmount
|
|
820
900
|
|
|
901
|
+
if (!appContext.currentWorkflowStep || !appContext.currentWorkflowStep.data) {
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
|
|
821
905
|
return (
|
|
822
906
|
<>
|
|
823
907
|
{hasGuideShown ? (
|
|
@@ -862,6 +946,24 @@ const LivenessDetectionScreen = () => {
|
|
|
862
946
|
mask="url(#hole-mask)"
|
|
863
947
|
/>
|
|
864
948
|
</Svg>
|
|
949
|
+
{isRecording && (
|
|
950
|
+
<View
|
|
951
|
+
style={[
|
|
952
|
+
styles.recordingIndicatorContainer,
|
|
953
|
+
{ top: PREVIEW_RECT.minY - 40 },
|
|
954
|
+
]}
|
|
955
|
+
>
|
|
956
|
+
<View style={styles.recordingIndicator}>
|
|
957
|
+
<Animated.View
|
|
958
|
+
style={[
|
|
959
|
+
styles.recordingDot,
|
|
960
|
+
{ opacity: recordingDotOpacity },
|
|
961
|
+
]}
|
|
962
|
+
/>
|
|
963
|
+
<Text style={styles.recordingText}>REC</Text>
|
|
964
|
+
</View>
|
|
965
|
+
</View>
|
|
966
|
+
)}
|
|
865
967
|
<View
|
|
866
968
|
style={[
|
|
867
969
|
styles.instructionsContainerBottom,
|
|
@@ -986,6 +1088,33 @@ const styles = StyleSheet.create({
|
|
|
986
1088
|
width: '100%',
|
|
987
1089
|
zIndex: 1,
|
|
988
1090
|
},
|
|
1091
|
+
recordingIndicatorContainer: {
|
|
1092
|
+
position: 'absolute',
|
|
1093
|
+
width: '100%',
|
|
1094
|
+
alignItems: 'center',
|
|
1095
|
+
zIndex: 2,
|
|
1096
|
+
},
|
|
1097
|
+
recordingIndicator: {
|
|
1098
|
+
flexDirection: 'row',
|
|
1099
|
+
alignItems: 'center',
|
|
1100
|
+
gap: 8,
|
|
1101
|
+
backgroundColor: 'rgba(0, 0, 0, 0.6)',
|
|
1102
|
+
paddingVertical: 6,
|
|
1103
|
+
paddingHorizontal: 14,
|
|
1104
|
+
borderRadius: 20,
|
|
1105
|
+
},
|
|
1106
|
+
recordingDot: {
|
|
1107
|
+
width: 10,
|
|
1108
|
+
height: 10,
|
|
1109
|
+
borderRadius: 5,
|
|
1110
|
+
backgroundColor: '#ef4444',
|
|
1111
|
+
},
|
|
1112
|
+
recordingText: {
|
|
1113
|
+
color: '#ffffff',
|
|
1114
|
+
fontSize: 14,
|
|
1115
|
+
fontWeight: '700',
|
|
1116
|
+
letterSpacing: 1,
|
|
1117
|
+
},
|
|
989
1118
|
guide: {
|
|
990
1119
|
flex: 1,
|
|
991
1120
|
display: 'flex',
|