react-native-biometric-verifier 0.0.9 → 0.0.11

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.9",
3
+ "version": "0.0.11",
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,58 +1,113 @@
1
- import {
2
- StyleSheet,
3
- Dimensions,
4
- View,
5
- Modal,
6
- } from "react-native";
7
- import { normalize } from "react-native-elements";
8
- import FastImage from "react-native-fast-image";
9
-
10
- export default function Loader({ visible = true,source }) {
1
+ import React, { useEffect, useRef } from 'react';
2
+ import { Animated, Easing, View } from 'react-native';
3
+ import Icon from 'react-native-vector-icons/MaterialIcons';
4
+ import { ANIMATION_STATES, COLORS } from '../utils/constants';
5
+ import { styles } from './styles';
6
+ import FastImage from 'react-native-fast-image';
7
+
8
+ export const Loader = ({ state, source, isLoading = false }) => {
9
+ const spinValue = useRef(new Animated.Value(0)).current;
10
+ const pulseValue = useRef(new Animated.Value(1)).current;
11
+ const spinAnimation = useRef(null);
12
+ const pulseAnimation = useRef(null);
13
+
14
+ const size = 120; // Define the size constant
15
+
16
+ useEffect(() => {
17
+ // Stop ongoing animations when state changes
18
+ spinAnimation.current?.stop();
19
+ pulseAnimation.current?.stop();
20
+
21
+ if (
22
+ state === ANIMATION_STATES.PROCESSING ||
23
+ state === ANIMATION_STATES.FACE_SCAN ||
24
+ state === ANIMATION_STATES.QR_SCAN
25
+ ) {
26
+ spinAnimation.current = Animated.loop(
27
+ Animated.timing(spinValue, {
28
+ toValue: 1,
29
+ duration: 1500,
30
+ easing: Easing.linear,
31
+ useNativeDriver: true,
32
+ })
33
+ );
34
+ spinAnimation.current.start();
35
+ }
36
+ else if (
37
+ state === ANIMATION_STATES.SUCCESS ||
38
+ state === ANIMATION_STATES.ERROR
39
+ ) {
40
+ pulseAnimation.current = Animated.loop(
41
+ Animated.sequence([
42
+ Animated.timing(pulseValue, {
43
+ toValue: 1.1,
44
+ duration: 500,
45
+ useNativeDriver: true,
46
+ }),
47
+ Animated.timing(pulseValue, {
48
+ toValue: 1,
49
+ duration: 500,
50
+ useNativeDriver: true,
51
+ }),
52
+ ]),
53
+ { iterations: 2 }
54
+ );
55
+ pulseAnimation.current.start();
56
+ }
57
+ else {
58
+ spinValue.setValue(0);
59
+ pulseValue.setValue(1);
60
+ }
61
+
62
+ return () => {
63
+ spinAnimation.current?.stop();
64
+ pulseAnimation.current?.stop();
65
+ };
66
+ }, [state, spinValue, pulseValue]);
67
+
68
+ const getIcon = () => {
69
+ switch (state) {
70
+ case ANIMATION_STATES.FACE_SCAN:
71
+ return <Icon name="face" size={size * 0.5} color={COLORS.primary} />;
72
+ case ANIMATION_STATES.QR_SCAN:
73
+ return <Icon name="qr-code-scanner" size={size * 0.5} color={COLORS.primary} />;
74
+ case ANIMATION_STATES.PROCESSING:
75
+ return <Icon name="settings" size={size * 0.5} color={COLORS.info} />;
76
+ case ANIMATION_STATES.SUCCESS:
77
+ return <Icon name="check-circle" size={size * 0.5} color={COLORS.success} />;
78
+ case ANIMATION_STATES.ERROR:
79
+ return <Icon name="error" size={size * 0.5} color={COLORS.error} />;
80
+ default:
81
+ return <Icon name="hourglass-empty" size={size * 0.5} color={COLORS.gray} />;
82
+ }
83
+ };
84
+
11
85
  return (
12
- <Modal
13
- animationType="none"
14
- transparent={true}
15
- visible={visible}
16
- onRequestClose={() => {}}
17
- >
18
- <View style={styles.container}>
19
- <View style={styles.loaderBox}>
86
+ <View style={styles.indicatorWrapper}>
87
+ <Animated.View
88
+ style={[
89
+ styles.indicatorContainer,
90
+ {
91
+ width: size,
92
+ height: size,
93
+ transform: [
94
+ { scale: pulseValue },
95
+ ],
96
+ },
97
+ ]}
98
+ >
99
+ {isLoading && source ? (
20
100
  <FastImage
21
- style={styles.icon}
101
+ style={[styles.icon, { width: size * 0.8, height: size * 0.8 }]}
22
102
  source={source}
103
+ resizeMode={FastImage.resizeMode.contain}
23
104
  />
24
- </View>
25
- </View>
26
- </Modal>
105
+ ) : (
106
+ getIcon()
107
+ )}
108
+ </Animated.View>
109
+ </View>
27
110
  );
28
- }
29
-
30
- const { width, height } = Dimensions.get("window");
31
-
32
- const styles = StyleSheet.create({
33
- container: {
34
- justifyContent: "center",
35
- alignItems: "center",
36
- height,
37
- width,
38
- backgroundColor: "rgba(0,0,0,0.3)", // semi-transparent backdrop
39
- },
40
- loaderBox: {
41
- borderRadius: normalize(20),
42
- padding: 10,
43
- backgroundColor: "white",
44
- borderWidth: 1,
45
- borderColor: "lightblue",
46
- elevation: 5, // shadow on Android
47
- shadowColor: "#000", // shadow on iOS
48
- shadowOpacity: 0.2,
49
- shadowOffset: { width: 0, height: 2 },
50
- shadowRadius: 4,
51
- },
52
- icon: {
53
- height: normalize(70),
54
- width: normalize(70),
55
- justifyContent: "center",
56
- alignItems: "center",
57
- },
58
- });
111
+ };
112
+
113
+ export default Loader;
@@ -179,4 +179,22 @@ export const styles = StyleSheet.create({
179
179
  opacity: 0.8,
180
180
  fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
181
181
  },
182
+ capturedImageContainer: {
183
+ marginVertical: 15,
184
+ alignItems: 'center',
185
+ justifyContent: 'center',
186
+ },
187
+ capturedImage: {
188
+ width: 120,
189
+ height: 120,
190
+ borderRadius: 10,
191
+ borderWidth: 2,
192
+ borderColor: COLORS.primary,
193
+ },
194
+ capturedImageText: {
195
+ marginTop: 8,
196
+ fontSize: 14,
197
+ color: COLORS.light,
198
+ textAlign: 'center',
199
+ },
182
200
  });
package/src/index.js CHANGED
@@ -28,7 +28,6 @@ import Loader from "./components/Loader";
28
28
  import { CountdownTimer } from "./components/CountdownTimer";
29
29
  import { EmployeeCard } from "./components/EmployeeCard";
30
30
  import { Notification } from "./components/Notification";
31
- import { StateIndicator } from "./components/StateIndicator";
32
31
  import { styles } from "./components/styles";
33
32
  import { useNavigation } from "@react-navigation/native";
34
33
  import networkServiceCall from "./utils/NetworkServiceCall";
@@ -57,7 +56,6 @@ const BiometricVerificationModal = React.memo(
57
56
  const mountedRef = useRef(true);
58
57
  const responseRef = useRef(null);
59
58
  const processedRef = useRef(false);
60
- const lastDataRef = useRef(null);
61
59
 
62
60
  const safeCallback = useSafeCallback(callback, notifyMessage);
63
61
 
@@ -306,9 +304,8 @@ const BiometricVerificationModal = React.memo(
306
304
  }, [handleCountdownFinish, handleStartFaceScan, startCountdown]);
307
305
 
308
306
  useEffect(() => {
309
- if (data && data !== lastDataRef.current) {
307
+ if (data) {
310
308
  console.log("📥 New donor data received:", data);
311
- lastDataRef.current = data;
312
309
  setModalVisible(true);
313
310
  startProcess();
314
311
  }
@@ -344,8 +341,6 @@ const BiometricVerificationModal = React.memo(
344
341
  <Text style={styles.title}>Biometric Verification</Text>
345
342
  <Text style={styles.subTitle}>{state.currentStep}</Text>
346
343
 
347
- <StateIndicator state={state.animationState} size={120} />
348
-
349
344
  {state.employeeData && (
350
345
  <EmployeeCard
351
346
  employeeData={state.employeeData}
@@ -363,8 +358,12 @@ const BiometricVerificationModal = React.memo(
363
358
  duration={COUNTDOWN_DURATION}
364
359
  currentTime={countdown}
365
360
  />
366
-
367
- {loaderSource && <Loader source={loaderSource} />}
361
+
362
+ <Loader
363
+ state={state.animationState}
364
+ source={loaderSource}
365
+ isLoading={state.isLoading}
366
+ />
368
367
  </View>
369
368
  </Modal>
370
369
  )}
@@ -373,4 +372,4 @@ const BiometricVerificationModal = React.memo(
373
372
  }
374
373
  );
375
374
 
376
- export default BiometricVerificationModal;
375
+ export default BiometricVerificationModal;
@@ -1,112 +0,0 @@
1
- import React, { useEffect, useRef } from 'react';
2
- import { Animated, ActivityIndicator, Easing } from 'react-native';
3
- import Icon from 'react-native-vector-icons/MaterialIcons';
4
- import { ANIMATION_STATES, COLORS } from '../utils/constants';
5
- import { styles } from './styles';
6
-
7
- export const StateIndicator = ({ state, size = 100 }) => {
8
- const spinValue = useRef(new Animated.Value(0)).current;
9
- const pulseValue = useRef(new Animated.Value(1)).current;
10
- const spinAnimation = useRef(null);
11
- const pulseAnimation = useRef(null);
12
-
13
- useEffect(() => {
14
- // Stop ongoing animations when state changes
15
- spinValue.stopAnimation();
16
- pulseValue.stopAnimation();
17
-
18
- if (
19
- state === ANIMATION_STATES.PROCESSING ||
20
- state === ANIMATION_STATES.FACE_SCAN ||
21
- state === ANIMATION_STATES.QR_SCAN
22
- ) {
23
- spinAnimation.current = Animated.loop(
24
- Animated.timing(spinValue, {
25
- toValue: 1,
26
- duration: 1500,
27
- easing: Easing.linear,
28
- useNativeDriver: true,
29
- })
30
- );
31
- spinAnimation.current.start();
32
- }
33
- else if (
34
- state === ANIMATION_STATES.SUCCESS ||
35
- state === ANIMATION_STATES.ERROR
36
- ) {
37
- pulseAnimation.current = Animated.loop(
38
- Animated.sequence([
39
- Animated.timing(pulseValue, {
40
- toValue: 1.1,
41
- duration: 500,
42
- useNativeDriver: true,
43
- }),
44
- Animated.timing(pulseValue, {
45
- toValue: 1,
46
- duration: 500,
47
- useNativeDriver: true,
48
- }),
49
- ]),
50
- { iterations: 2 }
51
- );
52
- pulseAnimation.current.start();
53
- }
54
- else {
55
- spinValue.setValue(0);
56
- pulseValue.setValue(1);
57
- }
58
- }, [state, spinValue, pulseValue]);
59
-
60
- const spin = spinValue.interpolate({
61
- inputRange: [0, 1],
62
- outputRange: ['0deg', '360deg'],
63
- });
64
-
65
- const getIcon = () => {
66
- switch (state) {
67
- case ANIMATION_STATES.FACE_SCAN:
68
- return <Icon name="face" size={size * 0.5} color={COLORS.primary} />;
69
- case ANIMATION_STATES.QR_SCAN:
70
- return <Icon name="qr-code-scanner" size={size * 0.5} color={COLORS.primary} />;
71
- case ANIMATION_STATES.PROCESSING:
72
- return <Icon name="settings" size={size * 0.5} color={COLORS.info} />;
73
- case ANIMATION_STATES.SUCCESS:
74
- return <Icon name="check-circle" size={size * 0.5} color={COLORS.success} />;
75
- case ANIMATION_STATES.ERROR:
76
- return <Icon name="error" size={size * 0.5} color={COLORS.error} />;
77
- default:
78
- return <Icon name="hourglass-empty" size={size * 0.5} color={COLORS.gray} />;
79
- }
80
- };
81
-
82
- return (
83
- <Animated.View
84
- style={[
85
- styles.indicatorContainer,
86
- {
87
- width: size,
88
- height: size,
89
- transform: [
90
- { scale: pulseValue },
91
- ...(state === ANIMATION_STATES.PROCESSING ||
92
- state === ANIMATION_STATES.FACE_SCAN ||
93
- state === ANIMATION_STATES.QR_SCAN
94
- ? [{ rotate: spin }]
95
- : []),
96
- ],
97
- },
98
- ]}
99
- >
100
- {(state === ANIMATION_STATES.PROCESSING ||
101
- state === ANIMATION_STATES.FACE_SCAN ||
102
- state === ANIMATION_STATES.QR_SCAN) ? (
103
- <ActivityIndicator
104
- size="large"
105
- color={state === ANIMATION_STATES.PROCESSING ? COLORS.info : COLORS.primary}
106
- />
107
- ) : (
108
- getIcon()
109
- )}
110
- </Animated.View>
111
- );
112
- };