@test-web/react-native-sdk 1.0.0 → 2.1.0
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 +543 -26
- package/assets/images/Chrome-logo.svg +1 -0
- package/assets/images/Firefox-logo.png +0 -0
- package/assets/images/IDM-logo.jpg +0 -0
- package/assets/images/MRZOverlay.png +0 -0
- package/assets/images/Safari-logo.png +0 -0
- package/assets/images/aadhar.png +0 -0
- package/assets/images/camera-bg.png +0 -0
- package/assets/images/card-overlay-back.png +0 -0
- package/assets/images/card-overlay.png +0 -0
- package/assets/images/card-scan-back-icon.jpg +0 -0
- package/assets/images/card-scan-front-icon.png +0 -0
- package/assets/images/card-scan-icon-aadhaar-1.png +0 -0
- package/assets/images/card-scan-icon-aadhaar-back.png +0 -0
- package/assets/images/card-scan-icon-aadhaar-scan-qr.png +0 -0
- package/assets/images/card-scan-icon-aadhaar.png +0 -0
- package/assets/images/card-scan-icon-can-pr.png +0 -0
- package/assets/images/card-scan-icon-default-back.png +0 -0
- package/assets/images/card-scan-icon-dl.png +0 -0
- package/assets/images/card-scan-icon-greencard-back.jpg +0 -0
- package/assets/images/card-scan-icon-greencard.jpg +0 -0
- package/assets/images/card-scan-icon-hc.png +0 -0
- package/assets/images/card-scan-icon-ni-argentina-back.jpg +0 -0
- package/assets/images/card-scan-icon-ni-argentina-old.png +0 -0
- package/assets/images/card-scan-icon-ni-argentina.jpg +0 -0
- package/assets/images/card-scan-icon-ni-barcode.jpg +0 -0
- package/assets/images/card-scan-icon-ni-brazil-back.jpg +0 -0
- package/assets/images/card-scan-icon-ni-brazil.jpg +0 -0
- package/assets/images/card-scan-icon-ni-dominican-republic-back.png +0 -0
- package/assets/images/card-scan-icon-ni-dominican-republic-front.png +0 -0
- package/assets/images/card-scan-icon-ni-dominican-republic-mrz.png +0 -0
- package/assets/images/card-scan-icon-ni-dominicanaRepublic-back.jpg +0 -0
- package/assets/images/card-scan-icon-ni-france-back.png +0 -0
- package/assets/images/card-scan-icon-ni-france-front.png +0 -0
- package/assets/images/card-scan-icon-ni-france-scan-mrz.png +0 -0
- package/assets/images/card-scan-icon-ni-germany-back.jpg +0 -0
- package/assets/images/card-scan-icon-ni-germany.jpg +0 -0
- package/assets/images/card-scan-icon-ni-paraguay-back.png +0 -0
- package/assets/images/card-scan-icon-ni-paraguay-front.png +0 -0
- package/assets/images/card-scan-icon-ni-paraguay-scan-mrz.png +0 -0
- package/assets/images/card-scan-icon-ni-uae-back.png +0 -0
- package/assets/images/card-scan-icon-ni-uae-front.png +0 -0
- package/assets/images/card-scan-icon-ni-uae-scan-mrz.png +0 -0
- package/assets/images/card-scan-icon-ni-uganda-front.png +0 -0
- package/assets/images/card-scan-icon-ni-uganda-scan-mrz.png +0 -0
- package/assets/images/card-scan-icon-ni-ukrain-back.png +0 -0
- package/assets/images/card-scan-icon-ni-ukrain-front.png +0 -0
- package/assets/images/card-scan-icon-ni-ukrain-scan-mrz.png +0 -0
- package/assets/images/card-scan-icon-ni.png +0 -0
- package/assets/images/card-scan-icon-old.jpg +0 -0
- package/assets/images/card-scan-icon-pan.png +0 -0
- package/assets/images/card-scan-icon-passport-card-back.jpg +0 -0
- package/assets/images/card-scan-icon-passport-card.jpg +0 -0
- package/assets/images/card-scan-icon-passport-old.png +0 -0
- package/assets/images/card-scan-icon-passport.png +0 -0
- package/assets/images/card-scan-icon-pr.png +0 -0
- package/assets/images/card-scan-icon.jpg +0 -0
- package/assets/images/check.png +0 -0
- package/assets/images/chrome-animation-GPS-permissions-setting.gif +0 -0
- package/assets/images/chrome-animation-camera-permissions-setting.gif +0 -0
- package/assets/images/denied.png +0 -0
- package/assets/images/dl.png +0 -0
- package/assets/images/driver-license.png +0 -0
- package/assets/images/firefox-animation-permissions-setting.gif +0 -0
- package/assets/images/flashlight_on.png +0 -0
- package/assets/images/gallery.png +0 -0
- package/assets/images/greencard.png +0 -0
- package/assets/images/header.jpg +0 -0
- package/assets/images/health-card.png +0 -0
- package/assets/images/ic_camera_front_white_36px.svg +4 -0
- package/assets/images/ic_camera_rear_white_36px.svg +4 -0
- package/assets/images/ic_fullscreen_exit_white_48px.svg +4 -0
- package/assets/images/ic_fullscreen_white_48px.svg +4 -0
- package/assets/images/ic_photo_camera_white_48px.svg +5 -0
- package/assets/images/id-card.png +0 -0
- package/assets/images/idcardimg.png +0 -0
- package/assets/images/idmval-barcode.png +0 -0
- package/assets/images/information.png +0 -0
- package/assets/images/loader.gif +0 -0
- package/assets/images/loading.svg +1 -0
- package/assets/images/logo.jpg +0 -0
- package/assets/images/logo.png +0 -0
- package/assets/images/mrz-back.png +0 -0
- package/assets/images/mrz-ni.png +0 -0
- package/assets/images/mrz.png +0 -0
- package/assets/images/mrz1.png +0 -0
- package/assets/images/mrz_old.png +0 -0
- package/assets/images/mrz_small.png +0 -0
- package/assets/images/national-id.png +0 -0
- package/assets/images/nationalID.png +0 -0
- package/assets/images/no-wifi.png +0 -0
- package/assets/images/passport-card.png +0 -0
- package/assets/images/passport.png +0 -0
- package/assets/images/permit-card.png +0 -0
- package/assets/images/photo-overlay.png +0 -0
- package/assets/images/placeholder.jpg +0 -0
- package/assets/images/qr-code.png +0 -0
- package/assets/images/right-checkmark.jpg +0 -0
- package/assets/images/selfie.jpg +0 -0
- package/assets/images/showing-sec.png +0 -0
- package/assets/images/spinner.gif +0 -0
- package/assets/images/splash-icon.png +0 -0
- package/assets/images/take-selfie.jpg +0 -0
- package/assets/images/torch_off.png +0 -0
- package/assets/images/warning-icon.jpg +0 -0
- package/assets/images/warning-stick.jpg +0 -0
- package/assets/images/wrong-checkmark.jpg +0 -0
- package/package.json +35 -8
- package/src/apis/index.ts +338 -17
- package/src/components/common/Loader.tsx +16 -2
- package/src/config/apiConfig.ts +6 -0
- package/src/index.tsx +123 -7
- package/src/screens/BarcodeCapture.tsx +64 -12
- package/src/screens/DocumentCaptureBack.tsx +133 -24
- package/src/screens/DocumentCaptureFront.tsx +146 -24
- package/src/screens/MrzCapture.tsx +77 -12
- package/src/screens/SelectDocuments.tsx +37 -56
- package/src/screens/SelfieCapture.tsx +114 -18
- package/src/screens/ThankYou.tsx +34 -1
- package/src/services/getUserData.ts +111 -0
- package/src/types/IDMConf.ts +81 -7
- package/src/utils/base64.ts +25 -0
- package/src/utils/flowManager.ts +138 -0
- package/src/utils/imageProcessor.ts +96 -0
- package/src/utils/index.ts +18 -0
package/src/screens/ThankYou.tsx
CHANGED
|
@@ -1,14 +1,46 @@
|
|
|
1
|
-
import React from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { View } from 'react-native';
|
|
3
3
|
import ThemedText from '../components/ui/ThemedText';
|
|
4
4
|
import getStyles from '../styles/ThankYouStyles';
|
|
5
5
|
import { useTheme } from '../context/ThemeContext';
|
|
6
6
|
import { useOrientation } from '../hooks/useOrientation';
|
|
7
|
+
import { useIDM } from '../context/IDMConfigurationContext';
|
|
8
|
+
import Loader from '../components/common/Loader';
|
|
9
|
+
import { completeVerification } from '../apis';
|
|
7
10
|
|
|
8
11
|
export default function ThankYou() {
|
|
9
12
|
const { theme } = useTheme();
|
|
13
|
+
const { idmConf } = useIDM();
|
|
10
14
|
const orientation = useOrientation();
|
|
11
15
|
const styles = getStyles(theme, orientation);
|
|
16
|
+
const [loading, setLoading] = useState(false);
|
|
17
|
+
const [completed, setCompleted] = useState(false);
|
|
18
|
+
|
|
19
|
+
// Call complete verification API
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
const finalizeVerification = async () => {
|
|
22
|
+
if (completed || !idmConf.verificationCode) return;
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
setLoading(true);
|
|
26
|
+
const result = await completeVerification(idmConf);
|
|
27
|
+
console.log('Verification completed:', result);
|
|
28
|
+
setCompleted(true);
|
|
29
|
+
} catch (error: any) {
|
|
30
|
+
console.error('Error completing verification:', error);
|
|
31
|
+
// Don't show error to user, just log it
|
|
32
|
+
} finally {
|
|
33
|
+
setLoading(false);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Call after a short delay to ensure all data is submitted
|
|
38
|
+
const timer = setTimeout(() => {
|
|
39
|
+
finalizeVerification();
|
|
40
|
+
}, 1000);
|
|
41
|
+
|
|
42
|
+
return () => clearTimeout(timer);
|
|
43
|
+
}, [idmConf, completed]);
|
|
12
44
|
|
|
13
45
|
return (
|
|
14
46
|
<View style={styles.container}>
|
|
@@ -18,6 +50,7 @@ export default function ThankYou() {
|
|
|
18
50
|
<ThemedText style={styles.subtitle} type="title">
|
|
19
51
|
Your information{'\n'}has been uploaded
|
|
20
52
|
</ThemedText>
|
|
53
|
+
{loading && <Loader message="Finalizing verification..." />}
|
|
21
54
|
</View>
|
|
22
55
|
);
|
|
23
56
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Platform, PermissionsAndroid } from 'react-native';
|
|
2
|
+
|
|
3
|
+
interface UserData {
|
|
4
|
+
permissionGranted: boolean;
|
|
5
|
+
location: any | null;
|
|
6
|
+
ipAddress: string | null;
|
|
7
|
+
deviceInfo: any;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Gets user data including location, device info, and IP address
|
|
12
|
+
* Handles permission requests for Android
|
|
13
|
+
*/
|
|
14
|
+
const getUserData = async (): Promise<UserData> => {
|
|
15
|
+
let permissionGranted = true;
|
|
16
|
+
let location: any | null = null;
|
|
17
|
+
let ipAddress: string | null = null;
|
|
18
|
+
|
|
19
|
+
// Get device info (requires react-native-device-info)
|
|
20
|
+
let deviceInfo: any = {
|
|
21
|
+
brand: 'Unknown',
|
|
22
|
+
model: 'Unknown',
|
|
23
|
+
systemName: Platform.OS,
|
|
24
|
+
systemVersion: Platform.Version.toString(),
|
|
25
|
+
uniqueId: 'unknown',
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Try to import device info if available
|
|
30
|
+
const DeviceInfo = require('react-native-device-info');
|
|
31
|
+
deviceInfo = {
|
|
32
|
+
brand: DeviceInfo.getBrand(),
|
|
33
|
+
model: DeviceInfo.getModel(),
|
|
34
|
+
systemName: DeviceInfo.getSystemName(),
|
|
35
|
+
systemVersion: DeviceInfo.getSystemVersion(),
|
|
36
|
+
uniqueId: await DeviceInfo.getUniqueId(),
|
|
37
|
+
};
|
|
38
|
+
} catch (error) {
|
|
39
|
+
console.warn('react-native-device-info not available:', error);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Android: Check location permission
|
|
43
|
+
if (Platform.OS === 'android') {
|
|
44
|
+
try {
|
|
45
|
+
const alreadyGranted = await PermissionsAndroid.check(
|
|
46
|
+
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (!alreadyGranted) {
|
|
50
|
+
const granted = await PermissionsAndroid.request(
|
|
51
|
+
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
|
|
52
|
+
{
|
|
53
|
+
title: 'Location Permission',
|
|
54
|
+
message: 'This app needs access to your location.',
|
|
55
|
+
buttonPositive: 'OK',
|
|
56
|
+
buttonNegative: 'Cancel',
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
61
|
+
permissionGranted = false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.warn('Permission check error:', err);
|
|
66
|
+
permissionGranted = false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Get location if permission granted
|
|
71
|
+
if (permissionGranted) {
|
|
72
|
+
try {
|
|
73
|
+
// Try to import geolocation if available
|
|
74
|
+
const Geolocation = require('react-native-geolocation-service');
|
|
75
|
+
location = await new Promise((resolve, reject) => {
|
|
76
|
+
Geolocation.getCurrentPosition(
|
|
77
|
+
resolve,
|
|
78
|
+
reject,
|
|
79
|
+
{
|
|
80
|
+
enableHighAccuracy: true,
|
|
81
|
+
timeout: 15000,
|
|
82
|
+
maximumAge: 10000,
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
} catch (err) {
|
|
87
|
+
console.warn('Location fetch error:', err);
|
|
88
|
+
location = null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Get IP address
|
|
93
|
+
try {
|
|
94
|
+
const response = await fetch('https://api.ipify.org?format=json');
|
|
95
|
+
if (response.ok) {
|
|
96
|
+
const data = await response.json();
|
|
97
|
+
ipAddress = data.ip;
|
|
98
|
+
}
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.warn('IP fetch error:', err);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
permissionGranted,
|
|
105
|
+
location,
|
|
106
|
+
ipAddress,
|
|
107
|
+
deviceInfo,
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default getUserData;
|
package/src/types/IDMConf.ts
CHANGED
|
@@ -1,13 +1,58 @@
|
|
|
1
1
|
export interface IDMConf {
|
|
2
|
-
|
|
2
|
+
clientID: string;
|
|
3
|
+
clientSecret: string;
|
|
4
|
+
environment: 'sandbox' | 'production';
|
|
5
|
+
requestData: RequestData;
|
|
6
|
+
theme?: 'light' | 'dark' | any;
|
|
3
7
|
userDetails?: UserDetails;
|
|
4
8
|
accessToken?: string;
|
|
5
9
|
verificationCode?: string;
|
|
6
|
-
|
|
7
|
-
configuration?: any;
|
|
10
|
+
configuration?: Configuration;
|
|
8
11
|
countryDetails?: CountryDetail[];
|
|
9
|
-
selectedCountryDetails?:
|
|
10
|
-
requestConfiguration?:
|
|
12
|
+
selectedCountryDetails?: SelectedCountryDetail;
|
|
13
|
+
requestConfiguration?: RequestConfiguration;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface RequestData {
|
|
17
|
+
requestID: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
dateOfBirth?: string;
|
|
20
|
+
countryCode?: string;
|
|
21
|
+
mobile?: string;
|
|
22
|
+
callbackURL?: string;
|
|
23
|
+
redirectURL?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Configuration {
|
|
27
|
+
logo?: string;
|
|
28
|
+
hideFooter?: boolean;
|
|
29
|
+
skipInputName?: boolean;
|
|
30
|
+
enableLiveVideo?: boolean;
|
|
31
|
+
defaultWebTemplate?: boolean;
|
|
32
|
+
tenantName?: string;
|
|
33
|
+
geoLocationRequired?: boolean;
|
|
34
|
+
showEndUserVerificationHistory?: boolean;
|
|
35
|
+
enableLiveVideoMode?: boolean;
|
|
36
|
+
showFooterTenantName?: boolean;
|
|
37
|
+
verificationCode?: string | null | boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface RequestConfiguration {
|
|
41
|
+
requestID?: string | null;
|
|
42
|
+
callbackURL?: string | null;
|
|
43
|
+
uniqueID?: string | null;
|
|
44
|
+
status?: string | null;
|
|
45
|
+
documentType?: string | null;
|
|
46
|
+
barcodeType?: string | null;
|
|
47
|
+
skipBarcode?: boolean | null;
|
|
48
|
+
skipLiveness?: boolean | null;
|
|
49
|
+
enableCardBackCapture?: boolean | null;
|
|
50
|
+
qrCode?: string;
|
|
51
|
+
redirectURL?: string | null;
|
|
52
|
+
verificationLink?: string | null;
|
|
53
|
+
country?: string | null;
|
|
54
|
+
countryCode?: string | null;
|
|
55
|
+
liveVideo?: boolean | null;
|
|
11
56
|
}
|
|
12
57
|
|
|
13
58
|
export interface UserDetails {
|
|
@@ -17,12 +62,41 @@ export interface UserDetails {
|
|
|
17
62
|
latitude: number;
|
|
18
63
|
longitude: number;
|
|
19
64
|
};
|
|
65
|
+
} | null;
|
|
66
|
+
ipAddress?: string | null;
|
|
67
|
+
deviceInfo?: {
|
|
68
|
+
brand: string;
|
|
69
|
+
model: string;
|
|
70
|
+
systemName: string;
|
|
71
|
+
systemVersion: string;
|
|
72
|
+
uniqueId: string;
|
|
20
73
|
};
|
|
21
74
|
[key: string]: any;
|
|
22
75
|
}
|
|
23
76
|
|
|
24
77
|
export interface CountryDetail {
|
|
25
78
|
label: string;
|
|
26
|
-
value: string;
|
|
27
|
-
metadata:
|
|
79
|
+
value: string | number;
|
|
80
|
+
metadata: DocumentMetadata[];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface SelectedCountryDetail extends CountryDetail {
|
|
84
|
+
selectedMetaData?: DocumentMetadata;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface DocumentMetadata {
|
|
88
|
+
id: number;
|
|
89
|
+
country: string;
|
|
90
|
+
countryCode: string;
|
|
91
|
+
type: string; // DL, PP, NI, PC, GC, etc.
|
|
92
|
+
barcode: string; // PDF417 B, PDF417F, TD3, TD2, TD1, None
|
|
93
|
+
dateFormat: string;
|
|
94
|
+
distance: number;
|
|
95
|
+
comparedText: string | null;
|
|
96
|
+
alternateText: string;
|
|
97
|
+
live: boolean;
|
|
98
|
+
selected: boolean;
|
|
99
|
+
countryEuropean: boolean;
|
|
100
|
+
engineLanguage: boolean;
|
|
101
|
+
document_flow: string[]; // ["F"], ["F", "B"], etc.
|
|
28
102
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Encodes a string to base64
|
|
3
|
+
* @param str - The string to encode
|
|
4
|
+
* @returns Base64 encoded string
|
|
5
|
+
*/
|
|
6
|
+
export const base64Encode = (str: string): string => {
|
|
7
|
+
if (typeof btoa !== 'undefined') {
|
|
8
|
+
return btoa(str);
|
|
9
|
+
}
|
|
10
|
+
// Fallback for environments without btoa
|
|
11
|
+
return Buffer.from(str, 'utf-8').toString('base64');
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Decodes a base64 string
|
|
16
|
+
* @param str - The base64 string to decode
|
|
17
|
+
* @returns Decoded string
|
|
18
|
+
*/
|
|
19
|
+
export const base64Decode = (str: string): string => {
|
|
20
|
+
if (typeof atob !== 'undefined') {
|
|
21
|
+
return atob(str);
|
|
22
|
+
}
|
|
23
|
+
// Fallback for environments without atob
|
|
24
|
+
return Buffer.from(str, 'base64').toString('utf-8');
|
|
25
|
+
};
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { DocumentMetadata } from '../types/IDMConf';
|
|
2
|
+
|
|
3
|
+
export type FlowStep = 'F' | 'B'; // Front, Back
|
|
4
|
+
export type BarcodeType = 'PDF417 B' | 'PDF417F' | 'TD3' | 'TD2' | 'TD1' | 'None' | 'NONE';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Determines the next screen in the verification flow based on metadata
|
|
8
|
+
*/
|
|
9
|
+
export class FlowManager {
|
|
10
|
+
private metadata: DocumentMetadata;
|
|
11
|
+
private completedSteps: Set<FlowStep>;
|
|
12
|
+
|
|
13
|
+
constructor(metadata: DocumentMetadata) {
|
|
14
|
+
this.metadata = metadata;
|
|
15
|
+
this.completedSteps = new Set();
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Mark a step as completed
|
|
20
|
+
*/
|
|
21
|
+
markStepCompleted(step: FlowStep): void {
|
|
22
|
+
this.completedSteps.add(step);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the next screen to navigate to after capturing front document
|
|
27
|
+
*/
|
|
28
|
+
getNextScreenAfterFront(): string {
|
|
29
|
+
this.markStepCompleted('F');
|
|
30
|
+
|
|
31
|
+
const { document_flow, barcode } = this.metadata;
|
|
32
|
+
|
|
33
|
+
// Check if back capture is required
|
|
34
|
+
if (document_flow && document_flow.includes('B') && !this.completedSteps.has('B')) {
|
|
35
|
+
return 'BackDocumentAdvice';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Check for barcode/MRZ requirements
|
|
39
|
+
return this.getNextScreenForBarcode(barcode);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the next screen to navigate to after capturing back document
|
|
44
|
+
*/
|
|
45
|
+
getNextScreenAfterBack(): string {
|
|
46
|
+
this.markStepCompleted('B');
|
|
47
|
+
|
|
48
|
+
const { barcode } = this.metadata;
|
|
49
|
+
return this.getNextScreenForBarcode(barcode);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Determine next screen based on barcode type
|
|
54
|
+
*/
|
|
55
|
+
private getNextScreenForBarcode(barcode: string): string {
|
|
56
|
+
const barcodeUpper = barcode?.toUpperCase() || 'NONE';
|
|
57
|
+
|
|
58
|
+
if (barcodeUpper.includes('PDF417')) {
|
|
59
|
+
return 'BarcodeAdvice';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
if (['TD3', 'TD2', 'TD1'].includes(barcodeUpper)) {
|
|
63
|
+
return 'MrzAdvice';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// No barcode/MRZ required, go to thank you
|
|
67
|
+
return 'ThankYou';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Check if back capture is required
|
|
72
|
+
*/
|
|
73
|
+
requiresBackCapture(): boolean {
|
|
74
|
+
const { document_flow } = this.metadata;
|
|
75
|
+
return document_flow && document_flow.includes('B') || false;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Check if barcode scan is required
|
|
80
|
+
*/
|
|
81
|
+
requiresBarcodeCapture(): boolean {
|
|
82
|
+
const { barcode } = this.metadata;
|
|
83
|
+
const barcodeUpper = barcode?.toUpperCase() || 'NONE';
|
|
84
|
+
return barcodeUpper.includes('PDF417');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Check if MRZ scan is required
|
|
89
|
+
*/
|
|
90
|
+
requiresMRZCapture(): boolean {
|
|
91
|
+
const { barcode } = this.metadata;
|
|
92
|
+
const barcodeUpper = barcode?.toUpperCase() || 'NONE';
|
|
93
|
+
return ['TD3', 'TD2', 'TD1'].includes(barcodeUpper);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get all required steps for this document
|
|
98
|
+
*/
|
|
99
|
+
getRequiredSteps(): string[] {
|
|
100
|
+
const steps: string[] = [];
|
|
101
|
+
const { document_flow, barcode } = this.metadata;
|
|
102
|
+
|
|
103
|
+
if (document_flow) {
|
|
104
|
+
if (document_flow.includes('F')) steps.push('FrontCapture');
|
|
105
|
+
if (document_flow.includes('B')) steps.push('BackCapture');
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const barcodeUpper = barcode?.toUpperCase() || 'NONE';
|
|
109
|
+
if (barcodeUpper.includes('PDF417')) {
|
|
110
|
+
steps.push('BarcodeCapture');
|
|
111
|
+
} else if (['TD3', 'TD2', 'TD1'].includes(barcodeUpper)) {
|
|
112
|
+
steps.push('MRZCapture');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return steps;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Get MRZ engine type based on barcode type
|
|
120
|
+
*/
|
|
121
|
+
getMRZEngine(): string {
|
|
122
|
+
const { barcode } = this.metadata;
|
|
123
|
+
const barcodeUpper = barcode?.toUpperCase() || 'NONE';
|
|
124
|
+
|
|
125
|
+
if (barcodeUpper === 'TD3') return 'TD3';
|
|
126
|
+
if (barcodeUpper === 'TD2') return 'TD2';
|
|
127
|
+
if (barcodeUpper === 'TD1') return 'TD1';
|
|
128
|
+
|
|
129
|
+
return 'TD3'; // Default
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Helper function to create a FlowManager instance
|
|
135
|
+
*/
|
|
136
|
+
export const createFlowManager = (metadata: DocumentMetadata): FlowManager => {
|
|
137
|
+
return new FlowManager(metadata);
|
|
138
|
+
};
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image processing utilities for document capture
|
|
3
|
+
* Handles image resizing and base64 conversion
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
interface ResizeOptions {
|
|
7
|
+
width?: number;
|
|
8
|
+
height?: number;
|
|
9
|
+
quality?: number;
|
|
10
|
+
format?: 'JPEG' | 'PNG';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resize and convert image to base64
|
|
15
|
+
* Requires react-native-image-resizer and react-native-fs
|
|
16
|
+
*/
|
|
17
|
+
export const processImageToBase64 = async (
|
|
18
|
+
imagePath: string,
|
|
19
|
+
options: ResizeOptions = {}
|
|
20
|
+
): Promise<string> => {
|
|
21
|
+
const {
|
|
22
|
+
width = 810,
|
|
23
|
+
height = 1080,
|
|
24
|
+
quality = 70,
|
|
25
|
+
format = 'JPEG',
|
|
26
|
+
} = options;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
// Try to import required libraries
|
|
30
|
+
const ImageResizer = require('react-native-image-resizer');
|
|
31
|
+
const RNFS = require('react-native-fs');
|
|
32
|
+
|
|
33
|
+
// Ensure path has file:// prefix
|
|
34
|
+
const fullPath = imagePath.startsWith('file://') ? imagePath : `file://${imagePath}`;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
// Resize the image
|
|
38
|
+
const resizedImage = await ImageResizer.createResizedImage(
|
|
39
|
+
fullPath,
|
|
40
|
+
width,
|
|
41
|
+
height,
|
|
42
|
+
format,
|
|
43
|
+
quality,
|
|
44
|
+
0,
|
|
45
|
+
undefined,
|
|
46
|
+
false
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Convert to base64
|
|
50
|
+
const base64Image = await RNFS.readFile(resizedImage.uri, 'base64');
|
|
51
|
+
return base64Image;
|
|
52
|
+
} catch (resizeError: any) {
|
|
53
|
+
console.warn('Image resizing failed, using original image:', resizeError.message);
|
|
54
|
+
|
|
55
|
+
// Fallback: use original image
|
|
56
|
+
const base64Image = await RNFS.readFile(fullPath, 'base64');
|
|
57
|
+
return base64Image;
|
|
58
|
+
}
|
|
59
|
+
} catch (error: any) {
|
|
60
|
+
console.error('Image processing error:', error);
|
|
61
|
+
throw new Error(`Failed to process image: ${error.message}`);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Validate image path
|
|
67
|
+
*/
|
|
68
|
+
export const validateImagePath = (path: string | undefined): boolean => {
|
|
69
|
+
if (!path) {
|
|
70
|
+
console.error('No image path provided');
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
return true;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get image dimensions (if needed)
|
|
78
|
+
*/
|
|
79
|
+
export const getImageDimensions = async (
|
|
80
|
+
imagePath: string
|
|
81
|
+
): Promise<{ width: number; height: number }> => {
|
|
82
|
+
try {
|
|
83
|
+
const Image = require('react-native').Image;
|
|
84
|
+
|
|
85
|
+
return new Promise((resolve, reject) => {
|
|
86
|
+
Image.getSize(
|
|
87
|
+
imagePath,
|
|
88
|
+
(width: number, height: number) => resolve({ width, height }),
|
|
89
|
+
(error: any) => reject(error)
|
|
90
|
+
);
|
|
91
|
+
});
|
|
92
|
+
} catch (error: any) {
|
|
93
|
+
console.error('Failed to get image dimensions:', error);
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -17,3 +17,21 @@ export {
|
|
|
17
17
|
BatchUpdater,
|
|
18
18
|
createStableRef,
|
|
19
19
|
} from './performance';
|
|
20
|
+
|
|
21
|
+
// Base64 utilities
|
|
22
|
+
export { base64Encode, base64Decode } from './base64';
|
|
23
|
+
|
|
24
|
+
// Document type labels
|
|
25
|
+
export const DOCUMENT_LABELS: Record<string, string> = {
|
|
26
|
+
DL: "Driver's License",
|
|
27
|
+
PP: 'Passport',
|
|
28
|
+
NI: 'National ID',
|
|
29
|
+
PC: 'Passport Card',
|
|
30
|
+
GC: 'Green Card',
|
|
31
|
+
HC: 'Health Card',
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const getDocumentLabel = (type: string): string => {
|
|
35
|
+
return DOCUMENT_LABELS[type] || type;
|
|
36
|
+
};
|
|
37
|
+
|