@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,184 +1,217 @@
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 getStyles from '../styles/DocumentCaptureStyles';
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/DocumentCaptureFrontStyle';
5
9
  import { useTheme } from '../context/ThemeContext';
6
- import { useOrientation } from '../hooks/useOrientation';
7
10
  import { useIDM } from '../context/IDMConfigurationContext';
8
- import Loader from '../components/common/Loader';
11
+ import { useOrientation } from '../hooks/useOrientation';
9
12
  import { captureDocumentFront } from '../apis';
10
- import { processImageToBase64, validateImagePath } from '../utils/imageProcessor';
11
- import { createFlowManager } from '../utils/flowManager';
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 DocumentCaptureFront() {
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 [loading, setLoading] = useState(false);
20
- const [hasPermission, setHasPermission] = useState(false);
21
- const [device, setDevice] = useState<any>(null);
22
- const cameraRef = useRef<any>(null);
30
+ const { failedCount, setFailedCount } = useFailedCount();
23
31
 
24
- // Initialize camera
25
- useEffect(() => {
26
- const initializeCamera = async () => {
27
- try {
28
- // Try to import camera libraries
29
- const { Camera, useCameraDevice, useCameraPermission } = require('react-native-vision-camera');
30
-
31
- // Check permission
32
- const { hasPermission: hasPerm, requestPermission } = useCameraPermission();
33
-
34
- if (!hasPerm) {
35
- const granted = await requestPermission();
36
- if (!granted) {
37
- navigation.navigate('CameraPermission' as never);
38
- return;
39
- }
40
- }
41
-
42
- setHasPermission(true);
43
-
44
- // Get back camera device
45
- const backDevice = useCameraDevice('back');
46
- if (!backDevice) {
47
- navigation.navigate('NoCameraFound' as never);
48
- return;
49
- }
50
-
51
- setDevice(backDevice);
52
- } catch (error) {
53
- console.warn('Camera initialization failed:', error);
54
- // Fallback for testing without camera
55
- setHasPermission(true);
56
- }
57
- };
58
-
59
- initializeCamera();
60
- }, [navigation]);
32
+ const [loading, setLoading] = useState(false);
61
33
 
62
- // Determine next navigation step based on metadata
34
+ // Decide next navigation step
63
35
  const handleNextStep = () => {
36
+ setFailedCount(0); // Reset failed count on successful capture
64
37
  const metaData = idmConf?.selectedCountryDetails?.selectedMetaData;
65
-
66
38
  if (!metaData) {
67
39
  navigation.navigate('ThankYou' as never);
68
40
  return;
69
41
  }
70
42
 
71
- const flowManager = createFlowManager(metaData);
72
- const nextScreen = flowManager.getNextScreenAfterFront();
73
- navigation.navigate(nextScreen as never);
43
+ const { document_flow, barcode } = metaData;
44
+
45
+ // If no flow, just go to ThankYou
46
+ if (!document_flow || document_flow.length === 0) {
47
+ navigation.navigate('ThankYou' as never);
48
+ return;
49
+ }
50
+
51
+ // Decide based on first element in flow
52
+ const currentStep = document_flow[0];
53
+
54
+ if (currentStep === 'F') {
55
+ if (document_flow.includes('B')) {
56
+ navigation.navigate('BackDocumentAdvice' as never);
57
+ return;
58
+ }
59
+ }
60
+
61
+ // If no more flow, handle barcode
62
+ if (barcode === 'PDF417 B' || barcode === 'PDF417F') {
63
+ navigation.navigate('BarcodeAdvice' as never);
64
+ } else if (['TD3', 'TD2', 'TD1'].includes(barcode)) {
65
+ navigation.navigate('MrzAdvice' as never);
66
+ } else {
67
+ navigation.navigate('ThankYou' as never);
68
+ }
74
69
  };
75
70
 
71
+ // Check for device availability
72
+ useEffect(() => {
73
+ if (!device) {
74
+ navigation.navigate('NoCameraFound' as never);
75
+ }
76
+ }, [device, navigation]);
77
+
78
+ // Show permission request screen if camera permission is not granted
79
+ if (!hasPermission) {
80
+ return (
81
+ <ScrollView contentContainerStyle={permissionStyles.container}>
82
+ <Header />
83
+
84
+ <ThemedText style={permissionStyles.maintitle} type="title">
85
+ Camera Access Required
86
+ </ThemedText>
87
+
88
+ <View style={{ padding: 20 }}>
89
+ <Text style={{ color: theme.colors.text, textAlign: 'center', marginBottom: 20 }}>
90
+ This app needs access to your camera to capture documents. Please grant camera permission.
91
+ </Text>
92
+ </View>
93
+
94
+ <Button
95
+ title="Grant Permission"
96
+ style={permissionStyles.buttonplace}
97
+ textStyle={permissionStyles.button}
98
+ onPress={async () => {
99
+ const granted = await requestPermission();
100
+ if (!granted) {
101
+ console.log('Camera permission denied');
102
+ }
103
+ }}
104
+ />
105
+ </ScrollView>
106
+ );
107
+ }
108
+
109
+ if (!device) return null;
110
+
111
+ // Take photo and process
76
112
  const takePhoto = async () => {
77
- if (!cameraRef.current) {
78
- // Fallback for testing without camera
79
- Alert.alert('Camera Not Available', 'Camera is not available in this environment.');
113
+ if (!cameraRef.current || loading) {
114
+ return;
115
+ }
116
+
117
+ // Validate required data before taking photo
118
+ if (!idmConf?.selectedCountryDetails?.selectedMetaData) {
119
+ Alert.alert('Error', 'Please select a document type before capturing.');
80
120
  return;
81
121
  }
82
122
 
83
123
  try {
84
124
  setLoading(true);
85
-
86
125
  const photo = await cameraRef.current.takePhoto();
87
126
 
88
- if (!validateImagePath(photo?.path)) {
89
- Alert.alert('Error', 'Failed to capture image');
90
- setLoading(false);
127
+ if (!photo?.path) {
128
+ console.error('No photo path available');
91
129
  return;
92
130
  }
93
131
 
94
- // Process image to base64
95
- const base64Image = await processImageToBase64(photo.path);
132
+ let base64Image;
133
+
134
+ try {
135
+ const resizedImage = await ImageResizer.createResizedImage(
136
+ `file://${photo.path}`,
137
+ 810,
138
+ 1080,
139
+ 'JPEG',
140
+ 70,
141
+ 0,
142
+ undefined,
143
+ false
144
+ );
145
+ base64Image = await RNFS.readFile(resizedImage.uri, 'base64');
146
+ } catch (resizeError: any) {
147
+ console.warn('Image resizing failed, using original image:', resizeError.message);
148
+ base64Image = await RNFS.readFile(photo.path, 'base64');
149
+ }
96
150
 
97
- // Prepare photo data for API
98
151
  const photoData = {
99
- latitude: String(idmConf.userDetails?.location?.coords?.latitude || ''),
100
- longitude: String(idmConf.userDetails?.location?.coords?.longitude || ''),
101
- token: String(idmConf.verificationCode || ''),
102
- persistLoc: 'false',
103
- metadataIndex: String(idmConf.selectedCountryDetails?.selectedMetaData?.id || ''),
152
+ latitude: String(idmConf?.userDetails?.location?.coords?.latitude || ''),
153
+ longitude: String(idmConf?.userDetails?.location?.coords?.longitude || ''),
154
+ token: String(idmConf?.verificationCode || ''),
155
+ persistLoc: String(false),
156
+ metadataIndex: String(idmConf?.selectedCountryDetails?.selectedMetaData?.id || ''),
104
157
  file: base64Image,
105
158
  };
106
159
 
107
- // Call API to capture document front
108
160
  const result = await captureDocumentFront(idmConf, photoData);
109
161
 
110
- if (result.status) {
111
- // Update configuration with response
162
+ if (typeof result.status === 'string') {
112
163
  setIDMConf({
113
164
  ...idmConf,
114
165
  requestConfiguration: result,
115
166
  });
116
-
117
- // Navigate to next step based on metadata
118
167
  handleNextStep();
119
168
  } else {
120
- // Handle error - navigate to retake screen
121
- navigation.navigate('RetakeSelfie' as never, {
122
- errorMessage: result.errorMessage || 'Failed to process document',
123
- });
169
+ // Failed document capture
170
+ setFailedCount((prev) => prev + 1);
171
+ if (failedCount >= 4) {
172
+ // After 5 failed attempts (0-4), reset and proceed to ThankYou
173
+ setFailedCount(0);
174
+ navigation.navigate('ThankYou' as never);
175
+ } else {
176
+ (navigation.navigate as any)('RetakeSelfie', { errorMessage: result.errorMessage });
177
+ }
124
178
  }
125
179
  } catch (error: any) {
126
- console.error('Error taking photo:', error);
180
+ console.error('Error taking photo:', error.message);
127
181
  Alert.alert('Error', 'Failed to capture or process the image.');
128
182
  } finally {
129
183
  setLoading(false);
130
184
  }
131
185
  };
132
186
 
133
- // Render camera if available
134
- const renderCamera = () => {
135
- try {
136
- const { Camera } = require('react-native-vision-camera');
137
-
138
- if (hasPermission && device) {
139
- return (
140
- <Camera
141
- ref={cameraRef}
142
- style={StyleSheet.absoluteFill}
143
- device={device}
144
- isActive={true}
145
- photo={true}
146
- />
147
- );
148
- }
149
- } catch (error) {
150
- console.warn('Camera render failed:', error);
151
- }
152
-
153
- // Fallback placeholder
154
- return (
155
- <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
156
- <Text style={{ color: '#fff', textAlign: 'center', marginTop: 100 }}>
157
- Camera Preview
158
- </Text>
159
- </View>
160
- );
161
- };
162
-
163
187
  return (
164
188
  <View style={styles.container}>
165
189
  <Text style={styles.topLabel}>Document Front Side</Text>
166
190
 
167
- {renderCamera()}
191
+ {device && (
192
+ <Camera
193
+ ref={cameraRef}
194
+ style={StyleSheet.absoluteFill}
195
+ device={device}
196
+ isActive={true}
197
+ photo={true}
198
+ />
199
+ )}
168
200
 
169
- {/* Card overlay */}
170
201
  <Image
171
202
  source={require('../../assets/images/card-overlay.png')}
172
203
  style={styles.cardoverlay}
173
204
  resizeMode="cover"
174
205
  />
175
206
 
176
- <Text style={styles.bottomLabel}>
177
- Position your document fully within the frame
178
- </Text>
207
+ <Text style={styles.bottomLabel}>Position your document fully within the frame</Text>
179
208
 
180
209
  <View style={styles.buttonplace}>
181
- <TouchableOpacity style={styles.captureButton} onPress={takePhoto} />
210
+ <TouchableOpacity
211
+ style={styles.captureButton}
212
+ onPress={takePhoto}
213
+ disabled={loading}
214
+ />
182
215
  </View>
183
216
 
184
217
  {loading && <Loader />}
@@ -1,21 +1,21 @@
1
1
  import { useNavigation } from '@react-navigation/native';
2
- import { Image, ScrollView, View } from 'react-native';
2
+ import { Image, ScrollView } from 'react-native';
3
3
  import Button from '../components/ui/Button';
4
4
  import ThemedText from '../components/ui/ThemedText';
5
5
  import getStyles from '../styles/DocumentAdviceStyles';
6
6
  import { useTheme } from '../context/ThemeContext';
7
7
  import { useOrientation } from '../hooks/useOrientation';
8
+ import { useIDM } from '../context/IDMConfigurationContext';
8
9
  import { useMemo } from 'react';
10
+ import { getCardScanImage } from '../utils/imagesHelper';
9
11
 
10
12
  export default function FrontDocumentAdvice() {
11
13
  const { theme } = useTheme();
14
+ const { idmConf } = useIDM();
12
15
  const orientation = useOrientation();
13
16
  const navigation = useNavigation();
14
17
 
15
- const styles = useMemo(
16
- () => getStyles(theme, orientation),
17
- [theme, orientation]
18
- );
18
+ const styles = useMemo(() => getStyles(theme, orientation), [theme, orientation]);
19
19
 
20
20
  const handleRedirect = () => {
21
21
  navigation.navigate('DocumentCaptureFront' as never);
@@ -27,19 +27,14 @@ export default function FrontDocumentAdvice() {
27
27
  Capture Your ID Front
28
28
  </ThemedText>
29
29
 
30
- {/* Placeholder for ID card image */}
31
- <View
32
- style={[
33
- styles.idcard,
34
- {
35
- backgroundColor: '#f0f0f0',
36
- justifyContent: 'center',
37
- alignItems: 'center',
38
- },
39
- ]}
40
- >
41
- <ThemedText>ID Front Image</ThemedText>
42
- </View>
30
+ <Image
31
+ source={getCardScanImage(
32
+ idmConf?.selectedCountryDetails?.selectedMetaData?.type,
33
+ idmConf?.selectedCountryDetails?.selectedMetaData?.countryCode
34
+ )}
35
+ style={styles.idcard}
36
+ resizeMode="contain"
37
+ />
43
38
 
44
39
  <Button
45
40
  title="Take a photo"
@@ -1,6 +1,14 @@
1
1
  import { useNavigation } from '@react-navigation/native';
2
2
  import * as React from 'react';
3
- import { ScrollView, Text, View, PermissionsAndroid, Platform } from 'react-native';
3
+ import {
4
+ ScrollView,
5
+ Text,
6
+ View,
7
+ PermissionsAndroid,
8
+ Platform,
9
+ Linking,
10
+ Alert
11
+ } from 'react-native';
4
12
  import Button from '../components/ui/Button';
5
13
  import ThemedText from '../components/ui/ThemedText';
6
14
  import { useTheme } from '../context/ThemeContext';
@@ -12,35 +20,133 @@ export default function LocationPermission() {
12
20
  const orientation = useOrientation();
13
21
  const styles = getStyles(theme, orientation);
14
22
  const navigation = useNavigation();
23
+ const [isRequesting, setIsRequesting] = React.useState(false);
24
+
25
+ const navigateNext = () => {
26
+ if (navigation.canGoBack()) {
27
+ navigation.goBack();
28
+ } else {
29
+ navigation.navigate('VerifyIdentity' as never);
30
+ }
31
+ };
15
32
 
16
33
  const requestLocationPermission = async () => {
17
- if (Platform.OS === 'android') {
18
- try {
34
+ if (isRequesting) {
35
+ return;
36
+ }
37
+
38
+ setIsRequesting(true);
39
+
40
+ try {
41
+ if (Platform.OS === 'android') {
42
+ // Check if permission is already granted
43
+ const hasPermission = await PermissionsAndroid.check(
44
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
45
+ );
46
+
47
+ if (hasPermission) {
48
+ console.log('Location permission already granted');
49
+ navigateNext();
50
+ return;
51
+ }
52
+
53
+ // Request permission
19
54
  const granted = await PermissionsAndroid.request(
20
55
  PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
21
56
  {
22
57
  title: 'Location Permission',
23
- message: 'This app needs access to your location.',
24
- buttonPositive: 'OK',
58
+ message: 'This app needs access to your location to verify your identity.',
59
+ buttonPositive: 'Allow',
60
+ buttonNegative: 'Deny',
25
61
  }
26
62
  );
63
+
27
64
  if (granted === PermissionsAndroid.RESULTS.GRANTED) {
28
- if (navigation.canGoBack()) {
29
- navigation.goBack();
30
- } else {
31
- navigation.navigate('VerifyIdentity' as never);
65
+ console.log('Location permission granted');
66
+ // Try to get location using Geolocation service
67
+ try {
68
+ const Geolocation = require('react-native-geolocation-service');
69
+ if (Geolocation && typeof Geolocation.getCurrentPosition === 'function') {
70
+ Geolocation.getCurrentPosition(
71
+ (position: any) => {
72
+ console.log('Location obtained:', position);
73
+ navigateNext();
74
+ },
75
+ (error: any) => {
76
+ console.warn('Location error:', error);
77
+ navigateNext();
78
+ },
79
+ {
80
+ enableHighAccuracy: true,
81
+ timeout: 15000,
82
+ maximumAge: 10000,
83
+ }
84
+ );
85
+ } else {
86
+ navigateNext();
87
+ }
88
+ } catch (geoError) {
89
+ console.warn('Geolocation service error:', geoError);
90
+ navigateNext();
32
91
  }
92
+ } else if (granted === PermissionsAndroid.RESULTS.DENIED) {
93
+ console.log('Location permission denied');
94
+ Alert.alert(
95
+ 'Permission Denied',
96
+ 'Location permission is required to continue. Please grant permission.',
97
+ [{ text: 'OK' }]
98
+ );
99
+ } else if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
100
+ console.log('Location permission denied permanently');
101
+ Alert.alert(
102
+ 'Permission Required',
103
+ 'Location permission is required. Please enable it in app settings.',
104
+ [
105
+ { text: 'Cancel', style: 'cancel' },
106
+ {
107
+ text: 'Open Settings',
108
+ onPress: () => Linking.openSettings(),
109
+ },
110
+ ]
111
+ );
33
112
  }
34
- } catch (err) {
35
- console.warn(err);
36
- }
37
- } else {
38
- // For iOS, navigate directly (implement iOS permission separately)
39
- if (navigation.canGoBack()) {
40
- navigation.goBack();
41
113
  } else {
42
- navigation.navigate('VerifyIdentity' as never);
114
+ // For iOS, request location permission through Geolocation
115
+ try {
116
+ const Geolocation = require('react-native-geolocation-service');
117
+ if (Geolocation && typeof Geolocation.requestAuthorization === 'function') {
118
+ const auth = await Geolocation.requestAuthorization('whenInUse');
119
+ if (auth === 'granted') {
120
+ console.log('iOS location permission granted');
121
+ navigateNext();
122
+ } else {
123
+ console.log('iOS location permission denied:', auth);
124
+ Alert.alert(
125
+ 'Permission Required',
126
+ 'Location permission is required. Please enable it in app settings.',
127
+ [
128
+ { text: 'Cancel', style: 'cancel' },
129
+ {
130
+ text: 'Open Settings',
131
+ onPress: () => Linking.openSettings(),
132
+ },
133
+ ]
134
+ );
135
+ }
136
+ } else {
137
+ // Fallback for iOS
138
+ navigateNext();
139
+ }
140
+ } catch (err) {
141
+ console.warn('iOS location permission error:', err);
142
+ Alert.alert('Error', 'Failed to request location permission. Please try again.');
143
+ }
43
144
  }
145
+ } catch (err: any) {
146
+ console.error('Permission request error:', err);
147
+ Alert.alert('Error', 'Failed to request location permission. Please try again.');
148
+ } finally {
149
+ setIsRequesting(false);
44
150
  }
45
151
  };
46
152
 
@@ -68,6 +174,7 @@ export default function LocationPermission() {
68
174
  style={styles.buttonplace}
69
175
  textStyle={styles.button}
70
176
  onPress={requestLocationPermission}
177
+ disabled={isRequesting}
71
178
  />
72
179
  </ScrollView>
73
180
  );
@@ -1,22 +1,36 @@
1
1
  import { useNavigation } from '@react-navigation/native';
2
- import { ScrollView, View } from 'react-native';
2
+ import { ScrollView, View, Image } from 'react-native';
3
3
  import Button from '../components/ui/Button';
4
4
  import ThemedText from '../components/ui/ThemedText';
5
5
  import getStyles from '../styles/BarcodeAdviceStyles';
6
6
  import { useTheme } from '../context/ThemeContext';
7
7
  import { useOrientation } from '../hooks/useOrientation';
8
+ import { useIDM } from '../context/IDMConfigurationContext';
9
+ import { getMRZImage } from '../utils/imagesHelper';
8
10
  import { useMemo } from 'react';
9
11
 
10
12
  export default function MrzAdvice() {
11
13
  const { theme } = useTheme();
12
14
  const orientation = useOrientation();
13
15
  const navigation = useNavigation();
16
+ const { idmConf } = useIDM();
14
17
 
15
18
  const styles = useMemo(
16
19
  () => getStyles(theme, orientation),
17
20
  [theme, orientation]
18
21
  );
19
22
 
23
+ const mrzImage = useMemo(() => {
24
+ const metadata = idmConf?.selectedCountryDetails?.selectedMetaData;
25
+ if (!metadata) return null;
26
+
27
+ return getMRZImage(
28
+ metadata.type || '',
29
+ metadata.countryCode || '',
30
+ metadata.barcode || ''
31
+ );
32
+ }, [idmConf]);
33
+
20
34
  const handleRedirect = () => {
21
35
  navigation.navigate('MrzCapture' as never);
22
36
  };
@@ -27,20 +41,20 @@ export default function MrzAdvice() {
27
41
  Scan the MRZ of your ID
28
42
  </ThemedText>
29
43
 
30
- {/* Placeholder for MRZ image */}
31
- <View
32
- style={[
33
- styles.idcard,
34
- {
35
- backgroundColor: '#f0f0f0',
36
- justifyContent: 'center',
37
- alignItems: 'center',
38
- },
39
- ]}
40
- >
41
- <ThemedText>MRZ Zone Image</ThemedText>
44
+ <View style={styles.idcard}>
45
+ {mrzImage && (
46
+ <Image
47
+ source={mrzImage}
48
+ style={{ width: '100%', height: '100%' }}
49
+ resizeMode="contain"
50
+ />
51
+ )}
42
52
  </View>
43
53
 
54
+ <ThemedText style={[styles.maintitle, { fontSize: 14, marginTop: 20 }]}>
55
+ Position the MRZ zone (bottom lines) within the frame
56
+ </ThemedText>
57
+
44
58
  <Button
45
59
  title="Scan"
46
60
  style={styles.buttonplace}