@transfergratis/react-native-sdk 0.1.22 → 0.1.24
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/android/src/main/AndroidManifest.xml +9 -4
- package/build/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.js +26 -3
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +21 -0
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
- package/build/components/KYCElements/CameraCapture.js +4 -3
- package/build/components/KYCElements/CameraCapture.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
- package/build/components/KYCElements/FileUpload.js +5 -4
- package/build/components/KYCElements/FileUpload.js.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.js +5 -4
- package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +193 -237
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
- package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js +3 -2
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +3 -2
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +3 -2
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCapture.js +4 -3
- package/build/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +182 -39
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
- package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/WelcomeTemplate.js +243 -0
- package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
- package/build/components/TemplateKYCExample.d.ts +4 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +5 -68
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +2 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +95 -9
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/components/example/DynamicTemplateExample.d.ts +10 -0
- package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
- package/build/components/example/DynamicTemplateExample.js +241 -0
- package/build/components/example/DynamicTemplateExample.js.map +1 -0
- package/build/config/allowedDomains.d.ts +30 -0
- package/build/config/allowedDomains.d.ts.map +1 -0
- package/build/config/allowedDomains.js +127 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +68 -43
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/hooks/useTemplateLoader.d.ts +14 -0
- package/build/hooks/useTemplateLoader.d.ts.map +1 -0
- package/build/hooks/useTemplateLoader.js +85 -0
- package/build/hooks/useTemplateLoader.js.map +1 -0
- package/build/i18n/en/index.d.ts +9 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +9 -0
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +9 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +9 -0
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +5 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +8 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.js +1 -0
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +4 -1
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +17 -5
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +45 -0
- package/build/modules/api/TemplateService.d.ts.map +1 -0
- package/build/modules/api/TemplateService.js +145 -0
- package/build/modules/api/TemplateService.js.map +1 -0
- package/build/modules/api/types.d.ts +1 -0
- package/build/modules/api/types.d.ts.map +1 -1
- package/build/modules/api/types.js.map +1 -1
- package/build/types/KYC.types.d.ts +144 -4
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js +15 -0
- package/build/types/KYC.types.js.map +1 -1
- package/build/utils/cropByObb.d.ts +1 -0
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +70 -0
- package/build/utils/cropByObb.js.map +1 -1
- package/build/utils/platformAlert.d.ts +20 -0
- package/build/utils/platformAlert.d.ts.map +1 -0
- package/build/utils/platformAlert.js +67 -0
- package/build/utils/platformAlert.js.map +1 -0
- package/build/utils/template-transformer.d.ts +10 -0
- package/build/utils/template-transformer.d.ts.map +1 -0
- package/build/utils/template-transformer.js +353 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +102 -20
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +196 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +2 -2
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +2 -2
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +195 -41
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +16 -71
- package/src/components/TemplateKYCFlowRefactored.tsx +121 -11
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/allowedDomains.ts +152 -0
- package/src/hooks/useTemplateKYCFlow.tsx +71 -46
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +10 -0
- package/src/i18n/fr/index.ts +9 -0
- package/src/index.ts +11 -0
- package/src/modules/api/CardAuthentification.ts +1 -1
- package/src/modules/api/KYCService.ts +18 -8
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/modules/api/types.ts +1 -0
- package/src/types/KYC.types.ts +188 -3
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/platformAlert.ts +85 -0
- package/src/utils/template-transformer.ts +433 -0
- package/src/web/WebKYCEntry.tsx +122 -24
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet,
|
|
2
|
+
import { View, Text, StyleSheet, Platform } from 'react-native';
|
|
3
3
|
import { LocationConfig, TemplateComponent } from '../../types/KYC.types';
|
|
4
4
|
import ShieldIcon from '../Svgs/Schield';
|
|
5
5
|
import GpsIcon from '../Svgs/GpsIcon';
|
|
@@ -7,6 +7,7 @@ import { Button } from '../ui/Button';
|
|
|
7
7
|
import * as Location from 'expo-location';
|
|
8
8
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
9
9
|
import { useI18n } from '../../hooks/useI18n';
|
|
10
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
10
11
|
|
|
11
12
|
interface LocationCaptureTemplateProps {
|
|
12
13
|
component: TemplateComponent;
|
|
@@ -31,6 +32,12 @@ export const LocationCaptureTemplate: React.FC<LocationCaptureTemplateProps> = (
|
|
|
31
32
|
|
|
32
33
|
const requestLocationPermission = async (): Promise<boolean> => {
|
|
33
34
|
try {
|
|
35
|
+
// On web, use browser's geolocation API directly
|
|
36
|
+
if (Platform.OS === 'web') {
|
|
37
|
+
return await requestWebGeolocationPermission();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// On native, use expo-location
|
|
34
41
|
const { status } = await Location.requestForegroundPermissionsAsync();
|
|
35
42
|
return status === 'granted';
|
|
36
43
|
} catch (err) {
|
|
@@ -39,13 +46,42 @@ export const LocationCaptureTemplate: React.FC<LocationCaptureTemplateProps> = (
|
|
|
39
46
|
}
|
|
40
47
|
};
|
|
41
48
|
|
|
49
|
+
const requestWebGeolocationPermission = async (): Promise<boolean> => {
|
|
50
|
+
if (!navigator.geolocation) {
|
|
51
|
+
console.error('Geolocation is not supported by this browser');
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
try {
|
|
56
|
+
// Check if permission API is available
|
|
57
|
+
if ('permissions' in navigator) {
|
|
58
|
+
const result = await navigator.permissions.query({ name: 'geolocation' as PermissionName });
|
|
59
|
+
return result.state === 'granted' || result.state === 'prompt';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Fallback: assume we can request permission
|
|
63
|
+
return true;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.warn('Could not check geolocation permission:', err);
|
|
66
|
+
// Assume we can try to request
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
42
71
|
const getCurrentLocation = async () => {
|
|
43
72
|
try {
|
|
44
73
|
setIsRequesting(true);
|
|
45
74
|
|
|
75
|
+
// Web platform: use browser geolocation API
|
|
76
|
+
if (Platform.OS === 'web') {
|
|
77
|
+
await getWebGeolocation();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Native platform: use expo-location
|
|
46
82
|
const hasLocationPermission = await requestLocationPermission();
|
|
47
83
|
if (!hasLocationPermission) {
|
|
48
|
-
|
|
84
|
+
showAlert(
|
|
49
85
|
t('kyc.locationCapture.permissionDeniedTitle'),
|
|
50
86
|
t('kyc.locationCapture.permissionDeniedMessage')
|
|
51
87
|
);
|
|
@@ -56,7 +92,7 @@ export const LocationCaptureTemplate: React.FC<LocationCaptureTemplateProps> = (
|
|
|
56
92
|
// Vérifier si la localisation est activée
|
|
57
93
|
const isLocationEnabled = await Location.hasServicesEnabledAsync();
|
|
58
94
|
if (!isLocationEnabled) {
|
|
59
|
-
|
|
95
|
+
showAlert(
|
|
60
96
|
t('kyc.locationCapture.locationDisabledTitle'),
|
|
61
97
|
t('kyc.locationCapture.locationDisabledMessage')
|
|
62
98
|
);
|
|
@@ -83,15 +119,66 @@ export const LocationCaptureTemplate: React.FC<LocationCaptureTemplateProps> = (
|
|
|
83
119
|
|
|
84
120
|
} catch (error) {
|
|
85
121
|
console.error('Erreur lors de la récupération de la localisation:', error);
|
|
86
|
-
|
|
122
|
+
showAlert(t('kyc.locationCapture.errorTitle'), t('kyc.locationCapture.errorMessage'));
|
|
87
123
|
} finally {
|
|
88
124
|
setIsRequesting(false);
|
|
89
125
|
}
|
|
90
126
|
};
|
|
91
127
|
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
128
|
+
const getWebGeolocation = async () => {
|
|
129
|
+
return new Promise<void>((resolve, reject) => {
|
|
130
|
+
if (!navigator.geolocation) {
|
|
131
|
+
showAlert(
|
|
132
|
+
t('kyc.locationCapture.errorTitle'),
|
|
133
|
+
'Geolocation is not supported by your browser. Please use a modern browser or enable location services.'
|
|
134
|
+
);
|
|
135
|
+
reject(new Error('Geolocation not supported'));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const options: PositionOptions = {
|
|
140
|
+
enableHighAccuracy: config.accuracy === 'high',
|
|
141
|
+
timeout: 10000,
|
|
142
|
+
maximumAge: 0,
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
navigator.geolocation.getCurrentPosition(
|
|
146
|
+
(position) => {
|
|
147
|
+
const locationData = {
|
|
148
|
+
latitude: position.coords.latitude,
|
|
149
|
+
longitude: position.coords.longitude,
|
|
150
|
+
accuracy: position.coords.accuracy,
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
setCurrentLocation(locationData);
|
|
154
|
+
onValueChange(locationData);
|
|
155
|
+
resolve();
|
|
156
|
+
},
|
|
157
|
+
(error) => {
|
|
158
|
+
console.error('Geolocation error:', error);
|
|
159
|
+
|
|
160
|
+
let errorMessage = t('kyc.locationCapture.errorMessage');
|
|
161
|
+
|
|
162
|
+
switch (error.code) {
|
|
163
|
+
case error.PERMISSION_DENIED:
|
|
164
|
+
errorMessage = Platform.OS === 'web'
|
|
165
|
+
? 'Location permission denied. Please enable location access in your browser settings and try again.'
|
|
166
|
+
: t('kyc.locationCapture.permissionDeniedMessage');
|
|
167
|
+
break;
|
|
168
|
+
case error.POSITION_UNAVAILABLE:
|
|
169
|
+
errorMessage = 'Location information is unavailable. Please check your device settings.';
|
|
170
|
+
break;
|
|
171
|
+
case error.TIMEOUT:
|
|
172
|
+
errorMessage = 'Location request timed out. Please try again.';
|
|
173
|
+
break;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
showAlert(t('kyc.locationCapture.errorTitle'), errorMessage);
|
|
177
|
+
reject(error);
|
|
178
|
+
},
|
|
179
|
+
options
|
|
180
|
+
);
|
|
181
|
+
});
|
|
95
182
|
};
|
|
96
183
|
|
|
97
184
|
// Passer automatiquement à l'étape suivante quand la localisation est obtenue
|
|
@@ -135,26 +222,7 @@ export const LocationCaptureTemplate: React.FC<LocationCaptureTemplateProps> = (
|
|
|
135
222
|
</Text>
|
|
136
223
|
</View>
|
|
137
224
|
|
|
138
|
-
{currentLocation
|
|
139
|
-
<View style={styles.locationInfo}>
|
|
140
|
-
<Text style={styles.locationTitle}>{t('kyc.locationCapture.obtained')}</Text>
|
|
141
|
-
<Text style={styles.locationDetails}>
|
|
142
|
-
{t('kyc.locationCapture.latitudeLabel')}: {currentLocation.latitude.toFixed(6)}
|
|
143
|
-
</Text>
|
|
144
|
-
<Text style={styles.locationDetails}>
|
|
145
|
-
{t('kyc.locationCapture.longitudeLabel')}: {currentLocation.longitude.toFixed(6)}
|
|
146
|
-
</Text>
|
|
147
|
-
<Text style={styles.locationDetails}>
|
|
148
|
-
{t('kyc.locationCapture.accuracyLabel')}: {currentLocation.accuracy.toFixed(1)}m
|
|
149
|
-
</Text>
|
|
150
|
-
<Button
|
|
151
|
-
title={t('kyc.locationCapture.resetButton')}
|
|
152
|
-
fullWidth
|
|
153
|
-
onPress={resetLocation}
|
|
154
|
-
style={{ paddingVertical: 20, marginTop: 24, backgroundColor: '#6B7280' }}
|
|
155
|
-
/>
|
|
156
|
-
</View>
|
|
157
|
-
) : (
|
|
225
|
+
{!currentLocation && (
|
|
158
226
|
<Button
|
|
159
227
|
title={isRequesting ? t('kyc.locationCapture.fetching') : t('kyc.locationCapture.enableButton')}
|
|
160
228
|
fullWidth
|
|
@@ -184,23 +252,6 @@ const styles = StyleSheet.create({
|
|
|
184
252
|
shadowOpacity: 0.15,
|
|
185
253
|
maxWidth: 760,
|
|
186
254
|
},
|
|
187
|
-
locationInfo: {
|
|
188
|
-
alignItems: 'center',
|
|
189
|
-
width: '100%',
|
|
190
|
-
marginTop: 24,
|
|
191
|
-
},
|
|
192
|
-
locationTitle: {
|
|
193
|
-
fontSize: 20,
|
|
194
|
-
fontWeight: '600',
|
|
195
|
-
color: '#2DBD60',
|
|
196
|
-
marginBottom: 16,
|
|
197
|
-
},
|
|
198
|
-
locationDetails: {
|
|
199
|
-
fontSize: 14,
|
|
200
|
-
color: '#666',
|
|
201
|
-
marginBottom: 4,
|
|
202
|
-
fontFamily: 'monospace',
|
|
203
|
-
},
|
|
204
255
|
title: {
|
|
205
256
|
fontSize: 24,
|
|
206
257
|
fontWeight: 'bold',
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
Text,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
TouchableOpacity,
|
|
7
|
-
Alert,
|
|
8
7
|
ActivityIndicator,
|
|
9
8
|
Animated,
|
|
10
9
|
} from 'react-native';
|
|
10
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
11
11
|
import Svg, { Rect, Path, Mask, Defs } from 'react-native-svg';
|
|
12
12
|
import { Camera, useCameraDevice } from 'react-native-vision-camera';
|
|
13
13
|
import { OrientationVideoConfig, OrientationVideoState } from '../../types/KYC.types';
|
|
@@ -204,7 +204,7 @@ export const OrientationVideoCapture: React.FC<OrientationVideoCaptureProps> = (
|
|
|
204
204
|
|
|
205
205
|
const retake = () => {
|
|
206
206
|
if (state.retakeCount >= finalConfig.maxRetakes) {
|
|
207
|
-
|
|
207
|
+
showAlert('Max Retakes Reached', 'You have reached the maximum number of retakes.');
|
|
208
208
|
return;
|
|
209
209
|
}
|
|
210
210
|
|
|
@@ -4,11 +4,11 @@ import {
|
|
|
4
4
|
Text,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
TouchableOpacity,
|
|
7
|
-
Alert,
|
|
8
7
|
ActivityIndicator,
|
|
9
8
|
Animated,
|
|
10
9
|
Dimensions,
|
|
11
10
|
} from 'react-native';
|
|
11
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
12
12
|
import Svg, { Rect, Path, Mask, Defs } from 'react-native-svg';
|
|
13
13
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
14
14
|
import { OrientationVideoConfig, OrientationVideoState } from '../../types/KYC.types';
|
|
@@ -183,7 +183,7 @@ export const OrientationVideoCaptureEnhanced: React.FC<OrientationVideoCaptureEn
|
|
|
183
183
|
|
|
184
184
|
const retake = () => {
|
|
185
185
|
if (state.retakeCount >= finalConfig.maxRetakes) {
|
|
186
|
-
|
|
186
|
+
showAlert('Max Retakes Reached', 'You have reached the maximum number of retakes.');
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
189
|
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
Text,
|
|
5
5
|
StyleSheet,
|
|
6
6
|
TouchableOpacity,
|
|
7
|
-
Alert,
|
|
8
7
|
ActivityIndicator,
|
|
9
8
|
Animated,
|
|
10
9
|
} from 'react-native';
|
|
10
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
11
11
|
import Svg, { Rect, Path, Mask, Defs } from 'react-native-svg';
|
|
12
12
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
13
13
|
import { OrientationVideoConfig, OrientationVideoState } from '../../types/KYC.types';
|
|
@@ -157,7 +157,7 @@ export const OrientationVideoCaptureFinal: React.FC<OrientationVideoCaptureFinal
|
|
|
157
157
|
|
|
158
158
|
const retake = () => {
|
|
159
159
|
if (state.retakeCount >= finalConfig.maxRetakes) {
|
|
160
|
-
|
|
160
|
+
showAlert('Max Retakes Reached', 'You have reached the maximum number of retakes.');
|
|
161
161
|
return;
|
|
162
162
|
}
|
|
163
163
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
|
-
import { View, Text, TouchableOpacity, StyleSheet, Image
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, Image } from 'react-native';
|
|
3
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
3
4
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
4
5
|
|
|
5
6
|
import { KYCElement } from '../../types/KYC.types';
|
|
@@ -39,12 +40,12 @@ export const SelfieCapture: React.FC<SelfieCaptureProps> = ({
|
|
|
39
40
|
onValueChange(result.path);
|
|
40
41
|
setShowCamera(false);
|
|
41
42
|
} else {
|
|
42
|
-
|
|
43
|
+
showAlert('Erreur', result.error || 'Impossible de prendre le selfie');
|
|
43
44
|
}
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
const handleError = (event: { message: string }) => {
|
|
47
|
-
|
|
48
|
+
showAlert('Erreur', event.message);
|
|
48
49
|
setShowCamera(false);
|
|
49
50
|
};
|
|
50
51
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet,
|
|
3
|
-
import {
|
|
2
|
+
import { View, Text, StyleSheet, ScrollView, Image, TouchableOpacity } from 'react-native';
|
|
3
|
+
import { showAlert } from '../../utils/platformAlert';
|
|
4
|
+
import { TemplateComponent, LocalizedText, ISilentCaptureResult, OrientationType, GovernmentDocumentType } from '../../types/KYC.types';
|
|
4
5
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
5
6
|
import { Button } from '../ui/Button';
|
|
6
7
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
@@ -30,14 +31,15 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
30
31
|
language,
|
|
31
32
|
}) => {
|
|
32
33
|
const { t } = useI18n();
|
|
33
|
-
const config = component.config as SelfieConfig;
|
|
34
|
-
const orientations: OrientationType[] = (
|
|
34
|
+
// const config = component.config as SelfieConfig;
|
|
35
|
+
const orientations: OrientationType[] = (['center','left','right']) as OrientationType[];
|
|
35
36
|
const { actions, state, } = useTemplateKYCFlowContext();
|
|
36
37
|
|
|
37
38
|
const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
|
|
38
39
|
|
|
39
|
-
|
|
40
40
|
const [showCamera, setShowCamera] = useState(false);
|
|
41
|
+
const [showPreview, setShowPreview] = useState(false);
|
|
42
|
+
const [previewImagePath, setPreviewImagePath] = useState<string | null>(null);
|
|
41
43
|
const [currentOrientation, setCurrentOrientation] = useState<OrientationType>(orientations[0]);
|
|
42
44
|
const [capturedImages, setCapturedImages] = useState<Record<string, IImagePayload>>(value || {});
|
|
43
45
|
|
|
@@ -78,7 +80,7 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
78
80
|
case 'left':
|
|
79
81
|
return state.currentLanguage === "en" ? "Left Profil Selfie" : "Selfie profil gauche";
|
|
80
82
|
case 'right':
|
|
81
|
-
return state.currentLanguage === "en" ? "
|
|
83
|
+
return state.currentLanguage === "en" ? "Right Profile Selfie" : "Selfie profil droit";
|
|
82
84
|
default:
|
|
83
85
|
return getLocalizedText(component.labels as LocalizedText);
|
|
84
86
|
}
|
|
@@ -162,39 +164,80 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
162
164
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
|
|
163
165
|
}
|
|
164
166
|
}).catch((e: any) => {
|
|
165
|
-
|
|
166
167
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du selfie' }));
|
|
167
168
|
});
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
// Capture manuelle - affiche le preview
|
|
171
173
|
const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
|
|
172
|
-
console.log("
|
|
174
|
+
console.log("handleCapture called", result, silentCaptureResult);
|
|
173
175
|
|
|
176
|
+
// Priorité 1: Si la capture silencieuse a réussi, utiliser ce path
|
|
174
177
|
if (silentCaptureResult.success && silentCaptureResult.path) {
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
setPreviewImagePath(silentCaptureResult.path);
|
|
179
|
+
setShowCamera(false);
|
|
180
|
+
setShowPreview(true);
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Priorité 2: Capture manuelle normale
|
|
185
|
+
if (result.success && result.path) {
|
|
186
|
+
setPreviewImagePath(result.path);
|
|
187
|
+
setShowCamera(false);
|
|
188
|
+
setShowPreview(true);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Confirmer la capture depuis le preview
|
|
193
|
+
const handleConfirmCapture = async () => {
|
|
194
|
+
if (!previewImagePath) return;
|
|
195
|
+
|
|
196
|
+
try {
|
|
197
|
+
const base64 = await pathToBase64(previewImagePath);
|
|
177
198
|
const newImages: Record<string, any> = {
|
|
178
199
|
...capturedImages,
|
|
179
|
-
[currentOrientation]: { dir:
|
|
200
|
+
[currentOrientation]: { dir: previewImagePath, file: base64 } as IImagePayload,
|
|
180
201
|
};
|
|
181
202
|
setCapturedImages(newImages);
|
|
182
203
|
onValueChange(newImages);
|
|
183
|
-
|
|
184
|
-
|
|
204
|
+
setShowPreview(false);
|
|
205
|
+
setPreviewImagePath(null);
|
|
206
|
+
setSilentCaptureResult({ success: false, isAnalyzing: false });
|
|
185
207
|
|
|
186
208
|
// Passer à l'orientation suivante si disponible
|
|
187
209
|
const currentIndex = orientations.indexOf(currentOrientation as OrientationType);
|
|
188
210
|
if (currentIndex < orientations.length - 1) {
|
|
189
|
-
|
|
211
|
+
const nextOrientation = orientations[currentIndex + 1];
|
|
212
|
+
setCurrentOrientation(nextOrientation);
|
|
213
|
+
// Rouvrir automatiquement la caméra pour la prochaine orientation
|
|
214
|
+
setShowCamera(true);
|
|
190
215
|
}
|
|
216
|
+
// Si toutes les orientations sont complétées, on reste sur la vue principale
|
|
217
|
+
} catch (e) {
|
|
218
|
+
console.error("Error confirming capture", e);
|
|
219
|
+
showAlert('Erreur', 'Erreur lors de la sauvegarde de l\'image');
|
|
191
220
|
}
|
|
192
221
|
};
|
|
222
|
+
|
|
223
|
+
// Reprendre la photo depuis le preview
|
|
224
|
+
const handleRetakeFromPreview = () => {
|
|
225
|
+
setShowPreview(false);
|
|
226
|
+
setPreviewImagePath(null);
|
|
227
|
+
setSilentCaptureResult({ success: false, isAnalyzing: false });
|
|
228
|
+
setShowCamera(true);
|
|
229
|
+
};
|
|
193
230
|
const idCardData = useMemo(() => {
|
|
194
231
|
const idCardID = Object.keys(state.componentData).find((c: string) => c === "1");
|
|
195
232
|
if (idCardID) {
|
|
196
233
|
const _idCardData = state.componentData[idCardID];
|
|
197
|
-
|
|
234
|
+
const documentType = _idCardData?.documentType;
|
|
235
|
+
// Map national_id to identity_card for selfie capture
|
|
236
|
+
const mappedDocumentType = documentType === 'national_id' ? 'identity_card' : documentType;
|
|
237
|
+
return {
|
|
238
|
+
country: _idCardData?.country,
|
|
239
|
+
documentType: mappedDocumentType as GovernmentDocumentType
|
|
240
|
+
};
|
|
198
241
|
}
|
|
199
242
|
return null;
|
|
200
243
|
}, [state.componentData]);
|
|
@@ -202,7 +245,7 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
202
245
|
|
|
203
246
|
|
|
204
247
|
const handleError = (event: { message: string }) => {
|
|
205
|
-
|
|
248
|
+
showAlert('Erreur', event.message);
|
|
206
249
|
setShowCamera(false);
|
|
207
250
|
};
|
|
208
251
|
|
|
@@ -216,7 +259,60 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
216
259
|
};
|
|
217
260
|
console.log("Current Orientation", currentOrientation);
|
|
218
261
|
|
|
262
|
+
// Vue Preview
|
|
263
|
+
if (showPreview && previewImagePath) {
|
|
264
|
+
return (
|
|
265
|
+
<View style={styles.containerCamera}>
|
|
266
|
+
<View style={styles.previewContainer}>
|
|
267
|
+
<View style={styles.previewHeader}>
|
|
268
|
+
<TouchableOpacity onPress={handleRetakeFromPreview} style={styles.backButton}>
|
|
269
|
+
<Text style={styles.backButtonText}>←</Text>
|
|
270
|
+
</TouchableOpacity>
|
|
271
|
+
<Text style={styles.previewTitle}>
|
|
272
|
+
{state.currentLanguage === "en" ? "Preview" : "Aperçu"}
|
|
273
|
+
</Text>
|
|
274
|
+
<View style={{ width: 40 }} />
|
|
275
|
+
</View>
|
|
276
|
+
|
|
277
|
+
<View style={styles.previewImageContainer}>
|
|
278
|
+
<Image
|
|
279
|
+
source={{ uri: previewImagePath }}
|
|
280
|
+
style={styles.previewImage}
|
|
281
|
+
resizeMode="contain"
|
|
282
|
+
/>
|
|
283
|
+
</View>
|
|
284
|
+
|
|
285
|
+
<Text style={styles.previewInstructions}>
|
|
286
|
+
{state.currentLanguage === "en"
|
|
287
|
+
? "Is your face clearly visible?"
|
|
288
|
+
: "Votre visage est-il clairement visible ?"}
|
|
289
|
+
</Text>
|
|
290
|
+
|
|
291
|
+
<View style={styles.previewButtonsContainer}>
|
|
292
|
+
<TouchableOpacity
|
|
293
|
+
style={styles.retakeButton}
|
|
294
|
+
onPress={handleRetakeFromPreview}
|
|
295
|
+
>
|
|
296
|
+
<Text style={styles.retakeButtonText}>
|
|
297
|
+
{state.currentLanguage === "en" ? "Retake" : "Reprendre"}
|
|
298
|
+
</Text>
|
|
299
|
+
</TouchableOpacity>
|
|
300
|
+
|
|
301
|
+
<TouchableOpacity
|
|
302
|
+
style={styles.confirmButton}
|
|
303
|
+
onPress={handleConfirmCapture}
|
|
304
|
+
>
|
|
305
|
+
<Text style={styles.confirmButtonText}>
|
|
306
|
+
{state.currentLanguage === "en" ? "Use Photo" : "Utiliser"}
|
|
307
|
+
</Text>
|
|
308
|
+
</TouchableOpacity>
|
|
309
|
+
</View>
|
|
310
|
+
</View>
|
|
311
|
+
</View>
|
|
312
|
+
);
|
|
313
|
+
}
|
|
219
314
|
|
|
315
|
+
// Vue Camera
|
|
220
316
|
if (showCamera) {
|
|
221
317
|
return (
|
|
222
318
|
<View style={styles.containerCamera}>
|
|
@@ -322,18 +418,13 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
322
418
|
<View style={{ height: 10 }} />
|
|
323
419
|
{isAllOrientationsCompleted() ? <>
|
|
324
420
|
<Button title={t('common.continue')} fullWidth style={{ paddingVertical: 20, paddingTop: 12 }} onPress={async () => {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
documentType: idCardData?.documentType as GovernmentDocumentType,
|
|
330
|
-
}
|
|
331
|
-
console.log("value", JSON.stringify(truncateFields(value), null, 2));
|
|
332
|
-
onValueChange(value);
|
|
333
|
-
} else {
|
|
334
|
-
Alert.alert('Erreur', 'Veuillez sélectionner un pays et un type de document');
|
|
335
|
-
return;
|
|
421
|
+
const value = {
|
|
422
|
+
...capturedImages,
|
|
423
|
+
...(idCardData?.country ? { country: idCardData.country } : {}),
|
|
424
|
+
...(idCardData?.documentType ? { documentType: idCardData.documentType } : {}),
|
|
336
425
|
}
|
|
426
|
+
console.log("value", JSON.stringify(truncateFields(value), null, 2));
|
|
427
|
+
onValueChange(value);
|
|
337
428
|
actions.nextComponent();
|
|
338
429
|
}} />
|
|
339
430
|
</> : (
|
|
@@ -370,23 +461,17 @@ const styles = StyleSheet.create({
|
|
|
370
461
|
borderRadius: 20,
|
|
371
462
|
paddingVertical: 20,
|
|
372
463
|
paddingHorizontal: 16,
|
|
373
|
-
// shadow
|
|
374
464
|
shadowColor: '#000',
|
|
375
465
|
shadowOffset: { width: 0, height: 2 },
|
|
376
466
|
shadowOpacity: 0.35,
|
|
377
467
|
shadowRadius: 4.84,
|
|
378
468
|
elevation: 10,
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
// padding: 16,
|
|
382
469
|
},
|
|
383
470
|
containerCamera: {
|
|
384
471
|
flex: 1,
|
|
385
|
-
// maxWidth: 760,
|
|
386
472
|
width: '100%',
|
|
387
473
|
height: '100%',
|
|
388
|
-
|
|
389
|
-
backgroundColor: '#f5f5f5',
|
|
474
|
+
backgroundColor: '#000',
|
|
390
475
|
},
|
|
391
476
|
title: {
|
|
392
477
|
fontSize: 24,
|
|
@@ -404,7 +489,6 @@ const styles = StyleSheet.create({
|
|
|
404
489
|
},
|
|
405
490
|
camera: {
|
|
406
491
|
flex: 1,
|
|
407
|
-
// borderRadius: 12,
|
|
408
492
|
overflow: 'hidden',
|
|
409
493
|
},
|
|
410
494
|
attemptsText: {
|
|
@@ -414,7 +498,6 @@ const styles = StyleSheet.create({
|
|
|
414
498
|
marginBottom: 10,
|
|
415
499
|
},
|
|
416
500
|
orientationsContainer: {
|
|
417
|
-
// flex: 1,
|
|
418
501
|
},
|
|
419
502
|
orientationContainer: {
|
|
420
503
|
backgroundColor: 'white',
|
|
@@ -434,7 +517,6 @@ const styles = StyleSheet.create({
|
|
|
434
517
|
fontSize: 18,
|
|
435
518
|
fontWeight: '600',
|
|
436
519
|
color: '#333',
|
|
437
|
-
// marginBottom: 12,
|
|
438
520
|
},
|
|
439
521
|
capturedImageContainer: {
|
|
440
522
|
alignItems: 'center',
|
|
@@ -464,14 +546,86 @@ const styles = StyleSheet.create({
|
|
|
464
546
|
fontWeight: '600',
|
|
465
547
|
color: 'white',
|
|
466
548
|
},
|
|
549
|
+
// Preview styles
|
|
550
|
+
previewContainer: {
|
|
551
|
+
flex: 1,
|
|
552
|
+
backgroundColor: '#000',
|
|
553
|
+
justifyContent: 'space-between',
|
|
554
|
+
},
|
|
555
|
+
previewHeader: {
|
|
556
|
+
flexDirection: 'row',
|
|
557
|
+
alignItems: 'center',
|
|
558
|
+
justifyContent: 'space-between',
|
|
559
|
+
paddingHorizontal: 16,
|
|
560
|
+
paddingTop: 50,
|
|
561
|
+
paddingBottom: 16,
|
|
562
|
+
},
|
|
563
|
+
backButton: {
|
|
564
|
+
width: 40,
|
|
565
|
+
height: 40,
|
|
566
|
+
borderRadius: 20,
|
|
567
|
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
568
|
+
justifyContent: 'center',
|
|
569
|
+
alignItems: 'center',
|
|
570
|
+
},
|
|
571
|
+
backButtonText: {
|
|
572
|
+
color: 'white',
|
|
573
|
+
fontSize: 24,
|
|
574
|
+
fontWeight: 'bold',
|
|
575
|
+
},
|
|
576
|
+
previewTitle: {
|
|
577
|
+
color: 'white',
|
|
578
|
+
fontSize: 18,
|
|
579
|
+
fontWeight: '600',
|
|
580
|
+
},
|
|
581
|
+
previewImageContainer: {
|
|
582
|
+
flex: 1,
|
|
583
|
+
justifyContent: 'center',
|
|
584
|
+
alignItems: 'center',
|
|
585
|
+
paddingHorizontal: 20,
|
|
586
|
+
},
|
|
587
|
+
previewImage: {
|
|
588
|
+
width: '100%',
|
|
589
|
+
height: '80%',
|
|
590
|
+
borderRadius: 16,
|
|
591
|
+
},
|
|
592
|
+
previewInstructions: {
|
|
593
|
+
color: 'white',
|
|
594
|
+
fontSize: 16,
|
|
595
|
+
textAlign: 'center',
|
|
596
|
+
paddingHorizontal: 20,
|
|
597
|
+
marginBottom: 20,
|
|
598
|
+
},
|
|
599
|
+
previewButtonsContainer: {
|
|
600
|
+
flexDirection: 'row',
|
|
601
|
+
justifyContent: 'space-between',
|
|
602
|
+
paddingHorizontal: 20,
|
|
603
|
+
paddingBottom: 40,
|
|
604
|
+
gap: 16,
|
|
605
|
+
},
|
|
467
606
|
retakeButton: {
|
|
468
|
-
|
|
469
|
-
|
|
607
|
+
flex: 1,
|
|
608
|
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
609
|
+
paddingVertical: 16,
|
|
470
610
|
paddingHorizontal: 20,
|
|
471
|
-
borderRadius:
|
|
611
|
+
borderRadius: 12,
|
|
612
|
+
alignItems: 'center',
|
|
472
613
|
},
|
|
473
614
|
retakeButtonText: {
|
|
474
|
-
fontSize:
|
|
615
|
+
fontSize: 16,
|
|
616
|
+
fontWeight: '600',
|
|
617
|
+
color: 'white',
|
|
618
|
+
},
|
|
619
|
+
confirmButton: {
|
|
620
|
+
flex: 1,
|
|
621
|
+
backgroundColor: '#2DBD60',
|
|
622
|
+
paddingVertical: 16,
|
|
623
|
+
paddingHorizontal: 20,
|
|
624
|
+
borderRadius: 12,
|
|
625
|
+
alignItems: 'center',
|
|
626
|
+
},
|
|
627
|
+
confirmButtonText: {
|
|
628
|
+
fontSize: 16,
|
|
475
629
|
fontWeight: '600',
|
|
476
630
|
color: 'white',
|
|
477
631
|
},
|