react-native-biometric-verifier 0.0.8 → 0.0.9

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.8",
3
+ "version": "0.0.9",
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
  "scripts": {
@@ -0,0 +1,24 @@
1
+ import { useCallback } from "react";
2
+
3
+ /**
4
+ * Hook to safely execute a callback function with error handling
5
+ */
6
+ export const useSafeCallback = (callback, notifyMessage) => {
7
+ return useCallback(
8
+ (response) => {
9
+ if (typeof callback === "function") {
10
+ try {
11
+ callback(response);
12
+ } catch (err) {
13
+ console.error("Callback execution failed:", err);
14
+ if (typeof notifyMessage === "function") {
15
+ notifyMessage("Unexpected error while processing callback.", "error");
16
+ }
17
+ }
18
+ } else {
19
+ console.log("Biometric Verification Response:", response);
20
+ }
21
+ },
22
+ [callback, notifyMessage]
23
+ );
24
+ };
package/src/index.js CHANGED
@@ -1,326 +1,376 @@
1
- import React, { useState, useEffect, useRef, useCallback } from 'react';
1
+ import React, {
2
+ useState,
3
+ useEffect,
4
+ useRef,
5
+ useCallback,
6
+ useMemo,
7
+ } from "react";
2
8
  import {
3
9
  View,
4
10
  TouchableOpacity,
5
11
  Text,
6
12
  Modal,
7
- } from 'react-native';
8
- import Icon from 'react-native-vector-icons/MaterialIcons';
9
- import { useCountdown } from './hooks/useCountdown';
10
- import { useGeolocation } from './hooks/useGeolocation';
11
- import { useImageProcessing } from './hooks/useImageProcessing';
12
- import { useNotifyMessage } from './hooks/useNotifyMessage';
13
- import { getDistanceInMeters } from './utils/distanceCalculator';
13
+ InteractionManager,
14
+ } from "react-native";
15
+ import Icon from "react-native-vector-icons/MaterialIcons";
16
+ import { useCountdown } from "./hooks/useCountdown";
17
+ import { useGeolocation } from "./hooks/useGeolocation";
18
+ import { useImageProcessing } from "./hooks/useImageProcessing";
19
+ import { useNotifyMessage } from "./hooks/useNotifyMessage";
20
+ import { getDistanceInMeters } from "./utils/distanceCalculator";
14
21
  import {
15
22
  ANIMATION_STATES,
16
23
  COLORS,
17
24
  COUNTDOWN_DURATION,
18
25
  MAX_DISTANCE_METERS,
19
- } from './utils/constants';
20
- import Loader from './components/Loader';
21
- import { CountdownTimer } from './components/CountdownTimer';
22
- import { EmployeeCard } from './components/EmployeeCard';
23
- import { Notification } from './components/Notification';
24
- import { StateIndicator } from './components/StateIndicator';
25
- import { styles } from './components/styles';
26
- import { useNavigation } from '@react-navigation/native';
27
- import networkServiceCall from './utils/NetworkServiceCall';
28
- import { getLoaderGif } from './utils/getLoaderGif';
29
-
30
- const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback, apiurl }) => {
31
- const navigation = useNavigation();
32
-
33
- const { countdown, startCountdown, resetCountdown } = useCountdown();
34
- const { requestLocationPermission, getCurrentLocation } = useGeolocation();
35
- const { convertImageToBase64 } = useImageProcessing();
36
- const { notification, fadeAnim, slideAnim, notifyMessage } = useNotifyMessage();
37
-
38
- const [state, setState] = useState({
39
- isLoading: false,
40
- currentStep: 'Start',
41
- employeeData: null,
42
- modalVisible: false,
43
- animationState: ANIMATION_STATES.FACE_SCAN,
44
- });
45
-
46
- const dataRef = useRef(data);
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
- );
65
-
66
- useEffect(() => {
67
- return () => {
68
- mountedRef.current = false;
69
- };
70
- }, []);
71
-
72
- useEffect(() => {
73
- dataRef.current = data;
74
- }, [data]);
75
-
76
- const updateState = useCallback((newState) => {
77
- if (mountedRef.current) {
78
- setState((prev) => ({ ...prev, ...newState }));
79
- }
80
- }, []);
81
-
82
- const resetState = useCallback(() => {
83
- updateState({
84
- currentStep: 'Start',
26
+ } from "./utils/constants";
27
+ import Loader from "./components/Loader";
28
+ import { CountdownTimer } from "./components/CountdownTimer";
29
+ import { EmployeeCard } from "./components/EmployeeCard";
30
+ import { Notification } from "./components/Notification";
31
+ import { StateIndicator } from "./components/StateIndicator";
32
+ import { styles } from "./components/styles";
33
+ import { useNavigation } from "@react-navigation/native";
34
+ import networkServiceCall from "./utils/NetworkServiceCall";
35
+ import { getLoaderGif } from "./utils/getLoaderGif";
36
+ import { useSafeCallback } from "./hooks/useSafeCallback";
37
+
38
+ const BiometricVerificationModal = React.memo(
39
+ ({ data, qrscan = false, callback, apiurl }) => {
40
+ const navigation = useNavigation();
41
+
42
+ const { countdown, startCountdown, resetCountdown } = useCountdown();
43
+ const { requestLocationPermission, getCurrentLocation } = useGeolocation();
44
+ const { convertImageToBase64 } = useImageProcessing();
45
+ const { notification, fadeAnim, slideAnim, notifyMessage } =
46
+ useNotifyMessage();
47
+
48
+ const [modalVisible, setModalVisible] = useState(false);
49
+ const [state, setState] = useState({
50
+ isLoading: false,
51
+ currentStep: "Start",
85
52
  employeeData: null,
86
53
  animationState: ANIMATION_STATES.FACE_SCAN,
87
- isLoading: false,
88
54
  });
89
- resetCountdown();
90
- }, [resetCountdown, updateState]);
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
-
102
- const handleCountdownFinish = useCallback(() => {
103
- handleProcessError('Time is up! Please try again.');
104
- updateState({ modalVisible: false });
105
- if (navigation.canGoBack()) navigation.goBack();
106
- }, [handleProcessError, navigation, updateState]);
107
-
108
- const validateApiUrl = useCallback(() => {
109
- if (!apiurl || typeof apiurl !== 'string') {
110
- handleProcessError('Invalid API URL configuration.');
111
- return false;
112
- }
113
- return true;
114
- }, [apiurl, handleProcessError]);
115
-
116
- const uploadFaceScan = useCallback(
117
- async (selfie) => {
118
- if (!validateApiUrl()) return;
119
-
120
- const currentData = dataRef.current;
121
- if (!currentData) {
122
- handleProcessError('Employee data not found.');
123
- return;
124
- }
125
55
 
126
- let base64;
127
- try {
128
- base64 = await convertImageToBase64(selfie?.uri);
129
- } catch (err) {
130
- handleProcessError('Image conversion failed.', err);
131
- return;
56
+ const dataRef = useRef(data);
57
+ const mountedRef = useRef(true);
58
+ const responseRef = useRef(null);
59
+ const processedRef = useRef(false);
60
+ const lastDataRef = useRef(null);
61
+
62
+ const safeCallback = useSafeCallback(callback, notifyMessage);
63
+
64
+ /** Cleanup on unmount */
65
+ useEffect(() => {
66
+ return () => {
67
+ mountedRef.current = false;
68
+ };
69
+ }, []);
70
+
71
+ useEffect(() => {
72
+ dataRef.current = data;
73
+ }, [data]);
74
+
75
+ const updateState = useCallback((newState) => {
76
+ if (mountedRef.current) {
77
+ setState((prev) => {
78
+ const merged = { ...prev, ...newState };
79
+ return prev !== merged ? merged : prev;
80
+ });
132
81
  }
133
-
134
- if (!base64) {
135
- handleProcessError('Failed to process image.');
136
- return;
137
- }
138
-
139
- updateState({
140
- isLoading: true,
141
- animationState: ANIMATION_STATES.PROCESSING,
82
+ }, []);
83
+
84
+ const resetState = useCallback(() => {
85
+ console.log("🔄 Resetting biometric modal...");
86
+ setState({
87
+ isLoading: false,
88
+ currentStep: "Start",
89
+ employeeData: null,
90
+ animationState: ANIMATION_STATES.FACE_SCAN,
142
91
  });
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
- }
168
- } else {
169
- handleProcessError(
170
- response?.data?.error?.message || 'Face not recognized. Please try again.'
171
- );
172
- }
173
- } catch (error) {
174
- handleProcessError('Connection error. Please check your network.', error);
175
- } finally {
176
- updateState({ isLoading: false });
92
+ setModalVisible(false);
93
+ processedRef.current = false;
94
+ resetCountdown();
95
+ }, [resetCountdown]);
96
+
97
+ const handleProcessError = useCallback(
98
+ (message, errorObj = null) => {
99
+ if (errorObj) console.error(message, errorObj);
100
+ notifyMessage(message, "error");
101
+ updateState({
102
+ animationState: ANIMATION_STATES.ERROR,
103
+ isLoading: false,
104
+ });
105
+ setTimeout(resetState, 1200);
106
+ },
107
+ [notifyMessage, resetState, updateState]
108
+ );
109
+
110
+ const handleCountdownFinish = useCallback(() => {
111
+ handleProcessError("Time is up! Please try again.");
112
+ resetState();
113
+ if (navigation.canGoBack()) navigation.goBack();
114
+ }, [handleProcessError, navigation, resetState]);
115
+
116
+ const validateApiUrl = useCallback(() => {
117
+ if (!apiurl || typeof apiurl !== "string") {
118
+ handleProcessError("Invalid API URL configuration.");
119
+ return false;
177
120
  }
178
- },
179
- [convertImageToBase64, notifyMessage, qrscan, resetState, updateState, validateApiUrl, safeCallback]
180
- );
121
+ return true;
122
+ }, [apiurl, handleProcessError]);
123
+
124
+ const uploadFaceScan = useCallback(
125
+ async (selfie) => {
126
+ if (!validateApiUrl()) return;
127
+ const currentData = dataRef.current;
128
+ if (!currentData) {
129
+ handleProcessError("Employee data not found.");
130
+ return;
131
+ }
181
132
 
182
- const handleStartFaceScan = useCallback(() => {
183
- updateState({
184
- currentStep: 'Identity Verification',
185
- animationState: ANIMATION_STATES.FACE_SCAN,
186
- });
187
- navigation.navigate('CCaptureImageWithoutEdit', {
188
- facedetection: true,
189
- cameratype: 'front',
190
- onSelect: uploadFaceScan,
191
- });
192
- }, [navigation, updateState, uploadFaceScan]);
133
+ updateState({
134
+ isLoading: true,
135
+ animationState: ANIMATION_STATES.PROCESSING,
136
+ });
137
+
138
+ InteractionManager.runAfterInteractions(async () => {
139
+ let base64;
140
+ try {
141
+ base64 = await convertImageToBase64(selfie?.uri);
142
+ } catch (err) {
143
+ handleProcessError("Image conversion failed.", err);
144
+ return;
145
+ }
193
146
 
194
- const startQRCodeScan = useCallback(() => {
195
- updateState({
196
- currentStep: 'Location Verification',
197
- animationState: ANIMATION_STATES.QR_SCAN,
198
- });
199
- navigation.navigate('CCaptureImageWithoutEdit', {
200
- hidebuttons: true,
201
- cameratype: 'back',
202
- cameramoduletype: 2,
203
- onSelect: handleQRScanned,
204
- });
205
- }, [navigation, updateState]);
147
+ if (!base64) {
148
+ handleProcessError("Failed to process image.");
149
+ return;
150
+ }
206
151
 
207
- const handleQRScanned = useCallback(
208
- async (qrCodeData) => {
209
- if (!validateApiUrl()) return;
152
+ try {
153
+ const body = { image: base64 };
154
+ const header = { faceid: currentData };
155
+ const buttonapi = `${apiurl}python/recognize`;
156
+ console.log("buttonapi", buttonapi);
157
+ const response = await networkServiceCall(
158
+ "POST",
159
+ buttonapi,
160
+ header,
161
+ body
162
+ );
163
+
164
+ if (response?.httpstatus === 200) {
165
+ responseRef.current = response;
166
+ updateState({
167
+ employeeData: response.data?.data || null,
168
+ animationState: ANIMATION_STATES.SUCCESS,
169
+ isLoading: false,
170
+ });
171
+ notifyMessage("Identity verified successfully!", "success");
172
+
173
+ if (qrscan) {
174
+ setTimeout(() => startQRCodeScan(), 1200);
175
+ } else {
176
+ safeCallback(responseRef.current);
177
+ setTimeout(() => resetState(), 1200);
178
+ }
179
+ } else {
180
+ handleProcessError(
181
+ response?.data?.error?.message ||
182
+ "Face not recognized. Please try again."
183
+ );
184
+ }
185
+ } catch (error) {
186
+ handleProcessError(
187
+ "Connection error. Please check your network.",
188
+ error
189
+ );
190
+ }
191
+ });
192
+ },
193
+ [
194
+ convertImageToBase64,
195
+ notifyMessage,
196
+ qrscan,
197
+ resetState,
198
+ updateState,
199
+ validateApiUrl,
200
+ safeCallback,
201
+ ]
202
+ );
203
+
204
+ const handleStartFaceScan = useCallback(() => {
205
+ updateState({
206
+ currentStep: "Identity Verification",
207
+ animationState: ANIMATION_STATES.FACE_SCAN,
208
+ });
209
+ navigation.navigate("CCaptureImageWithoutEdit", {
210
+ facedetection: true,
211
+ cameratype: "front",
212
+ onSelect: uploadFaceScan,
213
+ });
214
+ }, [navigation, updateState, uploadFaceScan]);
210
215
 
216
+ const startQRCodeScan = useCallback(() => {
211
217
  updateState({
212
- animationState: ANIMATION_STATES.PROCESSING,
213
- isLoading: true,
218
+ currentStep: "Location Verification",
219
+ animationState: ANIMATION_STATES.QR_SCAN,
220
+ });
221
+ navigation.navigate("CCaptureImageWithoutEdit", {
222
+ hidebuttons: true,
223
+ cameratype: "back",
224
+ cameramoduletype: 2,
225
+ onSelect: handleQRScanned,
214
226
  });
227
+ }, [navigation, updateState]);
215
228
 
216
- try {
217
- const hasPermission = await requestLocationPermission();
218
- if (!hasPermission) {
219
- handleProcessError('Location permission not granted.');
220
- return;
221
- }
229
+ const handleQRScanned = useCallback(
230
+ async (qrCodeData) => {
231
+ if (!validateApiUrl()) return;
222
232
 
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
- }
233
+ updateState({
234
+ animationState: ANIMATION_STATES.PROCESSING,
235
+ isLoading: true,
236
+ });
237
+
238
+ try {
239
+ const hasPermission = await requestLocationPermission();
240
+ if (!hasPermission) {
241
+ handleProcessError("Location permission not granted.");
242
+ return;
243
+ }
228
244
 
229
- const location = await getCurrentLocation();
230
- const [latStr, lngStr] = qrString.split(',');
231
- const lat = parseFloat(latStr);
232
- const lng = parseFloat(lngStr);
245
+ const qrString =
246
+ typeof qrCodeData === "object" ? qrCodeData?.data : qrCodeData;
247
+ if (!qrString || typeof qrString !== "string") {
248
+ handleProcessError("Invalid QR code. Please try again.");
249
+ return;
250
+ }
233
251
 
234
- const validCoords = !isNaN(lat) && !isNaN(lng);
235
- const validDev = !isNaN(location?.latitude) && !isNaN(location?.longitude);
252
+ const location = await getCurrentLocation();
253
+ const [latStr, lngStr] = qrString.split(",");
254
+ const lat = parseFloat(latStr);
255
+ const lng = parseFloat(lngStr);
236
256
 
237
- if (validCoords && validDev) {
238
- const distance = getDistanceInMeters(lat, lng, location.latitude, location.longitude);
257
+ const validCoords = !isNaN(lat) && !isNaN(lng);
258
+ const validDev =
259
+ !isNaN(location?.latitude) && !isNaN(location?.longitude);
239
260
 
240
- if (distance <= MAX_DISTANCE_METERS) {
241
- safeCallback(responseRef.current);
242
- notifyMessage('Location verified successfully!', 'success');
243
- updateState({ animationState: ANIMATION_STATES.SUCCESS });
261
+ if (validCoords && validDev) {
262
+ const distance = getDistanceInMeters(
263
+ lat,
264
+ lng,
265
+ location.latitude,
266
+ location.longitude
267
+ );
244
268
 
245
- setTimeout(() => {
246
- updateState({ modalVisible: false });
247
- resetState();
248
- }, 1500);
269
+ if (distance <= MAX_DISTANCE_METERS) {
270
+ safeCallback(responseRef.current);
271
+ notifyMessage("Location verified successfully!", "success");
272
+ updateState({
273
+ animationState: ANIMATION_STATES.SUCCESS,
274
+ isLoading: false,
275
+ });
276
+ setTimeout(() => resetState(), 1200);
277
+ } else {
278
+ handleProcessError(
279
+ `Location mismatch (${distance.toFixed(0)}m away).`
280
+ );
281
+ }
249
282
  } else {
250
- handleProcessError(`Location mismatch (${distance.toFixed(0)}m away).`);
283
+ handleProcessError("Invalid coordinates in QR code.");
251
284
  }
252
- } else {
253
- handleProcessError('Invalid coordinates in QR code.');
285
+ } catch (error) {
286
+ handleProcessError(
287
+ "Unable to verify location. Please try again.",
288
+ error
289
+ );
254
290
  }
255
- } catch (error) {
256
- handleProcessError('Unable to verify location. Please try again.', error);
257
- } finally {
258
- updateState({ isLoading: false });
291
+ },
292
+ [
293
+ getCurrentLocation,
294
+ notifyMessage,
295
+ requestLocationPermission,
296
+ resetState,
297
+ updateState,
298
+ validateApiUrl,
299
+ safeCallback,
300
+ ]
301
+ );
302
+
303
+ const startProcess = useCallback(() => {
304
+ startCountdown(COUNTDOWN_DURATION, handleCountdownFinish);
305
+ handleStartFaceScan();
306
+ }, [handleCountdownFinish, handleStartFaceScan, startCountdown]);
307
+
308
+ useEffect(() => {
309
+ if (data && data !== lastDataRef.current) {
310
+ console.log("📥 New donor data received:", data);
311
+ lastDataRef.current = data;
312
+ setModalVisible(true);
313
+ startProcess();
259
314
  }
260
- },
261
- [getCurrentLocation, notifyMessage, requestLocationPermission, resetState, updateState, validateApiUrl, safeCallback]
262
- );
263
-
264
- const startProcess = useCallback(() => {
265
- startCountdown(COUNTDOWN_DURATION, handleCountdownFinish);
266
- handleStartFaceScan();
267
- }, [handleCountdownFinish, handleStartFaceScan, startCountdown]);
268
-
269
- useEffect(() => {
270
- if (data) {
271
- updateState({ modalVisible: true });
272
- startProcess();
273
- }
274
- }, [data, startProcess, updateState]);
275
-
276
- return (
277
- <Modal
278
- visible={state.modalVisible}
279
- animationType="slide"
280
- transparent
281
- onRequestClose={() => {
282
- updateState({ modalVisible: false });
283
- resetState();
284
- }}
285
- statusBarTranslucent={true}
286
- >
287
- <View style={styles.modalBg}>
288
- <TouchableOpacity
289
- style={styles.close}
290
- onPress={() => {
291
- updateState({ modalVisible: false });
292
- resetState();
293
- }}
294
- accessibilityLabel="Close modal"
295
- hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
296
- >
297
- <Icon name="close" size={24} color={COLORS.light} />
298
- </TouchableOpacity>
299
-
300
- <Text style={styles.title}>Biometric Verification</Text>
301
- <Text style={styles.subTitle}>{state.currentStep}</Text>
302
-
303
- <StateIndicator state={state.animationState} size={120} />
304
-
305
- {state.employeeData && (
306
- <EmployeeCard employeeData={state.employeeData} apiurl={apiurl} />
315
+ }, [data, startProcess]);
316
+
317
+ const loaderSource = useMemo(
318
+ () =>
319
+ state.isLoading &&
320
+ getLoaderGif(state.animationState, state.currentStep, apiurl),
321
+ [state.isLoading, state.animationState, state.currentStep, apiurl]
322
+ );
323
+
324
+ return (
325
+ <>
326
+ {modalVisible && (
327
+ <Modal
328
+ visible={modalVisible}
329
+ animationType="slide"
330
+ transparent
331
+ onRequestClose={resetState}
332
+ statusBarTranslucent
333
+ >
334
+ <View style={styles.modalBg}>
335
+ <TouchableOpacity
336
+ style={styles.close}
337
+ onPress={resetState}
338
+ accessibilityLabel="Close modal"
339
+ hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
340
+ >
341
+ <Icon name="close" size={24} color={COLORS.light} />
342
+ </TouchableOpacity>
343
+
344
+ <Text style={styles.title}>Biometric Verification</Text>
345
+ <Text style={styles.subTitle}>{state.currentStep}</Text>
346
+
347
+ <StateIndicator state={state.animationState} size={120} />
348
+
349
+ {state.employeeData && (
350
+ <EmployeeCard
351
+ employeeData={state.employeeData}
352
+ apiurl={apiurl}
353
+ />
354
+ )}
355
+
356
+ <Notification
357
+ notification={notification}
358
+ fadeAnim={fadeAnim}
359
+ slideAnim={slideAnim}
360
+ />
361
+
362
+ <CountdownTimer
363
+ duration={COUNTDOWN_DURATION}
364
+ currentTime={countdown}
365
+ />
366
+
367
+ {loaderSource && <Loader source={loaderSource} />}
368
+ </View>
369
+ </Modal>
307
370
  )}
308
-
309
- <Notification
310
- notification={notification}
311
- fadeAnim={fadeAnim}
312
- slideAnim={slideAnim}
313
- />
314
-
315
- <CountdownTimer duration={COUNTDOWN_DURATION} currentTime={countdown} />
316
-
317
- {state.isLoading &&
318
- getLoaderGif(state.animationState, state.currentStep, apiurl) && (
319
- <Loader source={getLoaderGif(state.animationState, state.currentStep, apiurl)} />
320
- )}
321
- </View>
322
- </Modal>
323
- );
324
- });
371
+ </>
372
+ );
373
+ }
374
+ );
325
375
 
326
376
  export default BiometricVerificationModal;