@test-web/react-native-sdk 1.0.1 → 2.0.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 (125) hide show
  1. package/README.md +824 -26
  2. package/assets/images/Chrome-logo.svg +1 -0
  3. package/assets/images/Firefox-logo.png +0 -0
  4. package/assets/images/IDM-logo.jpg +0 -0
  5. package/assets/images/MRZOverlay.png +0 -0
  6. package/assets/images/Safari-logo.png +0 -0
  7. package/assets/images/aadhar.png +0 -0
  8. package/assets/images/camera-bg.png +0 -0
  9. package/assets/images/card-overlay-back.png +0 -0
  10. package/assets/images/card-overlay.png +0 -0
  11. package/assets/images/card-scan-back-icon.jpg +0 -0
  12. package/assets/images/card-scan-front-icon.png +0 -0
  13. package/assets/images/card-scan-icon-aadhaar-1.png +0 -0
  14. package/assets/images/card-scan-icon-aadhaar-back.png +0 -0
  15. package/assets/images/card-scan-icon-aadhaar-scan-qr.png +0 -0
  16. package/assets/images/card-scan-icon-aadhaar.png +0 -0
  17. package/assets/images/card-scan-icon-can-pr.png +0 -0
  18. package/assets/images/card-scan-icon-default-back.png +0 -0
  19. package/assets/images/card-scan-icon-dl.png +0 -0
  20. package/assets/images/card-scan-icon-greencard-back.jpg +0 -0
  21. package/assets/images/card-scan-icon-greencard.jpg +0 -0
  22. package/assets/images/card-scan-icon-hc.png +0 -0
  23. package/assets/images/card-scan-icon-ni-argentina-back.jpg +0 -0
  24. package/assets/images/card-scan-icon-ni-argentina-old.png +0 -0
  25. package/assets/images/card-scan-icon-ni-argentina.jpg +0 -0
  26. package/assets/images/card-scan-icon-ni-barcode.jpg +0 -0
  27. package/assets/images/card-scan-icon-ni-brazil-back.jpg +0 -0
  28. package/assets/images/card-scan-icon-ni-brazil.jpg +0 -0
  29. package/assets/images/card-scan-icon-ni-dominican-republic-back.png +0 -0
  30. package/assets/images/card-scan-icon-ni-dominican-republic-front.png +0 -0
  31. package/assets/images/card-scan-icon-ni-dominican-republic-mrz.png +0 -0
  32. package/assets/images/card-scan-icon-ni-dominicanaRepublic-back.jpg +0 -0
  33. package/assets/images/card-scan-icon-ni-france-back.png +0 -0
  34. package/assets/images/card-scan-icon-ni-france-front.png +0 -0
  35. package/assets/images/card-scan-icon-ni-france-scan-mrz.png +0 -0
  36. package/assets/images/card-scan-icon-ni-germany-back.jpg +0 -0
  37. package/assets/images/card-scan-icon-ni-germany.jpg +0 -0
  38. package/assets/images/card-scan-icon-ni-paraguay-back.png +0 -0
  39. package/assets/images/card-scan-icon-ni-paraguay-front.png +0 -0
  40. package/assets/images/card-scan-icon-ni-paraguay-scan-mrz.png +0 -0
  41. package/assets/images/card-scan-icon-ni-uae-back.png +0 -0
  42. package/assets/images/card-scan-icon-ni-uae-front.png +0 -0
  43. package/assets/images/card-scan-icon-ni-uae-scan-mrz.png +0 -0
  44. package/assets/images/card-scan-icon-ni-uganda-front.png +0 -0
  45. package/assets/images/card-scan-icon-ni-uganda-scan-mrz.png +0 -0
  46. package/assets/images/card-scan-icon-ni-ukrain-back.png +0 -0
  47. package/assets/images/card-scan-icon-ni-ukrain-front.png +0 -0
  48. package/assets/images/card-scan-icon-ni-ukrain-scan-mrz.png +0 -0
  49. package/assets/images/card-scan-icon-ni.png +0 -0
  50. package/assets/images/card-scan-icon-old.jpg +0 -0
  51. package/assets/images/card-scan-icon-pan.png +0 -0
  52. package/assets/images/card-scan-icon-passport-card-back.jpg +0 -0
  53. package/assets/images/card-scan-icon-passport-card.jpg +0 -0
  54. package/assets/images/card-scan-icon-passport-old.png +0 -0
  55. package/assets/images/card-scan-icon-passport.png +0 -0
  56. package/assets/images/card-scan-icon-pr.png +0 -0
  57. package/assets/images/card-scan-icon.jpg +0 -0
  58. package/assets/images/check.png +0 -0
  59. package/assets/images/chrome-animation-GPS-permissions-setting.gif +0 -0
  60. package/assets/images/chrome-animation-camera-permissions-setting.gif +0 -0
  61. package/assets/images/denied.png +0 -0
  62. package/assets/images/dl.png +0 -0
  63. package/assets/images/driver-license.png +0 -0
  64. package/assets/images/firefox-animation-permissions-setting.gif +0 -0
  65. package/assets/images/flashlight_on.png +0 -0
  66. package/assets/images/gallery.png +0 -0
  67. package/assets/images/greencard.png +0 -0
  68. package/assets/images/header.jpg +0 -0
  69. package/assets/images/health-card.png +0 -0
  70. package/assets/images/ic_camera_front_white_36px.svg +4 -0
  71. package/assets/images/ic_camera_rear_white_36px.svg +4 -0
  72. package/assets/images/ic_fullscreen_exit_white_48px.svg +4 -0
  73. package/assets/images/ic_fullscreen_white_48px.svg +4 -0
  74. package/assets/images/ic_photo_camera_white_48px.svg +5 -0
  75. package/assets/images/id-card.png +0 -0
  76. package/assets/images/idcardimg.png +0 -0
  77. package/assets/images/idmval-barcode.png +0 -0
  78. package/assets/images/information.png +0 -0
  79. package/assets/images/loader.gif +0 -0
  80. package/assets/images/loading.svg +1 -0
  81. package/assets/images/logo.jpg +0 -0
  82. package/assets/images/logo.png +0 -0
  83. package/assets/images/mrz-back.png +0 -0
  84. package/assets/images/mrz-ni.png +0 -0
  85. package/assets/images/mrz.png +0 -0
  86. package/assets/images/mrz1.png +0 -0
  87. package/assets/images/mrz_old.png +0 -0
  88. package/assets/images/mrz_small.png +0 -0
  89. package/assets/images/national-id.png +0 -0
  90. package/assets/images/nationalID.png +0 -0
  91. package/assets/images/no-wifi.png +0 -0
  92. package/assets/images/passport-card.png +0 -0
  93. package/assets/images/passport.png +0 -0
  94. package/assets/images/permit-card.png +0 -0
  95. package/assets/images/photo-overlay.png +0 -0
  96. package/assets/images/placeholder.jpg +0 -0
  97. package/assets/images/qr-code.png +0 -0
  98. package/assets/images/right-checkmark.jpg +0 -0
  99. package/assets/images/selfie.jpg +0 -0
  100. package/assets/images/showing-sec.png +0 -0
  101. package/assets/images/spinner.gif +0 -0
  102. package/assets/images/splash-icon.png +0 -0
  103. package/assets/images/take-selfie.jpg +0 -0
  104. package/assets/images/torch_off.png +0 -0
  105. package/assets/images/warning-icon.jpg +0 -0
  106. package/assets/images/warning-stick.jpg +0 -0
  107. package/assets/images/wrong-checkmark.jpg +0 -0
  108. package/package.json +40 -4
  109. package/src/apis/index.ts +338 -17
  110. package/src/components/common/Loader.tsx +16 -2
  111. package/src/config/apiConfig.ts +6 -0
  112. package/src/index.tsx +123 -7
  113. package/src/screens/BarcodeCapture.tsx +154 -24
  114. package/src/screens/DocumentCaptureBack.tsx +133 -24
  115. package/src/screens/DocumentCaptureFront.tsx +146 -24
  116. package/src/screens/MrzCapture.tsx +205 -16
  117. package/src/screens/SelectDocuments.tsx +37 -56
  118. package/src/screens/SelfieCapture.tsx +114 -18
  119. package/src/screens/ThankYou.tsx +34 -1
  120. package/src/services/getUserData.ts +111 -0
  121. package/src/types/IDMConf.ts +81 -7
  122. package/src/utils/base64.ts +25 -0
  123. package/src/utils/flowManager.ts +138 -0
  124. package/src/utils/imageProcessor.ts +96 -0
  125. package/src/utils/index.ts +18 -0
@@ -1,30 +1,172 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, StyleSheet } from 'react-native';
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import { View, Text, StyleSheet, Alert } from 'react-native';
3
3
  import { useNavigation } from '@react-navigation/native';
4
4
  import { useTheme } from '../context/ThemeContext';
5
5
  import { useOrientation } from '../hooks/useOrientation';
6
+ import { useIDM } from '../context/IDMConfigurationContext';
6
7
  import getStyles from '../styles/ScannerStyles';
7
8
  import Loader from '../components/common/Loader';
9
+ import { captureBarcode } from '../apis';
8
10
 
9
11
  export default function BarcodeCapture() {
10
12
  const { theme } = useTheme();
13
+ const { idmConf, setIDMConf } = useIDM();
11
14
  const orientation = useOrientation();
12
15
  const styles = getStyles(theme, orientation);
13
16
  const navigation = useNavigation();
14
17
  const [loading, setLoading] = useState(false);
18
+ const [scannedData, setScannedData] = useState<string | null>(null);
19
+ const [isScanning, setIsScanning] = useState(true);
20
+ const scannerRef = useRef<any>(null);
15
21
 
16
- // Static implementation - simulate barcode scanning
17
- React.useEffect(() => {
18
- const timer = setTimeout(() => {
22
+ // Initialize Dynamsoft Barcode Scanner
23
+ useEffect(() => {
24
+ let isMounted = true;
25
+
26
+ const initializeScanner = async () => {
27
+ try {
28
+ // Try to import Dynamsoft Capture Vision
29
+ const { DCVCameraView, DBRRuntimeSettings } = require('dynamsoft-capture-vision-react-native');
30
+
31
+ // License initialization would go here
32
+ // await DCVBarcodeReader.initLicense('YOUR_LICENSE_KEY');
33
+
34
+ console.log('Dynamsoft Barcode Scanner initialized');
35
+ } catch (error) {
36
+ console.warn('Dynamsoft not available, using fallback:', error);
37
+ // Fallback to simulation if Dynamsoft not installed
38
+ if (isMounted) {
39
+ simulateBarcodeScan();
40
+ }
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 {
19
61
  setLoading(true);
20
- setTimeout(() => {
21
- setLoading(false);
62
+
63
+ const barcodeData = {
64
+ token: String(idmConf.verificationCode || ''),
65
+ text: barcodeText,
66
+ };
67
+
68
+ // Call API to submit barcode
69
+ const result = await captureBarcode(idmConf, barcodeData);
70
+
71
+ if (result.status) {
72
+ // Update configuration with response
73
+ setIDMConf({
74
+ ...idmConf,
75
+ requestConfiguration: { ...idmConf.requestConfiguration, ...result },
76
+ });
77
+
78
+ // Navigate to ThankYou
22
79
  navigation.navigate('ThankYou' as never);
23
- }, 2000);
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
+ },
89
+ },
90
+ ]);
91
+ }
92
+ } catch (error: any) {
93
+ console.error('Error processing barcode:', error);
94
+ Alert.alert('Error', 'Failed to process barcode', [
95
+ {
96
+ text: 'Retry',
97
+ onPress: () => {
98
+ setIsScanning(true);
99
+ setScannedData(null);
100
+ setLoading(false);
101
+ },
102
+ },
103
+ ]);
104
+ }
105
+ };
106
+
107
+ // Fallback simulation if Dynamsoft not available
108
+ const simulateBarcodeScan = () => {
109
+ const timer = setTimeout(() => {
110
+ if (isScanning) {
111
+ const simulatedBarcode = 'SIMULATED_PDF417_' + Date.now();
112
+ handleBarcodeScanned(simulatedBarcode);
113
+ }
24
114
  }, 3000);
25
115
 
26
116
  return () => clearTimeout(timer);
27
- }, [navigation]);
117
+ };
118
+
119
+ // Render Dynamsoft Camera View
120
+ const renderBarcodeScanner = () => {
121
+ try {
122
+ const { DCVCameraView } = require('dynamsoft-capture-vision-react-native');
123
+
124
+ return (
125
+ <DCVCameraView
126
+ ref={scannerRef}
127
+ style={StyleSheet.absoluteFill}
128
+ scanRegionVisible={true}
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
+ );
138
+ } catch (error) {
139
+ console.warn('Dynamsoft camera view not available:', error);
140
+
141
+ // Fallback UI
142
+ return (
143
+ <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
144
+ <Text
145
+ style={{
146
+ color: '#fff',
147
+ textAlign: 'center',
148
+ marginTop: 200,
149
+ fontSize: 16,
150
+ }}
151
+ >
152
+ {loading ? 'Processing...' : 'Scanning for barcode...'}
153
+ </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
+ </View>
167
+ );
168
+ }
169
+ };
28
170
 
29
171
  return (
30
172
  <View style={styles.container}>
@@ -32,19 +174,7 @@ export default function BarcodeCapture() {
32
174
  Fill the box with the barcode{'\n'}until it turns green
33
175
  </Text>
34
176
 
35
- {/* Camera placeholder */}
36
- <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
37
- <Text
38
- style={{
39
- color: '#fff',
40
- textAlign: 'center',
41
- marginTop: 200,
42
- fontSize: 16,
43
- }}
44
- >
45
- Barcode Scanner (Static)
46
- </Text>
47
- </View>
177
+ {renderBarcodeScanner()}
48
178
 
49
179
  {/* Barcode overlay box */}
50
180
  <View
@@ -55,12 +185,12 @@ export default function BarcodeCapture() {
55
185
  width: 350,
56
186
  height: 100,
57
187
  borderWidth: 2,
58
- borderColor: '#00ff00',
188
+ borderColor: scannedData ? '#00ff00' : '#ffffff',
59
189
  backgroundColor: 'transparent',
60
190
  }}
61
191
  />
62
192
 
63
- {loading && <Loader />}
193
+ {loading && <Loader message="Processing barcode..." />}
64
194
  </View>
65
195
  );
66
196
  }
@@ -1,54 +1,163 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from 'react-native';
3
3
  import { useNavigation } from '@react-navigation/native';
4
4
  import getStyles from '../styles/DocumentCaptureStyles';
5
5
  import { useTheme } from '../context/ThemeContext';
6
6
  import { useOrientation } from '../hooks/useOrientation';
7
+ import { useIDM } from '../context/IDMConfigurationContext';
7
8
  import Loader from '../components/common/Loader';
9
+ import { captureDocumentBack } from '../apis';
10
+ import { processImageToBase64, validateImagePath } from '../utils/imageProcessor';
11
+ import { createFlowManager } from '../utils/flowManager';
8
12
 
9
13
  export default function DocumentCaptureBack() {
10
14
  const { theme } = useTheme();
15
+ const { idmConf, setIDMConf } = useIDM();
11
16
  const orientation = useOrientation();
12
17
  const styles = getStyles(theme, orientation);
13
18
  const navigation = useNavigation();
14
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);
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
+ };
54
+
55
+ initializeCamera();
56
+ }, [navigation]);
57
+
58
+ // Determine next navigation step based on metadata
59
+ const handleNextStep = () => {
60
+ const metaData = idmConf?.selectedCountryDetails?.selectedMetaData;
61
+
62
+ if (!metaData) {
63
+ navigation.navigate('ThankYou' as never);
64
+ return;
65
+ }
66
+
67
+ const flowManager = createFlowManager(metaData);
68
+ const nextScreen = flowManager.getNextScreenAfterBack();
69
+ navigation.navigate(nextScreen as never);
70
+ };
15
71
 
16
72
  const takePhoto = async () => {
73
+ if (!cameraRef.current) {
74
+ Alert.alert('Camera Not Available', 'Camera is not available in this environment.');
75
+ return;
76
+ }
77
+
17
78
  try {
18
79
  setLoading(true);
19
- // Static implementation - simulate photo capture
20
- setTimeout(() => {
80
+
81
+ const photo = await cameraRef.current.takePhoto();
82
+
83
+ if (!validateImagePath(photo?.path)) {
84
+ Alert.alert('Error', 'Failed to capture image');
21
85
  setLoading(false);
22
- // Navigate to ThankYou after capturing back
23
- navigation.navigate('ThankYou' as never);
24
- }, 1000);
25
- } catch (error) {
86
+ return;
87
+ }
88
+
89
+ // Process image to base64
90
+ const base64Image = await processImageToBase64(photo.path);
91
+
92
+ // Prepare photo data for API
93
+ const photoData = {
94
+ token: String(idmConf.verificationCode || ''),
95
+ file: base64Image,
96
+ };
97
+
98
+ // Call API to capture document back
99
+ const result = await captureDocumentBack(idmConf, photoData);
100
+
101
+ if (result.status) {
102
+ // Update configuration with response
103
+ setIDMConf({
104
+ ...idmConf,
105
+ requestConfiguration: { ...idmConf.requestConfiguration, ...result },
106
+ });
107
+
108
+ // Navigate to next step based on metadata
109
+ handleNextStep();
110
+ } else {
111
+ navigation.navigate('RetakeSelfie' as never, {
112
+ errorMessage: result.errorMessage || 'Failed to process document',
113
+ });
114
+ }
115
+ } catch (error: any) {
26
116
  console.error('Error taking photo:', error);
117
+ Alert.alert('Error', 'Failed to capture or process the image.');
118
+ } finally {
27
119
  setLoading(false);
28
120
  }
29
121
  };
30
122
 
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
+
31
151
  return (
32
152
  <View style={styles.container}>
33
153
  <Text style={styles.topLabel}>Document Back Side</Text>
34
154
 
35
- {/* Camera placeholder */}
36
- <View style={StyleSheet.absoluteFill}>
37
- <View
38
- style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}
39
- ></View>
40
- </View>
155
+ {renderCamera()}
41
156
 
42
- {/* Card overlay placeholder */}
43
- <View
44
- style={[
45
- styles.cardoverlay,
46
- {
47
- borderWidth: 2,
48
- borderColor: '#fff',
49
- backgroundColor: 'transparent',
50
- },
51
- ]}
157
+ <Image
158
+ source={require('../../assets/images/card-overlay-back.png')}
159
+ style={styles.cardoverlay}
160
+ resizeMode="cover"
52
161
  />
53
162
 
54
163
  <Text style={styles.bottomLabel}>
@@ -1,54 +1,176 @@
1
- import React, { useState } from 'react';
2
- import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
1
+ import React, { useEffect, useRef, useState } from 'react';
2
+ import { View, Text, TouchableOpacity, StyleSheet, Image, Alert } from 'react-native';
3
3
  import { useNavigation } from '@react-navigation/native';
4
4
  import getStyles from '../styles/DocumentCaptureStyles';
5
5
  import { useTheme } from '../context/ThemeContext';
6
6
  import { useOrientation } from '../hooks/useOrientation';
7
+ import { useIDM } from '../context/IDMConfigurationContext';
7
8
  import Loader from '../components/common/Loader';
9
+ import { captureDocumentFront } from '../apis';
10
+ import { processImageToBase64, validateImagePath } from '../utils/imageProcessor';
11
+ import { createFlowManager } from '../utils/flowManager';
8
12
 
9
13
  export default function DocumentCaptureFront() {
10
14
  const { theme } = useTheme();
15
+ const { idmConf, setIDMConf } = useIDM();
11
16
  const orientation = useOrientation();
12
17
  const styles = getStyles(theme, orientation);
13
18
  const navigation = useNavigation();
14
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);
23
+
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]);
61
+
62
+ // Determine next navigation step based on metadata
63
+ const handleNextStep = () => {
64
+ const metaData = idmConf?.selectedCountryDetails?.selectedMetaData;
65
+
66
+ if (!metaData) {
67
+ navigation.navigate('ThankYou' as never);
68
+ return;
69
+ }
70
+
71
+ const flowManager = createFlowManager(metaData);
72
+ const nextScreen = flowManager.getNextScreenAfterFront();
73
+ navigation.navigate(nextScreen as never);
74
+ };
15
75
 
16
76
  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.');
80
+ return;
81
+ }
82
+
17
83
  try {
18
84
  setLoading(true);
19
- // Static implementation - simulate photo capture
20
- setTimeout(() => {
85
+
86
+ const photo = await cameraRef.current.takePhoto();
87
+
88
+ if (!validateImagePath(photo?.path)) {
89
+ Alert.alert('Error', 'Failed to capture image');
21
90
  setLoading(false);
22
- // Navigate to BackDocumentAdvice after capturing front
23
- navigation.navigate('BackDocumentAdvice' as never);
24
- }, 1000);
25
- } catch (error) {
91
+ return;
92
+ }
93
+
94
+ // Process image to base64
95
+ const base64Image = await processImageToBase64(photo.path);
96
+
97
+ // Prepare photo data for API
98
+ 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 || ''),
104
+ file: base64Image,
105
+ };
106
+
107
+ // Call API to capture document front
108
+ const result = await captureDocumentFront(idmConf, photoData);
109
+
110
+ if (result.status) {
111
+ // Update configuration with response
112
+ setIDMConf({
113
+ ...idmConf,
114
+ requestConfiguration: result,
115
+ });
116
+
117
+ // Navigate to next step based on metadata
118
+ handleNextStep();
119
+ } else {
120
+ // Handle error - navigate to retake screen
121
+ navigation.navigate('RetakeSelfie' as never, {
122
+ errorMessage: result.errorMessage || 'Failed to process document',
123
+ });
124
+ }
125
+ } catch (error: any) {
26
126
  console.error('Error taking photo:', error);
127
+ Alert.alert('Error', 'Failed to capture or process the image.');
128
+ } finally {
27
129
  setLoading(false);
28
130
  }
29
131
  };
30
132
 
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
+
31
163
  return (
32
164
  <View style={styles.container}>
33
165
  <Text style={styles.topLabel}>Document Front Side</Text>
34
166
 
35
- {/* Camera placeholder */}
36
- <View style={StyleSheet.absoluteFill}>
37
- <View
38
- style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}
39
- ></View>
40
- </View>
167
+ {renderCamera()}
41
168
 
42
- {/* Card overlay placeholder */}
43
- <View
44
- style={[
45
- styles.cardoverlay,
46
- {
47
- borderWidth: 2,
48
- borderColor: '#fff',
49
- backgroundColor: 'transparent',
50
- },
51
- ]}
169
+ {/* Card overlay */}
170
+ <Image
171
+ source={require('../../assets/images/card-overlay.png')}
172
+ style={styles.cardoverlay}
173
+ resizeMode="cover"
52
174
  />
53
175
 
54
176
  <Text style={styles.bottomLabel}>