@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.
Files changed (62) hide show
  1. package/README.md +58 -0
  2. package/android/build.gradle +50 -0
  3. package/android/src/main/AndroidManifest.xml +24 -0
  4. package/android/src/main/java/com/yourcompany/sdk/YourSDKModule.java +40 -0
  5. package/android/src/main/java/com/yourcompany/sdk/YourSDKPackage.java +24 -0
  6. package/ios/YourSDK-Bridging-Header.h +1 -0
  7. package/ios/YourSDK-Info.plist +30 -0
  8. package/ios/YourSDK.podspec +19 -0
  9. package/ios/YourSDKModule.h +5 -0
  10. package/ios/YourSDKModule.m +32 -0
  11. package/ios/YourSDKModule.swift +22 -0
  12. package/package.json +38 -0
  13. package/src/apis/index.ts +32 -0
  14. package/src/components/common/CustomOverlay.tsx +44 -0
  15. package/src/components/common/Footer.tsx +30 -0
  16. package/src/components/common/Header.tsx +29 -0
  17. package/src/components/common/Loader.tsx +23 -0
  18. package/src/components/index.tsx +6 -0
  19. package/src/components/ui/Button.tsx +41 -0
  20. package/src/components/ui/ThemedText.tsx +42 -0
  21. package/src/components/ui/index.tsx +2 -0
  22. package/src/context/IDMConfigurationContext.tsx +44 -0
  23. package/src/context/KeyboardContext.tsx +61 -0
  24. package/src/context/ThemeContext.tsx +34 -0
  25. package/src/context/themes.ts +134 -0
  26. package/src/hooks/useOrientation.ts +25 -0
  27. package/src/index.tsx +110 -0
  28. package/src/native/NativeModule.ts +10 -0
  29. package/src/screens/BackDocumentAdvice.tsx +52 -0
  30. package/src/screens/BarcodeAdvice.tsx +52 -0
  31. package/src/screens/BarcodeCapture.tsx +66 -0
  32. package/src/screens/CameraPermission.tsx +74 -0
  33. package/src/screens/DocumentCaptureBack.tsx +65 -0
  34. package/src/screens/DocumentCaptureFront.tsx +65 -0
  35. package/src/screens/FrontDocumentAdvice.tsx +52 -0
  36. package/src/screens/LocationPermission.tsx +74 -0
  37. package/src/screens/MrzAdvice.tsx +52 -0
  38. package/src/screens/MrzCapture.tsx +64 -0
  39. package/src/screens/NoCameraFound.tsx +64 -0
  40. package/src/screens/RetakeSelfie.tsx +56 -0
  41. package/src/screens/SelectDocuments.tsx +250 -0
  42. package/src/screens/SelfieAdvice.tsx +35 -0
  43. package/src/screens/SelfieCapture.tsx +56 -0
  44. package/src/screens/ThankYou.tsx +23 -0
  45. package/src/screens/VerifyIdentity.tsx +54 -0
  46. package/src/screens/index.tsx +17 -0
  47. package/src/styles/BarcodeAdviceStyles.ts +45 -0
  48. package/src/styles/DocumentAdviceStyles.ts +44 -0
  49. package/src/styles/DocumentCaptureStyles.ts +55 -0
  50. package/src/styles/PermissionStyle.ts +61 -0
  51. package/src/styles/RetakeStyles.ts +67 -0
  52. package/src/styles/ScannerStyles.ts +28 -0
  53. package/src/styles/SelectDocumentsStyles.ts +90 -0
  54. package/src/styles/SelfieAdviceStyles.ts +61 -0
  55. package/src/styles/SelfieCaptureStyles.ts +48 -0
  56. package/src/styles/ThankYouStyles.ts +31 -0
  57. package/src/styles/VerifyIdentityStyles.ts +86 -0
  58. package/src/types/IDMConf.ts +28 -0
  59. package/src/types/index.ts +12 -0
  60. package/src/utils/index.ts +19 -0
  61. package/src/utils/metadata_new.json +1 -0
  62. package/src/utils/performance.ts +176 -0
@@ -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 DocumentCaptureFront() {
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 BackDocumentAdvice after capturing front
23
+ navigation.navigate('BackDocumentAdvice' 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 Front 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
+ }
@@ -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 FrontDocumentAdvice() {
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('DocumentCaptureFront' as never);
22
+ };
23
+
24
+ return (
25
+ <ScrollView contentContainerStyle={styles.container}>
26
+ <ThemedText style={styles.maintitle} type="title">
27
+ Capture Your ID Front
28
+ </ThemedText>
29
+
30
+ {/* Placeholder for ID card image */}
31
+ <View
32
+ style={[
33
+ styles.idcard,
34
+ {
35
+ backgroundColor: '#f0f0f0',
36
+ justifyContent: 'center',
37
+ alignItems: 'center',
38
+ },
39
+ ]}
40
+ >
41
+ <ThemedText>ID Front 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,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 LocationPermission() {
11
+ const { theme } = useTheme();
12
+ const orientation = useOrientation();
13
+ const styles = getStyles(theme, orientation);
14
+ const navigation = useNavigation();
15
+
16
+ const requestLocationPermission = async () => {
17
+ if (Platform.OS === 'android') {
18
+ try {
19
+ const granted = await PermissionsAndroid.request(
20
+ PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
21
+ {
22
+ title: 'Location Permission',
23
+ message: 'This app needs access to your location.',
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
+ Location 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 location to function correctly. Please
62
+ grant location permission.
63
+ </Text>
64
+ </View>
65
+
66
+ <Button
67
+ title="Grant Permission"
68
+ style={styles.buttonplace}
69
+ textStyle={styles.button}
70
+ onPress={requestLocationPermission}
71
+ />
72
+ </ScrollView>
73
+ );
74
+ }
@@ -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 MrzAdvice() {
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('MrzCapture' as never);
22
+ };
23
+
24
+ return (
25
+ <ScrollView contentContainerStyle={styles.container}>
26
+ <ThemedText style={styles.maintitle} type="title">
27
+ Scan the MRZ of your ID
28
+ </ThemedText>
29
+
30
+ {/* Placeholder for MRZ image */}
31
+ <View
32
+ style={[
33
+ styles.idcard,
34
+ {
35
+ backgroundColor: '#f0f0f0',
36
+ justifyContent: 'center',
37
+ alignItems: 'center',
38
+ },
39
+ ]}
40
+ >
41
+ <ThemedText>MRZ Zone 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,64 @@
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 MrzCapture() {
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 MRZ 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}>Align the MRZ code in the box</Text>
32
+
33
+ {/* Camera placeholder */}
34
+ <View style={[StyleSheet.absoluteFill, { backgroundColor: '#000' }]}>
35
+ <Text
36
+ style={{
37
+ color: '#fff',
38
+ textAlign: 'center',
39
+ marginTop: 200,
40
+ fontSize: 16,
41
+ }}
42
+ >
43
+ MRZ Scanner (Static)
44
+ </Text>
45
+ </View>
46
+
47
+ {/* MRZ overlay box */}
48
+ <View
49
+ style={{
50
+ position: 'absolute',
51
+ top: '40%',
52
+ alignSelf: 'center',
53
+ width: 380,
54
+ height: 78,
55
+ borderWidth: 2,
56
+ borderColor: '#00ff00',
57
+ backgroundColor: 'transparent',
58
+ }}
59
+ />
60
+
61
+ {loading && <Loader />}
62
+ </View>
63
+ );
64
+ }
@@ -0,0 +1,64 @@
1
+ import * as React from 'react';
2
+ import { ScrollView, Text, View, Platform, StyleSheet } from 'react-native';
3
+ import { useTheme } from '../context/ThemeContext';
4
+ import { useOrientation } from '../hooks/useOrientation';
5
+
6
+ export default function NoCameraFound() {
7
+ const { theme } = useTheme();
8
+ const orientation = useOrientation();
9
+ const [hasCamera, setHasCamera] = React.useState(true);
10
+
11
+ React.useEffect(() => {
12
+ if (Platform.OS === 'android') {
13
+ const hasCameraOnAndroid = true;
14
+ setHasCamera(hasCameraOnAndroid);
15
+ } else {
16
+ setHasCamera(true);
17
+ }
18
+ }, []);
19
+
20
+ return (
21
+ <ScrollView
22
+ contentContainerStyle={[
23
+ styles.container,
24
+ { backgroundColor: theme.colors.background },
25
+ ]}
26
+ >
27
+ <View style={{ padding: 20 }}>
28
+ {!hasCamera && (
29
+ <Text
30
+ style={{
31
+ color: theme.colors.text,
32
+ textAlign: 'center',
33
+ marginBottom: 20,
34
+ fontSize: 18,
35
+ }}
36
+ >
37
+ No camera found on this device. This feature requires a camera to
38
+ function.
39
+ </Text>
40
+ )}
41
+ {hasCamera && (
42
+ <Text
43
+ style={{
44
+ color: theme.colors.text,
45
+ textAlign: 'center',
46
+ marginBottom: 20,
47
+ fontSize: 18,
48
+ }}
49
+ >
50
+ Camera is available on this device.
51
+ </Text>
52
+ )}
53
+ </View>
54
+ </ScrollView>
55
+ );
56
+ }
57
+
58
+ const styles = StyleSheet.create({
59
+ container: {
60
+ flex: 1,
61
+ justifyContent: 'center',
62
+ alignItems: 'center',
63
+ },
64
+ });
@@ -0,0 +1,56 @@
1
+ import { useNavigation, useRoute } from '@react-navigation/native';
2
+ import * as React from 'react';
3
+ import { ScrollView, Text, View } from 'react-native';
4
+ import Button from '../components/ui/Button';
5
+ import { useTheme } from '../context/ThemeContext';
6
+ import getStyles from '../styles/RetakeStyles';
7
+ import { useOrientation } from '../hooks/useOrientation';
8
+
9
+ interface RouteParams {
10
+ errorMessage?: string;
11
+ }
12
+
13
+ export default function RetakeSelfie() {
14
+ const { theme } = useTheme();
15
+ const orientation = useOrientation();
16
+ const styles = getStyles(theme, orientation);
17
+ const navigation = useNavigation();
18
+ const route = useRoute();
19
+ const params = route.params as RouteParams | undefined;
20
+ const errorMessage = params?.errorMessage;
21
+
22
+ const handleRetake = () => {
23
+ if (navigation.canGoBack()) {
24
+ navigation.goBack();
25
+ } else {
26
+ navigation.navigate('SelfieCapture' as never);
27
+ }
28
+ };
29
+
30
+ return (
31
+ <ScrollView contentContainerStyle={styles.container}>
32
+ <Text style={styles.title}>
33
+ Sorry we were unable to register your selfie.
34
+ </Text>
35
+ <Text style={styles.subtitle}>Please try again</Text>
36
+ <Text style={styles.errorMessage}>
37
+ Error: {errorMessage || 'Failed to verify selfie. Please try again.'}
38
+ </Text>
39
+ <Text style={styles.guideTitle}>Guide for Perfect Selfie Capture</Text>
40
+ <View style={styles.guideList}>
41
+ <Text style={styles.bullet}>
42
+ • Please bring your face close to the camera.
43
+ </Text>
44
+ <Text style={styles.bullet}>
45
+ • Please ensure your face covers the frame.
46
+ </Text>
47
+ </View>
48
+ <Button
49
+ title="Retake"
50
+ style={styles.buttonplace}
51
+ textStyle={styles.button}
52
+ onPress={handleRetake}
53
+ />
54
+ </ScrollView>
55
+ );
56
+ }
@@ -0,0 +1,250 @@
1
+ import { useNavigation } from '@react-navigation/native';
2
+ import React, { useState, useMemo, useCallback } from 'react';
3
+ import {
4
+ Alert,
5
+ Pressable,
6
+ ScrollView,
7
+ Text,
8
+ TextInput,
9
+ View,
10
+ } from 'react-native';
11
+ import Button from '../components/ui/Button';
12
+ import ThemedText from '../components/ui/ThemedText';
13
+ import getStyles from '../styles/SelectDocumentsStyles';
14
+ import { useTheme } from '../context/ThemeContext';
15
+ import { useOrientation } from '../hooks/useOrientation';
16
+ import { useIDM } from '../context/IDMConfigurationContext';
17
+ import { useKeyboard } from '../context/KeyboardContext';
18
+
19
+ // Static country data for demo
20
+ const STATIC_COUNTRIES = [
21
+ {
22
+ label: 'United States',
23
+ value: 'US',
24
+ metadata: [
25
+ { type: 'DL', id: '1' },
26
+ { type: 'PP', id: '2' },
27
+ { type: 'NI', id: '3' },
28
+ ],
29
+ },
30
+ {
31
+ label: 'Canada',
32
+ value: 'CA',
33
+ metadata: [
34
+ { type: 'DL', id: '4' },
35
+ { type: 'PP', id: '5' },
36
+ ],
37
+ },
38
+ {
39
+ label: 'United Kingdom',
40
+ value: 'GB',
41
+ metadata: [
42
+ { type: 'DL', id: '6' },
43
+ { type: 'PP', id: '7' },
44
+ { type: 'NI', id: '8' },
45
+ ],
46
+ },
47
+ ];
48
+
49
+ // Document type labels mapping
50
+ const DOCUMENT_LABELS: Record<string, string> = {
51
+ DL: "Driver's License",
52
+ PP: 'Passport',
53
+ NI: 'National ID',
54
+ PC: 'Passport Card',
55
+ GC: 'Green Card',
56
+ };
57
+
58
+ export default function SelectDocuments() {
59
+ const { theme } = useTheme();
60
+ const { isKeyboardVisible } = useKeyboard();
61
+ const { idmConf, updateIDMConf } = useIDM();
62
+ const orientation = useOrientation();
63
+ const styles = getStyles(theme, orientation);
64
+ const navigation = useNavigation();
65
+ const [filteredCountries, setFilteredCountries] = useState<any[]>(STATIC_COUNTRIES);
66
+ const [search, setSearch] = useState('');
67
+ const [selectedCountry, setSelectedCountry] = useState<string | null>(null);
68
+ const [dropdownVisible, setDropdownVisible] = useState(false);
69
+ const [selectedIdType, setSelectedIdType] = useState<string | null>(null);
70
+ const [idTypeDropdownVisible, setIdTypeDropdownVisible] = useState(false);
71
+
72
+ // Memoize ID card types based on selected country
73
+ const idCardTypes = useMemo(() => {
74
+ if (!selectedCountry) return [];
75
+ const country = STATIC_COUNTRIES.find((c) => c.value === selectedCountry);
76
+ return country?.metadata.map((doc) => ({
77
+ label: DOCUMENT_LABELS[doc.type] || doc.type,
78
+ value: doc.type,
79
+ })) || [];
80
+ }, [selectedCountry]);
81
+
82
+ // Memoize selected country label
83
+ const selectedCountryLabel = useMemo(() => {
84
+ return STATIC_COUNTRIES.find((c) => c.value === selectedCountry)?.label;
85
+ }, [selectedCountry]);
86
+
87
+ // Memoize selected ID type label
88
+ const selectedIdTypeLabel = useMemo(() => {
89
+ return idCardTypes.find((i) => i.value === selectedIdType)?.label;
90
+ }, [idCardTypes, selectedIdType]);
91
+
92
+ const toggleDropdown = useCallback(() => {
93
+ setDropdownVisible((prev) => !prev);
94
+ setIdTypeDropdownVisible(false);
95
+ }, []);
96
+
97
+ const toggleIdTypeDropdown = useCallback(() => {
98
+ setIdTypeDropdownVisible((prev) => !prev);
99
+ setDropdownVisible(false);
100
+ }, []);
101
+
102
+ const selectCountry = useCallback((value: string) => {
103
+ setSelectedCountry(value);
104
+ setDropdownVisible(false);
105
+ setSearch('');
106
+ setSelectedIdType(null);
107
+ setFilteredCountries(STATIC_COUNTRIES);
108
+
109
+ const selectedCountryData = STATIC_COUNTRIES.find((c) => c.value === value);
110
+ updateIDMConf({ selectedCountryDetails: selectedCountryData || null });
111
+ }, [updateIDMConf]);
112
+
113
+ const selectIdType = useCallback((value: string) => {
114
+ setSelectedIdType(value);
115
+ const selectedCountryData = STATIC_COUNTRIES.find((c) => c.value === selectedCountry);
116
+ updateIDMConf({
117
+ selectedCountryDetails: {
118
+ ...selectedCountryData,
119
+ selectedMetaData: selectedCountryData?.metadata?.find((c) => c.type === value),
120
+ },
121
+ });
122
+ setIdTypeDropdownVisible(false);
123
+ }, [selectedCountry, updateIDMConf]);
124
+
125
+ const handleContinue = useCallback(() => {
126
+ if (!selectedCountry || !selectedIdType) {
127
+ Alert.alert('Missing Fields', 'Please select both country and ID card type.');
128
+ return;
129
+ }
130
+ navigation.navigate('FrontDocumentAdvice' as never);
131
+ }, [selectedCountry, selectedIdType, navigation]);
132
+
133
+ const handleSearch = useCallback((text: string) => {
134
+ setSearch(text);
135
+ const filtered = STATIC_COUNTRIES.filter((c) =>
136
+ c.label.toLowerCase().includes(text.toLowerCase())
137
+ );
138
+ setFilteredCountries(filtered);
139
+ }, []);
140
+
141
+ const closeDropdowns = useCallback(() => {
142
+ setDropdownVisible(false);
143
+ setIdTypeDropdownVisible(false);
144
+ }, []);
145
+
146
+ return (
147
+ <View style={{ flex: 1, backgroundColor: theme.colors.background }}>
148
+ <Pressable style={{ flex: 1 }} onPress={closeDropdowns}>
149
+ <ScrollView
150
+ contentContainerStyle={styles.container}
151
+ keyboardShouldPersistTaps="handled"
152
+ >
153
+ <View>
154
+ <ThemedText style={styles.maintitle} type="title">
155
+ ID Issuer Country
156
+ </ThemedText>
157
+
158
+ <View style={{ marginHorizontal: 20, marginBottom: 20 }}>
159
+ <Pressable style={styles.dropdownTrigger} onPress={toggleDropdown}>
160
+ <Text
161
+ style={{
162
+ color: selectedCountry ? theme.colors.text : theme.colors.subtitle,
163
+ }}
164
+ >
165
+ {selectedCountryLabel || 'Select a country'}
166
+ </Text>
167
+ <Text style={styles.downArrow}>▼</Text>
168
+ </Pressable>
169
+ {dropdownVisible && (
170
+ <View style={[styles.dropdownList, { maxHeight: 300 }]}>
171
+ <TextInput
172
+ value={search}
173
+ onChangeText={handleSearch}
174
+ placeholder="Search country..."
175
+ placeholderTextColor={theme.colors.subtitle}
176
+ style={styles.searchbox}
177
+ />
178
+ <ScrollView nestedScrollEnabled>
179
+ {filteredCountries.map((item) => (
180
+ <Pressable
181
+ key={item.value}
182
+ onPress={() => selectCountry(item.value)}
183
+ style={styles.dropdownItem}
184
+ >
185
+ <Text style={styles.dopDownLable}>{item.label}</Text>
186
+ </Pressable>
187
+ ))}
188
+ </ScrollView>
189
+ </View>
190
+ )}
191
+ </View>
192
+
193
+ <View>
194
+ <ThemedText style={styles.maintitle} type="title">
195
+ Select ID Card Type
196
+ </ThemedText>
197
+
198
+ <View style={{ marginHorizontal: 20, marginBottom: 20 }}>
199
+ <Pressable style={styles.dropdownTrigger} onPress={toggleIdTypeDropdown}>
200
+ <Text
201
+ style={{
202
+ color: selectedIdType ? theme.colors.text : theme.colors.subtitle,
203
+ }}
204
+ >
205
+ {selectedIdTypeLabel || 'Select ID Card Type'}
206
+ </Text>
207
+ <Text style={styles.downArrow}>▼</Text>
208
+ </Pressable>
209
+ {idTypeDropdownVisible && (
210
+ <View style={[styles.dropdownList, { maxHeight: 200 }]}>
211
+ <ScrollView nestedScrollEnabled>
212
+ {idCardTypes.length > 0 ? (
213
+ idCardTypes.map((item) => (
214
+ <Pressable
215
+ key={item.value}
216
+ onPress={() => selectIdType(item.value)}
217
+ style={styles.dropdownItem}
218
+ >
219
+ <Text style={styles.dopDownLable}>{item.label}</Text>
220
+ </Pressable>
221
+ ))
222
+ ) : (
223
+ <View style={styles.dropdownItem}>
224
+ <Text style={styles.dopDownLable}>
225
+ {selectedCountry
226
+ ? 'No ID types available for this country'
227
+ : 'Please select a country first'}
228
+ </Text>
229
+ </View>
230
+ )}
231
+ </ScrollView>
232
+ </View>
233
+ )}
234
+ </View>
235
+ </View>
236
+ </View>
237
+
238
+ {!isKeyboardVisible && (
239
+ <Button
240
+ title="Continue"
241
+ style={styles.buttonplace}
242
+ textStyle={styles.button}
243
+ onPress={handleContinue}
244
+ />
245
+ )}
246
+ </ScrollView>
247
+ </Pressable>
248
+ </View>
249
+ );
250
+ }