@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.
Files changed (78) hide show
  1. package/android/build/.transforms/246c075ea944392e66db7aa639265547/results.bin +1 -0
  2. package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/yourcompany/sdk/BuildConfig.dex +0 -0
  3. package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/yourcompany/sdk/YourSDKModule.dex +0 -0
  4. package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/bundleLibRuntimeToDirDebug_dex/com/yourcompany/sdk/YourSDKPackage.dex +0 -0
  5. package/android/build/.transforms/246c075ea944392e66db7aa639265547/transformed/bundleLibRuntimeToDirDebug/desugar_graph.bin +0 -0
  6. package/android/build/.transforms/952af5a0e7b5b2ac3d48ad66eccefd1f/results.bin +1 -0
  7. package/android/build/.transforms/952af5a0e7b5b2ac3d48ad66eccefd1f/transformed/classes/classes_dex/classes.dex +0 -0
  8. package/android/build/generated/source/buildConfig/debug/com/yourcompany/sdk/BuildConfig.java +10 -0
  9. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +34 -0
  10. package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +18 -0
  11. package/android/build/intermediates/aar_metadata/debug/writeDebugAarMetadata/aar-metadata.properties +6 -0
  12. package/android/build/intermediates/annotation_processor_list/debug/javaPreCompileDebug/annotationProcessors.json +1 -0
  13. package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
  14. package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
  15. package/android/build/intermediates/compile_symbol_list/debug/generateDebugRFile/R.txt +0 -0
  16. package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -0
  17. package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +2 -0
  18. package/android/build/intermediates/incremental/mergeDebugAssets/merger.xml +2 -0
  19. package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +2 -0
  20. package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +2 -0
  21. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/yourcompany/sdk/BuildConfig.class +0 -0
  22. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/yourcompany/sdk/YourSDKModule.class +0 -0
  23. package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/com/yourcompany/sdk/YourSDKPackage.class +0 -0
  24. package/android/build/intermediates/local_only_symbol_list/debug/parseDebugLocalResources/R-def.txt +2 -0
  25. package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +56 -0
  26. package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +34 -0
  27. package/android/build/intermediates/navigation_json/debug/extractDeepLinksDebug/navigation.json +1 -0
  28. package/android/build/intermediates/nested_resources_validation_report/debug/generateDebugResources/nestedResourcesValidationReport.txt +1 -0
  29. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/yourcompany/sdk/BuildConfig.class +0 -0
  30. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/yourcompany/sdk/YourSDKModule.class +0 -0
  31. package/android/build/intermediates/runtime_library_classes_dir/debug/bundleLibRuntimeToDirDebug/com/yourcompany/sdk/YourSDKPackage.class +0 -0
  32. package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
  33. package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -0
  34. package/android/build/outputs/logs/manifest-merger-debug-report.txt +61 -0
  35. package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
  36. package/package.json +1 -1
  37. package/src/apis/captureBarcode.ts +37 -0
  38. package/src/apis/captureMRZ.ts +39 -0
  39. package/src/apis/checkLiveness.ts +48 -0
  40. package/src/apis/index.ts +26 -75
  41. package/src/components/common/Footer.tsx +19 -22
  42. package/src/components/common/Header.tsx +22 -21
  43. package/src/components/common/Loader.tsx +9 -28
  44. package/src/components/ui/Button.tsx +23 -31
  45. package/src/components/ui/ThemedText.tsx +21 -32
  46. package/src/context/FailedCountContext.tsx +35 -0
  47. package/src/context/IDMConfigurationContext.tsx +12 -2
  48. package/src/context/themes.ts +20 -0
  49. package/src/index.tsx +41 -29
  50. package/src/screens/BackDocumentAdvice.tsx +13 -18
  51. package/src/screens/BarcodeAdvice.tsx +39 -19
  52. package/src/screens/BarcodeCapture.tsx +127 -158
  53. package/src/screens/DocumentCaptureBack.tsx +145 -102
  54. package/src/screens/DocumentCaptureFront.tsx +146 -113
  55. package/src/screens/FrontDocumentAdvice.tsx +13 -18
  56. package/src/screens/LocationPermission.tsx +124 -17
  57. package/src/screens/MrzAdvice.tsx +27 -13
  58. package/src/screens/MrzCapture.tsx +233 -206
  59. package/src/screens/SelectDocuments.tsx +57 -66
  60. package/src/screens/SelfieAdvice.tsx +2 -3
  61. package/src/screens/SelfieCapture.tsx +135 -103
  62. package/src/screens/ThankYou.tsx +25 -31
  63. package/src/screens/VerifyIdentity.tsx +1 -0
  64. package/src/styles/BarcodeAdviceStyles.ts +6 -6
  65. package/src/styles/DocumentCaptureBackStyle.ts +70 -0
  66. package/src/styles/DocumentCaptureFrontStyle.ts +70 -0
  67. package/src/styles/FooterStyles.ts +27 -0
  68. package/src/styles/HeaderStyles.ts +20 -0
  69. package/src/styles/LoaderStyles.ts +14 -0
  70. package/src/styles/ScannerStyles.ts +46 -9
  71. package/src/styles/ThankYouStyles.ts +8 -11
  72. package/src/types/index.ts +8 -0
  73. package/src/utils/MRZ_README.md +111 -0
  74. package/src/utils/imagesHelper.ts +264 -0
  75. package/src/utils/metadata_new.json +11373 -1
  76. package/src/utils/mrzExamples.ts +127 -0
  77. package/src/utils/mrzParser.ts +303 -0
  78. package/src/utils/mrzUtils.ts +70 -0
@@ -1,253 +1,280 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
- import { View, Text, StyleSheet, Alert } from 'react-native';
3
- import { useNavigation } from '@react-navigation/native';
1
+ import { useEffect, useRef, useState } from 'react';
2
+ import { View, Text, ScrollView, Image } from 'react-native';
3
+ import { Camera, useCameraDevice, useCameraPermission } from 'react-native-vision-camera';
4
+ import TextRecognition from '@react-native-ml-kit/text-recognition';
4
5
  import { useTheme } from '../context/ThemeContext';
5
6
  import { useOrientation } from '../hooks/useOrientation';
6
7
  import { useIDM } from '../context/IDMConfigurationContext';
7
8
  import getStyles from '../styles/ScannerStyles';
8
- import Loader from '../components/common/Loader';
9
9
  import { captureMRZ } from '../apis';
10
- import { createFlowManager } from '../utils/flowManager';
10
+ import { useNavigation } from '@react-navigation/native';
11
+ import Loader from '../components/common/Loader';
12
+ import Header from '../components/common/Header';
13
+ import Button from '../components/ui/Button';
14
+ import ThemedText from '../components/ui/ThemedText';
15
+ import getPermissionStyles from '../styles/PermissionStyle';
16
+ const overlayImage = require('../../assets/images/MRZOverlay.png');
11
17
 
12
- export default function MrzCapture() {
18
+ function MrzCapture() {
13
19
  const { theme } = useTheme();
14
- const { idmConf, setIDMConf } = useIDM();
15
20
  const orientation = useOrientation();
16
21
  const styles = getStyles(theme, orientation);
22
+ const permissionStyles = getPermissionStyles(theme, orientation);
17
23
  const navigation = useNavigation();
18
- const [loading, setLoading] = useState(false);
19
- const [scannedData, setScannedData] = useState<string | null>(null);
20
- const [isScanning, setIsScanning] = useState(true);
21
- const [hasPermission, setHasPermission] = useState(false);
22
- const [device, setDevice] = useState<any>(null);
23
- const cameraRef = useRef<any>(null);
24
- const frameProcessorRef = useRef<any>(null);
25
-
26
- // Get MRZ engine type from metadata
27
- const getMRZEngine = () => {
28
- const metadata = idmConf?.selectedCountryDetails?.selectedMetaData;
29
- if (!metadata) return 'TD3';
30
-
31
- const flowManager = createFlowManager(metadata);
32
- return flowManager.getMRZEngine();
33
- };
24
+ const { idmConf } = useIDM();
34
25
 
35
- // Initialize camera with ML Kit
36
- useEffect(() => {
37
- let isMounted = true;
26
+ const { hasPermission, requestPermission } = useCameraPermission();
27
+ const device = useCameraDevice('back');
28
+ const cameraRef = useRef<Camera>(null);
38
29
 
39
- const initializeCamera = async () => {
40
- try {
41
- const { Camera, useCameraDevice, useCameraPermission } = require('react-native-vision-camera');
42
-
43
- const { hasPermission: hasPerm, requestPermission } = useCameraPermission();
44
-
45
- if (!hasPerm) {
46
- const granted = await requestPermission();
47
- if (!granted) {
48
- navigation.navigate('CameraPermission' as never);
49
- return;
50
- }
51
- }
52
-
53
- if (isMounted) {
54
- setHasPermission(true);
55
-
56
- const backDevice = useCameraDevice('back');
57
- if (!backDevice) {
58
- navigation.navigate('NoCameraFound' as never);
59
- return;
60
- }
61
-
62
- setDevice(backDevice);
63
- }
64
- } catch (error) {
65
- console.warn('Camera initialization failed:', error);
66
- if (isMounted) {
67
- simulateMRZScan();
68
- }
69
- }
70
- };
30
+ const [isCallingAPI, setIsCallingAPI] = useState(false);
31
+ const [autoScan, setAutoScan] = useState(true);
32
+ const scanIntervalRef = useRef<NodeJS.Timeout | null>(null);
33
+ const isProcessingRef = useRef(false);
34
+ const apiCalledRef = useRef(false);
71
35
 
72
- initializeCamera();
36
+ useEffect(() => {
37
+ if (!device) {
38
+ navigation.navigate('NoCameraFound' as never);
39
+ }
73
40
 
74
41
  return () => {
75
- isMounted = false;
76
- setIsScanning(false);
42
+ // Cleanup on unmount
43
+ if (scanIntervalRef.current) {
44
+ clearInterval(scanIntervalRef.current);
45
+ }
77
46
  };
78
- // eslint-disable-next-line react-hooks/exhaustive-deps
79
- }, []);
47
+ }, [device, navigation]);
80
48
 
81
- // Process MRZ with ML Kit Text Recognition
82
- const processMRZWithMLKit = async (frame: any) => {
83
- if (!isScanning) return;
49
+ // Normalize MRZ text - same as demo-testing-sdk
50
+ const normalizeMrz = (text: string) => {
51
+ return text
52
+ .replace(/[«]/g, '<')
53
+ .replace(/0/g, 'O')
54
+ .replace(/[^A-Z0-9<]/gi, '');
55
+ };
84
56
 
57
+ // Extract MRZ - improved to get only MRZ lines
58
+ const extractMRZ = async (imagePath: string): Promise<string | null> => {
85
59
  try {
86
- const { recognizeText } = require('@react-native-ml-kit/text-recognition');
87
-
88
- const result = await recognizeText(frame);
60
+ const result = await TextRecognition.recognize(imagePath);
89
61
 
90
- if (result && result.text) {
91
- // Check if text contains MRZ pattern
92
- const lines = result.text.split('\n');
93
- const mrzLines = lines.filter((line: string) =>
94
- line.length >= 30 && /^[A-Z0-9<]+$/.test(line)
95
- );
96
-
97
- if (mrzLines.length >= 2) {
98
- const mrzCode = mrzLines.join('\n');
99
- handleMRZScanned(mrzCode);
62
+ // Get all normalized lines
63
+ const allLines = result.blocks
64
+ .flatMap(block => block.lines)
65
+ .map(line => normalizeMrz(line.text))
66
+ .filter(text => text.length > 0);
67
+
68
+ // Filter only MRZ lines (30-44 characters, only A-Z, 0-9, <)
69
+ const mrzLines = allLines.filter(text => /^[A-Z0-9<]{30,44}$/.test(text));
70
+
71
+ console.log('All lines:', allLines.length);
72
+ console.log('MRZ lines found:', mrzLines.length);
73
+ mrzLines.forEach((line, idx) => {
74
+ console.log(`MRZ ${idx + 1}:`, line, `(${line.length} chars)`);
75
+ });
76
+
77
+ if (mrzLines.length === 0) {
78
+ return null;
79
+ }
80
+
81
+ // Determine document type and extract correct MRZ
82
+ let finalMRZ = '';
83
+
84
+ // TD1: 3 lines of 30 characters (ID cards) - Total: 90 characters
85
+ if (mrzLines.length >= 3) {
86
+ const td1Lines = mrzLines.filter(l => l.length === 30);
87
+ if (td1Lines.length >= 3) {
88
+ const combined = td1Lines.slice(0, 3).join('');
89
+ // Check if total matches: 30 + 30 + 30 = 90
90
+ if (combined.length === 90) {
91
+ finalMRZ = combined;
92
+ console.log('TD1 detected (3x30):', finalMRZ.length, 'chars - MATCH!');
93
+ return finalMRZ;
94
+ }
100
95
  }
101
96
  }
102
- } catch (error) {
103
- console.warn('ML Kit text recognition error:', error);
97
+
98
+ // TD3: 2 lines of 44 characters (Passports) - Total: 88 characters
99
+ if (mrzLines.length >= 2) {
100
+ const td3Lines = mrzLines.filter(l => l.length === 44);
101
+ if (td3Lines.length >= 2) {
102
+ const combined = td3Lines.slice(0, 2).join('');
103
+ // Check if total matches: 44 + 44 = 88
104
+ if (combined.length === 88) {
105
+ finalMRZ = combined;
106
+ console.log('TD3 detected (2x44):', finalMRZ.length, 'chars - MATCH!');
107
+ return finalMRZ;
108
+ }
109
+ }
110
+ }
111
+
112
+ // TD2: 2 lines of 36 characters - Total: 72 characters
113
+ if (mrzLines.length >= 2) {
114
+ const td2Lines = mrzLines.filter(l => l.length === 36);
115
+ if (td2Lines.length >= 2) {
116
+ const combined = td2Lines.slice(0, 2).join('');
117
+ // Check if total matches: 36 + 36 = 72
118
+ if (combined.length === 72) {
119
+ finalMRZ = combined;
120
+ console.log('TD2 detected (2x36):', finalMRZ.length, 'chars - MATCH!');
121
+ return finalMRZ;
122
+ }
123
+ }
124
+ }
125
+
126
+ // If character count doesn't match expected totals, return null to continue scanning
127
+ console.log('MRZ lines found but character count does not match expected format');
128
+ return null;
129
+ } catch (err) {
130
+ console.warn('Text recognition failed:', err);
131
+ return null;
104
132
  }
105
133
  };
106
134
 
107
- // Handle MRZ scan result
108
- const handleMRZScanned = async (mrzCode: string) => {
109
- if (loading || !mrzCode || !isScanning) return;
110
-
111
- setIsScanning(false);
112
- setScannedData(mrzCode);
135
+ // Take photo - same as demo-testing-sdk
136
+ const takePhoto = async () => {
137
+ if (!cameraRef.current || isProcessingRef.current || apiCalledRef.current) {
138
+ return;
139
+ }
113
140
 
114
141
  try {
115
- setLoading(true);
142
+ isProcessingRef.current = true;
116
143
 
117
- const mrzData = {
118
- token: String(idmConf.verificationCode || ''),
119
- code: mrzCode,
120
- engine: getMRZEngine(),
121
- };
144
+ const photo = await cameraRef.current.takePhoto({
145
+ flash: 'off',
146
+ enableShutterSound: false,
147
+ });
148
+
149
+ const mrzData = await extractMRZ('file://' + photo.path);
150
+
151
+ if (mrzData) {
152
+ // Stop auto-scanning immediately
153
+ setAutoScan(false);
154
+ if (scanIntervalRef.current) {
155
+ clearInterval(scanIntervalRef.current);
156
+ scanIntervalRef.current = null;
157
+ }
158
+
159
+ // Mark that API is being called
160
+ apiCalledRef.current = true;
161
+ setIsCallingAPI(true);
162
+
163
+ try {
164
+ const requestData = {
165
+ code: mrzData,
166
+ token: idmConf.verificationCode ?? '',
167
+ engine: 'idmerit',
168
+ };
122
169
 
123
- // Call API to submit MRZ
124
- const result = await captureMRZ(idmConf, mrzData);
125
-
126
- if (result.status) {
127
- // Update configuration with response
128
- setIDMConf({
129
- ...idmConf,
130
- requestConfiguration: { ...idmConf.requestConfiguration, ...result },
131
- });
132
-
133
- // Navigate to ThankYou
134
- navigation.navigate('ThankYou' as never);
135
- } else {
136
- Alert.alert('Error', result.errorMessage || 'Failed to process MRZ', [
137
- {
138
- text: 'Retry',
139
- onPress: () => {
140
- setIsScanning(true);
141
- setScannedData(null);
142
- setLoading(false);
143
- },
144
- },
145
- ]);
170
+ const response = await captureMRZ(idmConf, requestData);
171
+
172
+ console.log('API Response:', response);
173
+
174
+ // Check if response has a valid status field (string)
175
+ if (response && typeof response.status === 'string') {
176
+ console.log('Valid response with status:', response.status);
177
+ // Navigate to ThankYou only if we have a valid response
178
+ navigation.navigate('ThankYou' as never);
179
+ } else {
180
+ console.log('Invalid response - no status field, retrying...');
181
+ // Reset and allow retry if response is invalid
182
+ apiCalledRef.current = false;
183
+ setIsCallingAPI(false);
184
+ setAutoScan(true);
185
+ }
186
+ } catch (apiError) {
187
+ console.error('API call failed:', apiError);
188
+
189
+ // Reset on error to allow retry
190
+ apiCalledRef.current = false;
191
+ setIsCallingAPI(false);
192
+ setAutoScan(true);
193
+ }
194
+
195
+ console.log(mrzData);
146
196
  }
147
- } catch (error: any) {
148
- console.error('Error processing MRZ:', error);
149
- Alert.alert('Error', 'Failed to process MRZ', [
150
- {
151
- text: 'Retry',
152
- onPress: () => {
153
- setIsScanning(true);
154
- setScannedData(null);
155
- setLoading(false);
156
- },
157
- },
158
- ]);
197
+ } catch (err: any) {
198
+ console.error('MRZ scan error:', err);
199
+ } finally {
200
+ isProcessingRef.current = false;
159
201
  }
160
202
  };
161
203
 
162
- // Fallback simulation if ML Kit not available
163
- const simulateMRZScan = () => {
164
- const timer = setTimeout(() => {
165
- if (isScanning) {
166
- const simulatedMRZ =
167
- 'P<UTOERIKSSON<<ANNA<MARIA<<<<<<<<<<<<<<<<<<<<\nL898902C36UTO7408122F1204159ZE184226B<<<<<10';
168
- handleMRZScanned(simulatedMRZ);
169
- }
170
- }, 3000);
171
-
172
- return () => clearTimeout(timer);
173
- };
204
+ // Auto-scan effect
205
+ useEffect(() => {
206
+ if (device && autoScan && !isCallingAPI) {
207
+ // Start auto-scanning after 1 second delay
208
+ const startDelay = setTimeout(() => {
209
+ scanIntervalRef.current = setInterval(() => {
210
+ takePhoto();
211
+ }, 1000); // Scan every 1 second
212
+ }, 1000);
174
213
 
175
- // Render camera with frame processor
176
- const renderCamera = () => {
177
- try {
178
- const { Camera } = require('react-native-vision-camera');
179
-
180
- if (hasPermission && device) {
181
- return (
182
- <Camera
183
- ref={cameraRef}
184
- style={StyleSheet.absoluteFill}
185
- device={device}
186
- isActive={isScanning}
187
- frameProcessor={(frame) => {
188
- 'worklet';
189
- if (isScanning) {
190
- processMRZWithMLKit(frame);
191
- }
192
- }}
193
- />
194
- );
195
- }
196
- } catch (error) {
197
- console.warn('Camera render failed:', error);
214
+ return () => {
215
+ clearTimeout(startDelay);
216
+ if (scanIntervalRef.current) {
217
+ clearInterval(scanIntervalRef.current);
218
+ }
219
+ };
198
220
  }
221
+ }, [device, autoScan, isCallingAPI]);
199
222
 
200
- // Fallback UI
223
+ // Show permission request screen
224
+ if (!hasPermission) {
201
225
  return (
202
- <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
203
- <Text
204
- style={{
205
- color: '#fff',
206
- textAlign: 'center',
207
- marginTop: 200,
208
- fontSize: 16,
209
- }}
210
- >
211
- {loading ? 'Processing...' : `Scanning MRZ (${getMRZEngine()})...`}
212
- </Text>
213
- {scannedData && (
214
- <Text
215
- style={{
216
- color: '#0f0',
217
- textAlign: 'center',
218
- marginTop: 20,
219
- fontSize: 10,
220
- paddingHorizontal: 20,
221
- }}
222
- >
223
- Detected MRZ
226
+ <ScrollView contentContainerStyle={permissionStyles.container}>
227
+ <Header />
228
+
229
+ <ThemedText style={permissionStyles.maintitle} type="title">
230
+ Camera Access Required
231
+ </ThemedText>
232
+
233
+ <View style={{ padding: 20 }}>
234
+ <Text style={{ color: theme.colors.text, textAlign: 'center', marginBottom: 20 }}>
235
+ This app needs access to your camera to scan MRZ. Please grant camera permission.
224
236
  </Text>
225
- )}
226
- </View>
237
+ </View>
238
+
239
+ <Button
240
+ title="Grant Permission"
241
+ style={permissionStyles.buttonplace}
242
+ textStyle={permissionStyles.button}
243
+ onPress={async () => {
244
+ await requestPermission();
245
+ }}
246
+ />
247
+ </ScrollView>
227
248
  );
228
- };
249
+ }
250
+
251
+ if (!device) return null;
229
252
 
230
253
  return (
231
254
  <View style={styles.container}>
232
255
  <Text style={styles.topLabel}>Align the MRZ code in the box</Text>
233
-
234
- {renderCamera()}
235
-
236
- {/* MRZ overlay box */}
237
- <View
238
- style={{
239
- position: 'absolute',
240
- top: '40%',
241
- alignSelf: 'center',
242
- width: 380,
243
- height: 78,
244
- borderWidth: 2,
245
- borderColor: scannedData ? '#00ff00' : '#ffffff',
246
- backgroundColor: 'transparent',
247
- }}
256
+
257
+ <Camera
258
+ ref={cameraRef}
259
+ style={styles.camera}
260
+ device={device}
261
+ isActive={true}
262
+ photo
248
263
  />
249
-
250
- {loading && <Loader message="Processing MRZ..." />}
264
+
265
+ <Image
266
+ source={overlayImage}
267
+ style={styles.overlay}
268
+ resizeMode="stretch"
269
+ />
270
+
271
+ <Text style={styles.bottomLabel}>
272
+ {isCallingAPI ? 'Processing...' : 'Scanning...'}
273
+ </Text>
274
+
275
+ {isCallingAPI && <Loader />}
251
276
  </View>
252
277
  );
253
278
  }
279
+
280
+ export default MrzCapture;
@@ -1,5 +1,5 @@
1
1
  import { useNavigation } from '@react-navigation/native';
2
- import React, { useState, useMemo, useCallback, useEffect } from 'react';
2
+ import React, { useState, useEffect } from 'react';
3
3
  import {
4
4
  Alert,
5
5
  Pressable,
@@ -24,105 +24,92 @@ export default function SelectDocuments() {
24
24
  const orientation = useOrientation();
25
25
  const styles = getStyles(theme, orientation);
26
26
  const navigation = useNavigation();
27
-
27
+
28
28
  const [filteredCountries, setFilteredCountries] = useState<any[]>([]);
29
29
  const [search, setSearch] = useState('');
30
30
  const [selectedCountry, setSelectedCountry] = useState<string | null>(null);
31
31
  const [dropdownVisible, setDropdownVisible] = useState(false);
32
32
  const [selectedIdType, setSelectedIdType] = useState<string | null>(null);
33
33
  const [idTypeDropdownVisible, setIdTypeDropdownVisible] = useState(false);
34
+ const [idCardTypes, setIdCardTypes] = useState<any[]>([]);
34
35
 
35
- // Initialize filtered countries from context
36
- useEffect(() => {
37
- if (idmConf.countryDetails) {
38
- setFilteredCountries(idmConf.countryDetails);
39
- }
40
- }, [idmConf.countryDetails]);
41
-
42
- // Memoize ID card types based on selected country
43
- const idCardTypes = useMemo(() => {
44
- if (!selectedCountry || !idmConf.countryDetails) return [];
45
-
46
- const country = idmConf.countryDetails.find((c) => String(c.value) === String(selectedCountry));
47
- return country?.metadata.map((doc) => ({
48
- label: getDocumentLabel(doc.type),
49
- value: doc.type,
50
- metadata: doc,
51
- })) || [];
52
- }, [selectedCountry, idmConf.countryDetails]);
53
-
54
- // Memoize selected country label
55
- const selectedCountryLabel = useMemo(() => {
56
- if (!idmConf.countryDetails) return null;
57
- return idmConf.countryDetails.find((c) => String(c.value) === String(selectedCountry))?.label;
58
- }, [selectedCountry, idmConf.countryDetails]);
59
-
60
- // Memoize selected ID type label
61
- const selectedIdTypeLabel = useMemo(() => {
62
- return idCardTypes.find((i) => i.value === selectedIdType)?.label;
63
- }, [idCardTypes, selectedIdType]);
64
-
65
- const toggleDropdown = useCallback(() => {
36
+ const toggleDropdown = () => {
66
37
  setDropdownVisible((prev) => !prev);
67
38
  setIdTypeDropdownVisible(false);
68
- }, []);
39
+ };
69
40
 
70
- const toggleIdTypeDropdown = useCallback(() => {
41
+ const toggleIdTypeDropdown = () => {
71
42
  setIdTypeDropdownVisible((prev) => !prev);
72
43
  setDropdownVisible(false);
73
- }, []);
44
+ };
74
45
 
75
- const selectCountry = useCallback((value: string) => {
46
+ const selectCountry = (value: string) => {
76
47
  setSelectedCountry(value);
77
48
  setDropdownVisible(false);
78
49
  setSearch('');
79
- setSelectedIdType(null);
80
-
81
- if (idmConf.countryDetails) {
82
- setFilteredCountries(idmConf.countryDetails);
83
- }
84
50
 
85
- const selectedCountryData = idmConf.countryDetails?.find((c) => String(c.value) === String(value));
86
- updateIDMConf({ selectedCountryDetails: selectedCountryData || null });
87
- }, [idmConf.countryDetails, updateIDMConf]);
51
+ const selectedCountryData = idmConf.countryDetails?.find((c: any) => c.value === value);
52
+ updateIDMConf({
53
+ selectedCountryDetails: selectedCountryData || null,
54
+ });
88
55
 
89
- const selectIdType = useCallback((value: string) => {
56
+ if (selectedCountryData?.metadata) {
57
+ const types = selectedCountryData.metadata.map((doc: any) => ({
58
+ label: getDocumentLabel(doc.type),
59
+ value: doc.type,
60
+ }));
61
+ setIdCardTypes(types);
62
+ } else {
63
+ setIdCardTypes([]);
64
+ }
65
+ setSelectedIdType(null);
66
+ setFilteredCountries(idmConf.countryDetails || []);
67
+ };
68
+
69
+ const selectIdType = (value: string) => {
90
70
  setSelectedIdType(value);
91
-
92
- const selectedCountryData = idmConf.countryDetails?.find((c) => String(c.value) === String(selectedCountry));
93
- const selectedMetaData = selectedCountryData?.metadata?.find((c) => c.type === value);
94
-
95
71
  updateIDMConf({
96
72
  selectedCountryDetails: {
97
- ...selectedCountryData,
98
- selectedMetaData,
73
+ ...idmConf.selectedCountryDetails,
74
+ selectedMetaData: idmConf.selectedCountryDetails?.metadata?.find((c: any) => c.type === value),
99
75
  },
100
76
  });
101
77
  setIdTypeDropdownVisible(false);
102
- }, [selectedCountry, idmConf.countryDetails, updateIDMConf]);
78
+ };
103
79
 
104
- const handleContinue = useCallback(() => {
80
+ const handleContinue = () => {
105
81
  if (!selectedCountry || !selectedIdType) {
106
82
  Alert.alert('Missing Fields', 'Please select both country and ID card type.');
107
83
  return;
108
84
  }
109
- navigation.navigate('FrontDocumentAdvice' as never);
110
- }, [selectedCountry, selectedIdType, navigation]);
111
85
 
112
- const handleSearch = useCallback((text: string) => {
86
+ // Check document flow to determine initial navigation
87
+ const metaData = idmConf?.selectedCountryDetails?.selectedMetaData;
88
+ const flow = metaData?.document_flow;
89
+
90
+ if (flow && flow[0] === 'B') {
91
+ navigation.navigate('BackDocumentAdvice' as never);
92
+ } else {
93
+ navigation.navigate('FrontDocumentAdvice' as never);
94
+ }
95
+ };
96
+
97
+ useEffect(() => {
98
+ setFilteredCountries(idmConf.countryDetails || []);
99
+ }, [idmConf.countryDetails]);
100
+
101
+ const handleSearch = (text: string) => {
113
102
  setSearch(text);
114
- if (!idmConf.countryDetails) return;
115
-
116
- const filtered = idmConf.countryDetails.filter((c) =>
103
+ const filtered = (idmConf.countryDetails || []).filter((c: any) =>
117
104
  c.label.toLowerCase().includes(text.toLowerCase())
118
105
  );
119
106
  setFilteredCountries(filtered);
120
- }, [idmConf.countryDetails]);
107
+ };
121
108
 
122
- const closeDropdowns = useCallback(() => {
109
+ const closeDropdowns = () => {
123
110
  setDropdownVisible(false);
124
111
  setIdTypeDropdownVisible(false);
125
- }, []);
112
+ };
126
113
 
127
114
  return (
128
115
  <View style={{ flex: 1, backgroundColor: theme.colors.background }}>
@@ -143,7 +130,9 @@ export default function SelectDocuments() {
143
130
  color: selectedCountry ? theme.colors.text : theme.colors.subtitle,
144
131
  }}
145
132
  >
146
- {selectedCountryLabel || 'Select a country'}
133
+ {selectedCountry
134
+ ? idmConf.countryDetails?.find((c: any) => c.value === selectedCountry)?.label
135
+ : 'Select a country'}
147
136
  </Text>
148
137
  <Text style={styles.downArrow}>▼</Text>
149
138
  </Pressable>
@@ -160,7 +149,7 @@ export default function SelectDocuments() {
160
149
  {filteredCountries.map((item) => (
161
150
  <Pressable
162
151
  key={item.value}
163
- onPress={() => selectCountry(String(item.value))}
152
+ onPress={() => selectCountry(item.value)}
164
153
  style={styles.dropdownItem}
165
154
  >
166
155
  <Text style={styles.dopDownLable}>{item.label}</Text>
@@ -183,7 +172,9 @@ export default function SelectDocuments() {
183
172
  color: selectedIdType ? theme.colors.text : theme.colors.subtitle,
184
173
  }}
185
174
  >
186
- {selectedIdTypeLabel || 'Select ID Card Type'}
175
+ {selectedIdType
176
+ ? idCardTypes.find((i) => i.value === selectedIdType)?.label
177
+ : 'Select ID Card Type'}
187
178
  </Text>
188
179
  <Text style={styles.downArrow}>▼</Text>
189
180
  </Pressable>