react-native-biometric-verifier 0.0.8 → 0.0.10

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