@transfergratis/react-native-sdk 0.1.23 → 0.1.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build/intermediates/aapt_friendly_merged_manifests/debug/processDebugManifest/aapt/AndroidManifest.xml +12 -5
- package/android/build/intermediates/aar_main_jar/debug/syncDebugLibJars/classes.jar +0 -0
- package/android/build/intermediates/annotations_typedef_file/debug/extractDebugAnnotations/typedefs.txt +0 -0
- package/android/build/intermediates/incremental/debug/packageDebugResources/compile-file-map.properties +1 -1
- package/android/build/intermediates/incremental/debug-mergeJavaRes/merge-state +0 -0
- package/android/build/intermediates/manifest_merge_blame_file/debug/processDebugManifest/manifest-merger-blame-debug-report.txt +61 -59
- package/android/build/intermediates/merged_java_res/debug/mergeDebugJavaResource/feature-transfergratis-react-native-sdk.jar +0 -0
- package/android/build/intermediates/merged_manifest/debug/processDebugManifest/AndroidManifest.xml +12 -5
- package/android/build/kotlin/compileDebugKotlin/cacheable/last-build.bin +0 -0
- package/android/build/kotlin/compileDebugKotlin/local-state/build-history.bin +0 -0
- package/android/build/outputs/aar/transfergratis-react-native-sdk-debug.aar +0 -0
- package/android/build/outputs/logs/manifest-merger-debug-report.txt +26 -34
- package/android/src/main/AndroidManifest.xml +13 -5
- package/build/components/EnhancedCameraView.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.js +26 -3
- package/build/components/EnhancedCameraView.js.map +1 -1
- package/build/components/EnhancedCameraView.web.d.ts.map +1 -1
- package/build/components/EnhancedCameraView.web.js +21 -0
- package/build/components/EnhancedCameraView.web.js.map +1 -1
- package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts +12 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js +283 -0
- package/build/components/KYCElements/AdditionalDocumentsTemplate.js.map +1 -0
- package/build/components/KYCElements/CameraCapture.d.ts.map +1 -1
- package/build/components/KYCElements/CameraCapture.js +4 -3
- package/build/components/KYCElements/CameraCapture.js.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts +5 -2
- package/build/components/KYCElements/CountrySelectionTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/CountrySelectionTemplate.js +360 -101
- package/build/components/KYCElements/CountrySelectionTemplate.js.map +1 -1
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/EmailVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js +193 -0
- package/build/components/KYCElements/EmailVerificationTemplate.js.map +1 -0
- package/build/components/KYCElements/FileUpload.d.ts.map +1 -1
- package/build/components/KYCElements/FileUpload.js +5 -4
- package/build/components/KYCElements/FileUpload.js.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/FileUploadTemplate.js +5 -4
- package/build/components/KYCElements/FileUploadTemplate.js.map +1 -1
- package/build/components/KYCElements/IDCardCapture.d.ts.map +1 -1
- package/build/components/KYCElements/IDCardCapture.js +356 -227
- package/build/components/KYCElements/IDCardCapture.js.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/LocationCaptureTemplate.js +78 -37
- package/build/components/KYCElements/LocationCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCapture.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCapture.js +5 -4
- package/build/components/KYCElements/OrientationVideoCapture.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureEnhanced.js.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts +2 -0
- package/build/components/KYCElements/OrientationVideoCaptureFinal.d.ts.map +1 -1
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js +5 -4
- package/build/components/KYCElements/OrientationVideoCaptureFinal.js.map +1 -1
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PersonalInformationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js +120 -0
- package/build/components/KYCElements/PersonalInformationTemplate.js.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts +12 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js +185 -0
- package/build/components/KYCElements/PhoneVerificationTemplate.js.map +1 -0
- package/build/components/KYCElements/SelfieCapture.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCapture.js +4 -3
- package/build/components/KYCElements/SelfieCapture.js.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.d.ts.map +1 -1
- package/build/components/KYCElements/SelfieCaptureTemplate.js +189 -42
- package/build/components/KYCElements/SelfieCaptureTemplate.js.map +1 -1
- package/build/components/KYCElements/WelcomeTemplate.d.ts +12 -0
- package/build/components/KYCElements/WelcomeTemplate.d.ts.map +1 -0
- package/build/components/KYCElements/WelcomeTemplate.js +243 -0
- package/build/components/KYCElements/WelcomeTemplate.js.map +1 -0
- package/build/components/TemplateKYCExample.d.ts +8 -2
- package/build/components/TemplateKYCExample.d.ts.map +1 -1
- package/build/components/TemplateKYCExample.js +10 -97
- package/build/components/TemplateKYCExample.js.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts +6 -1
- package/build/components/TemplateKYCFlowRefactored.d.ts.map +1 -1
- package/build/components/TemplateKYCFlowRefactored.js +108 -11
- package/build/components/TemplateKYCFlowRefactored.js.map +1 -1
- package/build/components/example/DynamicTemplateExample.d.ts +10 -0
- package/build/components/example/DynamicTemplateExample.d.ts.map +1 -0
- package/build/components/example/DynamicTemplateExample.js +241 -0
- package/build/components/example/DynamicTemplateExample.js.map +1 -0
- package/build/config/KYCConfig.d.ts +14 -0
- package/build/config/KYCConfig.d.ts.map +1 -0
- package/build/config/KYCConfig.js +26 -0
- package/build/config/KYCConfig.js.map +1 -0
- package/build/config/allowedDomains.d.ts +30 -0
- package/build/config/allowedDomains.d.ts.map +1 -0
- package/build/config/allowedDomains.js +112 -0
- package/build/config/allowedDomains.js.map +1 -0
- package/build/hooks/useOrientationVideo.d.ts +2 -1
- package/build/hooks/useOrientationVideo.d.ts.map +1 -1
- package/build/hooks/useOrientationVideo.js +3 -3
- package/build/hooks/useOrientationVideo.js.map +1 -1
- package/build/hooks/useTemplateKYCFlow.d.ts +6 -1
- package/build/hooks/useTemplateKYCFlow.d.ts.map +1 -1
- package/build/hooks/useTemplateKYCFlow.js +317 -34
- package/build/hooks/useTemplateKYCFlow.js.map +1 -1
- package/build/hooks/useTemplateLoader.d.ts +14 -0
- package/build/hooks/useTemplateLoader.d.ts.map +1 -0
- package/build/hooks/useTemplateLoader.js +85 -0
- package/build/hooks/useTemplateLoader.js.map +1 -0
- package/build/i18n/en/index.d.ts +49 -0
- package/build/i18n/en/index.d.ts.map +1 -1
- package/build/i18n/en/index.js +50 -1
- package/build/i18n/en/index.js.map +1 -1
- package/build/i18n/fr/index.d.ts +35 -0
- package/build/i18n/fr/index.d.ts.map +1 -1
- package/build/i18n/fr/index.js +36 -1
- package/build/i18n/fr/index.js.map +1 -1
- package/build/index.d.ts +6 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +10 -0
- package/build/index.js.map +1 -1
- package/build/modules/api/CardAuthentification.d.ts +24 -3
- package/build/modules/api/CardAuthentification.d.ts.map +1 -1
- package/build/modules/api/CardAuthentification.js +69 -10
- package/build/modules/api/CardAuthentification.js.map +1 -1
- package/build/modules/api/KYCService.d.ts +7 -7
- package/build/modules/api/KYCService.d.ts.map +1 -1
- package/build/modules/api/KYCService.js +108 -39
- package/build/modules/api/KYCService.js.map +1 -1
- package/build/modules/api/SelfieVerification.d.ts +3 -1
- package/build/modules/api/SelfieVerification.d.ts.map +1 -1
- package/build/modules/api/SelfieVerification.js +17 -1
- package/build/modules/api/SelfieVerification.js.map +1 -1
- package/build/modules/api/TemplateService.d.ts +44 -0
- package/build/modules/api/TemplateService.d.ts.map +1 -0
- package/build/modules/api/TemplateService.js +145 -0
- package/build/modules/api/TemplateService.js.map +1 -0
- package/build/types/KYC.types.d.ts +265 -4
- package/build/types/KYC.types.d.ts.map +1 -1
- package/build/types/KYC.types.js +15 -0
- package/build/types/KYC.types.js.map +1 -1
- package/build/types/env.types.d.ts +13 -0
- package/build/types/env.types.d.ts.map +1 -0
- package/build/types/env.types.js +2 -0
- package/build/types/env.types.js.map +1 -0
- package/build/utils/cropByObb.d.ts +1 -0
- package/build/utils/cropByObb.d.ts.map +1 -1
- package/build/utils/cropByObb.js +70 -0
- package/build/utils/cropByObb.js.map +1 -1
- package/build/utils/deviceDetection.d.ts +6 -0
- package/build/utils/deviceDetection.d.ts.map +1 -0
- package/build/utils/deviceDetection.js +12 -0
- package/build/utils/deviceDetection.js.map +1 -0
- package/build/utils/platformAlert.d.ts +20 -0
- package/build/utils/platformAlert.d.ts.map +1 -0
- package/build/utils/platformAlert.js +67 -0
- package/build/utils/platformAlert.js.map +1 -0
- package/build/utils/template-transformer.d.ts +10 -0
- package/build/utils/template-transformer.d.ts.map +1 -0
- package/build/utils/template-transformer.js +365 -0
- package/build/utils/template-transformer.js.map +1 -0
- package/build/web/WebKYCEntry.d.ts.map +1 -1
- package/build/web/WebKYCEntry.js +158 -32
- package/build/web/WebKYCEntry.js.map +1 -1
- package/package.json +1 -1
- package/plugin/build/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.js +3 -4
- package/plugin/src/withVisionCamera.ts +3 -4
- package/src/components/EnhancedCameraView.tsx +31 -2
- package/src/components/EnhancedCameraView.web.tsx +24 -0
- package/src/components/KYCElements/AdditionalDocumentsTemplate.tsx +346 -0
- package/src/components/KYCElements/CameraCapture.tsx +4 -3
- package/src/components/KYCElements/CountrySelectionTemplate.tsx +410 -113
- package/src/components/KYCElements/EmailVerificationTemplate.tsx +264 -0
- package/src/components/KYCElements/FileUpload.tsx +5 -4
- package/src/components/KYCElements/FileUploadTemplate.tsx +5 -4
- package/src/components/KYCElements/IDCardCapture.tsx +397 -254
- package/src/components/KYCElements/LocationCaptureTemplate.tsx +95 -44
- package/src/components/KYCElements/OrientationVideoCapture.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureEnhanced.tsx +6 -3
- package/src/components/KYCElements/OrientationVideoCaptureFinal.tsx +6 -3
- package/src/components/KYCElements/PersonalInformationTemplate.tsx +158 -0
- package/src/components/KYCElements/PhoneVerificationTemplate.tsx +253 -0
- package/src/components/KYCElements/SelfieCapture.tsx +4 -3
- package/src/components/KYCElements/SelfieCaptureTemplate.tsx +201 -44
- package/src/components/KYCElements/WelcomeTemplate.tsx +289 -0
- package/src/components/TemplateKYCExample.tsx +37 -108
- package/src/components/TemplateKYCFlowRefactored.tsx +148 -12
- package/src/components/example/DynamicTemplateExample.tsx +289 -0
- package/src/config/KYCConfig.ts +34 -0
- package/src/config/allowedDomains.ts +133 -0
- package/src/hooks/useOrientationVideo.ts +5 -4
- package/src/hooks/useTemplateKYCFlow.tsx +347 -32
- package/src/hooks/useTemplateLoader.ts +102 -0
- package/src/i18n/en/index.ts +53 -2
- package/src/i18n/fr/index.ts +37 -1
- package/src/index.ts +14 -0
- package/src/modules/api/CardAuthentification.ts +76 -11
- package/src/modules/api/KYCService.ts +129 -45
- package/src/modules/api/SelfieVerification.ts +25 -3
- package/src/modules/api/TemplateService.ts +167 -0
- package/src/types/KYC.types.ts +331 -3
- package/src/types/env.types.ts +13 -0
- package/src/utils/cropByObb.ts +83 -3
- package/src/utils/deviceDetection.ts +11 -0
- package/src/utils/platformAlert.ts +86 -0
- package/src/utils/template-transformer.ts +445 -0
- package/src/web/WebKYCEntry.tsx +199 -50
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { BackendKYCTemplate, TemplateAPIResponse } from '../../types/KYC.types';
|
|
3
|
+
import { logger } from '../../utils/logger';
|
|
4
|
+
import { errorMessage } from './KYCService';
|
|
5
|
+
import KYCConfig from '../../config/KYCConfig';
|
|
6
|
+
|
|
7
|
+
export class TemplateServiceClass {
|
|
8
|
+
private cache: Map<string, { template: BackendKYCTemplate; timestamp: number }> = new Map();
|
|
9
|
+
private cacheTimeout = 5 * 60 * 1000; // 5 minutes
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Get headers for API requests
|
|
13
|
+
*/
|
|
14
|
+
private getHeaders(apiKey?: string, token?: string) {
|
|
15
|
+
const headers: Record<string, string> = {
|
|
16
|
+
'Content-Type': 'application/json',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
if (apiKey) {
|
|
20
|
+
headers['Authorization'] = `ApiKey ${apiKey}`;
|
|
21
|
+
} else if (token) {
|
|
22
|
+
headers['Authorization'] = `Bearer ${token}`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return headers;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Fetch a template from the backend API
|
|
30
|
+
* @param templateId - The ID of the template to fetch
|
|
31
|
+
* @param apiKey - Optional API key for authentication
|
|
32
|
+
* @param token - Optional Bearer token for authentication
|
|
33
|
+
* @param useCache - Whether to use cached version if available (default: true)
|
|
34
|
+
* @returns The backend template
|
|
35
|
+
*/
|
|
36
|
+
async fetchTemplate(
|
|
37
|
+
templateId: string,
|
|
38
|
+
apiKey?: string,
|
|
39
|
+
token?: string,
|
|
40
|
+
useCache: boolean = true
|
|
41
|
+
): Promise<BackendKYCTemplate> {
|
|
42
|
+
// Check cache first
|
|
43
|
+
if (useCache) {
|
|
44
|
+
const cached = this.cache.get(templateId);
|
|
45
|
+
if (cached) {
|
|
46
|
+
const age = Date.now() - cached.timestamp;
|
|
47
|
+
if (age < this.cacheTimeout) {
|
|
48
|
+
logger.log(`Template ${templateId} loaded from cache`);
|
|
49
|
+
return cached.template;
|
|
50
|
+
} else {
|
|
51
|
+
// Cache expired, remove it
|
|
52
|
+
this.cache.delete(templateId);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const url = `${KYCConfig.getBackendUrl()}/templates/kyc/template/${templateId}/`;
|
|
59
|
+
logger.log(`Fetching template from: ${url} with apiKey: ${apiKey} and token: ${token}`, {
|
|
60
|
+
headers: this.getHeaders(apiKey, token)
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const response = await axios.get<TemplateAPIResponse>(url, {
|
|
64
|
+
headers: this.getHeaders(apiKey, token),
|
|
65
|
+
timeout: 30000, // 30 seconds timeout
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const template: BackendKYCTemplate = response.data;
|
|
69
|
+
|
|
70
|
+
// Validate template structure
|
|
71
|
+
if (!template.id || !template.name || !Array.isArray(template.components)) {
|
|
72
|
+
throw new Error('Invalid template structure received from API');
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Store in cache
|
|
76
|
+
this.cache.set(templateId, {
|
|
77
|
+
template,
|
|
78
|
+
timestamp: Date.now(),
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
logger.log(`Template ${templateId} fetched successfully`);
|
|
82
|
+
return template;
|
|
83
|
+
} catch (error: any) {
|
|
84
|
+
logger.error(`Error fetching template ${templateId}:`, JSON.stringify(errorMessage(error), null, 2));
|
|
85
|
+
|
|
86
|
+
// Extract backend error message if available
|
|
87
|
+
const backendMessage = errorMessage(error);
|
|
88
|
+
|
|
89
|
+
if (error.response?.status === 404) {
|
|
90
|
+
throw new Error(`Template not found: ${templateId}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (error.response?.status === 401 || error.response?.status === 403) {
|
|
94
|
+
throw new Error(`Unauthorized: Invalid API key or token`);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (backendMessage) {
|
|
98
|
+
throw new Error(`Failed to fetch template: ${JSON.stringify(backendMessage)}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error(`Failed to fetch template: ${error.message || 'Unknown error'}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Clear the template cache
|
|
107
|
+
* @param templateId - Optional template ID to clear. If not provided, clears all cache.
|
|
108
|
+
*/
|
|
109
|
+
clearCache(templateId?: string): void {
|
|
110
|
+
if (templateId) {
|
|
111
|
+
this.cache.delete(templateId);
|
|
112
|
+
logger.log(`Cache cleared for template: ${templateId}`);
|
|
113
|
+
} else {
|
|
114
|
+
this.cache.clear();
|
|
115
|
+
logger.log('All template cache cleared');
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get cached template if available
|
|
121
|
+
* @param templateId - The template ID
|
|
122
|
+
* @returns The cached template or null if not found/expired
|
|
123
|
+
*/
|
|
124
|
+
getCachedTemplate(templateId: string): BackendKYCTemplate | null {
|
|
125
|
+
const cached = this.cache.get(templateId);
|
|
126
|
+
if (cached) {
|
|
127
|
+
const age = Date.now() - cached.timestamp;
|
|
128
|
+
if (age < this.cacheTimeout) {
|
|
129
|
+
return cached.template;
|
|
130
|
+
} else {
|
|
131
|
+
this.cache.delete(templateId);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Fetch list of available templates (optional feature)
|
|
139
|
+
* @param apiKey - Optional API key for authentication
|
|
140
|
+
* @param token - Optional Bearer token for authentication
|
|
141
|
+
* @returns Array of template metadata
|
|
142
|
+
*/
|
|
143
|
+
async fetchTemplateList(apiKey?: string, token?: string): Promise<Array<{ id: string; name: Record<string, string>; version?: string; status?: string }>> {
|
|
144
|
+
try {
|
|
145
|
+
const url = `${KYCConfig.getBackendUrl()}/templates/kyc/templates`;
|
|
146
|
+
logger.log(`Fetching template list from: ${url}`);
|
|
147
|
+
|
|
148
|
+
const response = await axios.get<Array<{ id: string; name: Record<string, string>; version?: string; status?: string }>>(url, {
|
|
149
|
+
headers: this.getHeaders(apiKey, token),
|
|
150
|
+
timeout: 30000,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
logger.log(`Fetched ${response.data.length} templates`);
|
|
154
|
+
return response.data;
|
|
155
|
+
} catch (error: any) {
|
|
156
|
+
logger.error('Error fetching template list:', JSON.stringify(errorMessage(error), null, 2));
|
|
157
|
+
throw new Error(`Failed to fetch template list: ${error.message || 'Unknown error'}`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Export singleton instance as default
|
|
163
|
+
const templateService = new TemplateServiceClass();
|
|
164
|
+
export default templateService;
|
|
165
|
+
|
|
166
|
+
// Also export the class for advanced usage
|
|
167
|
+
// export { TemplateServiceClass };
|
package/src/types/KYC.types.ts
CHANGED
|
@@ -8,6 +8,7 @@ export interface Document {
|
|
|
8
8
|
uploadedAt: Date;
|
|
9
9
|
validated: boolean;
|
|
10
10
|
extractedData?: DocumentData;
|
|
11
|
+
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
export type DocumentType = 'passport' | 'id_card' | 'drivers_license';
|
|
@@ -325,6 +326,12 @@ export type GovernmentDocumentType =
|
|
|
325
326
|
| 'permanent_residence'
|
|
326
327
|
| 'national_id'
|
|
327
328
|
| 'work_permit'
|
|
329
|
+
| 'id'
|
|
330
|
+
| 'pp'
|
|
331
|
+
| 'dl'
|
|
332
|
+
| 'hic'
|
|
333
|
+
| 'pr'
|
|
334
|
+
| 'wp'
|
|
328
335
|
|
|
329
336
|
export const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, string> = {
|
|
330
337
|
identity_card: 'id',
|
|
@@ -334,6 +341,22 @@ export const GovernmentDocumentTypeShorted: Record<GovernmentDocumentType, strin
|
|
|
334
341
|
permanent_residence: 'pr',
|
|
335
342
|
national_id: 'id',
|
|
336
343
|
work_permit: 'wp',
|
|
344
|
+
id: 'id',
|
|
345
|
+
pp: 'pp',
|
|
346
|
+
dl: 'dl',
|
|
347
|
+
hic: 'hic',
|
|
348
|
+
pr: 'pr',
|
|
349
|
+
wp: 'wp',
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
export const GovernmentDocumentTypeBackend: Record<string, GovernmentDocumentType> = {
|
|
353
|
+
national_id: 'identity_card',
|
|
354
|
+
passport: 'passport',
|
|
355
|
+
driversLicense: 'drivers_licence',
|
|
356
|
+
healthInsuranceCard: 'health_insurance_card',
|
|
357
|
+
permanentResidence: 'permanent_residence',
|
|
358
|
+
workPermit: 'work_permit',
|
|
359
|
+
residencePermit: 'permanent_residence',
|
|
337
360
|
}
|
|
338
361
|
|
|
339
362
|
// Configuration spécifique pour chaque type de composant
|
|
@@ -342,7 +365,7 @@ export interface IDCardConfig {
|
|
|
342
365
|
allowed_formats: string[];
|
|
343
366
|
max_size_mb: number;
|
|
344
367
|
document_types: GovernmentDocumentType[]; // Types de documents disponibles
|
|
345
|
-
bbox_configs
|
|
368
|
+
bbox_configs?: Record<GovernmentDocumentType, {
|
|
346
369
|
xMin: number;
|
|
347
370
|
yMin: number;
|
|
348
371
|
xMax: number;
|
|
@@ -383,12 +406,18 @@ export type ComponentConfig =
|
|
|
383
406
|
| SelfieConfig
|
|
384
407
|
| FileUploadConfig
|
|
385
408
|
| LocationConfig
|
|
386
|
-
| CountrySelectionConfig
|
|
409
|
+
| CountrySelectionConfig
|
|
410
|
+
| CountrySelectionConfig
|
|
411
|
+
| WelcomeConfig
|
|
412
|
+
| EmailVerificationConfig
|
|
413
|
+
| PhoneVerificationConfig
|
|
414
|
+
| PersonalInformationConfig
|
|
415
|
+
| AdditionalDocumentsConfig;
|
|
387
416
|
|
|
388
417
|
// Nouveau type de composant basé sur le template JSON
|
|
389
418
|
export interface TemplateComponent {
|
|
390
419
|
id: number;
|
|
391
|
-
type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress';
|
|
420
|
+
type: 'id_card' | 'selfie' | 'file_upload' | 'location' | 'country_selection' | 'initialization' | 'review_submit' | 'verification_progress' | 'welcome' | 'email_verification' | 'phone_verification' | 'personal_information' | 'additional_documents';
|
|
392
421
|
order: number;
|
|
393
422
|
templateId?: number;
|
|
394
423
|
labels: LocalizedText | Record<string, LocalizedText>;
|
|
@@ -464,6 +493,7 @@ export interface UseTemplateReturn {
|
|
|
464
493
|
isComplete: boolean;
|
|
465
494
|
getLocalizedText: (text: LocalizedText) => string;
|
|
466
495
|
initializeSession: () => Promise<void>;
|
|
496
|
+
env: 'PRODUCTION' | 'SANDBOX';
|
|
467
497
|
}
|
|
468
498
|
|
|
469
499
|
|
|
@@ -502,4 +532,302 @@ export interface Country {
|
|
|
502
532
|
flag: string;
|
|
503
533
|
regionMapping?: ICountryMapping;
|
|
504
534
|
hasRegions?: boolean;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// ===== TYPES FOR BACKEND TEMPLATE FORMAT =====
|
|
538
|
+
|
|
539
|
+
// Backend component configuration types
|
|
540
|
+
export interface WelcomeConfig {
|
|
541
|
+
subtitle?: string;
|
|
542
|
+
buttonText?: string;
|
|
543
|
+
requirements?: string[];
|
|
544
|
+
estimatedTime?: string;
|
|
545
|
+
consentOptions?: {
|
|
546
|
+
showPrivacyPolicy?: boolean;
|
|
547
|
+
showTermsOfService?: boolean;
|
|
548
|
+
showMarketingConsent?: boolean;
|
|
549
|
+
};
|
|
550
|
+
welcomeMessage?: string;
|
|
551
|
+
showEstimatedTime?: boolean;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
export interface LocationCaptureConfig {
|
|
555
|
+
showMap?: boolean;
|
|
556
|
+
accuracy?: string;
|
|
557
|
+
required?: boolean;
|
|
558
|
+
maxDistance?: number;
|
|
559
|
+
captureMethod?: string;
|
|
560
|
+
allowManualEntry?: boolean;
|
|
561
|
+
countryRestrictions?: string[];
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export interface GovernmentIdConfig {
|
|
565
|
+
required?: boolean;
|
|
566
|
+
allowSkip?: boolean;
|
|
567
|
+
cameraOverlay?: {
|
|
568
|
+
bbox?: {
|
|
569
|
+
xMax?: number;
|
|
570
|
+
xMin?: number;
|
|
571
|
+
yMax?: number;
|
|
572
|
+
yMin?: number;
|
|
573
|
+
borderColor?: string;
|
|
574
|
+
borderWidth?: number;
|
|
575
|
+
cornerRadius?: number;
|
|
576
|
+
};
|
|
577
|
+
};
|
|
578
|
+
documentTypes?: string[];
|
|
579
|
+
requiredSides?: string;
|
|
580
|
+
cameraSettings?: {
|
|
581
|
+
quality?: number;
|
|
582
|
+
flashMode?: string;
|
|
583
|
+
maxRetakes?: number;
|
|
584
|
+
allowRetake?: boolean;
|
|
585
|
+
aspectRatio?: number;
|
|
586
|
+
};
|
|
587
|
+
validationRules?: string[];
|
|
588
|
+
regionsByCountry?: Record<string, any>;
|
|
589
|
+
selectedCountries?: string[];
|
|
590
|
+
authenticationMethods?: Record<string, Record<string, {
|
|
591
|
+
back?: string[];
|
|
592
|
+
front?: string[];
|
|
593
|
+
mrzTypes?: string[];
|
|
594
|
+
}>>;
|
|
595
|
+
documentTypesByCountry?: Record<string, string[]>;
|
|
596
|
+
instructionsByDocumentType?: Record<string, LocalizedText>;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
export interface SelfieCaptureConfig {
|
|
600
|
+
timeout?: number;
|
|
601
|
+
maxAttempts?: number;
|
|
602
|
+
faceMatching?: boolean;
|
|
603
|
+
captureMethod?: string;
|
|
604
|
+
qualityChecks?: string[];
|
|
605
|
+
livenessChecks?: string[];
|
|
606
|
+
matchThreshold?: string;
|
|
607
|
+
livenessEnabled?: boolean;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
export interface ReviewSubmitConfig {
|
|
611
|
+
allowEditing?: boolean;
|
|
612
|
+
displayOptions?: string[];
|
|
613
|
+
requireConsent?: boolean;
|
|
614
|
+
postSubmitActions?: string[];
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export type BackendComponentConfig =
|
|
618
|
+
| WelcomeConfig
|
|
619
|
+
| LocationCaptureConfig
|
|
620
|
+
| GovernmentIdConfig
|
|
621
|
+
| SelfieCaptureConfig
|
|
622
|
+
| ReviewSubmitConfig
|
|
623
|
+
| Record<string, any>;
|
|
624
|
+
|
|
625
|
+
// Backend component translation structure
|
|
626
|
+
export interface BackendComponentTranslations {
|
|
627
|
+
en?: {
|
|
628
|
+
title?: string;
|
|
629
|
+
description?: string;
|
|
630
|
+
instructions?: string;
|
|
631
|
+
buttonText?: string;
|
|
632
|
+
consentText?: string;
|
|
633
|
+
successMessage?: string;
|
|
634
|
+
};
|
|
635
|
+
fr?: {
|
|
636
|
+
title?: string;
|
|
637
|
+
description?: string;
|
|
638
|
+
instructions?: string;
|
|
639
|
+
buttonText?: string;
|
|
640
|
+
consentText?: string;
|
|
641
|
+
successMessage?: string;
|
|
642
|
+
};
|
|
643
|
+
[key: string]: {
|
|
644
|
+
title?: string;
|
|
645
|
+
description?: string;
|
|
646
|
+
instructions?: string;
|
|
647
|
+
buttonText?: string;
|
|
648
|
+
consentText?: string;
|
|
649
|
+
successMessage?: string;
|
|
650
|
+
} | undefined;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Backend template component
|
|
654
|
+
export interface BackendTemplateComponent {
|
|
655
|
+
id: string;
|
|
656
|
+
type: 'welcome' | 'location-capture' | 'government-id' | 'selfie-capture' | 'review-submit' | 'verification-progress' | 'email-verification' | 'phone-verification' | 'personal-information' | 'additional-documents';
|
|
657
|
+
order: number;
|
|
658
|
+
config: BackendComponentConfig;
|
|
659
|
+
required?: boolean;
|
|
660
|
+
translations: BackendComponentTranslations;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
// Backend template structure
|
|
664
|
+
export interface BackendKYCTemplate {
|
|
665
|
+
id: string;
|
|
666
|
+
name: {
|
|
667
|
+
en: string;
|
|
668
|
+
fr: string;
|
|
669
|
+
[key: string]: string;
|
|
670
|
+
};
|
|
671
|
+
status?: string;
|
|
672
|
+
version?: string;
|
|
673
|
+
components: BackendTemplateComponent[];
|
|
674
|
+
description?: {
|
|
675
|
+
en: string;
|
|
676
|
+
fr: string;
|
|
677
|
+
[key: string]: string;
|
|
678
|
+
};
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
// ===== NEW CONFIG TYPES FOR INPUT VERIFICATION =====
|
|
682
|
+
|
|
683
|
+
export interface EmailVerificationConfig {
|
|
684
|
+
allowResend: boolean;
|
|
685
|
+
otpSettings: {
|
|
686
|
+
codeLength: number;
|
|
687
|
+
expiryTime: number;
|
|
688
|
+
};
|
|
689
|
+
resendCooldown: number;
|
|
690
|
+
emailValidation: {
|
|
691
|
+
corporateOnly: boolean;
|
|
692
|
+
validateFormat: boolean;
|
|
693
|
+
checkDisposable: boolean;
|
|
694
|
+
checkDeliverability: boolean;
|
|
695
|
+
};
|
|
696
|
+
verificationMethod: string;
|
|
697
|
+
required?: boolean;
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
export interface PhoneVerificationConfig {
|
|
701
|
+
required: boolean;
|
|
702
|
+
codeExpiry: number;
|
|
703
|
+
codeLength: number;
|
|
704
|
+
maxAttempts: number;
|
|
705
|
+
resendDelay: number;
|
|
706
|
+
countryRestrictions: string;
|
|
707
|
+
verificationMethods: string[];
|
|
708
|
+
allowSkipForVerified: boolean;
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
export interface PersonalInfoField {
|
|
712
|
+
id: string;
|
|
713
|
+
type: string;
|
|
714
|
+
label: string;
|
|
715
|
+
required: boolean;
|
|
716
|
+
placeholder?: string;
|
|
717
|
+
autoFillFromId?: boolean;
|
|
718
|
+
minAge?: number;
|
|
719
|
+
verifyAge?: boolean;
|
|
720
|
+
dateFormat?: string;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
export interface PersonalInformationConfig {
|
|
724
|
+
fields: PersonalInfoField[];
|
|
725
|
+
validationSettings: {
|
|
726
|
+
validateOnBlur: boolean;
|
|
727
|
+
showAllErrorsAtOnce: boolean;
|
|
728
|
+
preventSubmissionWithErrors: boolean;
|
|
729
|
+
};
|
|
730
|
+
required?: boolean;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export interface DocumentCategory {
|
|
734
|
+
enabled: boolean;
|
|
735
|
+
category: string;
|
|
736
|
+
documentTypes: string[];
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export interface AdditionalDocumentsConfig {
|
|
740
|
+
required: boolean;
|
|
741
|
+
maxSizeEach: string;
|
|
742
|
+
maxDocuments: number;
|
|
743
|
+
minDocuments: number;
|
|
744
|
+
documentCategories: DocumentCategory[];
|
|
745
|
+
customDocumentTypes: any[];
|
|
746
|
+
showBasedOnUserProfile: boolean;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// API response for template
|
|
750
|
+
export interface TemplateAPIResponse {
|
|
751
|
+
id: string;
|
|
752
|
+
name: {
|
|
753
|
+
en: string;
|
|
754
|
+
fr: string;
|
|
755
|
+
[key: string]: string;
|
|
756
|
+
};
|
|
757
|
+
status?: string;
|
|
758
|
+
version?: string;
|
|
759
|
+
components: BackendTemplateComponent[];
|
|
760
|
+
description?: {
|
|
761
|
+
en: string;
|
|
762
|
+
fr: string;
|
|
763
|
+
[key: string]: string;
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
// ===== NEW CONFIG TYPES FOR INPUT VERIFICATION =====
|
|
768
|
+
|
|
769
|
+
export interface EmailVerificationConfig {
|
|
770
|
+
allowResend: boolean;
|
|
771
|
+
otpSettings: {
|
|
772
|
+
codeLength: number;
|
|
773
|
+
expiryTime: number;
|
|
774
|
+
};
|
|
775
|
+
resendCooldown: number;
|
|
776
|
+
emailValidation: {
|
|
777
|
+
corporateOnly: boolean;
|
|
778
|
+
validateFormat: boolean;
|
|
779
|
+
checkDisposable: boolean;
|
|
780
|
+
checkDeliverability: boolean;
|
|
781
|
+
};
|
|
782
|
+
verificationMethod: string;
|
|
783
|
+
required?: boolean;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
export interface PhoneVerificationConfig {
|
|
787
|
+
required: boolean;
|
|
788
|
+
codeExpiry: number;
|
|
789
|
+
codeLength: number;
|
|
790
|
+
maxAttempts: number;
|
|
791
|
+
resendDelay: number;
|
|
792
|
+
countryRestrictions: string;
|
|
793
|
+
verificationMethods: string[];
|
|
794
|
+
allowSkipForVerified: boolean;
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
export interface PersonalInfoField {
|
|
798
|
+
id: string;
|
|
799
|
+
type: string;
|
|
800
|
+
label: string;
|
|
801
|
+
required: boolean;
|
|
802
|
+
placeholder?: string;
|
|
803
|
+
autoFillFromId?: boolean;
|
|
804
|
+
minAge?: number;
|
|
805
|
+
verifyAge?: boolean;
|
|
806
|
+
dateFormat?: string;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
export interface PersonalInformationConfig {
|
|
810
|
+
fields: PersonalInfoField[];
|
|
811
|
+
validationSettings: {
|
|
812
|
+
validateOnBlur: boolean;
|
|
813
|
+
showAllErrorsAtOnce: boolean;
|
|
814
|
+
preventSubmissionWithErrors: boolean;
|
|
815
|
+
};
|
|
816
|
+
required?: boolean;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
export interface DocumentCategory {
|
|
820
|
+
enabled: boolean;
|
|
821
|
+
category: string;
|
|
822
|
+
documentTypes: string[];
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
export interface AdditionalDocumentsConfig {
|
|
826
|
+
required: boolean;
|
|
827
|
+
maxSizeEach: string;
|
|
828
|
+
maxDocuments: number;
|
|
829
|
+
minDocuments: number;
|
|
830
|
+
documentCategories: DocumentCategory[];
|
|
831
|
+
customDocumentTypes: any[];
|
|
832
|
+
showBasedOnUserProfile: boolean;
|
|
505
833
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KYC Environment types
|
|
3
|
+
* PRODUCTION: Full verification with AI checks
|
|
4
|
+
* SANDBOX: Skip AI verification, only save data to backend
|
|
5
|
+
*/
|
|
6
|
+
export type KycEnvironment = 'PRODUCTION' | 'SANDBOX';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Backend Environment types
|
|
10
|
+
* PRODUCTION: Production backend
|
|
11
|
+
* TEST: Test backend
|
|
12
|
+
*/
|
|
13
|
+
export type BackendEnvironment = 'PRODUCTION' | 'TEST';
|
package/src/utils/cropByObb.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Image as RNImage, Platform } from 'react-native';
|
|
2
2
|
import * as ImageManipulator from "expo-image-manipulator";
|
|
3
|
+
import { truncateFields } from '../modules/api/KYCService';
|
|
4
|
+
import { logger } from './logger';
|
|
3
5
|
|
|
4
6
|
type Point = [number, number];
|
|
5
7
|
|
|
@@ -81,13 +83,14 @@ export async function cropImageWithBBox(uri: string, bbox: any) {
|
|
|
81
83
|
|
|
82
84
|
// 3️⃣ Convertir le bbox à la taille réelle scale 0.10 = 10%
|
|
83
85
|
const crop = {
|
|
84
|
-
originX: bbox.minX
|
|
85
|
-
originY: bbox.minY
|
|
86
|
-
width: bbox.width
|
|
86
|
+
originX: bbox.minX,
|
|
87
|
+
originY: bbox.minY,
|
|
88
|
+
width: bbox.width,
|
|
87
89
|
height: bbox.height,
|
|
88
90
|
};
|
|
89
91
|
|
|
90
92
|
// 4️⃣ Appliquer le crop
|
|
93
|
+
// @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
|
|
91
94
|
const result = await ImageManipulator.manipulateAsync(
|
|
92
95
|
uri,
|
|
93
96
|
[{ crop }],
|
|
@@ -97,3 +100,80 @@ export async function cropImageWithBBox(uri: string, bbox: any) {
|
|
|
97
100
|
console.log("Image recadrée:", result.uri);
|
|
98
101
|
return result.uri;
|
|
99
102
|
}
|
|
103
|
+
|
|
104
|
+
// Fonction pour rogner avec une tolérance de 10% autour du bbox
|
|
105
|
+
export async function cropImageWithBBoxWithTolerance(uri: string, bbox: any, tolerance: number = 0.1): Promise<string> {
|
|
106
|
+
console.log("cropImageWithBBoxWithTolerance", JSON.stringify(truncateFields({ uri, bbox, tolerance }), null, 2));
|
|
107
|
+
|
|
108
|
+
return new Promise<string>((resolve, reject) => {
|
|
109
|
+
let isResolved = false;
|
|
110
|
+
|
|
111
|
+
// Timeout de sécurité (15 secondes)
|
|
112
|
+
const timeout = setTimeout(() => {
|
|
113
|
+
if (!isResolved) {
|
|
114
|
+
isResolved = true;
|
|
115
|
+
console.warn("Timeout lors du rognage avec tolérance, utilisation de l'image originale");
|
|
116
|
+
resolve(uri); // Retourner l'URI original en cas de timeout
|
|
117
|
+
}
|
|
118
|
+
}, 15000);
|
|
119
|
+
|
|
120
|
+
// 1️⃣ Récupère la taille originale de l'image
|
|
121
|
+
RNImage.getSize(uri, (imageWidth, imageHeight) => {
|
|
122
|
+
if (isResolved) return;
|
|
123
|
+
|
|
124
|
+
// 2️⃣ Calculer la tolérance en pixels (10% de chaque côté)
|
|
125
|
+
const toleranceX = bbox.width * tolerance;
|
|
126
|
+
const toleranceY = bbox.height * tolerance;
|
|
127
|
+
|
|
128
|
+
// 3️⃣ Ajuster le bbox avec la tolérance
|
|
129
|
+
// Calculer les nouvelles coordonnées avec tolérance
|
|
130
|
+
const newMinX = Math.max(0, bbox.minX - toleranceX);
|
|
131
|
+
const newMinY = Math.max(0, bbox.minY - toleranceY);
|
|
132
|
+
const newMaxX = Math.min(imageWidth, bbox.minX + bbox.width + toleranceX);
|
|
133
|
+
const newMaxY = Math.min(imageHeight, bbox.minY + bbox.height + toleranceY);
|
|
134
|
+
|
|
135
|
+
// Calculer la largeur et hauteur finales
|
|
136
|
+
const finalWidth = newMaxX - newMinX;
|
|
137
|
+
const finalHeight = newMaxY - newMinY;
|
|
138
|
+
|
|
139
|
+
const crop = {
|
|
140
|
+
originX: newMinX,
|
|
141
|
+
originY: newMinY,
|
|
142
|
+
width: finalWidth,
|
|
143
|
+
height: finalHeight,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
logger.log("cropImageWithBBoxWithTolerance - crop params", JSON.stringify(truncateFields({ uri, crop, tolerance, imageWidth, imageHeight }), null, 2));
|
|
147
|
+
|
|
148
|
+
// 4️⃣ Appliquer le crop avec tolérance
|
|
149
|
+
// @ts-ignore - manipulateAsync is deprecated but still functional, new API (useImageManipulator) is a React hook and not suitable for utility functions
|
|
150
|
+
ImageManipulator.manipulateAsync(
|
|
151
|
+
uri,
|
|
152
|
+
[{ crop }],
|
|
153
|
+
{ compress: 0.95, format: ImageManipulator.SaveFormat.JPEG }
|
|
154
|
+
)
|
|
155
|
+
.then((result) => {
|
|
156
|
+
if (isResolved) return;
|
|
157
|
+
isResolved = true;
|
|
158
|
+
clearTimeout(timeout);
|
|
159
|
+
console.log("Image recadrée avec tolérance:", result.uri);
|
|
160
|
+
resolve(result.uri);
|
|
161
|
+
})
|
|
162
|
+
.catch((error) => {
|
|
163
|
+
if (isResolved) return;
|
|
164
|
+
isResolved = true;
|
|
165
|
+
clearTimeout(timeout);
|
|
166
|
+
console.error("Erreur lors du rognage avec tolérance:", error);
|
|
167
|
+
// En cas d'erreur, retourner l'URI original au lieu de rejeter
|
|
168
|
+
resolve(uri);
|
|
169
|
+
});
|
|
170
|
+
}, (error) => {
|
|
171
|
+
if (isResolved) return;
|
|
172
|
+
isResolved = true;
|
|
173
|
+
clearTimeout(timeout);
|
|
174
|
+
console.error("Erreur lors de la récupération de la taille de l'image:", error);
|
|
175
|
+
// En cas d'erreur, retourner l'URI original au lieu de rejeter
|
|
176
|
+
resolve(uri);
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checks if the current environment is a mobile web browser based on User Agent.
|
|
3
|
+
* This can be used to distinguish between desktop web and mobile web.
|
|
4
|
+
*/
|
|
5
|
+
export const isMobileWeb = (): boolean => {
|
|
6
|
+
if (typeof window === 'undefined' || !window.navigator) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
const userAgent = window.navigator.userAgent;
|
|
10
|
+
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
|
|
11
|
+
};
|