react-native-biometric-verifier 0.0.7 → 0.0.8
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/index.js +142 -147
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -45,6 +45,23 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
|
|
|
45
45
|
|
|
46
46
|
const dataRef = useRef(data);
|
|
47
47
|
const mountedRef = useRef(true);
|
|
48
|
+
const responseRef = useRef(null);
|
|
49
|
+
|
|
50
|
+
const safeCallback = useCallback(
|
|
51
|
+
(response) => {
|
|
52
|
+
if (typeof callback === 'function') {
|
|
53
|
+
try {
|
|
54
|
+
callback(response);
|
|
55
|
+
} catch (err) {
|
|
56
|
+
console.error('Callback execution failed:', err);
|
|
57
|
+
notifyMessage('Unexpected error while processing callback.', 'error');
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
console.log('Biometric Verification Response:', response);
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
[callback, notifyMessage]
|
|
64
|
+
);
|
|
48
65
|
|
|
49
66
|
useEffect(() => {
|
|
50
67
|
return () => {
|
|
@@ -58,7 +75,7 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
|
|
|
58
75
|
|
|
59
76
|
const updateState = useCallback((newState) => {
|
|
60
77
|
if (mountedRef.current) {
|
|
61
|
-
setState(prev => ({ ...prev, ...newState }));
|
|
78
|
+
setState((prev) => ({ ...prev, ...newState }));
|
|
62
79
|
}
|
|
63
80
|
}, []);
|
|
64
81
|
|
|
@@ -72,100 +89,95 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
|
|
|
72
89
|
resetCountdown();
|
|
73
90
|
}, [resetCountdown, updateState]);
|
|
74
91
|
|
|
92
|
+
const handleProcessError = useCallback(
|
|
93
|
+
(message, errorObj = null) => {
|
|
94
|
+
if (errorObj) console.error(message, errorObj);
|
|
95
|
+
notifyMessage(message, 'error');
|
|
96
|
+
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
97
|
+
setTimeout(resetState, 1200);
|
|
98
|
+
},
|
|
99
|
+
[notifyMessage, resetState, updateState]
|
|
100
|
+
);
|
|
101
|
+
|
|
75
102
|
const handleCountdownFinish = useCallback(() => {
|
|
76
|
-
|
|
77
|
-
updateState({
|
|
78
|
-
modalVisible: false,
|
|
79
|
-
animationState: ANIMATION_STATES.ERROR,
|
|
80
|
-
});
|
|
81
|
-
resetState();
|
|
103
|
+
handleProcessError('Time is up! Please try again.');
|
|
104
|
+
updateState({ modalVisible: false });
|
|
82
105
|
if (navigation.canGoBack()) navigation.goBack();
|
|
83
|
-
}, [
|
|
106
|
+
}, [handleProcessError, navigation, updateState]);
|
|
84
107
|
|
|
85
108
|
const validateApiUrl = useCallback(() => {
|
|
86
109
|
if (!apiurl || typeof apiurl !== 'string') {
|
|
87
|
-
|
|
88
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
110
|
+
handleProcessError('Invalid API URL configuration.');
|
|
89
111
|
return false;
|
|
90
112
|
}
|
|
91
113
|
return true;
|
|
92
|
-
}, [apiurl,
|
|
93
|
-
|
|
94
|
-
const uploadFaceScan = useCallback(async (selfie) => {
|
|
95
|
-
if (!validateApiUrl()) return;
|
|
96
|
-
|
|
97
|
-
const currentData = dataRef.current;
|
|
98
|
-
if (!currentData) {
|
|
99
|
-
notifyMessage('Employee data not found.', 'error');
|
|
100
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
101
|
-
return;
|
|
102
|
-
}
|
|
114
|
+
}, [apiurl, handleProcessError]);
|
|
103
115
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
} catch (err) {
|
|
108
|
-
notifyMessage('Image conversion failed.', 'error');
|
|
109
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (!base64) {
|
|
114
|
-
notifyMessage('Failed to process image.', 'error');
|
|
115
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
updateState({
|
|
120
|
-
isLoading: true,
|
|
121
|
-
animationState: ANIMATION_STATES.PROCESSING,
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
try {
|
|
125
|
-
const body = { image: base64 };
|
|
126
|
-
const header = { faceid: currentData };
|
|
127
|
-
const buttonapi = `${apiurl}python/recognize`;
|
|
116
|
+
const uploadFaceScan = useCallback(
|
|
117
|
+
async (selfie) => {
|
|
118
|
+
if (!validateApiUrl()) return;
|
|
128
119
|
|
|
129
|
-
|
|
120
|
+
const currentData = dataRef.current;
|
|
121
|
+
if (!currentData) {
|
|
122
|
+
handleProcessError('Employee data not found.');
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
130
125
|
|
|
131
|
-
|
|
126
|
+
let base64;
|
|
127
|
+
try {
|
|
128
|
+
base64 = await convertImageToBase64(selfie?.uri);
|
|
129
|
+
} catch (err) {
|
|
130
|
+
handleProcessError('Image conversion failed.', err);
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
132
133
|
|
|
133
|
-
if (
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
animationState: ANIMATION_STATES.SUCCESS,
|
|
138
|
-
});
|
|
134
|
+
if (!base64) {
|
|
135
|
+
handleProcessError('Failed to process image.');
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
139
138
|
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
updateState({
|
|
140
|
+
isLoading: true,
|
|
141
|
+
animationState: ANIMATION_STATES.PROCESSING,
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const body = { image: base64 };
|
|
146
|
+
const header = { faceid: currentData };
|
|
147
|
+
const buttonapi = `${apiurl}python/recognize`;
|
|
148
|
+
console.log('buttonapi', buttonapi);
|
|
149
|
+
const response = await networkServiceCall('POST', buttonapi, header, body);
|
|
150
|
+
|
|
151
|
+
if (response?.httpstatus === 200) {
|
|
152
|
+
responseRef.current = response;
|
|
153
|
+
updateState({
|
|
154
|
+
employeeData: response.data?.data || null,
|
|
155
|
+
animationState: ANIMATION_STATES.SUCCESS,
|
|
156
|
+
});
|
|
157
|
+
notifyMessage('Identity verified successfully!', 'success');
|
|
158
|
+
|
|
159
|
+
if (qrscan) {
|
|
160
|
+
setTimeout(() => startQRCodeScan(), 1500);
|
|
161
|
+
} else {
|
|
162
|
+
setTimeout(() => {
|
|
163
|
+
safeCallback(responseRef.current);
|
|
164
|
+
updateState({ modalVisible: false });
|
|
165
|
+
resetState();
|
|
166
|
+
}, 1500);
|
|
167
|
+
}
|
|
142
168
|
} else {
|
|
143
|
-
|
|
144
|
-
try
|
|
145
|
-
|
|
146
|
-
} catch (err) {
|
|
147
|
-
console.error("Callback execution failed:", err);
|
|
148
|
-
notifyMessage('Unexpected error after verification.', 'error');
|
|
149
|
-
}
|
|
150
|
-
updateState({ modalVisible: false });
|
|
151
|
-
resetState();
|
|
152
|
-
}, 1500);
|
|
169
|
+
handleProcessError(
|
|
170
|
+
response?.data?.error?.message || 'Face not recognized. Please try again.'
|
|
171
|
+
);
|
|
153
172
|
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
);
|
|
159
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
173
|
+
} catch (error) {
|
|
174
|
+
handleProcessError('Connection error. Please check your network.', error);
|
|
175
|
+
} finally {
|
|
176
|
+
updateState({ isLoading: false });
|
|
160
177
|
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
165
|
-
} finally {
|
|
166
|
-
updateState({ isLoading: false });
|
|
167
|
-
}
|
|
168
|
-
}, [convertImageToBase64, notifyMessage, qrscan, resetState, updateState, callback, validateApiUrl]);
|
|
178
|
+
},
|
|
179
|
+
[convertImageToBase64, notifyMessage, qrscan, resetState, updateState, validateApiUrl, safeCallback]
|
|
180
|
+
);
|
|
169
181
|
|
|
170
182
|
const handleStartFaceScan = useCallback(() => {
|
|
171
183
|
updateState({
|
|
@@ -192,74 +204,62 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
|
|
|
192
204
|
});
|
|
193
205
|
}, [navigation, updateState]);
|
|
194
206
|
|
|
195
|
-
const handleQRScanned = useCallback(
|
|
196
|
-
|
|
207
|
+
const handleQRScanned = useCallback(
|
|
208
|
+
async (qrCodeData) => {
|
|
209
|
+
if (!validateApiUrl()) return;
|
|
197
210
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
211
|
+
updateState({
|
|
212
|
+
animationState: ANIMATION_STATES.PROCESSING,
|
|
213
|
+
isLoading: true,
|
|
214
|
+
});
|
|
202
215
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
const qrString = typeof qrCodeData === 'object' ? qrCodeData?.data : qrCodeData;
|
|
212
|
-
if (!qrString || typeof qrString !== 'string') {
|
|
213
|
-
notifyMessage('Invalid QR code. Please try again.', 'error');
|
|
214
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
216
|
+
try {
|
|
217
|
+
const hasPermission = await requestLocationPermission();
|
|
218
|
+
if (!hasPermission) {
|
|
219
|
+
handleProcessError('Location permission not granted.');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
217
222
|
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
const qrString = typeof qrCodeData === 'object' ? qrCodeData?.data : qrCodeData;
|
|
224
|
+
if (!qrString || typeof qrString !== 'string') {
|
|
225
|
+
handleProcessError('Invalid QR code. Please try again.');
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
222
228
|
|
|
223
|
-
|
|
224
|
-
|
|
229
|
+
const location = await getCurrentLocation();
|
|
230
|
+
const [latStr, lngStr] = qrString.split(',');
|
|
231
|
+
const lat = parseFloat(latStr);
|
|
232
|
+
const lng = parseFloat(lngStr);
|
|
225
233
|
|
|
226
|
-
|
|
227
|
-
const
|
|
234
|
+
const validCoords = !isNaN(lat) && !isNaN(lng);
|
|
235
|
+
const validDev = !isNaN(location?.latitude) && !isNaN(location?.longitude);
|
|
228
236
|
|
|
229
|
-
if (
|
|
230
|
-
|
|
231
|
-
callback?.(dataRef.current);
|
|
232
|
-
} catch (err) {
|
|
233
|
-
console.error("Callback execution failed:", err);
|
|
234
|
-
notifyMessage('Unexpected error during location verification.', 'error');
|
|
235
|
-
}
|
|
237
|
+
if (validCoords && validDev) {
|
|
238
|
+
const distance = getDistanceInMeters(lat, lng, location.latitude, location.longitude);
|
|
236
239
|
|
|
237
|
-
|
|
238
|
-
|
|
240
|
+
if (distance <= MAX_DISTANCE_METERS) {
|
|
241
|
+
safeCallback(responseRef.current);
|
|
242
|
+
notifyMessage('Location verified successfully!', 'success');
|
|
243
|
+
updateState({ animationState: ANIMATION_STATES.SUCCESS });
|
|
239
244
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
245
|
+
setTimeout(() => {
|
|
246
|
+
updateState({ modalVisible: false });
|
|
247
|
+
resetState();
|
|
248
|
+
}, 1500);
|
|
249
|
+
} else {
|
|
250
|
+
handleProcessError(`Location mismatch (${distance.toFixed(0)}m away).`);
|
|
251
|
+
}
|
|
244
252
|
} else {
|
|
245
|
-
|
|
246
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
247
|
-
resetState();
|
|
253
|
+
handleProcessError('Invalid coordinates in QR code.');
|
|
248
254
|
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
255
|
+
} catch (error) {
|
|
256
|
+
handleProcessError('Unable to verify location. Please try again.', error);
|
|
257
|
+
} finally {
|
|
258
|
+
updateState({ isLoading: false });
|
|
253
259
|
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
updateState({ animationState: ANIMATION_STATES.ERROR });
|
|
258
|
-
resetState();
|
|
259
|
-
} finally {
|
|
260
|
-
updateState({ isLoading: false });
|
|
261
|
-
}
|
|
262
|
-
}, [callback, getCurrentLocation, notifyMessage, requestLocationPermission, resetState, updateState, validateApiUrl]);
|
|
260
|
+
},
|
|
261
|
+
[getCurrentLocation, notifyMessage, requestLocationPermission, resetState, updateState, validateApiUrl, safeCallback]
|
|
262
|
+
);
|
|
263
263
|
|
|
264
264
|
const startProcess = useCallback(() => {
|
|
265
265
|
startCountdown(COUNTDOWN_DURATION, handleCountdownFinish);
|
|
@@ -303,10 +303,7 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
|
|
|
303
303
|
<StateIndicator state={state.animationState} size={120} />
|
|
304
304
|
|
|
305
305
|
{state.employeeData && (
|
|
306
|
-
<EmployeeCard
|
|
307
|
-
employeeData={state.employeeData}
|
|
308
|
-
apiurl={apiurl}
|
|
309
|
-
/>
|
|
306
|
+
<EmployeeCard employeeData={state.employeeData} apiurl={apiurl} />
|
|
310
307
|
)}
|
|
311
308
|
|
|
312
309
|
<Notification
|
|
@@ -315,14 +312,12 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
|
|
|
315
312
|
slideAnim={slideAnim}
|
|
316
313
|
/>
|
|
317
314
|
|
|
318
|
-
<CountdownTimer
|
|
319
|
-
duration={COUNTDOWN_DURATION}
|
|
320
|
-
currentTime={countdown}
|
|
321
|
-
/>
|
|
315
|
+
<CountdownTimer duration={COUNTDOWN_DURATION} currentTime={countdown} />
|
|
322
316
|
|
|
323
|
-
{state.isLoading &&
|
|
324
|
-
|
|
325
|
-
|
|
317
|
+
{state.isLoading &&
|
|
318
|
+
getLoaderGif(state.animationState, state.currentStep, apiurl) && (
|
|
319
|
+
<Loader source={getLoaderGif(state.animationState, state.currentStep, apiurl)} />
|
|
320
|
+
)}
|
|
326
321
|
</View>
|
|
327
322
|
</Modal>
|
|
328
323
|
);
|