@trustchex/react-native-sdk 1.267.0 → 1.354.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 (72) hide show
  1. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +8 -2
  2. package/lib/module/Screens/Dynamic/IdentityDocumentEIDScanningScreen.js +5 -1
  3. package/lib/module/Screens/Dynamic/IdentityDocumentScanningScreen.js +5 -1
  4. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +29 -15
  5. package/lib/module/Screens/Static/OTPVerificationScreen.js +285 -0
  6. package/lib/module/Screens/Static/ResultScreen.js +90 -26
  7. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +48 -134
  8. package/lib/module/Shared/Components/DebugNavigationPanel.js +252 -0
  9. package/lib/module/Shared/Components/EIDScanner.js +142 -17
  10. package/lib/module/Shared/Components/FaceCamera.js +23 -11
  11. package/lib/module/Shared/Components/IdentityDocumentCamera.js +295 -44
  12. package/lib/module/Shared/Components/NavigationManager.js +19 -3
  13. package/lib/module/Shared/Config/camera-enhancement.config.js +58 -0
  14. package/lib/module/Shared/Contexts/AppContext.js +1 -0
  15. package/lib/module/Shared/Libs/camera.utils.js +221 -1
  16. package/lib/module/Shared/Libs/frame-enhancement.utils.js +133 -0
  17. package/lib/module/Shared/Libs/mrz.utils.js +98 -1
  18. package/lib/module/Translation/Resources/en.js +30 -0
  19. package/lib/module/Translation/Resources/tr.js +30 -0
  20. package/lib/module/Trustchex.js +49 -39
  21. package/lib/module/version.js +1 -1
  22. package/lib/typescript/src/Screens/Dynamic/ContractAcceptanceScreen.d.ts.map +1 -1
  23. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.d.ts.map +1 -1
  24. package/lib/typescript/src/Screens/Dynamic/IdentityDocumentScanningScreen.d.ts.map +1 -1
  25. package/lib/typescript/src/Screens/Dynamic/LivenessDetectionScreen.d.ts.map +1 -1
  26. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts +3 -0
  27. package/lib/typescript/src/Screens/Static/OTPVerificationScreen.d.ts.map +1 -0
  28. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  29. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  30. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts +3 -0
  31. package/lib/typescript/src/Shared/Components/DebugNavigationPanel.d.ts.map +1 -0
  32. package/lib/typescript/src/Shared/Components/EIDScanner.d.ts.map +1 -1
  33. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  34. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  35. package/lib/typescript/src/Shared/Components/NavigationManager.d.ts.map +1 -1
  36. package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts +54 -0
  37. package/lib/typescript/src/Shared/Config/camera-enhancement.config.d.ts.map +1 -0
  38. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts +2 -0
  39. package/lib/typescript/src/Shared/Contexts/AppContext.d.ts.map +1 -1
  40. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts +65 -1
  41. package/lib/typescript/src/Shared/Libs/camera.utils.d.ts.map +1 -1
  42. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts +25 -0
  43. package/lib/typescript/src/Shared/Libs/frame-enhancement.utils.d.ts.map +1 -0
  44. package/lib/typescript/src/Shared/Libs/mrz.utils.d.ts.map +1 -1
  45. package/lib/typescript/src/Translation/Resources/en.d.ts +30 -0
  46. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  47. package/lib/typescript/src/Translation/Resources/tr.d.ts +30 -0
  48. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  49. package/lib/typescript/src/Trustchex.d.ts.map +1 -1
  50. package/lib/typescript/src/version.d.ts +1 -1
  51. package/package.json +3 -3
  52. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +6 -2
  53. package/src/Screens/Dynamic/IdentityDocumentEIDScanningScreen.tsx +3 -1
  54. package/src/Screens/Dynamic/IdentityDocumentScanningScreen.tsx +3 -1
  55. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +27 -17
  56. package/src/Screens/Static/OTPVerificationScreen.tsx +379 -0
  57. package/src/Screens/Static/ResultScreen.tsx +160 -101
  58. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +51 -196
  59. package/src/Shared/Components/DebugNavigationPanel.tsx +262 -0
  60. package/src/Shared/Components/EIDScanner.tsx +144 -19
  61. package/src/Shared/Components/FaceCamera.tsx +38 -21
  62. package/src/Shared/Components/IdentityDocumentCamera.tsx +399 -101
  63. package/src/Shared/Components/NavigationManager.tsx +19 -3
  64. package/src/Shared/Config/camera-enhancement.config.ts +46 -0
  65. package/src/Shared/Contexts/AppContext.ts +3 -0
  66. package/src/Shared/Libs/camera.utils.ts +240 -1
  67. package/src/Shared/Libs/frame-enhancement.utils.ts +217 -0
  68. package/src/Shared/Libs/mrz.utils.ts +78 -1
  69. package/src/Translation/Resources/en.ts +30 -0
  70. package/src/Translation/Resources/tr.ts +30 -0
  71. package/src/Trustchex.tsx +58 -46
  72. package/src/version.ts +1 -1
@@ -2,6 +2,7 @@ import React, { useContext, useEffect, useState } from 'react';
2
2
 
3
3
  import type { FieldRecords } from 'mrz';
4
4
  import { Alert, SafeAreaView, StyleSheet, View } from 'react-native';
5
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
5
6
  import IdentityDocumentCamera, {
6
7
  type DocumentScannedData,
7
8
  } from '../../Shared/Components/IdentityDocumentCamera';
@@ -27,6 +28,7 @@ const IdentityDocumentScanningScreen = () => {
27
28
  const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
28
29
  const appContext = useContext(AppContext);
29
30
  const { t } = useTranslation();
31
+ const insets = useSafeAreaInsets();
30
32
  const [allowedDocumentTypes, setAllowedDocumentTypes] = useState<
31
33
  ('I' | 'P')[] | null
32
34
  >(null);
@@ -158,7 +160,7 @@ const IdentityDocumentScanningScreen = () => {
158
160
  }}
159
161
  showDebugImages={false}
160
162
  />
161
- <View style={styles.footer}>
163
+ <View style={[styles.footer, { bottom: insets.bottom }]}>
162
164
  <NavigationManager ref={navigationManagerRef} />
163
165
  </View>
164
166
  </SafeAreaView>
@@ -15,6 +15,8 @@ import {
15
15
  Platform,
16
16
  Vibration,
17
17
  } from 'react-native';
18
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
19
+ import { SafeAreaView } from 'react-native-safe-area-context';
18
20
  import NativeCircularProgress from '../../Shared/Components/NativeCircularProgress';
19
21
  import { Camera } from 'react-native-vision-camera';
20
22
  import type { Face } from '../../Shared/VisionCameraPlugins/FaceDetector';
@@ -86,6 +88,7 @@ const LivenessDetectionScreen = () => {
86
88
  const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
87
89
  const { t } = useTranslation();
88
90
  const [isRecording, setIsRecording] = useState(false);
91
+ const insets = useSafeAreaInsets();
89
92
 
90
93
  // Track screen view and exit
91
94
  useScreenTracking('liveness_detection');
@@ -512,7 +515,7 @@ const LivenessDetectionScreen = () => {
512
515
  return (
513
516
  <>
514
517
  {!hasGuideShown ? (
515
- <View style={styles.guide}>
518
+ <SafeAreaView style={styles.guide}>
516
519
  <LottieView
517
520
  source={require('../../Shared/Animations/face-scan.json')}
518
521
  style={styles.guideAnimation}
@@ -539,15 +542,17 @@ const LivenessDetectionScreen = () => {
539
542
  • {t('livenessDetectionScreen.guidePoint4')}
540
543
  </Text>
541
544
  </View>
542
- <StyledButton
543
- mode="contained"
544
- onPress={() => {
545
- setHasGuideShown(true);
546
- }}
547
- >
548
- {t('general.letsGo')}
549
- </StyledButton>
550
- </View>
545
+ <View style={{ paddingBottom: insets.bottom }}>
546
+ <StyledButton
547
+ mode="contained"
548
+ onPress={() => {
549
+ setHasGuideShown(true);
550
+ }}
551
+ >
552
+ {t('general.letsGo')}
553
+ </StyledButton>
554
+ </View>
555
+ </SafeAreaView>
551
556
  ) : (
552
557
  <>
553
558
  <FaceCamera
@@ -558,11 +563,11 @@ const LivenessDetectionScreen = () => {
558
563
  <NativeCircularProgress
559
564
  style={styles.circularProgress}
560
565
  size={PREVIEW_SIZE}
561
- width={10}
562
- backgroundWidth={5}
566
+ width={8}
567
+ backgroundWidth={8}
563
568
  fill={state.progressFill}
564
569
  tintColor={appContext.branding.primaryColor}
565
- backgroundColor="white"
570
+ backgroundColor="rgba(255, 255, 255, 0.3)"
566
571
  />
567
572
  {state.brightnessLow && (
568
573
  <LottieView
@@ -575,7 +580,12 @@ const LivenessDetectionScreen = () => {
575
580
  <Svg style={styles.svgMask}>
576
581
  <Mask id="hole-mask">
577
582
  <SvgRect width="100%" height="100%" fill="white" />
578
- <Circle cx="50%" cy="50%" r="25%" fill="black" />
583
+ <Circle
584
+ cx={windowWidth / 2}
585
+ cy={windowHeight / 2}
586
+ r={PREVIEW_SIZE / 2}
587
+ fill="black"
588
+ />
579
589
  </Mask>
580
590
 
581
591
  <SvgRect
@@ -585,7 +595,7 @@ const LivenessDetectionScreen = () => {
585
595
  mask="url(#hole-mask)"
586
596
  />
587
597
  </Svg>
588
- <View style={styles.instructionsContainerTop}>
598
+ <View style={[styles.instructionsContainerTop, { paddingTop: insets.top }]}>
589
599
  <Text style={styles.instructions}>
590
600
  {(() => {
591
601
  if (state.brightnessLow) {
@@ -602,7 +612,7 @@ const LivenessDetectionScreen = () => {
602
612
  })()}
603
613
  </Text>
604
614
  </View>
605
- <View style={styles.instructionsContainerBottom}>
615
+ <View style={[styles.instructionsContainerBottom, { paddingBottom: insets.bottom }]}>
606
616
  <Text style={styles.action}>
607
617
  {state.faceDetected &&
608
618
  !state.faceTooBig &&
@@ -613,7 +623,7 @@ const LivenessDetectionScreen = () => {
613
623
  </View>
614
624
  </>
615
625
  )}
616
- <View style={styles.footer}>
626
+ <View style={[styles.footer, { bottom: insets.bottom }]}>
617
627
  <NavigationManager ref={navigationManagerRef} />
618
628
  </View>
619
629
  </>
@@ -0,0 +1,379 @@
1
+ import React, {
2
+ useCallback,
3
+ useContext,
4
+ useEffect,
5
+ useRef,
6
+ useState,
7
+ } from 'react';
8
+ import {
9
+ SafeAreaView,
10
+ Text,
11
+ StyleSheet,
12
+ Alert,
13
+ View,
14
+ Image,
15
+ Dimensions,
16
+ KeyboardAvoidingView,
17
+ Platform,
18
+ ScrollView,
19
+ } from 'react-native';
20
+ import { useSafeAreaInsets } from 'react-native-safe-area-context';
21
+ import { useRoute, type RouteProp } from '@react-navigation/native';
22
+ import AppContext from '../../Shared/Contexts/AppContext';
23
+ import httpClient, { BadRequestError, NotFoundError, TooManyRequestsError } from '../../Shared/Libs/http-client';
24
+ import { useTranslation } from 'react-i18next';
25
+ import LanguageSelector from '../../Shared/Components/LanguageSelector';
26
+ import NavigationManager, {
27
+ type NavigationManagerRef,
28
+ } from '../../Shared/Components/NavigationManager';
29
+ import type { VerificationSession } from '../../Shared/Types/verificationSession';
30
+ import StyledButton from '../../Shared/Components/StyledButton';
31
+ import StyledTextInput from '../../Shared/Components/StyledTextInput';
32
+ import { useTheme } from '../../Shared/Contexts/ThemeContext';
33
+ import LottieView from 'lottie-react-native';
34
+ import {
35
+ getSimulatedDemoData,
36
+ } from '../../Shared/Libs/demo.utils';
37
+ import {
38
+ trackError,
39
+ useScreenTracking,
40
+ } from '../../Shared/Libs/analytics.utils';
41
+
42
+ type OTPVerificationScreenParams = {
43
+ sessionId: string;
44
+ };
45
+
46
+ const OTPVerificationScreen = () => {
47
+ const route = useRoute<RouteProp<{ params: OTPVerificationScreenParams }, 'params'>>();
48
+ const sessionId = route.params?.sessionId;
49
+ const [code, setCode] = useState('');
50
+ const [isSendAgainEnabled, setIsSendAgainEnabled] = useState(false);
51
+ const [isCodeGettingVerified, setIsCodeGettingVerified] = useState(false);
52
+ const appContext = useContext(AppContext);
53
+ const { t } = useTranslation();
54
+ const navigationManagerRef = useRef<NavigationManagerRef>(null);
55
+ const initialized = useRef(false);
56
+ const insets = useSafeAreaInsets();
57
+ const theme = useTheme();
58
+ const primaryColor = theme.colors.primary;
59
+
60
+ useScreenTracking('otp_verification');
61
+
62
+ // Guard: If sessionId is not provided, show error
63
+ if (!sessionId) {
64
+ return (
65
+ <SafeAreaView style={[styles.container, { backgroundColor: theme.colors.background }]}>
66
+ <Text style={[styles.errorText, { color: theme.colors.error }]}>
67
+ {t('general.sessionNotFound')}
68
+ </Text>
69
+ </SafeAreaView>
70
+ );
71
+ }
72
+
73
+ const apiUrl = `${appContext.baseUrl}/api/app/mobile`;
74
+
75
+ const sendVerificationCode = useCallback(
76
+ async (sessionId: string) => {
77
+ try {
78
+ await httpClient.post(
79
+ `${apiUrl}/verification-sessions/${sessionId}`,
80
+ {},
81
+ appContext.isDemoSession
82
+ ? getSimulatedDemoData('SEND_VERIFICATION_CODE')
83
+ : undefined
84
+ );
85
+ return true;
86
+ } catch (error) {
87
+ if (error instanceof NotFoundError) {
88
+ Alert.alert(
89
+ t('general.error'),
90
+ t('verificationSessionCheckScreen.noVerificationSessionFound')
91
+ );
92
+ } else if (error instanceof TooManyRequestsError) {
93
+ return true;
94
+ } else {
95
+ trackError(
96
+ 'VERIFICATION_CODE_SEND_FAILED',
97
+ 'Failed to send verification code',
98
+ 'otp_verification',
99
+ 'high',
100
+ { recoverable: true, userAction: 'send_verification_code' }
101
+ );
102
+ Alert.alert(
103
+ t('general.error'),
104
+ t('verificationSessionCheckScreen.cannotSendVerificationCode')
105
+ );
106
+ }
107
+ }
108
+ return false;
109
+ },
110
+ [apiUrl, appContext.isDemoSession, t]
111
+ );
112
+
113
+ const getVerifiedSession = useCallback(
114
+ async (sessionId: string, verificationCode: string) => {
115
+ try {
116
+ const response = await httpClient.post<
117
+ VerificationSession,
118
+ { code: string }
119
+ >(
120
+ `${apiUrl}/verification-sessions/${sessionId}`,
121
+ {
122
+ code: verificationCode,
123
+ },
124
+ appContext.isDemoSession
125
+ ? getSimulatedDemoData<
126
+ VerificationSession | undefined,
127
+ { code: string }
128
+ >('GET_VERIFIED_SESSION', { code: verificationCode })
129
+ : undefined
130
+ );
131
+
132
+ return response;
133
+ } catch (error) {
134
+ if (error instanceof NotFoundError) {
135
+ Alert.alert(
136
+ t('general.error'),
137
+ t('verificationSessionCheckScreen.noVerificationSessionFound')
138
+ );
139
+ } else if (error instanceof BadRequestError) {
140
+ // Wrong OTP code - expected user behavior, not actionable
141
+ } else {
142
+ trackError(
143
+ 'VERIFIED_SESSION_CHECK_ERROR',
144
+ error instanceof Error ? error.message : 'Unknown error',
145
+ 'otp_verification',
146
+ 'high',
147
+ { recoverable: false, userAction: 'check_verified_session' }
148
+ );
149
+ }
150
+ }
151
+ },
152
+ [apiUrl, appContext.isDemoSession, t]
153
+ );
154
+
155
+ const sendOTPCode = useCallback(
156
+ async (sessionId: string) => {
157
+ if (!sessionId) {
158
+ return false;
159
+ }
160
+
161
+ const isSent = await sendVerificationCode(sessionId);
162
+
163
+ if (!isSent) {
164
+ return false;
165
+ }
166
+
167
+ setIsSendAgainEnabled(false);
168
+
169
+ setTimeout(
170
+ () => {
171
+ setIsSendAgainEnabled(true);
172
+ },
173
+ 3 * 60 * 1000
174
+ );
175
+
176
+ return true;
177
+ },
178
+ [sendVerificationCode]
179
+ );
180
+
181
+ useEffect(() => {
182
+ if (!initialized.current && sessionId) {
183
+ initialized.current = true;
184
+ sendOTPCode(sessionId);
185
+ }
186
+ }, [sessionId, sendOTPCode]);
187
+
188
+ return (
189
+ <SafeAreaView style={styles.safeAreaContainer}>
190
+ <KeyboardAvoidingView
191
+ style={styles.keyboardAvoidingView}
192
+ behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
193
+ keyboardVerticalOffset={Platform.OS === 'ios' ? 0 : 20}
194
+ >
195
+ <ScrollView
196
+ style={styles.container}
197
+ contentContainerStyle={styles.scrollContainer}
198
+ keyboardShouldPersistTaps="handled"
199
+ showsVerticalScrollIndicator={false}
200
+ >
201
+ <View style={styles.mainContent}>
202
+ <LanguageSelector />
203
+ <View style={styles.mainHeader}>
204
+ {appContext.branding.logoUrl ? (
205
+ <Image
206
+ source={{ uri: appContext.branding.logoUrl }}
207
+ style={styles.mainLogo}
208
+ />
209
+ ) : (
210
+ <Image
211
+ source={require('../../Shared/Assets/trustchex-logo-black.png')}
212
+ style={styles.trustchexLogo}
213
+ />
214
+ )}
215
+ </View>
216
+ {isCodeGettingVerified ? (
217
+ <LottieView
218
+ source={require('../../Shared/Animations/loading.json')}
219
+ style={styles.loadingAnimation}
220
+ autoPlay
221
+ loop={true}
222
+ resizeMode="cover"
223
+ />
224
+ ) : (
225
+ <>
226
+ <Text style={styles.mainText}>
227
+ {t('verificationSessionCheckScreen.codeText')}
228
+ </Text>
229
+ <StyledTextInput
230
+ autoFocus={true}
231
+ placeholder=""
232
+ borderColor={primaryColor}
233
+ focusedBorderColor={primaryColor}
234
+ inputStyle={styles.otpCodeTextInput}
235
+ keyboardType="number-pad"
236
+ textContentType="oneTimeCode"
237
+ autoComplete="sms-otp"
238
+ maxLength={6}
239
+ onChangeText={(text) => {
240
+ const numericText = text.replace(/[^0-9]/g, '');
241
+ if (numericText.length <= 6) {
242
+ setCode(numericText);
243
+ if (numericText.length === 6) {
244
+ (async () => {
245
+ setIsCodeGettingVerified(true);
246
+ const verifiedSession = await getVerifiedSession(
247
+ sessionId,
248
+ numericText
249
+ );
250
+ if (verifiedSession?.identificationId) {
251
+ appContext.identificationInfo.identificationId =
252
+ verifiedSession?.identificationId;
253
+ // Update workflow steps from verified session if available
254
+ if (verifiedSession.workflowSteps) {
255
+ appContext.workflowSteps = verifiedSession.workflowSteps;
256
+ }
257
+ setCode('');
258
+ navigationManagerRef.current?.navigateToNextStep();
259
+ } else {
260
+ appContext.onError?.('Invalid OTP code');
261
+ Alert.alert(
262
+ t('general.error'),
263
+ t('verificationSessionCheckScreen.codeError')
264
+ );
265
+ setCode('');
266
+ setIsCodeGettingVerified(false);
267
+ }
268
+ })();
269
+ }
270
+ }
271
+ }}
272
+ value={code}
273
+ />
274
+ <StyledButton
275
+ mode="contained"
276
+ disabled={!isSendAgainEnabled}
277
+ onPress={() => {
278
+ sendOTPCode(sessionId);
279
+ }}
280
+ >
281
+ {t('verificationSessionCheckScreen.sendCodeAgain')}
282
+ </StyledButton>
283
+ </>
284
+ )}
285
+ </View>
286
+ {appContext.branding.logoUrl && (
287
+ <View style={[styles.footer, { bottom: insets.bottom + 15 }]}>
288
+ <Text style={styles.mainText}>Powered By</Text>
289
+ <Image
290
+ source={require('../../Shared/Assets/trustchex-logo-black.png')}
291
+ style={styles.poweredByLogo}
292
+ />
293
+ </View>
294
+ )}
295
+ <NavigationManager ref={navigationManagerRef} />
296
+ </ScrollView>
297
+ </KeyboardAvoidingView>
298
+ </SafeAreaView>
299
+ );
300
+ };
301
+
302
+ const styles = StyleSheet.create({
303
+ safeAreaContainer: {
304
+ flex: 1,
305
+ backgroundColor: 'white',
306
+ },
307
+ keyboardAvoidingView: {
308
+ flex: 1,
309
+ },
310
+ container: {
311
+ flex: 1,
312
+ },
313
+ scrollContainer: {
314
+ flexGrow: 1,
315
+ padding: 20,
316
+ alignItems: 'center',
317
+ justifyContent: 'center',
318
+ },
319
+ loadingAnimation: {
320
+ width: '100%',
321
+ height: '100%',
322
+ maxHeight: 200,
323
+ },
324
+ mainHeader: {
325
+ display: 'flex',
326
+ flexDirection: 'column',
327
+ alignItems: 'center',
328
+ justifyContent: 'center',
329
+ },
330
+ mainContent: {
331
+ flex: 1,
332
+ display: 'flex',
333
+ gap: 20,
334
+ justifyContent: 'center',
335
+ width: '100%',
336
+ maxWidth: 400,
337
+ },
338
+ mainText: {
339
+ color: 'black',
340
+ textAlign: 'center',
341
+ },
342
+ mainLogo: {
343
+ width: Dimensions.get('window').width - 40,
344
+ height: Dimensions.get('window').width / 4,
345
+ maxWidth: 300,
346
+ maxHeight: 300,
347
+ resizeMode: 'contain',
348
+ },
349
+ trustchexLogo: {
350
+ width: 300,
351
+ height: 75,
352
+ resizeMode: 'contain',
353
+ },
354
+ poweredByLogo: {
355
+ width: 120,
356
+ height: 30,
357
+ resizeMode: 'contain',
358
+ },
359
+ footer: {
360
+ display: 'flex',
361
+ flexDirection: 'column',
362
+ justifyContent: 'center',
363
+ alignItems: 'center',
364
+ position: 'absolute',
365
+ },
366
+ otpCodeTextInput: {
367
+ fontWeight: '800',
368
+ fontSize: 28,
369
+ textAlign: 'center',
370
+ letterSpacing: 8,
371
+ },
372
+ errorText: {
373
+ fontSize: 16,
374
+ textAlign: 'center',
375
+ padding: 20,
376
+ },
377
+ });
378
+
379
+ export default OTPVerificationScreen;