react-native-biometric-verifier 0.0.7 → 0.0.8

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +142 -147
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-biometric-verifier",
3
- "version": "0.0.7",
3
+ "version": "0.0.8",
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": {
package/src/index.js CHANGED
@@ -45,6 +45,23 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
45
45
 
46
46
  const dataRef = useRef(data);
47
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
+ );
48
65
 
49
66
  useEffect(() => {
50
67
  return () => {
@@ -58,7 +75,7 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
58
75
 
59
76
  const updateState = useCallback((newState) => {
60
77
  if (mountedRef.current) {
61
- setState(prev => ({ ...prev, ...newState }));
78
+ setState((prev) => ({ ...prev, ...newState }));
62
79
  }
63
80
  }, []);
64
81
 
@@ -72,100 +89,95 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
72
89
  resetCountdown();
73
90
  }, [resetCountdown, updateState]);
74
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
+
75
102
  const handleCountdownFinish = useCallback(() => {
76
- notifyMessage('Time is up! Please try again.', 'error');
77
- updateState({
78
- modalVisible: false,
79
- animationState: ANIMATION_STATES.ERROR,
80
- });
81
- resetState();
103
+ handleProcessError('Time is up! Please try again.');
104
+ updateState({ modalVisible: false });
82
105
  if (navigation.canGoBack()) navigation.goBack();
83
- }, [navigation, notifyMessage, resetState, updateState]);
106
+ }, [handleProcessError, navigation, updateState]);
84
107
 
85
108
  const validateApiUrl = useCallback(() => {
86
109
  if (!apiurl || typeof apiurl !== 'string') {
87
- notifyMessage('Invalid API URL configuration.', 'error');
88
- updateState({ animationState: ANIMATION_STATES.ERROR });
110
+ handleProcessError('Invalid API URL configuration.');
89
111
  return false;
90
112
  }
91
113
  return true;
92
- }, [apiurl, notifyMessage, updateState]);
93
-
94
- const uploadFaceScan = useCallback(async (selfie) => {
95
- if (!validateApiUrl()) return;
96
-
97
- const currentData = dataRef.current;
98
- if (!currentData) {
99
- notifyMessage('Employee data not found.', 'error');
100
- updateState({ animationState: ANIMATION_STATES.ERROR });
101
- return;
102
- }
114
+ }, [apiurl, handleProcessError]);
103
115
 
104
- let base64;
105
- try {
106
- base64 = await convertImageToBase64(selfie?.uri);
107
- } catch (err) {
108
- notifyMessage('Image conversion failed.', 'error');
109
- updateState({ animationState: ANIMATION_STATES.ERROR });
110
- return;
111
- }
112
-
113
- if (!base64) {
114
- notifyMessage('Failed to process image.', 'error');
115
- updateState({ animationState: ANIMATION_STATES.ERROR });
116
- return;
117
- }
118
-
119
- updateState({
120
- isLoading: true,
121
- animationState: ANIMATION_STATES.PROCESSING,
122
- });
123
-
124
- try {
125
- const body = { image: base64 };
126
- const header = { faceid: currentData };
127
- const buttonapi = `${apiurl}python/recognize`;
116
+ const uploadFaceScan = useCallback(
117
+ async (selfie) => {
118
+ if (!validateApiUrl()) return;
128
119
 
129
- console.log("buttonapi--------------------", buttonapi);
120
+ const currentData = dataRef.current;
121
+ if (!currentData) {
122
+ handleProcessError('Employee data not found.');
123
+ return;
124
+ }
130
125
 
131
- const response = await networkServiceCall("POST", buttonapi, header, body);
126
+ let base64;
127
+ try {
128
+ base64 = await convertImageToBase64(selfie?.uri);
129
+ } catch (err) {
130
+ handleProcessError('Image conversion failed.', err);
131
+ return;
132
+ }
132
133
 
133
- if (response?.httpstatus === 200) {
134
- notifyMessage('Identity verified successfully!', 'success');
135
- updateState({
136
- employeeData: response.data?.data || null,
137
- animationState: ANIMATION_STATES.SUCCESS,
138
- });
134
+ if (!base64) {
135
+ handleProcessError('Failed to process image.');
136
+ return;
137
+ }
139
138
 
140
- if (qrscan) {
141
- setTimeout(() => startQRCodeScan(), 1500);
139
+ updateState({
140
+ isLoading: true,
141
+ animationState: ANIMATION_STATES.PROCESSING,
142
+ });
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
+ }
142
168
  } else {
143
- setTimeout(() => {
144
- try {
145
- callback?.(dataRef.current);
146
- } catch (err) {
147
- console.error("Callback execution failed:", err);
148
- notifyMessage('Unexpected error after verification.', 'error');
149
- }
150
- updateState({ modalVisible: false });
151
- resetState();
152
- }, 1500);
169
+ handleProcessError(
170
+ response?.data?.error?.message || 'Face not recognized. Please try again.'
171
+ );
153
172
  }
154
- } else {
155
- notifyMessage(
156
- response?.data?.error?.message || 'Face not recognized. Please try again.',
157
- 'error'
158
- );
159
- updateState({ animationState: ANIMATION_STATES.ERROR });
173
+ } catch (error) {
174
+ handleProcessError('Connection error. Please check your network.', error);
175
+ } finally {
176
+ updateState({ isLoading: false });
160
177
  }
161
- } catch (error) {
162
- console.error("Face recognition API error:", error);
163
- notifyMessage('Connection error. Please check your network.', 'error');
164
- updateState({ animationState: ANIMATION_STATES.ERROR });
165
- } finally {
166
- updateState({ isLoading: false });
167
- }
168
- }, [convertImageToBase64, notifyMessage, qrscan, resetState, updateState, callback, validateApiUrl]);
178
+ },
179
+ [convertImageToBase64, notifyMessage, qrscan, resetState, updateState, validateApiUrl, safeCallback]
180
+ );
169
181
 
170
182
  const handleStartFaceScan = useCallback(() => {
171
183
  updateState({
@@ -192,74 +204,62 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
192
204
  });
193
205
  }, [navigation, updateState]);
194
206
 
195
- const handleQRScanned = useCallback(async (qrCodeData) => {
196
- if (!validateApiUrl()) return;
207
+ const handleQRScanned = useCallback(
208
+ async (qrCodeData) => {
209
+ if (!validateApiUrl()) return;
197
210
 
198
- updateState({
199
- animationState: ANIMATION_STATES.PROCESSING,
200
- isLoading: true,
201
- });
211
+ updateState({
212
+ animationState: ANIMATION_STATES.PROCESSING,
213
+ isLoading: true,
214
+ });
202
215
 
203
- try {
204
- const hasPermission = await requestLocationPermission();
205
- if (!hasPermission) {
206
- notifyMessage('Location permission not granted.', 'error');
207
- updateState({ animationState: ANIMATION_STATES.ERROR });
208
- return;
209
- }
210
-
211
- const qrString = typeof qrCodeData === 'object' ? qrCodeData?.data : qrCodeData;
212
- if (!qrString || typeof qrString !== 'string') {
213
- notifyMessage('Invalid QR code. Please try again.', 'error');
214
- updateState({ animationState: ANIMATION_STATES.ERROR });
215
- return;
216
- }
216
+ try {
217
+ const hasPermission = await requestLocationPermission();
218
+ if (!hasPermission) {
219
+ handleProcessError('Location permission not granted.');
220
+ return;
221
+ }
217
222
 
218
- const location = await getCurrentLocation();
219
- const [latStr, lngStr] = qrString.split(',');
220
- const lat = parseFloat(latStr);
221
- const lng = parseFloat(lngStr);
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
+ }
222
228
 
223
- const validCoords = !isNaN(lat) && !isNaN(lng);
224
- const validDev = !isNaN(location?.latitude) && !isNaN(location?.longitude);
229
+ const location = await getCurrentLocation();
230
+ const [latStr, lngStr] = qrString.split(',');
231
+ const lat = parseFloat(latStr);
232
+ const lng = parseFloat(lngStr);
225
233
 
226
- if (validCoords && validDev) {
227
- const distance = getDistanceInMeters(lat, lng, location.latitude, location.longitude);
234
+ const validCoords = !isNaN(lat) && !isNaN(lng);
235
+ const validDev = !isNaN(location?.latitude) && !isNaN(location?.longitude);
228
236
 
229
- if (distance <= MAX_DISTANCE_METERS) {
230
- try {
231
- callback?.(dataRef.current);
232
- } catch (err) {
233
- console.error("Callback execution failed:", err);
234
- notifyMessage('Unexpected error during location verification.', 'error');
235
- }
237
+ if (validCoords && validDev) {
238
+ const distance = getDistanceInMeters(lat, lng, location.latitude, location.longitude);
236
239
 
237
- notifyMessage('Location verified successfully!', 'success');
238
- updateState({ animationState: ANIMATION_STATES.SUCCESS });
240
+ if (distance <= MAX_DISTANCE_METERS) {
241
+ safeCallback(responseRef.current);
242
+ notifyMessage('Location verified successfully!', 'success');
243
+ updateState({ animationState: ANIMATION_STATES.SUCCESS });
239
244
 
240
- setTimeout(() => {
241
- updateState({ modalVisible: false });
242
- resetState();
243
- }, 1500);
245
+ setTimeout(() => {
246
+ updateState({ modalVisible: false });
247
+ resetState();
248
+ }, 1500);
249
+ } else {
250
+ handleProcessError(`Location mismatch (${distance.toFixed(0)}m away).`);
251
+ }
244
252
  } else {
245
- notifyMessage(`Location mismatch (${distance.toFixed(0)}m away).`, 'error');
246
- updateState({ animationState: ANIMATION_STATES.ERROR });
247
- resetState();
253
+ handleProcessError('Invalid coordinates in QR code.');
248
254
  }
249
- } else {
250
- notifyMessage('Invalid coordinates in QR code.', 'error');
251
- updateState({ animationState: ANIMATION_STATES.ERROR });
252
- resetState();
255
+ } catch (error) {
256
+ handleProcessError('Unable to verify location. Please try again.', error);
257
+ } finally {
258
+ updateState({ isLoading: false });
253
259
  }
254
- } catch (error) {
255
- console.error("QR scan handling failed:", error);
256
- notifyMessage('Unable to verify location. Please try again.', 'error');
257
- updateState({ animationState: ANIMATION_STATES.ERROR });
258
- resetState();
259
- } finally {
260
- updateState({ isLoading: false });
261
- }
262
- }, [callback, getCurrentLocation, notifyMessage, requestLocationPermission, resetState, updateState, validateApiUrl]);
260
+ },
261
+ [getCurrentLocation, notifyMessage, requestLocationPermission, resetState, updateState, validateApiUrl, safeCallback]
262
+ );
263
263
 
264
264
  const startProcess = useCallback(() => {
265
265
  startCountdown(COUNTDOWN_DURATION, handleCountdownFinish);
@@ -303,10 +303,7 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
303
303
  <StateIndicator state={state.animationState} size={120} />
304
304
 
305
305
  {state.employeeData && (
306
- <EmployeeCard
307
- employeeData={state.employeeData}
308
- apiurl={apiurl}
309
- />
306
+ <EmployeeCard employeeData={state.employeeData} apiurl={apiurl} />
310
307
  )}
311
308
 
312
309
  <Notification
@@ -315,14 +312,12 @@ const BiometricVerificationModal = React.memo(({ data, qrscan = false, callback,
315
312
  slideAnim={slideAnim}
316
313
  />
317
314
 
318
- <CountdownTimer
319
- duration={COUNTDOWN_DURATION}
320
- currentTime={countdown}
321
- />
315
+ <CountdownTimer duration={COUNTDOWN_DURATION} currentTime={countdown} />
322
316
 
323
- {state.isLoading && getLoaderGif(state.animationState, state.currentStep, apiurl) && (
324
- <Loader source={getLoaderGif(state.animationState, state.currentStep, apiurl)} />
325
- )}
317
+ {state.isLoading &&
318
+ getLoaderGif(state.animationState, state.currentStep, apiurl) && (
319
+ <Loader source={getLoaderGif(state.animationState, state.currentStep, apiurl)} />
320
+ )}
326
321
  </View>
327
322
  </Modal>
328
323
  );