react-native-biometric-verifier 0.0.33 → 0.0.38
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
CHANGED
|
@@ -24,7 +24,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
24
24
|
showCodeScanner = false,
|
|
25
25
|
isLoading = false,
|
|
26
26
|
frameProcessorFps = 1,
|
|
27
|
-
livenessLevel = 0,
|
|
27
|
+
livenessLevel = 0,
|
|
28
28
|
}) => {
|
|
29
29
|
const cameraRef = useRef(null);
|
|
30
30
|
const [cameraDevice, setCameraDevice] = useState(null);
|
|
@@ -42,6 +42,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
42
42
|
const [antiSpoofConfidence, setAntiSpoofConfidence] = useState(0);
|
|
43
43
|
const [isFaceCentered, setIsFaceCentered] = useState(false);
|
|
44
44
|
const [hasSingleFace, setHasSingleFace] = useState(false);
|
|
45
|
+
const [recognitionImageBase64, setRecognitionImageBase64] = useState(null);
|
|
45
46
|
|
|
46
47
|
const captured = useRef(false);
|
|
47
48
|
const isMounted = useRef(true);
|
|
@@ -60,6 +61,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
60
61
|
setAntiSpoofConfidence(0);
|
|
61
62
|
setIsFaceCentered(false);
|
|
62
63
|
setHasSingleFace(false);
|
|
64
|
+
setRecognitionImageBase64(null);
|
|
63
65
|
}, []);
|
|
64
66
|
|
|
65
67
|
const codeScanner = useCodeScanner({
|
|
@@ -76,38 +78,13 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
76
78
|
});
|
|
77
79
|
|
|
78
80
|
const onStableFaceDetected = useCallback(
|
|
79
|
-
async (faceRect) => {
|
|
81
|
+
async (faceRect, Result) => {
|
|
80
82
|
if (!isMounted.current) return;
|
|
81
83
|
if (captured.current) return;
|
|
82
|
-
|
|
83
84
|
captured.current = true;
|
|
84
85
|
setFaces([faceRect]);
|
|
85
|
-
|
|
86
86
|
try {
|
|
87
|
-
|
|
88
|
-
throw new Error('Camera ref not available');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const photo = await cameraRef.current.takePhoto({
|
|
92
|
-
flash: 'off',
|
|
93
|
-
qualityPrioritization: 'quality',
|
|
94
|
-
enableShutterSound: false,
|
|
95
|
-
skipMetadata: true,
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
if (!photo || !photo.path) {
|
|
99
|
-
throw new Error('Failed to capture photo - no path returned');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
const photopath = `file://${photo.path}`;
|
|
103
|
-
const fileName = photopath.substr(photopath.lastIndexOf('/') + 1);
|
|
104
|
-
const photoData = {
|
|
105
|
-
uri: photopath,
|
|
106
|
-
filename: fileName,
|
|
107
|
-
filetype: 'image/jpeg',
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
onCapture(photoData, faceRect);
|
|
87
|
+
onCapture(Result?.recognitionImageBase64);
|
|
111
88
|
} catch (e) {
|
|
112
89
|
console.error('Capture error:', e);
|
|
113
90
|
captured.current = false;
|
|
@@ -130,6 +107,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
130
107
|
setAntiSpoofConfidence(antiSpoofState.confidence || 0);
|
|
131
108
|
setIsFaceCentered(antiSpoofState.isFaceCentered || false);
|
|
132
109
|
setHasSingleFace(antiSpoofState.hasSingleFace || false);
|
|
110
|
+
setRecognitionImageBase64(antiSpoofState.recognitionImageBase64 || null);
|
|
133
111
|
}
|
|
134
112
|
|
|
135
113
|
if (count === 1) {
|
|
@@ -163,6 +141,11 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
163
141
|
const onAntiSpoofUpdate = useCallback((result) => {
|
|
164
142
|
if (!isMounted.current) return;
|
|
165
143
|
try {
|
|
144
|
+
// Update recognition image when available
|
|
145
|
+
if (result?.recognitionImageBase64) {
|
|
146
|
+
setRecognitionImageBase64(result.recognitionImageBase64);
|
|
147
|
+
}
|
|
148
|
+
|
|
166
149
|
// Animate live indicator when face becomes live
|
|
167
150
|
if (result?.isLive && !isFaceLive) {
|
|
168
151
|
Animated.spring(liveIndicatorAnim, {
|
|
@@ -109,6 +109,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
109
109
|
lastResult: null,
|
|
110
110
|
isLive: false,
|
|
111
111
|
confidence: 0,
|
|
112
|
+
recognitionImageBase64: null, // Added to store base64 image
|
|
112
113
|
},
|
|
113
114
|
|
|
114
115
|
// Face centering
|
|
@@ -147,6 +148,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
147
148
|
state.antiSpoof.lastResult = null;
|
|
148
149
|
state.antiSpoof.isLive = false;
|
|
149
150
|
state.antiSpoof.confidence = 0;
|
|
151
|
+
state.antiSpoof.recognitionImageBase64 = null; // Reset base64 image
|
|
150
152
|
state.flags.hasSingleFace = false;
|
|
151
153
|
state.centering.centeredFrames = 0;
|
|
152
154
|
state.flags.isFaceCentered = false;
|
|
@@ -300,6 +302,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
300
302
|
consecutiveLiveFrames: 0,
|
|
301
303
|
isFaceCentered: false,
|
|
302
304
|
hasSingleFace: false,
|
|
305
|
+
recognitionImageBase64: null,
|
|
303
306
|
});
|
|
304
307
|
return;
|
|
305
308
|
}
|
|
@@ -314,6 +317,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
314
317
|
consecutiveLiveFrames: 0,
|
|
315
318
|
isFaceCentered: false,
|
|
316
319
|
hasSingleFace: false,
|
|
320
|
+
recognitionImageBase64: null,
|
|
317
321
|
});
|
|
318
322
|
return;
|
|
319
323
|
}
|
|
@@ -360,19 +364,28 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
360
364
|
if (state.flags.isFaceCentered) {
|
|
361
365
|
try {
|
|
362
366
|
antiSpoofResult = faceAntiSpoofFrameProcessor?.(frame);
|
|
367
|
+
|
|
363
368
|
if (antiSpoofResult != null) {
|
|
364
369
|
state.antiSpoof.lastResult = antiSpoofResult;
|
|
365
370
|
|
|
366
371
|
const isLive = antiSpoofResult.isLive === true;
|
|
367
372
|
const confidence = antiSpoofResult.combinedScore || antiSpoofResult.neuralNetworkScore || 0;
|
|
373
|
+
const recognitionImageBase64 = antiSpoofResult.recognitionImageBase64 || null;
|
|
368
374
|
|
|
375
|
+
// Store the base64 image when face is live
|
|
369
376
|
if (isLive && confidence > ANTI_SPOOF_CONFIDENCE_THRESHOLD) {
|
|
370
377
|
state.antiSpoof.consecutiveLiveFrames = Math.min(
|
|
371
378
|
REQUIRED_CONSECUTIVE_LIVE_FRAMES,
|
|
372
379
|
state.antiSpoof.consecutiveLiveFrames + 1
|
|
373
380
|
);
|
|
381
|
+
// Store the base64 image when we have a live face
|
|
382
|
+
if (recognitionImageBase64) {
|
|
383
|
+
state.antiSpoof.recognitionImageBase64 = recognitionImageBase64;
|
|
384
|
+
}
|
|
374
385
|
} else {
|
|
375
386
|
state.antiSpoof.consecutiveLiveFrames = Math.max(0, state.antiSpoof.consecutiveLiveFrames - 1);
|
|
387
|
+
// Clear base64 image if face is not live
|
|
388
|
+
state.antiSpoof.recognitionImageBase64 = null;
|
|
376
389
|
}
|
|
377
390
|
state.antiSpoof.isLive = state.antiSpoof.consecutiveLiveFrames >= REQUIRED_CONSECUTIVE_LIVE_FRAMES;
|
|
378
391
|
state.antiSpoof.confidence = confidence;
|
|
@@ -385,16 +398,19 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
385
398
|
rawResult: antiSpoofResult,
|
|
386
399
|
consecutiveLiveFrames: state.antiSpoof.consecutiveLiveFrames,
|
|
387
400
|
isFaceCentered: state.flags.isFaceCentered,
|
|
401
|
+
recognitionImageBase64: state.antiSpoof.recognitionImageBase64,
|
|
388
402
|
});
|
|
389
403
|
}
|
|
390
404
|
}
|
|
391
405
|
} catch (antiSpoofError) {
|
|
392
406
|
// Silent error handling
|
|
407
|
+
console.log('Anti-spoof error:', antiSpoofError);
|
|
393
408
|
}
|
|
394
409
|
} else {
|
|
395
410
|
// Reset anti-spoof if face not centered
|
|
396
411
|
state.antiSpoof.consecutiveLiveFrames = 0;
|
|
397
412
|
state.antiSpoof.isLive = false;
|
|
413
|
+
state.antiSpoof.recognitionImageBase64 = null;
|
|
398
414
|
}
|
|
399
415
|
|
|
400
416
|
// Liveness logic - optimized
|
|
@@ -461,6 +477,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
461
477
|
consecutiveLiveFrames: state.antiSpoof.consecutiveLiveFrames,
|
|
462
478
|
isFaceCentered: state.flags.isFaceCentered,
|
|
463
479
|
hasSingleFace: true,
|
|
480
|
+
recognitionImageBase64: state.antiSpoof.recognitionImageBase64,
|
|
464
481
|
});
|
|
465
482
|
}
|
|
466
483
|
|
|
@@ -479,9 +496,13 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
479
496
|
|
|
480
497
|
if (shouldCapture) {
|
|
481
498
|
state.flags.captured = true;
|
|
499
|
+
// Include the base64 image in the stable face detection callback
|
|
482
500
|
runOnStable(
|
|
483
501
|
{ x, y, width, height },
|
|
484
|
-
|
|
502
|
+
{
|
|
503
|
+
...state.antiSpoof.lastResult,
|
|
504
|
+
recognitionImageBase64: state.antiSpoof.recognitionImageBase64
|
|
505
|
+
}
|
|
485
506
|
);
|
|
486
507
|
}
|
|
487
508
|
} else {
|
|
@@ -492,6 +513,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
492
513
|
state.flags.hasSingleFace = false;
|
|
493
514
|
state.centering.centeredFrames = 0;
|
|
494
515
|
state.flags.isFaceCentered = false;
|
|
516
|
+
state.antiSpoof.recognitionImageBase64 = null;
|
|
495
517
|
|
|
496
518
|
runOnFaces(detected.length, 0, state.liveness.step, false, {
|
|
497
519
|
isLive: false,
|
|
@@ -499,10 +521,12 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
499
521
|
consecutiveLiveFrames: 0,
|
|
500
522
|
isFaceCentered: false,
|
|
501
523
|
hasSingleFace: false,
|
|
524
|
+
recognitionImageBase64: null,
|
|
502
525
|
});
|
|
503
526
|
}
|
|
504
527
|
} catch (err) {
|
|
505
528
|
// Error boundary - ensure frame is released
|
|
529
|
+
console.log('Frame processor error:', err);
|
|
506
530
|
} finally {
|
|
507
531
|
frame.release?.();
|
|
508
532
|
}
|
|
@@ -527,6 +551,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
527
551
|
state.antiSpoof.lastResult = null;
|
|
528
552
|
state.antiSpoof.isLive = false;
|
|
529
553
|
state.antiSpoof.confidence = 0;
|
|
554
|
+
state.antiSpoof.recognitionImageBase64 = null; // Reset base64 image
|
|
530
555
|
state.flags.hasSingleFace = false;
|
|
531
556
|
state.centering.centeredFrames = 0;
|
|
532
557
|
state.flags.isFaceCentered = false;
|
|
@@ -561,6 +586,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
561
586
|
lastResult: null,
|
|
562
587
|
isLive: false,
|
|
563
588
|
confidence: 0,
|
|
589
|
+
recognitionImageBase64: null, // Reset base64 image
|
|
564
590
|
},
|
|
565
591
|
centering: {
|
|
566
592
|
centeredFrames: 0,
|
|
@@ -612,6 +638,7 @@ export const useFaceDetectionFrameProcessor = ({
|
|
|
612
638
|
lastResult: sharedState.value.antiSpoof.lastResult,
|
|
613
639
|
hasSingleFace: sharedState.value.flags.hasSingleFace,
|
|
614
640
|
isFaceCentered: sharedState.value.flags.isFaceCentered,
|
|
641
|
+
recognitionImageBase64: sharedState.value.antiSpoof.recognitionImageBase64,
|
|
615
642
|
},
|
|
616
643
|
};
|
|
617
644
|
};
|
package/src/index.js
CHANGED
|
@@ -216,7 +216,7 @@ const BiometricModal = React.memo(
|
|
|
216
216
|
|
|
217
217
|
// Face scan upload
|
|
218
218
|
const uploadFaceScan = useCallback(
|
|
219
|
-
async (
|
|
219
|
+
async (base64) => {
|
|
220
220
|
if (!validateApiUrl()) return;
|
|
221
221
|
const currentData = dataRef.current;
|
|
222
222
|
|
|
@@ -231,81 +231,66 @@ const BiometricModal = React.memo(
|
|
|
231
231
|
animationState: Global.AnimationStates.processing,
|
|
232
232
|
});
|
|
233
233
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
updateState({
|
|
239
|
-
loadingType: Global.LoadingTypes.imageProcessing,
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
base64 = await convertImageToBase64(selfie?.uri);
|
|
243
|
-
} catch (err) {
|
|
244
|
-
console.error("Image conversion failed:", err);
|
|
245
|
-
handleProcessError("Image conversion failed.", err);
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
234
|
+
if (!base64) {
|
|
235
|
+
handleProcessError("Failed to process image.");
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
248
238
|
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
}
|
|
239
|
+
try {
|
|
240
|
+
const body = { image: base64 };
|
|
241
|
+
const header = { faceid: currentData };
|
|
242
|
+
const buttonapi = `${apiurl}python/recognize`;
|
|
253
243
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
244
|
+
updateState({
|
|
245
|
+
loadingType: Global.LoadingTypes.networkRequest,
|
|
246
|
+
});
|
|
247
|
+
console.log("API URL:", buttonapi);
|
|
248
|
+
console.log("Sending request to API...", JSON.stringify(body));
|
|
249
|
+
const response = await networkServiceCall(
|
|
250
|
+
"POST",
|
|
251
|
+
buttonapi,
|
|
252
|
+
header,
|
|
253
|
+
body
|
|
254
|
+
);
|
|
255
|
+
console.log("API Response:", JSON.stringify(response));
|
|
256
|
+
if (response?.httpstatus === 200 && response?.data?.data) {
|
|
257
|
+
responseRef.current = response;
|
|
258
258
|
|
|
259
259
|
updateState({
|
|
260
|
-
|
|
260
|
+
employeeData: response.data?.data || null,
|
|
261
|
+
animationState: Global.AnimationStates.success,
|
|
262
|
+
isLoading: false,
|
|
263
|
+
loadingType: Global.LoadingTypes.none,
|
|
261
264
|
});
|
|
262
265
|
|
|
263
|
-
|
|
264
|
-
"POST",
|
|
265
|
-
buttonapi,
|
|
266
|
-
header,
|
|
267
|
-
body
|
|
268
|
-
);
|
|
269
|
-
|
|
270
|
-
if (response?.httpstatus === 200) {
|
|
271
|
-
responseRef.current = response;
|
|
272
|
-
|
|
273
|
-
updateState({
|
|
274
|
-
employeeData: response.data?.data || null,
|
|
275
|
-
animationState: Global.AnimationStates.success,
|
|
276
|
-
isLoading: false,
|
|
277
|
-
loadingType: Global.LoadingTypes.none,
|
|
278
|
-
});
|
|
266
|
+
notifyMessage("Identity verified successfully!", "success");
|
|
279
267
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
} else {
|
|
285
|
-
safeCallback(responseRef.current);
|
|
286
|
-
|
|
287
|
-
if (resetTimeoutRef.current) {
|
|
288
|
-
clearTimeout(resetTimeoutRef.current);
|
|
289
|
-
}
|
|
268
|
+
if (qrscan) {
|
|
269
|
+
setTimeout(() => startQRCodeScan(), 1200);
|
|
270
|
+
} else {
|
|
271
|
+
safeCallback(responseRef.current);
|
|
290
272
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}, 1200);
|
|
273
|
+
if (resetTimeoutRef.current) {
|
|
274
|
+
clearTimeout(resetTimeoutRef.current);
|
|
294
275
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
);
|
|
276
|
+
|
|
277
|
+
resetTimeoutRef.current = setTimeout(() => {
|
|
278
|
+
resetState();
|
|
279
|
+
}, 1200);
|
|
300
280
|
}
|
|
301
|
-
}
|
|
302
|
-
console.error("Network request failed:", error);
|
|
281
|
+
} else {
|
|
303
282
|
handleProcessError(
|
|
304
|
-
|
|
305
|
-
|
|
283
|
+
response?.data?.message ||
|
|
284
|
+
"Face not recognized. Please try again."
|
|
306
285
|
);
|
|
307
286
|
}
|
|
308
|
-
})
|
|
287
|
+
} catch (error) {
|
|
288
|
+
console.error("Network request failed:", error);
|
|
289
|
+
handleProcessError(
|
|
290
|
+
"Connection error. Please check your network.",
|
|
291
|
+
error
|
|
292
|
+
);
|
|
293
|
+
}
|
|
309
294
|
},
|
|
310
295
|
[
|
|
311
296
|
convertImageToBase64,
|