@sanctum-key/react-native-sdk 1.0.11 → 1.0.12
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/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/package.json +1 -1
- package/src/components/KYCElements/IDCardCapture.tsx +226 -296
package/build/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"IDCardCapture.d.ts","sourceRoot":"","sources":["../../../../src/components/KYCElements/IDCardCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"IDCardCapture.d.ts","sourceRoot":"","sources":["../../../../src/components/KYCElements/IDCardCapture.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAuC,MAAM,OAAO,CAAC;AAI5D,OAAO,EAAE,iBAAiB,EAAoI,MAAM,uBAAuB,CAAC;AAc5L,UAAU,cAAc;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAC;CAAE;AAC3F,UAAU,kBAAkB;IAAG,SAAS,EAAE,iBAAiB,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;IAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAAC,KAAK,IAAI,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAAE;AAExO,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA+atD,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, Image, ScrollView, Platform, ActivityIndicator, TouchableOpacity } from 'react-native';
|
|
2
|
+
import { View, Text, StyleSheet, Image, ScrollView, Platform, ActivityIndicator, TouchableOpacity } from 'react-native'; // 🚨 Added TouchableOpacity
|
|
3
3
|
import { showAlert } from '../../utils/platformAlert';
|
|
4
4
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
5
5
|
import { GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
|
|
@@ -13,11 +13,7 @@ import { getDocumentTypeInfo } from '../../utils/get-document-type-info';
|
|
|
13
13
|
import pathToBase64 from '../../utils/pathToBase64';
|
|
14
14
|
import { cropByObb, cropImageWithBBoxWithTolerance, getObbConfidence, OBB_CONFIDENCE_THRESHOLD } from '../../utils/cropByObb';
|
|
15
15
|
import REGION_MAPPING from '../../config/region_mapping.json';
|
|
16
|
-
const ISO_TO_COUNTRY_NAME = {
|
|
17
|
-
'KE': 'Kenya', 'CM': 'Cameroon', 'NG': 'Nigeria', 'CA': 'Canada',
|
|
18
|
-
'FR': 'France', 'GH': 'Ghana', 'ZA': 'South Africa', 'GB': 'Britain',
|
|
19
|
-
'CI': 'Ivory Coast', 'SN': 'Senegal', 'TG': 'Togo', 'ML': 'Mali'
|
|
20
|
-
};
|
|
16
|
+
const ISO_TO_COUNTRY_NAME = { 'KE': 'Kenya', 'CM': 'Cameroon', 'NG': 'Nigeria', 'CA': 'Canada', 'FR': 'France', 'GH': 'Ghana', 'ZA': 'South Africa', 'GB': 'Britain', 'CI': 'Ivory Coast', 'SN': 'Senegal', 'TG': 'Togo', 'ML': 'Mali' };
|
|
21
17
|
export const IDCardCapture = ({ component, value = {}, onValueChange, error, language = 'en' }) => {
|
|
22
18
|
const { t, locale } = useI18n();
|
|
23
19
|
const [showCamera, setShowCamera] = useState(false);
|
|
@@ -27,20 +23,15 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
27
23
|
const [silentCaptureResult, setSilentCaptureResult] = useState({ success: false, isAnalyzing: false });
|
|
28
24
|
const [isProcessingCapture, setIsProcessingCapture] = useState(false);
|
|
29
25
|
const [processingImagePath, setProcessingImagePath] = useState(null);
|
|
26
|
+
// 🚨 ADDED: Key to force camera re-mount
|
|
30
27
|
const [cameraKey, setCameraKey] = useState(0);
|
|
31
|
-
const documentTypeMapping = {
|
|
32
|
-
'nationalId': 'national_id', 'passport': 'passport', 'driversLicense': 'drivers_licence',
|
|
33
|
-
'residencePermit': 'permanent_residence', 'healthInsuranceCard': 'health_insurance_card',
|
|
34
|
-
};
|
|
28
|
+
const documentTypeMapping = { 'nationalId': 'national_id', 'passport': 'passport', 'driversLicense': 'drivers_licence', 'residencePermit': 'permanent_residence', 'healthInsuranceCard': 'health_insurance_card', };
|
|
35
29
|
const { actions, state, env } = useTemplateKYCFlowContext();
|
|
36
30
|
const getLocalizedText = (text) => {
|
|
37
31
|
if (text && typeof text[currentSide] === 'object' && text[currentSide][locale])
|
|
38
32
|
return text[currentSide][locale] || '';
|
|
39
33
|
return "";
|
|
40
34
|
};
|
|
41
|
-
const refreshCamera = () => {
|
|
42
|
-
setCameraKey(prev => prev + 1);
|
|
43
|
-
};
|
|
44
35
|
const countrySelectionData = useMemo(() => {
|
|
45
36
|
const countrySelectionComponent = state.template.components.find(c => c.type === 'country_selection');
|
|
46
37
|
return countrySelectionComponent ? state.componentData[countrySelectionComponent.id] : null;
|
|
@@ -53,6 +44,14 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
53
44
|
return { type: mappedType, region: countrySelectionData.region || 'root' };
|
|
54
45
|
}, [countrySelectionData, documentTypeMapping]);
|
|
55
46
|
const countryData = useMemo(() => countrySelectionData, [countrySelectionData]);
|
|
47
|
+
const [isRebootingCamera, setIsRebootingCamera] = useState(false);
|
|
48
|
+
const refreshCamera = () => {
|
|
49
|
+
setIsRebootingCamera(true);
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
setCameraKey(prev => prev + 1);
|
|
52
|
+
setIsRebootingCamera(false);
|
|
53
|
+
}, 500);
|
|
54
|
+
};
|
|
56
55
|
useEffect(() => {
|
|
57
56
|
if (value && Object.keys(value).length > 0) {
|
|
58
57
|
if (JSON.stringify(value) !== JSON.stringify(capturedImages)) {
|
|
@@ -61,21 +60,14 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
61
60
|
const currentImageData = updatedImages[currentSide];
|
|
62
61
|
if (currentImageData?.dir) {
|
|
63
62
|
setSilentCaptureResult(prev => ({
|
|
64
|
-
...prev,
|
|
65
|
-
path: currentImageData.dir,
|
|
66
|
-
success: true,
|
|
67
|
-
isAnalyzing: false,
|
|
68
|
-
mrz: currentImageData.mrz || '',
|
|
69
|
-
templatePath: currentImageData.templatePath || '',
|
|
63
|
+
...prev, path: currentImageData.dir, success: true, isAnalyzing: false, mrz: currentImageData.mrz || '', templatePath: currentImageData.templatePath || '',
|
|
70
64
|
}));
|
|
71
65
|
}
|
|
72
66
|
}
|
|
73
67
|
}
|
|
74
68
|
}, [value, currentSide]);
|
|
75
69
|
const cameraConfig = useMemo(() => {
|
|
76
|
-
const instructions = selectedDocumentType
|
|
77
|
-
? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
|
|
78
|
-
: getLocalizedText(component.instructions);
|
|
70
|
+
const instructions = selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr) : getLocalizedText(component.instructions);
|
|
79
71
|
return {
|
|
80
72
|
cameraType: 'back', flashMode: 'auto',
|
|
81
73
|
overlay: { guideText: instructions, bbox: { xMin: 15, yMin: 20, xMax: 85, yMax: 70, borderColor: '#2DBD60', borderWidth: 3, cornerRadius: 8 } }
|
|
@@ -102,7 +94,6 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
102
94
|
const rawCountryName = ISO_TO_COUNTRY_NAME[countryData?.code || ''] || countryData?.code || countryKey;
|
|
103
95
|
const baseMapping = REGION_MAPPING.regionMapping || REGION_MAPPING;
|
|
104
96
|
let countryMapping = baseMapping[rawCountryName];
|
|
105
|
-
// Fallback search in case of case mismatches
|
|
106
97
|
if (!countryMapping) {
|
|
107
98
|
const foundKey = Object.keys(baseMapping).find(k => k.toLowerCase() === rawCountryName.toLowerCase() || k.toLowerCase() === countryKey.toLowerCase());
|
|
108
99
|
if (foundKey)
|
|
@@ -197,7 +188,6 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
197
188
|
return;
|
|
198
189
|
if (result.success && result.path) {
|
|
199
190
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
|
|
200
|
-
// 🚨 Force a template fetch if we haven't successfully saved the current side yet
|
|
201
191
|
let templatePath = capturedImages[currentSide]?.templatePath || '';
|
|
202
192
|
let templateBbox;
|
|
203
193
|
let templateResponse;
|
|
@@ -238,19 +228,7 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
238
228
|
matchedBackAuthMethod = 'MRZ';
|
|
239
229
|
}
|
|
240
230
|
const backMrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || 'TD1';
|
|
241
|
-
verificationRes = await backVerification({
|
|
242
|
-
path: result.path,
|
|
243
|
-
regionMapping: {
|
|
244
|
-
authMethod: matchedBackAuthMethod ? [matchedBackAuthMethod] : regionMappings.authMethod,
|
|
245
|
-
mrzTypes: regionMappings.mrzTypes
|
|
246
|
-
},
|
|
247
|
-
selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type] || '',
|
|
248
|
-
code: countryData?.code || '',
|
|
249
|
-
currentSide,
|
|
250
|
-
templatePath,
|
|
251
|
-
mrzType: backMrzType,
|
|
252
|
-
templateResponse
|
|
253
|
-
}, env);
|
|
231
|
+
verificationRes = await backVerification({ path: result.path, regionMapping: { authMethod: matchedBackAuthMethod ? [matchedBackAuthMethod] : regionMappings.authMethod, mrzTypes: regionMappings.mrzTypes }, selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type] || '', code: countryData?.code || '', currentSide, templatePath, mrzType: backMrzType, templateResponse }, env);
|
|
254
232
|
}
|
|
255
233
|
const bbox = verificationRes?.bbox || templateBbox;
|
|
256
234
|
const mrz = verificationRes?.mrz ? JSON.stringify(verificationRes.mrz) : "";
|
|
@@ -272,7 +250,9 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
272
250
|
setShowCamera(false);
|
|
273
251
|
setIsProcessingCapture(false);
|
|
274
252
|
};
|
|
275
|
-
useEffect(() => {
|
|
253
|
+
useEffect(() => {
|
|
254
|
+
actions.showCustomStepper(!showCamera);
|
|
255
|
+
}, [showCamera]);
|
|
276
256
|
if (!countrySelectionData || !selectedDocumentType) {
|
|
277
257
|
return (<View style={styles.root}>
|
|
278
258
|
<View style={styles.previewContainer}>
|
|
@@ -286,41 +266,31 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
286
266
|
// --- CAMERA RENDER ---
|
|
287
267
|
if (showCamera) {
|
|
288
268
|
const isBusy = isProcessingCapture;
|
|
269
|
+
if (isRebootingCamera) {
|
|
270
|
+
return (<View style={[styles.root, { justifyContent: 'center', alignItems: 'center', backgroundColor: '#000' }]}>
|
|
271
|
+
<ActivityIndicator size="large" color="#2DBD60"/>
|
|
272
|
+
<Text style={{ color: 'white', marginTop: 20, fontSize: 16 }}>Initializing Camera...</Text>
|
|
273
|
+
</View>);
|
|
274
|
+
}
|
|
289
275
|
return (<View style={styles.root}>
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
<
|
|
294
|
-
|
|
295
|
-
|
|
276
|
+
<View style={[styles.cameraWrapper, { flex: 1, minHeight: 400 }]}>
|
|
277
|
+
|
|
278
|
+
<View style={styles.headerContainer}>
|
|
279
|
+
<Text style={styles.headerTitle}>
|
|
280
|
+
{selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : ''}
|
|
281
|
+
</Text>
|
|
282
|
+
<View style={styles.stepBadge}>
|
|
283
|
+
<Text style={styles.stepText}>
|
|
284
|
+
{t('kyc.idCardCapture.captureTitle', { side: currentSide === 'front' ? locale === 'en' ? 'Front' : 'Recto' : locale === 'en' ? 'Back' : 'Verso' })}
|
|
296
285
|
</Text>
|
|
297
|
-
<View style={styles.stepBadge}>
|
|
298
|
-
<Text style={styles.stepText}>
|
|
299
|
-
{t('kyc.idCardCapture.captureTitle', { side: currentSide === 'front' ? locale === 'en' ? 'Front' : 'Recto' : locale === 'en' ? 'Back' : 'Verso' })}
|
|
300
|
-
</Text>
|
|
301
|
-
</View>
|
|
302
286
|
</View>
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
{state.currentLanguage === 'en' ? 'Scanning...' : 'Analyse...'}
|
|
311
|
-
</Text>
|
|
312
|
-
</View>
|
|
313
|
-
</View>)}
|
|
314
|
-
{isBusy && (<View style={StyleSheet.absoluteFillObject}>
|
|
315
|
-
{processingImagePath && (<Image source={{ uri: processingImagePath.startsWith('file://') ? processingImagePath : `file://${processingImagePath}` }} style={StyleSheet.absoluteFillObject} resizeMode="cover"/>)}
|
|
316
|
-
<View style={styles.processingOverlay}>
|
|
317
|
-
<ActivityIndicator size="large" color="#2DBD60"/>
|
|
318
|
-
<Text style={styles.processingText}>
|
|
319
|
-
{state.currentLanguage === 'en' ? 'Perfect!\nProcessing Document...' : 'Parfait!\nTraitement du document...'}
|
|
320
|
-
</Text>
|
|
321
|
-
</View>
|
|
322
|
-
</View>)}
|
|
323
|
-
<IdCardOverlay xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax} instructions={cameraConfig.overlay.guideText} cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0} isSuccess={silentCaptureResult.success} language={state.currentLanguage} stepperProps={{
|
|
287
|
+
</View>
|
|
288
|
+
|
|
289
|
+
<View style={[styles.cameraFeedContainer, { flex: 1, backgroundColor: '#000' }]}>
|
|
290
|
+
|
|
291
|
+
<EnhancedCameraView key={`${currentSide}-${cameraKey}`} showCamera={true} isProcessing={isBusy} cameraType={cameraConfig.cameraType} style={styles.absoluteFillObject} onError={handleError} onSilentCapture={handleSilentCapture} silentCaptureResult={silentCaptureResult} overlayComponent={<>
|
|
292
|
+
{/* We ONLY put the ID frame here, because if the camera fails, we don't care if the frame fails too */}
|
|
293
|
+
<IdCardOverlay xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax} instructions={cameraConfig.overlay.guideText} cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0} isSuccess={silentCaptureResult.success} language={state.currentLanguage} stepperProps={{
|
|
324
294
|
back: () => {
|
|
325
295
|
if (currentSide === 'back') {
|
|
326
296
|
setCurrentSide('front');
|
|
@@ -342,18 +312,46 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
342
312
|
selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
|
|
343
313
|
step: state.currentComponentIndex + 1, totalSteps: state.template.components.length, side: currentSide,
|
|
344
314
|
}}/>
|
|
315
|
+
</>}/>
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
{!isBusy && silentCaptureResult.isAnalyzing && (<View style={styles.topAnalyzingPillContainer}>
|
|
319
|
+
<View style={styles.topAnalyzingPill}>
|
|
320
|
+
<ActivityIndicator size="small" color="white"/>
|
|
321
|
+
<Text style={styles.analyzingPillText}>
|
|
322
|
+
{state.currentLanguage === 'en' ? 'Scanning...' : 'Analyse...'}
|
|
323
|
+
</Text>
|
|
324
|
+
</View>
|
|
325
|
+
</View>)}
|
|
326
|
+
|
|
327
|
+
{isBusy && (<View style={[StyleSheet.absoluteFillObject, { zIndex: 9999 }]}>
|
|
328
|
+
{processingImagePath && (<Image source={{ uri: processingImagePath.startsWith('file://') ? processingImagePath : `file://${processingImagePath}` }} style={StyleSheet.absoluteFillObject} resizeMode="cover"/>)}
|
|
329
|
+
<View style={styles.processingOverlay}>
|
|
330
|
+
<ActivityIndicator size="large" color="#2DBD60"/>
|
|
331
|
+
<Text style={styles.processingText}>
|
|
332
|
+
{state.currentLanguage === 'en' ? 'Perfect!\nProcessing Document...' : 'Parfait!\nTraitement du document...'}
|
|
333
|
+
</Text>
|
|
334
|
+
</View>
|
|
335
|
+
</View>)}
|
|
336
|
+
|
|
337
|
+
{!isBusy && (<View style={styles.escapeHatchContainer}>
|
|
338
|
+
{/* Refresh Button */}
|
|
339
|
+
<TouchableOpacity style={styles.fallbackRefreshButton} onPress={refreshCamera}>
|
|
340
|
+
<Text style={styles.fallbackRefreshText}>↻ Refresh Camera</Text>
|
|
341
|
+
</TouchableOpacity>
|
|
342
|
+
|
|
343
|
+
<TouchableOpacity style={[styles.fallbackRefreshButton, { marginTop: 15, backgroundColor: 'rgba(220, 38, 38, 0.8)', borderColor: '#DC2626' }]} onPress={() => setShowCamera(false)}>
|
|
344
|
+
<Text style={styles.fallbackRefreshText}>Cancel / Go Back</Text>
|
|
345
|
+
</TouchableOpacity>
|
|
346
|
+
</View>)}
|
|
347
|
+
|
|
348
|
+
{silentCaptureResult.error && !isBusy ? (<View style={[styles.floatingErrorBanner, { zIndex: 10000 }]}>
|
|
349
|
+
<Text style={styles.floatingErrorText}>⚠️ {silentCaptureResult.error}</Text>
|
|
350
|
+
</View>) : null}
|
|
345
351
|
|
|
346
|
-
<TouchableOpacity style={styles.refreshButton} onPress={refreshCamera}>
|
|
347
|
-
<Text style={styles.refreshButtonText}>Refresh Camera</Text>
|
|
348
|
-
</TouchableOpacity>
|
|
349
|
-
</>}/>
|
|
350
|
-
{/* Elegant Floating Error Banner below the cutout */}
|
|
351
|
-
{silentCaptureResult.error && !isBusy ? (<View style={styles.floatingErrorBanner}>
|
|
352
|
-
<Text style={styles.floatingErrorText}>⚠️ {silentCaptureResult.error}</Text>
|
|
353
|
-
</View>) : null}
|
|
354
|
-
</View>
|
|
355
352
|
</View>
|
|
356
|
-
</View>
|
|
353
|
+
</View>
|
|
354
|
+
</View>);
|
|
357
355
|
}
|
|
358
356
|
return (<View style={styles.root}>
|
|
359
357
|
<View style={styles.previewContainer}>
|
|
@@ -402,121 +400,39 @@ export const IDCardCapture = ({ component, value = {}, onValueChange, error, lan
|
|
|
402
400
|
};
|
|
403
401
|
const styles = StyleSheet.create({
|
|
404
402
|
root: {
|
|
405
|
-
flex: 1,
|
|
406
|
-
|
|
407
|
-
backgroundColor: 'transparent',
|
|
408
|
-
alignSelf: 'center',
|
|
409
|
-
...(Platform.OS === 'web'
|
|
410
|
-
? {
|
|
411
|
-
minHeight: '85vh',
|
|
412
|
-
justifyContent: 'center',
|
|
413
|
-
alignItems: 'center',
|
|
414
|
-
// Note: backdropFilter is valid in React Native Web but TS might complain, cast safely
|
|
415
|
-
backdropFilter: 'blur(8px)'
|
|
416
|
-
}
|
|
417
|
-
: {})
|
|
403
|
+
flex: 1, width: '100%', backgroundColor: 'transparent', alignSelf: 'center',
|
|
404
|
+
...(Platform.OS === 'web' ? { minHeight: '85vh', justifyContent: 'center', alignItems: 'center', backdropFilter: 'blur(8px)' } : {})
|
|
418
405
|
},
|
|
419
406
|
cameraWrapper: {
|
|
420
|
-
width: '100%',
|
|
421
|
-
|
|
422
|
-
overflow: 'hidden',
|
|
423
|
-
...(Platform.OS === 'web'
|
|
424
|
-
? {
|
|
425
|
-
maxWidth: 500,
|
|
426
|
-
height: 700,
|
|
427
|
-
maxHeight: '90vh', // TypeScript will now ignore this thanks to the cast below
|
|
428
|
-
borderRadius: 24,
|
|
429
|
-
shadowColor: '#000',
|
|
430
|
-
shadowOffset: { width: 0, height: 20 },
|
|
431
|
-
shadowOpacity: 0.25,
|
|
432
|
-
shadowRadius: 35,
|
|
433
|
-
elevation: 24,
|
|
434
|
-
} // 🚨 CAST TO ANY
|
|
435
|
-
: {
|
|
436
|
-
flex: 1,
|
|
437
|
-
})
|
|
407
|
+
width: '100%', backgroundColor: '#FFFFFF', overflow: 'hidden',
|
|
408
|
+
...(Platform.OS === 'web' ? { maxWidth: 500, height: 700, maxHeight: '90vh', borderRadius: 24, shadowColor: '#000', shadowOffset: { width: 0, height: 20 }, shadowOpacity: 0.25, shadowRadius: 35, elevation: 24, } : { flex: 1, })
|
|
438
409
|
},
|
|
439
410
|
headerContainer: {
|
|
440
|
-
flexDirection: 'row',
|
|
441
|
-
alignItems: 'center',
|
|
442
|
-
justifyContent: 'space-between',
|
|
443
|
-
paddingHorizontal: 24,
|
|
444
|
-
paddingVertical: 18,
|
|
445
|
-
backgroundColor: '#FFFFFF',
|
|
446
|
-
borderBottomWidth: 1,
|
|
447
|
-
borderBottomColor: '#F1F5F9',
|
|
448
|
-
zIndex: 10,
|
|
449
|
-
// Mobile hidden, Web visible to replace floating text
|
|
411
|
+
flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 24, paddingVertical: 18, backgroundColor: '#FFFFFF', borderBottomWidth: 1, borderBottomColor: '#F1F5F9', zIndex: 10,
|
|
450
412
|
...(Platform.OS !== 'web' ? { display: 'none' } : {})
|
|
451
413
|
},
|
|
452
|
-
headerTitle: {
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
},
|
|
457
|
-
|
|
458
|
-
backgroundColor: '
|
|
459
|
-
paddingHorizontal: 12,
|
|
460
|
-
paddingVertical: 6,
|
|
461
|
-
borderRadius: 20,
|
|
462
|
-
},
|
|
463
|
-
stepText: {
|
|
464
|
-
fontSize: 13,
|
|
465
|
-
fontWeight: '600',
|
|
466
|
-
color: '#64748B',
|
|
467
|
-
},
|
|
468
|
-
cameraFeedContainer: {
|
|
469
|
-
flex: 1,
|
|
470
|
-
position: 'relative',
|
|
471
|
-
backgroundColor: '#000',
|
|
472
|
-
},
|
|
473
|
-
camera: {
|
|
474
|
-
flex: 1,
|
|
414
|
+
headerTitle: { fontSize: 18, fontWeight: '700', color: '#0F172A', },
|
|
415
|
+
stepBadge: { backgroundColor: '#F1F5F9', paddingHorizontal: 12, paddingVertical: 6, borderRadius: 20, },
|
|
416
|
+
stepText: { fontSize: 13, fontWeight: '600', color: '#64748B', },
|
|
417
|
+
cameraFeedContainer: { flex: 1, position: 'relative', backgroundColor: '#000', },
|
|
418
|
+
camera: { flex: 1, },
|
|
419
|
+
refreshButton: {
|
|
420
|
+
position: 'absolute', bottom: 100, alignSelf: 'center', backgroundColor: 'rgba(0,0,0,0.6)', paddingVertical: 12, paddingHorizontal: 24, borderRadius: 24, zIndex: 500
|
|
475
421
|
},
|
|
422
|
+
refreshButtonText: { color: 'white', fontWeight: 'bold', fontSize: 16 },
|
|
476
423
|
previewContainer: {
|
|
477
|
-
width: '100%',
|
|
478
|
-
backgroundColor: 'white',
|
|
479
|
-
borderRadius: 12,
|
|
480
|
-
paddingVertical: 24,
|
|
481
|
-
paddingHorizontal: 20,
|
|
482
|
-
shadowColor: '#000',
|
|
483
|
-
shadowOffset: { width: 0, height: 4 },
|
|
484
|
-
shadowOpacity: 0.1,
|
|
485
|
-
shadowRadius: 12,
|
|
486
|
-
elevation: 8,
|
|
424
|
+
width: '100%', backgroundColor: 'white', borderRadius: 12, paddingVertical: 24, paddingHorizontal: 20, shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 12, elevation: 8,
|
|
487
425
|
...(Platform.OS === 'web' ? { alignSelf: 'center', maxWidth: 600 } : { margin: 10, width: '95%' })
|
|
488
426
|
},
|
|
489
|
-
previewItemContainer: {
|
|
490
|
-
flexGrow: 1,
|
|
491
|
-
},
|
|
427
|
+
previewItemContainer: { flexGrow: 1, },
|
|
492
428
|
title: { fontSize: 24, fontWeight: 'bold', color: '#333', marginBottom: 8, textAlign: 'center' },
|
|
493
429
|
description: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 24, lineHeight: 22 },
|
|
494
430
|
sideContainer: { marginBottom: 24 },
|
|
495
431
|
sideTitle: { fontSize: 25, fontWeight: 'bold', color: '#000', marginBottom: 12, textAlign: 'center' },
|
|
496
|
-
imagePreviewWrapper: {
|
|
497
|
-
width: '100%', height: 220, borderRadius: 12, padding: 1, overflow: 'hidden',
|
|
498
|
-
shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.18, shadowRadius: 8, elevation: 8, backgroundColor: '#f0f0f0'
|
|
499
|
-
},
|
|
432
|
+
imagePreviewWrapper: { width: '100%', height: 220, borderRadius: 12, padding: 1, overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.18, shadowRadius: 8, elevation: 8, backgroundColor: '#f0f0f0' },
|
|
500
433
|
previewImage: { width: '100%', height: '100%', borderRadius: 12, resizeMode: 'cover' },
|
|
501
434
|
floatingErrorBanner: {
|
|
502
|
-
position: 'absolute',
|
|
503
|
-
bottom: 30, // Pushed to the bottom for professional feel
|
|
504
|
-
left: 24,
|
|
505
|
-
right: 24,
|
|
506
|
-
backgroundColor: '#FEF2F2',
|
|
507
|
-
borderWidth: 1,
|
|
508
|
-
borderColor: '#FCA5A5',
|
|
509
|
-
paddingVertical: 12,
|
|
510
|
-
paddingHorizontal: 16,
|
|
511
|
-
borderRadius: 12,
|
|
512
|
-
alignItems: 'center',
|
|
513
|
-
justifyContent: 'center',
|
|
514
|
-
shadowColor: '#DC2626',
|
|
515
|
-
shadowOffset: { width: 0, height: 4 },
|
|
516
|
-
shadowOpacity: 0.1,
|
|
517
|
-
shadowRadius: 8,
|
|
518
|
-
elevation: 8,
|
|
519
|
-
zIndex: 100
|
|
435
|
+
position: 'absolute', bottom: 30, left: 24, right: 24, backgroundColor: '#FEF2F2', borderWidth: 1, borderColor: '#FCA5A5', paddingVertical: 12, paddingHorizontal: 16, borderRadius: 12, alignItems: 'center', justifyContent: 'center', shadowColor: '#DC2626', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.1, shadowRadius: 8, elevation: 8, zIndex: 100
|
|
520
436
|
},
|
|
521
437
|
floatingErrorText: { color: '#991B1B', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
522
438
|
processingOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.6)', justifyContent: 'center', alignItems: 'center', zIndex: 9999 },
|
|
@@ -527,7 +443,39 @@ const styles = StyleSheet.create({
|
|
|
527
443
|
topAnalyzingPillContainer: { position: 'absolute', top: Platform.OS === 'android' ? 60 : 50, left: 0, right: 0, alignItems: 'center', zIndex: 100 },
|
|
528
444
|
topAnalyzingPill: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.6)', paddingVertical: 8, paddingHorizontal: 16, borderRadius: 20, gap: 8 },
|
|
529
445
|
analyzingPillText: { color: 'white', fontSize: 14, fontWeight: 'bold' },
|
|
530
|
-
|
|
531
|
-
|
|
446
|
+
escapeHatchContainer: {
|
|
447
|
+
position: 'absolute',
|
|
448
|
+
bottom: 40,
|
|
449
|
+
left: 0,
|
|
450
|
+
right: 0,
|
|
451
|
+
alignItems: 'center',
|
|
452
|
+
justifyContent: 'center',
|
|
453
|
+
zIndex: 99999, // Guarantees it is the top-most element
|
|
454
|
+
},
|
|
455
|
+
fallbackRefreshButton: {
|
|
456
|
+
backgroundColor: 'rgba(0, 0, 0, 0.8)', // Darker so it's visible on white or black
|
|
457
|
+
borderWidth: 1,
|
|
458
|
+
borderColor: 'rgba(255, 255, 255, 0.5)',
|
|
459
|
+
paddingVertical: 12,
|
|
460
|
+
paddingHorizontal: 24,
|
|
461
|
+
borderRadius: 24,
|
|
462
|
+
shadowColor: '#000',
|
|
463
|
+
shadowOffset: { width: 0, height: 4 },
|
|
464
|
+
shadowOpacity: 0.3,
|
|
465
|
+
shadowRadius: 5,
|
|
466
|
+
elevation: 8,
|
|
467
|
+
},
|
|
468
|
+
fallbackRefreshText: {
|
|
469
|
+
color: '#FFFFFF',
|
|
470
|
+
fontWeight: 'bold',
|
|
471
|
+
fontSize: 16,
|
|
472
|
+
},
|
|
473
|
+
absoluteFillObject: {
|
|
474
|
+
position: 'absolute',
|
|
475
|
+
top: 0,
|
|
476
|
+
left: 0,
|
|
477
|
+
right: 0,
|
|
478
|
+
bottom: 0,
|
|
479
|
+
},
|
|
532
480
|
});
|
|
533
481
|
//# sourceMappingURL=IDCardCapture.js.map
|