react-native-biometric-verifier 0.0.42 → 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.42",
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;
@@ -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
@@ -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, qrscan = false, callback, apiurl, onclose, frameProcessorFps, livenessLevel, fileurl, imageurl, navigation, MaxDistanceMeters = 100, 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();
@@ -48,13 +62,14 @@ const BiometricModal = React.memo(
48
62
 
49
63
  // State
50
64
  const [modalVisible, setModalVisible] = useState(false);
51
- const [cameraType, setCameraType] = useState("front");
65
+ const [cameraType, setCameraType] = useState("back"); // Start with back camera for QR scan
52
66
  const [state, setState] = useState({
53
67
  isLoading: false,
54
68
  loadingType: Global.LoadingTypes.none,
55
69
  currentStep: "Start",
56
70
  employeeData: null,
57
- animationState: Global.AnimationStates.faceScan,
71
+ animationState: Global.AnimationStates.qrScan, // Start with QR scan animation
72
+ qrData: null, // Store QR data for later use in face recognition
58
73
  });
59
74
 
60
75
  // Refs
@@ -63,11 +78,18 @@ const BiometricModal = React.memo(
63
78
  const responseRef = useRef(null);
64
79
  const processedRef = useRef(false);
65
80
  const resetTimeoutRef = useRef(null);
66
-
67
81
  // Animation values
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,14 +167,17 @@ 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,
152
176
  loadingType: Global.LoadingTypes.none,
153
177
  currentStep: "Start",
154
178
  employeeData: null,
155
- animationState: Global.AnimationStates.faceScan,
179
+ animationState: Global.AnimationStates.qrScan,
180
+ qrData: null,
156
181
  });
157
182
 
158
183
  setModalVisible(false);
@@ -164,7 +189,7 @@ const BiometricModal = React.memo(
164
189
  clearTimeout(resetTimeoutRef.current);
165
190
  resetTimeoutRef.current = null;
166
191
  }
167
- }, [resetCountdown, clearNotification]);
192
+ }, [resetCountdown, clearNotification, onclose]);
168
193
 
169
194
  // Error handler
170
195
  const handleProcessError = useCallback(
@@ -195,7 +220,7 @@ const BiometricModal = React.memo(
195
220
  const handleCountdownFinish = useCallback(() => {
196
221
  handleProcessError("Time is up! Please try again.");
197
222
 
198
- if (navigation.canGoBack()) {
223
+ if (navigation?.canGoBack?.()) {
199
224
  navigation.goBack();
200
225
  }
201
226
  }, [handleProcessError, navigation]);
@@ -210,112 +235,7 @@ const BiometricModal = React.memo(
210
235
  return true;
211
236
  }, [apiurl, handleProcessError]);
212
237
 
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
238
+ // QR code processing - FIRST STEP
319
239
  const handleQRScanned = useCallback(
320
240
  async (qrCodeData) => {
321
241
  if (!validateApiUrl()) return;
@@ -352,14 +272,12 @@ const BiometricModal = React.memo(
352
272
 
353
273
  const location = await getCurrentLocation();
354
274
 
355
- const [latStr, lngStr] = qrString.split(",");
275
+ const [latStr, lngStr, qrkey] = qrString.split(",");
356
276
  const lat = parseFloat(latStr);
357
277
  const lng = parseFloat(lngStr);
358
278
  const validCoords = !isNaN(lat) && !isNaN(lng);
359
- const validDev =
360
- !isNaN(location?.latitude) && !isNaN(location?.longitude);
361
-
362
- if (validCoords && validDev) {
279
+ const validDev = !isNaN(location?.latitude) && !isNaN(location?.longitude);
280
+ if (validCoords && validDev && qrkey === depkey) {
363
281
  updateState({
364
282
  loadingType: Global.LoadingTypes.calculateDistance,
365
283
  });
@@ -370,7 +288,6 @@ const BiometricModal = React.memo(
370
288
  location.latitude,
371
289
  location.longitude
372
290
  );
373
-
374
291
  if (distance <= MaxDistanceMeters) {
375
292
  const locationDetails = {
376
293
  qrLocation: {
@@ -387,27 +304,24 @@ const BiometricModal = React.memo(
387
304
  verifiedAt: new Date().toISOString(),
388
305
  };
389
306
 
307
+ // Store location details and QR data for face recognition
390
308
  responseRef.current = {
391
- ...(responseRef.current || {}), // existing faceData
392
309
  location: locationDetails,
393
310
  };
394
311
 
395
- safeCallback(responseRef.current);
396
-
397
- notifyMessage("Location verified successfully!", "success");
398
-
312
+ // Store QR data in state for face recognition step
399
313
  updateState({
314
+ qrData: qrString,
400
315
  animationState: Global.AnimationStates.success,
401
316
  isLoading: false,
402
317
  loadingType: Global.LoadingTypes.none,
403
318
  });
404
319
 
405
- if (resetTimeoutRef.current) {
406
- clearTimeout(resetTimeoutRef.current);
407
- }
320
+ notifyMessage("Location verified successfully!", "success");
408
321
 
409
- resetTimeoutRef.current = setTimeout(() => {
410
- resetState();
322
+ // Start face recognition after a brief delay
323
+ setTimeout(() => {
324
+ startFaceRecognition();
411
325
  }, 1200);
412
326
  }
413
327
  else {
@@ -430,49 +344,168 @@ const BiometricModal = React.memo(
430
344
  getCurrentLocation,
431
345
  notifyMessage,
432
346
  requestLocationPermission,
347
+ updateState,
348
+ validateApiUrl,
349
+ handleProcessError,
350
+ depkey,
351
+ MaxDistanceMeters
352
+ ]
353
+ );
354
+
355
+ // Face scan upload - SECOND STEP
356
+ const uploadFaceScan = useCallback(
357
+ async (selfie) => {
358
+ if (!validateApiUrl()) return;
359
+
360
+ // Check if QR scan was completed successfully
361
+ if (!state.qrData) {
362
+ handleProcessError("Please complete QR scan first.");
363
+ return;
364
+ }
365
+
366
+ const currentData = dataRef.current;
367
+
368
+ if (!currentData) {
369
+ handleProcessError("Employee data not found.");
370
+ return;
371
+ }
372
+
373
+ updateState({
374
+ isLoading: true,
375
+ loadingType: Global.LoadingTypes.faceRecognition,
376
+ animationState: Global.AnimationStates.processing,
377
+ });
378
+
379
+ InteractionManager.runAfterInteractions(async () => {
380
+ let base64;
381
+
382
+ try {
383
+ updateState({
384
+ loadingType: Global.LoadingTypes.imageProcessing,
385
+ });
386
+
387
+ base64 = await convertImageToBase64(selfie?.uri);
388
+ } catch (err) {
389
+ console.error("Image conversion failed:", err);
390
+ handleProcessError("Image conversion failed.", err);
391
+ return;
392
+ }
393
+
394
+ if (!base64) {
395
+ handleProcessError("Failed to process image.");
396
+ return;
397
+ }
398
+
399
+ try {
400
+ const body = { image: base64 };
401
+ const header = { faceid: currentData };
402
+ const buttonapi = `${apiurl}python/recognize`;
403
+
404
+ updateState({
405
+ loadingType: Global.LoadingTypes.networkRequest,
406
+ });
407
+
408
+ const response = await networkServiceCall(
409
+ "POST",
410
+ buttonapi,
411
+ header,
412
+ body
413
+ );
414
+
415
+ if (response?.httpstatus === 200) {
416
+ // Combine face recognition response with QR location data
417
+ responseRef.current = {
418
+ ...responseRef.current, // Contains location data from QR scan
419
+ ...response.data?.data || {},
420
+ faceRecognition: response.data?.data || null,
421
+ };
422
+
423
+ updateState({
424
+ employeeData: response.data?.data || null,
425
+ animationState: Global.AnimationStates.success,
426
+ isLoading: false,
427
+ loadingType: Global.LoadingTypes.none,
428
+ });
429
+
430
+ notifyMessage("Identity verified successfully!", "success");
431
+
432
+ // Call the callback with combined data
433
+ safeCallback(responseRef.current);
434
+
435
+ if (resetTimeoutRef.current) {
436
+ clearTimeout(resetTimeoutRef.current);
437
+ }
438
+
439
+ resetTimeoutRef.current = setTimeout(() => {
440
+ resetState();
441
+ }, 1200);
442
+ } else {
443
+ handleProcessError(
444
+ response?.data?.message ||
445
+ "Face not recognized. Please try again."
446
+ );
447
+ }
448
+ } catch (error) {
449
+ console.error("Network request failed:", error);
450
+ handleProcessError(
451
+ "Connection error. Please check your network.",
452
+ error
453
+ );
454
+ }
455
+ });
456
+ },
457
+ [
458
+ convertImageToBase64,
459
+ notifyMessage,
433
460
  resetState,
434
461
  updateState,
435
462
  validateApiUrl,
436
463
  safeCallback,
437
- handleProcessError
464
+ handleProcessError,
465
+ state.qrData,
466
+ apiurl
438
467
  ]
439
468
  );
440
469
 
441
470
  // Image capture handler
442
471
  const handleImageCapture = useCallback(
443
472
  async (capturedData) => {
444
- if (state.currentStep === "Identity Verification") {
445
- uploadFaceScan(capturedData);
446
- } else if (state.currentStep === "Location Verification") {
447
- handleQRScanned(capturedData);
473
+ if (state.currentStep === "Location Verification") {
474
+ await handleQRScanned(capturedData);
475
+ } else if (state.currentStep === "Identity Verification") {
476
+ await uploadFaceScan(capturedData);
448
477
  }
449
478
  },
450
479
  [state.currentStep, uploadFaceScan, handleQRScanned]
451
480
  );
452
481
 
453
- // Start face scan
454
- const handleStartFaceScan = useCallback(() => {
482
+ // Start QR code scan - FIRST STEP
483
+ const handleStartQRScan = useCallback(() => {
455
484
  updateState({
456
- currentStep: "Identity Verification",
457
- animationState: Global.AnimationStates.faceScan,
485
+ currentStep: "Location Verification",
486
+ animationState: Global.AnimationStates.qrScan,
458
487
  });
459
- setCameraType("front");
488
+ setCameraType("back");
460
489
  }, [updateState]);
461
490
 
462
- // Start QR code scan
463
- const startQRCodeScan = useCallback(() => {
491
+ // Start face recognition - SECOND STEP
492
+ const startFaceRecognition = useCallback(() => {
464
493
  updateState({
465
- currentStep: "Location Verification",
466
- animationState: Global.AnimationStates.qrScan,
494
+ currentStep: "Identity Verification",
495
+ animationState: Global.AnimationStates.faceScan,
467
496
  });
468
- setCameraType("back");
497
+ setCameraType("front");
469
498
  }, [updateState]);
470
499
 
471
500
  // Start the verification process
472
501
  const startProcess = useCallback(() => {
473
502
  startCountdown(handleCountdownFinish);
474
- handleStartFaceScan();
475
- }, [handleCountdownFinish, handleStartFaceScan, startCountdown]);
503
+ if (qrscan) {
504
+ handleStartQRScan();
505
+ } else {
506
+ startFaceRecognition();
507
+ }
508
+ }, [handleCountdownFinish, handleStartQRScan, startCountdown, startFaceRecognition, qrscan]);
476
509
 
477
510
  // Open modal when data is received
478
511
  useEffect(() => {
@@ -535,7 +568,10 @@ const BiometricModal = React.memo(
535
568
  </View>
536
569
 
537
570
  <View style={styles.topContainerstep}>
538
- <StepIndicator currentStep={state.currentStep} qrscan={qrscan} />
571
+ <StepIndicator
572
+ currentStep={state.currentStep}
573
+ qrscan={qrscan}
574
+ />
539
575
  </View>
540
576
 
541
577
  {state.employeeData && (
@@ -568,6 +604,12 @@ const BiometricModal = React.memo(
568
604
  }
569
605
  );
570
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
+
571
613
  const styles = StyleSheet.create({
572
614
  modalContainer: {
573
615
  flex: 1,
@@ -662,4 +704,4 @@ const styles = StyleSheet.create({
662
704
  },
663
705
  });
664
706
 
665
- export default BiometricModal;
707
+ export default MemoizedBiometricModal;