react-native-biometric-verifier 0.0.42 → 0.0.43

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-biometric-verifier",
3
- "version": "0.0.42",
3
+ "version": "0.0.43",
4
4
  "description": "A React Native module for biometric verification with face recognition and QR code scanning",
5
5
  "main": "src/index.js",
6
6
  "private": false,
@@ -7,45 +7,17 @@ import { Global } from '../utils/Global';
7
7
  const StepIndicator = ({ currentStep, qrscan }) => {
8
8
  return (
9
9
  <View style={styles.statusContainer}>
10
- {/* Identity Step */}
11
- <View style={styles.statusItem}>
12
- <Icon
13
- name="face"
14
- size={20}
15
- color={
16
- currentStep === "Identity Verification" ||
17
- currentStep === "Location Verification" ||
18
- currentStep === "Complete"
19
- ? Global.AppTheme.primary
20
- : Global.AppTheme.light
21
- }
22
- style={styles.statusIcon}
23
- />
24
- <Text
25
- style={[
26
- styles.statusText,
27
- (currentStep === "Identity Verification" ||
28
- currentStep === "Location Verification" ||
29
- currentStep === "Complete") && styles.statusTextActive,
30
- ]}
31
- >
32
- Identity
33
- </Text>
34
- </View>
35
-
36
- {/* Show Location only if qrscan = true */}
37
10
  {qrscan && (
38
11
  <>
39
- <View style={styles.statusSeparator} />
40
12
  <View style={styles.statusItem}>
41
13
  <Icon
42
14
  name="location-on"
43
15
  size={20}
44
16
  color={
45
17
  currentStep === "Location Verification" ||
46
- currentStep === "Complete"
47
- ? Global.AppTheme.primary
48
- : Global.AppTheme.light
18
+ currentStep === "Complete"
19
+ ? Global.AppTheme.light
20
+ : Global.AppTheme.primary
49
21
  }
50
22
  style={styles.statusIcon}
51
23
  />
@@ -56,11 +28,41 @@ const StepIndicator = ({ currentStep, qrscan }) => {
56
28
  currentStep === "Complete") && styles.statusTextActive,
57
29
  ]}
58
30
  >
59
- Location
31
+ QR
60
32
  </Text>
61
33
  </View>
34
+ <View style={styles.statusSeparator} />
62
35
  </>
63
36
  )}
37
+
38
+ {/* Identity Step */}
39
+ <View style={styles.statusItem}>
40
+ <Icon
41
+ name="face"
42
+ size={20}
43
+ color={
44
+ currentStep === "Identity Verification" ||
45
+ currentStep === "Location Verification" ||
46
+ currentStep === "Complete"
47
+ ? Global.AppTheme.light
48
+ : Global.AppTheme.primary
49
+ }
50
+ style={styles.statusIcon}
51
+ />
52
+ <Text
53
+ style={[
54
+ styles.statusText,
55
+ (currentStep === "Identity Verification" ||
56
+ currentStep === "Location Verification" ||
57
+ currentStep === "Complete") && styles.statusTextActive,
58
+ ]}
59
+ >
60
+ ID
61
+ </Text>
62
+ </View>
63
+
64
+ {/* Show Location only if qrscan = true */}
65
+
64
66
  </View>
65
67
  );
66
68
  };
package/src/index.js CHANGED
@@ -38,7 +38,7 @@ import CaptureImageWithoutEdit from "./components/CaptureImageWithoutEdit";
38
38
  import StepIndicator from "./components/StepIndicator";
39
39
 
40
40
  const BiometricModal = React.memo(
41
- ({ data, qrscan = false, callback, apiurl, onclose, frameProcessorFps, livenessLevel, fileurl, imageurl, navigation, MaxDistanceMeters = 100, duration = 100 }) => {
41
+ ({ data, depkey, qrscan = false, callback, apiurl, onclose, frameProcessorFps, livenessLevel, fileurl, imageurl, navigation, MaxDistanceMeters = 50, duration = 100 }) => {
42
42
  // Custom hooks
43
43
  const { countdown, startCountdown, resetCountdown, pauseCountdown, resumeCountdown } = useCountdown(duration);
44
44
  const { requestLocationPermission, getCurrentLocation } = useGeolocation();
@@ -48,13 +48,14 @@ const BiometricModal = React.memo(
48
48
 
49
49
  // State
50
50
  const [modalVisible, setModalVisible] = useState(false);
51
- const [cameraType, setCameraType] = useState("front");
51
+ const [cameraType, setCameraType] = useState("back"); // Start with back camera for QR scan
52
52
  const [state, setState] = useState({
53
53
  isLoading: false,
54
54
  loadingType: Global.LoadingTypes.none,
55
55
  currentStep: "Start",
56
56
  employeeData: null,
57
- animationState: Global.AnimationStates.faceScan,
57
+ animationState: Global.AnimationStates.qrScan, // Start with QR scan animation
58
+ qrData: null, // Store QR data for later use in face recognition
58
59
  });
59
60
 
60
61
  // Refs
@@ -63,7 +64,6 @@ const BiometricModal = React.memo(
63
64
  const responseRef = useRef(null);
64
65
  const processedRef = useRef(false);
65
66
  const resetTimeoutRef = useRef(null);
66
-
67
67
  // Animation values
68
68
  const iconScaleAnim = useRef(new Animated.Value(1)).current;
69
69
  const iconOpacityAnim = useRef(new Animated.Value(0)).current;
@@ -152,7 +152,8 @@ const BiometricModal = React.memo(
152
152
  loadingType: Global.LoadingTypes.none,
153
153
  currentStep: "Start",
154
154
  employeeData: null,
155
- animationState: Global.AnimationStates.faceScan,
155
+ animationState: Global.AnimationStates.qrScan,
156
+ qrData: null,
156
157
  });
157
158
 
158
159
  setModalVisible(false);
@@ -210,112 +211,7 @@ const BiometricModal = React.memo(
210
211
  return true;
211
212
  }, [apiurl, handleProcessError]);
212
213
 
213
- // Face scan upload
214
- const uploadFaceScan = useCallback(
215
- async (selfie) => {
216
- if (!validateApiUrl()) return;
217
- const currentData = dataRef.current;
218
-
219
- if (!currentData) {
220
- handleProcessError("Employee data not found.");
221
- return;
222
- }
223
-
224
- updateState({
225
- isLoading: true,
226
- loadingType: Global.LoadingTypes.faceRecognition,
227
- animationState: Global.AnimationStates.processing,
228
- });
229
-
230
- InteractionManager.runAfterInteractions(async () => {
231
- let base64;
232
-
233
- try {
234
- updateState({
235
- loadingType: Global.LoadingTypes.imageProcessing,
236
- });
237
-
238
- base64 = await convertImageToBase64(selfie?.uri);
239
- } catch (err) {
240
- console.error("Image conversion failed:", err);
241
- handleProcessError("Image conversion failed.", err);
242
- return;
243
- }
244
-
245
- if (!base64) {
246
- handleProcessError("Failed to process image.");
247
- return;
248
- }
249
-
250
- try {
251
- const body = { image: base64 };
252
- const header = { faceid: currentData };
253
- const buttonapi = `${apiurl}python/recognize`;
254
-
255
- updateState({
256
- loadingType: Global.LoadingTypes.networkRequest,
257
- });
258
-
259
- const response = await networkServiceCall(
260
- "POST",
261
- buttonapi,
262
- header,
263
- body
264
- );
265
-
266
- if (response?.httpstatus === 200) {
267
- responseRef.current = response;
268
-
269
- updateState({
270
- employeeData: response.data?.data || null,
271
- animationState: Global.AnimationStates.success,
272
- isLoading: false,
273
- loadingType: Global.LoadingTypes.none,
274
- });
275
-
276
- notifyMessage("Identity verified successfully!", "success");
277
-
278
- if (qrscan) {
279
- setTimeout(() => startQRCodeScan(), 1200);
280
- } else {
281
- safeCallback(responseRef.current);
282
-
283
- if (resetTimeoutRef.current) {
284
- clearTimeout(resetTimeoutRef.current);
285
- }
286
-
287
- resetTimeoutRef.current = setTimeout(() => {
288
- resetState();
289
- }, 1200);
290
- }
291
- } else {
292
- handleProcessError(
293
- response?.data?.message ||
294
- "Face not recognized. Please try again."
295
- );
296
- }
297
- } catch (error) {
298
- console.error("Network request failed:", error);
299
- handleProcessError(
300
- "Connection error. Please check your network.",
301
- error
302
- );
303
- }
304
- });
305
- },
306
- [
307
- convertImageToBase64,
308
- notifyMessage,
309
- qrscan,
310
- resetState,
311
- updateState,
312
- validateApiUrl,
313
- safeCallback,
314
- handleProcessError
315
- ]
316
- );
317
-
318
- // QR code processing
214
+ // QR code processing - FIRST STEP
319
215
  const handleQRScanned = useCallback(
320
216
  async (qrCodeData) => {
321
217
  if (!validateApiUrl()) return;
@@ -352,14 +248,12 @@ const BiometricModal = React.memo(
352
248
 
353
249
  const location = await getCurrentLocation();
354
250
 
355
- const [latStr, lngStr] = qrString.split(",");
251
+ const [latStr, lngStr, qrkey] = qrString.split(",");
356
252
  const lat = parseFloat(latStr);
357
253
  const lng = parseFloat(lngStr);
358
254
  const validCoords = !isNaN(lat) && !isNaN(lng);
359
- const validDev =
360
- !isNaN(location?.latitude) && !isNaN(location?.longitude);
361
-
362
- if (validCoords && validDev) {
255
+ const validDev = !isNaN(location?.latitude) && !isNaN(location?.longitude);
256
+ if (validCoords && validDev && qrkey === depkey) {
363
257
  updateState({
364
258
  loadingType: Global.LoadingTypes.calculateDistance,
365
259
  });
@@ -370,7 +264,6 @@ const BiometricModal = React.memo(
370
264
  location.latitude,
371
265
  location.longitude
372
266
  );
373
-
374
267
  if (distance <= MaxDistanceMeters) {
375
268
  const locationDetails = {
376
269
  qrLocation: {
@@ -387,27 +280,24 @@ const BiometricModal = React.memo(
387
280
  verifiedAt: new Date().toISOString(),
388
281
  };
389
282
 
283
+ // Store location details and QR data for face recognition
390
284
  responseRef.current = {
391
- ...(responseRef.current || {}), // existing faceData
392
285
  location: locationDetails,
393
286
  };
394
287
 
395
- safeCallback(responseRef.current);
396
-
397
- notifyMessage("Location verified successfully!", "success");
398
-
288
+ // Store QR data in state for face recognition step
399
289
  updateState({
290
+ qrData: qrString,
400
291
  animationState: Global.AnimationStates.success,
401
292
  isLoading: false,
402
293
  loadingType: Global.LoadingTypes.none,
403
294
  });
404
295
 
405
- if (resetTimeoutRef.current) {
406
- clearTimeout(resetTimeoutRef.current);
407
- }
296
+ notifyMessage("Location verified successfully!", "success");
408
297
 
409
- resetTimeoutRef.current = setTimeout(() => {
410
- resetState();
298
+ // Start face recognition after a brief delay
299
+ setTimeout(() => {
300
+ startFaceRecognition();
411
301
  }, 1200);
412
302
  }
413
303
  else {
@@ -430,49 +320,165 @@ const BiometricModal = React.memo(
430
320
  getCurrentLocation,
431
321
  notifyMessage,
432
322
  requestLocationPermission,
323
+ updateState,
324
+ validateApiUrl,
325
+ handleProcessError
326
+ ]
327
+ );
328
+
329
+ // Face scan upload - SECOND STEP
330
+ const uploadFaceScan = useCallback(
331
+ async (selfie) => {
332
+ if (!validateApiUrl()) return;
333
+
334
+ // Check if QR scan was completed successfully
335
+ if (!state.qrData) {
336
+ handleProcessError("Please complete QR scan first.");
337
+ return;
338
+ }
339
+
340
+ const currentData = dataRef.current;
341
+
342
+ if (!currentData) {
343
+ handleProcessError("Employee data not found.");
344
+ return;
345
+ }
346
+
347
+ updateState({
348
+ isLoading: true,
349
+ loadingType: Global.LoadingTypes.faceRecognition,
350
+ animationState: Global.AnimationStates.processing,
351
+ });
352
+
353
+ InteractionManager.runAfterInteractions(async () => {
354
+ let base64;
355
+
356
+ try {
357
+ updateState({
358
+ loadingType: Global.LoadingTypes.imageProcessing,
359
+ });
360
+
361
+ base64 = await convertImageToBase64(selfie?.uri);
362
+ } catch (err) {
363
+ console.error("Image conversion failed:", err);
364
+ handleProcessError("Image conversion failed.", err);
365
+ return;
366
+ }
367
+
368
+ if (!base64) {
369
+ handleProcessError("Failed to process image.");
370
+ return;
371
+ }
372
+
373
+ try {
374
+ const body = { image: base64 };
375
+ const header = { faceid: currentData };
376
+ const buttonapi = `${apiurl}python/recognize`;
377
+
378
+ updateState({
379
+ loadingType: Global.LoadingTypes.networkRequest,
380
+ });
381
+
382
+ const response = await networkServiceCall(
383
+ "POST",
384
+ buttonapi,
385
+ header,
386
+ body
387
+ );
388
+
389
+ if (response?.httpstatus === 200) {
390
+ // Combine face recognition response with QR location data
391
+ responseRef.current = {
392
+ ...responseRef.current, // Contains location data from QR scan
393
+ ...response.data?.data || {},
394
+ faceRecognition: response.data?.data || null,
395
+ };
396
+
397
+ updateState({
398
+ employeeData: response.data?.data || null,
399
+ animationState: Global.AnimationStates.success,
400
+ isLoading: false,
401
+ loadingType: Global.LoadingTypes.none,
402
+ });
403
+
404
+ notifyMessage("Identity verified successfully!", "success");
405
+
406
+ // Call the callback with combined data
407
+ safeCallback(responseRef.current);
408
+
409
+ if (resetTimeoutRef.current) {
410
+ clearTimeout(resetTimeoutRef.current);
411
+ }
412
+
413
+ resetTimeoutRef.current = setTimeout(() => {
414
+ resetState();
415
+ }, 1200);
416
+ } else {
417
+ handleProcessError(
418
+ response?.data?.message ||
419
+ "Face not recognized. Please try again."
420
+ );
421
+ }
422
+ } catch (error) {
423
+ console.error("Network request failed:", error);
424
+ handleProcessError(
425
+ "Connection error. Please check your network.",
426
+ error
427
+ );
428
+ }
429
+ });
430
+ },
431
+ [
432
+ convertImageToBase64,
433
+ notifyMessage,
433
434
  resetState,
434
435
  updateState,
435
436
  validateApiUrl,
436
437
  safeCallback,
437
- handleProcessError
438
+ handleProcessError,
439
+ state.qrData
438
440
  ]
439
441
  );
440
442
 
441
443
  // Image capture handler
442
444
  const handleImageCapture = useCallback(
443
445
  async (capturedData) => {
444
- if (state.currentStep === "Identity Verification") {
445
- uploadFaceScan(capturedData);
446
- } else if (state.currentStep === "Location Verification") {
446
+ if (state.currentStep === "Location Verification") {
447
447
  handleQRScanned(capturedData);
448
+ } else if (state.currentStep === "Identity Verification") {
449
+ uploadFaceScan(capturedData);
448
450
  }
449
451
  },
450
452
  [state.currentStep, uploadFaceScan, handleQRScanned]
451
453
  );
452
454
 
453
- // Start face scan
454
- const handleStartFaceScan = useCallback(() => {
455
+ // Start QR code scan - FIRST STEP
456
+ const handleStartQRScan = useCallback(() => {
455
457
  updateState({
456
- currentStep: "Identity Verification",
457
- animationState: Global.AnimationStates.faceScan,
458
+ currentStep: "Location Verification",
459
+ animationState: Global.AnimationStates.qrScan,
458
460
  });
459
- setCameraType("front");
461
+ setCameraType("back");
460
462
  }, [updateState]);
461
463
 
462
- // Start QR code scan
463
- const startQRCodeScan = useCallback(() => {
464
+ // Start face recognition - SECOND STEP
465
+ const startFaceRecognition = useCallback(() => {
464
466
  updateState({
465
- currentStep: "Location Verification",
466
- animationState: Global.AnimationStates.qrScan,
467
+ currentStep: "Identity Verification",
468
+ animationState: Global.AnimationStates.faceScan,
467
469
  });
468
- setCameraType("back");
470
+ setCameraType("front");
469
471
  }, [updateState]);
470
472
 
471
473
  // Start the verification process
472
474
  const startProcess = useCallback(() => {
473
475
  startCountdown(handleCountdownFinish);
474
- handleStartFaceScan();
475
- }, [handleCountdownFinish, handleStartFaceScan, startCountdown]);
476
+ if (qrscan) {
477
+ handleStartQRScan();
478
+ } else {
479
+ startFaceRecognition();
480
+ }
481
+ }, [handleCountdownFinish, handleStartQRScan, startCountdown, startFaceRecognition]);
476
482
 
477
483
  // Open modal when data is received
478
484
  useEffect(() => {
@@ -481,7 +487,7 @@ const BiometricModal = React.memo(
481
487
  setModalVisible(true);
482
488
  startProcess();
483
489
  }
484
- }, [data, modalVisible, startProcess]);
490
+ }, [data, modalVisible, startProcess, qrscan]);
485
491
 
486
492
  // Determine if camera should be shown
487
493
  const shouldShowCamera =
@@ -535,7 +541,10 @@ const BiometricModal = React.memo(
535
541
  </View>
536
542
 
537
543
  <View style={styles.topContainerstep}>
538
- <StepIndicator currentStep={state.currentStep} qrscan={qrscan} />
544
+ <StepIndicator
545
+ currentStep={state.currentStep}
546
+ qrscan={qrscan}
547
+ />
539
548
  </View>
540
549
 
541
550
  {state.employeeData && (