@sanctum-key/react-native-sdk 1.0.9 → 1.0.11
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/README.md +3 -3
- package/android/build/.transforms/{c9d62bb333688ab562f51958998d5a48 → 9e34a0354bf8964d60c4c1392f5aa5b2}/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/generated/source/buildConfig/debug/kyc/{SanctumKey → sanctumkey}/com/BuildConfig.java +2 -2
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +1 -1
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/output-metadata.json +1 -1
- package/android/build/intermediates/compile_library_classes_jar/debug/bundleLibCompileToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/compile_r_class_jar/debug/generateDebugRFile/R.jar +0 -0
- package/android/build/intermediates/compiled_local_resources/debug/compileDebugLibraryResources/out/xml_file_paths.xml.flat +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +2 -2
- package/android/build/intermediates/incremental/debug/packageDebugResources/merger.xml +1 -1
- package/android/build/intermediates/incremental/mergeDebugJniLibFolders/merger.xml +1 -1
- package/android/build/intermediates/incremental/mergeDebugShaders/merger.xml +1 -1
- package/android/build/intermediates/incremental/packageDebugAssets/merger.xml +1 -1
- package/android/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes/kyc/{SanctumKey → sanctumkey}/com/BuildConfig.class +0 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +42 -42
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +1 -1
- package/android/build/intermediates/runtime_library_classes_jar/debug/bundleLibRuntimeToJarDebug/classes.jar +0 -0
- package/android/build/intermediates/symbol_list_with_package_name/debug/generateDebugRFile/package-aware-r.txt +1 -1
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/inputs/source-to-output.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-attributes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/class-fq-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/internal-name-to-source.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/proto.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/source-to-classes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/subtypes.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/jvm/kotlin/supertypes.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab.keystream.len +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/file-to-id.tab_i +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/id-to-file.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.keystream +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab.values.at +0 -0
- package/android/build/kotlin/compileDebugKotlin/cacheable/caches-jvm/lookups/lookups.tab_i +0 -0
- 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/logs/manifest-merger-debug-report.txt +52 -52
- package/android/build/tmp/compileDebugJavaWithJavac/previous-compilation-data.bin +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$1$5$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$1$5$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$3.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$4.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$5.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunction$6.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$3.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$AsyncFunctionWithPromise$4.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$$inlined$View$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$1.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule$definition$lambda$5$lambda$4$$inlined$Prop$2.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkModule.class +0 -0
- package/android/build/tmp/kotlin-classes/debug/kyc/{SanctumKey → sanctumkey}/com/SanctumKeySdkView.class +0 -0
- package/android/build.gradle +2 -2
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/java/kyc/{transfergratis/com/TransfergratisSdkModule.kt → sanctumkey/com/SanctumKeySdkModule.kt} +21 -18
- package/android/src/main/java/kyc/{transfergratis/com/TransfergratisSdkView.kt → sanctumkey/com/SanctumKeySdkView.kt} +2 -2
- package/build/package.json +5 -5
- package/build/src/App.d.ts +2 -2
- package/build/src/App.d.ts.map +1 -1
- package/build/src/App.js +2 -2
- package/build/src/App.js.map +1 -1
- package/build/src/{TransfergratisSdk.types.d.ts → SanctumKeySdk.types.d.ts} +3 -3
- package/build/src/SanctumKeySdk.types.d.ts.map +1 -0
- package/build/src/SanctumKeySdk.types.js +2 -0
- package/build/src/SanctumKeySdk.types.js.map +1 -0
- package/build/src/{TransfergratisSdkModule.d.ts → SanctumKeySdkModule.d.ts} +4 -4
- package/build/src/SanctumKeySdkModule.d.ts.map +1 -0
- package/build/src/{TransfergratisSdkModule.js → SanctumKeySdkModule.js} +2 -2
- package/build/src/SanctumKeySdkModule.js.map +1 -0
- package/build/src/{TransfergratisSdkModule.web.d.ts → SanctumKeySdkModule.web.d.ts} +4 -4
- package/build/src/SanctumKeySdkModule.web.d.ts.map +1 -0
- package/build/src/{TransfergratisSdkModule.web.js → SanctumKeySdkModule.web.js} +3 -3
- package/build/src/SanctumKeySdkModule.web.js.map +1 -0
- package/build/src/SanctumKeySdkView.d.ts +4 -0
- package/build/src/SanctumKeySdkView.d.ts.map +1 -0
- package/build/src/SanctumKeySdkView.js +7 -0
- package/build/src/SanctumKeySdkView.js.map +1 -0
- package/build/src/SanctumKeySdkView.web.d.ts +4 -0
- package/build/src/SanctumKeySdkView.web.d.ts.map +1 -0
- package/build/src/{TransfergratisSdkView.web.js → SanctumKeySdkView.web.js} +2 -2
- package/build/src/SanctumKeySdkView.web.js.map +1 -0
- package/build/src/components/KYCElements/CountrySelection.d.ts.map +1 -1
- package/build/src/components/KYCElements/CountrySelection.js +259 -63
- package/build/src/components/KYCElements/CountrySelection.js.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/src/components/KYCElements/IDCardCapture.js +231 -69
- 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 +160 -21
- package/build/src/components/KYCElements/PhoneVerificationTemplate.js.map +1 -1
- package/build/src/components/NativeCameraView.js +1 -1
- package/build/src/components/NativeCameraView.js.map +1 -1
- package/build/src/components/TemplateKYCExample.d.ts +4 -3
- package/build/src/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/src/components/TemplateKYCExample.js +2 -2
- package/build/src/components/TemplateKYCExample.js.map +1 -1
- package/build/src/config/allowedDomains.js +6 -6
- package/build/src/config/allowedDomains.js.map +1 -1
- package/build/src/config/region_mapping.json +727 -0
- package/build/src/index.d.ts +3 -3
- package/build/src/index.d.ts.map +1 -1
- package/build/src/index.js +3 -3
- package/build/src/index.js.map +1 -1
- package/build/src/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/src/modules/api/CardAuthentification.js +3 -7
- package/build/src/modules/api/CardAuthentification.js.map +1 -1
- package/build/src/modules/api/KYCService.d.ts +1 -2
- package/build/src/modules/api/KYCService.d.ts.map +1 -1
- package/build/src/modules/api/KYCService.js +112 -60
- package/build/src/modules/api/KYCService.js.map +1 -1
- package/build/src/modules/camera/NativeCameraModule.js +17 -17
- package/build/src/modules/camera/NativeCameraModule.js.map +1 -1
- package/build/src/web/WebKYCEntry.d.ts +2 -2
- package/build/src/web/WebKYCEntry.d.ts.map +1 -1
- package/build/src/web/WebKYCEntry.js +3 -2
- package/build/src/web/WebKYCEntry.js.map +1 -1
- package/expo-module.config.json +3 -3
- package/ios/TransfergratisSdk.podspec +2 -2
- package/ios/TransfergratisSdkModule.swift +12 -12
- package/package.json +5 -5
- package/src/App.tsx +2 -2
- package/src/{TransfergratisSdk.types.ts → SanctumKeySdk.types.ts} +2 -2
- package/src/{TransfergratisSdkModule.ts → SanctumKeySdkModule.ts} +3 -3
- package/src/{TransfergratisSdkModule.web.ts → SanctumKeySdkModule.web.ts} +3 -3
- package/src/SanctumKeySdkView.tsx +11 -0
- package/src/{TransfergratisSdkView.web.tsx → SanctumKeySdkView.web.tsx} +2 -2
- package/src/components/KYCElements/CountrySelection.tsx +300 -74
- package/src/components/KYCElements/IDCardCapture.tsx +322 -157
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +201 -29
- package/src/components/NativeCameraView.tsx +1 -1
- package/src/components/TemplateKYCExample.tsx +23 -4
- package/src/config/allowedDomains.ts +6 -6
- package/src/i18n/README.md +1 -1
- package/src/index.ts +3 -3
- package/src/modules/api/CardAuthentification.ts +5 -8
- package/src/modules/api/KYCService.ts +174 -106
- package/src/modules/camera/NativeCameraModule.ts +17 -17
- package/src/web/WebKYCEntry.tsx +3 -3
- package/android/build/.transforms/ab90740579f5bd05b27b4343ada2d1c9/transformed/classes/classes_dex/classes.dex +0 -0
- package/android/build/.transforms/c9d62bb333688ab562f51958998d5a48/results.bin +0 -1
- package/android/build/tmp/compileDebugJavaWithJavac/compileTransaction/stash-dir/BuildConfig.class.uniqueId0 +0 -0
- package/build/src/TransfergratisSdk.types.d.ts.map +0 -1
- package/build/src/TransfergratisSdk.types.js +0 -2
- package/build/src/TransfergratisSdk.types.js.map +0 -1
- package/build/src/TransfergratisSdkModule.d.ts.map +0 -1
- package/build/src/TransfergratisSdkModule.js.map +0 -1
- package/build/src/TransfergratisSdkModule.web.d.ts.map +0 -1
- package/build/src/TransfergratisSdkModule.web.js.map +0 -1
- package/build/src/TransfergratisSdkView.d.ts +0 -4
- package/build/src/TransfergratisSdkView.d.ts.map +0 -1
- package/build/src/TransfergratisSdkView.js +0 -7
- package/build/src/TransfergratisSdkView.js.map +0 -1
- package/build/src/TransfergratisSdkView.web.d.ts +0 -4
- package/build/src/TransfergratisSdkView.web.d.ts.map +0 -1
- package/build/src/TransfergratisSdkView.web.js.map +0 -1
- package/src/TransfergratisSdkView.tsx +0 -11
- /package/android/build/.transforms/{ab90740579f5bd05b27b4343ada2d1c9 → 9e34a0354bf8964d60c4c1392f5aa5b2}/results.bin +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { View, Text, StyleSheet, Image, ScrollView, Platform, ActivityIndicator } from 'react-native';
|
|
2
|
+
import { View, Text, StyleSheet, Image, ScrollView, Platform, ActivityIndicator, TouchableOpacity } from 'react-native';
|
|
3
3
|
import { showAlert } from '../../utils/platformAlert';
|
|
4
4
|
import { EnhancedCameraView } from '../EnhancedCameraView';
|
|
5
5
|
import { TemplateComponent, LocalizedText, GovernmentDocumentType, ISilentCaptureResult, IBbox, GovernmentDocumentTypeShorted, GovernmentDocumentTypeBackend } from '../../types/KYC.types';
|
|
@@ -12,6 +12,13 @@ import { backVerification, checkTemplateType, frontVerification } from '../../mo
|
|
|
12
12
|
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
|
+
import REGION_MAPPING from '../../config/region_mapping.json';
|
|
16
|
+
|
|
17
|
+
const ISO_TO_COUNTRY_NAME: Record<string, string> = {
|
|
18
|
+
'KE': 'Kenya', 'CM': 'Cameroon', 'NG': 'Nigeria', 'CA': 'Canada',
|
|
19
|
+
'FR': 'France', 'GH': 'Ghana', 'ZA': 'South Africa', 'GB': 'Britain',
|
|
20
|
+
'CI': 'Ivory Coast', 'SN': 'Senegal', 'TG': 'Togo', 'ML': 'Mali'
|
|
21
|
+
};
|
|
15
22
|
|
|
16
23
|
interface IIDCardPayload { dir: string; file: string; mrz: string; templatePath?: string; }
|
|
17
24
|
interface IDCardCaptureProps {
|
|
@@ -30,9 +37,10 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
30
37
|
const [currentSide, setCurrentSide] = useState<'front' | 'back'>('front');
|
|
31
38
|
const [bboxBySide, setBboxBySide] = useState<Record<string, IBbox>>({});
|
|
32
39
|
const [silentCaptureResult, setSilentCaptureResult] = useState<ISilentCaptureResult>({ success: false, isAnalyzing: false });
|
|
33
|
-
|
|
34
40
|
const [isProcessingCapture, setIsProcessingCapture] = useState(false);
|
|
35
41
|
const [processingImagePath, setProcessingImagePath] = useState<string | null>(null);
|
|
42
|
+
const [cameraKey, setCameraKey] = useState(0);
|
|
43
|
+
|
|
36
44
|
|
|
37
45
|
const documentTypeMapping: Record<string, GovernmentDocumentType> = {
|
|
38
46
|
'nationalId': 'national_id', 'passport': 'passport', 'driversLicense': 'drivers_licence',
|
|
@@ -46,6 +54,10 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
46
54
|
return "";
|
|
47
55
|
};
|
|
48
56
|
|
|
57
|
+
const refreshCamera = () => {
|
|
58
|
+
setCameraKey(prev => prev + 1);
|
|
59
|
+
};
|
|
60
|
+
|
|
49
61
|
const countrySelectionData = useMemo(() => {
|
|
50
62
|
const countrySelectionComponent = state.template.components.find(c => c.type === 'country_selection');
|
|
51
63
|
return countrySelectionComponent ? state.componentData[countrySelectionComponent.id] : null;
|
|
@@ -65,23 +77,26 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
65
77
|
if (JSON.stringify(value) !== JSON.stringify(capturedImages)) {
|
|
66
78
|
const updatedImages = value as Record<string, IIDCardPayload>;
|
|
67
79
|
setCapturedImages(updatedImages);
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
80
|
+
|
|
81
|
+
const currentImageData = updatedImages[currentSide];
|
|
82
|
+
if (currentImageData?.dir) {
|
|
83
|
+
setSilentCaptureResult(prev => ({
|
|
84
|
+
...prev,
|
|
85
|
+
path: currentImageData.dir,
|
|
86
|
+
success: true,
|
|
87
|
+
isAnalyzing: false,
|
|
88
|
+
mrz: currentImageData.mrz || '',
|
|
89
|
+
templatePath: currentImageData.templatePath || '',
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
76
92
|
}
|
|
77
93
|
}
|
|
78
|
-
}, [value]);
|
|
94
|
+
}, [value, currentSide]);
|
|
79
95
|
|
|
80
96
|
const cameraConfig = useMemo(() => {
|
|
81
97
|
const instructions = selectedDocumentType
|
|
82
98
|
? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).instructions.en : getDocumentTypeInfo(selectedDocumentType.type).instructions.fr)
|
|
83
99
|
: getLocalizedText(component.instructions as Record<string, LocalizedText>);
|
|
84
|
-
|
|
85
100
|
return {
|
|
86
101
|
cameraType: 'back' as const, flashMode: 'auto' as const,
|
|
87
102
|
overlay: { guideText: instructions, bbox: { xMin: 15, yMin: 20, xMax: 85, yMax: 70, borderColor: '#2DBD60', borderWidth: 3, cornerRadius: 8 } }
|
|
@@ -89,31 +104,55 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
89
104
|
}, [selectedDocumentType, locale, component.instructions]);
|
|
90
105
|
|
|
91
106
|
const retakePicture = (sideToRetake: 'front' | 'back') => {
|
|
92
|
-
// Completely wipe all processing states to prevent leakage
|
|
93
107
|
setIsProcessingCapture(false);
|
|
94
108
|
setProcessingImagePath(null);
|
|
95
109
|
setSilentCaptureResult({ path: '', success: false, isAnalyzing: false, error: '', templatePath: '', mrz: '', bbox: undefined });
|
|
96
|
-
|
|
97
|
-
setShowCamera(true);
|
|
110
|
+
setShowCamera(true);
|
|
98
111
|
actions.showCustomStepper(false);
|
|
99
|
-
|
|
100
112
|
setCapturedImages((prev) => { const newState = { ...prev }; delete newState[sideToRetake]; return newState; });
|
|
101
113
|
if (value) { const newValue = { ...value }; delete newValue[sideToRetake]; onValueChange(newValue); }
|
|
102
114
|
};
|
|
103
115
|
|
|
104
|
-
const getCurrentSideVerification = (currentSide: string) => {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
116
|
+
const getCurrentSideVerification = (currentSide: string, countryKey: string) => {
|
|
117
|
+
const rawDocType = countrySelectionData?.documentType;
|
|
118
|
+
|
|
119
|
+
if (!rawDocType || !countryKey) {
|
|
120
|
+
return { authMethod: [], mrzTypes: [], regionMapping: null, key: 'root' };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const rawCountryName = ISO_TO_COUNTRY_NAME[countryData?.code || ''] || countryData?.code || countryKey;
|
|
124
|
+
const baseMapping = (REGION_MAPPING as any).regionMapping || REGION_MAPPING;
|
|
125
|
+
|
|
126
|
+
let countryMapping = baseMapping[rawCountryName];
|
|
127
|
+
|
|
128
|
+
// Fallback search in case of case mismatches
|
|
129
|
+
if (!countryMapping) {
|
|
130
|
+
const foundKey = Object.keys(baseMapping).find(k => k.toLowerCase() === rawCountryName.toLowerCase() || k.toLowerCase() === countryKey.toLowerCase());
|
|
131
|
+
if (foundKey) countryMapping = baseMapping[foundKey];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (!countryMapping) {
|
|
135
|
+
return { authMethod: [], mrzTypes: [], regionMapping: null, key: 'root' };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const regionMapping = countryMapping[rawDocType];
|
|
139
|
+
if (!regionMapping) {
|
|
140
|
+
return { authMethod: [], mrzTypes: [], regionMapping: null, key: 'root' };
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const authMethod: string[] = [];
|
|
144
|
+
const mrzTypes: string[] = [];
|
|
145
|
+
const key = countrySelectionData.region?.trim()?.length > 0 ? countrySelectionData.region.trim() : 'root';
|
|
146
|
+
|
|
109
147
|
if (regionMapping?.[key] && Array.isArray(regionMapping[key])) {
|
|
110
148
|
regionMapping[key].forEach((item: any) => {
|
|
111
|
-
if (item[currentSide
|
|
112
|
-
authMethod.push(item[currentSide
|
|
149
|
+
if (item[currentSide]) {
|
|
150
|
+
authMethod.push(item[currentSide]);
|
|
113
151
|
if (item?.mrz_type) mrzTypes.push(item?.mrz_type);
|
|
114
152
|
}
|
|
115
153
|
});
|
|
116
154
|
}
|
|
155
|
+
|
|
117
156
|
return { authMethod: removeDuplicates(authMethod), mrzTypes: removeDuplicates(mrzTypes), regionMapping, key };
|
|
118
157
|
}
|
|
119
158
|
|
|
@@ -121,7 +160,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
121
160
|
if (!mapping || !mapping[selectedDocumentType]) return null;
|
|
122
161
|
const fileName = templatePath.split("/").pop()?.replace(".jpg", "").replace(".png", "");
|
|
123
162
|
if (!fileName) return null;
|
|
124
|
-
const pyName = `${fileName}.py`;
|
|
163
|
+
const pyName = `${fileName}.py`;
|
|
125
164
|
const found = mapping[selectedDocumentType].find((item: any) => item.py_file === pyName);
|
|
126
165
|
return found?.mrz_type || null;
|
|
127
166
|
}
|
|
@@ -137,10 +176,8 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
137
176
|
|
|
138
177
|
const autoCapture = async (capturePath: string, verified: ISilentCaptureResult) => {
|
|
139
178
|
if (isProcessingCapture) return;
|
|
140
|
-
|
|
141
|
-
setIsProcessingCapture(true);
|
|
179
|
+
setIsProcessingCapture(true);
|
|
142
180
|
setProcessingImagePath(capturePath);
|
|
143
|
-
|
|
144
181
|
try {
|
|
145
182
|
let imagePathForUpload = capturePath;
|
|
146
183
|
if (verified.bbox) {
|
|
@@ -150,13 +187,11 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
150
187
|
imagePathForUpload = capturePath;
|
|
151
188
|
}
|
|
152
189
|
}
|
|
153
|
-
|
|
154
190
|
const base64 = await pathToBase64(imagePathForUpload);
|
|
155
191
|
const newImages = {
|
|
156
192
|
...capturedImages,
|
|
157
193
|
[currentSide]: { dir: imagePathForUpload, file: base64, mrz: verified.mrz || "", templatePath: verified.templatePath },
|
|
158
194
|
};
|
|
159
|
-
|
|
160
195
|
setCapturedImages(newImages);
|
|
161
196
|
if (verified.country && verified.documentType) {
|
|
162
197
|
onValueChange({
|
|
@@ -165,15 +200,13 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
165
200
|
documentType: GovernmentDocumentTypeBackend[verified.documentType as keyof typeof GovernmentDocumentTypeBackend] || '',
|
|
166
201
|
});
|
|
167
202
|
}
|
|
168
|
-
|
|
169
203
|
setTimeout(() => {
|
|
170
204
|
setShowCamera(false);
|
|
171
205
|
actions.showCustomStepper(true);
|
|
172
206
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false }));
|
|
173
207
|
setIsProcessingCapture(false);
|
|
174
|
-
setProcessingImagePath(null);
|
|
208
|
+
setProcessingImagePath(null);
|
|
175
209
|
}, 600);
|
|
176
|
-
|
|
177
210
|
} catch (e: any) {
|
|
178
211
|
showAlert('Error', e?.message || 'Impossible de capturer la photo');
|
|
179
212
|
setSilentCaptureResult(prev => ({ ...prev, isAnalyzing: false, success: false }));
|
|
@@ -184,25 +217,23 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
184
217
|
|
|
185
218
|
const handleSilentCapture = async (result: { success: boolean; path?: string; error?: string }) => {
|
|
186
219
|
if (silentCaptureResult.isAnalyzing || isProcessingCapture) return;
|
|
187
|
-
|
|
188
220
|
if (result.success && result.path) {
|
|
189
221
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: true, success: false, error: '' }));
|
|
190
|
-
|
|
222
|
+
|
|
223
|
+
// 🚨 Force a template fetch if we haven't successfully saved the current side yet
|
|
224
|
+
let templatePath = capturedImages[currentSide]?.templatePath || '';
|
|
191
225
|
let templateBbox: IBbox | undefined;
|
|
192
226
|
let templateResponse: any;
|
|
193
|
-
|
|
227
|
+
|
|
194
228
|
if (!selectedDocumentType) {
|
|
195
229
|
setSilentCaptureResult((prev) => ({ ...prev, isAnalyzing: false, success: false, error: 'Document type not selected' })); return;
|
|
196
230
|
}
|
|
197
231
|
|
|
198
|
-
const regionMappings = getCurrentSideVerification(currentSide);
|
|
199
|
-
|
|
200
232
|
try {
|
|
201
|
-
if (templatePath
|
|
233
|
+
if (!templatePath) {
|
|
202
234
|
const templateType = await checkTemplateType({ path: result.path || '', docType: selectedDocumentType?.type as GovernmentDocumentType, docRegion: countryData?.code || "", postfix: currentSide }, env);
|
|
203
235
|
templateResponse = templateType;
|
|
204
236
|
if (templateType.template_path) templatePath = templateType.template_path;
|
|
205
|
-
|
|
206
237
|
if (templateType.card_obb) {
|
|
207
238
|
const obbConfidence = getObbConfidence((templateType as any).card_obb);
|
|
208
239
|
if (obbConfidence !== null && obbConfidence < OBB_CONFIDENCE_THRESHOLD) {
|
|
@@ -216,26 +247,46 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
216
247
|
}
|
|
217
248
|
}
|
|
218
249
|
|
|
250
|
+
const extractedCountryKey = templatePath ? templatePath.split('/')[0] : (ISO_TO_COUNTRY_NAME[countryData?.code || ''] || 'root');
|
|
251
|
+
const regionMappings = getCurrentSideVerification(currentSide, extractedCountryKey);
|
|
252
|
+
|
|
219
253
|
let verificationRes: any;
|
|
220
254
|
if (currentSide === 'front') {
|
|
221
255
|
const matchedAuthMethod = getCorrespondingAuthMethod(templatePath, regionMappings.regionMapping, regionMappings.key || '', 'front');
|
|
222
|
-
const mrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || '';
|
|
256
|
+
const mrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || 'TD1';
|
|
223
257
|
verificationRes = await frontVerification({ path: result.path, regionMapping: { authMethod: matchedAuthMethod ? [matchedAuthMethod] : regionMappings.authMethod, mrzTypes: regionMappings.mrzTypes }, selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType?.type as keyof typeof GovernmentDocumentTypeShorted] || '', code: countryData?.code || '', currentSide, templatePath, mrzType }, env);
|
|
224
258
|
} else {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
259
|
+
let matchedBackAuthMethod = getCorrespondingAuthMethod(templatePath, regionMappings.regionMapping, regionMappings.key || '', 'back');
|
|
260
|
+
|
|
261
|
+
if (!matchedBackAuthMethod && currentSide === 'back') {
|
|
262
|
+
matchedBackAuthMethod = 'MRZ';
|
|
263
|
+
}
|
|
229
264
|
|
|
265
|
+
const backMrzType = getCorrespondingMrzType(templatePath, regionMappings.regionMapping, regionMappings.key || '') || 'TD1';
|
|
266
|
+
|
|
267
|
+
verificationRes = await backVerification({
|
|
268
|
+
path: result.path,
|
|
269
|
+
regionMapping: {
|
|
270
|
+
authMethod: matchedBackAuthMethod ? [matchedBackAuthMethod] : regionMappings.authMethod,
|
|
271
|
+
mrzTypes: regionMappings.mrzTypes
|
|
272
|
+
},
|
|
273
|
+
selectedDocumentType: GovernmentDocumentTypeShorted[selectedDocumentType.type as keyof typeof GovernmentDocumentTypeShorted] || '',
|
|
274
|
+
code: countryData?.code || '',
|
|
275
|
+
currentSide,
|
|
276
|
+
templatePath,
|
|
277
|
+
mrzType: backMrzType,
|
|
278
|
+
templateResponse
|
|
279
|
+
}, env);
|
|
280
|
+
}
|
|
281
|
+
|
|
230
282
|
const bbox = verificationRes?.bbox || templateBbox;
|
|
231
283
|
const mrz = verificationRes?.mrz ? JSON.stringify(verificationRes.mrz) : "";
|
|
232
|
-
|
|
233
284
|
const verifiedResult: ISilentCaptureResult = { path: result.path, templatePath, bbox, success: true, mrz, isAnalyzing: false, country: countryData?.code, documentType: selectedDocumentType.type };
|
|
285
|
+
|
|
234
286
|
setSilentCaptureResult(verifiedResult);
|
|
235
287
|
if (bbox) setBboxBySide(prev => ({ ...prev, [currentSide]: bbox }));
|
|
236
|
-
|
|
237
288
|
await autoCapture(result.path, verifiedResult);
|
|
238
|
-
|
|
289
|
+
|
|
239
290
|
} catch (error: any) {
|
|
240
291
|
const isCardNotFullyInFrame = error?.message === 'CARD_NOT_FULLY_IN_FRAME' || error?.message?.includes('entirement');
|
|
241
292
|
const errorMessage = isCardNotFullyInFrame ? t('kyc.idCardCapture.cardNotFullyInFrame') : (error?.message || 'Erreur de détection');
|
|
@@ -253,7 +304,7 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
253
304
|
if (!countrySelectionData || !selectedDocumentType) {
|
|
254
305
|
return (
|
|
255
306
|
<View style={styles.root}>
|
|
256
|
-
<View style={styles.
|
|
307
|
+
<View style={styles.previewContainer}>
|
|
257
308
|
<Text style={styles.title}>{getLocalizedText(component.labels)}</Text>
|
|
258
309
|
<Text style={styles.description}>
|
|
259
310
|
{state.currentLanguage === "en" ? "Please complete the country and document selection first." : "Veuillez d'abord compléter la sélection du pays et du document."}
|
|
@@ -265,84 +316,100 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
265
316
|
|
|
266
317
|
// --- CAMERA RENDER ---
|
|
267
318
|
if (showCamera) {
|
|
268
|
-
const isBusy = isProcessingCapture;
|
|
269
|
-
|
|
319
|
+
const isBusy = isProcessingCapture;
|
|
270
320
|
return (
|
|
271
|
-
<View style={styles.
|
|
272
|
-
<
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
<View style={styles.topAnalyzingPill}>
|
|
286
|
-
<ActivityIndicator size="small" color="white" />
|
|
287
|
-
<Text style={styles.analyzingPillText}>
|
|
288
|
-
{state.currentLanguage === 'en' ? 'Scanning...' : 'Analyse...'}
|
|
289
|
-
</Text>
|
|
290
|
-
</View>
|
|
291
|
-
</View>
|
|
292
|
-
)}
|
|
321
|
+
<View style={styles.root}>
|
|
322
|
+
<View style={styles.cameraWrapper}>
|
|
323
|
+
|
|
324
|
+
{/* Web/Desktop Clean Header */}
|
|
325
|
+
<View style={styles.headerContainer}>
|
|
326
|
+
<Text style={styles.headerTitle}>
|
|
327
|
+
{selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : ''}
|
|
328
|
+
</Text>
|
|
329
|
+
<View style={styles.stepBadge}>
|
|
330
|
+
<Text style={styles.stepText}>
|
|
331
|
+
{t('kyc.idCardCapture.captureTitle', { side: currentSide === 'front' ? locale === 'en' ? 'Front' : 'Recto' : locale === 'en' ? 'Back' : 'Verso' })}
|
|
332
|
+
</Text>
|
|
333
|
+
</View>
|
|
334
|
+
</View>
|
|
293
335
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
336
|
+
<View style={styles.cameraFeedContainer}>
|
|
337
|
+
<EnhancedCameraView
|
|
338
|
+
key={`${currentSide}-${cameraKey}`}
|
|
339
|
+
showCamera={true}
|
|
340
|
+
isProcessing={isBusy}
|
|
341
|
+
cameraType={cameraConfig.cameraType}
|
|
342
|
+
style={styles.camera}
|
|
343
|
+
onError={handleError}
|
|
344
|
+
onSilentCapture={handleSilentCapture}
|
|
345
|
+
silentCaptureResult={silentCaptureResult}
|
|
346
|
+
overlayComponent={
|
|
347
|
+
<>
|
|
348
|
+
{!isBusy && silentCaptureResult.isAnalyzing && (
|
|
349
|
+
<View style={styles.topAnalyzingPillContainer}>
|
|
350
|
+
<View style={styles.topAnalyzingPill}>
|
|
351
|
+
<ActivityIndicator size="small" color="white" />
|
|
352
|
+
<Text style={styles.analyzingPillText}>
|
|
353
|
+
{state.currentLanguage === 'en' ? 'Scanning...' : 'Analyse...'}
|
|
354
|
+
</Text>
|
|
355
|
+
</View>
|
|
356
|
+
</View>
|
|
302
357
|
)}
|
|
303
|
-
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
358
|
+
{isBusy && (
|
|
359
|
+
<View style={StyleSheet.absoluteFillObject}>
|
|
360
|
+
{processingImagePath && (
|
|
361
|
+
<Image
|
|
362
|
+
source={{ uri: processingImagePath.startsWith('file://') ? processingImagePath : `file://${processingImagePath}` }}
|
|
363
|
+
style={StyleSheet.absoluteFillObject}
|
|
364
|
+
resizeMode="cover"
|
|
365
|
+
/>
|
|
366
|
+
)}
|
|
367
|
+
<View style={styles.processingOverlay}>
|
|
368
|
+
<ActivityIndicator size="large" color="#2DBD60" />
|
|
369
|
+
<Text style={styles.processingText}>
|
|
370
|
+
{state.currentLanguage === 'en' ? 'Perfect!\nProcessing Document...' : 'Parfait!\nTraitement du document...'}
|
|
371
|
+
</Text>
|
|
372
|
+
</View>
|
|
373
|
+
</View>
|
|
374
|
+
)}
|
|
375
|
+
<IdCardOverlay
|
|
376
|
+
xMin={cameraConfig.overlay.bbox.xMin} yMin={cameraConfig.overlay.bbox.yMin} xMax={cameraConfig.overlay.bbox.xMax} yMax={cameraConfig.overlay.bbox.yMax}
|
|
377
|
+
instructions={cameraConfig.overlay.guideText}
|
|
378
|
+
cornerOpacity={cameraConfig.overlay.bbox.cornerRadius || 0 as number}
|
|
379
|
+
isSuccess={silentCaptureResult.success}
|
|
380
|
+
language={state.currentLanguage}
|
|
381
|
+
stepperProps={{
|
|
382
|
+
back: () => {
|
|
383
|
+
if (currentSide === 'back') {
|
|
384
|
+
setCurrentSide('front');
|
|
385
|
+
setShowCamera(false);
|
|
386
|
+
setIsProcessingCapture(false);
|
|
387
|
+
setProcessingImagePath(null);
|
|
388
|
+
if (capturedImages['front']?.dir) {
|
|
389
|
+
const frontImage = capturedImages['front'];
|
|
390
|
+
setSilentCaptureResult((prev) => ({ ...prev, path: frontImage.dir, success: true, isAnalyzing: false, error: '', mrz: frontImage.mrz || '', templatePath: frontImage.templatePath || '', bbox: bboxBySide['front'] }));
|
|
391
|
+
} else { setSilentCaptureResult((prev) => ({ ...prev, path: '', success: false, isAnalyzing: false, error: '', templatePath: '' })); }
|
|
392
|
+
} else { actions.previousComponent(); }
|
|
393
|
+
},
|
|
394
|
+
selectedDocumentType: selectedDocumentType ? (locale === 'en' ? getDocumentTypeInfo(selectedDocumentType.type).name.en : getDocumentTypeInfo(selectedDocumentType.type).name.fr) : '',
|
|
395
|
+
step: state.currentComponentIndex + 1, totalSteps: state.template.components.length, side: currentSide,
|
|
396
|
+
}}
|
|
397
|
+
/>
|
|
398
|
+
|
|
399
|
+
<TouchableOpacity style={styles.refreshButton} onPress={refreshCamera}>
|
|
400
|
+
<Text style={styles.refreshButtonText}>Refresh Camera</Text>
|
|
401
|
+
</TouchableOpacity>
|
|
402
|
+
</>
|
|
403
|
+
}
|
|
404
|
+
/>
|
|
405
|
+
{/* Elegant Floating Error Banner below the cutout */}
|
|
406
|
+
{silentCaptureResult.error && !isBusy ? (
|
|
407
|
+
<View style={styles.floatingErrorBanner}>
|
|
408
|
+
<Text style={styles.floatingErrorText}>⚠️ {silentCaptureResult.error}</Text>
|
|
409
|
+
</View>
|
|
410
|
+
) : null}
|
|
344
411
|
</View>
|
|
345
|
-
|
|
412
|
+
</View>
|
|
346
413
|
</View>
|
|
347
414
|
);
|
|
348
415
|
}
|
|
@@ -359,7 +426,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
359
426
|
{getLocalizedText(component.instructions)}
|
|
360
427
|
</Text>
|
|
361
428
|
<View style={{ alignItems: 'center', justifyContent: 'center', flexDirection: "column", gap: 16 }}>
|
|
362
|
-
|
|
363
429
|
{silentCaptureResult?.error === 'TOO_FAR_AWAY' && (
|
|
364
430
|
<View style={styles.warningBanner}>
|
|
365
431
|
<Text style={styles.warningText}>
|
|
@@ -367,7 +433,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
367
433
|
</Text>
|
|
368
434
|
</View>
|
|
369
435
|
)}
|
|
370
|
-
|
|
371
436
|
<View style={styles.imagePreviewWrapper}>
|
|
372
437
|
{capturedImages[currentSide]?.dir ? (
|
|
373
438
|
<Image source={{ uri: capturedImages[currentSide].dir }} style={styles.previewImage} />
|
|
@@ -375,7 +440,6 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
375
440
|
<Image source={{ uri: silentCaptureResult.path }} style={styles.previewImage} />
|
|
376
441
|
) : null}
|
|
377
442
|
</View>
|
|
378
|
-
|
|
379
443
|
{!capturedImages[currentSide]?.dir && (
|
|
380
444
|
<Button
|
|
381
445
|
title={state.currentLanguage === "en" ? "Start Scanning" : "Commencer la numérisation"}
|
|
@@ -383,22 +447,19 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
383
447
|
variant="primary" size="large" fullWidth
|
|
384
448
|
/>
|
|
385
449
|
)}
|
|
386
|
-
|
|
387
450
|
{capturedImages[currentSide]?.dir && (
|
|
388
451
|
<>
|
|
389
452
|
<Button title={t('kyc.idCardCapture.retakeButton')} onPress={() => retakePicture(currentSide)} variant="outline" size="medium" fullWidth />
|
|
390
453
|
<Button title={t('common.next')} onPress={() => {
|
|
391
454
|
if (!selectedDocumentType) { showAlert('Error', 'Document type not selected'); return; }
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
setIsProcessingCapture(false);
|
|
401
|
-
setProcessingImagePath(null);
|
|
455
|
+
if (currentSide === 'back' || selectedDocumentType.type === 'passport') {
|
|
456
|
+
actions.nextComponent();
|
|
457
|
+
} else {
|
|
458
|
+
setShowCamera(true);
|
|
459
|
+
setCurrentSide('back');
|
|
460
|
+
setSilentCaptureResult({ success: false, isAnalyzing: false, path: '', error: '', templatePath: undefined });
|
|
461
|
+
setIsProcessingCapture(false);
|
|
462
|
+
setProcessingImagePath(null);
|
|
402
463
|
}
|
|
403
464
|
}} variant="primary" size="large" fullWidth />
|
|
404
465
|
</>
|
|
@@ -412,28 +473,132 @@ export const IDCardCapture: React.FC<IDCardCaptureProps> = ({ component, value =
|
|
|
412
473
|
};
|
|
413
474
|
|
|
414
475
|
const styles = StyleSheet.create({
|
|
415
|
-
root: {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
476
|
+
root: {
|
|
477
|
+
flex: 1,
|
|
478
|
+
width: '100%',
|
|
479
|
+
backgroundColor: 'transparent',
|
|
480
|
+
alignSelf: 'center',
|
|
481
|
+
...(Platform.OS === 'web'
|
|
482
|
+
? ({
|
|
483
|
+
minHeight: '85vh',
|
|
484
|
+
justifyContent: 'center',
|
|
485
|
+
alignItems: 'center',
|
|
486
|
+
// Note: backdropFilter is valid in React Native Web but TS might complain, cast safely
|
|
487
|
+
backdropFilter: 'blur(8px)'
|
|
488
|
+
} as any)
|
|
489
|
+
: {})
|
|
490
|
+
},
|
|
491
|
+
cameraWrapper: {
|
|
492
|
+
width: '100%',
|
|
493
|
+
backgroundColor: '#FFFFFF',
|
|
494
|
+
overflow: 'hidden',
|
|
495
|
+
...(Platform.OS === 'web'
|
|
496
|
+
? ({
|
|
497
|
+
maxWidth: 500,
|
|
498
|
+
height: 700,
|
|
499
|
+
maxHeight: '90vh', // TypeScript will now ignore this thanks to the cast below
|
|
500
|
+
borderRadius: 24,
|
|
501
|
+
shadowColor: '#000',
|
|
502
|
+
shadowOffset: { width: 0, height: 20 },
|
|
503
|
+
shadowOpacity: 0.25,
|
|
504
|
+
shadowRadius: 35,
|
|
505
|
+
elevation: 24,
|
|
506
|
+
} as any) // 🚨 CAST TO ANY
|
|
507
|
+
: {
|
|
508
|
+
flex: 1,
|
|
509
|
+
})
|
|
510
|
+
},
|
|
511
|
+
headerContainer: {
|
|
512
|
+
flexDirection: 'row',
|
|
513
|
+
alignItems: 'center',
|
|
514
|
+
justifyContent: 'space-between',
|
|
515
|
+
paddingHorizontal: 24,
|
|
516
|
+
paddingVertical: 18,
|
|
517
|
+
backgroundColor: '#FFFFFF',
|
|
518
|
+
borderBottomWidth: 1,
|
|
519
|
+
borderBottomColor: '#F1F5F9',
|
|
520
|
+
zIndex: 10,
|
|
521
|
+
// Mobile hidden, Web visible to replace floating text
|
|
522
|
+
...(Platform.OS !== 'web' ? { display: 'none' } : {})
|
|
523
|
+
},
|
|
524
|
+
headerTitle: {
|
|
525
|
+
fontSize: 18,
|
|
526
|
+
fontWeight: '700',
|
|
527
|
+
color: '#0F172A',
|
|
528
|
+
},
|
|
529
|
+
stepBadge: {
|
|
530
|
+
backgroundColor: '#F1F5F9',
|
|
531
|
+
paddingHorizontal: 12,
|
|
532
|
+
paddingVertical: 6,
|
|
533
|
+
borderRadius: 20,
|
|
534
|
+
},
|
|
535
|
+
stepText: {
|
|
536
|
+
fontSize: 13,
|
|
537
|
+
fontWeight: '600',
|
|
538
|
+
color: '#64748B',
|
|
539
|
+
},
|
|
540
|
+
cameraFeedContainer: {
|
|
541
|
+
flex: 1,
|
|
542
|
+
position: 'relative',
|
|
543
|
+
backgroundColor: '#000',
|
|
544
|
+
},
|
|
436
545
|
camera: {
|
|
437
546
|
flex: 1,
|
|
438
547
|
},
|
|
548
|
+
previewContainer: {
|
|
549
|
+
width: '100%',
|
|
550
|
+
backgroundColor: 'white',
|
|
551
|
+
borderRadius: 12,
|
|
552
|
+
paddingVertical: 24,
|
|
553
|
+
paddingHorizontal: 20,
|
|
554
|
+
shadowColor: '#000',
|
|
555
|
+
shadowOffset: { width: 0, height: 4 },
|
|
556
|
+
shadowOpacity: 0.1,
|
|
557
|
+
shadowRadius: 12,
|
|
558
|
+
elevation: 8,
|
|
559
|
+
...(Platform.OS === 'web' ? { alignSelf: 'center', maxWidth: 600 } : { margin: 10, width: '95%' })
|
|
560
|
+
},
|
|
561
|
+
previewItemContainer: {
|
|
562
|
+
flexGrow: 1,
|
|
563
|
+
},
|
|
564
|
+
title: { fontSize: 24, fontWeight: 'bold', color: '#333', marginBottom: 8, textAlign: 'center' },
|
|
565
|
+
description: { fontSize: 16, color: '#666', textAlign: 'center', marginBottom: 24, lineHeight: 22 },
|
|
566
|
+
sideContainer: { marginBottom: 24 },
|
|
567
|
+
sideTitle: { fontSize: 25, fontWeight: 'bold', color: '#000', marginBottom: 12, textAlign: 'center' },
|
|
568
|
+
imagePreviewWrapper: {
|
|
569
|
+
width: '100%', height: 220, borderRadius: 12, padding: 1, overflow: 'hidden',
|
|
570
|
+
shadowColor: '#000', shadowOffset: { width: 0, height: 4 }, shadowOpacity: 0.18, shadowRadius: 8, elevation: 8, backgroundColor: '#f0f0f0'
|
|
571
|
+
},
|
|
572
|
+
previewImage: { width: '100%', height: '100%', borderRadius: 12, resizeMode: 'cover' },
|
|
573
|
+
floatingErrorBanner: {
|
|
574
|
+
position: 'absolute',
|
|
575
|
+
bottom: 30, // Pushed to the bottom for professional feel
|
|
576
|
+
left: 24,
|
|
577
|
+
right: 24,
|
|
578
|
+
backgroundColor: '#FEF2F2',
|
|
579
|
+
borderWidth: 1,
|
|
580
|
+
borderColor: '#FCA5A5',
|
|
581
|
+
paddingVertical: 12,
|
|
582
|
+
paddingHorizontal: 16,
|
|
583
|
+
borderRadius: 12,
|
|
584
|
+
alignItems: 'center',
|
|
585
|
+
justifyContent: 'center',
|
|
586
|
+
shadowColor: '#DC2626',
|
|
587
|
+
shadowOffset: { width: 0, height: 4 },
|
|
588
|
+
shadowOpacity: 0.1,
|
|
589
|
+
shadowRadius: 8,
|
|
590
|
+
elevation: 8,
|
|
591
|
+
zIndex: 100
|
|
592
|
+
},
|
|
593
|
+
floatingErrorText: { color: '#991B1B', fontSize: 14, fontWeight: '700', textAlign: 'center' },
|
|
594
|
+
processingOverlay: { flex: 1, backgroundColor: 'rgba(0, 0, 0, 0.6)', justifyContent: 'center', alignItems: 'center', zIndex: 9999 },
|
|
595
|
+
processingText: { color: '#FFF', fontSize: 18, fontWeight: 'bold', marginTop: 16, textAlign: 'center' },
|
|
596
|
+
warningBanner: { backgroundColor: '#FF9500', padding: 12, borderRadius: 8, width: '100%', shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.2, shadowRadius: 4, elevation: 4 },
|
|
597
|
+
warningText: { color: 'white', fontWeight: 'bold', textAlign: 'center', fontSize: 16 },
|
|
598
|
+
errorText: { color: '#dc2626', fontSize: 14, marginTop: 8, textAlign: 'center' },
|
|
599
|
+
topAnalyzingPillContainer: { position: 'absolute', top: Platform.OS === 'android' ? 60 : 50, left: 0, right: 0, alignItems: 'center', zIndex: 100 },
|
|
600
|
+
topAnalyzingPill: { flexDirection: 'row', alignItems: 'center', backgroundColor: 'rgba(0,0,0,0.6)', paddingVertical: 8, paddingHorizontal: 16, borderRadius: 20, gap: 8 },
|
|
601
|
+
analyzingPillText: { color: 'white', fontSize: 14, fontWeight: 'bold' },
|
|
602
|
+
refreshButton: { position: 'absolute', bottom: 100, alignSelf: 'center', backgroundColor: 'rgba(0,0,0,0.5)', padding: 10, borderRadius: 20, zIndex: 500 },
|
|
603
|
+
refreshButtonText: { color: 'white', fontWeight: 'bold' },
|
|
439
604
|
});
|