@trustchex/react-native-sdk 1.214.0 → 1.245.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 (48) 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 +2 -2
  4. package/lib/module/Screens/Static/VerificationSessionCheckScreen.js +148 -99
  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/VerificationSessionCheckScreen.d.ts.map +1 -1
  16. package/lib/typescript/src/Shared/Components/FaceCamera.d.ts.map +1 -1
  17. package/lib/typescript/src/Shared/Components/IdentityDocumentCamera.d.ts.map +1 -1
  18. package/lib/typescript/src/Shared/Components/StyledButton.d.ts +6 -0
  19. package/lib/typescript/src/Shared/Components/StyledButton.d.ts.map +1 -0
  20. package/lib/typescript/src/Shared/Constants/index.d.ts +2 -0
  21. package/lib/typescript/src/Shared/Constants/index.d.ts.map +1 -0
  22. package/lib/typescript/src/Shared/Constants/validation.constants.d.ts +20 -0
  23. package/lib/typescript/src/Shared/Constants/validation.constants.d.ts.map +1 -0
  24. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts +2 -2
  25. package/lib/typescript/src/Shared/Libs/demo.utils.d.ts.map +1 -1
  26. package/lib/typescript/src/Translation/Resources/en.d.ts +1 -3
  27. package/lib/typescript/src/Translation/Resources/en.d.ts.map +1 -1
  28. package/lib/typescript/src/Translation/Resources/tr.d.ts +1 -6
  29. package/lib/typescript/src/Translation/Resources/tr.d.ts.map +1 -1
  30. package/package.json +1 -1
  31. package/src/Screens/Dynamic/ContractAcceptanceScreen.tsx +3 -3
  32. package/src/Screens/Dynamic/LivenessDetectionScreen.tsx +3 -3
  33. package/src/Screens/Static/ResultScreen.tsx +3 -3
  34. package/src/Screens/Static/VerificationSessionCheckScreen.tsx +179 -123
  35. package/src/Shared/Components/EIDScanner.tsx +7 -7
  36. package/src/Shared/Components/FaceCamera.tsx +6 -5
  37. package/src/Shared/Components/IdentityDocumentCamera.tsx +6 -5
  38. package/src/Shared/Components/QrCodeScannerCamera.tsx +3 -3
  39. package/src/Shared/Components/StyledButton.tsx +35 -0
  40. package/src/Shared/Constants/index.ts +1 -0
  41. package/src/Shared/Constants/validation.constants.ts +24 -0
  42. package/src/Shared/Libs/demo.utils.ts +5 -4
  43. package/src/Translation/Resources/en.ts +51 -54
  44. package/src/Translation/Resources/tr.ts +50 -55
  45. package/lib/module/Shared/Components/OTPCodeInput.js +0 -74
  46. package/lib/typescript/src/Shared/Components/OTPCodeInput.d.ts +0 -10
  47. package/lib/typescript/src/Shared/Components/OTPCodeInput.d.ts.map +0 -1
  48. package/src/Shared/Components/OTPCodeInput.tsx +0 -93
@@ -20,25 +20,28 @@ import {
20
20
  import { useSafeAreaInsets } from 'react-native-safe-area-context';
21
21
  import AppContext from '../../Shared/Contexts/AppContext';
22
22
  import httpClient, { NotFoundError } from '../../Shared/Libs/http-client';
23
- import OTPCodeInput from '../../Shared/Components/OTPCodeInput';
24
23
  import { useTranslation } from 'react-i18next';
25
24
  import LanguageSelector from '../../Shared/Components/LanguageSelector';
26
25
  import NavigationManager, {
27
26
  type NavigationManagerRef,
28
27
  } from '../../Shared/Components/NavigationManager';
29
28
  import type { VerificationSession } from '../../Shared/Types/verificationSession';
30
- import { Button, TextInput, useTheme } from 'react-native-paper';
29
+ import { TextInput, useTheme } from 'react-native-paper';
30
+ import StyledButton from '../../Shared/Components/StyledButton';
31
31
  import LottieView from 'lottie-react-native';
32
32
  import {
33
33
  getSimulatedDemoData,
34
34
  isDemoSession,
35
35
  } from '../../Shared/Libs/demo.utils';
36
36
  import { useNavigation } from '@react-navigation/native';
37
+ import {
38
+ SESSION_CODE_CHARSET_PATTERN,
39
+ SESSION_CODE_VALIDATION_PATTERN,
40
+ } from '../../Shared/Constants/validation.constants';
37
41
 
38
42
  const VerificationSessionCheckScreen = () => {
39
- const [emailOrPhone, setEmailOrPhone] = useState('');
43
+ const [sessionCode, setSessionCode] = useState('');
40
44
  const [code, setCode] = useState('');
41
- const [isEnabled, setIsEnabled] = useState(false);
42
45
  const [isCheckingSession, setIsCheckingSession] = useState(false);
43
46
  const [isSendAgainEnabled, setIsSendAgainEnabled] = useState(false);
44
47
  const [isCodeSent, setIsCodeSent] = useState(false);
@@ -60,14 +63,8 @@ const VerificationSessionCheckScreen = () => {
60
63
  }
61
64
  }, [appContext.baseUrl]);
62
65
 
63
- const validateEmail = useCallback((value: string) => {
64
- const re = /\S+@\S+\.\S+/;
65
- return re.test(value);
66
- }, []);
67
-
68
- const validatePhoneNumber = useCallback((value: string) => {
69
- const re = /^(5(\d{9}))/;
70
- return re.test(value);
66
+ const validateSessionCode = useCallback((value: string) => {
67
+ return SESSION_CODE_VALIDATION_PATTERN.test(value.toUpperCase());
71
68
  }, []);
72
69
 
73
70
  const getSession = useCallback(
@@ -94,29 +91,20 @@ const VerificationSessionCheckScreen = () => {
94
91
  [apiUrl, appContext.isDemoSession, t]
95
92
  );
96
93
 
97
- const getSessions = useCallback(
98
- async (value: string) => {
94
+ const getSessionByCode = useCallback(
95
+ async (inputCode: string) => {
99
96
  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
- }
97
+ appContext.isDemoSession = isDemoSession(inputCode);
98
+ const response = await httpClient.get<{ id: string }>(
99
+ `${apiUrl}/verification-sessions?sessionCode=${inputCode.toUpperCase()}`,
100
+ appContext.isDemoSession
101
+ ? getSimulatedDemoData<{ id: string } | undefined, void>(
102
+ 'GET_SESSION_BY_CODE'
103
+ )
104
+ : undefined
105
+ );
118
106
 
119
- if (!response || response.length === 0) {
107
+ if (!response || !response.id) {
120
108
  throw new NotFoundError();
121
109
  }
122
110
 
@@ -130,7 +118,7 @@ const VerificationSessionCheckScreen = () => {
130
118
  }
131
119
  }
132
120
  },
133
- [apiUrl, appContext, t, validateEmail, validatePhoneNumber]
121
+ [apiUrl, appContext, t]
134
122
  );
135
123
 
136
124
  const sendVerificationCode = useCallback(
@@ -194,7 +182,7 @@ const VerificationSessionCheckScreen = () => {
194
182
  [apiUrl, appContext.isDemoSession, t]
195
183
  );
196
184
 
197
- const sendCode = useCallback(
185
+ const sendOTPCode = useCallback(
198
186
  async (sessionId: string) => {
199
187
  if (!sessionId) {
200
188
  return false;
@@ -237,7 +225,7 @@ const VerificationSessionCheckScreen = () => {
237
225
  }
238
226
 
239
227
  if (session?.sendOTP) {
240
- return sendCode(session.id);
228
+ return sendOTPCode(session.id);
241
229
  } else if (session?.identificationId) {
242
230
  appContext.identificationInfo.identificationId =
243
231
  session?.identificationId;
@@ -254,7 +242,7 @@ const VerificationSessionCheckScreen = () => {
254
242
  appContext,
255
243
  appContext.identificationInfo.sessionId,
256
244
  getSession,
257
- sendCode,
245
+ sendOTPCode,
258
246
  theme.colors,
259
247
  theme.colors.primary,
260
248
  theme.colors.secondary,
@@ -302,107 +290,138 @@ const VerificationSessionCheckScreen = () => {
302
290
  <Text style={styles.mainText}>
303
291
  {t('verificationSessionCheckScreen.mainText')}
304
292
  </Text>
305
- <TextInput
306
- mode="outlined"
307
- autoCapitalize="none"
308
- placeholder={t(
309
- 'verificationSessionCheckScreen.inputPlaceholder'
293
+ <View style={styles.inputContainer}>
294
+ <TextInput
295
+ mode="outlined"
296
+ autoCapitalize="characters"
297
+ placeholder=""
298
+ outlineColor={theme.colors.primary}
299
+ activeOutlineColor={theme.colors.primary}
300
+ style={styles.sessionCodeTextInput}
301
+ contentStyle={styles.sessionCodeInputWithText}
302
+ onChangeText={async (text) => {
303
+ const alphanumericText = text
304
+ .toUpperCase()
305
+ .replace(SESSION_CODE_CHARSET_PATTERN, '');
306
+ if (alphanumericText.length <= 8) {
307
+ setSessionCode(alphanumericText);
308
+ if (
309
+ validateSessionCode(alphanumericText) &&
310
+ alphanumericText.length === 8
311
+ ) {
312
+ try {
313
+ setIsCheckingSession(true);
314
+ const session =
315
+ await getSessionByCode(alphanumericText);
316
+ if (session?.id) {
317
+ appContext.identificationInfo.sessionId =
318
+ session.id;
319
+ } else {
320
+ setSessionCode('');
321
+ }
322
+ } catch {
323
+ setSessionCode('');
324
+ } finally {
325
+ setTimeout(() => {
326
+ setIsCheckingSession(false);
327
+ }, 1000);
328
+ }
329
+ }
330
+ }
331
+ }}
332
+ value={sessionCode.toUpperCase()}
333
+ maxLength={8}
334
+ />
335
+ {!sessionCode && (
336
+ <Text style={styles.placeholderText}>
337
+ {t('verificationSessionCheckScreen.enterSessionCode')}
338
+ </Text>
310
339
  )}
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
340
+ </View>
341
+ <View style={styles.horizontalLineContainer}>
342
+ <View
343
+ style={[
344
+ styles.horizontalLine,
345
+ { backgroundColor: theme.colors.primary },
346
+ ]}
347
+ />
348
+ <Text style={styles.mainText}>
349
+ {t('verificationSessionCheckScreen.or')}
350
+ </Text>
351
+ <View
352
+ style={[
353
+ styles.horizontalLine,
354
+ { backgroundColor: theme.colors.primary },
355
+ ]}
356
+ />
357
+ </View>
358
+ <StyledButton
321
359
  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
- }
360
+ onPress={() => {
361
+ navigation.navigate('QrCodeScanningScreen' as never);
339
362
  }}
340
363
  >
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
- )}
364
+ {t('verificationSessionCheckScreen.scanQRCode')}
365
+ </StyledButton>
362
366
  </>
363
367
  ) : (
364
368
  <>
365
369
  <Text style={styles.mainText}>
366
370
  {t('verificationSessionCheckScreen.codeText')}
367
371
  </Text>
368
- <OTPCodeInput
369
- value={code}
372
+ <TextInput
373
+ mode="outlined"
374
+ autoFocus={true}
375
+ placeholder=""
376
+ outlineColor={theme.colors.primary}
377
+ activeOutlineColor={theme.colors.primary}
378
+ style={styles.otpCodeTextInput}
379
+ contentStyle={styles.otpCodeInputWithText}
380
+ keyboardType="number-pad"
381
+ textContentType="oneTimeCode"
382
+ autoComplete="sms-otp"
383
+ maxLength={6}
370
384
  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);
385
+ const numericText = text.replace(/[^0-9]/g, '');
386
+ if (numericText.length <= 6) {
387
+ setCode(numericText);
388
+ if (numericText.length === 6) {
389
+ (async () => {
390
+ setIsCodeGettingVerified(true);
391
+ const verifiedSession = await getVerifiedSession(
392
+ appContext.identificationInfo.sessionId,
393
+ numericText
394
+ );
395
+ if (verifiedSession?.identificationId) {
396
+ appContext.identificationInfo.identificationId =
397
+ verifiedSession?.identificationId;
398
+ setCode('');
399
+ setIsCodeSent(false);
400
+ navigationManagerRef.current?.navigateToNextStep();
401
+ } else {
402
+ appContext.onError?.('Invalid OTP code');
403
+ Alert.alert(
404
+ t('general.error'),
405
+ t('verificationSessionCheckScreen.codeError')
406
+ );
407
+ setCode('');
408
+ setIsCodeGettingVerified(false);
409
+ }
410
+ })();
411
+ }
394
412
  }
395
413
  }}
414
+ value={code}
396
415
  />
397
- <Button
416
+ <StyledButton
398
417
  mode="contained"
399
418
  disabled={!isSendAgainEnabled}
400
419
  onPress={() => {
401
- sendCode(appContext.identificationInfo.sessionId);
420
+ sendOTPCode(appContext.identificationInfo.sessionId);
402
421
  }}
403
422
  >
404
423
  {t('verificationSessionCheckScreen.sendCodeAgain')}
405
- </Button>
424
+ </StyledButton>
406
425
  </>
407
426
  )}
408
427
  </View>
@@ -440,17 +459,15 @@ const styles = StyleSheet.create({
440
459
  justifyContent: 'center',
441
460
  },
442
461
  horizontalLineContainer: {
443
- display: 'flex',
444
462
  flexDirection: 'row',
445
463
  alignItems: 'center',
446
- justifyContent: 'center',
464
+ justifyContent: 'space-between',
447
465
  gap: 10,
448
- padding: 10,
466
+ paddingVertical: 10,
449
467
  },
450
468
  horizontalLine: {
451
469
  flex: 1,
452
- height: 1,
453
- backgroundColor: '#aaa',
470
+ height: 2,
454
471
  },
455
472
  loadingAnimation: {
456
473
  width: '100%',
@@ -519,6 +536,45 @@ const styles = StyleSheet.create({
519
536
  alignItems: 'center',
520
537
  position: 'absolute',
521
538
  },
539
+ inputContainer: {
540
+ position: 'relative',
541
+ width: '100%',
542
+ },
543
+ sessionCodeTextInput: {
544
+ backgroundColor: 'white',
545
+ },
546
+ sessionCodeInputWithText: {
547
+ textTransform: 'uppercase',
548
+ fontWeight: '800',
549
+ fontSize: 28,
550
+ textAlign: 'center',
551
+ letterSpacing: 8,
552
+ },
553
+ placeholderText: {
554
+ position: 'absolute',
555
+ top: 0,
556
+ left: 0,
557
+ right: 0,
558
+ height: '100%',
559
+ lineHeight: 56,
560
+ fontSize: 16,
561
+ color: '#666',
562
+ textAlign: 'center',
563
+ pointerEvents: 'none',
564
+ },
565
+ sessionCodeInputPlaceholder: {
566
+ fontSize: 16,
567
+ textAlign: 'center',
568
+ },
569
+ otpCodeTextInput: {
570
+ backgroundColor: 'white',
571
+ },
572
+ otpCodeInputWithText: {
573
+ fontWeight: '800',
574
+ fontSize: 28,
575
+ textAlign: 'center',
576
+ letterSpacing: 8,
577
+ },
522
578
  });
523
579
 
524
580
  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}$/;