@test-web/react-native-sdk 2.2.0 → 2.4.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/build/.transforms/246c075ea944392e66db7aa639265547/results.bin +1 -0
- package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/yourcompany/sdk/BuildConfig.dex +0 -0
- package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/yourcompany/sdk/YourSDKModule.dex +0 -0
- package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/yourcompany/sdk/YourSDKPackage.dex +0 -0
- package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
- package/android/build/.transforms/952af5a0e7b5b2ac3d48ad66eccefd1f/results.bin +1 -0
- package/android/build/.transforms/952af5a0e7b5b2ac3d48ad66eccefd1f/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/buildConfig/debug/com/yourcompany/sdk/BuildConfig.java +10 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +34 -0
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
- package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
- package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
- package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/yourcompany/sdk/BuildConfig.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/yourcompany/sdk/YourSDKModule.class +0 -0
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/yourcompany/sdk/YourSDKPackage.class +0 -0
- package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +2 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +56 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +34 -0
- package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
- package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/yourcompany/sdk/BuildConfig.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/yourcompany/sdk/YourSDKModule.class +0 -0
- package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/yourcompany/sdk/YourSDKPackage.class +0 -0
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +61 -0
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/package.json +1 -1
- package/src/apis/captureBarcode.ts +37 -0
- package/src/apis/captureMRZ.ts +39 -0
- package/src/apis/checkLiveness.ts +48 -0
- package/src/apis/index.ts +26 -75
- package/src/components/common/Footer.tsx +19 -22
- package/src/components/common/Header.tsx +22 -21
- package/src/components/common/Loader.tsx +9 -28
- package/src/components/ui/Button.tsx +23 -31
- package/src/components/ui/ThemedText.tsx +21 -32
- package/src/context/FailedCountContext.tsx +35 -0
- package/src/context/IDMConfigurationContext.tsx +12 -2
- package/src/context/themes.ts +20 -0
- package/src/index.tsx +41 -29
- package/src/screens/BackDocumentAdvice.tsx +13 -18
- package/src/screens/BarcodeAdvice.tsx +39 -19
- package/src/screens/BarcodeCapture.tsx +127 -158
- package/src/screens/DocumentCaptureBack.tsx +145 -102
- package/src/screens/DocumentCaptureFront.tsx +146 -113
- package/src/screens/FrontDocumentAdvice.tsx +13 -18
- package/src/screens/LocationPermission.tsx +124 -17
- package/src/screens/MrzAdvice.tsx +27 -13
- package/src/screens/MrzCapture.tsx +233 -206
- package/src/screens/SelectDocuments.tsx +57 -66
- package/src/screens/SelfieAdvice.tsx +2 -3
- package/src/screens/SelfieCapture.tsx +135 -103
- package/src/screens/ThankYou.tsx +25 -31
- package/src/screens/VerifyIdentity.tsx +1 -0
- package/src/styles/BarcodeAdviceStyles.ts +6 -6
- package/src/styles/DocumentCaptureBackStyle.ts +70 -0
- package/src/styles/DocumentCaptureFrontStyle.ts +70 -0
- package/src/styles/FooterStyles.ts +27 -0
- package/src/styles/HeaderStyles.ts +20 -0
- package/src/styles/LoaderStyles.ts +14 -0
- package/src/styles/ScannerStyles.ts +46 -9
- package/src/styles/ThankYouStyles.ts +8 -11
- package/src/types/index.ts +8 -0
- package/src/utils/MRZ_README.md +111 -0
- package/src/utils/imagesHelper.ts +264 -0
- package/src/utils/metadata_new.json +11373 -1
- package/src/utils/mrzExamples.ts +127 -0
- package/src/utils/mrzParser.ts +303 -0
- package/src/utils/mrzUtils.ts +70 -0
|
@@ -1,168 +1,147 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import { View, Text
|
|
3
|
-
import
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { View, Text } from 'react-native';
|
|
3
|
+
import getStyles from '../styles/ScannerStyles';
|
|
4
4
|
import { useTheme } from '../context/ThemeContext';
|
|
5
5
|
import { useOrientation } from '../hooks/useOrientation';
|
|
6
|
-
import { useIDM } from '../context/IDMConfigurationContext';
|
|
7
|
-
import getStyles from '../styles/ScannerStyles';
|
|
8
6
|
import Loader from '../components/common/Loader';
|
|
9
7
|
import { captureBarcode } from '../apis';
|
|
8
|
+
import { useIDM } from '../context/IDMConfigurationContext';
|
|
9
|
+
import { useNavigation } from '@react-navigation/native';
|
|
10
|
+
import CustomOverlay from '../components/common/CustomOverlay';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
// Note: This component requires dynamsoft-capture-vision-react-native
|
|
13
|
+
// Install with: npm install dynamsoft-capture-vision-react-native
|
|
14
|
+
|
|
15
|
+
const BarcodeCapture: React.FC = () => {
|
|
12
16
|
const { theme } = useTheme();
|
|
13
|
-
const { idmConf, setIDMConf } = useIDM();
|
|
14
17
|
const orientation = useOrientation();
|
|
15
|
-
const
|
|
18
|
+
const { idmConf } = useIDM();
|
|
16
19
|
const navigation = useNavigation();
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const
|
|
20
|
+
const styles = getStyles(theme, orientation);
|
|
21
|
+
const cameraViewRef = useRef<any>(null);
|
|
22
|
+
|
|
23
|
+
const [isScanningEnabled, setIsScanningEnabled] = useState(true);
|
|
24
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
21
25
|
|
|
22
|
-
// Initialize Dynamsoft Barcode Scanner
|
|
23
26
|
useEffect(() => {
|
|
24
|
-
let
|
|
27
|
+
let cleanupFn: (() => void) | undefined;
|
|
25
28
|
|
|
26
|
-
const
|
|
29
|
+
const initialize = async () => {
|
|
27
30
|
try {
|
|
28
|
-
//
|
|
29
|
-
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
31
|
+
// Import Dynamsoft modules
|
|
32
|
+
const {
|
|
33
|
+
CameraEnhancer,
|
|
34
|
+
CameraView,
|
|
35
|
+
CaptureVisionRouter,
|
|
36
|
+
EnumPresetTemplate,
|
|
37
|
+
EnumBarcodeFormat,
|
|
38
|
+
LicenseManager,
|
|
39
|
+
} = require('dynamsoft-capture-vision-react-native');
|
|
40
|
+
|
|
41
|
+
await LicenseManager.initLicense(
|
|
42
|
+
'DLS2eyJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSJ9'
|
|
43
|
+
);
|
|
44
|
+
await CameraEnhancer.requestCameraPermission();
|
|
45
|
+
|
|
46
|
+
const camera = await CameraEnhancer.getInstance();
|
|
47
|
+
const router = await CaptureVisionRouter.getInstance();
|
|
48
|
+
|
|
49
|
+
if (cameraViewRef.current) {
|
|
50
|
+
await camera.setCameraView(cameraViewRef.current);
|
|
40
51
|
}
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
initializeScanner();
|
|
45
|
-
|
|
46
|
-
return () => {
|
|
47
|
-
isMounted = false;
|
|
48
|
-
setIsScanning(false);
|
|
49
|
-
};
|
|
50
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
51
|
-
}, []);
|
|
52
|
-
|
|
53
|
-
// Handle barcode scan result
|
|
54
|
-
const handleBarcodeScanned = async (barcodeText: string) => {
|
|
55
|
-
if (loading || !barcodeText || !isScanning) return;
|
|
56
|
-
|
|
57
|
-
setIsScanning(false);
|
|
58
|
-
setScannedData(barcodeText);
|
|
59
|
-
|
|
60
|
-
try {
|
|
61
|
-
setLoading(true);
|
|
62
|
-
|
|
63
|
-
const barcodeData = {
|
|
64
|
-
token: String(idmConf.verificationCode || ''),
|
|
65
|
-
text: barcodeText,
|
|
66
|
-
};
|
|
67
52
|
|
|
68
|
-
|
|
69
|
-
const result = await captureBarcode(idmConf, barcodeData);
|
|
53
|
+
await router.setInput(camera);
|
|
70
54
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
// Navigate to ThankYou
|
|
79
|
-
navigation.navigate('ThankYou' as never);
|
|
80
|
-
} else {
|
|
81
|
-
Alert.alert('Error', result.errorMessage || 'Failed to process barcode', [
|
|
82
|
-
{
|
|
83
|
-
text: 'Retry',
|
|
84
|
-
onPress: () => {
|
|
85
|
-
setIsScanning(true);
|
|
86
|
-
setScannedData(null);
|
|
87
|
-
setLoading(false);
|
|
88
|
-
},
|
|
55
|
+
const settings = {
|
|
56
|
+
timeout: 1000,
|
|
57
|
+
barcodeSettings: {
|
|
58
|
+
expectedBarcodesCount: 1,
|
|
59
|
+
barcodeFormats: Number(EnumBarcodeFormat.BF_MICRO_PDF417),
|
|
89
60
|
},
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
await router.updateSettings(
|
|
64
|
+
settings,
|
|
65
|
+
EnumPresetTemplate.PT_READ_SINGLE_BARCODE
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const receiver = router.addResultReceiver({
|
|
69
|
+
onDecodedBarcodesReceived: async (result: any) => {
|
|
70
|
+
const firstBarcode = result.items?.[0];
|
|
71
|
+
if (firstBarcode?.text) {
|
|
72
|
+
setIsScanningEnabled(false);
|
|
73
|
+
setIsLoading(true);
|
|
74
|
+
|
|
75
|
+
const requestData = {
|
|
76
|
+
text: firstBarcode.text,
|
|
77
|
+
token: idmConf.verificationCode ?? '',
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const barcodeScanResp = await captureBarcode(idmConf, requestData);
|
|
82
|
+
console.log('barcodeScanResp', barcodeScanResp);
|
|
83
|
+
|
|
84
|
+
// Check if verification was already processed (409 status)
|
|
85
|
+
if (barcodeScanResp.status === 409) {
|
|
86
|
+
console.log('Verification already processed, proceeding to ThankYou');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
navigation.navigate('ThankYou' as never);
|
|
90
|
+
} catch (error) {
|
|
91
|
+
console.error('Barcode capture error:', error);
|
|
92
|
+
setIsScanningEnabled(true);
|
|
93
|
+
} finally {
|
|
94
|
+
setIsLoading(false);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
101
97
|
},
|
|
102
|
-
}
|
|
103
|
-
]);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
98
|
+
});
|
|
106
99
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
100
|
+
await camera.open();
|
|
101
|
+
await router.startCapturing(EnumPresetTemplate.PT_READ_SINGLE_BARCODE);
|
|
102
|
+
|
|
103
|
+
cleanupFn = () => {
|
|
104
|
+
router.removeResultReceiver(receiver);
|
|
105
|
+
router.stopCapturing();
|
|
106
|
+
camera.close();
|
|
107
|
+
};
|
|
108
|
+
} catch (err) {
|
|
109
|
+
console.error('Dynamsoft initialization error:', err);
|
|
110
|
+
console.warn(
|
|
111
|
+
'Barcode scanning requires dynamsoft-capture-vision-react-native library'
|
|
112
|
+
);
|
|
113
113
|
}
|
|
114
|
-
}
|
|
114
|
+
};
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
};
|
|
116
|
+
initialize();
|
|
118
117
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
return () => {
|
|
119
|
+
if (cleanupFn) cleanupFn();
|
|
120
|
+
};
|
|
121
|
+
}, [isScanningEnabled, idmConf, navigation]);
|
|
123
122
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
overlayVisible={true}
|
|
130
|
-
onBarcodeScanned={(results: any) => {
|
|
131
|
-
if (results && results.length > 0 && isScanning) {
|
|
132
|
-
const barcodeText = results[0].barcodeText || results[0].text;
|
|
133
|
-
handleBarcodeScanned(barcodeText);
|
|
134
|
-
}
|
|
135
|
-
}}
|
|
136
|
-
/>
|
|
137
|
-
);
|
|
123
|
+
// Render Dynamsoft CameraView or fallback
|
|
124
|
+
const renderCameraView = () => {
|
|
125
|
+
try {
|
|
126
|
+
const { CameraView } = require('dynamsoft-capture-vision-react-native');
|
|
127
|
+
return <CameraView ref={cameraViewRef} style={styles.camera} />;
|
|
138
128
|
} catch (error) {
|
|
139
|
-
console.warn('Dynamsoft camera view not available:', error);
|
|
140
|
-
|
|
141
|
-
// Fallback UI
|
|
142
129
|
return (
|
|
143
|
-
<View
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
|
|
130
|
+
<View
|
|
131
|
+
style={[
|
|
132
|
+
styles.camera,
|
|
133
|
+
{
|
|
134
|
+
backgroundColor: '#000',
|
|
135
|
+
justifyContent: 'center',
|
|
136
|
+
alignItems: 'center',
|
|
137
|
+
},
|
|
138
|
+
]}
|
|
139
|
+
>
|
|
140
|
+
<Text style={{ color: '#fff', textAlign: 'center', padding: 20 }}>
|
|
141
|
+
Barcode scanning requires{'\n'}
|
|
142
|
+
dynamsoft-capture-vision-react-native{'\n\n'}
|
|
143
|
+
Install with:{'\n'}npm install dynamsoft-capture-vision-react-native
|
|
153
144
|
</Text>
|
|
154
|
-
{scannedData && (
|
|
155
|
-
<Text
|
|
156
|
-
style={{
|
|
157
|
-
color: '#0f0',
|
|
158
|
-
textAlign: 'center',
|
|
159
|
-
marginTop: 20,
|
|
160
|
-
fontSize: 12,
|
|
161
|
-
}}
|
|
162
|
-
>
|
|
163
|
-
Detected: {scannedData.substring(0, 30)}...
|
|
164
|
-
</Text>
|
|
165
|
-
)}
|
|
166
145
|
</View>
|
|
167
146
|
);
|
|
168
147
|
}
|
|
@@ -174,23 +153,13 @@ export default function BarcodeCapture() {
|
|
|
174
153
|
Fill the box with the barcode{'\n'}until it turns green
|
|
175
154
|
</Text>
|
|
176
155
|
|
|
177
|
-
{
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
position: 'absolute',
|
|
183
|
-
top: '40%',
|
|
184
|
-
alignSelf: 'center',
|
|
185
|
-
width: 350,
|
|
186
|
-
height: 100,
|
|
187
|
-
borderWidth: 2,
|
|
188
|
-
borderColor: scannedData ? '#00ff00' : '#ffffff',
|
|
189
|
-
backgroundColor: 'transparent',
|
|
190
|
-
}}
|
|
191
|
-
/>
|
|
192
|
-
|
|
193
|
-
{loading && <Loader message="Processing barcode..." />}
|
|
156
|
+
{renderCameraView()}
|
|
157
|
+
|
|
158
|
+
<CustomOverlay holeWidth={350} holeHeight={100} />
|
|
159
|
+
|
|
160
|
+
{isLoading && <Loader />}
|
|
194
161
|
</View>
|
|
195
162
|
);
|
|
196
|
-
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export default BarcodeCapture;
|
|
@@ -1,158 +1,199 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from 'react-native';
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, Image, Alert, ScrollView } from 'react-native';
|
|
3
3
|
import { useNavigation } from '@react-navigation/native';
|
|
4
|
-
import
|
|
4
|
+
import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
|
|
5
|
+
import ImageResizer from 'react-native-image-resizer';
|
|
6
|
+
import RNFS from 'react-native-fs';
|
|
7
|
+
|
|
8
|
+
import getStyles from '../styles/DocumentCaptureBackStyle';
|
|
5
9
|
import { useTheme } from '../context/ThemeContext';
|
|
6
|
-
import { useOrientation } from '../hooks/useOrientation';
|
|
7
10
|
import { useIDM } from '../context/IDMConfigurationContext';
|
|
8
|
-
import
|
|
11
|
+
import { useOrientation } from '../hooks/useOrientation';
|
|
9
12
|
import { captureDocumentBack } from '../apis';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
13
|
+
import Loader from '../components/common/Loader';
|
|
14
|
+
import Header from '../components/common/Header';
|
|
15
|
+
import Button from '../components/ui/Button';
|
|
16
|
+
import ThemedText from '../components/ui/ThemedText';
|
|
17
|
+
import getPermissionStyles from '../styles/PermissionStyle';
|
|
18
|
+
import { useFailedCount } from '../context/FailedCountContext';
|
|
12
19
|
|
|
13
20
|
export default function DocumentCaptureBack() {
|
|
14
21
|
const { theme } = useTheme();
|
|
15
22
|
const { idmConf, setIDMConf } = useIDM();
|
|
16
23
|
const orientation = useOrientation();
|
|
17
24
|
const styles = getStyles(theme, orientation);
|
|
25
|
+
const permissionStyles = getPermissionStyles(theme, orientation);
|
|
26
|
+
const { hasPermission, requestPermission } = useCameraPermission();
|
|
27
|
+
const device = useCameraDevice('back');
|
|
28
|
+
const cameraRef = useRef<Camera>(null);
|
|
18
29
|
const navigation = useNavigation();
|
|
19
|
-
const
|
|
20
|
-
const [hasPermission, setHasPermission] = useState(false);
|
|
21
|
-
const [device, setDevice] = useState<any>(null);
|
|
22
|
-
const cameraRef = useRef<any>(null);
|
|
23
|
-
|
|
24
|
-
// Initialize camera
|
|
25
|
-
useEffect(() => {
|
|
26
|
-
const initializeCamera = async () => {
|
|
27
|
-
try {
|
|
28
|
-
const { Camera, useCameraDevice, useCameraPermission } = require('react-native-vision-camera');
|
|
29
|
-
|
|
30
|
-
const { hasPermission: hasPerm, requestPermission } = useCameraPermission();
|
|
31
|
-
|
|
32
|
-
if (!hasPerm) {
|
|
33
|
-
const granted = await requestPermission();
|
|
34
|
-
if (!granted) {
|
|
35
|
-
navigation.navigate('CameraPermission' as never);
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
setHasPermission(true);
|
|
41
|
-
|
|
42
|
-
const backDevice = useCameraDevice('back');
|
|
43
|
-
if (!backDevice) {
|
|
44
|
-
navigation.navigate('NoCameraFound' as never);
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
setDevice(backDevice);
|
|
49
|
-
} catch (error) {
|
|
50
|
-
console.warn('Camera initialization failed:', error);
|
|
51
|
-
setHasPermission(true);
|
|
52
|
-
}
|
|
53
|
-
};
|
|
30
|
+
const { failedCount, setFailedCount } = useFailedCount();
|
|
54
31
|
|
|
55
|
-
|
|
56
|
-
}, [navigation]);
|
|
32
|
+
const [loading, setLoading] = useState(false);
|
|
57
33
|
|
|
58
|
-
//
|
|
34
|
+
// Decide next navigation step
|
|
59
35
|
const handleNextStep = () => {
|
|
36
|
+
setFailedCount(0); // Reset failed count on successful capture
|
|
60
37
|
const metaData = idmConf?.selectedCountryDetails?.selectedMetaData;
|
|
61
|
-
|
|
62
38
|
if (!metaData) {
|
|
63
39
|
navigation.navigate('ThankYou' as never);
|
|
64
40
|
return;
|
|
65
41
|
}
|
|
66
42
|
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
43
|
+
const { document_flow, barcode } = metaData;
|
|
44
|
+
|
|
45
|
+
if (!document_flow || document_flow.length === 0) {
|
|
46
|
+
// Check if barcode scan is needed even without document flow
|
|
47
|
+
if (barcode === 'PDF417 B' || barcode === 'PDF417F') {
|
|
48
|
+
navigation.navigate('BarcodeAdvice' as never);
|
|
49
|
+
} else if (['TD3', 'TD2', 'TD1'].includes(barcode)) {
|
|
50
|
+
navigation.navigate('MrzAdvice' as never);
|
|
51
|
+
} else {
|
|
52
|
+
navigation.navigate('ThankYou' as never);
|
|
53
|
+
}
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Decide based on first element in flow
|
|
58
|
+
const currentStep = document_flow[0];
|
|
59
|
+
|
|
60
|
+
if (currentStep === 'B') {
|
|
61
|
+
if (document_flow.includes('F')) {
|
|
62
|
+
navigation.navigate('FrontDocumentAdvice' as never);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// If no more flow, handle barcode
|
|
68
|
+
if (barcode === 'PDF417 B' || barcode === 'PDF417F') {
|
|
69
|
+
navigation.navigate('BarcodeAdvice' as never);
|
|
70
|
+
} else if (['TD3', 'TD2', 'TD1'].includes(barcode)) {
|
|
71
|
+
navigation.navigate('MrzAdvice' as never);
|
|
72
|
+
} else {
|
|
73
|
+
navigation.navigate('ThankYou' as never);
|
|
74
|
+
}
|
|
70
75
|
};
|
|
71
76
|
|
|
77
|
+
// Check for device availability
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (!device) {
|
|
80
|
+
navigation.navigate('NoCameraFound' as never);
|
|
81
|
+
}
|
|
82
|
+
}, [device, navigation]);
|
|
83
|
+
|
|
84
|
+
// Show permission request screen if camera permission is not granted
|
|
85
|
+
if (!hasPermission) {
|
|
86
|
+
return (
|
|
87
|
+
<ScrollView contentContainerStyle={permissionStyles.container}>
|
|
88
|
+
<Header />
|
|
89
|
+
|
|
90
|
+
<ThemedText style={permissionStyles.maintitle} type="title">
|
|
91
|
+
Camera Access Required
|
|
92
|
+
</ThemedText>
|
|
93
|
+
|
|
94
|
+
<View style={{ padding: 20 }}>
|
|
95
|
+
<Text style={{ color: theme.colors.text, textAlign: 'center', marginBottom: 20 }}>
|
|
96
|
+
This app needs access to your camera to capture documents. Please grant camera permission.
|
|
97
|
+
</Text>
|
|
98
|
+
</View>
|
|
99
|
+
|
|
100
|
+
<Button
|
|
101
|
+
title="Grant Permission"
|
|
102
|
+
style={permissionStyles.buttonplace}
|
|
103
|
+
textStyle={permissionStyles.button}
|
|
104
|
+
onPress={async () => {
|
|
105
|
+
const granted = await requestPermission();
|
|
106
|
+
if (!granted) {
|
|
107
|
+
console.log('Camera permission denied');
|
|
108
|
+
}
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
</ScrollView>
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!device) return null;
|
|
116
|
+
|
|
117
|
+
// Take photo and process
|
|
72
118
|
const takePhoto = async () => {
|
|
73
|
-
if (!cameraRef.current) {
|
|
74
|
-
Alert.alert('Camera Not Available', 'Camera is not available in this environment.');
|
|
119
|
+
if (!cameraRef.current || loading) {
|
|
75
120
|
return;
|
|
76
121
|
}
|
|
77
122
|
|
|
78
123
|
try {
|
|
79
124
|
setLoading(true);
|
|
80
|
-
|
|
81
125
|
const photo = await cameraRef.current.takePhoto();
|
|
82
126
|
|
|
83
|
-
if (!
|
|
84
|
-
|
|
127
|
+
if (!photo?.path) {
|
|
128
|
+
console.error('No photo path available');
|
|
85
129
|
setLoading(false);
|
|
86
130
|
return;
|
|
87
131
|
}
|
|
88
132
|
|
|
89
|
-
|
|
90
|
-
|
|
133
|
+
let base64Image;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
const resizedImage = await ImageResizer.createResizedImage(
|
|
137
|
+
`file://${photo.path}`,
|
|
138
|
+
810,
|
|
139
|
+
1080,
|
|
140
|
+
'JPEG',
|
|
141
|
+
70,
|
|
142
|
+
0,
|
|
143
|
+
undefined,
|
|
144
|
+
false
|
|
145
|
+
);
|
|
146
|
+
base64Image = await RNFS.readFile(resizedImage.uri, 'base64');
|
|
147
|
+
} catch (resizeError: any) {
|
|
148
|
+
console.warn('Image resizing failed, using original image:', resizeError.message);
|
|
149
|
+
base64Image = await RNFS.readFile(photo.path, 'base64');
|
|
150
|
+
}
|
|
91
151
|
|
|
92
|
-
// Prepare photo data for API
|
|
93
152
|
const photoData = {
|
|
94
153
|
token: String(idmConf.verificationCode || ''),
|
|
95
154
|
file: base64Image,
|
|
96
155
|
};
|
|
97
156
|
|
|
98
|
-
// Call API to capture document back
|
|
99
157
|
const result = await captureDocumentBack(idmConf, photoData);
|
|
100
158
|
|
|
101
|
-
if (result.status) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
// Navigate to next step based on metadata
|
|
159
|
+
if (typeof result.status === 'string') {
|
|
160
|
+
setIDMConf((prev) => ({
|
|
161
|
+
...prev,
|
|
162
|
+
requestConfiguration: result,
|
|
163
|
+
}));
|
|
109
164
|
handleNextStep();
|
|
110
165
|
} else {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
166
|
+
// Failed document capture
|
|
167
|
+
setFailedCount((prev) => prev + 1);
|
|
168
|
+
if (failedCount >= 4) {
|
|
169
|
+
// After 5 failed attempts (0-4), reset and proceed to ThankYou
|
|
170
|
+
setFailedCount(0);
|
|
171
|
+
navigation.navigate('ThankYou' as never);
|
|
172
|
+
} else {
|
|
173
|
+
navigation.navigate('RetakeSelfie' as never, { errorMessage: result.errorMessage });
|
|
174
|
+
}
|
|
114
175
|
}
|
|
115
176
|
} catch (error: any) {
|
|
116
|
-
console.error('Error taking photo:', error);
|
|
177
|
+
console.error('Error taking photo:', error.message);
|
|
117
178
|
Alert.alert('Error', 'Failed to capture or process the image.');
|
|
118
179
|
} finally {
|
|
119
180
|
setLoading(false);
|
|
120
181
|
}
|
|
121
182
|
};
|
|
122
183
|
|
|
123
|
-
const renderCamera = () => {
|
|
124
|
-
try {
|
|
125
|
-
const { Camera } = require('react-native-vision-camera');
|
|
126
|
-
|
|
127
|
-
if (hasPermission && device) {
|
|
128
|
-
return (
|
|
129
|
-
<Camera
|
|
130
|
-
ref={cameraRef}
|
|
131
|
-
style={StyleSheet.absoluteFill}
|
|
132
|
-
device={device}
|
|
133
|
-
isActive={true}
|
|
134
|
-
photo={true}
|
|
135
|
-
/>
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.warn('Camera render failed:', error);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return (
|
|
143
|
-
<View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
|
|
144
|
-
<Text style={{ color: '#fff', textAlign: 'center', marginTop: 100 }}>
|
|
145
|
-
Camera Preview
|
|
146
|
-
</Text>
|
|
147
|
-
</View>
|
|
148
|
-
);
|
|
149
|
-
};
|
|
150
|
-
|
|
151
184
|
return (
|
|
152
185
|
<View style={styles.container}>
|
|
153
186
|
<Text style={styles.topLabel}>Document Back Side</Text>
|
|
154
187
|
|
|
155
|
-
{
|
|
188
|
+
{device && (
|
|
189
|
+
<Camera
|
|
190
|
+
ref={cameraRef}
|
|
191
|
+
style={StyleSheet.absoluteFill}
|
|
192
|
+
device={device}
|
|
193
|
+
isActive={true}
|
|
194
|
+
photo={true}
|
|
195
|
+
/>
|
|
196
|
+
)}
|
|
156
197
|
|
|
157
198
|
<Image
|
|
158
199
|
source={require('../../assets/images/card-overlay-back.png')}
|
|
@@ -160,12 +201,14 @@ export default function DocumentCaptureBack() {
|
|
|
160
201
|
resizeMode="cover"
|
|
161
202
|
/>
|
|
162
203
|
|
|
163
|
-
<Text style={styles.bottomLabel}>
|
|
164
|
-
Position your document fully within the frame
|
|
165
|
-
</Text>
|
|
204
|
+
<Text style={styles.bottomLabel}>Position your document fully within the frame</Text>
|
|
166
205
|
|
|
167
206
|
<View style={styles.buttonplace}>
|
|
168
|
-
<TouchableOpacity
|
|
207
|
+
<TouchableOpacity
|
|
208
|
+
style={styles.captureButton}
|
|
209
|
+
onPress={takePhoto}
|
|
210
|
+
disabled={loading}
|
|
211
|
+
/>
|
|
169
212
|
</View>
|
|
170
213
|
|
|
171
214
|
{loading && <Loader />}
|