@trustchex/react-native-sdk 1.267.0 → 1.354.0
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/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +8 -2
- package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -1
- package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -1
- package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +29 -15
- package/lib/module/Screens/Static/OTPVerificationScreen.js +285 -0
- package/lib/module/Screens/Static/ResultScreen.js +90 -26
- package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +48 -134
- package/lib/module/Shared/Components/DebugNavigationPanel.js +252 -0
- package/lib/module/Shared/Components/EIDScanner.js +142 -17
- package/lib/module/Shared/Components/FaceCamera.js +23 -11
- package/lib/module/Shared/Components/IdentityDocumentCamera.js +295 -44
- package/lib/module/Shared/Components/NavigationManager.js +19 -3
- package/lib/module/Shared/Config/camera-enhancement.config.js +58 -0
- package/lib/module/Shared/Contexts/AppContext.js +1 -0
- package/lib/module/Shared/Libs/camera.utils.js +221 -1
- package/lib/module/Shared/Libs/frame-enhancement.utils.js +133 -0
- package/lib/module/Shared/Libs/mrz.utils.js +98 -1
- package/lib/module/Translation/Resources/en.js +30 -0
- package/lib/module/Translation/Resources/tr.js +30 -0
- package/lib/module/Trustchex.js +49 -39
- package/lib/module/version.js +1 -1
- package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts +3 -0
- package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -0
- package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
- package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts +3 -0
- package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts +54 -0
- package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +2 -0
- package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +65 -1
- package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +1 -1
- package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts +25 -0
- package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts.map +1 -0
- package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/en.d.ts +30 -0
- package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
- package/lib/typescript/src/Translation/Resources/tr.d.ts +30 -0
- package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
- package/lib/typescript/src/Trustchex.d.ts.map +1 -1
- package/lib/typescript/src/version.d.ts +1 -1
- package/package.json +3 -3
- package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +6 -2
- package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +3 -1
- package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +3 -1
- package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +27 -17
- package/src/Screens/Static/OTPVerificationScreen.tsx +379 -0
- package/src/Screens/Static/ResultScreen.tsx +160 -101
- package/src/Screens/Static/VerificationSessionCheckScreen.tsx +51 -196
- package/src/Shared/Components/DebugNavigationPanel.tsx +262 -0
- package/src/Shared/Components/EIDScanner.tsx +144 -19
- package/src/Shared/Components/FaceCamera.tsx +38 -21
- package/src/Shared/Components/IdentityDocumentCamera.tsx +399 -101
- package/src/Shared/Components/NavigationManager.tsx +19 -3
- package/src/Shared/Config/camera-enhancement.config.ts +46 -0
- package/src/Shared/Contexts/AppContext.ts +3 -0
- package/src/Shared/Libs/camera.utils.ts +240 -1
- package/src/Shared/Libs/frame-enhancement.utils.ts +217 -0
- package/src/Shared/Libs/mrz.utils.ts +78 -1
- package/src/Translation/Resources/en.ts +30 -0
- package/src/Translation/Resources/tr.ts +30 -0
- package/src/Trustchex.tsx +58 -46
- package/src/version.ts +1 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
4
|
-
import { Alert, View, Text, Image, StyleSheet } from 'react-native';
|
|
3
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
4
|
+
import { Alert, View, Text, Image, StyleSheet, Animated } from 'react-native';
|
|
5
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
5
6
|
import NFCManager from 'react-native-nfc-manager';
|
|
6
7
|
import DeviceInfo from 'react-native-device-info';
|
|
7
8
|
import { eidReader } from "../EIDReader/eidReader.js";
|
|
@@ -23,6 +24,7 @@ const EIDScanner = ({
|
|
|
23
24
|
isNFCSupported
|
|
24
25
|
}) => {
|
|
25
26
|
useKeepAwake();
|
|
27
|
+
const insets = useSafeAreaInsets();
|
|
26
28
|
const [isScanning, setIsScanning] = React.useState(false);
|
|
27
29
|
const [hasNfc, setHasNFC] = React.useState(false);
|
|
28
30
|
const [isEnabled, setIsEnabled] = React.useState(false);
|
|
@@ -30,6 +32,39 @@ const EIDScanner = ({
|
|
|
30
32
|
const [documentMRZInfo, setDocumentMRZInfo] = React.useState();
|
|
31
33
|
const [documentFaceImageMimeType, setDocumentFaceImageMimeType] = React.useState();
|
|
32
34
|
const [progress, setProgress] = React.useState(0);
|
|
35
|
+
const [progressStage, setProgressStage] = React.useState('');
|
|
36
|
+
|
|
37
|
+
// Animation for pulse indicator
|
|
38
|
+
const pulseAnim = useRef(new Animated.Value(1)).current;
|
|
39
|
+
|
|
40
|
+
// Format date from YYMMDD to DD/MM/YYYY (matching Flutter SDK)
|
|
41
|
+
const formatDate = useCallback(dateStr => {
|
|
42
|
+
if (!dateStr) return '';
|
|
43
|
+
try {
|
|
44
|
+
// Check if it's YYMMDD format (6 digits from NFC)
|
|
45
|
+
if (dateStr.length === 6 && /^\d{6}$/.test(dateStr)) {
|
|
46
|
+
const yy = dateStr.substring(0, 2);
|
|
47
|
+
const mm = dateStr.substring(2, 4);
|
|
48
|
+
const dd = dateStr.substring(4, 6);
|
|
49
|
+
|
|
50
|
+
// Assume 19xx if YY >= 50, else 20xx
|
|
51
|
+
const year = parseInt(yy) >= 50 ? `19${yy}` : `20${yy}`;
|
|
52
|
+
return `${dd}/${mm}/${year}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Otherwise try to parse as ISO 8601
|
|
56
|
+
const date = new Date(dateStr);
|
|
57
|
+
if (!isNaN(date.getTime())) {
|
|
58
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
59
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
60
|
+
const year = date.getFullYear();
|
|
61
|
+
return `${day}/${month}/${year}`;
|
|
62
|
+
}
|
|
63
|
+
return dateStr;
|
|
64
|
+
} catch {
|
|
65
|
+
return dateStr;
|
|
66
|
+
}
|
|
67
|
+
}, []);
|
|
33
68
|
const [isScanned, setIsScanned] = React.useState(false);
|
|
34
69
|
const [hasGuideShown, setHasGuideShown] = React.useState(false);
|
|
35
70
|
const {
|
|
@@ -45,6 +80,7 @@ const EIDScanner = ({
|
|
|
45
80
|
setIsScanning(true);
|
|
46
81
|
setIsScanned(false);
|
|
47
82
|
setProgress(0);
|
|
83
|
+
setProgressStage('');
|
|
48
84
|
|
|
49
85
|
// Track EID scan start using analytics helper
|
|
50
86
|
const docType = documentType || 'UNKNOWN';
|
|
@@ -71,6 +107,16 @@ const EIDScanner = ({
|
|
|
71
107
|
if (documentNumber && dateOfBirth && dateOfExpiry) {
|
|
72
108
|
const passportData = await eidReader(documentNumber, dateOfBirth, dateOfExpiry, progressValue => {
|
|
73
109
|
setProgress(progressValue);
|
|
110
|
+
// Update stage message based on progress
|
|
111
|
+
if (progressValue <= 20) {
|
|
112
|
+
setProgressStage(t('eidScannerScreen.connecting'));
|
|
113
|
+
} else if (progressValue <= 30) {
|
|
114
|
+
setProgressStage(t('eidScannerScreen.readingMRZ'));
|
|
115
|
+
} else if (progressValue < 100) {
|
|
116
|
+
setProgressStage(t('eidScannerScreen.readingFaceImage'));
|
|
117
|
+
} else {
|
|
118
|
+
setProgressStage(t('eidScannerScreen.completing'));
|
|
119
|
+
}
|
|
74
120
|
});
|
|
75
121
|
if (passportData) {
|
|
76
122
|
const scanDuration = Date.now() - startTime;
|
|
@@ -113,6 +159,7 @@ const EIDScanner = ({
|
|
|
113
159
|
} finally {
|
|
114
160
|
setIsScanning(false);
|
|
115
161
|
setProgress(0);
|
|
162
|
+
setProgressStage('');
|
|
116
163
|
setHasGuideShown(false);
|
|
117
164
|
}
|
|
118
165
|
}, [documentNumber, dateOfBirth, dateOfExpiry, documentType, hasNfc, isEnabled, attemptNumber, t]);
|
|
@@ -173,14 +220,56 @@ const EIDScanner = ({
|
|
|
173
220
|
setVoiceGuidanceMessage(message);
|
|
174
221
|
}
|
|
175
222
|
}, [hasGuideShown, hasNfc, isEnabled, isScanning, progress, isScanned, documentType, t, appContext.currentWorkflowStep?.data?.voiceGuidanceActive]);
|
|
223
|
+
|
|
224
|
+
// Pulse animation effect when scanning
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
if (isScanning && progress > 0) {
|
|
227
|
+
const animation = Animated.loop(Animated.sequence([Animated.timing(pulseAnim, {
|
|
228
|
+
toValue: 1.3,
|
|
229
|
+
duration: 800,
|
|
230
|
+
useNativeDriver: true
|
|
231
|
+
}), Animated.timing(pulseAnim, {
|
|
232
|
+
toValue: 1,
|
|
233
|
+
duration: 800,
|
|
234
|
+
useNativeDriver: true
|
|
235
|
+
})]));
|
|
236
|
+
animation.start();
|
|
237
|
+
return () => animation.stop();
|
|
238
|
+
} else {
|
|
239
|
+
pulseAnim.setValue(1);
|
|
240
|
+
}
|
|
241
|
+
}, [isScanning, progress, pulseAnim]);
|
|
176
242
|
useEffect(() => {
|
|
177
243
|
if (voiceGuidanceMessage && appContext.currentWorkflowStep?.data?.voiceGuidanceActive) {
|
|
178
244
|
speakWithDebounce(voiceGuidanceMessage);
|
|
179
245
|
}
|
|
180
246
|
}, [voiceGuidanceMessage, appContext.currentWorkflowStep?.data?.voiceGuidanceActive]);
|
|
181
|
-
return /*#__PURE__*/
|
|
247
|
+
return /*#__PURE__*/_jsxs(View, {
|
|
182
248
|
style: styles.container,
|
|
183
|
-
children:
|
|
249
|
+
children: [hasNfc && !isScanned && progress > 0 && /*#__PURE__*/_jsxs(View, {
|
|
250
|
+
style: [styles.topProgressContainer, {
|
|
251
|
+
paddingTop: insets.top + 16
|
|
252
|
+
}],
|
|
253
|
+
children: [/*#__PURE__*/_jsxs(View, {
|
|
254
|
+
style: styles.progressTextContainer,
|
|
255
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
256
|
+
style: styles.topScanningText,
|
|
257
|
+
children: progressStage || t('eidScannerScreen.readingDocument')
|
|
258
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
259
|
+
style: [styles.progressPercentage, {
|
|
260
|
+
color: appContext.branding.primaryColor
|
|
261
|
+
}],
|
|
262
|
+
children: [Math.round(progress), "%"]
|
|
263
|
+
})]
|
|
264
|
+
}), /*#__PURE__*/_jsx(NativeProgressBar, {
|
|
265
|
+
progress: progress / 100,
|
|
266
|
+
width: null,
|
|
267
|
+
height: 30,
|
|
268
|
+
borderRadius: 8,
|
|
269
|
+
color: appContext.branding.primaryColor,
|
|
270
|
+
backgroundColor: "#E5E5EA"
|
|
271
|
+
})]
|
|
272
|
+
}), !hasGuideShown && !isScanned ? /*#__PURE__*/_jsxs(View, {
|
|
184
273
|
style: styles.guide,
|
|
185
274
|
children: [/*#__PURE__*/_jsx(LottieView, {
|
|
186
275
|
source: require('../../Shared/Animations/nfc.json'),
|
|
@@ -281,7 +370,7 @@ const EIDScanner = ({
|
|
|
281
370
|
children: [t('eidScannerScreen.birthDate'), ":"]
|
|
282
371
|
}), /*#__PURE__*/_jsx(Text, {
|
|
283
372
|
style: styles.mrzInfoText,
|
|
284
|
-
children: documentMRZInfo.getDateOfBirth()
|
|
373
|
+
children: formatDate(documentMRZInfo.getDateOfBirth())
|
|
285
374
|
})]
|
|
286
375
|
}), /*#__PURE__*/_jsxs(View, {
|
|
287
376
|
style: styles.mrzInfoItem,
|
|
@@ -299,7 +388,7 @@ const EIDScanner = ({
|
|
|
299
388
|
children: [t('eidScannerScreen.expirationDate'), ":"]
|
|
300
389
|
}), /*#__PURE__*/_jsx(Text, {
|
|
301
390
|
style: styles.mrzInfoText,
|
|
302
|
-
children: documentMRZInfo.getDateOfExpiry()
|
|
391
|
+
children: formatDate(documentMRZInfo.getDateOfExpiry())
|
|
303
392
|
})]
|
|
304
393
|
})]
|
|
305
394
|
}), !hasNfc && /*#__PURE__*/_jsx(Text, {
|
|
@@ -332,16 +421,7 @@ const EIDScanner = ({
|
|
|
332
421
|
}) || documentType === 'UNKNOWN' && /*#__PURE__*/_jsx(Text, {
|
|
333
422
|
style: styles.mainText,
|
|
334
423
|
children: t('eidScannerScreen.placeDocumentOnNFC')
|
|
335
|
-
})), hasNfc && isEnabled &&
|
|
336
|
-
style: styles.mainText,
|
|
337
|
-
children: t('eidScannerScreen.readingDocument')
|
|
338
|
-
}), hasNfc && !isScanned && progress > 0 && /*#__PURE__*/_jsx(NativeProgressBar, {
|
|
339
|
-
progress: progress / 100,
|
|
340
|
-
width: null,
|
|
341
|
-
height: 30,
|
|
342
|
-
borderRadius: 8,
|
|
343
|
-
color: appContext.branding.primaryColor
|
|
344
|
-
}), hasNfc && isEnabled && isScanned && /*#__PURE__*/_jsx(View, {
|
|
424
|
+
})), hasNfc && isEnabled && isScanned && /*#__PURE__*/_jsx(View, {
|
|
345
425
|
style: styles.buttonsContainer,
|
|
346
426
|
children: /*#__PURE__*/_jsx(StyledButton, {
|
|
347
427
|
mode: "contained",
|
|
@@ -354,7 +434,7 @@ const EIDScanner = ({
|
|
|
354
434
|
children: t('eidScannerScreen.approveAndContinue')
|
|
355
435
|
})
|
|
356
436
|
})]
|
|
357
|
-
})
|
|
437
|
+
})]
|
|
358
438
|
});
|
|
359
439
|
};
|
|
360
440
|
const styles = StyleSheet.create({
|
|
@@ -364,6 +444,51 @@ const styles = StyleSheet.create({
|
|
|
364
444
|
gap: 10,
|
|
365
445
|
padding: 20
|
|
366
446
|
},
|
|
447
|
+
topProgressContainer: {
|
|
448
|
+
position: 'absolute',
|
|
449
|
+
top: 0,
|
|
450
|
+
left: 0,
|
|
451
|
+
right: 0,
|
|
452
|
+
zIndex: 1000,
|
|
453
|
+
paddingHorizontal: 20,
|
|
454
|
+
paddingVertical: 16,
|
|
455
|
+
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
|
456
|
+
gap: 8
|
|
457
|
+
},
|
|
458
|
+
progressTextContainer: {
|
|
459
|
+
flexDirection: 'row',
|
|
460
|
+
justifyContent: 'space-between',
|
|
461
|
+
alignItems: 'center',
|
|
462
|
+
width: '100%'
|
|
463
|
+
},
|
|
464
|
+
topScanningText: {
|
|
465
|
+
fontSize: 18,
|
|
466
|
+
fontWeight: 'bold',
|
|
467
|
+
color: '#000',
|
|
468
|
+
flex: 1
|
|
469
|
+
},
|
|
470
|
+
progressPercentage: {
|
|
471
|
+
fontSize: 18,
|
|
472
|
+
fontWeight: 'bold',
|
|
473
|
+
marginLeft: 12
|
|
474
|
+
},
|
|
475
|
+
scanningIndicator: {
|
|
476
|
+
flexDirection: 'row',
|
|
477
|
+
alignItems: 'center',
|
|
478
|
+
justifyContent: 'center',
|
|
479
|
+
gap: 8
|
|
480
|
+
},
|
|
481
|
+
pulseCircle: {
|
|
482
|
+
width: 8,
|
|
483
|
+
height: 8,
|
|
484
|
+
borderRadius: 4,
|
|
485
|
+
opacity: 0.8
|
|
486
|
+
},
|
|
487
|
+
scanningIndicatorText: {
|
|
488
|
+
fontSize: 14,
|
|
489
|
+
color: '#666',
|
|
490
|
+
fontWeight: '500'
|
|
491
|
+
},
|
|
367
492
|
buttonsContainer: {
|
|
368
493
|
flexDirection: 'column',
|
|
369
494
|
gap: 10
|
|
@@ -2,17 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
import { useIsFocused } from '@react-navigation/native';
|
|
4
4
|
import { useKeepAwake } from "../Libs/native-keep-awake.utils.js";
|
|
5
|
-
import React, { useEffect, useState } from 'react';
|
|
5
|
+
import React, { useEffect, useState, useMemo } from 'react';
|
|
6
6
|
import { StyleSheet, Text, View, Platform, Linking, Dimensions, ActivityIndicator, NativeModules } from 'react-native';
|
|
7
7
|
import { useCameraDevice, useCameraPermission, Camera, useFrameProcessor, useCameraFormat, runAtTargetFps, useMicrophonePermission } from 'react-native-vision-camera';
|
|
8
8
|
import { runAsync } from "../Libs/worklet.utils.js";
|
|
9
9
|
import { useFaceDetector } from "../VisionCameraPlugins/FaceDetector/index.js";
|
|
10
10
|
import { Worklets, useSharedValue } from 'react-native-worklets-core';
|
|
11
11
|
import { crop } from "../VisionCameraPlugins/Cropper/index.js";
|
|
12
|
-
import { isCircularRegionBright } from "../Libs/camera.utils.js";
|
|
12
|
+
import { isCircularRegionBright, isBlurry } from "../Libs/camera.utils.js";
|
|
13
13
|
import { useTranslation } from 'react-i18next';
|
|
14
14
|
import StyledButton from "./StyledButton.js";
|
|
15
15
|
import { useTheme } from "../Contexts/ThemeContext.js";
|
|
16
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
16
17
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
18
|
const VIDEO_WIDTH = 1280;
|
|
18
19
|
const VIDEO_HEIGHT = 720;
|
|
@@ -43,9 +44,7 @@ const FaceCamera = ({
|
|
|
43
44
|
fps: 30
|
|
44
45
|
}]);
|
|
45
46
|
const isCameraInitialized = useSharedValue(false);
|
|
46
|
-
const {
|
|
47
|
-
detectFaces
|
|
48
|
-
} = useFaceDetector({
|
|
47
|
+
const faceDetectorOptions = useMemo(() => ({
|
|
49
48
|
contourMode: 'none',
|
|
50
49
|
landmarkMode: 'none',
|
|
51
50
|
classificationMode: 'all',
|
|
@@ -54,7 +53,10 @@ const FaceCamera = ({
|
|
|
54
53
|
autoScale: true,
|
|
55
54
|
windowWidth: windowWidth,
|
|
56
55
|
windowHeight: windowHeight
|
|
57
|
-
});
|
|
56
|
+
}), [windowWidth, windowHeight]);
|
|
57
|
+
const {
|
|
58
|
+
detectFaces
|
|
59
|
+
} = useFaceDetector(faceDetectorOptions);
|
|
58
60
|
const {
|
|
59
61
|
t
|
|
60
62
|
} = useTranslation();
|
|
@@ -72,6 +74,7 @@ const FaceCamera = ({
|
|
|
72
74
|
}, [cameraPermission, microphonePermission]);
|
|
73
75
|
const handleFaces = Worklets.createRunOnJS((faces, image, isBright) => {
|
|
74
76
|
if (faces.length > 0) {
|
|
77
|
+
console.log('[FaceCamera] handleFaces called - faces:', faces.length, 'bright:', isBright);
|
|
75
78
|
onFacesDetected(faces, image, isBright);
|
|
76
79
|
}
|
|
77
80
|
});
|
|
@@ -151,6 +154,12 @@ const FaceCamera = ({
|
|
|
151
154
|
}
|
|
152
155
|
isBright = luminanceSum / pixelCount > 60;
|
|
153
156
|
}
|
|
157
|
+
|
|
158
|
+
// Skip blurry frames to ensure sharp face captures
|
|
159
|
+
// Lower threshold (10) for better frame acceptance
|
|
160
|
+
if (isBlurry(frame, 10)) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
154
163
|
const image = crop(frame, {
|
|
155
164
|
cropRegion: {
|
|
156
165
|
top: 0,
|
|
@@ -163,10 +172,13 @@ const FaceCamera = ({
|
|
|
163
172
|
});
|
|
164
173
|
if (image && image.base64) {
|
|
165
174
|
const faces = detectFaces(frame);
|
|
175
|
+
if (faces.length > 0) {
|
|
176
|
+
console.log('[FaceCamera] Faces detected:', faces.length);
|
|
177
|
+
}
|
|
166
178
|
handleFaces(faces, image.base64, isBright);
|
|
167
179
|
}
|
|
168
180
|
} catch (error) {
|
|
169
|
-
console.error('Face detection error:', error);
|
|
181
|
+
console.error('[FaceCamera] Face detection error:', error);
|
|
170
182
|
}
|
|
171
183
|
};
|
|
172
184
|
const frameProcessor = useFrameProcessor(frame => {
|
|
@@ -192,7 +204,7 @@ const FaceCamera = ({
|
|
|
192
204
|
}
|
|
193
205
|
}, [handleFaces, isCameraInitialized]);
|
|
194
206
|
if (!permissionsRequested) {
|
|
195
|
-
return /*#__PURE__*/_jsx(
|
|
207
|
+
return /*#__PURE__*/_jsx(SafeAreaView, {
|
|
196
208
|
style: styles.permissionContainer,
|
|
197
209
|
children: /*#__PURE__*/_jsx(ActivityIndicator, {
|
|
198
210
|
size: "large",
|
|
@@ -202,7 +214,7 @@ const FaceCamera = ({
|
|
|
202
214
|
}
|
|
203
215
|
if (!cameraPermission.hasPermission) {
|
|
204
216
|
// Camera permission denied by user - their choice, not actionable
|
|
205
|
-
return /*#__PURE__*/_jsxs(
|
|
217
|
+
return /*#__PURE__*/_jsxs(SafeAreaView, {
|
|
206
218
|
style: styles.permissionContainer,
|
|
207
219
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
208
220
|
style: styles.permissionText,
|
|
@@ -218,7 +230,7 @@ const FaceCamera = ({
|
|
|
218
230
|
}
|
|
219
231
|
if (!microphonePermission.hasPermission) {
|
|
220
232
|
// Microphone permission denied by user - their choice, not actionable
|
|
221
|
-
return /*#__PURE__*/_jsxs(
|
|
233
|
+
return /*#__PURE__*/_jsxs(SafeAreaView, {
|
|
222
234
|
style: styles.permissionContainer,
|
|
223
235
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
224
236
|
style: styles.permissionText,
|
|
@@ -234,7 +246,7 @@ const FaceCamera = ({
|
|
|
234
246
|
}
|
|
235
247
|
if (device == null) {
|
|
236
248
|
// No camera device - device limitation, not actionable
|
|
237
|
-
return /*#__PURE__*/_jsx(
|
|
249
|
+
return /*#__PURE__*/_jsx(SafeAreaView, {
|
|
238
250
|
style: styles.permissionContainer,
|
|
239
251
|
children: /*#__PURE__*/_jsx(Text, {
|
|
240
252
|
style: styles.permissionText,
|