@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,5 +1,6 @@
|
|
|
1
|
-
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
-
import { Alert, View, Text, Image, StyleSheet } from 'react-native';
|
|
1
|
+
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { Alert, View, Text, Image, StyleSheet, Animated } from 'react-native';
|
|
3
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
3
4
|
import NFCManager from 'react-native-nfc-manager';
|
|
4
5
|
import DeviceInfo from 'react-native-device-info';
|
|
5
6
|
import { MRZInfo } from '../EIDReader/lds/icao/mrzInfo';
|
|
@@ -40,6 +41,7 @@ const EIDScanner = ({
|
|
|
40
41
|
isNFCSupported,
|
|
41
42
|
}: eIDScannerProps) => {
|
|
42
43
|
useKeepAwake();
|
|
44
|
+
const insets = useSafeAreaInsets();
|
|
43
45
|
const [isScanning, setIsScanning] = React.useState(false);
|
|
44
46
|
const [hasNfc, setHasNFC] = React.useState(false);
|
|
45
47
|
const [isEnabled, setIsEnabled] = React.useState(false);
|
|
@@ -48,6 +50,41 @@ const EIDScanner = ({
|
|
|
48
50
|
const [documentFaceImageMimeType, setDocumentFaceImageMimeType] =
|
|
49
51
|
React.useState<string>();
|
|
50
52
|
const [progress, setProgress] = React.useState(0);
|
|
53
|
+
const [progressStage, setProgressStage] = React.useState<string>('');
|
|
54
|
+
|
|
55
|
+
// Animation for pulse indicator
|
|
56
|
+
const pulseAnim = useRef(new Animated.Value(1)).current;
|
|
57
|
+
|
|
58
|
+
// Format date from YYMMDD to DD/MM/YYYY (matching Flutter SDK)
|
|
59
|
+
const formatDate = useCallback((dateStr?: string | null) => {
|
|
60
|
+
if (!dateStr) return '';
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
// Check if it's YYMMDD format (6 digits from NFC)
|
|
64
|
+
if (dateStr.length === 6 && /^\d{6}$/.test(dateStr)) {
|
|
65
|
+
const yy = dateStr.substring(0, 2);
|
|
66
|
+
const mm = dateStr.substring(2, 4);
|
|
67
|
+
const dd = dateStr.substring(4, 6);
|
|
68
|
+
|
|
69
|
+
// Assume 19xx if YY >= 50, else 20xx
|
|
70
|
+
const year = parseInt(yy) >= 50 ? `19${yy}` : `20${yy}`;
|
|
71
|
+
return `${dd}/${mm}/${year}`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Otherwise try to parse as ISO 8601
|
|
75
|
+
const date = new Date(dateStr);
|
|
76
|
+
if (!isNaN(date.getTime())) {
|
|
77
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
78
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
79
|
+
const year = date.getFullYear();
|
|
80
|
+
return `${day}/${month}/${year}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return dateStr;
|
|
84
|
+
} catch {
|
|
85
|
+
return dateStr;
|
|
86
|
+
}
|
|
87
|
+
}, []);
|
|
51
88
|
const [isScanned, setIsScanned] = React.useState(false);
|
|
52
89
|
const [hasGuideShown, setHasGuideShown] = React.useState(false);
|
|
53
90
|
const { t } = useTranslation();
|
|
@@ -63,6 +100,7 @@ const EIDScanner = ({
|
|
|
63
100
|
setIsScanning(true);
|
|
64
101
|
setIsScanned(false);
|
|
65
102
|
setProgress(0);
|
|
103
|
+
setProgressStage('');
|
|
66
104
|
|
|
67
105
|
// Track EID scan start using analytics helper
|
|
68
106
|
const docType =
|
|
@@ -113,6 +151,16 @@ const EIDScanner = ({
|
|
|
113
151
|
dateOfExpiry,
|
|
114
152
|
(progressValue) => {
|
|
115
153
|
setProgress(progressValue);
|
|
154
|
+
// Update stage message based on progress
|
|
155
|
+
if (progressValue <= 20) {
|
|
156
|
+
setProgressStage(t('eidScannerScreen.connecting'));
|
|
157
|
+
} else if (progressValue <= 30) {
|
|
158
|
+
setProgressStage(t('eidScannerScreen.readingMRZ'));
|
|
159
|
+
} else if (progressValue < 100) {
|
|
160
|
+
setProgressStage(t('eidScannerScreen.readingFaceImage'));
|
|
161
|
+
} else {
|
|
162
|
+
setProgressStage(t('eidScannerScreen.completing'));
|
|
163
|
+
}
|
|
116
164
|
}
|
|
117
165
|
);
|
|
118
166
|
if (passportData) {
|
|
@@ -193,6 +241,7 @@ const EIDScanner = ({
|
|
|
193
241
|
} finally {
|
|
194
242
|
setIsScanning(false);
|
|
195
243
|
setProgress(0);
|
|
244
|
+
setProgressStage('');
|
|
196
245
|
setHasGuideShown(false);
|
|
197
246
|
}
|
|
198
247
|
}, [
|
|
@@ -282,6 +331,30 @@ const EIDScanner = ({
|
|
|
282
331
|
appContext.currentWorkflowStep?.data?.voiceGuidanceActive,
|
|
283
332
|
]);
|
|
284
333
|
|
|
334
|
+
// Pulse animation effect when scanning
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
if (isScanning && progress > 0) {
|
|
337
|
+
const animation = Animated.loop(
|
|
338
|
+
Animated.sequence([
|
|
339
|
+
Animated.timing(pulseAnim, {
|
|
340
|
+
toValue: 1.3,
|
|
341
|
+
duration: 800,
|
|
342
|
+
useNativeDriver: true,
|
|
343
|
+
}),
|
|
344
|
+
Animated.timing(pulseAnim, {
|
|
345
|
+
toValue: 1,
|
|
346
|
+
duration: 800,
|
|
347
|
+
useNativeDriver: true,
|
|
348
|
+
}),
|
|
349
|
+
])
|
|
350
|
+
);
|
|
351
|
+
animation.start();
|
|
352
|
+
return () => animation.stop();
|
|
353
|
+
} else {
|
|
354
|
+
pulseAnim.setValue(1);
|
|
355
|
+
}
|
|
356
|
+
}, [isScanning, progress, pulseAnim]);
|
|
357
|
+
|
|
285
358
|
useEffect(() => {
|
|
286
359
|
if (
|
|
287
360
|
voiceGuidanceMessage &&
|
|
@@ -296,6 +369,28 @@ const EIDScanner = ({
|
|
|
296
369
|
|
|
297
370
|
return (
|
|
298
371
|
<View style={styles.container}>
|
|
372
|
+
{/* NFC scan progress indicator and text at the top */}
|
|
373
|
+
{hasNfc && !isScanned && progress > 0 && (
|
|
374
|
+
<View style={[styles.topProgressContainer, { paddingTop: insets.top + 16 }]}>
|
|
375
|
+
<View style={styles.progressTextContainer}>
|
|
376
|
+
<Text style={styles.topScanningText}>
|
|
377
|
+
{progressStage || t('eidScannerScreen.readingDocument')}
|
|
378
|
+
</Text>
|
|
379
|
+
<Text style={[styles.progressPercentage, { color: appContext.branding.primaryColor }]}>
|
|
380
|
+
{Math.round(progress)}%
|
|
381
|
+
</Text>
|
|
382
|
+
</View>
|
|
383
|
+
<NativeProgressBar
|
|
384
|
+
progress={progress / 100}
|
|
385
|
+
width={null}
|
|
386
|
+
height={30}
|
|
387
|
+
borderRadius={8}
|
|
388
|
+
color={appContext.branding.primaryColor}
|
|
389
|
+
backgroundColor="#E5E5EA"
|
|
390
|
+
/>
|
|
391
|
+
</View>
|
|
392
|
+
)}
|
|
393
|
+
|
|
299
394
|
{!hasGuideShown && !isScanned ? (
|
|
300
395
|
<View style={styles.guide}>
|
|
301
396
|
<LottieView
|
|
@@ -405,7 +500,7 @@ const EIDScanner = ({
|
|
|
405
500
|
{t('eidScannerScreen.birthDate')}:
|
|
406
501
|
</Text>
|
|
407
502
|
<Text style={styles.mrzInfoText}>
|
|
408
|
-
{documentMRZInfo.getDateOfBirth()}
|
|
503
|
+
{formatDate(documentMRZInfo.getDateOfBirth())}
|
|
409
504
|
</Text>
|
|
410
505
|
</View>
|
|
411
506
|
<View style={styles.mrzInfoItem}>
|
|
@@ -421,7 +516,7 @@ const EIDScanner = ({
|
|
|
421
516
|
{t('eidScannerScreen.expirationDate')}:
|
|
422
517
|
</Text>
|
|
423
518
|
<Text style={styles.mrzInfoText}>
|
|
424
|
-
{documentMRZInfo.getDateOfExpiry()}
|
|
519
|
+
{formatDate(documentMRZInfo.getDateOfExpiry())}
|
|
425
520
|
</Text>
|
|
426
521
|
</View>
|
|
427
522
|
</View>
|
|
@@ -479,21 +574,6 @@ const EIDScanner = ({
|
|
|
479
574
|
</Text>
|
|
480
575
|
)))}
|
|
481
576
|
|
|
482
|
-
{hasNfc && isEnabled && isScanning && progress > 0 && (
|
|
483
|
-
<Text style={styles.mainText}>
|
|
484
|
-
{t('eidScannerScreen.readingDocument')}
|
|
485
|
-
</Text>
|
|
486
|
-
)}
|
|
487
|
-
|
|
488
|
-
{hasNfc && !isScanned && progress > 0 && (
|
|
489
|
-
<NativeProgressBar
|
|
490
|
-
progress={progress / 100}
|
|
491
|
-
width={null}
|
|
492
|
-
height={30}
|
|
493
|
-
borderRadius={8}
|
|
494
|
-
color={appContext.branding.primaryColor}
|
|
495
|
-
/>
|
|
496
|
-
)}
|
|
497
577
|
|
|
498
578
|
{hasNfc && isEnabled && isScanned && (
|
|
499
579
|
<View style={styles.buttonsContainer}>
|
|
@@ -532,6 +612,51 @@ const styles = StyleSheet.create({
|
|
|
532
612
|
gap: 10,
|
|
533
613
|
padding: 20,
|
|
534
614
|
},
|
|
615
|
+
topProgressContainer: {
|
|
616
|
+
position: 'absolute',
|
|
617
|
+
top: 0,
|
|
618
|
+
left: 0,
|
|
619
|
+
right: 0,
|
|
620
|
+
zIndex: 1000,
|
|
621
|
+
paddingHorizontal: 20,
|
|
622
|
+
paddingVertical: 16,
|
|
623
|
+
backgroundColor: 'rgba(255, 255, 255, 0.95)',
|
|
624
|
+
gap: 8,
|
|
625
|
+
},
|
|
626
|
+
progressTextContainer: {
|
|
627
|
+
flexDirection: 'row',
|
|
628
|
+
justifyContent: 'space-between',
|
|
629
|
+
alignItems: 'center',
|
|
630
|
+
width: '100%',
|
|
631
|
+
},
|
|
632
|
+
topScanningText: {
|
|
633
|
+
fontSize: 18,
|
|
634
|
+
fontWeight: 'bold',
|
|
635
|
+
color: '#000',
|
|
636
|
+
flex: 1,
|
|
637
|
+
},
|
|
638
|
+
progressPercentage: {
|
|
639
|
+
fontSize: 18,
|
|
640
|
+
fontWeight: 'bold',
|
|
641
|
+
marginLeft: 12,
|
|
642
|
+
},
|
|
643
|
+
scanningIndicator: {
|
|
644
|
+
flexDirection: 'row',
|
|
645
|
+
alignItems: 'center',
|
|
646
|
+
justifyContent: 'center',
|
|
647
|
+
gap: 8,
|
|
648
|
+
},
|
|
649
|
+
pulseCircle: {
|
|
650
|
+
width: 8,
|
|
651
|
+
height: 8,
|
|
652
|
+
borderRadius: 4,
|
|
653
|
+
opacity: 0.8,
|
|
654
|
+
},
|
|
655
|
+
scanningIndicatorText: {
|
|
656
|
+
fontSize: 14,
|
|
657
|
+
color: '#666',
|
|
658
|
+
fontWeight: '500',
|
|
659
|
+
},
|
|
535
660
|
buttonsContainer: {
|
|
536
661
|
flexDirection: 'column',
|
|
537
662
|
gap: 10,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useIsFocused } from '@react-navigation/native';
|
|
2
2
|
import { useKeepAwake } from '../Libs/native-keep-awake.utils';
|
|
3
|
-
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import React, { useEffect, useState, useMemo } from 'react';
|
|
4
4
|
import {
|
|
5
5
|
StyleSheet,
|
|
6
6
|
Text,
|
|
@@ -28,10 +28,11 @@ import {
|
|
|
28
28
|
} from '../VisionCameraPlugins/FaceDetector';
|
|
29
29
|
import { Worklets, useSharedValue } from 'react-native-worklets-core';
|
|
30
30
|
import { crop } from '../VisionCameraPlugins/Cropper';
|
|
31
|
-
import { isCircularRegionBright } from '../Libs/camera.utils';
|
|
31
|
+
import { isCircularRegionBright, isBlurry } from '../Libs/camera.utils';
|
|
32
32
|
import { useTranslation } from 'react-i18next';
|
|
33
33
|
import StyledButton from './StyledButton';
|
|
34
34
|
import { useTheme } from '../Contexts/ThemeContext';
|
|
35
|
+
import { SafeAreaView } from 'react-native-safe-area-context';
|
|
35
36
|
|
|
36
37
|
export type FaceCameraProps = {
|
|
37
38
|
onFacesDetected: (
|
|
@@ -78,16 +79,22 @@ const FaceCamera = ({
|
|
|
78
79
|
},
|
|
79
80
|
]);
|
|
80
81
|
const isCameraInitialized = useSharedValue(false);
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
|
|
83
|
+
const faceDetectorOptions = useMemo(
|
|
84
|
+
() => ({
|
|
85
|
+
contourMode: 'none' as const,
|
|
86
|
+
landmarkMode: 'none' as const,
|
|
87
|
+
classificationMode: 'all' as const,
|
|
88
|
+
performanceMode: 'accurate' as const,
|
|
89
|
+
trackingEnabled: false,
|
|
90
|
+
autoScale: true,
|
|
91
|
+
windowWidth: windowWidth,
|
|
92
|
+
windowHeight: windowHeight,
|
|
93
|
+
}),
|
|
94
|
+
[windowWidth, windowHeight]
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const { detectFaces } = useFaceDetector(faceDetectorOptions);
|
|
91
98
|
const { t } = useTranslation();
|
|
92
99
|
|
|
93
100
|
useEffect(() => {
|
|
@@ -106,6 +113,7 @@ const FaceCamera = ({
|
|
|
106
113
|
const handleFaces = Worklets.createRunOnJS(
|
|
107
114
|
(faces: Face[], image: string, isBright: boolean) => {
|
|
108
115
|
if (faces.length > 0) {
|
|
116
|
+
console.log('[FaceCamera] handleFaces called - faces:', faces.length, 'bright:', isBright);
|
|
109
117
|
onFacesDetected(faces, image, isBright);
|
|
110
118
|
}
|
|
111
119
|
}
|
|
@@ -192,6 +200,12 @@ const FaceCamera = ({
|
|
|
192
200
|
isBright = (luminanceSum / pixelCount) > 60;
|
|
193
201
|
}
|
|
194
202
|
|
|
203
|
+
// Skip blurry frames to ensure sharp face captures
|
|
204
|
+
// Lower threshold (10) for better frame acceptance
|
|
205
|
+
if (isBlurry(frame, 10)) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
195
209
|
const image = crop(frame, {
|
|
196
210
|
cropRegion: {
|
|
197
211
|
top: 0,
|
|
@@ -205,10 +219,13 @@ const FaceCamera = ({
|
|
|
205
219
|
|
|
206
220
|
if (image && image.base64) {
|
|
207
221
|
const faces = detectFaces(frame);
|
|
222
|
+
if (faces.length > 0) {
|
|
223
|
+
console.log('[FaceCamera] Faces detected:', faces.length);
|
|
224
|
+
}
|
|
208
225
|
handleFaces(faces, image.base64, isBright);
|
|
209
226
|
}
|
|
210
227
|
} catch (error) {
|
|
211
|
-
console.error('Face detection error:', error);
|
|
228
|
+
console.error('[FaceCamera] Face detection error:', error);
|
|
212
229
|
}
|
|
213
230
|
};
|
|
214
231
|
|
|
@@ -237,16 +254,16 @@ const FaceCamera = ({
|
|
|
237
254
|
|
|
238
255
|
if (!permissionsRequested) {
|
|
239
256
|
return (
|
|
240
|
-
<
|
|
257
|
+
<SafeAreaView style={styles.permissionContainer}>
|
|
241
258
|
<ActivityIndicator size="large" color={theme.colors.primary} />
|
|
242
|
-
</
|
|
259
|
+
</SafeAreaView>
|
|
243
260
|
);
|
|
244
261
|
}
|
|
245
262
|
|
|
246
263
|
if (!cameraPermission.hasPermission) {
|
|
247
264
|
// Camera permission denied by user - their choice, not actionable
|
|
248
265
|
return (
|
|
249
|
-
<
|
|
266
|
+
<SafeAreaView style={styles.permissionContainer}>
|
|
250
267
|
<Text style={styles.permissionText}>
|
|
251
268
|
{t('general.noCameraPermissionGiven')}
|
|
252
269
|
</Text>
|
|
@@ -258,14 +275,14 @@ const FaceCamera = ({
|
|
|
258
275
|
>
|
|
259
276
|
{t('general.openSettings')}
|
|
260
277
|
</StyledButton>
|
|
261
|
-
</
|
|
278
|
+
</SafeAreaView>
|
|
262
279
|
);
|
|
263
280
|
}
|
|
264
281
|
|
|
265
282
|
if (!microphonePermission.hasPermission) {
|
|
266
283
|
// Microphone permission denied by user - their choice, not actionable
|
|
267
284
|
return (
|
|
268
|
-
<
|
|
285
|
+
<SafeAreaView style={styles.permissionContainer}>
|
|
269
286
|
<Text style={styles.permissionText}>
|
|
270
287
|
{t('general.noMicrophonePermissionGiven')}
|
|
271
288
|
</Text>
|
|
@@ -277,18 +294,18 @@ const FaceCamera = ({
|
|
|
277
294
|
>
|
|
278
295
|
{t('general.openSettings')}
|
|
279
296
|
</StyledButton>
|
|
280
|
-
</
|
|
297
|
+
</SafeAreaView>
|
|
281
298
|
);
|
|
282
299
|
}
|
|
283
300
|
|
|
284
301
|
if (device == null) {
|
|
285
302
|
// No camera device - device limitation, not actionable
|
|
286
303
|
return (
|
|
287
|
-
<
|
|
304
|
+
<SafeAreaView style={styles.permissionContainer}>
|
|
288
305
|
<Text style={styles.permissionText}>
|
|
289
306
|
{t('general.noCameraDetected')}
|
|
290
307
|
</Text>
|
|
291
|
-
</
|
|
308
|
+
</SafeAreaView>
|
|
292
309
|
);
|
|
293
310
|
}
|
|
294
311
|
|