@dloizides/auth-web 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext } from 'react';
2
- import { View, Text, TextInput, TouchableOpacity, ActivityIndicator, StyleSheet } from 'react-native';
2
+ import { StyleSheet, View, Text, TextInput, TouchableOpacity, ActivityIndicator, Pressable } from 'react-native';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import { useMutation } from '@tanstack/react-query';
5
5
  import { BffAuthClient, createFetchHttpClient } from '@dloizides/auth-client';
@@ -68,6 +68,77 @@ var DEFAULT_PIN_LABELS = {
68
68
  missingPin: "Enter your event PIN.",
69
69
  invalidPin: "That PIN is incorrect, has expired, or is locked out."
70
70
  };
71
+ var DEFAULT_DEVICE_PIN_UNLOCK_LABELS = {
72
+ title: "Welcome back, {name}",
73
+ titleNoName: "Welcome back",
74
+ description: "Enter your {count}-digit PIN to sign in on this device.",
75
+ pinLabel: "PIN",
76
+ pinPlaceholder: "Enter your PIN",
77
+ submit: "Unlock",
78
+ submitting: "Signing in...",
79
+ usePasswordInstead: "Sign in with password instead",
80
+ usePasswordHint: "Switch to signing in with your username and password",
81
+ errorIncomplete: "Enter all {count} digits of your PIN.",
82
+ errorInvalid: "That PIN is incorrect. Try again.",
83
+ errorLockedOut: "Too many attempts. Try again later.",
84
+ errorLockedOutRetry: "Too many attempts. Try again in {count} seconds.",
85
+ errorRateLimited: "Too many requests. Try again later.",
86
+ errorRateLimitedRetry: "Too many requests. Try again in {count} seconds.",
87
+ errorGeneric: "Something went wrong. Try again, or sign in with your password.",
88
+ digitFilledHint: "PIN digit entered",
89
+ digitEmptyHint: "PIN digit not yet entered"
90
+ };
91
+ var DEFAULT_DEVICE_PIN_ENROLL_LABELS = {
92
+ offerTitle: "Set a PIN for faster sign-in?",
93
+ offerDescription: "Next time, sign in on this device with a short PIN instead of your full password.",
94
+ offerAccept: "Set up a PIN",
95
+ offerAcceptHint: "Open the form to choose a quick sign-in PIN",
96
+ offerSkip: "Not now",
97
+ offerSkipHint: "Dismiss the PIN setup offer",
98
+ formTitle: "Choose your PIN",
99
+ formDescription: "Pick a {count}-digit PIN you will remember. You will use it to sign in on this device.",
100
+ lengthLabel: "PIN length",
101
+ lengthOptionHint: "Use a {count}-digit PIN",
102
+ pinLabel: "New PIN",
103
+ pinPlaceholder: "Enter a new PIN",
104
+ confirmLabel: "Confirm PIN",
105
+ confirmPlaceholder: "Re-enter your PIN",
106
+ submit: "Save PIN",
107
+ submitting: "Saving...",
108
+ cancel: "Cancel",
109
+ cancelHint: "Close the PIN setup form without saving",
110
+ errorMismatch: "The PINs must match and be exactly {count} digits.",
111
+ errorUnauthorized: "Your session has expired. Sign in again to set a PIN.",
112
+ errorForbidden: "A PIN is already set for this session.",
113
+ errorInvalidPin: "That PIN is not allowed. Choose a different one.",
114
+ errorFailed: "Could not set your PIN. Try again later."
115
+ };
116
+ var DEFAULT_DEVICE_PIN_SETTINGS_LABELS = {
117
+ title: "Quick PIN sign-in",
118
+ description: "Sign in on this device with a short PIN instead of your full password.",
119
+ statusEnabled: "Quick PIN sign-in is enabled on this device.",
120
+ statusDisabled: "Quick PIN sign-in is off.",
121
+ enable: "Enable quick PIN sign-in",
122
+ enableHint: "Set a PIN so you can sign in faster on this device",
123
+ disable: "Disable",
124
+ disableHint: "Remove the PIN sign-in from this device",
125
+ disabling: "Disabling...",
126
+ disableFailed: "Could not disable PIN sign-in. Try again later."
127
+ };
128
+ var DEFAULT_PASSKEY_LOGIN_LABELS = {
129
+ signInButton: "Sign in with a passkey",
130
+ signInHint: "Use your fingerprint, face, or security key to sign in",
131
+ errorCancelled: "Passkey sign-in was cancelled. You can try again or sign in another way.",
132
+ errorFailed: "Passkey sign-in didn't work. Try again, or sign in another way."
133
+ };
134
+ var DEFAULT_PASSKEY_SETTINGS_LABELS = {
135
+ title: "Passkeys",
136
+ description: "Add a passkey to sign in with your fingerprint, face, or a security key instead of a password.",
137
+ reauthHint: "You'll be asked to confirm your password before adding a passkey.",
138
+ addButton: "Add a passkey",
139
+ addHint: "Start setting up a passkey for this account",
140
+ registeredSuccess: "Your passkey was added. You can now use it to sign in."
141
+ };
71
142
  var DEFAULT_RESET_PASSWORD_LABELS = {
72
143
  title: "Reset password",
73
144
  description: "Choose a new password for your account.",
@@ -116,7 +187,39 @@ var AuthTestIds = {
116
187
  pinForm: "auth-pin-form",
117
188
  pinInput: "auth-pin-input",
118
189
  pinSubmitButton: "auth-pin-submit",
119
- pinError: "auth-pin-error"
190
+ pinError: "auth-pin-error",
191
+ // Device-PIN unlock gate
192
+ devicePinUnlock: "auth-device-pin-unlock",
193
+ devicePinUnlockInput: "auth-device-pin-unlock-input",
194
+ devicePinUnlockSubmit: "auth-device-pin-unlock-submit",
195
+ devicePinUnlockUsePassword: "auth-device-pin-unlock-use-password",
196
+ devicePinUnlockError: "auth-device-pin-unlock-error",
197
+ // Device-PIN enrol form + length picker
198
+ devicePinEnrollForm: "auth-device-pin-enroll-form",
199
+ devicePinEnrollLength: "auth-device-pin-enroll-length",
200
+ devicePinEnrollPin: "auth-device-pin-enroll-pin",
201
+ devicePinEnrollConfirm: "auth-device-pin-enroll-confirm",
202
+ devicePinEnrollSubmit: "auth-device-pin-enroll-submit",
203
+ devicePinEnrollCancel: "auth-device-pin-enroll-cancel",
204
+ devicePinEnrollError: "auth-device-pin-enroll-error",
205
+ // Device-PIN offer (post-login)
206
+ devicePinOffer: "auth-device-pin-offer",
207
+ devicePinOfferAccept: "auth-device-pin-offer-accept",
208
+ devicePinOfferSkip: "auth-device-pin-offer-skip",
209
+ // Device-PIN settings card
210
+ devicePinSettings: "auth-device-pin-settings",
211
+ devicePinSettingsStatus: "auth-device-pin-settings-status",
212
+ devicePinSettingsEnable: "auth-device-pin-settings-enable",
213
+ devicePinSettingsDisable: "auth-device-pin-settings-disable",
214
+ devicePinSettingsError: "auth-device-pin-settings-error",
215
+ // Passkey login button
216
+ passkeyLogin: "auth-passkey-login",
217
+ passkeyLoginButton: "auth-passkey-login-button",
218
+ passkeyLoginError: "auth-passkey-login-error",
219
+ // Passkey settings card
220
+ passkeySettings: "auth-passkey-settings",
221
+ passkeySettingsAdd: "auth-passkey-settings-add",
222
+ passkeySettingsSuccess: "auth-passkey-settings-success"
120
223
  };
121
224
  function withTestIdPrefix(baseId, prefix) {
122
225
  return prefix === void 0 || prefix === "" ? baseId : `${prefix}-${baseId}`;
@@ -210,6 +313,32 @@ function useAuthStyles(theme) {
210
313
  helperText: {
211
314
  fontSize: typography.caption,
212
315
  color: colors.textSecondary
316
+ },
317
+ escapeLink: {
318
+ alignSelf: "center",
319
+ marginTop: spacing.md,
320
+ paddingVertical: spacing.xs / 2
321
+ },
322
+ secondaryButton: {
323
+ borderRadius: radii.input,
324
+ borderWidth: 1,
325
+ borderColor: colors.primary,
326
+ paddingVertical: BUTTON_VERTICAL_PADDING,
327
+ alignItems: "center",
328
+ justifyContent: "center",
329
+ marginTop: spacing.sm,
330
+ backgroundColor: colors.surface
331
+ },
332
+ secondaryButtonText: {
333
+ fontSize: typography.body,
334
+ fontWeight: "600",
335
+ color: colors.primary
336
+ },
337
+ statusText: {
338
+ fontSize: typography.label,
339
+ fontWeight: "600",
340
+ marginTop: spacing.sm,
341
+ color: colors.text
213
342
  }
214
343
  });
215
344
  }, [theme]);
@@ -371,7 +500,7 @@ function LoginForm({
371
500
  testIdPrefix
372
501
  }) {
373
502
  const theme = useAuthTheme(themeProp);
374
- const styles = useAuthStyles(theme);
503
+ const styles3 = useAuthStyles(theme);
375
504
  const labels = useMemo(
376
505
  () => ({ ...DEFAULT_LOGIN_LABELS, ...labelsProp }),
377
506
  [labelsProp]
@@ -398,12 +527,12 @@ function LoginForm({
398
527
  const handleSubmit = useCallback(() => {
399
528
  void runLogin();
400
529
  }, [runLogin]);
401
- const submitButtonStyle = isSubmitting ? [styles.primaryButton, styles.primaryButtonDisabled] : styles.primaryButton;
402
- return /* @__PURE__ */ jsx(View, { style: styles.screen, children: /* @__PURE__ */ jsxs(View, { style: styles.card, testID: withTestIdPrefix(AuthTestIds.loginForm, testIdPrefix), children: [
403
- /* @__PURE__ */ jsx(Text, { style: styles.title, children: labels.title }),
404
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: labels.subtitle }),
405
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
406
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.usernameLabel }),
530
+ const submitButtonStyle = isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
531
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsxs(View, { style: styles3.card, testID: withTestIdPrefix(AuthTestIds.loginForm, testIdPrefix), children: [
532
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
533
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.subtitle }),
534
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
535
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.usernameLabel }),
407
536
  /* @__PURE__ */ jsx(
408
537
  TextInput,
409
538
  {
@@ -414,15 +543,15 @@ function LoginForm({
414
543
  editable: !isSubmitting,
415
544
  placeholder: labels.usernamePlaceholder,
416
545
  placeholderTextColor: theme.colors.textSecondary,
417
- style: styles.input,
546
+ style: styles3.input,
418
547
  testID: withTestIdPrefix(AuthTestIds.loginUsernameInput, testIdPrefix),
419
548
  value: username,
420
549
  onChangeText: setUsername
421
550
  }
422
551
  )
423
552
  ] }),
424
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
425
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.passwordLabel }),
553
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
554
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.passwordLabel }),
426
555
  /* @__PURE__ */ jsx(
427
556
  TextInput,
428
557
  {
@@ -434,7 +563,7 @@ function LoginForm({
434
563
  editable: !isSubmitting,
435
564
  placeholder: labels.passwordPlaceholder,
436
565
  placeholderTextColor: theme.colors.textSecondary,
437
- style: styles.input,
566
+ style: styles3.input,
438
567
  testID: withTestIdPrefix(AuthTestIds.loginPasswordInput, testIdPrefix),
439
568
  value: password,
440
569
  onChangeText: setPassword
@@ -444,7 +573,7 @@ function LoginForm({
444
573
  errorText !== null ? /* @__PURE__ */ jsx(
445
574
  Text,
446
575
  {
447
- style: styles.errorText,
576
+ style: styles3.errorText,
448
577
  testID: withTestIdPrefix(AuthTestIds.loginError, testIdPrefix),
449
578
  children: errorText
450
579
  }
@@ -456,10 +585,10 @@ function LoginForm({
456
585
  accessibilityLabel: labels.forgotPassword,
457
586
  accessibilityRole: "link",
458
587
  disabled: isSubmitting,
459
- style: styles.fieldGroup,
588
+ style: styles3.fieldGroup,
460
589
  testID: withTestIdPrefix(AuthTestIds.loginForgotLink, testIdPrefix),
461
590
  onPress: onForgotPassword,
462
- children: /* @__PURE__ */ jsx(Text, { style: styles.linkText, children: labels.forgotPassword })
591
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.linkText, children: labels.forgotPassword })
463
592
  }
464
593
  ) : null,
465
594
  /* @__PURE__ */ jsx(
@@ -472,7 +601,7 @@ function LoginForm({
472
601
  style: submitButtonStyle,
473
602
  testID: withTestIdPrefix(AuthTestIds.loginSubmitButton, testIdPrefix),
474
603
  onPress: handleSubmit,
475
- children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.submit })
604
+ children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
476
605
  }
477
606
  ),
478
607
  onSignUp !== void 0 ? /* @__PURE__ */ jsx(
@@ -482,10 +611,10 @@ function LoginForm({
482
611
  accessibilityLabel: labels.signUp,
483
612
  accessibilityRole: "link",
484
613
  disabled: isSubmitting,
485
- style: styles.fieldGroup,
614
+ style: styles3.fieldGroup,
486
615
  testID: withTestIdPrefix(AuthTestIds.loginSignUpLink, testIdPrefix),
487
616
  onPress: onSignUp,
488
- children: /* @__PURE__ */ jsx(Text, { style: styles.linkText, children: labels.signUp })
617
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.linkText, children: labels.signUp })
489
618
  }
490
619
  ) : null
491
620
  ] }) });
@@ -523,7 +652,7 @@ function ForgotPasswordForm({
523
652
  testIdPrefix
524
653
  }) {
525
654
  const theme = useAuthTheme(themeProp);
526
- const styles = useAuthStyles(theme);
655
+ const styles3 = useAuthStyles(theme);
527
656
  const labels = useMemo(
528
657
  () => ({ ...DEFAULT_FORGOT_PASSWORD_LABELS, ...labelsProp }),
529
658
  [labelsProp]
@@ -555,25 +684,25 @@ function ForgotPasswordForm({
555
684
  }
556
685
  mutation.mutate({ email: trimmedEmail, resetUrlTemplate });
557
686
  }, [email, mutation, resetUrlTemplate, labels.invalidEmail]);
558
- const submitButtonStyle = isPending ? [styles.primaryButton, styles.primaryButtonDisabled] : styles.primaryButton;
559
- return /* @__PURE__ */ jsx(View, { style: styles.screen, children: /* @__PURE__ */ jsxs(
687
+ const submitButtonStyle = isPending ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
688
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsxs(
560
689
  View,
561
690
  {
562
- style: styles.card,
691
+ style: styles3.card,
563
692
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordForm, testIdPrefix),
564
693
  children: [
565
- /* @__PURE__ */ jsx(Text, { style: styles.title, children: labels.title }),
694
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
566
695
  submitted ? /* @__PURE__ */ jsx(
567
696
  Text,
568
697
  {
569
- style: styles.successText,
698
+ style: styles3.successText,
570
699
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordSuccess, testIdPrefix),
571
700
  children: labels.successMessage
572
701
  }
573
702
  ) : /* @__PURE__ */ jsxs(Fragment, { children: [
574
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: labels.description }),
575
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
576
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.emailLabel }),
703
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.description }),
704
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
705
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.emailLabel }),
577
706
  /* @__PURE__ */ jsx(
578
707
  TextInput,
579
708
  {
@@ -585,7 +714,7 @@ function ForgotPasswordForm({
585
714
  keyboardType: "email-address",
586
715
  placeholder: labels.emailPlaceholder,
587
716
  placeholderTextColor: theme.colors.textSecondary,
588
- style: styles.input,
717
+ style: styles3.input,
589
718
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordEmailInput, testIdPrefix),
590
719
  value: email,
591
720
  onChangeText: setEmail
@@ -595,7 +724,7 @@ function ForgotPasswordForm({
595
724
  errorText !== null ? /* @__PURE__ */ jsx(
596
725
  Text,
597
726
  {
598
- style: styles.errorText,
727
+ style: styles3.errorText,
599
728
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordError, testIdPrefix),
600
729
  children: errorText
601
730
  }
@@ -610,7 +739,7 @@ function ForgotPasswordForm({
610
739
  style: submitButtonStyle,
611
740
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordSubmitButton, testIdPrefix),
612
741
  onPress: handleSubmit,
613
- children: isPending ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.submit })
742
+ children: isPending ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
614
743
  }
615
744
  )
616
745
  ] })
@@ -702,7 +831,7 @@ function ForgotPasswordFields({
702
831
  testIdPrefix
703
832
  }) {
704
833
  const theme = useAuthTheme(themeProp);
705
- const styles = useAuthStyles(theme);
834
+ const styles3 = useAuthStyles(theme);
706
835
  const modalStyles = useModalStyles(theme);
707
836
  const labels = useMemo(
708
837
  () => ({ ...DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, ...labelsProp }),
@@ -715,7 +844,7 @@ function ForgotPasswordFields({
715
844
  reset();
716
845
  }
717
846
  }, [visible, reset]);
718
- const submitButtonStyle = form.canSubmit ? styles.primaryButton : [styles.primaryButton, styles.primaryButtonDisabled];
847
+ const submitButtonStyle = form.canSubmit ? styles3.primaryButton : [styles3.primaryButton, styles3.primaryButtonDisabled];
719
848
  const handleCancel = () => {
720
849
  form.reset();
721
850
  onCancel?.();
@@ -726,25 +855,25 @@ function ForgotPasswordFields({
726
855
  };
727
856
  if (form.submitted) {
728
857
  return /* @__PURE__ */ jsxs(View, { style: modalStyles.body, testID: withTestIdPrefix(AuthTestIds.forgotPasswordForm, testIdPrefix), children: [
729
- /* @__PURE__ */ jsx(Text, { style: styles.successText, testID: withTestIdPrefix(AuthTestIds.forgotPasswordSuccess, testIdPrefix), children: labels.successMessage }),
858
+ /* @__PURE__ */ jsx(Text, { style: styles3.successText, testID: withTestIdPrefix(AuthTestIds.forgotPasswordSuccess, testIdPrefix), children: labels.successMessage }),
730
859
  onClose !== void 0 ? /* @__PURE__ */ jsx(View, { style: modalStyles.actions, children: /* @__PURE__ */ jsx(
731
860
  TouchableOpacity,
732
861
  {
733
862
  accessibilityHint: labels.close,
734
863
  accessibilityLabel: labels.close,
735
864
  accessibilityRole: "button",
736
- style: styles.primaryButton,
865
+ style: styles3.primaryButton,
737
866
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordCloseButton, testIdPrefix),
738
867
  onPress: handleClose,
739
- children: /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.close })
868
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.close })
740
869
  }
741
870
  ) }) : null
742
871
  ] });
743
872
  }
744
873
  return /* @__PURE__ */ jsxs(View, { style: modalStyles.body, testID: withTestIdPrefix(AuthTestIds.forgotPasswordForm, testIdPrefix), children: [
745
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: labels.description }),
746
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
747
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.emailLabel }),
874
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.description }),
875
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
876
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.emailLabel }),
748
877
  /* @__PURE__ */ jsx(
749
878
  TextInput,
750
879
  {
@@ -756,14 +885,14 @@ function ForgotPasswordFields({
756
885
  keyboardType: "email-address",
757
886
  placeholder: labels.emailPlaceholder,
758
887
  placeholderTextColor: theme.colors.textSecondary,
759
- style: styles.input,
888
+ style: styles3.input,
760
889
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordEmailInput, testIdPrefix),
761
890
  value: form.email,
762
891
  onChangeText: form.setEmail
763
892
  }
764
893
  )
765
894
  ] }),
766
- form.hasNetworkError ? /* @__PURE__ */ jsx(Text, { style: styles.errorText, testID: withTestIdPrefix(AuthTestIds.forgotPasswordError, testIdPrefix), children: labels.networkError }) : null,
895
+ form.hasNetworkError ? /* @__PURE__ */ jsx(Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.forgotPasswordError, testIdPrefix), children: labels.networkError }) : null,
767
896
  /* @__PURE__ */ jsxs(View, { style: modalStyles.actions, children: [
768
897
  onCancel !== void 0 ? /* @__PURE__ */ jsx(
769
898
  TouchableOpacity,
@@ -788,7 +917,7 @@ function ForgotPasswordFields({
788
917
  style: submitButtonStyle,
789
918
  testID: withTestIdPrefix(AuthTestIds.forgotPasswordSubmitButton, testIdPrefix),
790
919
  onPress: form.submit,
791
- children: form.isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.submit })
920
+ children: form.isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
792
921
  }
793
922
  )
794
923
  ] })
@@ -967,24 +1096,24 @@ function ResetPasswordForm({
967
1096
  testIdPrefix
968
1097
  }) {
969
1098
  const theme = useAuthTheme(themeProp);
970
- const styles = useAuthStyles(theme);
1099
+ const styles3 = useAuthStyles(theme);
971
1100
  const labels = useMemo(
972
1101
  () => ({ ...DEFAULT_RESET_PASSWORD_LABELS, ...labelsProp }),
973
1102
  [labelsProp]
974
1103
  );
975
1104
  const form = useResetPasswordForm({ client, token, onSuccess });
976
1105
  const message = errorMessage(form.errorKey, labels);
977
- const submitButtonStyle = form.isSubmitting ? [styles.primaryButton, styles.primaryButtonDisabled] : styles.primaryButton;
978
- return /* @__PURE__ */ jsx(View, { style: styles.screen, children: /* @__PURE__ */ jsxs(
1106
+ const submitButtonStyle = form.isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
1107
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsxs(
979
1108
  View,
980
1109
  {
981
- style: styles.card,
1110
+ style: styles3.card,
982
1111
  testID: withTestIdPrefix(AuthTestIds.resetPasswordForm, testIdPrefix),
983
1112
  children: [
984
- /* @__PURE__ */ jsx(Text, { style: styles.title, children: labels.title }),
985
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: labels.description }),
986
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
987
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.newPasswordLabel }),
1113
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
1114
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.description }),
1115
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
1116
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.newPasswordLabel }),
988
1117
  /* @__PURE__ */ jsx(
989
1118
  TextInput,
990
1119
  {
@@ -996,15 +1125,15 @@ function ResetPasswordForm({
996
1125
  editable: !form.isSubmitting,
997
1126
  placeholder: labels.newPasswordPlaceholder,
998
1127
  placeholderTextColor: theme.colors.textSecondary,
999
- style: styles.input,
1128
+ style: styles3.input,
1000
1129
  testID: withTestIdPrefix(AuthTestIds.resetPasswordNewInput, testIdPrefix),
1001
1130
  value: form.newPassword,
1002
1131
  onChangeText: form.setNewPassword
1003
1132
  }
1004
1133
  )
1005
1134
  ] }),
1006
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
1007
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.confirmPasswordLabel }),
1135
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
1136
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.confirmPasswordLabel }),
1008
1137
  /* @__PURE__ */ jsx(
1009
1138
  TextInput,
1010
1139
  {
@@ -1016,7 +1145,7 @@ function ResetPasswordForm({
1016
1145
  editable: !form.isSubmitting,
1017
1146
  placeholder: labels.confirmPasswordPlaceholder,
1018
1147
  placeholderTextColor: theme.colors.textSecondary,
1019
- style: styles.input,
1148
+ style: styles3.input,
1020
1149
  testID: withTestIdPrefix(AuthTestIds.resetPasswordConfirmInput, testIdPrefix),
1021
1150
  value: form.confirmPassword,
1022
1151
  onChangeText: form.setConfirmPassword
@@ -1026,7 +1155,7 @@ function ResetPasswordForm({
1026
1155
  message !== null ? /* @__PURE__ */ jsx(
1027
1156
  Text,
1028
1157
  {
1029
- style: styles.errorText,
1158
+ style: styles3.errorText,
1030
1159
  testID: withTestIdPrefix(AuthTestIds.resetPasswordError, testIdPrefix),
1031
1160
  children: message
1032
1161
  }
@@ -1041,7 +1170,7 @@ function ResetPasswordForm({
1041
1170
  style: submitButtonStyle,
1042
1171
  testID: withTestIdPrefix(AuthTestIds.resetPasswordSubmitButton, testIdPrefix),
1043
1172
  onPress: form.submit,
1044
- children: form.isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.submit })
1173
+ children: form.isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
1045
1174
  }
1046
1175
  )
1047
1176
  ]
@@ -1049,7 +1178,7 @@ function ResetPasswordForm({
1049
1178
  ) });
1050
1179
  }
1051
1180
  function OtpRequestStep({
1052
- styles,
1181
+ styles: styles3,
1053
1182
  theme,
1054
1183
  labels,
1055
1184
  testIdPrefix,
@@ -1058,12 +1187,12 @@ function OtpRequestStep({
1058
1187
  onSubmit
1059
1188
  }) {
1060
1189
  const [email, setEmail] = useState("");
1061
- const buttonStyle = isSubmitting ? [styles.primaryButton, styles.primaryButtonDisabled] : styles.primaryButton;
1190
+ const buttonStyle = isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
1062
1191
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1063
- /* @__PURE__ */ jsx(Text, { style: styles.title, children: labels.requestTitle }),
1064
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: labels.requestDescription }),
1065
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
1066
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.emailLabel }),
1192
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.requestTitle }),
1193
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.requestDescription }),
1194
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
1195
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.emailLabel }),
1067
1196
  /* @__PURE__ */ jsx(
1068
1197
  TextInput,
1069
1198
  {
@@ -1075,14 +1204,14 @@ function OtpRequestStep({
1075
1204
  keyboardType: "email-address",
1076
1205
  placeholder: labels.emailPlaceholder,
1077
1206
  placeholderTextColor: theme.colors.textSecondary,
1078
- style: styles.input,
1207
+ style: styles3.input,
1079
1208
  testID: withTestIdPrefix(AuthTestIds.otpEmailInput, testIdPrefix),
1080
1209
  value: email,
1081
1210
  onChangeText: setEmail
1082
1211
  }
1083
1212
  )
1084
1213
  ] }),
1085
- errorText !== null ? /* @__PURE__ */ jsx(Text, { style: styles.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText }) : null,
1214
+ errorText !== null ? /* @__PURE__ */ jsx(Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText }) : null,
1086
1215
  /* @__PURE__ */ jsx(
1087
1216
  TouchableOpacity,
1088
1217
  {
@@ -1093,13 +1222,13 @@ function OtpRequestStep({
1093
1222
  style: buttonStyle,
1094
1223
  testID: withTestIdPrefix(AuthTestIds.otpRequestButton, testIdPrefix),
1095
1224
  onPress: () => onSubmit(email.trim()),
1096
- children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.requestSubmit })
1225
+ children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.requestSubmit })
1097
1226
  }
1098
1227
  )
1099
1228
  ] });
1100
1229
  }
1101
1230
  function OtpVerifyStep({
1102
- styles,
1231
+ styles: styles3,
1103
1232
  theme,
1104
1233
  labels,
1105
1234
  testIdPrefix,
@@ -1111,13 +1240,13 @@ function OtpVerifyStep({
1111
1240
  onChangeEmail
1112
1241
  }) {
1113
1242
  const [code, setCode] = useState("");
1114
- const buttonStyle = isSubmitting ? [styles.primaryButton, styles.primaryButtonDisabled] : styles.primaryButton;
1243
+ const buttonStyle = isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
1115
1244
  const description = labels.verifyDescription.replace("{identifier}", identifier);
1116
1245
  return /* @__PURE__ */ jsxs(Fragment, { children: [
1117
- /* @__PURE__ */ jsx(Text, { style: styles.title, children: labels.verifyTitle }),
1118
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: description }),
1119
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
1120
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.codeLabel }),
1246
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.verifyTitle }),
1247
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: description }),
1248
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
1249
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.codeLabel }),
1121
1250
  /* @__PURE__ */ jsx(
1122
1251
  TextInput,
1123
1252
  {
@@ -1129,14 +1258,14 @@ function OtpVerifyStep({
1129
1258
  keyboardType: "number-pad",
1130
1259
  placeholder: labels.codePlaceholder,
1131
1260
  placeholderTextColor: theme.colors.textSecondary,
1132
- style: styles.input,
1261
+ style: styles3.input,
1133
1262
  testID: withTestIdPrefix(AuthTestIds.otpCodeInput, testIdPrefix),
1134
1263
  value: code,
1135
1264
  onChangeText: setCode
1136
1265
  }
1137
1266
  )
1138
1267
  ] }),
1139
- errorText !== null ? /* @__PURE__ */ jsx(Text, { style: styles.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText }) : null,
1268
+ errorText !== null ? /* @__PURE__ */ jsx(Text, { style: styles3.errorText, testID: withTestIdPrefix(AuthTestIds.otpError, testIdPrefix), children: errorText }) : null,
1140
1269
  /* @__PURE__ */ jsx(
1141
1270
  TouchableOpacity,
1142
1271
  {
@@ -1147,7 +1276,7 @@ function OtpVerifyStep({
1147
1276
  style: buttonStyle,
1148
1277
  testID: withTestIdPrefix(AuthTestIds.otpVerifyButton, testIdPrefix),
1149
1278
  onPress: () => onSubmit(code.trim()),
1150
- children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.verifySubmit })
1279
+ children: isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.verifySubmit })
1151
1280
  }
1152
1281
  ),
1153
1282
  /* @__PURE__ */ jsx(
@@ -1157,10 +1286,10 @@ function OtpVerifyStep({
1157
1286
  accessibilityLabel: isSubmitting ? labels.resending : labels.resend,
1158
1287
  accessibilityRole: "link",
1159
1288
  disabled: isSubmitting,
1160
- style: styles.fieldGroup,
1289
+ style: styles3.fieldGroup,
1161
1290
  testID: withTestIdPrefix(AuthTestIds.otpResendButton, testIdPrefix),
1162
1291
  onPress: onResend,
1163
- children: /* @__PURE__ */ jsx(Text, { style: styles.linkText, children: isSubmitting ? labels.resending : labels.resend })
1292
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.linkText, children: isSubmitting ? labels.resending : labels.resend })
1164
1293
  }
1165
1294
  ),
1166
1295
  /* @__PURE__ */ jsx(
@@ -1172,7 +1301,7 @@ function OtpVerifyStep({
1172
1301
  disabled: isSubmitting,
1173
1302
  testID: withTestIdPrefix(AuthTestIds.otpChangeEmailButton, testIdPrefix),
1174
1303
  onPress: onChangeEmail,
1175
- children: /* @__PURE__ */ jsx(Text, { style: styles.linkText, children: labels.changeEmail })
1304
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.linkText, children: labels.changeEmail })
1176
1305
  }
1177
1306
  )
1178
1307
  ] });
@@ -1341,7 +1470,7 @@ function OtpForm({
1341
1470
  testIdPrefix
1342
1471
  }) {
1343
1472
  const theme = useAuthTheme(themeProp);
1344
- const styles = useAuthStyles(theme);
1473
+ const styles3 = useAuthStyles(theme);
1345
1474
  const labels = useMemo(
1346
1475
  () => ({ ...DEFAULT_OTP_LABELS, ...labelsProp }),
1347
1476
  [labelsProp]
@@ -1359,13 +1488,13 @@ function OtpForm({
1359
1488
  otp.reset();
1360
1489
  }, [otp]);
1361
1490
  const errorText = localError ?? transportErrorFor(otp.step, otp.error, labels);
1362
- return /* @__PURE__ */ jsx(View, { style: styles.screen, children: /* @__PURE__ */ jsx(View, { style: styles.card, testID: withTestIdPrefix(AuthTestIds.otpForm, testIdPrefix), children: otp.step === "requestCode" /* RequestCode */ ? /* @__PURE__ */ jsx(
1491
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsx(View, { style: styles3.card, testID: withTestIdPrefix(AuthTestIds.otpForm, testIdPrefix), children: otp.step === "requestCode" /* RequestCode */ ? /* @__PURE__ */ jsx(
1363
1492
  OtpRequestStep,
1364
1493
  {
1365
1494
  errorText,
1366
1495
  isSubmitting: otp.isSubmitting,
1367
1496
  labels,
1368
- styles,
1497
+ styles: styles3,
1369
1498
  testIdPrefix,
1370
1499
  theme,
1371
1500
  onSubmit: handleRequest
@@ -1377,7 +1506,7 @@ function OtpForm({
1377
1506
  identifier: otp.identifier,
1378
1507
  isSubmitting: otp.isSubmitting,
1379
1508
  labels,
1380
- styles,
1509
+ styles: styles3,
1381
1510
  testIdPrefix,
1382
1511
  theme,
1383
1512
  onChangeEmail: handleChangeEmail,
@@ -1466,7 +1595,7 @@ function PinForm({
1466
1595
  testIdPrefix
1467
1596
  }) {
1468
1597
  const theme = useAuthTheme(themeProp);
1469
- const styles = useAuthStyles(theme);
1598
+ const styles3 = useAuthStyles(theme);
1470
1599
  const labels = useMemo(
1471
1600
  () => ({ ...DEFAULT_PIN_LABELS, ...labelsProp }),
1472
1601
  [labelsProp]
@@ -1476,12 +1605,12 @@ function PinForm({
1476
1605
  const [pinValue, setPinValue] = useState("");
1477
1606
  const handleSubmit = useSubmitHandler(pin, labels, setLocalError, onSuccess);
1478
1607
  const errorText = localError ?? transportErrorFor2(pin.error, labels);
1479
- const buttonStyle = pin.isSubmitting ? [styles.primaryButton, styles.primaryButtonDisabled] : styles.primaryButton;
1480
- return /* @__PURE__ */ jsx(View, { style: styles.screen, children: /* @__PURE__ */ jsxs(View, { style: styles.card, testID: withTestIdPrefix(AuthTestIds.pinForm, testIdPrefix), children: [
1481
- /* @__PURE__ */ jsx(Text, { style: styles.title, children: labels.title }),
1482
- /* @__PURE__ */ jsx(Text, { style: styles.subtitle, children: labels.description }),
1483
- /* @__PURE__ */ jsxs(View, { style: styles.fieldGroup, children: [
1484
- /* @__PURE__ */ jsx(Text, { style: styles.label, children: labels.pinLabel }),
1608
+ const buttonStyle = pin.isSubmitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
1609
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsxs(View, { style: styles3.card, testID: withTestIdPrefix(AuthTestIds.pinForm, testIdPrefix), children: [
1610
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
1611
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.description }),
1612
+ /* @__PURE__ */ jsxs(View, { style: styles3.fieldGroup, children: [
1613
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.pinLabel }),
1485
1614
  /* @__PURE__ */ jsx(
1486
1615
  TextInput,
1487
1616
  {
@@ -1494,7 +1623,7 @@ function PinForm({
1494
1623
  placeholder: labels.pinPlaceholder,
1495
1624
  placeholderTextColor: theme.colors.textSecondary,
1496
1625
  secureTextEntry: true,
1497
- style: styles.input,
1626
+ style: styles3.input,
1498
1627
  testID: withTestIdPrefix(AuthTestIds.pinInput, testIdPrefix),
1499
1628
  value: pinValue,
1500
1629
  onChangeText: setPinValue
@@ -1504,7 +1633,7 @@ function PinForm({
1504
1633
  errorText !== null ? /* @__PURE__ */ jsx(
1505
1634
  Text,
1506
1635
  {
1507
- style: styles.errorText,
1636
+ style: styles3.errorText,
1508
1637
  testID: withTestIdPrefix(AuthTestIds.pinError, testIdPrefix),
1509
1638
  children: errorText
1510
1639
  }
@@ -1519,11 +1648,918 @@ function PinForm({
1519
1648
  style: buttonStyle,
1520
1649
  testID: withTestIdPrefix(AuthTestIds.pinSubmitButton, testIdPrefix),
1521
1650
  onPress: () => handleSubmit(pinValue.trim()),
1522
- children: pin.isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles.primaryButtonText, children: labels.submit })
1651
+ children: pin.isSubmitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
1523
1652
  }
1524
1653
  )
1525
1654
  ] }) });
1526
1655
  }
1656
+ var DOT_SIZE = 18;
1657
+ var DOT_BORDER_WIDTH = 2;
1658
+ var DOT_RADIUS = 9;
1659
+ var ROW_GAP = 12;
1660
+ var ROW_MIN_HEIGHT = 44;
1661
+ var ROW_VERTICAL_MARGIN = 16;
1662
+ var HIDDEN_SIZE = 1;
1663
+ function sanitise(raw, length) {
1664
+ return raw.replace(/\D/g, "").slice(0, length);
1665
+ }
1666
+ function useDotStyles(theme) {
1667
+ return useMemo(
1668
+ () => ({
1669
+ filled: { backgroundColor: theme.colors.primary, borderColor: theme.colors.primary },
1670
+ empty: { backgroundColor: theme.colors.background, borderColor: theme.colors.border }
1671
+ }),
1672
+ [theme]
1673
+ );
1674
+ }
1675
+ function DevicePinInput({
1676
+ value,
1677
+ length,
1678
+ disabled,
1679
+ testID,
1680
+ testIdPrefix,
1681
+ accessibilityLabel,
1682
+ accessibilityHint,
1683
+ filledHint,
1684
+ emptyHint,
1685
+ theme: themeProp,
1686
+ onChange
1687
+ }) {
1688
+ const theme = useAuthTheme(themeProp);
1689
+ const inputRef = useRef(null);
1690
+ const dotStyles = useDotStyles(theme);
1691
+ const cells = Array.from({ length }, (_unused, index) => index);
1692
+ const baseId = withTestIdPrefix(testID, testIdPrefix);
1693
+ return /* @__PURE__ */ jsxs(
1694
+ Pressable,
1695
+ {
1696
+ accessibilityHint,
1697
+ accessibilityLabel,
1698
+ accessibilityRole: "button",
1699
+ disabled,
1700
+ style: styles.row,
1701
+ testID: `${baseId}-row`,
1702
+ onPress: () => inputRef.current?.focus(),
1703
+ children: [
1704
+ cells.map((index) => {
1705
+ const filled = index < value.length;
1706
+ return /* @__PURE__ */ jsx(
1707
+ View,
1708
+ {
1709
+ accessibilityLabel: filled ? filledHint : emptyHint,
1710
+ style: [styles.dot, filled ? dotStyles.filled : dotStyles.empty],
1711
+ testID: `${baseId}-dot-${String(index)}`
1712
+ },
1713
+ index
1714
+ );
1715
+ }),
1716
+ /* @__PURE__ */ jsx(
1717
+ TextInput,
1718
+ {
1719
+ ref: inputRef,
1720
+ accessibilityHint,
1721
+ accessibilityLabel,
1722
+ autoFocus: true,
1723
+ caretHidden: true,
1724
+ editable: !disabled,
1725
+ keyboardType: "number-pad",
1726
+ maxLength: length,
1727
+ secureTextEntry: true,
1728
+ style: styles.hiddenInput,
1729
+ testID: baseId,
1730
+ value,
1731
+ onChangeText: (raw) => onChange(sanitise(raw, length))
1732
+ }
1733
+ )
1734
+ ]
1735
+ }
1736
+ );
1737
+ }
1738
+ var styles = StyleSheet.create({
1739
+ dot: {
1740
+ borderRadius: DOT_RADIUS,
1741
+ borderWidth: DOT_BORDER_WIDTH,
1742
+ height: DOT_SIZE,
1743
+ width: DOT_SIZE
1744
+ },
1745
+ hiddenInput: {
1746
+ height: HIDDEN_SIZE,
1747
+ opacity: 0,
1748
+ position: "absolute",
1749
+ width: HIDDEN_SIZE
1750
+ },
1751
+ row: {
1752
+ alignItems: "center",
1753
+ flexDirection: "row",
1754
+ gap: ROW_GAP,
1755
+ justifyContent: "center",
1756
+ marginVertical: ROW_VERTICAL_MARGIN,
1757
+ minHeight: ROW_MIN_HEIGHT
1758
+ }
1759
+ });
1760
+
1761
+ // src/components/interpolate.ts
1762
+ function interpolate(template, values) {
1763
+ return template.replace(/\{(\w+)\}/g, (match, key) => {
1764
+ const value = values[key];
1765
+ return value === void 0 ? match : String(value);
1766
+ });
1767
+ }
1768
+
1769
+ // src/devicePin/devicePinConstants.ts
1770
+ var DEVICE_PIN_ALLOWED_DIGITS = [4, 6, 8];
1771
+ var DEVICE_PIN_DEFAULT_DIGITS = 4;
1772
+ function isAllowedPinDigits(value) {
1773
+ return DEVICE_PIN_ALLOWED_DIGITS.includes(value);
1774
+ }
1775
+ var PILL_VERTICAL_PADDING = 10;
1776
+ var PILL_FONT_SIZE = 15;
1777
+ function usePickerStyles(theme) {
1778
+ return useMemo(
1779
+ () => ({
1780
+ pill: { borderColor: theme.colors.border },
1781
+ active: { backgroundColor: theme.colors.primary, borderColor: theme.colors.primary },
1782
+ text: { color: theme.colors.text },
1783
+ textActive: { color: theme.colors.onPrimary }
1784
+ }),
1785
+ [theme]
1786
+ );
1787
+ }
1788
+ function DevicePinLengthPicker({
1789
+ selected,
1790
+ disabled,
1791
+ optionHint,
1792
+ testIdPrefix,
1793
+ theme: themeProp,
1794
+ onSelect
1795
+ }) {
1796
+ const theme = useAuthTheme(themeProp);
1797
+ const dynamic = usePickerStyles(theme);
1798
+ const baseId = withTestIdPrefix(AuthTestIds.devicePinEnrollLength, testIdPrefix);
1799
+ return /* @__PURE__ */ jsx(View, { style: styles2.row, children: DEVICE_PIN_ALLOWED_DIGITS.map((option) => {
1800
+ const active = option === selected;
1801
+ return /* @__PURE__ */ jsx(
1802
+ Pressable,
1803
+ {
1804
+ accessibilityHint: interpolate(optionHint, { count: option }),
1805
+ accessibilityLabel: String(option),
1806
+ accessibilityRole: "button",
1807
+ accessibilityState: { selected: active },
1808
+ disabled,
1809
+ style: [styles2.pill, dynamic.pill, active ? dynamic.active : null],
1810
+ testID: `${baseId}-${String(option)}`,
1811
+ onPress: () => onSelect(option),
1812
+ children: /* @__PURE__ */ jsx(Text, { style: [styles2.text, dynamic.text, active ? dynamic.textActive : null], children: String(option) })
1813
+ },
1814
+ option
1815
+ );
1816
+ }) });
1817
+ }
1818
+ var styles2 = StyleSheet.create({
1819
+ pill: {
1820
+ alignItems: "center",
1821
+ borderRadius: 8,
1822
+ borderWidth: 1,
1823
+ flex: 1,
1824
+ paddingVertical: PILL_VERTICAL_PADDING
1825
+ },
1826
+ row: {
1827
+ flexDirection: "row",
1828
+ gap: 8,
1829
+ marginBottom: 8
1830
+ },
1831
+ text: {
1832
+ fontSize: PILL_FONT_SIZE,
1833
+ fontWeight: "700"
1834
+ }
1835
+ });
1836
+
1837
+ // src/devicePin/DevicePinErrorKey.ts
1838
+ var DevicePinErrorKey = /* @__PURE__ */ ((DevicePinErrorKey2) => {
1839
+ DevicePinErrorKey2["Incomplete"] = "incomplete";
1840
+ DevicePinErrorKey2["Invalid"] = "invalid";
1841
+ DevicePinErrorKey2["LockedOut"] = "locked_out";
1842
+ DevicePinErrorKey2["RateLimited"] = "rate_limited";
1843
+ DevicePinErrorKey2["Generic"] = "generic";
1844
+ return DevicePinErrorKey2;
1845
+ })(DevicePinErrorKey || {});
1846
+ function useDevicePinUnlock({
1847
+ client,
1848
+ digits,
1849
+ onSignedIn
1850
+ }) {
1851
+ const [pin, setPinState] = useState("");
1852
+ const [submitting, setSubmitting] = useState(false);
1853
+ const [errorKey, setErrorKey] = useState(null);
1854
+ const [retryAfterSeconds, setRetryAfterSeconds] = useState(null);
1855
+ const setPin = useCallback((next) => {
1856
+ setPinState(next);
1857
+ setErrorKey(null);
1858
+ setRetryAfterSeconds(null);
1859
+ }, []);
1860
+ const submit = useCallback(() => {
1861
+ setErrorKey(null);
1862
+ setRetryAfterSeconds(null);
1863
+ if (pin.length < digits) {
1864
+ setErrorKey("incomplete" /* Incomplete */);
1865
+ return;
1866
+ }
1867
+ setSubmitting(true);
1868
+ client.unlockWithDevicePin({ pin }).then((result) => {
1869
+ if (result.status === "success") {
1870
+ onSignedIn(result.user);
1871
+ return;
1872
+ }
1873
+ if (result.status === "invalid") {
1874
+ setErrorKey("invalid" /* Invalid */);
1875
+ return;
1876
+ }
1877
+ if (result.status === "locked") {
1878
+ setRetryAfterSeconds(result.retryAfterSeconds);
1879
+ setErrorKey("locked_out" /* LockedOut */);
1880
+ return;
1881
+ }
1882
+ if (result.status === "rateLimited") {
1883
+ setRetryAfterSeconds(result.retryAfterSeconds);
1884
+ setErrorKey("rate_limited" /* RateLimited */);
1885
+ return;
1886
+ }
1887
+ setErrorKey("generic" /* Generic */);
1888
+ }).catch(() => {
1889
+ setErrorKey("generic" /* Generic */);
1890
+ }).finally(() => {
1891
+ setSubmitting(false);
1892
+ });
1893
+ }, [client, pin, digits, onSignedIn]);
1894
+ return { pin, submitting, errorKey, retryAfterSeconds, setPin, submit };
1895
+ }
1896
+ function resolveErrorText(errorKey, digits, retryAfter, labels) {
1897
+ if (errorKey === null) {
1898
+ return null;
1899
+ }
1900
+ if (errorKey === "incomplete" /* Incomplete */) {
1901
+ return interpolate(labels.errorIncomplete, { count: digits });
1902
+ }
1903
+ if (errorKey === "invalid" /* Invalid */) {
1904
+ return labels.errorInvalid;
1905
+ }
1906
+ if (errorKey === "locked_out" /* LockedOut */) {
1907
+ return retryAfter !== null ? interpolate(labels.errorLockedOutRetry, { count: retryAfter }) : labels.errorLockedOut;
1908
+ }
1909
+ if (errorKey === "rate_limited" /* RateLimited */) {
1910
+ return retryAfter !== null ? interpolate(labels.errorRateLimitedRetry, { count: retryAfter }) : labels.errorRateLimited;
1911
+ }
1912
+ return labels.errorGeneric;
1913
+ }
1914
+ function DevicePinUnlockScreen({
1915
+ client,
1916
+ digits,
1917
+ rememberedUsername,
1918
+ theme: themeProp,
1919
+ labels: labelsProp,
1920
+ testIdPrefix,
1921
+ onSignedIn,
1922
+ onUsePassword
1923
+ }) {
1924
+ const theme = useAuthTheme(themeProp);
1925
+ const styles3 = useAuthStyles(theme);
1926
+ const labels = useMemo(
1927
+ () => ({ ...DEFAULT_DEVICE_PIN_UNLOCK_LABELS, ...labelsProp }),
1928
+ [labelsProp]
1929
+ );
1930
+ const { pin, submitting, errorKey, retryAfterSeconds, setPin, submit } = useDevicePinUnlock({
1931
+ client,
1932
+ digits,
1933
+ onSignedIn
1934
+ });
1935
+ const hasName = rememberedUsername !== "";
1936
+ const title = hasName ? interpolate(labels.title, { name: rememberedUsername }) : labels.titleNoName;
1937
+ const error = resolveErrorText(errorKey, digits, retryAfterSeconds, labels);
1938
+ const buttonStyle = submitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
1939
+ return /* @__PURE__ */ jsx(View, { style: styles3.screen, children: /* @__PURE__ */ jsxs(
1940
+ View,
1941
+ {
1942
+ style: styles3.card,
1943
+ testID: withTestIdPrefix(AuthTestIds.devicePinUnlock, testIdPrefix),
1944
+ children: [
1945
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: title }),
1946
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: interpolate(labels.description, { count: digits }) }),
1947
+ /* @__PURE__ */ jsx(
1948
+ DevicePinInput,
1949
+ {
1950
+ accessibilityHint: labels.pinPlaceholder,
1951
+ accessibilityLabel: labels.pinLabel,
1952
+ disabled: submitting,
1953
+ emptyHint: labels.digitEmptyHint,
1954
+ filledHint: labels.digitFilledHint,
1955
+ length: digits,
1956
+ testID: AuthTestIds.devicePinUnlockInput,
1957
+ testIdPrefix,
1958
+ theme,
1959
+ value: pin,
1960
+ onChange: setPin
1961
+ }
1962
+ ),
1963
+ error !== null ? /* @__PURE__ */ jsx(
1964
+ Text,
1965
+ {
1966
+ style: styles3.errorText,
1967
+ testID: withTestIdPrefix(AuthTestIds.devicePinUnlockError, testIdPrefix),
1968
+ children: error
1969
+ }
1970
+ ) : null,
1971
+ /* @__PURE__ */ jsx(
1972
+ TouchableOpacity,
1973
+ {
1974
+ accessibilityHint: labels.submit,
1975
+ accessibilityLabel: submitting ? labels.submitting : labels.submit,
1976
+ accessibilityRole: "button",
1977
+ disabled: submitting,
1978
+ style: buttonStyle,
1979
+ testID: withTestIdPrefix(AuthTestIds.devicePinUnlockSubmit, testIdPrefix),
1980
+ onPress: submit,
1981
+ children: submitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
1982
+ }
1983
+ ),
1984
+ /* @__PURE__ */ jsx(
1985
+ TouchableOpacity,
1986
+ {
1987
+ accessibilityHint: labels.usePasswordHint,
1988
+ accessibilityLabel: labels.usePasswordInstead,
1989
+ accessibilityRole: "button",
1990
+ disabled: submitting,
1991
+ style: styles3.escapeLink,
1992
+ testID: withTestIdPrefix(AuthTestIds.devicePinUnlockUsePassword, testIdPrefix),
1993
+ onPress: onUsePassword,
1994
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.linkText, children: labels.usePasswordInstead })
1995
+ }
1996
+ )
1997
+ ]
1998
+ }
1999
+ ) });
2000
+ }
2001
+
2002
+ // src/devicePin/DevicePinEnrollErrorKey.ts
2003
+ var DevicePinEnrollErrorKey = /* @__PURE__ */ ((DevicePinEnrollErrorKey2) => {
2004
+ DevicePinEnrollErrorKey2["Mismatch"] = "mismatch";
2005
+ DevicePinEnrollErrorKey2["Unauthorized"] = "unauthorized";
2006
+ DevicePinEnrollErrorKey2["Forbidden"] = "forbidden";
2007
+ DevicePinEnrollErrorKey2["InvalidPin"] = "invalid_pin";
2008
+ DevicePinEnrollErrorKey2["Failed"] = "failed";
2009
+ return DevicePinEnrollErrorKey2;
2010
+ })(DevicePinEnrollErrorKey || {});
2011
+ function errorKeyFor(result) {
2012
+ if (result.status === "unauthorized") {
2013
+ return "unauthorized" /* Unauthorized */;
2014
+ }
2015
+ if (result.status === "forbidden") {
2016
+ return "forbidden" /* Forbidden */;
2017
+ }
2018
+ if (result.status === "invalidPin") {
2019
+ return "invalid_pin" /* InvalidPin */;
2020
+ }
2021
+ return "failed" /* Failed */;
2022
+ }
2023
+ function useDevicePinEnroll({
2024
+ client,
2025
+ digits,
2026
+ onEnrolled
2027
+ }) {
2028
+ const [pin, setPinState] = useState("");
2029
+ const [confirmPin, setConfirmState] = useState("");
2030
+ const [submitting, setSubmitting] = useState(false);
2031
+ const [errorKey, setErrorKey] = useState(null);
2032
+ const setPin = useCallback((next) => {
2033
+ setPinState(next);
2034
+ setErrorKey(null);
2035
+ }, []);
2036
+ const setConfirmPin = useCallback((next) => {
2037
+ setConfirmState(next);
2038
+ setErrorKey(null);
2039
+ }, []);
2040
+ const submit = useCallback(() => {
2041
+ setErrorKey(null);
2042
+ const wrongLength = pin.length !== digits;
2043
+ const mismatched = pin !== confirmPin;
2044
+ if (wrongLength || mismatched) {
2045
+ setErrorKey("mismatch" /* Mismatch */);
2046
+ return;
2047
+ }
2048
+ setSubmitting(true);
2049
+ client.enrollDevicePin({ pin, digits }).then((result) => {
2050
+ if (result.status === "success") {
2051
+ onEnrolled();
2052
+ return;
2053
+ }
2054
+ setErrorKey(errorKeyFor(result));
2055
+ }).catch(() => {
2056
+ setErrorKey("failed" /* Failed */);
2057
+ }).finally(() => {
2058
+ setSubmitting(false);
2059
+ });
2060
+ }, [client, pin, confirmPin, digits, onEnrolled]);
2061
+ return { pin, confirmPin, submitting, errorKey, setPin, setConfirmPin, submit };
2062
+ }
2063
+ function resolveErrorText2(errorKey, digits, labels) {
2064
+ if (errorKey === null) {
2065
+ return null;
2066
+ }
2067
+ if (errorKey === "mismatch" /* Mismatch */) {
2068
+ return interpolate(labels.errorMismatch, { count: digits });
2069
+ }
2070
+ if (errorKey === "unauthorized" /* Unauthorized */) {
2071
+ return labels.errorUnauthorized;
2072
+ }
2073
+ if (errorKey === "forbidden" /* Forbidden */) {
2074
+ return labels.errorForbidden;
2075
+ }
2076
+ if (errorKey === "invalid_pin" /* InvalidPin */) {
2077
+ return labels.errorInvalidPin;
2078
+ }
2079
+ return labels.errorFailed;
2080
+ }
2081
+ function DevicePinEnrollForm({
2082
+ client,
2083
+ initialDigits = DEVICE_PIN_DEFAULT_DIGITS,
2084
+ theme: themeProp,
2085
+ labels: labelsProp,
2086
+ testIdPrefix,
2087
+ onEnrolled,
2088
+ onCancel
2089
+ }) {
2090
+ const theme = useAuthTheme(themeProp);
2091
+ const styles3 = useAuthStyles(theme);
2092
+ const labels = useMemo(
2093
+ () => ({ ...DEFAULT_DEVICE_PIN_ENROLL_LABELS, ...labelsProp }),
2094
+ [labelsProp]
2095
+ );
2096
+ const [digits, setDigits] = useState(initialDigits);
2097
+ const { pin, confirmPin, submitting, errorKey, setPin, setConfirmPin, submit } = useDevicePinEnroll({ client, digits, onEnrolled });
2098
+ const error = resolveErrorText2(errorKey, digits, labels);
2099
+ const buttonStyle = submitting ? [styles3.primaryButton, styles3.primaryButtonDisabled] : styles3.primaryButton;
2100
+ return /* @__PURE__ */ jsxs(
2101
+ View,
2102
+ {
2103
+ style: styles3.card,
2104
+ testID: withTestIdPrefix(AuthTestIds.devicePinEnrollForm, testIdPrefix),
2105
+ children: [
2106
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.formTitle }),
2107
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: interpolate(labels.formDescription, { count: digits }) }),
2108
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.lengthLabel }),
2109
+ /* @__PURE__ */ jsx(
2110
+ DevicePinLengthPicker,
2111
+ {
2112
+ disabled: submitting,
2113
+ optionHint: labels.lengthOptionHint,
2114
+ selected: digits,
2115
+ testIdPrefix,
2116
+ theme,
2117
+ onSelect: (option) => {
2118
+ setDigits(option);
2119
+ setPin("");
2120
+ setConfirmPin("");
2121
+ }
2122
+ }
2123
+ ),
2124
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.pinLabel }),
2125
+ /* @__PURE__ */ jsx(
2126
+ DevicePinInput,
2127
+ {
2128
+ accessibilityHint: labels.pinPlaceholder,
2129
+ accessibilityLabel: labels.pinLabel,
2130
+ disabled: submitting,
2131
+ emptyHint: labels.pinPlaceholder,
2132
+ filledHint: labels.pinLabel,
2133
+ length: digits,
2134
+ testID: AuthTestIds.devicePinEnrollPin,
2135
+ testIdPrefix,
2136
+ theme,
2137
+ value: pin,
2138
+ onChange: setPin
2139
+ }
2140
+ ),
2141
+ /* @__PURE__ */ jsx(Text, { style: styles3.label, children: labels.confirmLabel }),
2142
+ /* @__PURE__ */ jsx(
2143
+ DevicePinInput,
2144
+ {
2145
+ accessibilityHint: labels.confirmPlaceholder,
2146
+ accessibilityLabel: labels.confirmLabel,
2147
+ disabled: submitting,
2148
+ emptyHint: labels.confirmPlaceholder,
2149
+ filledHint: labels.confirmLabel,
2150
+ length: digits,
2151
+ testID: AuthTestIds.devicePinEnrollConfirm,
2152
+ testIdPrefix,
2153
+ theme,
2154
+ value: confirmPin,
2155
+ onChange: setConfirmPin
2156
+ }
2157
+ ),
2158
+ error !== null ? /* @__PURE__ */ jsx(
2159
+ Text,
2160
+ {
2161
+ style: styles3.errorText,
2162
+ testID: withTestIdPrefix(AuthTestIds.devicePinEnrollError, testIdPrefix),
2163
+ children: error
2164
+ }
2165
+ ) : null,
2166
+ /* @__PURE__ */ jsx(
2167
+ TouchableOpacity,
2168
+ {
2169
+ accessibilityHint: labels.submit,
2170
+ accessibilityLabel: submitting ? labels.submitting : labels.submit,
2171
+ accessibilityRole: "button",
2172
+ disabled: submitting,
2173
+ style: buttonStyle,
2174
+ testID: withTestIdPrefix(AuthTestIds.devicePinEnrollSubmit, testIdPrefix),
2175
+ onPress: submit,
2176
+ children: submitting ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.onPrimary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.submit })
2177
+ }
2178
+ ),
2179
+ /* @__PURE__ */ jsx(
2180
+ TouchableOpacity,
2181
+ {
2182
+ accessibilityHint: labels.cancelHint,
2183
+ accessibilityLabel: labels.cancel,
2184
+ accessibilityRole: "button",
2185
+ disabled: submitting,
2186
+ style: styles3.escapeLink,
2187
+ testID: withTestIdPrefix(AuthTestIds.devicePinEnrollCancel, testIdPrefix),
2188
+ onPress: onCancel,
2189
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.linkText, children: labels.cancel })
2190
+ }
2191
+ )
2192
+ ]
2193
+ }
2194
+ );
2195
+ }
2196
+ function DevicePinOffer({
2197
+ client,
2198
+ initialDigits,
2199
+ theme: themeProp,
2200
+ labels: labelsProp,
2201
+ testIdPrefix,
2202
+ onDismiss
2203
+ }) {
2204
+ const theme = useAuthTheme(themeProp);
2205
+ const styles3 = useAuthStyles(theme);
2206
+ const labels = useMemo(
2207
+ () => ({ ...DEFAULT_DEVICE_PIN_ENROLL_LABELS, ...labelsProp }),
2208
+ [labelsProp]
2209
+ );
2210
+ const [expanded, setExpanded] = useState(false);
2211
+ const cardTestId = withTestIdPrefix(AuthTestIds.devicePinOffer, testIdPrefix);
2212
+ if (expanded) {
2213
+ return /* @__PURE__ */ jsx(View, { style: styles3.card, testID: cardTestId, children: /* @__PURE__ */ jsx(
2214
+ DevicePinEnrollForm,
2215
+ {
2216
+ client,
2217
+ initialDigits,
2218
+ labels: labelsProp,
2219
+ testIdPrefix,
2220
+ theme,
2221
+ onCancel: onDismiss,
2222
+ onEnrolled: onDismiss
2223
+ }
2224
+ ) });
2225
+ }
2226
+ return /* @__PURE__ */ jsxs(View, { style: styles3.card, testID: cardTestId, children: [
2227
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.offerTitle }),
2228
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.offerDescription }),
2229
+ /* @__PURE__ */ jsx(
2230
+ TouchableOpacity,
2231
+ {
2232
+ accessibilityHint: labels.offerAcceptHint,
2233
+ accessibilityLabel: labels.offerAccept,
2234
+ accessibilityRole: "button",
2235
+ style: styles3.primaryButton,
2236
+ testID: withTestIdPrefix(AuthTestIds.devicePinOfferAccept, testIdPrefix),
2237
+ onPress: () => setExpanded(true),
2238
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.offerAccept })
2239
+ }
2240
+ ),
2241
+ /* @__PURE__ */ jsx(
2242
+ TouchableOpacity,
2243
+ {
2244
+ accessibilityHint: labels.offerSkipHint,
2245
+ accessibilityLabel: labels.offerSkip,
2246
+ accessibilityRole: "button",
2247
+ style: styles3.secondaryButton,
2248
+ testID: withTestIdPrefix(AuthTestIds.devicePinOfferSkip, testIdPrefix),
2249
+ onPress: onDismiss,
2250
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.secondaryButtonText, children: labels.offerSkip })
2251
+ }
2252
+ )
2253
+ ] });
2254
+ }
2255
+ function useDevicePinDisable({
2256
+ client,
2257
+ initialHasPin,
2258
+ onChanged
2259
+ }) {
2260
+ const [hasPin, setHasPin] = useState(initialHasPin);
2261
+ const [disabling, setDisabling] = useState(false);
2262
+ const [failed, setFailed] = useState(false);
2263
+ const disable = useCallback(() => {
2264
+ setFailed(false);
2265
+ setDisabling(true);
2266
+ client.disableDevicePin().then((ok) => {
2267
+ if (ok) {
2268
+ setHasPin(false);
2269
+ onChanged?.(false);
2270
+ return;
2271
+ }
2272
+ setFailed(true);
2273
+ }).catch(() => {
2274
+ setFailed(true);
2275
+ }).finally(() => {
2276
+ setDisabling(false);
2277
+ });
2278
+ }, [client, onChanged]);
2279
+ const markEnabled = useCallback(() => {
2280
+ setHasPin(true);
2281
+ onChanged?.(true);
2282
+ }, [onChanged]);
2283
+ return { hasPin, disabling, failed, disable, markEnabled };
2284
+ }
2285
+ function DevicePinSettingsCard({
2286
+ client,
2287
+ initialHasPin,
2288
+ theme: themeProp,
2289
+ labels: labelsProp,
2290
+ enrollLabels,
2291
+ testIdPrefix,
2292
+ onChanged
2293
+ }) {
2294
+ const theme = useAuthTheme(themeProp);
2295
+ const styles3 = useAuthStyles(theme);
2296
+ const labels = useMemo(
2297
+ () => ({ ...DEFAULT_DEVICE_PIN_SETTINGS_LABELS, ...labelsProp }),
2298
+ [labelsProp]
2299
+ );
2300
+ const resolvedEnrollLabels = useMemo(
2301
+ () => ({ ...DEFAULT_DEVICE_PIN_ENROLL_LABELS, ...enrollLabels }),
2302
+ [enrollLabels]
2303
+ );
2304
+ const [enrolling, setEnrolling] = useState(false);
2305
+ const { hasPin, disabling, failed, disable, markEnabled } = useDevicePinDisable({
2306
+ client,
2307
+ initialHasPin,
2308
+ onChanged
2309
+ });
2310
+ const handleEnrolled = () => {
2311
+ setEnrolling(false);
2312
+ markEnabled();
2313
+ };
2314
+ const disableButtonStyle = disabling ? [styles3.secondaryButton, styles3.primaryButtonDisabled] : styles3.secondaryButton;
2315
+ return /* @__PURE__ */ jsxs(
2316
+ View,
2317
+ {
2318
+ style: styles3.card,
2319
+ testID: withTestIdPrefix(AuthTestIds.devicePinSettings, testIdPrefix),
2320
+ children: [
2321
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
2322
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.description }),
2323
+ /* @__PURE__ */ jsx(
2324
+ Text,
2325
+ {
2326
+ style: styles3.statusText,
2327
+ testID: withTestIdPrefix(AuthTestIds.devicePinSettingsStatus, testIdPrefix),
2328
+ children: hasPin ? labels.statusEnabled : labels.statusDisabled
2329
+ }
2330
+ ),
2331
+ failed ? /* @__PURE__ */ jsx(
2332
+ Text,
2333
+ {
2334
+ style: styles3.errorText,
2335
+ testID: withTestIdPrefix(AuthTestIds.devicePinSettingsError, testIdPrefix),
2336
+ children: labels.disableFailed
2337
+ }
2338
+ ) : null,
2339
+ renderAction()
2340
+ ]
2341
+ }
2342
+ );
2343
+ function renderAction() {
2344
+ if (enrolling) {
2345
+ return /* @__PURE__ */ jsx(
2346
+ DevicePinEnrollForm,
2347
+ {
2348
+ client,
2349
+ labels: resolvedEnrollLabels,
2350
+ testIdPrefix,
2351
+ theme,
2352
+ onCancel: () => setEnrolling(false),
2353
+ onEnrolled: handleEnrolled
2354
+ }
2355
+ );
2356
+ }
2357
+ if (hasPin) {
2358
+ return /* @__PURE__ */ jsx(
2359
+ TouchableOpacity,
2360
+ {
2361
+ accessibilityHint: labels.disableHint,
2362
+ accessibilityLabel: disabling ? labels.disabling : labels.disable,
2363
+ accessibilityRole: "button",
2364
+ disabled: disabling,
2365
+ style: disableButtonStyle,
2366
+ testID: withTestIdPrefix(AuthTestIds.devicePinSettingsDisable, testIdPrefix),
2367
+ onPress: disable,
2368
+ children: disabling ? /* @__PURE__ */ jsx(ActivityIndicator, { color: theme.colors.primary, size: "small" }) : /* @__PURE__ */ jsx(Text, { style: styles3.secondaryButtonText, children: labels.disable })
2369
+ }
2370
+ );
2371
+ }
2372
+ return /* @__PURE__ */ jsx(
2373
+ TouchableOpacity,
2374
+ {
2375
+ accessibilityHint: labels.enableHint,
2376
+ accessibilityLabel: labels.enable,
2377
+ accessibilityRole: "button",
2378
+ style: styles3.primaryButton,
2379
+ testID: withTestIdPrefix(AuthTestIds.devicePinSettingsEnable, testIdPrefix),
2380
+ onPress: () => setEnrolling(true),
2381
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.enable })
2382
+ }
2383
+ );
2384
+ }
2385
+ }
2386
+
2387
+ // src/passkey/passkeyNavigation.ts
2388
+ var PASSKEY_LOGIN_ENDPOINT = "/bff/passkey/login";
2389
+ var PASSKEY_REGISTER_ENDPOINT = "/bff/passkey/register";
2390
+ var RETURN_URL_PARAM = "returnUrl";
2391
+ var PASSKEY_ERROR_PARAM = "passkeyError";
2392
+ var PASSKEY_PARAM = "passkey";
2393
+ var PASSKEY_REGISTERED_VALUE = "registered";
2394
+ var PASSKEY_ERROR_CANCELLED = "cancelled";
2395
+ var PASSKEY_ERROR_FAILED = "failed";
2396
+ function buildNavigationUrl(endpoint, returnUrl) {
2397
+ return `${endpoint}?${RETURN_URL_PARAM}=${encodeURIComponent(returnUrl)}`;
2398
+ }
2399
+ function readSearchParams() {
2400
+ if (typeof window === "undefined") {
2401
+ return null;
2402
+ }
2403
+ return new URLSearchParams(window.location.search);
2404
+ }
2405
+ function startPasskeyLogin(returnUrl) {
2406
+ if (typeof window === "undefined") {
2407
+ return;
2408
+ }
2409
+ window.location.assign(buildNavigationUrl(PASSKEY_LOGIN_ENDPOINT, returnUrl));
2410
+ }
2411
+ function startPasskeyRegistration(returnUrl) {
2412
+ if (typeof window === "undefined") {
2413
+ return;
2414
+ }
2415
+ window.location.assign(buildNavigationUrl(PASSKEY_REGISTER_ENDPOINT, returnUrl));
2416
+ }
2417
+ function readPasskeyError() {
2418
+ const params = readSearchParams();
2419
+ if (params === null) {
2420
+ return null;
2421
+ }
2422
+ const value = params.get(PASSKEY_ERROR_PARAM);
2423
+ if (value === PASSKEY_ERROR_CANCELLED) {
2424
+ return PASSKEY_ERROR_CANCELLED;
2425
+ }
2426
+ if (value === PASSKEY_ERROR_FAILED) {
2427
+ return PASSKEY_ERROR_FAILED;
2428
+ }
2429
+ return null;
2430
+ }
2431
+ function readPasskeyRegistered() {
2432
+ const params = readSearchParams();
2433
+ if (params === null) {
2434
+ return false;
2435
+ }
2436
+ return params.get(PASSKEY_PARAM) === PASSKEY_REGISTERED_VALUE;
2437
+ }
2438
+ var DEFAULT_RETURN_URL = "/";
2439
+ function PasskeyLoginButton({
2440
+ returnUrl = DEFAULT_RETURN_URL,
2441
+ theme: themeProp,
2442
+ labels: labelsProp,
2443
+ testIdPrefix
2444
+ }) {
2445
+ const theme = useAuthTheme(themeProp);
2446
+ const styles3 = useAuthStyles(theme);
2447
+ const labels = useMemo(
2448
+ () => ({ ...DEFAULT_PASSKEY_LOGIN_LABELS, ...labelsProp }),
2449
+ [labelsProp]
2450
+ );
2451
+ const error = readPasskeyError();
2452
+ return /* @__PURE__ */ jsxs(View, { testID: withTestIdPrefix(AuthTestIds.passkeyLogin, testIdPrefix), children: [
2453
+ error !== null ? /* @__PURE__ */ jsx(
2454
+ Text,
2455
+ {
2456
+ style: styles3.errorText,
2457
+ testID: withTestIdPrefix(AuthTestIds.passkeyLoginError, testIdPrefix),
2458
+ children: error === "cancelled" ? labels.errorCancelled : labels.errorFailed
2459
+ }
2460
+ ) : null,
2461
+ /* @__PURE__ */ jsx(
2462
+ TouchableOpacity,
2463
+ {
2464
+ accessibilityHint: labels.signInHint,
2465
+ accessibilityLabel: labels.signInButton,
2466
+ accessibilityRole: "button",
2467
+ style: styles3.secondaryButton,
2468
+ testID: withTestIdPrefix(AuthTestIds.passkeyLoginButton, testIdPrefix),
2469
+ onPress: () => startPasskeyLogin(returnUrl),
2470
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.secondaryButtonText, children: labels.signInButton })
2471
+ }
2472
+ )
2473
+ ] });
2474
+ }
2475
+ var DEFAULT_RETURN_PATH = "/";
2476
+ function readCurrentPath() {
2477
+ if (typeof window === "undefined") {
2478
+ return DEFAULT_RETURN_PATH;
2479
+ }
2480
+ return window.location.pathname;
2481
+ }
2482
+ function PasskeySettingsCard({
2483
+ returnUrl,
2484
+ theme: themeProp,
2485
+ labels: labelsProp,
2486
+ testIdPrefix
2487
+ }) {
2488
+ const theme = useAuthTheme(themeProp);
2489
+ const styles3 = useAuthStyles(theme);
2490
+ const labels = useMemo(
2491
+ () => ({ ...DEFAULT_PASSKEY_SETTINGS_LABELS, ...labelsProp }),
2492
+ [labelsProp]
2493
+ );
2494
+ const justRegistered = readPasskeyRegistered();
2495
+ const target = returnUrl ?? readCurrentPath();
2496
+ return /* @__PURE__ */ jsxs(
2497
+ View,
2498
+ {
2499
+ style: styles3.card,
2500
+ testID: withTestIdPrefix(AuthTestIds.passkeySettings, testIdPrefix),
2501
+ children: [
2502
+ /* @__PURE__ */ jsx(Text, { style: styles3.title, children: labels.title }),
2503
+ /* @__PURE__ */ jsx(Text, { style: styles3.subtitle, children: labels.description }),
2504
+ /* @__PURE__ */ jsx(Text, { style: styles3.helperText, children: labels.reauthHint }),
2505
+ justRegistered ? /* @__PURE__ */ jsx(
2506
+ Text,
2507
+ {
2508
+ style: styles3.successText,
2509
+ testID: withTestIdPrefix(AuthTestIds.passkeySettingsSuccess, testIdPrefix),
2510
+ children: labels.registeredSuccess
2511
+ }
2512
+ ) : null,
2513
+ /* @__PURE__ */ jsx(
2514
+ TouchableOpacity,
2515
+ {
2516
+ accessibilityHint: labels.addHint,
2517
+ accessibilityLabel: labels.addButton,
2518
+ accessibilityRole: "button",
2519
+ style: styles3.primaryButton,
2520
+ testID: withTestIdPrefix(AuthTestIds.passkeySettingsAdd, testIdPrefix),
2521
+ onPress: () => startPasskeyRegistration(target),
2522
+ children: /* @__PURE__ */ jsx(Text, { style: styles3.primaryButtonText, children: labels.addButton })
2523
+ }
2524
+ )
2525
+ ]
2526
+ }
2527
+ );
2528
+ }
2529
+ var EMPTY_CONFIG = {
2530
+ methods: [],
2531
+ registrationEnabled: false,
2532
+ deviceState: {
2533
+ rememberedUsername: null,
2534
+ hasPin: false,
2535
+ pinDigits: null,
2536
+ preferredMethod: null
2537
+ }
2538
+ };
2539
+ function useBffLoginConfig(client) {
2540
+ const [config, setConfig] = useState(EMPTY_CONFIG);
2541
+ const [loading, setLoading] = useState(true);
2542
+ useEffect(() => {
2543
+ let active = true;
2544
+ client.getLoginConfig().then((next) => {
2545
+ if (active) {
2546
+ setConfig(next);
2547
+ }
2548
+ }).catch(() => {
2549
+ if (active) {
2550
+ setConfig(EMPTY_CONFIG);
2551
+ }
2552
+ }).finally(() => {
2553
+ if (active) {
2554
+ setLoading(false);
2555
+ }
2556
+ });
2557
+ return () => {
2558
+ active = false;
2559
+ };
2560
+ }, [client]);
2561
+ return { config, loading };
2562
+ }
1527
2563
  async function lazyFetchHttpClient(request) {
1528
2564
  const fetchImpl = typeof fetch === "function" ? fetch.bind(globalThis) : void 0;
1529
2565
  if (fetchImpl === void 0) {
@@ -1567,6 +2603,6 @@ function resolvePostLoginRoute(user, table) {
1567
2603
  return table.fallback ?? null;
1568
2604
  }
1569
2605
 
1570
- export { AuthTestIds, AuthThemeProvider, BffAuthStatus, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, ForgotPasswordFields, ForgotPasswordForm, LoginForm, OtpForm, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasswordPolicyError, PinForm, ResetPasswordError, ResetPasswordForm, collectUserRoles, createBffAuthClient, defaultAuthTheme, isPasswordValid, isValidForgotPasswordEmail, resolvePostLoginRoute, useAuthTheme, useBffAuth, useBffForgotPassword, useBffResetPassword, useForgotPasswordSubmit, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
2606
+ export { AuthTestIds, AuthThemeProvider, BffAuthStatus, DEFAULT_DEVICE_PIN_ENROLL_LABELS, DEFAULT_DEVICE_PIN_SETTINGS_LABELS, DEFAULT_DEVICE_PIN_UNLOCK_LABELS, DEFAULT_FORGOT_PASSWORD_FIELDS_LABELS, DEFAULT_FORGOT_PASSWORD_LABELS, DEFAULT_LOGIN_LABELS, DEFAULT_OTP_LABELS, DEFAULT_PASSKEY_LOGIN_LABELS, DEFAULT_PASSKEY_SETTINGS_LABELS, DEFAULT_PIN_LABELS, DEFAULT_RESET_PASSWORD_LABELS, DEVICE_PIN_ALLOWED_DIGITS, DEVICE_PIN_DEFAULT_DIGITS, DevicePinEnrollErrorKey, DevicePinEnrollForm, DevicePinErrorKey, DevicePinInput, DevicePinLengthPicker, DevicePinOffer, DevicePinSettingsCard, DevicePinUnlockScreen, ForgotPasswordFields, ForgotPasswordForm, LoginForm, OtpForm, OtpLoginStep, PASSWORD_MAX_LENGTH, PASSWORD_MIN_LENGTH, PasskeyLoginButton, PasskeySettingsCard, PasswordPolicyError, PinForm, ResetPasswordError, ResetPasswordForm, collectUserRoles, createBffAuthClient, defaultAuthTheme, isAllowedPinDigits, isPasswordValid, isValidForgotPasswordEmail, readPasskeyError, readPasskeyRegistered, resolvePostLoginRoute, startPasskeyLogin, startPasskeyRegistration, useAuthTheme, useBffAuth, useBffForgotPassword, useBffLoginConfig, useBffResetPassword, useDevicePinDisable, useDevicePinEnroll, useDevicePinUnlock, useForgotPasswordSubmit, useOtpLogin, usePinLogin, useResetPasswordForm, validatePasswordPolicy, withTestIdPrefix };
1571
2607
  //# sourceMappingURL=index.mjs.map
1572
2608
  //# sourceMappingURL=index.mjs.map