@trustchex/react-native-sdk 1.215.0 → 1.248.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 (49) hide show
  1. package/lib/module/Screens/Dynamic/ContractAcceptanceScreen.js +2 -2
  2. package/lib/module/Screens/Dynamic/LivenessDetectionScreen.js +2 -2
  3. package/lib/module/Screens/Static/ResultScreen.js +4 -9
  4. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +150 -106
  5. package/lib/module/Shared/Components/EIDScanner.js +4 -4
  6. package/lib/module/Shared/Components/FaceCamera.js +4 -3
  7. package/lib/module/Shared/Components/IdentityDocumentCamera.js +4 -3
  8. package/lib/module/Shared/Components/QrCodeScannerCamera.js +2 -2
  9. package/lib/module/Shared/Components/StyledButton.js +30 -0
  10. package/lib/module/Shared/Constants/index.js +3 -0
  11. package/lib/module/Shared/Constants/validation.constants.js +24 -0
  12. package/lib/module/Shared/Libs/demo.utils.js +5 -3
  13. package/lib/module/Translation/Resources/en.js +50 -52
  14. package/lib/module/Translation/Resources/tr.js +48 -53
  15. package/lib/typescript/src/Screens/Static/ResultScreen.d.ts.map +1 -1
  16. package/lib/typescript/src/Screens/Static/VerificationSessionCheckScreen.d.ts.map +1 -1
  17. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  18. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  19. package/lib/typescript/src/Shared/Components/StyledButton.d.ts +6 -0
  20. package/lib/typescript/src/Shared/Components/StyledButton.d.ts.map +1 -0
  21. package/lib/typescript/src/Shared/Constants/index.d.ts +2 -0
  22. package/lib/typescript/src/Shared/Constants/index.d.ts.map +1 -0
  23. package/lib/typescript/src/Shared/Constants/validation.constants.d.ts +20 -0
  24. package/lib/typescript/src/Shared/Constants/validation.constants.d.ts.map +1 -0
  25. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts +2 -2
  26. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  27. package/lib/typescript/src/Translation/Resources/en.d.ts +1 -3
  28. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  29. package/lib/typescript/src/Translation/Resources/tr.d.ts +1 -6
  30. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  31. package/package.json +1 -1
  32. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +3 -3
  33. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +3 -3
  34. package/src/Screens/Static/ResultScreen.tsx +14 -12
  35. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +184 -131
  36. package/src/Shared/Components/EIDScanner.tsx +7 -7
  37. package/src/Shared/Components/FaceCamera.tsx +6 -5
  38. package/src/Shared/Components/IdentityDocumentCamera.tsx +6 -5
  39. package/src/Shared/Components/QrCodeScannerCamera.tsx +3 -3
  40. package/src/Shared/Components/StyledButton.tsx +35 -0
  41. package/src/Shared/Constants/index.ts +1 -0
  42. package/src/Shared/Constants/validation.constants.ts +24 -0
  43. package/src/Shared/Libs/demo.utils.ts +5 -4
  44. package/src/Translation/Resources/en.ts +51 -54
  45. package/src/Translation/Resources/tr.ts +50 -55
  46. package/lib/module/Shared/Components/OTPCodeInput.js +0 -74
  47. package/lib/typescript/src/Shared/Components/OTPCodeInput.d.ts +0 -10
  48. package/lib/typescript/src/Shared/Components/OTPCodeInput.d.ts.map +0 -1
  49. package/src/Shared/Components/OTPCodeInput.tsx +0 -93
@@ -2,6 +2,7 @@ import React, {
2
2
  useCallback,
3
3
  useContext,
4
4
  useEffect,
5
+ useMemo,
5
6
  useRef,
6
7
  useState,
7
8
  } from 'react';
@@ -20,25 +21,28 @@ import {
20
21
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
21
22
  import AppContext from '../../Shared/Contexts/AppContext';
22
23
  import httpClient, { NotFoundError } from '../../Shared/Libs/http-client';
23
- import OTPCodeInput from '../../Shared/Components/OTPCodeInput';
24
24
  import { useTranslation } from 'react-i18next';
25
25
  import LanguageSelector from '../../Shared/Components/LanguageSelector';
26
26
  import NavigationManager, {
27
27
  type NavigationManagerRef,
28
28
  } from '../../Shared/Components/NavigationManager';
29
29
  import type { VerificationSession } from '../../Shared/Types/verificationSession';
30
- import { Button, TextInput, useTheme } from 'react-native-paper';
30
+ import { TextInput, useTheme } from 'react-native-paper';
31
+ import StyledButton from '../../Shared/Components/StyledButton';
31
32
  import LottieView from 'lottie-react-native';
32
33
  import {
33
34
  getSimulatedDemoData,
34
35
  isDemoSession,
35
36
  } from '../../Shared/Libs/demo.utils';
36
37
  import { useNavigation } from '@react-navigation/native';
38
+ import {
39
+ SESSION_CODE_CHARSET_PATTERN,
40
+ SESSION_CODE_VALIDATION_PATTERN,
41
+ } from '../../Shared/Constants/validation.constants';
37
42
 
38
43
  const VerificationSessionCheckScreen = () => {
39
- const [emailOrPhone, setEmailOrPhone] = useState('');
44
+ const [sessionCode, setSessionCode] = useState('');
40
45
  const [code, setCode] = useState('');
41
- const [isEnabled, setIsEnabled] = useState(false);
42
46
  const [isCheckingSession, setIsCheckingSession] = useState(false);
43
47
  const [isSendAgainEnabled, setIsSendAgainEnabled] = useState(false);
44
48
  const [isCodeSent, setIsCodeSent] = useState(false);
@@ -47,27 +51,17 @@ const VerificationSessionCheckScreen = () => {
47
51
  const { t } = useTranslation();
48
52
  const navigationManagerRef = React.useRef<NavigationManagerRef>(null);
49
53
  const navigation = useNavigation();
50
- const [apiUrl, setApiUrl] = useState<string>(
51
- `${appContext.baseUrl}/api/app/mobile`
52
- );
53
54
  const theme = useTheme();
54
55
  const initialized = useRef(false);
55
56
  const insets = useSafeAreaInsets();
56
57
 
57
- useEffect(() => {
58
- if (appContext.baseUrl) {
59
- setApiUrl(`${appContext.baseUrl}/api/app/mobile`);
60
- }
61
- }, [appContext.baseUrl]);
62
-
63
- const validateEmail = useCallback((value: string) => {
64
- const re = /\S+@\S+\.\S+/;
65
- return re.test(value);
66
- }, []);
58
+ const apiUrl = useMemo(
59
+ () => `${appContext.baseUrl}/api/app/mobile`,
60
+ [appContext.baseUrl]
61
+ );
67
62
 
68
- const validatePhoneNumber = useCallback((value: string) => {
69
- const re = /^(5(\d{9}))/;
70
- return re.test(value);
63
+ const validateSessionCode = useCallback((value: string) => {
64
+ return SESSION_CODE_VALIDATION_PATTERN.test(value.toUpperCase());
71
65
  }, []);
72
66
 
73
67
  const getSession = useCallback(
@@ -94,29 +88,20 @@ const VerificationSessionCheckScreen = () => {
94
88
  [apiUrl, appContext.isDemoSession, t]
95
89
  );
96
90
 
97
- const getSessions = useCallback(
98
- async (value: string) => {
91
+ const getSessionByCode = useCallback(
92
+ async (inputCode: string) => {
99
93
  try {
100
- const isEmail = validateEmail(value);
101
- const isPhoneNumber = validatePhoneNumber(value);
102
- let response: VerificationSession[] = [];
103
- if (isEmail) {
104
- appContext.isDemoSession = isDemoSession(value);
105
- response = await httpClient.get<VerificationSession[]>(
106
- `${apiUrl}/verification-sessions?email=${value}`,
107
- appContext.isDemoSession
108
- ? getSimulatedDemoData<VerificationSession[] | undefined, void>(
109
- 'VERIFICATION_SESSIONS'
110
- )
111
- : undefined
112
- );
113
- } else if (isPhoneNumber) {
114
- response = await httpClient.get<VerificationSession[]>(
115
- `${apiUrl}/verification-sessions?phoneNumber=${value}`
116
- );
117
- }
94
+ appContext.isDemoSession = isDemoSession(inputCode);
95
+ const response = await httpClient.get<{ id: string }>(
96
+ `${apiUrl}/verification-sessions?sessionCode=${inputCode.toUpperCase()}`,
97
+ appContext.isDemoSession
98
+ ? getSimulatedDemoData<{ id: string } | undefined, void>(
99
+ 'GET_SESSION_BY_CODE'
100
+ )
101
+ : undefined
102
+ );
118
103
 
119
- if (!response || response.length === 0) {
104
+ if (!response || !response.id) {
120
105
  throw new NotFoundError();
121
106
  }
122
107
 
@@ -130,7 +115,7 @@ const VerificationSessionCheckScreen = () => {
130
115
  }
131
116
  }
132
117
  },
133
- [apiUrl, appContext, t, validateEmail, validatePhoneNumber]
118
+ [apiUrl, appContext, t]
134
119
  );
135
120
 
136
121
  const sendVerificationCode = useCallback(
@@ -194,7 +179,7 @@ const VerificationSessionCheckScreen = () => {
194
179
  [apiUrl, appContext.isDemoSession, t]
195
180
  );
196
181
 
197
- const sendCode = useCallback(
182
+ const sendOTPCode = useCallback(
198
183
  async (sessionId: string) => {
199
184
  if (!sessionId) {
200
185
  return false;
@@ -237,7 +222,7 @@ const VerificationSessionCheckScreen = () => {
237
222
  }
238
223
 
239
224
  if (session?.sendOTP) {
240
- return sendCode(session.id);
225
+ return sendOTPCode(session.id);
241
226
  } else if (session?.identificationId) {
242
227
  appContext.identificationInfo.identificationId =
243
228
  session?.identificationId;
@@ -254,7 +239,7 @@ const VerificationSessionCheckScreen = () => {
254
239
  appContext,
255
240
  appContext.identificationInfo.sessionId,
256
241
  getSession,
257
- sendCode,
242
+ sendOTPCode,
258
243
  theme.colors,
259
244
  theme.colors.primary,
260
245
  theme.colors.secondary,
@@ -302,107 +287,138 @@ const VerificationSessionCheckScreen = () => {
302
287
  <Text style={styles.mainText}>
303
288
  {t('verificationSessionCheckScreen.mainText')}
304
289
  </Text>
305
- <TextInput
306
- mode="outlined"
307
- autoCapitalize="none"
308
- placeholder={t(
309
- 'verificationSessionCheckScreen.inputPlaceholder'
290
+ <View style={styles.inputContainer}>
291
+ <TextInput
292
+ mode="outlined"
293
+ autoCapitalize="characters"
294
+ placeholder=""
295
+ outlineColor={theme.colors.primary}
296
+ activeOutlineColor={theme.colors.primary}
297
+ style={styles.sessionCodeTextInput}
298
+ contentStyle={styles.sessionCodeInputWithText}
299
+ onChangeText={async (text) => {
300
+ const alphanumericText = text
301
+ .toUpperCase()
302
+ .replace(SESSION_CODE_CHARSET_PATTERN, '');
303
+ if (alphanumericText.length <= 8) {
304
+ setSessionCode(alphanumericText);
305
+ if (
306
+ validateSessionCode(alphanumericText) &&
307
+ alphanumericText.length === 8
308
+ ) {
309
+ try {
310
+ setIsCheckingSession(true);
311
+ const session =
312
+ await getSessionByCode(alphanumericText);
313
+ if (session?.id) {
314
+ appContext.identificationInfo.sessionId =
315
+ session.id;
316
+ } else {
317
+ setSessionCode('');
318
+ }
319
+ } catch {
320
+ setSessionCode('');
321
+ } finally {
322
+ setTimeout(() => {
323
+ setIsCheckingSession(false);
324
+ }, 1000);
325
+ }
326
+ }
327
+ }
328
+ }}
329
+ value={sessionCode.toUpperCase()}
330
+ maxLength={8}
331
+ />
332
+ {!sessionCode && (
333
+ <Text style={styles.placeholderText}>
334
+ {t('verificationSessionCheckScreen.enterSessionCode')}
335
+ </Text>
310
336
  )}
311
- onChangeText={(text) => {
312
- setEmailOrPhone(text);
313
- if (validateEmail(text) || validatePhoneNumber(text)) {
314
- setIsEnabled(true);
315
- } else {
316
- setIsEnabled(false);
317
- }
318
- }}
319
- />
320
- <Button
337
+ </View>
338
+ <View style={styles.horizontalLineContainer}>
339
+ <View
340
+ style={[
341
+ styles.horizontalLine,
342
+ { backgroundColor: theme.colors.primary },
343
+ ]}
344
+ />
345
+ <Text style={styles.mainText}>
346
+ {t('verificationSessionCheckScreen.or')}
347
+ </Text>
348
+ <View
349
+ style={[
350
+ styles.horizontalLine,
351
+ { backgroundColor: theme.colors.primary },
352
+ ]}
353
+ />
354
+ </View>
355
+ <StyledButton
321
356
  mode="contained"
322
- disabled={!isEnabled}
323
- onPress={async () => {
324
- try {
325
- setIsCheckingSession(true);
326
- const sessions = await getSessions(emailOrPhone);
327
- if (sessions && sessions.length > 0) {
328
- appContext.identificationInfo.sessionId =
329
- sessions[0].id;
330
- }
331
- } catch {
332
- // Do nothing
333
- } finally {
334
- setTimeout(() => {
335
- setIsCheckingSession(false);
336
- setIsEnabled(false);
337
- }, 1000);
338
- }
357
+ onPress={() => {
358
+ navigation.navigate('QrCodeScanningScreen' as never);
339
359
  }}
340
360
  >
341
- {t('verificationSessionCheckScreen.continue')}
342
- </Button>
343
- {!isEnabled && (
344
- <>
345
- <View style={styles.horizontalLineContainer}>
346
- <View style={styles.horizontalLine} />
347
- <Text style={styles.mainText}>
348
- {t('verificationSessionCheckScreen.or')}
349
- </Text>
350
- <View style={styles.horizontalLine} />
351
- </View>
352
- <Button
353
- mode="contained"
354
- onPress={() => {
355
- navigation.navigate('QrCodeScanningScreen' as never);
356
- }}
357
- >
358
- {t('verificationSessionCheckScreen.scanQRCode')}
359
- </Button>
360
- </>
361
- )}
361
+ {t('verificationSessionCheckScreen.scanQRCode')}
362
+ </StyledButton>
362
363
  </>
363
364
  ) : (
364
365
  <>
365
366
  <Text style={styles.mainText}>
366
367
  {t('verificationSessionCheckScreen.codeText')}
367
368
  </Text>
368
- <OTPCodeInput
369
- value={code}
369
+ <TextInput
370
+ mode="outlined"
371
+ autoFocus={true}
372
+ placeholder=""
373
+ outlineColor={theme.colors.primary}
374
+ activeOutlineColor={theme.colors.primary}
375
+ style={styles.otpCodeTextInput}
376
+ contentStyle={styles.otpCodeInputWithText}
377
+ keyboardType="number-pad"
378
+ textContentType="oneTimeCode"
379
+ autoComplete="sms-otp"
380
+ maxLength={6}
370
381
  onChangeText={(text) => {
371
- setCode(text);
372
- }}
373
- codeLength={6}
374
- onCodeFilled={async (value) => {
375
- setIsCodeGettingVerified(true);
376
- const verifiedSession = await getVerifiedSession(
377
- appContext.identificationInfo.sessionId,
378
- value
379
- );
380
- if (verifiedSession?.identificationId) {
381
- appContext.identificationInfo.identificationId =
382
- verifiedSession?.identificationId;
383
- setCode('');
384
- setIsCodeSent(false);
385
- navigationManagerRef.current?.navigateToNextStep();
386
- } else {
387
- appContext.onError?.('Invalid OTP code');
388
- Alert.alert(
389
- t('general.error'),
390
- t('verificationSessionCheckScreen.codeError')
391
- );
392
- setCode('');
393
- setIsCodeGettingVerified(false);
382
+ const numericText = text.replace(/[^0-9]/g, '');
383
+ if (numericText.length <= 6) {
384
+ setCode(numericText);
385
+ if (numericText.length === 6) {
386
+ (async () => {
387
+ setIsCodeGettingVerified(true);
388
+ const verifiedSession = await getVerifiedSession(
389
+ appContext.identificationInfo.sessionId,
390
+ numericText
391
+ );
392
+ if (verifiedSession?.identificationId) {
393
+ appContext.identificationInfo.identificationId =
394
+ verifiedSession?.identificationId;
395
+ setCode('');
396
+ setIsCodeSent(false);
397
+ navigationManagerRef.current?.navigateToNextStep();
398
+ } else {
399
+ appContext.onError?.('Invalid OTP code');
400
+ Alert.alert(
401
+ t('general.error'),
402
+ t('verificationSessionCheckScreen.codeError')
403
+ );
404
+ setCode('');
405
+ setIsCodeGettingVerified(false);
406
+ }
407
+ })();
408
+ }
394
409
  }
395
410
  }}
411
+ value={code}
396
412
  />
397
- <Button
413
+ <StyledButton
398
414
  mode="contained"
399
415
  disabled={!isSendAgainEnabled}
400
416
  onPress={() => {
401
- sendCode(appContext.identificationInfo.sessionId);
417
+ sendOTPCode(appContext.identificationInfo.sessionId);
402
418
  }}
403
419
  >
404
420
  {t('verificationSessionCheckScreen.sendCodeAgain')}
405
- </Button>
421
+ </StyledButton>
406
422
  </>
407
423
  )}
408
424
  </View>
@@ -440,17 +456,15 @@ const styles = StyleSheet.create({
440
456
  justifyContent: 'center',
441
457
  },
442
458
  horizontalLineContainer: {
443
- display: 'flex',
444
459
  flexDirection: 'row',
445
460
  alignItems: 'center',
446
- justifyContent: 'center',
461
+ justifyContent: 'space-between',
447
462
  gap: 10,
448
- padding: 10,
463
+ paddingVertical: 10,
449
464
  },
450
465
  horizontalLine: {
451
466
  flex: 1,
452
- height: 1,
453
- backgroundColor: '#aaa',
467
+ height: 2,
454
468
  },
455
469
  loadingAnimation: {
456
470
  width: '100%',
@@ -519,6 +533,45 @@ const styles = StyleSheet.create({
519
533
  alignItems: 'center',
520
534
  position: 'absolute',
521
535
  },
536
+ inputContainer: {
537
+ position: 'relative',
538
+ width: '100%',
539
+ },
540
+ sessionCodeTextInput: {
541
+ backgroundColor: 'white',
542
+ },
543
+ sessionCodeInputWithText: {
544
+ textTransform: 'uppercase',
545
+ fontWeight: '800',
546
+ fontSize: 28,
547
+ textAlign: 'center',
548
+ letterSpacing: 8,
549
+ },
550
+ placeholderText: {
551
+ position: 'absolute',
552
+ top: 0,
553
+ left: 0,
554
+ right: 0,
555
+ height: '100%',
556
+ lineHeight: 56,
557
+ fontSize: 16,
558
+ color: '#666',
559
+ textAlign: 'center',
560
+ pointerEvents: 'none',
561
+ },
562
+ sessionCodeInputPlaceholder: {
563
+ fontSize: 16,
564
+ textAlign: 'center',
565
+ },
566
+ otpCodeTextInput: {
567
+ backgroundColor: 'white',
568
+ },
569
+ otpCodeInputWithText: {
570
+ fontWeight: '800',
571
+ fontSize: 28,
572
+ textAlign: 'center',
573
+ letterSpacing: 8,
574
+ },
522
575
  });
523
576
 
524
577
  export default VerificationSessionCheckScreen;
@@ -7,7 +7,7 @@ import NativeProgressBar from './NativeProgressBar';
7
7
  import type { FieldRecords } from 'mrz';
8
8
  import { useTranslation } from 'react-i18next';
9
9
  import AppContext from '../Contexts/AppContext';
10
- import { Button } from 'react-native-paper';
10
+ import StyledButton from './StyledButton';
11
11
  import LottieView from 'lottie-react-native';
12
12
  import { useKeepAwake } from '../Libs/native-keep-awake.utils';
13
13
  import { speakWithDebounce } from '../Libs/tts.utils';
@@ -183,7 +183,7 @@ const EIDScanner = ({
183
183
  {t('eidScannerScreen.guideText')}
184
184
  </Text>
185
185
  </View>
186
- <Button
186
+ <StyledButton
187
187
  mode="contained"
188
188
  onPress={() => {
189
189
  setHasGuideShown(true);
@@ -191,7 +191,7 @@ const EIDScanner = ({
191
191
  }}
192
192
  >
193
193
  {t('eidScannerScreen.startScanning')}
194
- </Button>
194
+ </StyledButton>
195
195
  </View>
196
196
  ) : (
197
197
  <>
@@ -309,7 +309,7 @@ const EIDScanner = ({
309
309
  <Text style={styles.mainText}>
310
310
  {t('eidScannerScreen.nfcNotEnabled')}
311
311
  </Text>
312
- <Button
312
+ <StyledButton
313
313
  mode="contained"
314
314
  onPress={async () => {
315
315
  await NFCManager.goToNfcSetting();
@@ -317,7 +317,7 @@ const EIDScanner = ({
317
317
  }}
318
318
  >
319
319
  {t('eidScannerScreen.enableNFC')}
320
- </Button>
320
+ </StyledButton>
321
321
  </View>
322
322
  )}
323
323
 
@@ -368,7 +368,7 @@ const EIDScanner = ({
368
368
 
369
369
  {hasNfc && isEnabled && isScanned && (
370
370
  <View style={styles.buttonsContainer}>
371
- <Button
371
+ <StyledButton
372
372
  mode="contained"
373
373
  disabled={!documentFaceImage || !documentMRZInfo}
374
374
  onPress={() => {
@@ -387,7 +387,7 @@ const EIDScanner = ({
387
387
  }}
388
388
  >
389
389
  {t('eidScannerScreen.approveAndContinue')}
390
- </Button>
390
+ </StyledButton>
391
391
  </View>
392
392
  )}
393
393
  </>
@@ -28,7 +28,8 @@ import { Worklets, useSharedValue } from 'react-native-worklets-core';
28
28
  import { crop } from '../VisionCameraPlugins/Cropper';
29
29
  import { isFrameBright } from '../Libs/camera.utils';
30
30
  import { useTranslation } from 'react-i18next';
31
- import { ActivityIndicator, Button } from 'react-native-paper';
31
+ import { ActivityIndicator } from 'react-native-paper';
32
+ import StyledButton from './StyledButton';
32
33
 
33
34
  export type FaceCameraProps = {
34
35
  onFacesDetected: (
@@ -174,14 +175,14 @@ const FaceCamera = ({
174
175
  <Text style={styles.permissionText}>
175
176
  {t('general.noCameraPermissionGiven')}
176
177
  </Text>
177
- <Button
178
+ <StyledButton
178
179
  mode="contained"
179
180
  onPress={() => {
180
181
  Linking.openSettings();
181
182
  }}
182
183
  >
183
184
  {t('general.openSettings')}
184
- </Button>
185
+ </StyledButton>
185
186
  </View>
186
187
  );
187
188
  }
@@ -192,14 +193,14 @@ const FaceCamera = ({
192
193
  <Text style={styles.permissionText}>
193
194
  {t('general.noMicrophonePermissionGiven')}
194
195
  </Text>
195
- <Button
196
+ <StyledButton
196
197
  mode="contained"
197
198
  onPress={() => {
198
199
  Linking.openSettings();
199
200
  }}
200
201
  >
201
202
  {t('general.openSettings')}
202
- </Button>
203
+ </StyledButton>
203
204
  </View>
204
205
  );
205
206
  }
@@ -46,7 +46,8 @@ import {
46
46
  import { getAverageBrightness } from '../Libs/camera.utils';
47
47
  import { useTranslation } from 'react-i18next';
48
48
  import LottieView from 'lottie-react-native';
49
- import { ActivityIndicator, Button } from 'react-native-paper';
49
+ import { ActivityIndicator } from 'react-native-paper';
50
+ import StyledButton from './StyledButton';
50
51
  import { type Barcode, scanCodes } from '../VisionCameraPlugins/BarcodeScanner';
51
52
  import { speakWithDebounce } from '../Libs/tts.utils';
52
53
  import AppContext from '../Contexts/AppContext';
@@ -981,14 +982,14 @@ const IdentityDocumentCamera = ({
981
982
  <Text style={styles.permissionText}>
982
983
  {t('general.noCameraPermissionGiven')}
983
984
  </Text>
984
- <Button
985
+ <StyledButton
985
986
  mode="contained"
986
987
  onPress={() => {
987
988
  Linking.openSettings();
988
989
  }}
989
990
  >
990
991
  {t('general.openSettings')}
991
- </Button>
992
+ </StyledButton>
992
993
  </View>
993
994
  );
994
995
  }
@@ -1044,14 +1045,14 @@ const IdentityDocumentCamera = ({
1044
1045
  • {t('identityDocumentCamera.guidePoint3')}
1045
1046
  </TextView>
1046
1047
  </View>
1047
- <Button
1048
+ <StyledButton
1048
1049
  mode="contained"
1049
1050
  onPress={() => {
1050
1051
  setHasGuideShown(true);
1051
1052
  }}
1052
1053
  >
1053
1054
  {t('general.letsGo')}
1054
- </Button>
1055
+ </StyledButton>
1055
1056
  </View>
1056
1057
  ) : (
1057
1058
  <>
@@ -22,7 +22,7 @@ import { useIsFocused } from '@react-navigation/native';
22
22
  import { useTranslation } from 'react-i18next';
23
23
  import LottieView from 'lottie-react-native';
24
24
  import { type Barcode, scanCodes } from '../VisionCameraPlugins/BarcodeScanner';
25
- import { Button } from 'react-native-paper';
25
+ import StyledButton from './StyledButton';
26
26
 
27
27
  export interface QrCodeScannerCameraProps {
28
28
  onQrCodeScanned: (data: string) => void;
@@ -175,14 +175,14 @@ const QrCodeScannerCamera = ({ onQrCodeScanned }: QrCodeScannerCameraProps) => {
175
175
  <Text style={styles.permissionText}>
176
176
  {t('general.noCameraPermissionGiven')}
177
177
  </Text>
178
- <Button
178
+ <StyledButton
179
179
  mode="contained"
180
180
  onPress={() => {
181
181
  Linking.openSettings();
182
182
  }}
183
183
  >
184
184
  {t('general.openSettings')}
185
- </Button>
185
+ </StyledButton>
186
186
  </View>
187
187
  );
188
188
  }
@@ -0,0 +1,35 @@
1
+ import React from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import { Button } from 'react-native-paper';
4
+ import type { ButtonProps } from 'react-native-paper';
5
+
6
+ type StyledButtonProps = ButtonProps;
7
+
8
+ const StyledButton: React.FC<StyledButtonProps> = ({
9
+ style,
10
+ labelStyle,
11
+ ...props
12
+ }) => {
13
+ return (
14
+ <Button
15
+ {...props}
16
+ style={[styles.button, style]}
17
+ labelStyle={[styles.label, labelStyle]}
18
+ />
19
+ );
20
+ };
21
+
22
+ const styles = StyleSheet.create({
23
+ button: {
24
+ borderRadius: 8,
25
+ height: 56,
26
+ justifyContent: 'center',
27
+ elevation: 2,
28
+ },
29
+ label: {
30
+ fontWeight: 'bold',
31
+ fontSize: 16,
32
+ },
33
+ });
34
+
35
+ export default StyledButton;
@@ -0,0 +1 @@
1
+ export * from './validation.constants';
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Validation constants used throughout the SDK.
3
+ * These constants ensure consistency between frontend and backend validation.
4
+ */
5
+
6
+ /**
7
+ * Character set for session codes.
8
+ * Excludes ambiguous characters (0, 1, I, O) to prevent confusion.
9
+ */
10
+ export const SESSION_CODE_CHARSET = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789';
11
+
12
+ /**
13
+ * Regular expression pattern for validating session code characters.
14
+ * Matches only characters from SESSION_CODE_CHARSET.
15
+ */
16
+ export const SESSION_CODE_CHARSET_PATTERN =
17
+ /[^ABCDEFGHJKLMNPQRSTUVWXYZ23456789]/g;
18
+
19
+ /**
20
+ * Regular expression for validating complete session codes.
21
+ * Session codes must be exactly 8 characters from SESSION_CODE_CHARSET.
22
+ */
23
+ export const SESSION_CODE_VALIDATION_PATTERN =
24
+ /^[ABCDEFGHJKLMNPQRSTUVWXYZ23456789]{8}$/;