react-native-biometric-verifier 0.0.43 → 0.0.44

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.43",
3
+ "version": "0.0.44",
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,
@@ -1,32 +1,23 @@
1
1
  import React, { useEffect, useRef } from 'react';
2
2
  import { Animated, Text, Platform, StyleSheet } from 'react-native';
3
3
  import Icon from 'react-native-vector-icons/MaterialIcons';
4
- import PropTypes from 'prop-types';
5
4
  import { Global } from '../utils/Global';
6
5
 
7
- export const Notification = ({ notification, fadeAnim, slideAnim }) => {
8
- if (!notification || typeof notification !== 'object') {
9
- console.warn('Notification: Invalid or missing notification object');
10
- return null;
11
- }
6
+ export const Notification = ({ notification = {}, fadeAnim, slideAnim }) => {
7
+ const { visible = false, type = 'info', message = '' } = notification;
12
8
 
13
- const { visible, type = 'info', message = '' } = notification;
14
- if (!visible) return null;
9
+ // Animations (must ALWAYS exist)
10
+ const scaleAnim = useRef(new Animated.Value(1)).current;
11
+ const shakeAnim = useRef(new Animated.Value(0)).current;
15
12
 
16
- // Icon and color mapping
17
13
  const iconMap = {
18
14
  success: { name: 'check-circle', color: Global.AppTheme.success },
19
15
  error: { name: 'error', color: Global.AppTheme.error },
20
16
  info: { name: 'info', color: Global.AppTheme.info },
21
17
  };
22
18
 
23
- const { name: iconName, color: iconColor } = iconMap[type] || iconMap.info;
24
-
25
- // Heartbeat animation (scale in/out)
26
- const scaleAnim = useRef(new Animated.Value(1)).current;
27
-
28
- // Shake animation (rotation wiggle)
29
- const shakeAnim = useRef(new Animated.Value(0)).current;
19
+ const { name: iconName, color: iconColor } =
20
+ iconMap[type] || iconMap.info;
30
21
 
31
22
  useEffect(() => {
32
23
  const heartbeat = Animated.loop(
@@ -78,20 +69,20 @@ export const Notification = ({ notification, fadeAnim, slideAnim }) => {
78
69
  heartbeat.stop();
79
70
  shake.stop();
80
71
  };
81
- }, [visible, type, scaleAnim, shakeAnim]);
72
+ }, [visible, type]);
82
73
 
83
- // Shake rotation mapping (small wiggle)
84
- const shakeInterpolate = shakeAnim.interpolate({
74
+ const shakeRotate = shakeAnim.interpolate({
85
75
  inputRange: [-1, 1],
86
76
  outputRange: ['-10deg', '10deg'],
87
77
  });
88
78
 
89
79
  return (
90
80
  <Animated.View
81
+ pointerEvents={visible ? 'auto' : 'none'}
91
82
  style={[
92
83
  styles.container,
93
84
  {
94
- opacity: fadeAnim instanceof Animated.Value ? fadeAnim : 1,
85
+ opacity: visible ? 1 : 0,
95
86
  transform: [
96
87
  { translateY: slideAnim instanceof Animated.Value ? slideAnim : 0 },
97
88
  ],
@@ -102,12 +93,13 @@ export const Notification = ({ notification, fadeAnim, slideAnim }) => {
102
93
  style={{
103
94
  transform: [
104
95
  { scale: scaleAnim },
105
- ...(type === 'error' ? [{ rotate: shakeInterpolate }] : []),
96
+ ...(type === 'error' ? [{ rotate: shakeRotate }] : []),
106
97
  ],
107
98
  }}
108
99
  >
109
- <Icon name={iconName} size={50} color={iconColor} style={styles.icon} />
100
+ <Icon name={iconName} size={50} color={iconColor} />
110
101
  </Animated.View>
102
+
111
103
  <Text style={styles.message}>
112
104
  {message || 'No message provided'}
113
105
  </Text>
@@ -142,20 +134,4 @@ const styles = StyleSheet.create({
142
134
  },
143
135
  });
144
136
 
145
- Notification.propTypes = {
146
- notification: PropTypes.shape({
147
- visible: PropTypes.bool.isRequired,
148
- type: PropTypes.oneOf(['success', 'error', 'info']),
149
- message: PropTypes.string,
150
- }),
151
- fadeAnim: PropTypes.instanceOf(Animated.Value),
152
- slideAnim: PropTypes.instanceOf(Animated.Value),
153
- };
154
-
155
- Notification.defaultProps = {
156
- notification: { visible: false, type: 'info', message: '' },
157
- fadeAnim: new Animated.Value(1),
158
- slideAnim: new Animated.Value(0),
159
- };
160
-
161
137
  export default Notification;
package/src/index.js CHANGED
@@ -3,7 +3,8 @@ import React, {
3
3
  useEffect,
4
4
  useRef,
5
5
  useCallback,
6
- useMemo,
6
+ useImperativeHandle,
7
+ forwardRef,
7
8
  } from "react";
8
9
  import {
9
10
  View,
@@ -37,8 +38,21 @@ import { Notification } from "./components/Notification";
37
38
  import CaptureImageWithoutEdit from "./components/CaptureImageWithoutEdit";
38
39
  import StepIndicator from "./components/StepIndicator";
39
40
 
40
- const BiometricModal = React.memo(
41
- ({ data, depkey, qrscan = false, callback, apiurl, onclose, frameProcessorFps, livenessLevel, fileurl, imageurl, navigation, MaxDistanceMeters = 50, duration = 100 }) => {
41
+ const BiometricModal = forwardRef(({
42
+ data,
43
+ depkey,
44
+ qrscan = false,
45
+ callback,
46
+ apiurl,
47
+ onclose,
48
+ frameProcessorFps,
49
+ livenessLevel,
50
+ fileurl,
51
+ imageurl,
52
+ navigation,
53
+ MaxDistanceMeters = 50,
54
+ duration = 100
55
+ }, ref) => {
42
56
  // Custom hooks
43
57
  const { countdown, startCountdown, resetCountdown, pauseCountdown, resumeCountdown } = useCountdown(duration);
44
58
  const { requestLocationPermission, getCurrentLocation } = useGeolocation();
@@ -68,6 +82,14 @@ const BiometricModal = React.memo(
68
82
  const iconScaleAnim = useRef(new Animated.Value(1)).current;
69
83
  const iconOpacityAnim = useRef(new Animated.Value(0)).current;
70
84
 
85
+ // Expose methods to parent via ref
86
+ useImperativeHandle(ref, () => ({
87
+ reset: resetState,
88
+ start: startProcess,
89
+ close: resetState,
90
+ getStatus: () => state,
91
+ }));
92
+
71
93
  // Cleanup on unmount
72
94
  useEffect(() => {
73
95
  return () => {
@@ -145,7 +167,9 @@ const BiometricModal = React.memo(
145
167
 
146
168
  // Reset state helper
147
169
  const resetState = useCallback(() => {
148
- onclose(false);
170
+ if (onclose) {
171
+ onclose(false);
172
+ }
149
173
 
150
174
  setState({
151
175
  isLoading: false,
@@ -165,7 +189,7 @@ const BiometricModal = React.memo(
165
189
  clearTimeout(resetTimeoutRef.current);
166
190
  resetTimeoutRef.current = null;
167
191
  }
168
- }, [resetCountdown, clearNotification]);
192
+ }, [resetCountdown, clearNotification, onclose]);
169
193
 
170
194
  // Error handler
171
195
  const handleProcessError = useCallback(
@@ -196,7 +220,7 @@ const BiometricModal = React.memo(
196
220
  const handleCountdownFinish = useCallback(() => {
197
221
  handleProcessError("Time is up! Please try again.");
198
222
 
199
- if (navigation.canGoBack()) {
223
+ if (navigation?.canGoBack?.()) {
200
224
  navigation.goBack();
201
225
  }
202
226
  }, [handleProcessError, navigation]);
@@ -322,7 +346,9 @@ const BiometricModal = React.memo(
322
346
  requestLocationPermission,
323
347
  updateState,
324
348
  validateApiUrl,
325
- handleProcessError
349
+ handleProcessError,
350
+ depkey,
351
+ MaxDistanceMeters
326
352
  ]
327
353
  );
328
354
 
@@ -436,7 +462,8 @@ const BiometricModal = React.memo(
436
462
  validateApiUrl,
437
463
  safeCallback,
438
464
  handleProcessError,
439
- state.qrData
465
+ state.qrData,
466
+ apiurl
440
467
  ]
441
468
  );
442
469
 
@@ -444,9 +471,9 @@ const BiometricModal = React.memo(
444
471
  const handleImageCapture = useCallback(
445
472
  async (capturedData) => {
446
473
  if (state.currentStep === "Location Verification") {
447
- handleQRScanned(capturedData);
474
+ await handleQRScanned(capturedData);
448
475
  } else if (state.currentStep === "Identity Verification") {
449
- uploadFaceScan(capturedData);
476
+ await uploadFaceScan(capturedData);
450
477
  }
451
478
  },
452
479
  [state.currentStep, uploadFaceScan, handleQRScanned]
@@ -478,7 +505,7 @@ const BiometricModal = React.memo(
478
505
  } else {
479
506
  startFaceRecognition();
480
507
  }
481
- }, [handleCountdownFinish, handleStartQRScan, startCountdown, startFaceRecognition]);
508
+ }, [handleCountdownFinish, handleStartQRScan, startCountdown, startFaceRecognition, qrscan]);
482
509
 
483
510
  // Open modal when data is received
484
511
  useEffect(() => {
@@ -487,7 +514,7 @@ const BiometricModal = React.memo(
487
514
  setModalVisible(true);
488
515
  startProcess();
489
516
  }
490
- }, [data, modalVisible, startProcess, qrscan]);
517
+ }, [data, modalVisible, startProcess]);
491
518
 
492
519
  // Determine if camera should be shown
493
520
  const shouldShowCamera =
@@ -577,6 +604,12 @@ const BiometricModal = React.memo(
577
604
  }
578
605
  );
579
606
 
607
+ // Wrap with memo after forwardRef
608
+ const MemoizedBiometricModal = React.memo(BiometricModal);
609
+
610
+ // Add display name for debugging
611
+ MemoizedBiometricModal.displayName = 'BiometricModal';
612
+
580
613
  const styles = StyleSheet.create({
581
614
  modalContainer: {
582
615
  flex: 1,
@@ -671,4 +704,4 @@ const styles = StyleSheet.create({
671
704
  },
672
705
  });
673
706
 
674
- export default BiometricModal;
707
+ export default MemoizedBiometricModal;