react-native-biometric-verifier 0.0.30 → 0.0.32
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/package.json +1 -1
- package/src/components/CaptureImageWithoutEdit.js +2 -6
- package/src/components/Card.js +6 -4
- package/src/components/Loader.js +10 -8
- package/src/hooks/useCountdown.js +4 -14
- package/src/hooks/useFaceDetectionFrameProcessor.js +1 -2
- package/src/hooks/useGeolocation.js +0 -1
- package/src/hooks/useImageProcessing.js +14 -29
- package/src/hooks/useNotifyMessage.js +3 -8
- package/src/hooks/useSafeCallback.js +0 -2
- package/src/index.js +6 -87
- package/src/utils/NetworkServiceCall.js +2 -11
- package/src/utils/distanceCalculator.js +14 -9
- package/src/utils/getLoaderGif.js +23 -18
package/package.json
CHANGED
|
@@ -67,7 +67,6 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
67
67
|
onCodeScanned: (codes) => {
|
|
68
68
|
try {
|
|
69
69
|
if (showCodeScanner && codes && codes[0]?.value && !isLoading) {
|
|
70
|
-
console.log('QR Code scanned:', codes[0].value);
|
|
71
70
|
onCapture(codes[0].value);
|
|
72
71
|
}
|
|
73
72
|
} catch (error) {
|
|
@@ -224,9 +223,8 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
224
223
|
if (newCameraPermission === 'granted') {
|
|
225
224
|
let devices = await Camera.getAvailableCameraDevices();
|
|
226
225
|
|
|
227
|
-
//
|
|
226
|
+
// Retry once after short delay if no devices found
|
|
228
227
|
if (!devices || devices.length === 0) {
|
|
229
|
-
console.warn('No camera devices yet, retrying in 300ms...');
|
|
230
228
|
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
231
229
|
devices = await Camera.getAvailableCameraDevices();
|
|
232
230
|
}
|
|
@@ -240,7 +238,6 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
240
238
|
|
|
241
239
|
setCameraDevice(device);
|
|
242
240
|
setShowCamera(true);
|
|
243
|
-
console.log('Camera device set successfully');
|
|
244
241
|
} else {
|
|
245
242
|
console.warn('Camera permission not granted');
|
|
246
243
|
}
|
|
@@ -444,11 +441,10 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
444
441
|
}
|
|
445
442
|
frameProcessorFps={frameProcessorFps}
|
|
446
443
|
onInitialized={() => {
|
|
447
|
-
console.log('Camera initialized successfully');
|
|
448
444
|
setCameraInitialized(true);
|
|
449
445
|
}}
|
|
450
446
|
onError={(error) => {
|
|
451
|
-
console.
|
|
447
|
+
console.error('Camera error:', error);
|
|
452
448
|
}}
|
|
453
449
|
exposure={0}
|
|
454
450
|
pixelFormat="yuv"
|
package/src/components/Card.js
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
import { View, Text, Image, StyleSheet, Platform } from 'react-native';
|
|
3
3
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
5
|
import { Global } from '../utils/Global';
|
|
6
6
|
|
|
7
7
|
export const Card = ({ employeeData, apiurl, fileurl = 'file/filedownload/photo/' }) => {
|
|
8
|
-
|
|
9
8
|
if (!employeeData || typeof employeeData !== 'object') {
|
|
10
|
-
console.warn('Card: Invalid or missing employeeData');
|
|
11
9
|
return null;
|
|
12
10
|
}
|
|
13
11
|
|
|
@@ -30,7 +28,11 @@ export const Card = ({ employeeData, apiurl, fileurl = 'file/filedownload/photo/
|
|
|
30
28
|
style={styles.image}
|
|
31
29
|
resizeMode="cover"
|
|
32
30
|
onError={() => {
|
|
33
|
-
|
|
31
|
+
try {
|
|
32
|
+
throw new Error(`Error loading image for employee: ${employeeName}`);
|
|
33
|
+
} catch (err) {
|
|
34
|
+
console.error(err);
|
|
35
|
+
}
|
|
34
36
|
}}
|
|
35
37
|
/>
|
|
36
38
|
) : (
|
package/src/components/Loader.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
2
|
import {
|
|
3
3
|
StyleSheet,
|
|
4
|
-
Dimensions,
|
|
5
4
|
View,
|
|
6
5
|
Modal,
|
|
7
6
|
Animated,
|
|
@@ -30,18 +29,23 @@ export default function Loader({
|
|
|
30
29
|
const [pulse] = useState(new Animated.Value(1));
|
|
31
30
|
const [fade] = useState(new Animated.Value(0));
|
|
32
31
|
const [imageSource, setImageSource] = useState(gifSource);
|
|
33
|
-
|
|
32
|
+
|
|
33
|
+
const error = getLoaderGif(state.animationState, state.currentStep, "http://emr.amalaims.org:9393/", imageurl);
|
|
34
|
+
|
|
34
35
|
// Reset imageSource whenever gifSource prop changes
|
|
35
36
|
useEffect(() => {
|
|
36
37
|
setImageSource(gifSource);
|
|
37
38
|
}, [gifSource]);
|
|
38
39
|
|
|
39
40
|
const handleImageError = () => {
|
|
40
|
-
|
|
41
|
-
|
|
41
|
+
try {
|
|
42
|
+
setImageSource(error);
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.error("Loader image error:", err);
|
|
45
|
+
}
|
|
42
46
|
};
|
|
43
47
|
|
|
44
|
-
// Rotation
|
|
48
|
+
// Rotation, pulse, and fade-in animations
|
|
45
49
|
useEffect(() => {
|
|
46
50
|
if (!gifSource) { // Only animate if not using a GIF
|
|
47
51
|
Animated.loop(
|
|
@@ -54,7 +58,6 @@ export default function Loader({
|
|
|
54
58
|
).start();
|
|
55
59
|
}
|
|
56
60
|
|
|
57
|
-
// Pulse animation
|
|
58
61
|
Animated.loop(
|
|
59
62
|
Animated.sequence([
|
|
60
63
|
Animated.timing(pulse, {
|
|
@@ -70,7 +73,6 @@ export default function Loader({
|
|
|
70
73
|
])
|
|
71
74
|
).start();
|
|
72
75
|
|
|
73
|
-
// Fade in animation
|
|
74
76
|
Animated.timing(fade, {
|
|
75
77
|
toValue: 1,
|
|
76
78
|
duration: 300,
|
|
@@ -186,4 +188,4 @@ const styles = StyleSheet.create({
|
|
|
186
188
|
shadowRadius: 3.84,
|
|
187
189
|
elevation: 5,
|
|
188
190
|
}
|
|
189
|
-
});
|
|
191
|
+
});
|
|
@@ -38,10 +38,7 @@ export const useCountdown = (onExpire) => {
|
|
|
38
38
|
|
|
39
39
|
// Start new timer
|
|
40
40
|
timerRef.current = setInterval(() => {
|
|
41
|
-
if (isPausedRef.current)
|
|
42
|
-
console.log("⏸️ Countdown paused, skipping tick");
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
41
|
+
if (isPausedRef.current) return;
|
|
45
42
|
|
|
46
43
|
countdownRef.current -= 1;
|
|
47
44
|
|
|
@@ -49,7 +46,6 @@ export const useCountdown = (onExpire) => {
|
|
|
49
46
|
clearInterval(timerRef.current);
|
|
50
47
|
timerRef.current = null;
|
|
51
48
|
|
|
52
|
-
// Safely call onExpire if it's a function
|
|
53
49
|
if (typeof expireCallback === 'function') {
|
|
54
50
|
expireCallback();
|
|
55
51
|
}
|
|
@@ -64,13 +60,11 @@ export const useCountdown = (onExpire) => {
|
|
|
64
60
|
|
|
65
61
|
// Pause the countdown
|
|
66
62
|
const pauseCountdown = useCallback(() => {
|
|
67
|
-
console.log("⏸️ Pausing countdown");
|
|
68
63
|
isPausedRef.current = true;
|
|
69
64
|
}, []);
|
|
70
65
|
|
|
71
66
|
// Resume the countdown
|
|
72
67
|
const resumeCountdown = useCallback(() => {
|
|
73
|
-
console.log("▶️ Resuming countdown");
|
|
74
68
|
isPausedRef.current = false;
|
|
75
69
|
}, []);
|
|
76
70
|
|
|
@@ -91,14 +85,10 @@ export const useCountdown = (onExpire) => {
|
|
|
91
85
|
}, []);
|
|
92
86
|
|
|
93
87
|
// Get current countdown value
|
|
94
|
-
const getCurrentCountdown = useCallback(() =>
|
|
95
|
-
return countdownRef.current;
|
|
96
|
-
}, []);
|
|
88
|
+
const getCurrentCountdown = useCallback(() => countdownRef.current, []);
|
|
97
89
|
|
|
98
90
|
// Check if countdown is paused
|
|
99
|
-
const isPaused = useCallback(() =>
|
|
100
|
-
return isPausedRef.current;
|
|
101
|
-
}, []);
|
|
91
|
+
const isPaused = useCallback(() => isPausedRef.current, []);
|
|
102
92
|
|
|
103
93
|
// Clean up on unmount
|
|
104
94
|
useEffect(() => {
|
|
@@ -119,4 +109,4 @@ export const useCountdown = (onExpire) => {
|
|
|
119
109
|
getCurrentCountdown,
|
|
120
110
|
isPaused
|
|
121
111
|
};
|
|
122
|
-
};
|
|
112
|
+
};
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
faceAntiSpoofFrameProcessor,
|
|
7
7
|
initializeFaceAntiSpoof,
|
|
8
8
|
isFaceAntiSpoofAvailable,
|
|
9
|
-
} from 'react-native-vision-camera-
|
|
9
|
+
} from 'react-native-vision-camera-spoof-detector';
|
|
10
10
|
|
|
11
11
|
// Optimized constants - tuned for performance
|
|
12
12
|
const FACE_STABILITY_THRESHOLD = 3;
|
|
@@ -360,7 +360,6 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
360
360
|
if (state.flags.isFaceCentered) {
|
|
361
361
|
try {
|
|
362
362
|
antiSpoofResult = faceAntiSpoofFrameProcessor?.(frame);
|
|
363
|
-
|
|
364
363
|
if (antiSpoofResult != null) {
|
|
365
364
|
state.antiSpoof.lastResult = antiSpoofResult;
|
|
366
365
|
|
|
@@ -18,51 +18,36 @@ export const useImageProcessing = () => {
|
|
|
18
18
|
*/
|
|
19
19
|
const convertImageToBase64 = useCallback(async (uri, includeMimeType = false) => {
|
|
20
20
|
try {
|
|
21
|
-
// Validate input
|
|
22
21
|
if (!uri || typeof uri !== 'string') {
|
|
23
22
|
throw new Error('Invalid image URI provided.');
|
|
24
23
|
}
|
|
25
24
|
|
|
26
|
-
// Optional: Check file info
|
|
25
|
+
// Optional: Check file info
|
|
27
26
|
try {
|
|
28
|
-
|
|
29
|
-
if (!fileInfo) {
|
|
30
|
-
console.warn('Unable to fetch file info. Proceeding with resize.');
|
|
31
|
-
}
|
|
27
|
+
await RNFS.stat(uri);
|
|
32
28
|
} catch {
|
|
33
|
-
|
|
29
|
+
// Skip warnings; silently ignore
|
|
34
30
|
}
|
|
35
31
|
|
|
36
32
|
// Resize image
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
false // Keep EXIF metadata
|
|
48
|
-
);
|
|
49
|
-
} catch (resizeError) {
|
|
50
|
-
throw new Error(`Failed to resize image: ${resizeError.message}`);
|
|
51
|
-
}
|
|
33
|
+
const resizedImage = await ImageResizer.createResizedImage(
|
|
34
|
+
uri,
|
|
35
|
+
Global.ImageResize.width,
|
|
36
|
+
Global.ImageResize.height,
|
|
37
|
+
Global.ImageResize.format, // 'JPEG' or 'PNG'
|
|
38
|
+
Global.ImageResize.quality, // e.g., 80
|
|
39
|
+
0, // Rotation
|
|
40
|
+
undefined, // Output path
|
|
41
|
+
false // Keep EXIF metadata
|
|
42
|
+
);
|
|
52
43
|
|
|
53
44
|
if (!resizedImage?.uri) {
|
|
54
45
|
throw new Error('Image resizing returned an invalid result.');
|
|
55
46
|
}
|
|
56
47
|
|
|
57
48
|
// Convert resized image to Base64
|
|
58
|
-
let base64Data;
|
|
59
|
-
try {
|
|
60
|
-
base64Data = await RNFS.readFile(resizedImage.uri, 'base64');
|
|
61
|
-
} catch (readError) {
|
|
62
|
-
throw new Error(`Failed to read resized image file: ${readError.message}`);
|
|
63
|
-
}
|
|
49
|
+
let base64Data = await RNFS.readFile(resizedImage.uri, 'base64');
|
|
64
50
|
|
|
65
|
-
// Optionally prepend MIME type
|
|
66
51
|
if (includeMimeType) {
|
|
67
52
|
const mimeType = Global.ImageResize.format.toLowerCase() === 'png' ? 'image/png' : 'image/jpeg';
|
|
68
53
|
base64Data = `data:${mimeType};base64,${base64Data}`;
|
|
@@ -53,22 +53,18 @@ export const useNotifyMessage = () => {
|
|
|
53
53
|
}), []);
|
|
54
54
|
|
|
55
55
|
const clearNotification = useCallback(() => {
|
|
56
|
-
// Clear any pending timeout
|
|
57
56
|
if (timeoutRef.current) {
|
|
58
57
|
clearTimeout(timeoutRef.current);
|
|
59
58
|
timeoutRef.current = null;
|
|
60
59
|
}
|
|
61
|
-
|
|
62
|
-
// Reset animations
|
|
60
|
+
|
|
63
61
|
fadeAnim.setValue(0);
|
|
64
62
|
slideAnim.setValue(-20);
|
|
65
|
-
|
|
66
|
-
// Hide notification
|
|
63
|
+
|
|
67
64
|
setNotification({ visible: false, message: '', type: 'info' });
|
|
68
65
|
}, [fadeAnim, slideAnim]);
|
|
69
66
|
|
|
70
67
|
const showNotification = useCallback((message, type = 'info') => {
|
|
71
|
-
// Clear any existing notification first
|
|
72
68
|
clearNotification();
|
|
73
69
|
|
|
74
70
|
setNotification({ visible: true, message, type });
|
|
@@ -88,7 +84,6 @@ export const useNotifyMessage = () => {
|
|
|
88
84
|
useNativeDriver: false,
|
|
89
85
|
}),
|
|
90
86
|
]).start(() => {
|
|
91
|
-
// Store timeout reference so we can clear it later
|
|
92
87
|
timeoutRef.current = setTimeout(() => {
|
|
93
88
|
Animated.parallel([
|
|
94
89
|
Animated.timing(fadeAnim, {
|
|
@@ -150,4 +145,4 @@ export const useNotifyMessage = () => {
|
|
|
150
145
|
getNotificationStyle,
|
|
151
146
|
notificationTextStyle,
|
|
152
147
|
};
|
|
153
|
-
};
|
|
148
|
+
};
|
package/src/index.js
CHANGED
|
@@ -74,15 +74,11 @@ const BiometricModal = React.memo(
|
|
|
74
74
|
|
|
75
75
|
// Cleanup on unmount
|
|
76
76
|
useEffect(() => {
|
|
77
|
-
console.log("🔧 BiometricModal mounted");
|
|
78
|
-
|
|
79
77
|
return () => {
|
|
80
|
-
console.log("🧹 BiometricModal unmounting - cleaning up");
|
|
81
78
|
mountedRef.current = false;
|
|
82
79
|
|
|
83
80
|
if (resetTimeoutRef.current) {
|
|
84
81
|
clearTimeout(resetTimeoutRef.current);
|
|
85
|
-
console.log("⏹️ Cleared reset timeout");
|
|
86
82
|
}
|
|
87
83
|
|
|
88
84
|
clearNotification();
|
|
@@ -92,10 +88,6 @@ const BiometricModal = React.memo(
|
|
|
92
88
|
// Update dataRef when data changes
|
|
93
89
|
useEffect(() => {
|
|
94
90
|
dataRef.current = data;
|
|
95
|
-
console.log(
|
|
96
|
-
"📋 Updated dataRef with new data:",
|
|
97
|
-
data ? "Available" : "Empty"
|
|
98
|
-
);
|
|
99
91
|
}, [data]);
|
|
100
92
|
|
|
101
93
|
// Animation helper
|
|
@@ -133,15 +125,11 @@ const BiometricModal = React.memo(
|
|
|
133
125
|
const merged = { ...prev, ...newState };
|
|
134
126
|
|
|
135
127
|
if (JSON.stringify(prev) !== JSON.stringify(merged)) {
|
|
136
|
-
console.log("🔄 State updated:", merged);
|
|
137
|
-
|
|
138
128
|
// Pause/resume countdown based on loading state
|
|
139
129
|
if (newState.isLoading !== undefined) {
|
|
140
130
|
if (newState.isLoading) {
|
|
141
|
-
console.log("⏸️ Pausing countdown due to loading");
|
|
142
131
|
pauseCountdown();
|
|
143
132
|
} else {
|
|
144
|
-
console.log("▶️ Resuming countdown after loading");
|
|
145
133
|
resumeCountdown();
|
|
146
134
|
}
|
|
147
135
|
}
|
|
@@ -156,14 +144,11 @@ const BiometricModal = React.memo(
|
|
|
156
144
|
|
|
157
145
|
return prev;
|
|
158
146
|
});
|
|
159
|
-
} else {
|
|
160
|
-
console.log("🚫 State update skipped - component unmounted");
|
|
161
147
|
}
|
|
162
148
|
}, [animateIcon, pauseCountdown, resumeCountdown]);
|
|
163
149
|
|
|
164
150
|
// Reset state helper
|
|
165
151
|
const resetState = useCallback(() => {
|
|
166
|
-
console.log("🔄 Resetting biometric modal state");
|
|
167
152
|
onclose(false);
|
|
168
153
|
|
|
169
154
|
setState({
|
|
@@ -182,15 +167,15 @@ const BiometricModal = React.memo(
|
|
|
182
167
|
if (resetTimeoutRef.current) {
|
|
183
168
|
clearTimeout(resetTimeoutRef.current);
|
|
184
169
|
resetTimeoutRef.current = null;
|
|
185
|
-
console.log("⏹️ Cleared reset timeout during reset");
|
|
186
170
|
}
|
|
187
171
|
}, [resetCountdown, clearNotification]);
|
|
188
172
|
|
|
189
173
|
// Error handler
|
|
190
174
|
const handleProcessError = useCallback(
|
|
191
175
|
(message, errorObj = null) => {
|
|
192
|
-
|
|
193
|
-
|
|
176
|
+
if (errorObj) {
|
|
177
|
+
console.error("Process Error:", errorObj);
|
|
178
|
+
}
|
|
194
179
|
|
|
195
180
|
notifyMessage(message, "error");
|
|
196
181
|
updateState({
|
|
@@ -204,7 +189,6 @@ const BiometricModal = React.memo(
|
|
|
204
189
|
}
|
|
205
190
|
|
|
206
191
|
resetTimeoutRef.current = setTimeout(() => {
|
|
207
|
-
console.log("⏰ Error timeout completed - resetting state");
|
|
208
192
|
resetState();
|
|
209
193
|
}, 1200);
|
|
210
194
|
},
|
|
@@ -213,11 +197,9 @@ const BiometricModal = React.memo(
|
|
|
213
197
|
|
|
214
198
|
// Countdown finish handler
|
|
215
199
|
const handleCountdownFinish = useCallback(() => {
|
|
216
|
-
console.log("⏰ Countdown finished");
|
|
217
200
|
handleProcessError("Time is up! Please try again.");
|
|
218
201
|
|
|
219
202
|
if (navigation.canGoBack()) {
|
|
220
|
-
console.log("↩️ Navigating back due to timeout");
|
|
221
203
|
navigation.goBack();
|
|
222
204
|
}
|
|
223
205
|
}, [handleProcessError, navigation]);
|
|
@@ -225,25 +207,20 @@ const BiometricModal = React.memo(
|
|
|
225
207
|
// API URL validation
|
|
226
208
|
const validateApiUrl = useCallback(() => {
|
|
227
209
|
if (!apiurl || typeof apiurl !== "string") {
|
|
228
|
-
console.error("❌ Invalid API URL:", apiurl);
|
|
229
210
|
handleProcessError("Invalid API URL configuration.");
|
|
230
211
|
return false;
|
|
231
212
|
}
|
|
232
213
|
|
|
233
|
-
console.log("✅ API URL validated:", apiurl);
|
|
234
214
|
return true;
|
|
235
215
|
}, [apiurl, handleProcessError]);
|
|
236
216
|
|
|
237
217
|
// Face scan upload
|
|
238
218
|
const uploadFaceScan = useCallback(
|
|
239
219
|
async (selfie) => {
|
|
240
|
-
console.log("📸 Uploading face scan");
|
|
241
|
-
|
|
242
220
|
if (!validateApiUrl()) return;
|
|
243
221
|
const currentData = dataRef.current;
|
|
244
222
|
|
|
245
223
|
if (!currentData) {
|
|
246
|
-
console.error("❌ No employee data available");
|
|
247
224
|
handleProcessError("Employee data not found.");
|
|
248
225
|
return;
|
|
249
226
|
}
|
|
@@ -258,21 +235,18 @@ const BiometricModal = React.memo(
|
|
|
258
235
|
let base64;
|
|
259
236
|
|
|
260
237
|
try {
|
|
261
|
-
console.log("🖼️ Converting image to base64");
|
|
262
238
|
updateState({
|
|
263
239
|
loadingType: Global.LoadingTypes.imageProcessing,
|
|
264
240
|
});
|
|
265
241
|
|
|
266
242
|
base64 = await convertImageToBase64(selfie?.uri);
|
|
267
|
-
console.log("✅ Image converted successfully");
|
|
268
243
|
} catch (err) {
|
|
269
|
-
console.error("
|
|
244
|
+
console.error("Image conversion failed:", err);
|
|
270
245
|
handleProcessError("Image conversion failed.", err);
|
|
271
246
|
return;
|
|
272
247
|
}
|
|
273
248
|
|
|
274
249
|
if (!base64) {
|
|
275
|
-
console.error("❌ Empty base64 data");
|
|
276
250
|
handleProcessError("Failed to process image.");
|
|
277
251
|
return;
|
|
278
252
|
}
|
|
@@ -281,7 +255,6 @@ const BiometricModal = React.memo(
|
|
|
281
255
|
const body = { image: base64 };
|
|
282
256
|
const header = { faceid: currentData };
|
|
283
257
|
const buttonapi = `${apiurl}python/recognize`;
|
|
284
|
-
console.log("🌐 Calling face recognition API:", buttonapi);
|
|
285
258
|
|
|
286
259
|
updateState({
|
|
287
260
|
loadingType: Global.LoadingTypes.networkRequest,
|
|
@@ -294,10 +267,7 @@ const BiometricModal = React.memo(
|
|
|
294
267
|
body
|
|
295
268
|
);
|
|
296
269
|
|
|
297
|
-
console.log("📨 API Response:", JSON.stringify(response));
|
|
298
|
-
|
|
299
270
|
if (response?.httpstatus === 200) {
|
|
300
|
-
console.log("✅ Face recognition successful");
|
|
301
271
|
responseRef.current = response;
|
|
302
272
|
|
|
303
273
|
updateState({
|
|
@@ -310,10 +280,8 @@ const BiometricModal = React.memo(
|
|
|
310
280
|
notifyMessage("Identity verified successfully!", "success");
|
|
311
281
|
|
|
312
282
|
if (qrscan) {
|
|
313
|
-
console.log("🔜 Proceeding to QR code scan");
|
|
314
283
|
setTimeout(() => startQRCodeScan(), 1200);
|
|
315
284
|
} else {
|
|
316
|
-
console.log("✅ Verification complete - calling callback");
|
|
317
285
|
safeCallback(responseRef.current);
|
|
318
286
|
|
|
319
287
|
if (resetTimeoutRef.current) {
|
|
@@ -321,19 +289,17 @@ const BiometricModal = React.memo(
|
|
|
321
289
|
}
|
|
322
290
|
|
|
323
291
|
resetTimeoutRef.current = setTimeout(() => {
|
|
324
|
-
console.log("⏰ Success timeout completed - resetting");
|
|
325
292
|
resetState();
|
|
326
293
|
}, 1200);
|
|
327
294
|
}
|
|
328
295
|
} else {
|
|
329
|
-
console.warn("⚠️ Face recognition failed:", response?.httpstatus);
|
|
330
296
|
handleProcessError(
|
|
331
297
|
response?.data?.message ||
|
|
332
298
|
"Face not recognized. Please try again."
|
|
333
299
|
);
|
|
334
300
|
}
|
|
335
301
|
} catch (error) {
|
|
336
|
-
console.error("
|
|
302
|
+
console.error("Network request failed:", error);
|
|
337
303
|
handleProcessError(
|
|
338
304
|
"Connection error. Please check your network.",
|
|
339
305
|
error
|
|
@@ -356,8 +322,6 @@ const BiometricModal = React.memo(
|
|
|
356
322
|
// QR code processing
|
|
357
323
|
const handleQRScanned = useCallback(
|
|
358
324
|
async (qrCodeData) => {
|
|
359
|
-
console.log("🔍 Processing scanned QR code");
|
|
360
|
-
|
|
361
325
|
if (!validateApiUrl()) return;
|
|
362
326
|
|
|
363
327
|
updateState({
|
|
@@ -367,7 +331,6 @@ const BiometricModal = React.memo(
|
|
|
367
331
|
});
|
|
368
332
|
|
|
369
333
|
try {
|
|
370
|
-
console.log("📍 Requesting location permission");
|
|
371
334
|
updateState({
|
|
372
335
|
loadingType: Global.LoadingTypes.locationPermission,
|
|
373
336
|
});
|
|
@@ -375,30 +338,23 @@ const BiometricModal = React.memo(
|
|
|
375
338
|
const hasPermission = await requestLocationPermission();
|
|
376
339
|
|
|
377
340
|
if (!hasPermission) {
|
|
378
|
-
console.error("❌ Location permission denied");
|
|
379
341
|
handleProcessError("Location permission not granted.");
|
|
380
342
|
return;
|
|
381
343
|
}
|
|
382
344
|
|
|
383
|
-
console.log("✅ Location permission granted");
|
|
384
|
-
|
|
385
345
|
const qrString =
|
|
386
346
|
typeof qrCodeData === "object" ? qrCodeData?.data : qrCodeData;
|
|
387
347
|
|
|
388
348
|
if (!qrString || typeof qrString !== "string") {
|
|
389
|
-
console.error("❌ Invalid QR code data:", qrCodeData);
|
|
390
349
|
handleProcessError("Invalid QR code. Please try again.");
|
|
391
350
|
return;
|
|
392
351
|
}
|
|
393
352
|
|
|
394
|
-
console.log("📋 QR code content:", qrString);
|
|
395
|
-
|
|
396
353
|
updateState({
|
|
397
354
|
loadingType: Global.LoadingTypes.gettingLocation,
|
|
398
355
|
});
|
|
399
356
|
|
|
400
357
|
const location = await getCurrentLocation();
|
|
401
|
-
console.log("📍 Device location:", location);
|
|
402
358
|
|
|
403
359
|
const [latStr, lngStr] = qrString.split(",");
|
|
404
360
|
const lat = parseFloat(latStr);
|
|
@@ -407,13 +363,6 @@ const BiometricModal = React.memo(
|
|
|
407
363
|
const validDev =
|
|
408
364
|
!isNaN(location?.latitude) && !isNaN(location?.longitude);
|
|
409
365
|
|
|
410
|
-
console.log(
|
|
411
|
-
"📊 Coordinates validation - QR:",
|
|
412
|
-
validCoords,
|
|
413
|
-
"Device:",
|
|
414
|
-
validDev
|
|
415
|
-
);
|
|
416
|
-
|
|
417
366
|
if (validCoords && validDev) {
|
|
418
367
|
updateState({
|
|
419
368
|
loadingType: Global.LoadingTypes.calculateDistance,
|
|
@@ -426,14 +375,7 @@ const BiometricModal = React.memo(
|
|
|
426
375
|
location.longitude
|
|
427
376
|
);
|
|
428
377
|
|
|
429
|
-
console.log(
|
|
430
|
-
"📏 Distance calculated:",
|
|
431
|
-
distance.toFixed(0),
|
|
432
|
-
"meters"
|
|
433
|
-
);
|
|
434
|
-
|
|
435
378
|
if (distance <= Global.MaxDistanceMeters) {
|
|
436
|
-
console.log("✅ Location verified successfully");
|
|
437
379
|
safeCallback(responseRef.current);
|
|
438
380
|
notifyMessage("Location verified successfully!", "success");
|
|
439
381
|
|
|
@@ -448,30 +390,18 @@ const BiometricModal = React.memo(
|
|
|
448
390
|
}
|
|
449
391
|
|
|
450
392
|
resetTimeoutRef.current = setTimeout(() => {
|
|
451
|
-
console.log("⏰ Location success timeout - resetting");
|
|
452
393
|
resetState();
|
|
453
394
|
}, 1200);
|
|
454
395
|
} else {
|
|
455
|
-
console.warn(
|
|
456
|
-
"⚠️ Location mismatch:",
|
|
457
|
-
distance.toFixed(0),
|
|
458
|
-
"m away"
|
|
459
|
-
);
|
|
460
396
|
handleProcessError(
|
|
461
397
|
`Location mismatch (${distance.toFixed(0)}m away).`
|
|
462
398
|
);
|
|
463
399
|
}
|
|
464
400
|
} else {
|
|
465
|
-
console.error("❌ Invalid coordinates:", {
|
|
466
|
-
qrLat: lat,
|
|
467
|
-
qrLng: lng,
|
|
468
|
-
devLat: location.latitude,
|
|
469
|
-
devLng: location.longitude,
|
|
470
|
-
});
|
|
471
401
|
handleProcessError("Invalid coordinates in QR code.");
|
|
472
402
|
}
|
|
473
403
|
} catch (error) {
|
|
474
|
-
console.error("
|
|
404
|
+
console.error("Location verification failed:", error);
|
|
475
405
|
handleProcessError(
|
|
476
406
|
"Unable to verify location. Please try again.",
|
|
477
407
|
error
|
|
@@ -493,13 +423,9 @@ const BiometricModal = React.memo(
|
|
|
493
423
|
// Image capture handler
|
|
494
424
|
const handleImageCapture = useCallback(
|
|
495
425
|
async (capturedData) => {
|
|
496
|
-
console.log("📷 Image captured for step:", state.currentStep);
|
|
497
|
-
|
|
498
426
|
if (state.currentStep === "Identity Verification") {
|
|
499
|
-
console.log("👤 Processing face verification");
|
|
500
427
|
uploadFaceScan(capturedData);
|
|
501
428
|
} else if (state.currentStep === "Location Verification") {
|
|
502
|
-
console.log("📍 Processing QR code verification");
|
|
503
429
|
handleQRScanned(capturedData);
|
|
504
430
|
}
|
|
505
431
|
},
|
|
@@ -508,7 +434,6 @@ const BiometricModal = React.memo(
|
|
|
508
434
|
|
|
509
435
|
// Start face scan
|
|
510
436
|
const handleStartFaceScan = useCallback(() => {
|
|
511
|
-
console.log("👤 Starting face scan");
|
|
512
437
|
updateState({
|
|
513
438
|
currentStep: "Identity Verification",
|
|
514
439
|
animationState: Global.AnimationStates.faceScan,
|
|
@@ -518,7 +443,6 @@ const BiometricModal = React.memo(
|
|
|
518
443
|
|
|
519
444
|
// Start QR code scan
|
|
520
445
|
const startQRCodeScan = useCallback(() => {
|
|
521
|
-
console.log("📍 Starting QR code scan");
|
|
522
446
|
updateState({
|
|
523
447
|
currentStep: "Location Verification",
|
|
524
448
|
animationState: Global.AnimationStates.qrScan,
|
|
@@ -528,7 +452,6 @@ const BiometricModal = React.memo(
|
|
|
528
452
|
|
|
529
453
|
// Start the verification process
|
|
530
454
|
const startProcess = useCallback(() => {
|
|
531
|
-
console.log("🚀 Starting verification process");
|
|
532
455
|
startCountdown(handleCountdownFinish);
|
|
533
456
|
handleStartFaceScan();
|
|
534
457
|
}, [handleCountdownFinish, handleStartFaceScan, startCountdown]);
|
|
@@ -536,16 +459,12 @@ const BiometricModal = React.memo(
|
|
|
536
459
|
// Open modal when data is received
|
|
537
460
|
useEffect(() => {
|
|
538
461
|
if (data && !modalVisible && !processedRef.current) {
|
|
539
|
-
console.log("📥 New data received, opening modal");
|
|
540
462
|
processedRef.current = true;
|
|
541
463
|
setModalVisible(true);
|
|
542
464
|
startProcess();
|
|
543
465
|
}
|
|
544
466
|
}, [data, modalVisible, startProcess]);
|
|
545
467
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
468
|
// Determine if camera should be shown
|
|
550
469
|
const shouldShowCamera =
|
|
551
470
|
(state.currentStep === "Identity Verification" ||
|
|
@@ -13,17 +13,8 @@ const networkServiceCall = async (method, url, extraHeaders = {}, body = {}) =>
|
|
|
13
13
|
dataset.body = JSON.stringify(body);
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
console.log("🌐 NetworkServiceCall Request:");
|
|
17
|
-
console.log("➡️ URL:", url);
|
|
18
|
-
console.log("➡️ Headers:", dataset.headers);
|
|
19
|
-
if (dataset.body) console.log("➡️ Body:", dataset.body);
|
|
20
|
-
|
|
21
16
|
const response = await fetch(url, dataset);
|
|
22
|
-
|
|
23
|
-
console.log("🌐 Response Status:", response.status);
|
|
24
|
-
|
|
25
17
|
const result = await response.json();
|
|
26
|
-
console.log("✅ API Success:", result);
|
|
27
18
|
return result;
|
|
28
19
|
|
|
29
20
|
} catch (error) {
|
|
@@ -32,12 +23,12 @@ const networkServiceCall = async (method, url, extraHeaders = {}, body = {}) =>
|
|
|
32
23
|
}
|
|
33
24
|
};
|
|
34
25
|
|
|
35
|
-
//
|
|
26
|
+
// GET API Call helper
|
|
36
27
|
export const getApiCall = (url, extraHeaders = {}) => {
|
|
37
28
|
return networkServiceCall('GET', url, extraHeaders);
|
|
38
29
|
};
|
|
39
30
|
|
|
40
|
-
//
|
|
31
|
+
// POST API Call helper
|
|
41
32
|
export const postApiCall = (url, body = {}, extraHeaders = {}) => {
|
|
42
33
|
return networkServiceCall('POST', url, extraHeaders, body);
|
|
43
34
|
};
|
|
@@ -9,17 +9,22 @@
|
|
|
9
9
|
* @returns {number} Distance in meters.
|
|
10
10
|
*/
|
|
11
11
|
export const getDistanceInMeters = (lat1, lng1, lat2, lng2) => {
|
|
12
|
-
|
|
12
|
+
try {
|
|
13
|
+
const toRad = (value) => (value * Math.PI) / 180;
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
const R = 6371000; // Earth radius in meters
|
|
16
|
+
const dLat = toRad(lat2 - lat1);
|
|
17
|
+
const dLng = toRad(lng2 - lng1);
|
|
17
18
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
const a =
|
|
20
|
+
Math.sin(dLat / 2) ** 2 +
|
|
21
|
+
Math.cos(toRad(lat1)) * Math.cos(toRad(lat2)) * Math.sin(dLng / 2) ** 2;
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
return R * c;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.error("Error calculating distance:", error);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
25
30
|
};
|
|
@@ -4,27 +4,32 @@ import { Global } from "./Global";
|
|
|
4
4
|
* Decides which GIF should be shown based on animationState or currentStep
|
|
5
5
|
* @param {string} animationState - Current animation state
|
|
6
6
|
* @param {string} currentStep - Current step of verification
|
|
7
|
+
* @param {string} apiurl - Base API URL
|
|
8
|
+
* @param {string} imageurl - Path to image folder (default: 'file/getCommonFile/image/')
|
|
7
9
|
* @returns {any} - Gif image source or null
|
|
8
10
|
*/
|
|
9
|
-
export const getLoaderGif = (animationState, currentStep,apiurl,imageurl ='file/getCommonFile/image/') => {
|
|
10
|
-
|
|
11
|
-
`${apiurl}${imageurl}Face.gif`;
|
|
12
|
-
|
|
13
|
-
`${apiurl}${imageurl}Location.gif`;
|
|
11
|
+
export const getLoaderGif = (animationState, currentStep, apiurl, imageurl = 'file/getCommonFile/image/') => {
|
|
12
|
+
try {
|
|
13
|
+
const FaceGifUrl = `${apiurl}${imageurl}Face.gif`;
|
|
14
|
+
const LocationGifUrl = `${apiurl}${imageurl}Location.gif`;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
if (
|
|
17
|
+
animationState === Global.AnimationStates.faceScan ||
|
|
18
|
+
currentStep === 'Identity Verification'
|
|
19
|
+
) {
|
|
20
|
+
return { uri: FaceGifUrl };
|
|
21
|
+
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
if (
|
|
24
|
+
animationState === Global.AnimationStates.qrScan ||
|
|
25
|
+
currentStep === 'Location Verification'
|
|
26
|
+
) {
|
|
27
|
+
return { uri: LocationGifUrl };
|
|
28
|
+
}
|
|
28
29
|
|
|
29
|
-
|
|
30
|
+
return null;
|
|
31
|
+
} catch (error) {
|
|
32
|
+
console.error("Error in getLoaderGif:", error);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
30
35
|
};
|