@transfergratis/react-native-sdk 0.1.23 → 0.1.25
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/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
- package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
- package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
- package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
- package/android/src/main/AndroidManifest.xml +13 -5
- 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/AdditionalDocumentsTemplate.d.ts +12 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
- 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/EmailVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
- 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 +356 -227
- 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.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js +5 -4
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
- 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 +189 -42
- 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 +8 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +10 -97
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +6 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +108 -11
- 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/KYCConfig.d.ts +14 -0
- package/build/config/KYCConfig.d.ts.map +1 -0
- package/build/config/KYCConfig.js +26 -0
- package/build/config/KYCConfig.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 +112 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useOrientationVideo.d.ts +2 -1
- package/build/hooks/useOrientationVideo.d.ts.map +1 -1
- package/build/hooks/useOrientationVideo.js +3 -3
- package/build/hooks/useOrientationVideo.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +317 -34
- 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 +49 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +50 -1
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +35 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +36 -1
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +24 -3
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +69 -10
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +7 -7
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +108 -39
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/SelfieVerification.d.ts +3 -1
- package/build/modules/api/SelfieVerification.d.ts.map +1 -1
- package/build/modules/api/SelfieVerification.js +17 -1
- package/build/modules/api/SelfieVerification.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +44 -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/types/KYC.types.d.ts +265 -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/types/env.types.d.ts +13 -0
- package/build/types/env.types.d.ts.map +1 -0
- package/build/types/env.types.js +2 -0
- package/build/types/env.types.js.map +1 -0
- 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/deviceDetection.d.ts +6 -0
- package/build/utils/deviceDetection.d.ts.map +1 -0
- package/build/utils/deviceDetection.js +12 -0
- package/build/utils/deviceDetection.js.map +1 -0
- 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 +365 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +158 -32
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.ts +3 -4
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +397 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +6 -3
- package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +201 -44
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +37 -108
- package/src/components/TemplateKYCFlowRefactored.tsx +148 -12
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/KYCConfig.ts +34 -0
- package/src/config/allowedDomains.ts +133 -0
- package/src/hooks/useOrientationVideo.ts +5 -4
- package/src/hooks/useTemplateKYCFlow.tsx +347 -32
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +53 -2
- package/src/i18n/fr/index.ts +37 -1
- package/src/index.ts +14 -0
- package/src/modules/api/CardAuthentification.ts +76 -11
- package/src/modules/api/KYCService.ts +129 -45
- package/src/modules/api/SelfieVerification.ts +25 -3
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/types/KYC.types.ts +331 -3
- package/src/types/env.types.ts +13 -0
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/deviceDetection.ts +11 -0
- package/src/utils/platformAlert.ts +86 -0
- package/src/utils/template-transformer.ts +445 -0
- package/src/web/WebKYCEntry.tsx +199 -50
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert } from 'react-native';
|
|
3
|
+
import { TemplateComponent, LocalizedText } from '../../types/KYC.types';
|
|
4
|
+
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
5
|
+
import { useI18n } from '../../hooks/useI18n';
|
|
6
|
+
import { Button } from '../ui/Button';
|
|
7
|
+
|
|
8
|
+
interface PhoneVerificationTemplateProps {
|
|
9
|
+
component: TemplateComponent;
|
|
10
|
+
value?: any;
|
|
11
|
+
onValueChange: (data: any) => void;
|
|
12
|
+
error?: string;
|
|
13
|
+
language?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
type VerificationStep = 'phone' | 'otp';
|
|
17
|
+
|
|
18
|
+
export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps> = ({
|
|
19
|
+
component,
|
|
20
|
+
value,
|
|
21
|
+
onValueChange,
|
|
22
|
+
error: propError,
|
|
23
|
+
}) => {
|
|
24
|
+
const { actions, getLocalizedText } = useTemplateKYCFlowContext();
|
|
25
|
+
const { t } = useI18n();
|
|
26
|
+
// const config = component.config as PhoneVerificationConfig;
|
|
27
|
+
|
|
28
|
+
// State
|
|
29
|
+
const [step, setStep] = useState<VerificationStep>('phone');
|
|
30
|
+
const [phone, setPhone] = useState('');
|
|
31
|
+
const [otp, setOtp] = useState('');
|
|
32
|
+
const [localError, setLocalError] = useState<string | null>(null);
|
|
33
|
+
const [isSimulating, setIsSimulating] = useState(false);
|
|
34
|
+
|
|
35
|
+
const title = getLocalizedText(component.labels as LocalizedText);
|
|
36
|
+
const instructions = getLocalizedText(component.instructions as LocalizedText);
|
|
37
|
+
|
|
38
|
+
// Determine button text based on step
|
|
39
|
+
const verifyButtonText = getLocalizedText((component.ui as any).buttonText) || t('common.verify') || 'Verify';
|
|
40
|
+
const sendButtonText = t('common.sendCode') || 'Send Verification Code';
|
|
41
|
+
const buttonText = step === 'phone' ? sendButtonText : verifyButtonText;
|
|
42
|
+
|
|
43
|
+
const handleSendCode = () => {
|
|
44
|
+
if (!phone || phone.length < 5) {
|
|
45
|
+
setLocalError(t('errors.invalidPhone') || 'Please enter a valid phone number');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
setLocalError(null);
|
|
50
|
+
setIsSimulating(true);
|
|
51
|
+
|
|
52
|
+
// Simulate API call to send code
|
|
53
|
+
setTimeout(() => {
|
|
54
|
+
setIsSimulating(false);
|
|
55
|
+
setStep('otp');
|
|
56
|
+
}, 1500);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleVerifyCode = () => {
|
|
60
|
+
if (!otp || otp.length < 4) {
|
|
61
|
+
setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setLocalError(null);
|
|
66
|
+
setIsSimulating(true);
|
|
67
|
+
|
|
68
|
+
// Simulate verification API
|
|
69
|
+
setTimeout(() => {
|
|
70
|
+
setIsSimulating(false);
|
|
71
|
+
|
|
72
|
+
// Mock validation logic
|
|
73
|
+
if (otp === '123456') {
|
|
74
|
+
onValueChange({ phone, otp, verified: true });
|
|
75
|
+
actions.nextComponent();
|
|
76
|
+
} else {
|
|
77
|
+
setLocalError(t('errors.wrongCode') || 'Invalid verification code. Try 123456');
|
|
78
|
+
}
|
|
79
|
+
}, 1500);
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const onChangePhone = (text: string) => {
|
|
83
|
+
setPhone(text);
|
|
84
|
+
if (localError) setLocalError(null);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const onChangeOtp = (text: string) => {
|
|
88
|
+
setOtp(text);
|
|
89
|
+
if (localError) setLocalError(null);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const handleBackToPhone = () => {
|
|
93
|
+
setStep('phone');
|
|
94
|
+
setOtp('');
|
|
95
|
+
setLocalError(null);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<View style={styles.container}>
|
|
100
|
+
<Text style={styles.title}>{title}</Text>
|
|
101
|
+
<Text style={styles.instructions}>
|
|
102
|
+
{step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${phone}`)}
|
|
103
|
+
</Text>
|
|
104
|
+
|
|
105
|
+
<View style={styles.contentContainer}>
|
|
106
|
+
{step === 'phone' ? (
|
|
107
|
+
<View style={styles.inputContainer}>
|
|
108
|
+
<Text style={styles.label}>{t('common.phone') || 'Phone Number'}</Text>
|
|
109
|
+
<TextInput
|
|
110
|
+
style={styles.input}
|
|
111
|
+
placeholder="+1234567890"
|
|
112
|
+
value={phone}
|
|
113
|
+
onChangeText={onChangePhone}
|
|
114
|
+
keyboardType="phone-pad"
|
|
115
|
+
autoComplete="tel"
|
|
116
|
+
editable={!isSimulating}
|
|
117
|
+
/>
|
|
118
|
+
</View>
|
|
119
|
+
) : (
|
|
120
|
+
<View style={styles.inputContainer}>
|
|
121
|
+
<Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>
|
|
122
|
+
<TextInput
|
|
123
|
+
style={styles.input}
|
|
124
|
+
placeholder="123456"
|
|
125
|
+
value={otp}
|
|
126
|
+
onChangeText={onChangeOtp}
|
|
127
|
+
keyboardType="number-pad"
|
|
128
|
+
maxLength={6}
|
|
129
|
+
editable={!isSimulating}
|
|
130
|
+
/>
|
|
131
|
+
<TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink}>
|
|
132
|
+
<Text style={styles.changeText}>{t('common.back') || 'Change number'}</Text>
|
|
133
|
+
</TouchableOpacity>
|
|
134
|
+
</View>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
{(localError || propError) && (
|
|
138
|
+
<Text style={styles.errorText}>{localError || propError}</Text>
|
|
139
|
+
)}
|
|
140
|
+
|
|
141
|
+
<Button
|
|
142
|
+
title={isSimulating ? (t('common.processing') || 'Processing...') : buttonText}
|
|
143
|
+
onPress={step === 'phone' ? handleSendCode : handleVerifyCode}
|
|
144
|
+
style={styles.button}
|
|
145
|
+
disabled={
|
|
146
|
+
isSimulating ||
|
|
147
|
+
(step === 'phone' ? !phone : !otp)
|
|
148
|
+
}
|
|
149
|
+
/>
|
|
150
|
+
|
|
151
|
+
{step === 'otp' && (
|
|
152
|
+
<TouchableOpacity
|
|
153
|
+
onPress={() => {
|
|
154
|
+
// Resend logic
|
|
155
|
+
Alert.alert(
|
|
156
|
+
t('common.codeResent') || 'Code Resent',
|
|
157
|
+
t('common.codeResentMessage', { email: phone }) || 'Code resent to ' + phone
|
|
158
|
+
);
|
|
159
|
+
}}
|
|
160
|
+
style={styles.resendButton}
|
|
161
|
+
disabled={isSimulating}
|
|
162
|
+
>
|
|
163
|
+
<Text style={styles.resendText}>{t('common.resendCode') || 'Resend Code'}</Text>
|
|
164
|
+
</TouchableOpacity>
|
|
165
|
+
)}
|
|
166
|
+
</View>
|
|
167
|
+
</View>
|
|
168
|
+
);
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const styles = StyleSheet.create({
|
|
172
|
+
container: {
|
|
173
|
+
padding: 24,
|
|
174
|
+
backgroundColor: 'white',
|
|
175
|
+
borderRadius: 16,
|
|
176
|
+
margin: 16,
|
|
177
|
+
shadowColor: '#000',
|
|
178
|
+
shadowOffset: { width: 0, height: 4 },
|
|
179
|
+
shadowOpacity: 0.1,
|
|
180
|
+
shadowRadius: 12,
|
|
181
|
+
elevation: 5,
|
|
182
|
+
width: '95%',
|
|
183
|
+
},
|
|
184
|
+
title: {
|
|
185
|
+
fontSize: 24,
|
|
186
|
+
fontWeight: '700',
|
|
187
|
+
marginBottom: 8,
|
|
188
|
+
color: '#1a1a1a',
|
|
189
|
+
textAlign: 'center',
|
|
190
|
+
},
|
|
191
|
+
instructions: {
|
|
192
|
+
fontSize: 16,
|
|
193
|
+
color: '#666',
|
|
194
|
+
marginBottom: 32,
|
|
195
|
+
lineHeight: 24,
|
|
196
|
+
textAlign: 'center',
|
|
197
|
+
},
|
|
198
|
+
contentContainer: {
|
|
199
|
+
// width: '100%',
|
|
200
|
+
},
|
|
201
|
+
inputContainer: {
|
|
202
|
+
marginBottom: 24,
|
|
203
|
+
},
|
|
204
|
+
label: {
|
|
205
|
+
fontSize: 14,
|
|
206
|
+
fontWeight: '600',
|
|
207
|
+
color: '#333',
|
|
208
|
+
marginBottom: 8,
|
|
209
|
+
marginLeft: 4,
|
|
210
|
+
},
|
|
211
|
+
input: {
|
|
212
|
+
borderWidth: 1,
|
|
213
|
+
borderColor: '#e0e0e0',
|
|
214
|
+
padding: 16,
|
|
215
|
+
borderRadius: 12,
|
|
216
|
+
fontSize: 16,
|
|
217
|
+
backgroundColor: '#f8f9fa',
|
|
218
|
+
color: '#333',
|
|
219
|
+
},
|
|
220
|
+
errorText: {
|
|
221
|
+
color: '#dc2626',
|
|
222
|
+
marginBottom: 16,
|
|
223
|
+
fontSize: 14,
|
|
224
|
+
textAlign: 'center',
|
|
225
|
+
backgroundColor: '#fee2e2',
|
|
226
|
+
padding: 8,
|
|
227
|
+
borderRadius: 8,
|
|
228
|
+
overflow: 'hidden',
|
|
229
|
+
},
|
|
230
|
+
button: {
|
|
231
|
+
height: 50,
|
|
232
|
+
borderRadius: 12,
|
|
233
|
+
width: "100%"
|
|
234
|
+
},
|
|
235
|
+
changeLink: {
|
|
236
|
+
alignSelf: 'flex-end',
|
|
237
|
+
marginTop: 8,
|
|
238
|
+
},
|
|
239
|
+
changeText: {
|
|
240
|
+
color: '#2DBD60',
|
|
241
|
+
fontSize: 14,
|
|
242
|
+
fontWeight: '500',
|
|
243
|
+
},
|
|
244
|
+
resendButton: {
|
|
245
|
+
marginTop: 16,
|
|
246
|
+
alignItems: 'center',
|
|
247
|
+
},
|
|
248
|
+
resendText: {
|
|
249
|
+
color: '#666',
|
|
250
|
+
fontSize: 14,
|
|
251
|
+
textDecorationLine: 'underline',
|
|
252
|
+
},
|
|
253
|
+
});
|
|
@@ -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[] = (
|
|
35
|
-
const { actions, state, } = useTemplateKYCFlowContext();
|
|
34
|
+
// const config = component.config as SelfieConfig;
|
|
35
|
+
const orientations: OrientationType[] = (['center','left','right']) as OrientationType[];
|
|
36
|
+
const { actions, state, env } = 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
|
}
|
|
@@ -150,10 +152,13 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
150
152
|
}
|
|
151
153
|
if (result.success && result.path) {
|
|
152
154
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
|
|
153
|
-
selfieVerification(result.path).then((response) => {
|
|
155
|
+
selfieVerification(result.path, env).then((response) => {
|
|
154
156
|
if (response.length > 0) {
|
|
155
157
|
const res = response[0];
|
|
156
|
-
|
|
158
|
+
// In SANDBOX mode, always accept the result regardless of orientation
|
|
159
|
+
if (env === 'SANDBOX' && res?.capture) {
|
|
160
|
+
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: true, error: '', path: result.path }));
|
|
161
|
+
} else if (res?.orientation_direction === getOrientationOpposite(currentOrientation) && res?.capture) {
|
|
157
162
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: true, error: '', path: result.path }));
|
|
158
163
|
} else {
|
|
159
164
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
|
|
@@ -162,39 +167,80 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
162
167
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Le selfie n\'est pas correct' }));
|
|
163
168
|
}
|
|
164
169
|
}).catch((e: any) => {
|
|
165
|
-
|
|
166
170
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: e?.message || 'Erreur de vérification du selfie' }));
|
|
167
171
|
});
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
// Capture manuelle - affiche le preview
|
|
171
176
|
const handleCapture = async (result: { success: boolean; path?: string; error?: string }) => {
|
|
172
|
-
console.log("
|
|
177
|
+
console.log("handleCapture called", result, silentCaptureResult);
|
|
173
178
|
|
|
179
|
+
// Priorité 1: Si la capture silencieuse a réussi, utiliser ce path
|
|
174
180
|
if (silentCaptureResult.success && silentCaptureResult.path) {
|
|
175
|
-
|
|
176
|
-
|
|
181
|
+
setPreviewImagePath(silentCaptureResult.path);
|
|
182
|
+
setShowCamera(false);
|
|
183
|
+
setShowPreview(true);
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Priorité 2: Capture manuelle normale
|
|
188
|
+
if (result.success && result.path) {
|
|
189
|
+
setPreviewImagePath(result.path);
|
|
190
|
+
setShowCamera(false);
|
|
191
|
+
setShowPreview(true);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Confirmer la capture depuis le preview
|
|
196
|
+
const handleConfirmCapture = async () => {
|
|
197
|
+
if (!previewImagePath) return;
|
|
198
|
+
|
|
199
|
+
try {
|
|
200
|
+
const base64 = await pathToBase64(previewImagePath);
|
|
177
201
|
const newImages: Record<string, any> = {
|
|
178
202
|
...capturedImages,
|
|
179
|
-
[currentOrientation]: { dir:
|
|
203
|
+
[currentOrientation]: { dir: previewImagePath, file: base64 } as IImagePayload,
|
|
180
204
|
};
|
|
181
205
|
setCapturedImages(newImages);
|
|
182
206
|
onValueChange(newImages);
|
|
183
|
-
|
|
184
|
-
|
|
207
|
+
setShowPreview(false);
|
|
208
|
+
setPreviewImagePath(null);
|
|
209
|
+
setSilentCaptureResult({ success: false, isAnalyzing: false });
|
|
185
210
|
|
|
186
211
|
// Passer à l'orientation suivante si disponible
|
|
187
212
|
const currentIndex = orientations.indexOf(currentOrientation as OrientationType);
|
|
188
213
|
if (currentIndex < orientations.length - 1) {
|
|
189
|
-
|
|
214
|
+
const nextOrientation = orientations[currentIndex + 1];
|
|
215
|
+
setCurrentOrientation(nextOrientation);
|
|
216
|
+
// Rouvrir automatiquement la caméra pour la prochaine orientation
|
|
217
|
+
setShowCamera(true);
|
|
190
218
|
}
|
|
219
|
+
// Si toutes les orientations sont complétées, on reste sur la vue principale
|
|
220
|
+
} catch (e) {
|
|
221
|
+
console.error("Error confirming capture", e);
|
|
222
|
+
showAlert('Erreur', 'Erreur lors de la sauvegarde de l\'image');
|
|
191
223
|
}
|
|
192
224
|
};
|
|
225
|
+
|
|
226
|
+
// Reprendre la photo depuis le preview
|
|
227
|
+
const handleRetakeFromPreview = () => {
|
|
228
|
+
setShowPreview(false);
|
|
229
|
+
setPreviewImagePath(null);
|
|
230
|
+
setSilentCaptureResult({ success: false, isAnalyzing: false });
|
|
231
|
+
setShowCamera(true);
|
|
232
|
+
};
|
|
193
233
|
const idCardData = useMemo(() => {
|
|
194
234
|
const idCardID = Object.keys(state.componentData).find((c: string) => c === "1");
|
|
195
235
|
if (idCardID) {
|
|
196
236
|
const _idCardData = state.componentData[idCardID];
|
|
197
|
-
|
|
237
|
+
const documentType = _idCardData?.documentType;
|
|
238
|
+
// Map national_id to identity_card for selfie capture
|
|
239
|
+
const mappedDocumentType = documentType === 'national_id' ? 'identity_card' : documentType;
|
|
240
|
+
return {
|
|
241
|
+
country: _idCardData?.country,
|
|
242
|
+
documentType: mappedDocumentType as GovernmentDocumentType
|
|
243
|
+
};
|
|
198
244
|
}
|
|
199
245
|
return null;
|
|
200
246
|
}, [state.componentData]);
|
|
@@ -202,7 +248,7 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
202
248
|
|
|
203
249
|
|
|
204
250
|
const handleError = (event: { message: string }) => {
|
|
205
|
-
|
|
251
|
+
showAlert('Erreur', event.message);
|
|
206
252
|
setShowCamera(false);
|
|
207
253
|
};
|
|
208
254
|
|
|
@@ -216,7 +262,60 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
216
262
|
};
|
|
217
263
|
console.log("Current Orientation", currentOrientation);
|
|
218
264
|
|
|
265
|
+
// Vue Preview
|
|
266
|
+
if (showPreview && previewImagePath) {
|
|
267
|
+
return (
|
|
268
|
+
<View style={styles.containerCamera}>
|
|
269
|
+
<View style={styles.previewContainer}>
|
|
270
|
+
<View style={styles.previewHeader}>
|
|
271
|
+
<TouchableOpacity onPress={handleRetakeFromPreview} style={styles.backButton}>
|
|
272
|
+
<Text style={styles.backButtonText}>←</Text>
|
|
273
|
+
</TouchableOpacity>
|
|
274
|
+
<Text style={styles.previewTitle}>
|
|
275
|
+
{state.currentLanguage === "en" ? "Preview" : "Aperçu"}
|
|
276
|
+
</Text>
|
|
277
|
+
<View style={{ width: 40 }} />
|
|
278
|
+
</View>
|
|
279
|
+
|
|
280
|
+
<View style={styles.previewImageContainer}>
|
|
281
|
+
<Image
|
|
282
|
+
source={{ uri: previewImagePath }}
|
|
283
|
+
style={styles.previewImage}
|
|
284
|
+
resizeMode="contain"
|
|
285
|
+
/>
|
|
286
|
+
</View>
|
|
219
287
|
|
|
288
|
+
<Text style={styles.previewInstructions}>
|
|
289
|
+
{state.currentLanguage === "en"
|
|
290
|
+
? "Is your face clearly visible?"
|
|
291
|
+
: "Votre visage est-il clairement visible ?"}
|
|
292
|
+
</Text>
|
|
293
|
+
|
|
294
|
+
<View style={styles.previewButtonsContainer}>
|
|
295
|
+
<TouchableOpacity
|
|
296
|
+
style={styles.retakeButton}
|
|
297
|
+
onPress={handleRetakeFromPreview}
|
|
298
|
+
>
|
|
299
|
+
<Text style={styles.retakeButtonText}>
|
|
300
|
+
{state.currentLanguage === "en" ? "Retake" : "Reprendre"}
|
|
301
|
+
</Text>
|
|
302
|
+
</TouchableOpacity>
|
|
303
|
+
|
|
304
|
+
<TouchableOpacity
|
|
305
|
+
style={styles.confirmButton}
|
|
306
|
+
onPress={handleConfirmCapture}
|
|
307
|
+
>
|
|
308
|
+
<Text style={styles.confirmButtonText}>
|
|
309
|
+
{state.currentLanguage === "en" ? "Use Photo" : "Utiliser"}
|
|
310
|
+
</Text>
|
|
311
|
+
</TouchableOpacity>
|
|
312
|
+
</View>
|
|
313
|
+
</View>
|
|
314
|
+
</View>
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Vue Camera
|
|
220
319
|
if (showCamera) {
|
|
221
320
|
return (
|
|
222
321
|
<View style={styles.containerCamera}>
|
|
@@ -322,18 +421,13 @@ export const SelfieCaptureTemplate: React.FC<SelfieCaptureTemplateProps> = ({
|
|
|
322
421
|
<View style={{ height: 10 }} />
|
|
323
422
|
{isAllOrientationsCompleted() ? <>
|
|
324
423
|
<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;
|
|
424
|
+
const value = {
|
|
425
|
+
...capturedImages,
|
|
426
|
+
...(idCardData?.country ? { country: idCardData.country } : {}),
|
|
427
|
+
...(idCardData?.documentType ? { documentType: idCardData.documentType } : {}),
|
|
336
428
|
}
|
|
429
|
+
console.log("value", JSON.stringify(truncateFields(value), null, 2));
|
|
430
|
+
onValueChange(value);
|
|
337
431
|
actions.nextComponent();
|
|
338
432
|
}} />
|
|
339
433
|
</> : (
|
|
@@ -370,23 +464,17 @@ const styles = StyleSheet.create({
|
|
|
370
464
|
borderRadius: 20,
|
|
371
465
|
paddingVertical: 20,
|
|
372
466
|
paddingHorizontal: 16,
|
|
373
|
-
// shadow
|
|
374
467
|
shadowColor: '#000',
|
|
375
468
|
shadowOffset: { width: 0, height: 2 },
|
|
376
469
|
shadowOpacity: 0.35,
|
|
377
470
|
shadowRadius: 4.84,
|
|
378
471
|
elevation: 10,
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
// padding: 16,
|
|
382
472
|
},
|
|
383
473
|
containerCamera: {
|
|
384
474
|
flex: 1,
|
|
385
|
-
// maxWidth: 760,
|
|
386
475
|
width: '100%',
|
|
387
476
|
height: '100%',
|
|
388
|
-
|
|
389
|
-
backgroundColor: '#f5f5f5',
|
|
477
|
+
backgroundColor: '#000',
|
|
390
478
|
},
|
|
391
479
|
title: {
|
|
392
480
|
fontSize: 24,
|
|
@@ -404,7 +492,6 @@ const styles = StyleSheet.create({
|
|
|
404
492
|
},
|
|
405
493
|
camera: {
|
|
406
494
|
flex: 1,
|
|
407
|
-
// borderRadius: 12,
|
|
408
495
|
overflow: 'hidden',
|
|
409
496
|
},
|
|
410
497
|
attemptsText: {
|
|
@@ -414,7 +501,6 @@ const styles = StyleSheet.create({
|
|
|
414
501
|
marginBottom: 10,
|
|
415
502
|
},
|
|
416
503
|
orientationsContainer: {
|
|
417
|
-
// flex: 1,
|
|
418
504
|
},
|
|
419
505
|
orientationContainer: {
|
|
420
506
|
backgroundColor: 'white',
|
|
@@ -434,7 +520,6 @@ const styles = StyleSheet.create({
|
|
|
434
520
|
fontSize: 18,
|
|
435
521
|
fontWeight: '600',
|
|
436
522
|
color: '#333',
|
|
437
|
-
// marginBottom: 12,
|
|
438
523
|
},
|
|
439
524
|
capturedImageContainer: {
|
|
440
525
|
alignItems: 'center',
|
|
@@ -464,14 +549,86 @@ const styles = StyleSheet.create({
|
|
|
464
549
|
fontWeight: '600',
|
|
465
550
|
color: 'white',
|
|
466
551
|
},
|
|
552
|
+
// Preview styles
|
|
553
|
+
previewContainer: {
|
|
554
|
+
flex: 1,
|
|
555
|
+
backgroundColor: '#000',
|
|
556
|
+
justifyContent: 'space-between',
|
|
557
|
+
},
|
|
558
|
+
previewHeader: {
|
|
559
|
+
flexDirection: 'row',
|
|
560
|
+
alignItems: 'center',
|
|
561
|
+
justifyContent: 'space-between',
|
|
562
|
+
paddingHorizontal: 16,
|
|
563
|
+
paddingTop: 50,
|
|
564
|
+
paddingBottom: 16,
|
|
565
|
+
},
|
|
566
|
+
backButton: {
|
|
567
|
+
width: 40,
|
|
568
|
+
height: 40,
|
|
569
|
+
borderRadius: 20,
|
|
570
|
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
571
|
+
justifyContent: 'center',
|
|
572
|
+
alignItems: 'center',
|
|
573
|
+
},
|
|
574
|
+
backButtonText: {
|
|
575
|
+
color: 'white',
|
|
576
|
+
fontSize: 24,
|
|
577
|
+
fontWeight: 'bold',
|
|
578
|
+
},
|
|
579
|
+
previewTitle: {
|
|
580
|
+
color: 'white',
|
|
581
|
+
fontSize: 18,
|
|
582
|
+
fontWeight: '600',
|
|
583
|
+
},
|
|
584
|
+
previewImageContainer: {
|
|
585
|
+
flex: 1,
|
|
586
|
+
justifyContent: 'center',
|
|
587
|
+
alignItems: 'center',
|
|
588
|
+
paddingHorizontal: 20,
|
|
589
|
+
},
|
|
590
|
+
previewImage: {
|
|
591
|
+
width: '100%',
|
|
592
|
+
height: '80%',
|
|
593
|
+
borderRadius: 16,
|
|
594
|
+
},
|
|
595
|
+
previewInstructions: {
|
|
596
|
+
color: 'white',
|
|
597
|
+
fontSize: 16,
|
|
598
|
+
textAlign: 'center',
|
|
599
|
+
paddingHorizontal: 20,
|
|
600
|
+
marginBottom: 20,
|
|
601
|
+
},
|
|
602
|
+
previewButtonsContainer: {
|
|
603
|
+
flexDirection: 'row',
|
|
604
|
+
justifyContent: 'space-between',
|
|
605
|
+
paddingHorizontal: 20,
|
|
606
|
+
paddingBottom: 40,
|
|
607
|
+
gap: 16,
|
|
608
|
+
},
|
|
467
609
|
retakeButton: {
|
|
468
|
-
|
|
469
|
-
|
|
610
|
+
flex: 1,
|
|
611
|
+
backgroundColor: 'rgba(255,255,255,0.2)',
|
|
612
|
+
paddingVertical: 16,
|
|
470
613
|
paddingHorizontal: 20,
|
|
471
|
-
borderRadius:
|
|
614
|
+
borderRadius: 12,
|
|
615
|
+
alignItems: 'center',
|
|
472
616
|
},
|
|
473
617
|
retakeButtonText: {
|
|
474
|
-
fontSize:
|
|
618
|
+
fontSize: 16,
|
|
619
|
+
fontWeight: '600',
|
|
620
|
+
color: 'white',
|
|
621
|
+
},
|
|
622
|
+
confirmButton: {
|
|
623
|
+
flex: 1,
|
|
624
|
+
backgroundColor: '#2DBD60',
|
|
625
|
+
paddingVertical: 16,
|
|
626
|
+
paddingHorizontal: 20,
|
|
627
|
+
borderRadius: 12,
|
|
628
|
+
alignItems: 'center',
|
|
629
|
+
},
|
|
630
|
+
confirmButtonText: {
|
|
631
|
+
fontSize: 16,
|
|
475
632
|
fontWeight: '600',
|
|
476
633
|
color: 'white',
|
|
477
634
|
},
|