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 +1 -1
- package/src/components/Loader.js +108 -53
- package/src/components/styles.js +18 -0
- package/src/index.js +8 -9
- package/src/components/StateIndicator.js +0 -112
package/package.json
CHANGED
package/src/components/Loader.js
CHANGED
|
@@ -1,58 +1,113 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
<
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
105
|
+
) : (
|
|
106
|
+
getIcon()
|
|
107
|
+
)}
|
|
108
|
+
</Animated.View>
|
|
109
|
+
</View>
|
|
27
110
|
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
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;
|
package/src/components/styles.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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
|
-
};
|