@sanctum-key/react-native-sdk 1.0.11 → 1.0.13
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/build/package.json +1 -1
- package/build/src/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/EmailVerificationTemplate.js +69 -37
- package/build/src/components/KYCElements/EmailVerificationTemplate.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +129 -181
- package/build/src/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/src/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -1
- package/build/src/components/KYCElements/PhoneVerificationTemplate.js +97 -65
- package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +1 -1
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/package.json +1 -1
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +127 -95
- package/src/components/KYCElements/IDCardCapture.tsx +226 -296
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +185 -165
- package/src/modules/api/KYCService.ts +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert, Pressable, Modal, FlatList } from 'react-native';
|
|
2
|
+
import { View, Text, StyleSheet, TextInput, TouchableOpacity, Alert, Pressable, Modal, FlatList, Platform } from 'react-native';
|
|
3
3
|
import { TemplateComponent, LocalizedText } from '../../types/KYC.types';
|
|
4
4
|
import { useTemplateKYCFlowContext } from '../../hooks/useTemplateKYCFlow';
|
|
5
5
|
import { useI18n } from '../../hooks/useI18n';
|
|
@@ -17,7 +17,6 @@ interface PhoneVerificationTemplateProps {
|
|
|
17
17
|
type VerificationStep = 'phone' | 'otp';
|
|
18
18
|
const CODE_LENGTH = 6;
|
|
19
19
|
|
|
20
|
-
// 🌍 The Country Codes Array
|
|
21
20
|
const COUNTRY_CODES = [
|
|
22
21
|
{ code: '+254', label: '🇰🇪 Kenya (+254)' },
|
|
23
22
|
{ code: '+255', label: '🇹🇿 Tanzania (+255)' },
|
|
@@ -38,7 +37,6 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
38
37
|
}) => {
|
|
39
38
|
const { actions, getLocalizedText, state, apiKey } = useTemplateKYCFlowContext();
|
|
40
39
|
const { t } = useI18n();
|
|
41
|
-
|
|
42
40
|
const auth = apiKey ? { apiKey } : (state.session?.token ? { token: state.session.token } : undefined);
|
|
43
41
|
const sessionId = state.session?.session_id || '';
|
|
44
42
|
|
|
@@ -47,19 +45,16 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
47
45
|
const [countryCode, setCountryCode] = useState('+254');
|
|
48
46
|
const [phone, setPhone] = useState('');
|
|
49
47
|
const [showCountryPicker, setShowCountryPicker] = useState(false);
|
|
50
|
-
|
|
51
48
|
const [otp, setOtp] = useState('');
|
|
52
49
|
const [localError, setLocalError] = useState<string | null>(null);
|
|
53
50
|
const [isSimulating, setIsSimulating] = useState(false);
|
|
54
51
|
|
|
55
52
|
// Track actual focus state for visual feedback
|
|
56
|
-
const [isInputFocused, setIsInputFocused] = useState(false);
|
|
57
|
-
|
|
53
|
+
const [isInputFocused, setIsInputFocused] = useState(false);
|
|
58
54
|
const inputRef = useRef<TextInput>(null);
|
|
59
55
|
|
|
60
56
|
const title = getLocalizedText(component.labels as LocalizedText);
|
|
61
57
|
const instructions = getLocalizedText(component.instructions as LocalizedText);
|
|
62
|
-
|
|
63
58
|
const verifyButtonText = getLocalizedText((component.ui as any).buttonText) || t('common.verify') || 'Verify';
|
|
64
59
|
const sendButtonText = t('common.sendCode') || 'Send Verification Code';
|
|
65
60
|
const buttonText = step === 'phone' ? sendButtonText : verifyButtonText;
|
|
@@ -74,14 +69,11 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
74
69
|
// --- SAFE AUTO FOCUS LOGIC ---
|
|
75
70
|
useEffect(() => {
|
|
76
71
|
let focusTimer: ReturnType<typeof setTimeout>;
|
|
77
|
-
|
|
78
|
-
// Only attempt focus when we are on the OTP step AND the loading state is completely finished
|
|
79
72
|
if (step === 'otp' && !isSimulating) {
|
|
80
73
|
focusTimer = setTimeout(() => {
|
|
81
74
|
inputRef.current?.focus();
|
|
82
|
-
}, 300);
|
|
75
|
+
}, 300);
|
|
83
76
|
}
|
|
84
|
-
|
|
85
77
|
return () => {
|
|
86
78
|
if (focusTimer) clearTimeout(focusTimer);
|
|
87
79
|
};
|
|
@@ -93,10 +85,8 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
93
85
|
setLocalError(t('errors.invalidPhone') || 'Please enter a valid phone number');
|
|
94
86
|
return;
|
|
95
87
|
}
|
|
96
|
-
|
|
97
88
|
setLocalError(null);
|
|
98
89
|
setIsSimulating(true);
|
|
99
|
-
|
|
100
90
|
const fullPhoneNumber = `${countryCode}${trimmedPhone}`;
|
|
101
91
|
|
|
102
92
|
try {
|
|
@@ -115,15 +105,12 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
115
105
|
setLocalError(t('errors.invalidCode') || 'Please enter the 6-digit code');
|
|
116
106
|
return;
|
|
117
107
|
}
|
|
118
|
-
|
|
119
108
|
setLocalError(null);
|
|
120
109
|
setIsSimulating(true);
|
|
121
|
-
|
|
122
110
|
const fullPhoneNumber = `${countryCode}${phone.trim()}`;
|
|
123
111
|
|
|
124
112
|
try {
|
|
125
113
|
await kycService.verifyWhatsAppCode(sessionId, otp.trim(), fullPhoneNumber, auth);
|
|
126
|
-
|
|
127
114
|
const data = { phone: fullPhoneNumber, otp, verified: true };
|
|
128
115
|
onValueChange(data);
|
|
129
116
|
actions.nextComponent(data);
|
|
@@ -131,7 +118,7 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
131
118
|
const msg = errorMessage(err) ?? err?.message ?? (t('errors.wrongCode') || 'Invalid verification code');
|
|
132
119
|
setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
|
|
133
120
|
setOtp('');
|
|
134
|
-
inputRef.current?.focus();
|
|
121
|
+
inputRef.current?.focus();
|
|
135
122
|
} finally {
|
|
136
123
|
setIsSimulating(false);
|
|
137
124
|
}
|
|
@@ -162,18 +149,15 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
162
149
|
{boxes.map((_, index) => {
|
|
163
150
|
const digit = otp[index] || '';
|
|
164
151
|
const isFilled = index < otp.length;
|
|
165
|
-
|
|
166
|
-
// Only highlight if the input is ACTUALLY focused.
|
|
167
152
|
const isActiveIndex = index === otp.length || (index === CODE_LENGTH - 1 && otp.length === CODE_LENGTH);
|
|
168
153
|
const isCurrent = isInputFocused && isActiveIndex;
|
|
169
|
-
|
|
170
154
|
return (
|
|
171
|
-
<View
|
|
172
|
-
key={index}
|
|
155
|
+
<View
|
|
156
|
+
key={index}
|
|
173
157
|
style={[
|
|
174
|
-
styles.otpBox,
|
|
158
|
+
styles.otpBox,
|
|
175
159
|
isFilled && styles.otpBoxFilled,
|
|
176
|
-
isCurrent && styles.otpBoxActive
|
|
160
|
+
isCurrent && styles.otpBoxActive
|
|
177
161
|
]}
|
|
178
162
|
>
|
|
179
163
|
<Text style={styles.otpBoxText}>{digit}</Text>
|
|
@@ -185,152 +169,169 @@ export const PhoneVerificationTemplate: React.FC<PhoneVerificationTemplateProps>
|
|
|
185
169
|
};
|
|
186
170
|
|
|
187
171
|
return (
|
|
188
|
-
<View style={styles.
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
{
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
{
|
|
196
|
-
|
|
197
|
-
<
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
172
|
+
<View style={styles.wrapper}>
|
|
173
|
+
<View style={styles.container}>
|
|
174
|
+
<Text style={styles.title}>{title}</Text>
|
|
175
|
+
<Text style={styles.instructions}>
|
|
176
|
+
{step === 'phone' ? instructions : (t('kyc.enterCodeSent') || `Please enter the code sent to ${countryCode} ${phone}`)}
|
|
177
|
+
</Text>
|
|
178
|
+
|
|
179
|
+
<View style={styles.contentContainer}>
|
|
180
|
+
{step === 'phone' ? (
|
|
181
|
+
<View style={styles.inputContainer}>
|
|
182
|
+
<Text style={styles.label}>{t('common.phone') || 'Phone Number'}</Text>
|
|
183
|
+
<View style={styles.phoneInputRow}>
|
|
184
|
+
<TouchableOpacity
|
|
185
|
+
style={styles.countryPickerBtn}
|
|
186
|
+
onPress={() => setShowCountryPicker(true)}
|
|
187
|
+
disabled={isSimulating}
|
|
188
|
+
activeOpacity={0.7}
|
|
189
|
+
>
|
|
190
|
+
<Text style={styles.countryPickerText}>{countryCode}</Text>
|
|
191
|
+
<Text style={styles.dropdownIcon}>▼</Text>
|
|
192
|
+
</TouchableOpacity>
|
|
208
193
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
194
|
+
<TextInput
|
|
195
|
+
style={[
|
|
196
|
+
styles.phoneInput,
|
|
197
|
+
// 🚨 Removes browser focus ring on web
|
|
198
|
+
Platform.OS === 'web' && { outlineStyle: 'none' } as any
|
|
199
|
+
]}
|
|
200
|
+
placeholder="712 345 678"
|
|
201
|
+
placeholderTextColor="#9CA3AF" // 🚨 Explicit light color so it looks like a placeholder
|
|
202
|
+
value={phone}
|
|
203
|
+
onChangeText={onChangePhone}
|
|
204
|
+
keyboardType="phone-pad"
|
|
205
|
+
autoComplete="tel"
|
|
206
|
+
editable={!isSimulating}
|
|
207
|
+
/>
|
|
208
|
+
</View>
|
|
218
209
|
</View>
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
210
|
+
) : (
|
|
211
|
+
<View style={styles.inputContainer}>
|
|
212
|
+
<Text style={styles.label}>{t('common.verificationCode') || 'Verification Code'}</Text>
|
|
213
|
+
<View style={styles.otpWrapper}>
|
|
214
|
+
{renderOtpBoxes()}
|
|
215
|
+
<TextInput
|
|
216
|
+
ref={inputRef}
|
|
217
|
+
style={[
|
|
218
|
+
styles.hiddenInput,
|
|
219
|
+
Platform.OS === 'web' && { outlineStyle: 'none' } as any
|
|
220
|
+
]}
|
|
221
|
+
value={otp}
|
|
222
|
+
onChangeText={onChangeOtp}
|
|
223
|
+
keyboardType="number-pad"
|
|
224
|
+
maxLength={CODE_LENGTH}
|
|
225
|
+
editable={!isSimulating}
|
|
226
|
+
textContentType="oneTimeCode"
|
|
227
|
+
caretHidden={true}
|
|
228
|
+
onFocus={() => setIsInputFocused(true)}
|
|
229
|
+
onBlur={() => setIsInputFocused(false)}
|
|
230
|
+
/>
|
|
231
|
+
</View>
|
|
232
|
+
<TouchableOpacity onPress={handleBackToPhone} style={styles.changeLink} disabled={isSimulating}>
|
|
233
|
+
<Text style={styles.changeText}>{t('common.back') || 'Change number'}</Text>
|
|
234
|
+
</TouchableOpacity>
|
|
240
235
|
</View>
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
236
|
+
)}
|
|
237
|
+
|
|
238
|
+
{(localError || propError) && (
|
|
239
|
+
<Text style={styles.errorText}>{localError || propError}</Text>
|
|
240
|
+
)}
|
|
241
|
+
|
|
242
|
+
<Button
|
|
243
|
+
title={isSimulating ? (t('common.processing') || 'Processing...') : buttonText}
|
|
244
|
+
onPress={step === 'phone' ? handleSendCode : handleVerifyCode}
|
|
245
|
+
style={styles.button}
|
|
246
|
+
disabled={
|
|
247
|
+
isSimulating ||
|
|
248
|
+
(step === 'phone' ? !phone : otp.length < CODE_LENGTH)
|
|
249
|
+
}
|
|
250
|
+
/>
|
|
251
|
+
|
|
252
|
+
{step === 'otp' && (
|
|
253
|
+
<TouchableOpacity
|
|
254
|
+
onPress={async () => {
|
|
255
|
+
if (isSimulating) return;
|
|
256
|
+
setLocalError(null);
|
|
257
|
+
setIsSimulating(true);
|
|
258
|
+
const fullPhoneNumber = `${countryCode}${phone.trim()}`;
|
|
259
|
+
try {
|
|
260
|
+
await kycService.sendWhatsAppVerificationCode(sessionId, fullPhoneNumber, auth);
|
|
261
|
+
Alert.alert(
|
|
262
|
+
t('common.codeResent') || 'Code Resent',
|
|
263
|
+
t('common.codeResentMessage', { email: fullPhoneNumber }) || 'Code resent to ' + fullPhoneNumber
|
|
264
|
+
);
|
|
265
|
+
inputRef.current?.focus();
|
|
266
|
+
} catch (err: any) {
|
|
267
|
+
const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');
|
|
268
|
+
setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
|
|
269
|
+
} finally {
|
|
270
|
+
setIsSimulating(false);
|
|
271
|
+
}
|
|
272
|
+
}}
|
|
273
|
+
style={styles.resendButton}
|
|
274
|
+
disabled={isSimulating}
|
|
275
|
+
>
|
|
276
|
+
<Text style={styles.resendText}>{t('common.resendCode') || 'Resend Code'}</Text>
|
|
244
277
|
</TouchableOpacity>
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
{
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
style={styles.button}
|
|
256
|
-
disabled={
|
|
257
|
-
isSimulating ||
|
|
258
|
-
(step === 'phone' ? !phone : otp.length < CODE_LENGTH)
|
|
259
|
-
}
|
|
260
|
-
/>
|
|
261
|
-
|
|
262
|
-
{step === 'otp' && (
|
|
278
|
+
)}
|
|
279
|
+
</View>
|
|
280
|
+
|
|
281
|
+
{/* COUNTRY PICKER MODAL */}
|
|
282
|
+
<Modal
|
|
283
|
+
visible={showCountryPicker}
|
|
284
|
+
animationType="slide"
|
|
285
|
+
transparent={true}
|
|
286
|
+
onRequestClose={() => setShowCountryPicker(false)}
|
|
287
|
+
>
|
|
263
288
|
<TouchableOpacity
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
setIsSimulating(true);
|
|
268
|
-
const fullPhoneNumber = `${countryCode}${phone.trim()}`;
|
|
269
|
-
try {
|
|
270
|
-
await kycService.sendWhatsAppVerificationCode(sessionId, fullPhoneNumber, auth);
|
|
271
|
-
Alert.alert(
|
|
272
|
-
t('common.codeResent') || 'Code Resent',
|
|
273
|
-
t('common.codeResentMessage', { email: fullPhoneNumber }) || 'Code resent to ' + fullPhoneNumber
|
|
274
|
-
);
|
|
275
|
-
inputRef.current?.focus();
|
|
276
|
-
} catch (err: any) {
|
|
277
|
-
const msg = errorMessage(err) ?? err?.message ?? (t('errors.sendCodeFailed') || 'Failed to send code');
|
|
278
|
-
setLocalError(typeof msg === 'string' ? msg : JSON.stringify(msg));
|
|
279
|
-
} finally {
|
|
280
|
-
setIsSimulating(false);
|
|
281
|
-
}
|
|
282
|
-
}}
|
|
283
|
-
style={styles.resendButton}
|
|
284
|
-
disabled={isSimulating}
|
|
289
|
+
style={styles.modalOverlay}
|
|
290
|
+
activeOpacity={1}
|
|
291
|
+
onPress={() => setShowCountryPicker(false)}
|
|
285
292
|
>
|
|
286
|
-
|
|
293
|
+
{/* 🚨 Added wrapper to center modal on desktop */}
|
|
294
|
+
<View style={styles.modalCenteredWrapper}>
|
|
295
|
+
<View style={styles.modalContent}>
|
|
296
|
+
<View style={styles.modalHeader}>
|
|
297
|
+
<Text style={styles.modalTitle}>Select Country</Text>
|
|
298
|
+
<TouchableOpacity onPress={() => setShowCountryPicker(false)}>
|
|
299
|
+
<Text style={styles.modalClose}>✕</Text>
|
|
300
|
+
</TouchableOpacity>
|
|
301
|
+
</View>
|
|
302
|
+
<FlatList
|
|
303
|
+
data={COUNTRY_CODES}
|
|
304
|
+
keyExtractor={(item) => item.code}
|
|
305
|
+
renderItem={({ item }) => (
|
|
306
|
+
<TouchableOpacity
|
|
307
|
+
style={styles.countryItem}
|
|
308
|
+
onPress={() => {
|
|
309
|
+
setCountryCode(item.code);
|
|
310
|
+
setShowCountryPicker(false);
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
<Text style={styles.countryItemLabel}>{item.label}</Text>
|
|
314
|
+
{countryCode === item.code && <Text style={styles.checkMark}>✓</Text>}
|
|
315
|
+
</TouchableOpacity>
|
|
316
|
+
)}
|
|
317
|
+
/>
|
|
318
|
+
</View>
|
|
319
|
+
</View>
|
|
287
320
|
</TouchableOpacity>
|
|
288
|
-
|
|
321
|
+
</Modal>
|
|
289
322
|
</View>
|
|
290
|
-
|
|
291
|
-
{/* COUNTRY PICKER MODAL */}
|
|
292
|
-
<Modal
|
|
293
|
-
visible={showCountryPicker}
|
|
294
|
-
animationType="slide"
|
|
295
|
-
transparent={true}
|
|
296
|
-
onRequestClose={() => setShowCountryPicker(false)}
|
|
297
|
-
>
|
|
298
|
-
<TouchableOpacity
|
|
299
|
-
style={styles.modalOverlay}
|
|
300
|
-
activeOpacity={1}
|
|
301
|
-
onPress={() => setShowCountryPicker(false)}
|
|
302
|
-
>
|
|
303
|
-
<View style={styles.modalContent}>
|
|
304
|
-
<View style={styles.modalHeader}>
|
|
305
|
-
<Text style={styles.modalTitle}>Select Country</Text>
|
|
306
|
-
<TouchableOpacity onPress={() => setShowCountryPicker(false)}>
|
|
307
|
-
<Text style={styles.modalClose}>✕</Text>
|
|
308
|
-
</TouchableOpacity>
|
|
309
|
-
</View>
|
|
310
|
-
<FlatList
|
|
311
|
-
data={COUNTRY_CODES}
|
|
312
|
-
keyExtractor={(item) => item.code}
|
|
313
|
-
renderItem={({ item }) => (
|
|
314
|
-
<TouchableOpacity
|
|
315
|
-
style={styles.countryItem}
|
|
316
|
-
onPress={() => {
|
|
317
|
-
setCountryCode(item.code);
|
|
318
|
-
setShowCountryPicker(false);
|
|
319
|
-
}}
|
|
320
|
-
>
|
|
321
|
-
<Text style={styles.countryItemLabel}>{item.label}</Text>
|
|
322
|
-
{countryCode === item.code && <Text style={styles.checkMark}>✓</Text>}
|
|
323
|
-
</TouchableOpacity>
|
|
324
|
-
)}
|
|
325
|
-
/>
|
|
326
|
-
</View>
|
|
327
|
-
</TouchableOpacity>
|
|
328
|
-
</Modal>
|
|
329
323
|
</View>
|
|
330
324
|
);
|
|
331
325
|
};
|
|
332
326
|
|
|
333
327
|
const styles = StyleSheet.create({
|
|
328
|
+
// 🚨 Added wrapper to easily center the component on web
|
|
329
|
+
wrapper: {
|
|
330
|
+
flex: 1,
|
|
331
|
+
width: '100%',
|
|
332
|
+
alignItems: 'center',
|
|
333
|
+
justifyContent: 'center',
|
|
334
|
+
},
|
|
334
335
|
container: {
|
|
335
336
|
padding: 24,
|
|
336
337
|
backgroundColor: 'white',
|
|
@@ -342,6 +343,8 @@ const styles = StyleSheet.create({
|
|
|
342
343
|
shadowRadius: 12,
|
|
343
344
|
elevation: 5,
|
|
344
345
|
width: '95%',
|
|
346
|
+
// 🚨 Max width constraint for web so inputs don't stretch infinitely
|
|
347
|
+
...(Platform.OS === 'web' ? { maxWidth: 450, paddingVertical: 40 } : {}),
|
|
345
348
|
},
|
|
346
349
|
title: {
|
|
347
350
|
fontSize: 24,
|
|
@@ -357,7 +360,9 @@ const styles = StyleSheet.create({
|
|
|
357
360
|
lineHeight: 24,
|
|
358
361
|
textAlign: 'center',
|
|
359
362
|
},
|
|
360
|
-
contentContainer: {
|
|
363
|
+
contentContainer: {
|
|
364
|
+
width: '100%',
|
|
365
|
+
},
|
|
361
366
|
inputContainer: {
|
|
362
367
|
marginBottom: 24,
|
|
363
368
|
},
|
|
@@ -380,10 +385,11 @@ const styles = StyleSheet.create({
|
|
|
380
385
|
borderWidth: 1,
|
|
381
386
|
borderColor: '#e0e0e0',
|
|
382
387
|
paddingHorizontal: 12,
|
|
383
|
-
paddingVertical: 16,
|
|
388
|
+
paddingVertical: Platform.OS === 'web' ? 14 : 16, // Adjusted for web inputs
|
|
384
389
|
borderRadius: 12,
|
|
385
390
|
backgroundColor: '#f8f9fa',
|
|
386
|
-
minWidth:
|
|
391
|
+
minWidth: 100,
|
|
392
|
+
...(Platform.OS === 'web' ? { cursor: 'pointer' } as any : {}),
|
|
387
393
|
},
|
|
388
394
|
countryPickerText: {
|
|
389
395
|
fontSize: 16,
|
|
@@ -396,10 +402,10 @@ const styles = StyleSheet.create({
|
|
|
396
402
|
marginLeft: 8,
|
|
397
403
|
},
|
|
398
404
|
phoneInput: {
|
|
399
|
-
flex: 1,
|
|
405
|
+
flex: 1,
|
|
400
406
|
borderWidth: 1,
|
|
401
407
|
borderColor: '#e0e0e0',
|
|
402
|
-
padding: 16,
|
|
408
|
+
padding: Platform.OS === 'web' ? 14 : 16,
|
|
403
409
|
borderRadius: 12,
|
|
404
410
|
fontSize: 16,
|
|
405
411
|
backgroundColor: '#f8f9fa',
|
|
@@ -410,12 +416,19 @@ const styles = StyleSheet.create({
|
|
|
410
416
|
backgroundColor: 'rgba(0,0,0,0.5)',
|
|
411
417
|
justifyContent: 'flex-end',
|
|
412
418
|
},
|
|
419
|
+
// 🚨 Centering wrapper for the modal on web
|
|
420
|
+
modalCenteredWrapper: {
|
|
421
|
+
width: '100%',
|
|
422
|
+
alignSelf: 'center',
|
|
423
|
+
...(Platform.OS === 'web' ? { maxWidth: 500, justifyContent: 'center', flex: 1 } : {}),
|
|
424
|
+
},
|
|
413
425
|
modalContent: {
|
|
414
426
|
backgroundColor: 'white',
|
|
415
427
|
borderTopLeftRadius: 24,
|
|
416
428
|
borderTopRightRadius: 24,
|
|
417
|
-
maxHeight: '
|
|
418
|
-
paddingBottom: 40,
|
|
429
|
+
maxHeight: '70%',
|
|
430
|
+
paddingBottom: Platform.OS === 'ios' ? 40 : 20,
|
|
431
|
+
...(Platform.OS === 'web' ? { borderRadius: 24, maxHeight: 500 } : {}),
|
|
419
432
|
},
|
|
420
433
|
modalHeader: {
|
|
421
434
|
flexDirection: 'row',
|
|
@@ -434,6 +447,7 @@ const styles = StyleSheet.create({
|
|
|
434
447
|
fontSize: 20,
|
|
435
448
|
color: '#666',
|
|
436
449
|
padding: 5,
|
|
450
|
+
...(Platform.OS === 'web' ? { cursor: 'pointer' } as any : {}),
|
|
437
451
|
},
|
|
438
452
|
countryItem: {
|
|
439
453
|
flexDirection: 'row',
|
|
@@ -443,6 +457,7 @@ const styles = StyleSheet.create({
|
|
|
443
457
|
paddingHorizontal: 24,
|
|
444
458
|
borderBottomWidth: 1,
|
|
445
459
|
borderBottomColor: '#f8f9fa',
|
|
460
|
+
...(Platform.OS === 'web' ? { cursor: 'pointer' } as any : {}),
|
|
446
461
|
},
|
|
447
462
|
countryItemLabel: {
|
|
448
463
|
fontSize: 16,
|
|
@@ -464,10 +479,12 @@ const styles = StyleSheet.create({
|
|
|
464
479
|
alignItems: 'center',
|
|
465
480
|
width: '100%',
|
|
466
481
|
height: '100%',
|
|
482
|
+
...(Platform.OS === 'web' ? { cursor: 'text' } as any : {}),
|
|
467
483
|
},
|
|
468
484
|
otpBox: {
|
|
469
485
|
width: '14%',
|
|
470
|
-
aspectRatio: 1,
|
|
486
|
+
aspectRatio: Platform.OS === 'web' ? undefined : 1, // Let height dictate aspect ratio on web
|
|
487
|
+
height: Platform.OS === 'web' ? 56 : undefined, // Fixed height prevents blowouts on web
|
|
471
488
|
borderWidth: 1,
|
|
472
489
|
borderColor: '#e0e0e0',
|
|
473
490
|
borderRadius: 12,
|
|
@@ -496,6 +513,7 @@ const styles = StyleSheet.create({
|
|
|
496
513
|
width: '100%',
|
|
497
514
|
height: '100%',
|
|
498
515
|
opacity: 0,
|
|
516
|
+
...(Platform.OS === 'web' ? { cursor: 'text' } as any : {}),
|
|
499
517
|
},
|
|
500
518
|
errorText: {
|
|
501
519
|
color: '#dc2626',
|
|
@@ -515,6 +533,7 @@ const styles = StyleSheet.create({
|
|
|
515
533
|
changeLink: {
|
|
516
534
|
alignSelf: 'flex-end',
|
|
517
535
|
marginTop: 12,
|
|
536
|
+
...(Platform.OS === 'web' ? { cursor: 'pointer' } as any : {}),
|
|
518
537
|
},
|
|
519
538
|
changeText: {
|
|
520
539
|
color: '#2DBD60',
|
|
@@ -524,6 +543,7 @@ const styles = StyleSheet.create({
|
|
|
524
543
|
resendButton: {
|
|
525
544
|
marginTop: 16,
|
|
526
545
|
alignItems: 'center',
|
|
546
|
+
...(Platform.OS === 'web' ? { cursor: 'pointer' } as any : {}),
|
|
527
547
|
},
|
|
528
548
|
resendText: {
|
|
529
549
|
color: '#666',
|
|
@@ -63,7 +63,7 @@ export class KYCService {
|
|
|
63
63
|
private faceServiceURL = 'https://face-infera.sanctumkey.com';
|
|
64
64
|
private textExtractionServiceURL = 'https://text-infera.sanctumkey.com';
|
|
65
65
|
private mrzServiceURL = 'https://mrz-infera.sanctumkey.com';
|
|
66
|
-
private barcodeServiceURL = 'https://kyc-engine.SanctumKey.net
|
|
66
|
+
private barcodeServiceURL = 'https://kyc-engine.SanctumKey.net';
|
|
67
67
|
private orientationServiceURL = 'http://18.188.180.154:8080';
|
|
68
68
|
|
|
69
69
|
// private faceServiceURL = 'https://kyc-engine.transfergratis.net:8000';
|