react-native-biometric-verifier 0.0.18 → 0.0.19
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/CaptureImageWithoutEdit.js +17 -17
- package/src/components/Card.js +6 -6
- package/src/components/CountdownTimer.js +3 -3
- package/src/components/Notification.js +6 -6
- package/src/components/StepIndicator.js +7 -7
- package/src/hooks/useCountdown.js +7 -7
- package/src/hooks/useImageProcessing.js +6 -6
- package/src/hooks/useNotifyMessage.js +4 -4
- package/src/index.js +30 -36
- package/src/utils/Global.js +48 -0
- package/src/utils/getLoaderGif.js +3 -3
- package/src/utils/constants.js +0 -73
package/package.json
CHANGED
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
useCameraFormat,
|
|
19
19
|
CameraRuntimeError,
|
|
20
20
|
} from 'react-native-vision-camera';
|
|
21
|
-
import {
|
|
21
|
+
import { Global } from '../utils/Global';
|
|
22
22
|
import { useFaceDetectionFrameProcessor } from '../hooks/useFaceDetectionFrameProcessor';
|
|
23
23
|
|
|
24
24
|
const CaptureImageWithoutEdit = React.memo(
|
|
@@ -389,7 +389,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
389
389
|
<View style={styles.cameraContainer}>
|
|
390
390
|
{cameraError ? (
|
|
391
391
|
<View style={styles.errorContainer}>
|
|
392
|
-
<Icon name="error-outline" size={40} color={
|
|
392
|
+
<Icon name="error-outline" size={40} color={Global.AppTheme.error} />
|
|
393
393
|
<Text style={styles.errorText}>{cameraError}</Text>
|
|
394
394
|
<TouchableOpacity
|
|
395
395
|
style={styles.retryButton}
|
|
@@ -401,7 +401,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
401
401
|
</View>
|
|
402
402
|
) : cameraPermission === 'denied' || cameraPermission === 'restricted' ? (
|
|
403
403
|
<View style={styles.placeholderContainer}>
|
|
404
|
-
<Icon name="camera-off" size={40} color={
|
|
404
|
+
<Icon name="camera-off" size={40} color={Global.AppTheme.light} />
|
|
405
405
|
<Text style={styles.placeholderText}>
|
|
406
406
|
Camera permission required. Please enable in settings.
|
|
407
407
|
</Text>
|
|
@@ -415,14 +415,14 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
415
415
|
</View>
|
|
416
416
|
) : cameraPermission === 'not-determined' ? (
|
|
417
417
|
<View style={styles.placeholderContainer}>
|
|
418
|
-
<ActivityIndicator size="large" color={
|
|
418
|
+
<ActivityIndicator size="large" color={Global.AppTheme.primary} />
|
|
419
419
|
<Text style={styles.placeholderText}>
|
|
420
420
|
Requesting camera access...
|
|
421
421
|
</Text>
|
|
422
422
|
</View>
|
|
423
423
|
) : !cameraDevice ? (
|
|
424
424
|
<View style={styles.placeholderContainer}>
|
|
425
|
-
<Icon name="camera-alt" size={40} color={
|
|
425
|
+
<Icon name="camera-alt" size={40} color={Global.AppTheme.light} />
|
|
426
426
|
<Text style={styles.placeholderText}>Camera not available</Text>
|
|
427
427
|
<TouchableOpacity
|
|
428
428
|
style={styles.retryButton}
|
|
@@ -434,7 +434,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
434
434
|
</View>
|
|
435
435
|
) : (
|
|
436
436
|
<View style={styles.placeholderContainer}>
|
|
437
|
-
<ActivityIndicator size="large" color={
|
|
437
|
+
<ActivityIndicator size="large" color={Global.AppTheme.primary} />
|
|
438
438
|
<Text style={styles.placeholderText}>Initializing camera...</Text>
|
|
439
439
|
</View>
|
|
440
440
|
)}
|
|
@@ -545,7 +545,7 @@ const CaptureImageWithoutEdit = React.memo(
|
|
|
545
545
|
disabled={isLoading || captured.current || livenessStep > 0}
|
|
546
546
|
accessibilityLabel="Flip camera"
|
|
547
547
|
>
|
|
548
|
-
<Icon name="flip-camera-ios" size={28} color={
|
|
548
|
+
<Icon name="flip-camera-ios" size={28} color={Global.AppTheme.light} />
|
|
549
549
|
</TouchableOpacity>
|
|
550
550
|
)}
|
|
551
551
|
</View>
|
|
@@ -562,7 +562,7 @@ const styles = StyleSheet.create({
|
|
|
562
562
|
width: '100%',
|
|
563
563
|
borderRadius: 12,
|
|
564
564
|
overflow: 'hidden',
|
|
565
|
-
backgroundColor:
|
|
565
|
+
backgroundColor: Global.AppTheme.dark,
|
|
566
566
|
minHeight: 300,
|
|
567
567
|
},
|
|
568
568
|
camera: {
|
|
@@ -582,24 +582,24 @@ const styles = StyleSheet.create({
|
|
|
582
582
|
padding: 20,
|
|
583
583
|
},
|
|
584
584
|
errorText: {
|
|
585
|
-
color:
|
|
585
|
+
color: Global.AppTheme.error,
|
|
586
586
|
fontSize: 16,
|
|
587
587
|
textAlign: 'center',
|
|
588
588
|
marginVertical: 16,
|
|
589
589
|
},
|
|
590
590
|
retryButton: {
|
|
591
|
-
backgroundColor:
|
|
591
|
+
backgroundColor: Global.AppTheme.primary,
|
|
592
592
|
paddingHorizontal: 20,
|
|
593
593
|
paddingVertical: 10,
|
|
594
594
|
borderRadius: 8,
|
|
595
595
|
marginTop: 10,
|
|
596
596
|
},
|
|
597
597
|
retryButtonText: {
|
|
598
|
-
color:
|
|
598
|
+
color: Global.AppTheme.light,
|
|
599
599
|
fontWeight: 'bold',
|
|
600
600
|
},
|
|
601
601
|
placeholderText: {
|
|
602
|
-
color:
|
|
602
|
+
color: Global.AppTheme.light,
|
|
603
603
|
fontSize: 16,
|
|
604
604
|
textAlign: 'center',
|
|
605
605
|
marginTop: 16,
|
|
@@ -689,12 +689,12 @@ const styles = StyleSheet.create({
|
|
|
689
689
|
borderWidth: 2,
|
|
690
690
|
},
|
|
691
691
|
stepCompleted: {
|
|
692
|
-
backgroundColor:
|
|
693
|
-
borderColor:
|
|
692
|
+
backgroundColor: Global.AppTheme.primary,
|
|
693
|
+
borderColor: Global.AppTheme.primary,
|
|
694
694
|
},
|
|
695
695
|
stepCurrent: {
|
|
696
|
-
backgroundColor:
|
|
697
|
-
borderColor:
|
|
696
|
+
backgroundColor: Global.AppTheme.primary,
|
|
697
|
+
borderColor: Global.AppTheme.primary,
|
|
698
698
|
opacity: 0.7,
|
|
699
699
|
},
|
|
700
700
|
stepPending: {
|
|
@@ -713,7 +713,7 @@ const styles = StyleSheet.create({
|
|
|
713
713
|
marginHorizontal: 4,
|
|
714
714
|
},
|
|
715
715
|
connectorCompleted: {
|
|
716
|
-
backgroundColor:
|
|
716
|
+
backgroundColor: Global.AppTheme.primary,
|
|
717
717
|
},
|
|
718
718
|
stepLabelsContainer: {
|
|
719
719
|
flexDirection: 'row',
|
package/src/components/Card.js
CHANGED
|
@@ -2,7 +2,7 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { View, Text, Image, StyleSheet, Platform } from 'react-native';
|
|
3
3
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
4
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import {
|
|
5
|
+
import { Global } from '../utils/Global';
|
|
6
6
|
|
|
7
7
|
export const Card = ({ employeeData, apiurl }) => {
|
|
8
8
|
const [imageError, setImageError] = useState(false);
|
|
@@ -44,7 +44,7 @@ export const Card = ({ employeeData, apiurl }) => {
|
|
|
44
44
|
{/* Verified Badge */}
|
|
45
45
|
<View style={styles.badgeWrapper}>
|
|
46
46
|
<View style={styles.badge}>
|
|
47
|
-
<Icon name="verified-user" size={16} color={
|
|
47
|
+
<Icon name="verified-user" size={16} color={Global.AppTheme.success} />
|
|
48
48
|
<Text style={styles.badgeText}>Identity Verified</Text>
|
|
49
49
|
</View>
|
|
50
50
|
</View>
|
|
@@ -82,7 +82,7 @@ const styles = StyleSheet.create({
|
|
|
82
82
|
height: 110,
|
|
83
83
|
borderRadius: 55,
|
|
84
84
|
borderWidth: 3,
|
|
85
|
-
borderColor:
|
|
85
|
+
borderColor: Global.AppTheme.primary,
|
|
86
86
|
overflow: 'hidden',
|
|
87
87
|
justifyContent: 'center',
|
|
88
88
|
alignItems: 'center',
|
|
@@ -100,12 +100,12 @@ const styles = StyleSheet.create({
|
|
|
100
100
|
fontSize: 20,
|
|
101
101
|
fontWeight: '600',
|
|
102
102
|
marginTop: 15,
|
|
103
|
-
color:
|
|
103
|
+
color: Global.AppTheme.light,
|
|
104
104
|
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif-medium',
|
|
105
105
|
},
|
|
106
106
|
id: {
|
|
107
107
|
fontSize: 15,
|
|
108
|
-
color:
|
|
108
|
+
color: Global.AppTheme.gray,
|
|
109
109
|
marginTop: 5,
|
|
110
110
|
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
111
111
|
},
|
|
@@ -127,7 +127,7 @@ const styles = StyleSheet.create({
|
|
|
127
127
|
},
|
|
128
128
|
badgeText: {
|
|
129
129
|
fontSize: 13,
|
|
130
|
-
color:
|
|
130
|
+
color: Global.AppTheme.light,
|
|
131
131
|
marginLeft: 6,
|
|
132
132
|
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
133
133
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect, useRef } from 'react';
|
|
2
2
|
import { View, Text, Animated, StyleSheet, Platform } from 'react-native';
|
|
3
|
-
import {
|
|
3
|
+
import { Global } from '../utils/Global';
|
|
4
4
|
|
|
5
5
|
export const CountdownTimer = ({ duration, currentTime }) => {
|
|
6
6
|
const progress = useRef(new Animated.Value(1)).current;
|
|
@@ -45,8 +45,8 @@ export const CountdownTimer = ({ duration, currentTime }) => {
|
|
|
45
45
|
|
|
46
46
|
// Change color when < 10 seconds
|
|
47
47
|
const isEnding = currentTime <= 10;
|
|
48
|
-
const circleColor = isEnding ? 'red' :
|
|
49
|
-
const textColor = isEnding ? 'red' :
|
|
48
|
+
const circleColor = isEnding ? 'red' : Global.AppTheme.light;
|
|
49
|
+
const textColor = isEnding ? 'red' : Global.AppTheme.light;
|
|
50
50
|
|
|
51
51
|
return (
|
|
52
52
|
<View style={styles.container}>
|
|
@@ -2,7 +2,7 @@ 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
4
|
import PropTypes from 'prop-types';
|
|
5
|
-
import {
|
|
5
|
+
import { Global } from '../utils/Global';
|
|
6
6
|
|
|
7
7
|
export const Notification = ({ notification, fadeAnim, slideAnim }) => {
|
|
8
8
|
if (!notification || typeof notification !== 'object') {
|
|
@@ -15,9 +15,9 @@ export const Notification = ({ notification, fadeAnim, slideAnim }) => {
|
|
|
15
15
|
|
|
16
16
|
// Icon and color mapping
|
|
17
17
|
const iconMap = {
|
|
18
|
-
success: { name: 'check-circle', color:
|
|
19
|
-
error: { name: 'error', color:
|
|
20
|
-
info: { name: 'info', color:
|
|
18
|
+
success: { name: 'check-circle', color: Global.AppTheme.success },
|
|
19
|
+
error: { name: 'error', color: Global.AppTheme.error },
|
|
20
|
+
info: { name: 'info', color: Global.AppTheme.info },
|
|
21
21
|
};
|
|
22
22
|
|
|
23
23
|
const { name: iconName, color: iconColor } = iconMap[type] || iconMap.info;
|
|
@@ -123,7 +123,7 @@ const styles = StyleSheet.create({
|
|
|
123
123
|
width: '100%',
|
|
124
124
|
flexDirection: 'row',
|
|
125
125
|
alignItems: 'center',
|
|
126
|
-
backgroundColor:
|
|
126
|
+
backgroundColor: Global.AppTheme.dark,
|
|
127
127
|
shadowColor: '#000',
|
|
128
128
|
shadowOffset: { width: 0, height: 2 },
|
|
129
129
|
shadowOpacity: 0.1,
|
|
@@ -135,7 +135,7 @@ const styles = StyleSheet.create({
|
|
|
135
135
|
},
|
|
136
136
|
message: {
|
|
137
137
|
fontSize: 14,
|
|
138
|
-
color:
|
|
138
|
+
color: Global.AppTheme.light,
|
|
139
139
|
fontWeight: '500',
|
|
140
140
|
flex: 1,
|
|
141
141
|
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { View, Text, StyleSheet } from 'react-native';
|
|
4
4
|
import Icon from 'react-native-vector-icons/MaterialIcons';
|
|
5
|
-
import {
|
|
5
|
+
import { Global } from '../utils/Global';
|
|
6
6
|
|
|
7
7
|
const StepIndicator = ({ currentStep, qrscan }) => {
|
|
8
8
|
return (
|
|
@@ -16,8 +16,8 @@ const StepIndicator = ({ currentStep, qrscan }) => {
|
|
|
16
16
|
currentStep === "Identity Verification" ||
|
|
17
17
|
currentStep === "Location Verification" ||
|
|
18
18
|
currentStep === "Complete"
|
|
19
|
-
?
|
|
20
|
-
:
|
|
19
|
+
? Global.AppTheme.primary
|
|
20
|
+
: Global.AppTheme.light
|
|
21
21
|
}
|
|
22
22
|
style={styles.statusIcon}
|
|
23
23
|
/>
|
|
@@ -44,8 +44,8 @@ const StepIndicator = ({ currentStep, qrscan }) => {
|
|
|
44
44
|
color={
|
|
45
45
|
currentStep === "Location Verification" ||
|
|
46
46
|
currentStep === "Complete"
|
|
47
|
-
?
|
|
48
|
-
:
|
|
47
|
+
? Global.AppTheme.primary
|
|
48
|
+
: Global.AppTheme.light
|
|
49
49
|
}
|
|
50
50
|
style={styles.statusIcon}
|
|
51
51
|
/>
|
|
@@ -81,7 +81,7 @@ const styles = StyleSheet.create({
|
|
|
81
81
|
},
|
|
82
82
|
statusText: {
|
|
83
83
|
fontSize: 12,
|
|
84
|
-
color:
|
|
84
|
+
color: Global.AppTheme.light,
|
|
85
85
|
opacity: 0.6,
|
|
86
86
|
},
|
|
87
87
|
statusTextActive: {
|
|
@@ -91,7 +91,7 @@ const styles = StyleSheet.create({
|
|
|
91
91
|
statusSeparator: {
|
|
92
92
|
width: 40,
|
|
93
93
|
height: 1,
|
|
94
|
-
backgroundColor:
|
|
94
|
+
backgroundColor: Global.AppTheme.light,
|
|
95
95
|
opacity: 0.3,
|
|
96
96
|
marginHorizontal: 15,
|
|
97
97
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { Global } from '../utils/Global';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Custom hook for a countdown timer with pause/resume functionality.
|
|
@@ -8,9 +8,9 @@ import { COUNTDOWN_DURATION } from '../utils/constants';
|
|
|
8
8
|
* @returns {Object} countdown, startCountdown, resetCountdown, pauseCountdown, resumeCountdown
|
|
9
9
|
*/
|
|
10
10
|
export const useCountdown = (onExpire) => {
|
|
11
|
-
const [countdown, setCountdown] = useState(
|
|
11
|
+
const [countdown, setCountdown] = useState(Global.CountdownDuration);
|
|
12
12
|
const timerRef = useRef(null);
|
|
13
|
-
const countdownRef = useRef(
|
|
13
|
+
const countdownRef = useRef(Global.CountdownDuration);
|
|
14
14
|
const isPausedRef = useRef(false);
|
|
15
15
|
const onExpireRef = useRef(onExpire);
|
|
16
16
|
|
|
@@ -23,8 +23,8 @@ export const useCountdown = (onExpire) => {
|
|
|
23
23
|
const startCountdown = useCallback((onExpireCallback) => {
|
|
24
24
|
try {
|
|
25
25
|
// Reset countdown
|
|
26
|
-
countdownRef.current =
|
|
27
|
-
setCountdown(
|
|
26
|
+
countdownRef.current = Global.CountdownDuration;
|
|
27
|
+
setCountdown(Global.CountdownDuration);
|
|
28
28
|
isPausedRef.current = false;
|
|
29
29
|
|
|
30
30
|
// Clear any existing timer
|
|
@@ -77,8 +77,8 @@ export const useCountdown = (onExpire) => {
|
|
|
77
77
|
// Reset countdown to initial duration
|
|
78
78
|
const resetCountdown = useCallback(() => {
|
|
79
79
|
try {
|
|
80
|
-
countdownRef.current =
|
|
81
|
-
setCountdown(
|
|
80
|
+
countdownRef.current = Global.CountdownDuration;
|
|
81
|
+
setCountdown(Global.CountdownDuration);
|
|
82
82
|
isPausedRef.current = false;
|
|
83
83
|
|
|
84
84
|
if (timerRef.current) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback } from 'react';
|
|
2
2
|
import ImageResizer from 'react-native-image-resizer';
|
|
3
3
|
import RNFS from 'react-native-fs';
|
|
4
|
-
import {
|
|
4
|
+
import { Global } from '../utils/Global';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Custom hook to process images: resize and convert to Base64.
|
|
@@ -38,10 +38,10 @@ export const useImageProcessing = () => {
|
|
|
38
38
|
try {
|
|
39
39
|
resizedImage = await ImageResizer.createResizedImage(
|
|
40
40
|
uri,
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
Global.ImageResize.width,
|
|
42
|
+
Global.ImageResize.height,
|
|
43
|
+
Global.ImageResize.format, // 'JPEG' or 'PNG'
|
|
44
|
+
Global.ImageResize.quality, // e.g., 80
|
|
45
45
|
0, // Rotation
|
|
46
46
|
undefined, // Output path (let library choose)
|
|
47
47
|
false // Keep EXIF metadata
|
|
@@ -64,7 +64,7 @@ export const useImageProcessing = () => {
|
|
|
64
64
|
|
|
65
65
|
// Optionally prepend MIME type
|
|
66
66
|
if (includeMimeType) {
|
|
67
|
-
const mimeType =
|
|
67
|
+
const mimeType = Global.ImageResize.format.toLowerCase() === 'png' ? 'image/png' : 'image/jpeg';
|
|
68
68
|
base64Data = `data:${mimeType};base64,${base64Data}`;
|
|
69
69
|
}
|
|
70
70
|
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
ToastAndroid,
|
|
8
8
|
Alert,
|
|
9
9
|
} from 'react-native';
|
|
10
|
-
import {
|
|
10
|
+
import { Global } from '../utils/Global';
|
|
11
11
|
|
|
12
12
|
export const useNotifyMessage = () => {
|
|
13
13
|
const [notification, setNotification] = useState({
|
|
@@ -22,9 +22,9 @@ export const useNotifyMessage = () => {
|
|
|
22
22
|
|
|
23
23
|
// Styles for notification container based on type
|
|
24
24
|
const getNotificationStyle = useCallback((type) => {
|
|
25
|
-
let backgroundColor =
|
|
26
|
-
if (type === 'success') backgroundColor =
|
|
27
|
-
if (type === 'error') backgroundColor =
|
|
25
|
+
let backgroundColor = Global.AppTheme.info;
|
|
26
|
+
if (type === 'success') backgroundColor = Global.AppTheme.success;
|
|
27
|
+
if (type === 'error') backgroundColor = Global.AppTheme.error;
|
|
28
28
|
|
|
29
29
|
return {
|
|
30
30
|
position: 'absolute',
|
package/src/index.js
CHANGED
|
@@ -27,13 +27,7 @@ import { useSafeCallback } from "./hooks/useSafeCallback";
|
|
|
27
27
|
|
|
28
28
|
// Utils
|
|
29
29
|
import { getDistanceInMeters } from "./utils/distanceCalculator";
|
|
30
|
-
import {
|
|
31
|
-
ANIMATION_STATES,
|
|
32
|
-
COLORS,
|
|
33
|
-
COUNTDOWN_DURATION,
|
|
34
|
-
MAX_DISTANCE_METERS,
|
|
35
|
-
LOADING_TYPES,
|
|
36
|
-
} from "./utils/constants";
|
|
30
|
+
import { Global } from "./utils/Global";
|
|
37
31
|
import networkServiceCall from "./utils/NetworkServiceCall";
|
|
38
32
|
import { getLoaderGif } from "./utils/getLoaderGif";
|
|
39
33
|
|
|
@@ -61,10 +55,10 @@ const BiometricModal = React.memo(
|
|
|
61
55
|
const [cameraType, setCameraType] = useState("front");
|
|
62
56
|
const [state, setState] = useState({
|
|
63
57
|
isLoading: false,
|
|
64
|
-
loadingType:
|
|
58
|
+
loadingType: Global.LoadingTypes.none,
|
|
65
59
|
currentStep: "Start",
|
|
66
60
|
employeeData: null,
|
|
67
|
-
animationState:
|
|
61
|
+
animationState: Global.AnimationStates.faceScan,
|
|
68
62
|
});
|
|
69
63
|
|
|
70
64
|
// Refs
|
|
@@ -174,10 +168,10 @@ const BiometricModal = React.memo(
|
|
|
174
168
|
|
|
175
169
|
setState({
|
|
176
170
|
isLoading: false,
|
|
177
|
-
loadingType:
|
|
171
|
+
loadingType: Global.LoadingTypes.none,
|
|
178
172
|
currentStep: "Start",
|
|
179
173
|
employeeData: null,
|
|
180
|
-
animationState:
|
|
174
|
+
animationState: Global.AnimationStates.faceScan,
|
|
181
175
|
});
|
|
182
176
|
|
|
183
177
|
setModalVisible(false);
|
|
@@ -200,9 +194,9 @@ const BiometricModal = React.memo(
|
|
|
200
194
|
|
|
201
195
|
notifyMessage(message, "error");
|
|
202
196
|
updateState({
|
|
203
|
-
animationState:
|
|
197
|
+
animationState: Global.AnimationStates.error,
|
|
204
198
|
isLoading: false,
|
|
205
|
-
loadingType:
|
|
199
|
+
loadingType: Global.LoadingTypes.none,
|
|
206
200
|
});
|
|
207
201
|
|
|
208
202
|
if (resetTimeoutRef.current) {
|
|
@@ -256,8 +250,8 @@ const BiometricModal = React.memo(
|
|
|
256
250
|
|
|
257
251
|
updateState({
|
|
258
252
|
isLoading: true,
|
|
259
|
-
loadingType:
|
|
260
|
-
animationState:
|
|
253
|
+
loadingType: Global.LoadingTypes.faceRecognition,
|
|
254
|
+
animationState: Global.AnimationStates.processing,
|
|
261
255
|
});
|
|
262
256
|
|
|
263
257
|
InteractionManager.runAfterInteractions(async () => {
|
|
@@ -266,7 +260,7 @@ const BiometricModal = React.memo(
|
|
|
266
260
|
try {
|
|
267
261
|
console.log("🖼️ Converting image to base64");
|
|
268
262
|
updateState({
|
|
269
|
-
loadingType:
|
|
263
|
+
loadingType: Global.LoadingTypes.imageProcessing,
|
|
270
264
|
});
|
|
271
265
|
|
|
272
266
|
base64 = await convertImageToBase64(selfie?.uri);
|
|
@@ -290,7 +284,7 @@ const BiometricModal = React.memo(
|
|
|
290
284
|
console.log("🌐 Calling face recognition API:", buttonapi);
|
|
291
285
|
|
|
292
286
|
updateState({
|
|
293
|
-
loadingType:
|
|
287
|
+
loadingType: Global.LoadingTypes.networkRequest,
|
|
294
288
|
});
|
|
295
289
|
|
|
296
290
|
const response = await networkServiceCall(
|
|
@@ -308,9 +302,9 @@ const BiometricModal = React.memo(
|
|
|
308
302
|
|
|
309
303
|
updateState({
|
|
310
304
|
employeeData: response.data?.data || null,
|
|
311
|
-
animationState:
|
|
305
|
+
animationState: Global.AnimationStates.success,
|
|
312
306
|
isLoading: false,
|
|
313
|
-
loadingType:
|
|
307
|
+
loadingType: Global.LoadingTypes.none,
|
|
314
308
|
});
|
|
315
309
|
|
|
316
310
|
notifyMessage("Identity verified successfully!", "success");
|
|
@@ -367,15 +361,15 @@ const BiometricModal = React.memo(
|
|
|
367
361
|
if (!validateApiUrl()) return;
|
|
368
362
|
|
|
369
363
|
updateState({
|
|
370
|
-
animationState:
|
|
364
|
+
animationState: Global.AnimationStates.processing,
|
|
371
365
|
isLoading: true,
|
|
372
|
-
loadingType:
|
|
366
|
+
loadingType: Global.LoadingTypes.locationVerification,
|
|
373
367
|
});
|
|
374
368
|
|
|
375
369
|
try {
|
|
376
370
|
console.log("📍 Requesting location permission");
|
|
377
371
|
updateState({
|
|
378
|
-
loadingType:
|
|
372
|
+
loadingType: Global.LoadingTypes.locationPermission,
|
|
379
373
|
});
|
|
380
374
|
|
|
381
375
|
const hasPermission = await requestLocationPermission();
|
|
@@ -400,7 +394,7 @@ const BiometricModal = React.memo(
|
|
|
400
394
|
console.log("📋 QR code content:", qrString);
|
|
401
395
|
|
|
402
396
|
updateState({
|
|
403
|
-
loadingType:
|
|
397
|
+
loadingType: Global.LoadingTypes.gettingLocation,
|
|
404
398
|
});
|
|
405
399
|
|
|
406
400
|
const location = await getCurrentLocation();
|
|
@@ -422,7 +416,7 @@ const BiometricModal = React.memo(
|
|
|
422
416
|
|
|
423
417
|
if (validCoords && validDev) {
|
|
424
418
|
updateState({
|
|
425
|
-
loadingType:
|
|
419
|
+
loadingType: Global.LoadingTypes.calculateDistance,
|
|
426
420
|
});
|
|
427
421
|
|
|
428
422
|
const distance = getDistanceInMeters(
|
|
@@ -438,15 +432,15 @@ const BiometricModal = React.memo(
|
|
|
438
432
|
"meters"
|
|
439
433
|
);
|
|
440
434
|
|
|
441
|
-
if (distance <=
|
|
435
|
+
if (distance <= Global.MaxDistanceMeters) {
|
|
442
436
|
console.log("✅ Location verified successfully");
|
|
443
437
|
safeCallback(responseRef.current);
|
|
444
438
|
notifyMessage("Location verified successfully!", "success");
|
|
445
439
|
|
|
446
440
|
updateState({
|
|
447
|
-
animationState:
|
|
441
|
+
animationState: Global.AnimationStates.success,
|
|
448
442
|
isLoading: false,
|
|
449
|
-
loadingType:
|
|
443
|
+
loadingType: Global.LoadingTypes.none,
|
|
450
444
|
});
|
|
451
445
|
|
|
452
446
|
if (resetTimeoutRef.current) {
|
|
@@ -517,7 +511,7 @@ const BiometricModal = React.memo(
|
|
|
517
511
|
console.log("👤 Starting face scan");
|
|
518
512
|
updateState({
|
|
519
513
|
currentStep: "Identity Verification",
|
|
520
|
-
animationState:
|
|
514
|
+
animationState: Global.AnimationStates.faceScan,
|
|
521
515
|
});
|
|
522
516
|
setCameraType("front");
|
|
523
517
|
}, [updateState]);
|
|
@@ -527,7 +521,7 @@ const BiometricModal = React.memo(
|
|
|
527
521
|
console.log("📍 Starting QR code scan");
|
|
528
522
|
updateState({
|
|
529
523
|
currentStep: "Location Verification",
|
|
530
|
-
animationState:
|
|
524
|
+
animationState: Global.AnimationStates.qrScan,
|
|
531
525
|
});
|
|
532
526
|
setCameraType("back");
|
|
533
527
|
}, [updateState]);
|
|
@@ -571,8 +565,8 @@ const BiometricModal = React.memo(
|
|
|
571
565
|
const shouldShowCamera =
|
|
572
566
|
(state.currentStep === "Identity Verification" ||
|
|
573
567
|
state.currentStep === "Location Verification") &&
|
|
574
|
-
state.animationState !==
|
|
575
|
-
state.animationState !==
|
|
568
|
+
state.animationState !== Global.AnimationStates.success &&
|
|
569
|
+
state.animationState !== Global.AnimationStates.error;
|
|
576
570
|
|
|
577
571
|
return (
|
|
578
572
|
<Modal
|
|
@@ -605,7 +599,7 @@ const BiometricModal = React.memo(
|
|
|
605
599
|
accessibilityLabel="Close modal"
|
|
606
600
|
hitSlop={{ top: 20, bottom: 20, left: 20, right: 20 }}
|
|
607
601
|
>
|
|
608
|
-
<Icon name="close" size={24} color={
|
|
602
|
+
<Icon name="close" size={24} color={Global.AppTheme.light} />
|
|
609
603
|
</TouchableOpacity>
|
|
610
604
|
|
|
611
605
|
<View style={styles.topContainer}>
|
|
@@ -639,7 +633,7 @@ const BiometricModal = React.memo(
|
|
|
639
633
|
|
|
640
634
|
<View style={styles.timerContainer}>
|
|
641
635
|
<CountdownTimer
|
|
642
|
-
duration={
|
|
636
|
+
duration={Global.CountdownDuration}
|
|
643
637
|
currentTime={countdown}
|
|
644
638
|
/>
|
|
645
639
|
</View>
|
|
@@ -657,7 +651,7 @@ const BiometricModal = React.memo(
|
|
|
657
651
|
const styles = StyleSheet.create({
|
|
658
652
|
modalContainer: {
|
|
659
653
|
flex: 1,
|
|
660
|
-
backgroundColor:
|
|
654
|
+
backgroundColor: Global.AppTheme.modalBackground || 'rgba(0, 0, 0, 0.85)',
|
|
661
655
|
},
|
|
662
656
|
cameraContainer: {
|
|
663
657
|
position: 'absolute',
|
|
@@ -694,7 +688,7 @@ const styles = StyleSheet.create({
|
|
|
694
688
|
title: {
|
|
695
689
|
fontSize: 26,
|
|
696
690
|
fontWeight: '700',
|
|
697
|
-
color:
|
|
691
|
+
color: Global.AppTheme.textLight || Global.AppTheme.light,
|
|
698
692
|
marginBottom: 5,
|
|
699
693
|
textAlign: 'left',
|
|
700
694
|
fontFamily: Platform.OS === 'ios' ? 'Helvetica Neue' : 'sans-serif',
|
|
@@ -704,7 +698,7 @@ const styles = StyleSheet.create({
|
|
|
704
698
|
},
|
|
705
699
|
subtitle: {
|
|
706
700
|
fontSize: 18,
|
|
707
|
-
color:
|
|
701
|
+
color: Global.AppTheme.textLight || Global.AppTheme.light,
|
|
708
702
|
marginBottom: 0,
|
|
709
703
|
fontWeight: '600',
|
|
710
704
|
textAlign: 'left',
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export class Global {
|
|
2
|
+
// ====== APP CONSTANTS ======
|
|
3
|
+
static AppTheme = {
|
|
4
|
+
primary: '#6C63FF',
|
|
5
|
+
primaryLight: '#8A85FF',
|
|
6
|
+
success: '#4CAF50',
|
|
7
|
+
error: '#F44336',
|
|
8
|
+
warning: '#FF9800',
|
|
9
|
+
info: '#2196F3',
|
|
10
|
+
dark: '#2D3748',
|
|
11
|
+
light: '#F7FAFC',
|
|
12
|
+
gray: '#A0AEC0',
|
|
13
|
+
background: '#F8F9FA',
|
|
14
|
+
cardBackground: '#FFFFFF',
|
|
15
|
+
textLight: '#FFFFFF',
|
|
16
|
+
shadow: '#00000033', // semi-transparent shadow
|
|
17
|
+
modalBackground: 'rgba(0, 0, 0, 0.85)',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
static LoadingTypes = {
|
|
21
|
+
none: 'none',
|
|
22
|
+
imageProcessing: 'imageProcessing',
|
|
23
|
+
faceRecognition: 'faceRecognition',
|
|
24
|
+
networkRequest: 'networkRequest',
|
|
25
|
+
locationPermission: 'locationPermission',
|
|
26
|
+
gettingLocation: 'gettingLocation',
|
|
27
|
+
calculateDistance: 'calculateDistance',
|
|
28
|
+
locationVerification: 'locationVerification',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
static AnimationStates = {
|
|
32
|
+
faceScan: 'faceScan',
|
|
33
|
+
qrScan: 'qrScan',
|
|
34
|
+
processing: 'processing',
|
|
35
|
+
success: 'success',
|
|
36
|
+
error: 'error',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
static ImageResize = {
|
|
40
|
+
width: 640,
|
|
41
|
+
height: 640,
|
|
42
|
+
format: 'JPEG', // 'PNG' or 'JPEG'
|
|
43
|
+
quality: 85, // 0–100
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
static CountdownDuration = 100; // seconds
|
|
47
|
+
static MaxDistanceMeters = 100; // Max allowed distance for QR verification
|
|
48
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Global } from "./Global";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Decides which GIF should be shown based on animationState or currentStep
|
|
@@ -13,14 +13,14 @@ export const getLoaderGif = (animationState, currentStep,APIURL) => {
|
|
|
13
13
|
`${APIURL}file/getCommonFile/image/Location.gif`;
|
|
14
14
|
|
|
15
15
|
if (
|
|
16
|
-
animationState ===
|
|
16
|
+
animationState === Global.AnimationStates.faceScan ||
|
|
17
17
|
currentStep === 'Identity Verification'
|
|
18
18
|
) {
|
|
19
19
|
return { uri: FaceGifUrl };
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
if (
|
|
23
|
-
animationState ===
|
|
23
|
+
animationState === Global.AnimationStates.qrScan ||
|
|
24
24
|
currentStep === 'Location Verification'
|
|
25
25
|
) {
|
|
26
26
|
return { uri: LocationGifUrl };
|
package/src/utils/constants.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
// ====== COLORS ======
|
|
2
|
-
export const COLORS = {
|
|
3
|
-
primary: '#6C63FF',
|
|
4
|
-
primaryLight: '#8A85FF',
|
|
5
|
-
success: '#4CAF50',
|
|
6
|
-
error: '#F44336',
|
|
7
|
-
warning: '#FF9800',
|
|
8
|
-
info: '#2196F3',
|
|
9
|
-
dark: '#2D3748',
|
|
10
|
-
light: '#F7FAFC',
|
|
11
|
-
gray: '#A0AEC0',
|
|
12
|
-
background: '#F8F9FA',
|
|
13
|
-
cardBackground: '#FFFFFF',
|
|
14
|
-
textLight: '#FFFFFF',
|
|
15
|
-
shadow: '#00000033', // semi-transparent shadow
|
|
16
|
-
modalBackground: 'rgba(0, 0, 0, 0.85)',
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// ====== LOADING TYPES ======
|
|
20
|
-
export const LOADING_TYPES = {
|
|
21
|
-
NONE: 'NONE',
|
|
22
|
-
CAMERA_INIT: 'CAMERA_INIT',
|
|
23
|
-
CAPTURING_PHOTO: 'CAPTURING_PHOTO',
|
|
24
|
-
IMAGE_PROCESSING: 'IMAGE_PROCESSING',
|
|
25
|
-
FACE_RECOGNITION: 'FACE_RECOGNITION',
|
|
26
|
-
NETWORK_REQUEST: 'NETWORK_REQUEST',
|
|
27
|
-
LOCATION_PERMISSION: 'LOCATION_PERMISSION',
|
|
28
|
-
GETTING_LOCATION: 'GETTING_LOCATION',
|
|
29
|
-
CALCULATING_DISTANCE: 'CALCULATING_DISTANCE',
|
|
30
|
-
LOCATION_VERIFICATION: 'LOCATION_VERIFICATION',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
// ====== ANIMATION STATES ======
|
|
34
|
-
export const ANIMATION_STATES = {
|
|
35
|
-
FACE_SCAN: 'faceScan',
|
|
36
|
-
QR_SCAN: 'qrScan',
|
|
37
|
-
PROCESSING: 'processing',
|
|
38
|
-
SUCCESS: 'success',
|
|
39
|
-
ERROR: 'error',
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
// ====== IMAGE RESIZE CONFIG ======
|
|
43
|
-
export const IMAGE_RESIZE = {
|
|
44
|
-
width: 640,
|
|
45
|
-
height: 640,
|
|
46
|
-
format: 'JPEG', // 'PNG' or 'JPEG'
|
|
47
|
-
quality: 85, // 0-100
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// ====== CONSTANTS ======
|
|
51
|
-
export const COUNTDOWN_DURATION = 100; // seconds
|
|
52
|
-
export const MAX_DISTANCE_METERS = 100; // Max allowed distance for QR verification
|
|
53
|
-
|
|
54
|
-
// ====== NOTIFICATION SETTINGS ======
|
|
55
|
-
export const NOTIFICATION = {
|
|
56
|
-
DEFAULT_DURATION: 3000, // ms
|
|
57
|
-
FADE_DURATION: 300, // ms
|
|
58
|
-
SLIDE_DISTANCE: 20, // px
|
|
59
|
-
VIBRATION_DURATION: 100, // ms
|
|
60
|
-
TOAST_OFFSET: 80, // Android Toast vertical offset
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
// ====== QR VERIFICATION SETTINGS ======
|
|
64
|
-
export const QR_VERIFICATION = {
|
|
65
|
-
MAX_ATTEMPTS: 3, // Maximum retries for QR scan
|
|
66
|
-
TIMEOUT: 10000, // Timeout in ms per scan
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
// ====== PLATFORM ======
|
|
70
|
-
export const PLATFORM = {
|
|
71
|
-
IOS: 'ios',
|
|
72
|
-
ANDROID: 'android',
|
|
73
|
-
};
|