@test-web/react-native-sdk 1.0.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 +58 -0
- package/android/build.gradle +50 -0
- package/android/src/main/AndroidManifest.xml +24 -0
- package/android/src/main/java/com/yourcompany/sdk/YourSDKModule.java +40 -0
- package/android/src/main/java/com/yourcompany/sdk/YourSDKPackage.java +24 -0
- package/ios/YourSDK-Bridging-Header.h +1 -0
- package/ios/YourSDK-Info.plist +30 -0
- package/ios/YourSDK.podspec +19 -0
- package/ios/YourSDKModule.h +5 -0
- package/ios/YourSDKModule.m +32 -0
- package/ios/YourSDKModule.swift +22 -0
- package/package.json +38 -0
- package/src/apis/index.ts +32 -0
- package/src/components/common/CustomOverlay.tsx +44 -0
- package/src/components/common/Footer.tsx +30 -0
- package/src/components/common/Header.tsx +29 -0
- package/src/components/common/Loader.tsx +23 -0
- package/src/components/index.tsx +6 -0
- package/src/components/ui/Button.tsx +41 -0
- package/src/components/ui/ThemedText.tsx +42 -0
- package/src/components/ui/index.tsx +2 -0
- package/src/context/IDMConfigurationContext.tsx +44 -0
- package/src/context/KeyboardContext.tsx +61 -0
- package/src/context/ThemeContext.tsx +34 -0
- package/src/context/themes.ts +134 -0
- package/src/hooks/useOrientation.ts +25 -0
- package/src/index.tsx +110 -0
- package/src/native/NativeModule.ts +10 -0
- package/src/screens/BackDocumentAdvice.tsx +52 -0
- package/src/screens/BarcodeAdvice.tsx +52 -0
- package/src/screens/BarcodeCapture.tsx +66 -0
- package/src/screens/CameraPermission.tsx +74 -0
- package/src/screens/DocumentCaptureBack.tsx +65 -0
- package/src/screens/DocumentCaptureFront.tsx +65 -0
- package/src/screens/FrontDocumentAdvice.tsx +52 -0
- package/src/screens/LocationPermission.tsx +74 -0
- package/src/screens/MrzAdvice.tsx +52 -0
- package/src/screens/MrzCapture.tsx +64 -0
- package/src/screens/NoCameraFound.tsx +64 -0
- package/src/screens/RetakeSelfie.tsx +56 -0
- package/src/screens/SelectDocuments.tsx +250 -0
- package/src/screens/SelfieAdvice.tsx +35 -0
- package/src/screens/SelfieCapture.tsx +56 -0
- package/src/screens/ThankYou.tsx +23 -0
- package/src/screens/VerifyIdentity.tsx +54 -0
- package/src/screens/index.tsx +17 -0
- package/src/styles/BarcodeAdviceStyles.ts +45 -0
- package/src/styles/DocumentAdviceStyles.ts +44 -0
- package/src/styles/DocumentCaptureStyles.ts +55 -0
- package/src/styles/PermissionStyle.ts +61 -0
- package/src/styles/RetakeStyles.ts +67 -0
- package/src/styles/ScannerStyles.ts +28 -0
- package/src/styles/SelectDocumentsStyles.ts +90 -0
- package/src/styles/SelfieAdviceStyles.ts +61 -0
- package/src/styles/SelfieCaptureStyles.ts +48 -0
- package/src/styles/ThankYouStyles.ts +31 -0
- package/src/styles/VerifyIdentityStyles.ts +86 -0
- package/src/types/IDMConf.ts +28 -0
- package/src/types/index.ts +12 -0
- package/src/utils/index.ts +19 -0
- package/src/utils/metadata_new.json +1 -0
- package/src/utils/performance.ts +176 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
createContext,
|
|
3
|
+
useContext,
|
|
4
|
+
useState,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useCallback,
|
|
8
|
+
ReactNode,
|
|
9
|
+
} from 'react';
|
|
10
|
+
import { Keyboard } from 'react-native';
|
|
11
|
+
|
|
12
|
+
interface KeyboardContextType {
|
|
13
|
+
isKeyboardVisible: boolean;
|
|
14
|
+
setKeyboardVisible: (visible: boolean) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const KeyboardContext = createContext<KeyboardContextType | undefined>(
|
|
18
|
+
undefined
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
interface KeyboardProviderProps {
|
|
22
|
+
children: ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function KeyboardProvider({ children }: KeyboardProviderProps) {
|
|
26
|
+
const [isKeyboardVisible, setKeyboardVisible] = useState(false);
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const showSubscription = Keyboard.addListener('keyboardDidShow', () => {
|
|
30
|
+
setKeyboardVisible(true);
|
|
31
|
+
});
|
|
32
|
+
const hideSubscription = Keyboard.addListener('keyboardDidHide', () => {
|
|
33
|
+
setKeyboardVisible(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
showSubscription.remove();
|
|
38
|
+
hideSubscription.remove();
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
// Memoize context value to prevent unnecessary re-renders
|
|
43
|
+
const value = useMemo(
|
|
44
|
+
() => ({ isKeyboardVisible, setKeyboardVisible }),
|
|
45
|
+
[isKeyboardVisible]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<KeyboardContext.Provider value={value}>
|
|
50
|
+
{children}
|
|
51
|
+
</KeyboardContext.Provider>
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function useKeyboard() {
|
|
56
|
+
const context = useContext(KeyboardContext);
|
|
57
|
+
if (!context) {
|
|
58
|
+
throw new Error('useKeyboard must be used within KeyboardProvider');
|
|
59
|
+
}
|
|
60
|
+
return context;
|
|
61
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Theme } from '../types';
|
|
3
|
+
import { themes } from './themes';
|
|
4
|
+
|
|
5
|
+
interface ThemeContextType {
|
|
6
|
+
theme: Theme;
|
|
7
|
+
setTheme: (theme: Theme) => void;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const ThemeContext = React.createContext<ThemeContextType>({
|
|
11
|
+
theme: themes.light,
|
|
12
|
+
setTheme: () => {},
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const useTheme = () => React.useContext(ThemeContext);
|
|
16
|
+
|
|
17
|
+
export const ThemeProvider = ({
|
|
18
|
+
children,
|
|
19
|
+
theme: initialTheme = themes.light,
|
|
20
|
+
}: {
|
|
21
|
+
children: React.ReactNode;
|
|
22
|
+
theme?: Theme;
|
|
23
|
+
}) => {
|
|
24
|
+
const [theme, setTheme] = React.useState(initialTheme);
|
|
25
|
+
|
|
26
|
+
// Memoize context value to prevent unnecessary re-renders
|
|
27
|
+
const value = React.useMemo(() => ({ theme, setTheme }), [theme]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ThemeContext.Provider value={value}>
|
|
31
|
+
{children}
|
|
32
|
+
</ThemeContext.Provider>
|
|
33
|
+
);
|
|
34
|
+
};
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { Theme } from '../types';
|
|
2
|
+
|
|
3
|
+
// 🌞 Light (default)
|
|
4
|
+
export const lightTheme: Theme = {
|
|
5
|
+
colors: {
|
|
6
|
+
primary: '#007bff',
|
|
7
|
+
background: '#ffffff',
|
|
8
|
+
text: '#111827',
|
|
9
|
+
subtitle: '#6b7280',
|
|
10
|
+
buttonText: '#ffffff',
|
|
11
|
+
buttonBackground: '#007bff',
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// 🌑 Dark (default)
|
|
16
|
+
export const darkTheme: Theme = {
|
|
17
|
+
colors: {
|
|
18
|
+
primary: '#3b82f6',
|
|
19
|
+
background: '#111827',
|
|
20
|
+
text: '#f9fafb',
|
|
21
|
+
subtitle: '#9ca3af',
|
|
22
|
+
buttonText: '#111827',
|
|
23
|
+
buttonBackground: '#3b82f6',
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// 🌅 Sunset (warm oranges)
|
|
28
|
+
export const sunsetTheme: Theme = {
|
|
29
|
+
colors: {
|
|
30
|
+
primary: '#f97316',
|
|
31
|
+
background: '#fff7ed',
|
|
32
|
+
text: '#7c2d12',
|
|
33
|
+
subtitle: '#b45309',
|
|
34
|
+
buttonText: '#ffffff',
|
|
35
|
+
buttonBackground: '#f97316',
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
// 🌊 Ocean (blues & teal)
|
|
40
|
+
export const oceanTheme: Theme = {
|
|
41
|
+
colors: {
|
|
42
|
+
primary: '#0284c7',
|
|
43
|
+
background: '#f0f9ff',
|
|
44
|
+
text: '#0c4a6e',
|
|
45
|
+
subtitle: '#0369a1',
|
|
46
|
+
buttonText: '#ffffff',
|
|
47
|
+
buttonBackground: '#0284c7',
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// 🌿 Forest (greens)
|
|
52
|
+
export const forestTheme: Theme = {
|
|
53
|
+
colors: {
|
|
54
|
+
primary: '#16a34a',
|
|
55
|
+
background: '#ffffff',
|
|
56
|
+
text: '#16a34a',
|
|
57
|
+
subtitle: '#16a34a',
|
|
58
|
+
buttonText: '#ffffff',
|
|
59
|
+
buttonBackground: '#3fa564',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
// 🎨 Pastel (lavender & purple)
|
|
64
|
+
export const pastelTheme: Theme = {
|
|
65
|
+
colors: {
|
|
66
|
+
primary: '#a78bfa',
|
|
67
|
+
background: '#faf5ff',
|
|
68
|
+
text: '#4c1d95',
|
|
69
|
+
subtitle: '#7c3aed',
|
|
70
|
+
buttonText: '#ffffff',
|
|
71
|
+
buttonBackground: '#a78bfa',
|
|
72
|
+
},
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
// ⚡ High Contrast (accessibility)
|
|
76
|
+
export const highContrastTheme: Theme = {
|
|
77
|
+
colors: {
|
|
78
|
+
primary: '#ffcc00',
|
|
79
|
+
background: '#000000',
|
|
80
|
+
text: '#ffffff',
|
|
81
|
+
subtitle: '#ffcc00',
|
|
82
|
+
buttonText: '#000000',
|
|
83
|
+
buttonBackground: '#ffcc00',
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
// 💼 Professional (neutral + blue accent)
|
|
88
|
+
export const professionalTheme: Theme = {
|
|
89
|
+
colors: {
|
|
90
|
+
primary: '#2563eb',
|
|
91
|
+
background: '#f9fafb',
|
|
92
|
+
text: '#1f2937',
|
|
93
|
+
subtitle: '#4b5563',
|
|
94
|
+
buttonText: '#ffffff',
|
|
95
|
+
buttonBackground: '#2563eb',
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// 🌸 Sakura (soft pink)
|
|
100
|
+
export const sakuraTheme: Theme = {
|
|
101
|
+
colors: {
|
|
102
|
+
primary: '#ec4899',
|
|
103
|
+
background: '#fff0f6',
|
|
104
|
+
text: '#831843',
|
|
105
|
+
subtitle: '#db2777',
|
|
106
|
+
buttonText: '#ffffff',
|
|
107
|
+
buttonBackground: '#ec4899',
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// 🌌 Midnight (deep purples)
|
|
112
|
+
export const midnightTheme: Theme = {
|
|
113
|
+
colors: {
|
|
114
|
+
primary: '#7c3aed',
|
|
115
|
+
background: '#1e1b4b',
|
|
116
|
+
text: '#f5f3ff',
|
|
117
|
+
subtitle: '#a78bfa',
|
|
118
|
+
buttonText: '#1e1b4b',
|
|
119
|
+
buttonBackground: '#7c3aed',
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const themes: Record<string, Theme> = {
|
|
124
|
+
light: lightTheme,
|
|
125
|
+
dark: darkTheme,
|
|
126
|
+
sunset: sunsetTheme,
|
|
127
|
+
ocean: oceanTheme,
|
|
128
|
+
forest: forestTheme,
|
|
129
|
+
pastel: pastelTheme,
|
|
130
|
+
highContrast: highContrastTheme,
|
|
131
|
+
professional: professionalTheme,
|
|
132
|
+
sakura: sakuraTheme,
|
|
133
|
+
midnight: midnightTheme,
|
|
134
|
+
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { Dimensions } from 'react-native';
|
|
3
|
+
|
|
4
|
+
export type Orientation = 'portrait' | 'landscape';
|
|
5
|
+
|
|
6
|
+
export function useOrientation(): Orientation {
|
|
7
|
+
const [orientation, setOrientation] = useState<Orientation>(
|
|
8
|
+
getOrientation()
|
|
9
|
+
);
|
|
10
|
+
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const subscription = Dimensions.addEventListener('change', () => {
|
|
13
|
+
setOrientation(getOrientation());
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return () => subscription?.remove();
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
19
|
+
return orientation;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function getOrientation(): Orientation {
|
|
23
|
+
const { width, height } = Dimensions.get('window');
|
|
24
|
+
return width > height ? 'landscape' : 'portrait';
|
|
25
|
+
}
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import React, { useState, useMemo, useCallback } from 'react';
|
|
2
|
+
import { NavigationContainer, useNavigationContainerRef } from '@react-navigation/native';
|
|
3
|
+
import { createNativeStackNavigator } from '@react-navigation/native-stack';
|
|
4
|
+
import BarcodeCapture from './screens/BarcodeCapture';
|
|
5
|
+
import FrontDocumentAdvice from './screens/FrontDocumentAdvice';
|
|
6
|
+
import BackDocumentAdvice from './screens/BackDocumentAdvice';
|
|
7
|
+
import BarcodeAdvice from './screens/BarcodeAdvice';
|
|
8
|
+
import DocumentCaptureFront from './screens/DocumentCaptureFront';
|
|
9
|
+
import DocumentCaptureBack from './screens/DocumentCaptureBack';
|
|
10
|
+
import SelectDocuments from './screens/SelectDocuments';
|
|
11
|
+
import SelfieAdvice from './screens/SelfieAdvice';
|
|
12
|
+
import SelfieCapture from './screens/SelfieCapture';
|
|
13
|
+
import ThankYou from './screens/ThankYou';
|
|
14
|
+
import VerifyIdentity from './screens/VerifyIdentity';
|
|
15
|
+
import RetakeSelfie from './screens/RetakeSelfie';
|
|
16
|
+
import LocationPermission from './screens/LocationPermission';
|
|
17
|
+
import CameraPermission from './screens/CameraPermission';
|
|
18
|
+
import NoCameraFound from './screens/NoCameraFound';
|
|
19
|
+
import MrzCapture from './screens/MrzCapture';
|
|
20
|
+
import MrzAdvice from './screens/MrzAdvice';
|
|
21
|
+
import { ThemeProvider } from './context/ThemeContext';
|
|
22
|
+
import { themes } from './context/themes';
|
|
23
|
+
import { IDMConf } from './types/IDMConf';
|
|
24
|
+
import { IDMProvider } from './context/IDMConfigurationContext';
|
|
25
|
+
import { KeyboardProvider, useKeyboard } from './context/KeyboardContext';
|
|
26
|
+
import Footer from './components/common/Footer';
|
|
27
|
+
import Header from './components/common/Header';
|
|
28
|
+
|
|
29
|
+
const Stack = createNativeStackNavigator();
|
|
30
|
+
|
|
31
|
+
// Screen options constant to avoid recreation
|
|
32
|
+
const SCREEN_OPTIONS = { headerShown: false };
|
|
33
|
+
|
|
34
|
+
// Routes where header/footer should be hidden
|
|
35
|
+
const HIDE_HEADER_FOOTER_ROUTES = new Set([
|
|
36
|
+
'SelfieCapture',
|
|
37
|
+
'DocumentCaptureFront',
|
|
38
|
+
'DocumentCaptureBack',
|
|
39
|
+
'BarcodeCapture',
|
|
40
|
+
'MrzCapture',
|
|
41
|
+
]);
|
|
42
|
+
|
|
43
|
+
interface IDMScanProps {
|
|
44
|
+
idmConf?: IDMConf;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export default function IDMScan({ idmConf = {} }: IDMScanProps) {
|
|
48
|
+
return (
|
|
49
|
+
<IDMProvider initialConf={idmConf}>
|
|
50
|
+
<KeyboardProvider>
|
|
51
|
+
<IDMScanContent idmConf={idmConf} />
|
|
52
|
+
</KeyboardProvider>
|
|
53
|
+
</IDMProvider>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function IDMScanContent({ idmConf }: { idmConf: IDMConf }) {
|
|
58
|
+
const { isKeyboardVisible } = useKeyboard();
|
|
59
|
+
const navigationRef = useNavigationContainerRef();
|
|
60
|
+
const [currentRoute, setCurrentRoute] = useState<string | undefined>();
|
|
61
|
+
|
|
62
|
+
// Memoize theme calculation
|
|
63
|
+
const theme = useMemo(
|
|
64
|
+
() =>
|
|
65
|
+
typeof idmConf.theme === 'string'
|
|
66
|
+
? themes[idmConf.theme] || themes.light
|
|
67
|
+
: idmConf.theme || themes.light,
|
|
68
|
+
[idmConf.theme]
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
// Memoize header/footer visibility
|
|
72
|
+
const shouldShowHeaderFooter = useMemo(
|
|
73
|
+
() => !HIDE_HEADER_FOOTER_ROUTES.has(currentRoute || ''),
|
|
74
|
+
[currentRoute]
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Optimize navigation state change handler
|
|
78
|
+
const handleStateChange = useCallback(() => {
|
|
79
|
+
const route = navigationRef.getCurrentRoute();
|
|
80
|
+
setCurrentRoute(route?.name);
|
|
81
|
+
}, [navigationRef]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<ThemeProvider theme={theme}>
|
|
85
|
+
{shouldShowHeaderFooter && <Header />}
|
|
86
|
+
<NavigationContainer ref={navigationRef} onStateChange={handleStateChange}>
|
|
87
|
+
<Stack.Navigator initialRouteName="VerifyIdentity" screenOptions={SCREEN_OPTIONS}>
|
|
88
|
+
<Stack.Screen name="VerifyIdentity" component={VerifyIdentity} />
|
|
89
|
+
<Stack.Screen name="SelfieAdvice" component={SelfieAdvice} />
|
|
90
|
+
<Stack.Screen name="SelfieCapture" component={SelfieCapture} />
|
|
91
|
+
<Stack.Screen name="SelectDocuments" component={SelectDocuments} />
|
|
92
|
+
<Stack.Screen name="FrontDocumentAdvice" component={FrontDocumentAdvice} />
|
|
93
|
+
<Stack.Screen name="BackDocumentAdvice" component={BackDocumentAdvice} />
|
|
94
|
+
<Stack.Screen name="BarcodeAdvice" component={BarcodeAdvice} />
|
|
95
|
+
<Stack.Screen name="DocumentCaptureFront" component={DocumentCaptureFront} />
|
|
96
|
+
<Stack.Screen name="DocumentCaptureBack" component={DocumentCaptureBack} />
|
|
97
|
+
<Stack.Screen name="BarcodeCapture" component={BarcodeCapture} />
|
|
98
|
+
<Stack.Screen name="ThankYou" component={ThankYou} />
|
|
99
|
+
<Stack.Screen name="RetakeSelfie" component={RetakeSelfie} />
|
|
100
|
+
<Stack.Screen name="LocationPermission" component={LocationPermission} />
|
|
101
|
+
<Stack.Screen name="CameraPermission" component={CameraPermission} />
|
|
102
|
+
<Stack.Screen name="NoCameraFound" component={NoCameraFound} />
|
|
103
|
+
<Stack.Screen name="MrzCapture" component={MrzCapture} />
|
|
104
|
+
<Stack.Screen name="MrzAdvice" component={MrzAdvice} />
|
|
105
|
+
</Stack.Navigator>
|
|
106
|
+
</NavigationContainer>
|
|
107
|
+
{!isKeyboardVisible && shouldShowHeaderFooter && <Footer />}
|
|
108
|
+
</ThemeProvider>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { NativeModules } from 'react-native';
|
|
2
|
+
|
|
3
|
+
const { YourSDKModule } = NativeModules;
|
|
4
|
+
|
|
5
|
+
export interface NativeSDKModule {
|
|
6
|
+
initialize(apiKey: string): Promise<string>;
|
|
7
|
+
performAction(action: string): Promise<string>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export default YourSDKModule as NativeSDKModule;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useNavigation } from '@react-navigation/native';
|
|
2
|
+
import { Image, ScrollView, View } from 'react-native';
|
|
3
|
+
import Button from '../components/ui/Button';
|
|
4
|
+
import ThemedText from '../components/ui/ThemedText';
|
|
5
|
+
import getStyles from '../styles/DocumentAdviceStyles';
|
|
6
|
+
import { useTheme } from '../context/ThemeContext';
|
|
7
|
+
import { useOrientation } from '../hooks/useOrientation';
|
|
8
|
+
import { useMemo } from 'react';
|
|
9
|
+
|
|
10
|
+
export default function BackDocumentAdvice() {
|
|
11
|
+
const { theme } = useTheme();
|
|
12
|
+
const orientation = useOrientation();
|
|
13
|
+
const navigation = useNavigation();
|
|
14
|
+
|
|
15
|
+
const styles = useMemo(
|
|
16
|
+
() => getStyles(theme, orientation),
|
|
17
|
+
[theme, orientation]
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const handleRedirect = () => {
|
|
21
|
+
navigation.navigate('DocumentCaptureBack' as never);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ScrollView contentContainerStyle={styles.container}>
|
|
26
|
+
<ThemedText style={styles.maintitle} type="title">
|
|
27
|
+
Capture Your ID Back
|
|
28
|
+
</ThemedText>
|
|
29
|
+
|
|
30
|
+
{/* Placeholder for ID card back image */}
|
|
31
|
+
<View
|
|
32
|
+
style={[
|
|
33
|
+
styles.idcard,
|
|
34
|
+
{
|
|
35
|
+
backgroundColor: '#f0f0f0',
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
},
|
|
39
|
+
]}
|
|
40
|
+
>
|
|
41
|
+
<ThemedText>ID Back Image</ThemedText>
|
|
42
|
+
</View>
|
|
43
|
+
|
|
44
|
+
<Button
|
|
45
|
+
title="Take a photo"
|
|
46
|
+
style={styles.buttonplace}
|
|
47
|
+
textStyle={styles.button}
|
|
48
|
+
onPress={handleRedirect}
|
|
49
|
+
/>
|
|
50
|
+
</ScrollView>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useNavigation } from '@react-navigation/native';
|
|
2
|
+
import { ScrollView, View } from 'react-native';
|
|
3
|
+
import Button from '../components/ui/Button';
|
|
4
|
+
import ThemedText from '../components/ui/ThemedText';
|
|
5
|
+
import getStyles from '../styles/BarcodeAdviceStyles';
|
|
6
|
+
import { useTheme } from '../context/ThemeContext';
|
|
7
|
+
import { useOrientation } from '../hooks/useOrientation';
|
|
8
|
+
import { useMemo } from 'react';
|
|
9
|
+
|
|
10
|
+
export default function BarcodeAdvice() {
|
|
11
|
+
const { theme } = useTheme();
|
|
12
|
+
const orientation = useOrientation();
|
|
13
|
+
const navigation = useNavigation();
|
|
14
|
+
|
|
15
|
+
const styles = useMemo(
|
|
16
|
+
() => getStyles(theme, orientation),
|
|
17
|
+
[theme, orientation]
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
const handleRedirect = () => {
|
|
21
|
+
navigation.navigate('BarcodeCapture' as never);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<ScrollView contentContainerStyle={styles.container}>
|
|
26
|
+
<ThemedText style={styles.maintitle} type="title">
|
|
27
|
+
Scan the barcode on the back of your ID
|
|
28
|
+
</ThemedText>
|
|
29
|
+
|
|
30
|
+
{/* Placeholder for barcode image */}
|
|
31
|
+
<View
|
|
32
|
+
style={[
|
|
33
|
+
styles.idcard,
|
|
34
|
+
{
|
|
35
|
+
backgroundColor: '#f0f0f0',
|
|
36
|
+
justifyContent: 'center',
|
|
37
|
+
alignItems: 'center',
|
|
38
|
+
},
|
|
39
|
+
]}
|
|
40
|
+
>
|
|
41
|
+
<ThemedText>Barcode Image</ThemedText>
|
|
42
|
+
</View>
|
|
43
|
+
|
|
44
|
+
<Button
|
|
45
|
+
title="Scan"
|
|
46
|
+
style={styles.buttonplace}
|
|
47
|
+
textStyle={styles.button}
|
|
48
|
+
onPress={handleRedirect}
|
|
49
|
+
/>
|
|
50
|
+
</ScrollView>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, StyleSheet } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import { useTheme } from '../context/ThemeContext';
|
|
5
|
+
import { useOrientation } from '../hooks/useOrientation';
|
|
6
|
+
import getStyles from '../styles/ScannerStyles';
|
|
7
|
+
import Loader from '../components/common/Loader';
|
|
8
|
+
|
|
9
|
+
export default function BarcodeCapture() {
|
|
10
|
+
const { theme } = useTheme();
|
|
11
|
+
const orientation = useOrientation();
|
|
12
|
+
const styles = getStyles(theme, orientation);
|
|
13
|
+
const navigation = useNavigation();
|
|
14
|
+
const [loading, setLoading] = useState(false);
|
|
15
|
+
|
|
16
|
+
// Static implementation - simulate barcode scanning
|
|
17
|
+
React.useEffect(() => {
|
|
18
|
+
const timer = setTimeout(() => {
|
|
19
|
+
setLoading(true);
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
setLoading(false);
|
|
22
|
+
navigation.navigate('ThankYou' as never);
|
|
23
|
+
}, 2000);
|
|
24
|
+
}, 3000);
|
|
25
|
+
|
|
26
|
+
return () => clearTimeout(timer);
|
|
27
|
+
}, [navigation]);
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<View style={styles.container}>
|
|
31
|
+
<Text style={styles.topLabel}>
|
|
32
|
+
Fill the box with the barcode{'\n'}until it turns green
|
|
33
|
+
</Text>
|
|
34
|
+
|
|
35
|
+
{/* Camera placeholder */}
|
|
36
|
+
<View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
|
|
37
|
+
<Text
|
|
38
|
+
style={{
|
|
39
|
+
color: '#fff',
|
|
40
|
+
textAlign: 'center',
|
|
41
|
+
marginTop: 200,
|
|
42
|
+
fontSize: 16,
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
Barcode Scanner (Static)
|
|
46
|
+
</Text>
|
|
47
|
+
</View>
|
|
48
|
+
|
|
49
|
+
{/* Barcode overlay box */}
|
|
50
|
+
<View
|
|
51
|
+
style={{
|
|
52
|
+
position: 'absolute',
|
|
53
|
+
top: '40%',
|
|
54
|
+
alignSelf: 'center',
|
|
55
|
+
width: 350,
|
|
56
|
+
height: 100,
|
|
57
|
+
borderWidth: 2,
|
|
58
|
+
borderColor: '#00ff00',
|
|
59
|
+
backgroundColor: 'transparent',
|
|
60
|
+
}}
|
|
61
|
+
/>
|
|
62
|
+
|
|
63
|
+
{loading && <Loader />}
|
|
64
|
+
</View>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { useNavigation } from '@react-navigation/native';
|
|
2
|
+
import * as React from 'react';
|
|
3
|
+
import { ScrollView, Text, View, PermissionsAndroid, Platform } from 'react-native';
|
|
4
|
+
import Button from '../components/ui/Button';
|
|
5
|
+
import ThemedText from '../components/ui/ThemedText';
|
|
6
|
+
import { useTheme } from '../context/ThemeContext';
|
|
7
|
+
import getStyles from '../styles/PermissionStyle';
|
|
8
|
+
import { useOrientation } from '../hooks/useOrientation';
|
|
9
|
+
|
|
10
|
+
export default function CameraPermission() {
|
|
11
|
+
const { theme } = useTheme();
|
|
12
|
+
const orientation = useOrientation();
|
|
13
|
+
const styles = getStyles(theme, orientation);
|
|
14
|
+
const navigation = useNavigation();
|
|
15
|
+
|
|
16
|
+
const requestCameraPermission = async () => {
|
|
17
|
+
if (Platform.OS === 'android') {
|
|
18
|
+
try {
|
|
19
|
+
const granted = await PermissionsAndroid.request(
|
|
20
|
+
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
21
|
+
{
|
|
22
|
+
title: 'Camera Permission',
|
|
23
|
+
message: 'This app needs access to your camera to take selfies.',
|
|
24
|
+
buttonPositive: 'OK',
|
|
25
|
+
}
|
|
26
|
+
);
|
|
27
|
+
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
|
|
28
|
+
if (navigation.canGoBack()) {
|
|
29
|
+
navigation.goBack();
|
|
30
|
+
} else {
|
|
31
|
+
navigation.navigate('VerifyIdentity' as never);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.warn(err);
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
// For iOS, navigate directly (implement iOS permission separately)
|
|
39
|
+
if (navigation.canGoBack()) {
|
|
40
|
+
navigation.goBack();
|
|
41
|
+
} else {
|
|
42
|
+
navigation.navigate('VerifyIdentity' as never);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<ScrollView contentContainerStyle={styles.container}>
|
|
49
|
+
<ThemedText style={styles.maintitle} type="title">
|
|
50
|
+
Camera Access Required
|
|
51
|
+
</ThemedText>
|
|
52
|
+
|
|
53
|
+
<View style={{ padding: 20 }}>
|
|
54
|
+
<Text
|
|
55
|
+
style={{
|
|
56
|
+
color: theme.colors.text,
|
|
57
|
+
textAlign: 'center',
|
|
58
|
+
marginBottom: 20,
|
|
59
|
+
}}
|
|
60
|
+
>
|
|
61
|
+
This app needs access to your camera to continue. Please grant camera
|
|
62
|
+
permission.
|
|
63
|
+
</Text>
|
|
64
|
+
</View>
|
|
65
|
+
|
|
66
|
+
<Button
|
|
67
|
+
title="Grant Permission"
|
|
68
|
+
style={styles.buttonplace}
|
|
69
|
+
textStyle={styles.button}
|
|
70
|
+
onPress={requestCameraPermission}
|
|
71
|
+
/>
|
|
72
|
+
</ScrollView>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet } from 'react-native';
|
|
3
|
+
import { useNavigation } from '@react-navigation/native';
|
|
4
|
+
import getStyles from '../styles/DocumentCaptureStyles';
|
|
5
|
+
import { useTheme } from '../context/ThemeContext';
|
|
6
|
+
import { useOrientation } from '../hooks/useOrientation';
|
|
7
|
+
import Loader from '../components/common/Loader';
|
|
8
|
+
|
|
9
|
+
export default function DocumentCaptureBack() {
|
|
10
|
+
const { theme } = useTheme();
|
|
11
|
+
const orientation = useOrientation();
|
|
12
|
+
const styles = getStyles(theme, orientation);
|
|
13
|
+
const navigation = useNavigation();
|
|
14
|
+
const [loading, setLoading] = useState(false);
|
|
15
|
+
|
|
16
|
+
const takePhoto = async () => {
|
|
17
|
+
try {
|
|
18
|
+
setLoading(true);
|
|
19
|
+
// Static implementation - simulate photo capture
|
|
20
|
+
setTimeout(() => {
|
|
21
|
+
setLoading(false);
|
|
22
|
+
// Navigate to ThankYou after capturing back
|
|
23
|
+
navigation.navigate('ThankYou' as never);
|
|
24
|
+
}, 1000);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('Error taking photo:', error);
|
|
27
|
+
setLoading(false);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<View style={styles.container}>
|
|
33
|
+
<Text style={styles.topLabel}>Document Back Side</Text>
|
|
34
|
+
|
|
35
|
+
{/* Camera placeholder */}
|
|
36
|
+
<View style={StyleSheet.absoluteFill}>
|
|
37
|
+
<View
|
|
38
|
+
style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}
|
|
39
|
+
></View>
|
|
40
|
+
</View>
|
|
41
|
+
|
|
42
|
+
{/* Card overlay placeholder */}
|
|
43
|
+
<View
|
|
44
|
+
style={[
|
|
45
|
+
styles.cardoverlay,
|
|
46
|
+
{
|
|
47
|
+
borderWidth: 2,
|
|
48
|
+
borderColor: '#fff',
|
|
49
|
+
backgroundColor: 'transparent',
|
|
50
|
+
},
|
|
51
|
+
]}
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
<Text style={styles.bottomLabel}>
|
|
55
|
+
Position your document fully within the frame
|
|
56
|
+
</Text>
|
|
57
|
+
|
|
58
|
+
<View style={styles.buttonplace}>
|
|
59
|
+
<TouchableOpacity style={styles.captureButton} onPress={takePhoto} />
|
|
60
|
+
</View>
|
|
61
|
+
|
|
62
|
+
{loading && <Loader />}
|
|
63
|
+
</View>
|
|
64
|
+
);
|
|
65
|
+
}
|