signer-test-sdk-react 0.0.11 → 0.0.13

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 (91) hide show
  1. package/README.md +1 -1
  2. package/dist/src/AbstraxnProvider.d.ts +2 -5
  3. package/dist/src/AbstraxnProvider.js +550 -50
  4. package/dist/src/AbstraxnProvider.js.map +1 -1
  5. package/dist/src/WalletModal.css +1973 -23
  6. package/dist/src/WalletModal.d.ts +2 -1
  7. package/dist/src/WalletModal.js +200 -30
  8. package/dist/src/WalletModal.js.map +1 -1
  9. package/dist/src/chains.d.ts +55 -0
  10. package/dist/src/chains.js +221 -0
  11. package/dist/src/chains.js.map +1 -0
  12. package/dist/src/components/OnboardingUI/OnboardingUIReact.js +24 -3
  13. package/dist/src/components/OnboardingUI/OnboardingUIReact.js.map +1 -1
  14. package/dist/src/components/OnboardingUI/OnboardingUIWeb.d.ts +11 -2
  15. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js +266 -36
  16. package/dist/src/components/OnboardingUI/OnboardingUIWeb.js.map +1 -1
  17. package/dist/src/components/OnboardingUI/components/PasskeyButton.js +1 -1
  18. package/dist/src/components/OnboardingUI/components/PasskeyButton.js.map +1 -1
  19. package/dist/src/components/QRCode.d.ts +13 -0
  20. package/dist/src/components/QRCode.js +6 -0
  21. package/dist/src/components/QRCode.js.map +1 -0
  22. package/dist/src/components/WalletModal/components/ChainSelector.css +180 -0
  23. package/dist/src/components/WalletModal/components/ChainSelector.d.ts +10 -0
  24. package/dist/src/components/WalletModal/components/ChainSelector.js +34 -0
  25. package/dist/src/components/WalletModal/components/ChainSelector.js.map +1 -0
  26. package/dist/src/components/WalletModal/components/ExportKeyModal.css +133 -0
  27. package/dist/src/components/WalletModal/components/ExportKeyModal.d.ts +9 -0
  28. package/dist/src/components/WalletModal/components/ExportKeyModal.js +31 -0
  29. package/dist/src/components/WalletModal/components/ExportKeyModal.js.map +1 -0
  30. package/dist/src/components/WalletModal/components/ExportWarningModal.css +2 -0
  31. package/dist/src/components/WalletModal/components/ExportWarningModal.d.ts +11 -0
  32. package/dist/src/components/WalletModal/components/ExportWarningModal.js +18 -0
  33. package/dist/src/components/WalletModal/components/ExportWarningModal.js.map +1 -0
  34. package/dist/src/components/WalletModal/components/ManageWalletModal.css +160 -0
  35. package/dist/src/components/WalletModal/components/ManageWalletModal.d.ts +12 -0
  36. package/dist/src/components/WalletModal/components/ManageWalletModal.js +21 -0
  37. package/dist/src/components/WalletModal/components/ManageWalletModal.js.map +1 -0
  38. package/dist/src/components/WalletModal/components/PreviewTransactionModal.css +128 -0
  39. package/dist/src/components/WalletModal/components/PreviewTransactionModal.d.ts +17 -0
  40. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js +10 -0
  41. package/dist/src/components/WalletModal/components/PreviewTransactionModal.js.map +1 -0
  42. package/dist/src/components/WalletModal/components/ReceiveModal.css +101 -0
  43. package/dist/src/components/WalletModal/components/ReceiveModal.d.ts +8 -0
  44. package/dist/src/components/WalletModal/components/ReceiveModal.js +22 -0
  45. package/dist/src/components/WalletModal/components/ReceiveModal.js.map +1 -0
  46. package/dist/src/components/WalletModal/components/SendModal.css +234 -0
  47. package/dist/src/components/WalletModal/components/SendModal.d.ts +18 -0
  48. package/dist/src/components/WalletModal/components/SendModal.js +127 -0
  49. package/dist/src/components/WalletModal/components/SendModal.js.map +1 -0
  50. package/dist/src/components/WalletModal/components/SuccessModal.css +86 -0
  51. package/dist/src/components/WalletModal/components/SuccessModal.d.ts +13 -0
  52. package/dist/src/components/WalletModal/components/SuccessModal.js +8 -0
  53. package/dist/src/components/WalletModal/components/SuccessModal.js.map +1 -0
  54. package/dist/src/components/WalletModal/components/UserAvatar.d.ts +9 -0
  55. package/dist/src/components/WalletModal/components/UserAvatar.js +31 -0
  56. package/dist/src/components/WalletModal/components/UserAvatar.js.map +1 -0
  57. package/dist/src/components/WalletModal/components/index.d.ts +21 -0
  58. package/dist/src/components/WalletModal/components/index.js +13 -0
  59. package/dist/src/components/WalletModal/components/index.js.map +1 -0
  60. package/dist/src/components/WalletModal/hooks/index.d.ts +6 -0
  61. package/dist/src/components/WalletModal/hooks/index.js +7 -0
  62. package/dist/src/components/WalletModal/hooks/index.js.map +1 -0
  63. package/dist/src/components/WalletModal/hooks/useAddressValidation.d.ts +4 -0
  64. package/dist/src/components/WalletModal/hooks/useAddressValidation.js +17 -0
  65. package/dist/src/components/WalletModal/hooks/useAddressValidation.js.map +1 -0
  66. package/dist/src/components/WalletModal/hooks/useAmountValidation.d.ts +4 -0
  67. package/dist/src/components/WalletModal/hooks/useAmountValidation.js +29 -0
  68. package/dist/src/components/WalletModal/hooks/useAmountValidation.js.map +1 -0
  69. package/dist/src/components/WalletModal/hooks/useSendTransaction.d.ts +20 -0
  70. package/dist/src/components/WalletModal/hooks/useSendTransaction.js +55 -0
  71. package/dist/src/components/WalletModal/hooks/useSendTransaction.js.map +1 -0
  72. package/dist/src/components/WalletModal/index.d.ts +5 -0
  73. package/dist/src/components/WalletModal/index.js +7 -0
  74. package/dist/src/components/WalletModal/index.js.map +1 -0
  75. package/dist/src/components/WalletModal/utils/addressUtils.d.ts +19 -0
  76. package/dist/src/components/WalletModal/utils/addressUtils.js +62 -0
  77. package/dist/src/components/WalletModal/utils/addressUtils.js.map +1 -0
  78. package/dist/src/components/WalletModal/utils/formatUtils.d.ts +20 -0
  79. package/dist/src/components/WalletModal/utils/formatUtils.js +47 -0
  80. package/dist/src/components/WalletModal/utils/formatUtils.js.map +1 -0
  81. package/dist/src/components/WalletModal/utils/index.d.ts +5 -0
  82. package/dist/src/components/WalletModal/utils/index.js +6 -0
  83. package/dist/src/components/WalletModal/utils/index.js.map +1 -0
  84. package/dist/src/index.d.ts +2 -1
  85. package/dist/src/index.js +1 -0
  86. package/dist/src/index.js.map +1 -1
  87. package/dist/src/types.d.ts +29 -0
  88. package/dist/src/wagmiConfig.js +6 -2
  89. package/dist/src/wagmiConfig.js.map +1 -1
  90. package/dist/tsconfig.tsbuildinfo +1 -1
  91. package/package.json +8 -8
@@ -590,6 +590,46 @@ const ONBOARDING_UI_STYLES = `
590
590
  width: 100%;
591
591
  }
592
592
 
593
+ /* Hide social buttons when OTP screen is visible - using class for better browser support */
594
+ .onboarding-card.onboarding-otp-active .onboarding-button-google,
595
+ .onboarding-card.onboarding-otp-active .onboarding-button-social,
596
+ .onboarding-card.onboarding-otp-active .onboarding-social-grid,
597
+ .onboarding-card.onboarding-otp-active .onboarding-divider:not(.onboarding-otp-divider),
598
+ .onboarding-card.onboarding-otp-active .onboarding-button-passkey,
599
+ .onboarding-card.onboarding-otp-active .onboarding-passkey-signup-link,
600
+ .onboarding-card.onboarding-otp-active .onboarding-passkey-divider,
601
+ .onboarding-card.onboarding-otp-active .onboarding-form:not(.onboarding-otp-form),
602
+ .onboarding-card.onboarding-otp-active .onboarding-input-group:not(.onboarding-otp-inputs-container) {
603
+ display: none !important;
604
+ visibility: hidden !important;
605
+ opacity: 0 !important;
606
+ height: 0 !important;
607
+ margin: 0 !important;
608
+ padding: 0 !important;
609
+ overflow: hidden !important;
610
+ pointer-events: none !important;
611
+ }
612
+
613
+ /* Fallback using :has() for modern browsers */
614
+ @supports selector(:has(*)) {
615
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-button-google,
616
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-button-social,
617
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-social-grid,
618
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-divider:not(.onboarding-otp-divider),
619
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-button-passkey,
620
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-passkey-signup-link,
621
+ .onboarding-card:has(.onboarding-otp-verification) .onboarding-passkey-divider {
622
+ display: none !important;
623
+ visibility: hidden !important;
624
+ opacity: 0 !important;
625
+ height: 0 !important;
626
+ margin: 0 !important;
627
+ padding: 0 !important;
628
+ overflow: hidden !important;
629
+ pointer-events: none !important;
630
+ }
631
+ }
632
+
593
633
  .onboarding-social-icon {
594
634
  width: 20px;
595
635
  height: 20px;
@@ -856,13 +896,17 @@ const ONBOARDING_UI_STYLES = `
856
896
  color: #b91c1c;
857
897
  }
858
898
 
859
- .onboarding-otp-resend-link:disabled {
899
+ .onboarding-otp-resend-link:disabled,
900
+ .onboarding-otp-resend-link-disabled {
860
901
  color: #9ca3af;
861
902
  cursor: not-allowed;
862
903
  text-decoration: none;
904
+ pointer-events: none;
905
+ opacity: 0.6;
863
906
  }
864
907
 
865
- .onboarding-theme-dark .onboarding-otp-resend-link:disabled {
908
+ .onboarding-theme-dark .onboarding-otp-resend-link:disabled,
909
+ .onboarding-theme-dark .onboarding-otp-resend-link-disabled {
866
910
  color: #6b7280;
867
911
  }
868
912
 
@@ -905,7 +949,8 @@ const ONBOARDING_UI_STYLES = `
905
949
  font-size: 14px;
906
950
  font-weight: 500;
907
951
  margin-top: 12px;
908
- display: inline-block;
952
+ display: block;
953
+ text-align: center;
909
954
  transition: color 0.15s ease;
910
955
  }
911
956
 
@@ -943,6 +988,7 @@ export class OnboardingUIWeb {
943
988
  emailInput = null;
944
989
  otpInput = null;
945
990
  otpInputs = [];
991
+ autoSubmitEnabled = true; // Flag to control auto-submit after failed attempts
946
992
  emailForm = null;
947
993
  continueButton = null;
948
994
  emailArrowButton = null;
@@ -970,6 +1016,7 @@ export class OnboardingUIWeb {
970
1016
  loading = false;
971
1017
  activeButton = null;
972
1018
  resendCooldown = 0;
1019
+ resendCooldownTimer = null;
973
1020
  externalWalletsEnabled = false;
974
1021
  constructor(config, externalWalletsEnabled = false) {
975
1022
  this.externalWalletsEnabled = externalWalletsEnabled;
@@ -1019,16 +1066,40 @@ export class OnboardingUIWeb {
1019
1066
  const params = new URLSearchParams(window.location.search);
1020
1067
  const success = params.get('success');
1021
1068
  const error = params.get('error');
1022
- // If success=true, always show loading (less restrictive)
1023
- if (success === 'true') {
1024
- // Ensure modal is open if in modal mode
1025
- if (this.config.modal && this.modalOverlay) {
1026
- this.modalOverlay.classList.remove('onboarding-modal-closing');
1027
- this.modalOverlay.classList.add('onboarding-modal-open');
1028
- this.modalOverlay.style.display = 'flex';
1029
- document.body.style.overflow = 'hidden';
1069
+ const accessToken = params.get('accessToken');
1070
+ const provider = params.get('provider') || params.get('authProvider');
1071
+ // Helper to extract provider from accessToken JWT
1072
+ const getAuthProviderFromToken = (token) => {
1073
+ try {
1074
+ const base64Url = token.split('.')[1];
1075
+ const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
1076
+ const jsonPayload = decodeURIComponent(window.atob(base64).split('').map(function (c) {
1077
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
1078
+ }).join(''));
1079
+ const payload = JSON.parse(jsonPayload);
1080
+ return (payload.authProvider || payload.provider || '').toLowerCase();
1030
1081
  }
1031
- this.showLoadingScreen();
1082
+ catch (e) {
1083
+ return null;
1084
+ }
1085
+ };
1086
+ // Detect OAuth callback: check for provider in URL or extract from accessToken
1087
+ let detectedProvider = provider;
1088
+ if (!detectedProvider && accessToken) {
1089
+ detectedProvider = getAuthProviderFromToken(accessToken);
1090
+ }
1091
+ const isOAuthCallback = detectedProvider && (detectedProvider === 'google' || detectedProvider === 'discord' || detectedProvider === 'twitter' || detectedProvider === 'x');
1092
+ const hasOAuthParams = isOAuthCallback || (success === 'true' && !!accessToken);
1093
+ // If success=true and it's an OAuth callback (Google, Discord, Twitter), show loading modal with header and footer
1094
+ if (success === 'true' && hasOAuthParams) {
1095
+ // Show loading modal immediately - it creates its own overlay
1096
+ // Use requestAnimationFrame + setTimeout to ensure DOM is ready
1097
+ requestAnimationFrame(() => {
1098
+ setTimeout(() => {
1099
+ // Directly show loading modal - it will create its own overlay with header and footer
1100
+ this.showLoadingModal();
1101
+ }, 100);
1102
+ });
1032
1103
  return;
1033
1104
  }
1034
1105
  // Check login source to decide if we should handle this (for errors)
@@ -1187,6 +1258,7 @@ export class OnboardingUIWeb {
1187
1258
  }
1188
1259
  /**
1189
1260
  * Show loading screen as a modal overlay (for inline components)
1261
+ * Public method to allow external access
1190
1262
  */
1191
1263
  showLoadingModal() {
1192
1264
  // Hide any other modals that might be showing (wallet selection, etc.)
@@ -1233,10 +1305,10 @@ export class OnboardingUIWeb {
1233
1305
  box-sizing: border-box;
1234
1306
  `,
1235
1307
  });
1236
- // Header with logo
1308
+ // Header with logo and title
1237
1309
  const header = this.createElement('div', {
1238
1310
  className: 'onboarding-header',
1239
- style: 'display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%;',
1311
+ style: 'display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%; margin-bottom: 32px;',
1240
1312
  });
1241
1313
  const logoSection = this.createElement('div', { className: 'onboarding-logo-section' });
1242
1314
  if (this.config.logo) {
@@ -1250,6 +1322,22 @@ export class OnboardingUIWeb {
1250
1322
  logoSection.appendChild(logoContainer);
1251
1323
  }
1252
1324
  header.appendChild(logoSection);
1325
+ // Add title
1326
+ const title = this.createElement('h1', {
1327
+ className: 'onboarding-title',
1328
+ textContent: this.config.onboardTitle || 'Sign in',
1329
+ style: `
1330
+ font-size: 24px;
1331
+ font-weight: 600;
1332
+ margin: 0;
1333
+ color: ${this.config.theme === 'dark' ? '#ffffff' : '#111827'};
1334
+ text-align: center;
1335
+ margin-bottom: 0;
1336
+ letter-spacing: -0.01em;
1337
+ line-height: 1.4;
1338
+ `,
1339
+ });
1340
+ header.appendChild(title);
1253
1341
  loadingCard.appendChild(header);
1254
1342
  // Spinner
1255
1343
  const spinnerColor = this.config.theme === 'dark' ? '#9ca3af' : '#111827';
@@ -1342,10 +1430,21 @@ export class OnboardingUIWeb {
1342
1430
  document.body.appendChild(loadingModalOverlay);
1343
1431
  }
1344
1432
  else {
1433
+ // Ensure modal is visible
1345
1434
  loadingModalOverlay.style.display = 'flex';
1435
+ loadingModalOverlay.style.visibility = 'visible';
1436
+ loadingModalOverlay.style.opacity = '1';
1437
+ loadingModalOverlay.style.zIndex = '9999999';
1346
1438
  }
1347
1439
  // Prevent body scroll
1348
1440
  document.body.style.overflow = 'hidden';
1441
+ // Force visibility (defensive check)
1442
+ if (loadingModalOverlay) {
1443
+ loadingModalOverlay.style.display = 'flex';
1444
+ loadingModalOverlay.style.visibility = 'visible';
1445
+ loadingModalOverlay.style.opacity = '1';
1446
+ loadingModalOverlay.style.zIndex = '9999999';
1447
+ }
1349
1448
  }
1350
1449
  /**
1351
1450
  * Hide loading modal overlay
@@ -1354,6 +1453,8 @@ export class OnboardingUIWeb {
1354
1453
  const loadingModalOverlay = document.getElementById('onboarding-loading-modal-overlay');
1355
1454
  if (loadingModalOverlay) {
1356
1455
  loadingModalOverlay.style.display = 'none';
1456
+ loadingModalOverlay.style.visibility = 'hidden';
1457
+ loadingModalOverlay.style.opacity = '0';
1357
1458
  document.body.style.overflow = '';
1358
1459
  }
1359
1460
  }
@@ -2422,12 +2523,11 @@ export class OnboardingUIWeb {
2422
2523
  }
2423
2524
  }
2424
2525
  /**
2425
- * Show OTP verification screen
2526
+ * Hide all login-related elements (social buttons, passkey, etc.)
2527
+ * This ensures they stay hidden on the OTP screen
2528
+ * Made public so it can be called from AbstraxnProvider
2426
2529
  */
2427
- showOtpInput() {
2428
- if (!this.rootElement)
2429
- return;
2430
- // Hide the email form and all login-related elements
2530
+ hideLoginElements() {
2431
2531
  if (this.emailForm) {
2432
2532
  this.emailForm.style.display = 'none';
2433
2533
  }
@@ -2470,6 +2570,15 @@ export class OnboardingUIWeb {
2470
2570
  if (this.footer) {
2471
2571
  this.footer.style.display = 'none';
2472
2572
  }
2573
+ }
2574
+ /**
2575
+ * Show OTP verification screen
2576
+ */
2577
+ showOtpInput() {
2578
+ if (!this.rootElement)
2579
+ return;
2580
+ // Hide the email form and all login-related elements
2581
+ this.hideLoginElements();
2473
2582
  // Create OTP verification screen
2474
2583
  this.createOtpVerificationScreen();
2475
2584
  }
@@ -2488,6 +2597,8 @@ export class OnboardingUIWeb {
2488
2597
  const card = this.rootElement.querySelector('.onboarding-card');
2489
2598
  if (!card)
2490
2599
  return;
2600
+ // Add class to card to indicate OTP screen is active (for CSS targeting)
2601
+ card.classList.add('onboarding-otp-active');
2491
2602
  // Create OTP verification container
2492
2603
  this.otpVerificationScreen = this.createElement('div', {
2493
2604
  className: 'onboarding-otp-verification',
@@ -2573,7 +2684,15 @@ export class OnboardingUIWeb {
2573
2684
  const resendLink = resendSection.querySelector('#otp-resend-link');
2574
2685
  if (resendLink) {
2575
2686
  this.resendButton = resendLink;
2576
- resendLink.addEventListener('click', () => this.handleResendOtp());
2687
+ resendLink.addEventListener('click', (e) => {
2688
+ e.preventDefault();
2689
+ e.stopPropagation();
2690
+ // Prevent click if cooldown is active
2691
+ if (this.resendCooldown > 0) {
2692
+ return;
2693
+ }
2694
+ this.handleResendOtp();
2695
+ });
2577
2696
  }
2578
2697
  // Footer (add to OTP screen as well)
2579
2698
  if (this.config.showFooter) {
@@ -2631,11 +2750,24 @@ export class OnboardingUIWeb {
2631
2750
  }
2632
2751
  // Append to card
2633
2752
  card.appendChild(this.otpVerificationScreen);
2753
+ // Ensure login elements are hidden when OTP screen is created
2754
+ // This is critical to prevent social buttons from showing
2755
+ this.hideLoginElements();
2756
+ // Also ensure they stay hidden after DOM updates
2757
+ setTimeout(() => {
2758
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
2759
+ this.hideLoginElements();
2760
+ }
2761
+ }, 0);
2634
2762
  // Focus first input
2635
2763
  setTimeout(() => {
2636
2764
  if (this.otpInputs[0]) {
2637
2765
  this.otpInputs[0].focus();
2638
2766
  }
2767
+ // Final check to ensure social buttons are hidden
2768
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
2769
+ this.hideLoginElements();
2770
+ }
2639
2771
  }, 100);
2640
2772
  // Start resend cooldown
2641
2773
  this.startResendCooldown();
@@ -2672,12 +2804,23 @@ export class OnboardingUIWeb {
2672
2804
  if (index < 5 && this.otpInputs[index + 1]) {
2673
2805
  this.otpInputs[index + 1].focus();
2674
2806
  }
2675
- // Don't auto-verify, wait for user to click Verify button
2807
+ // Re-enable auto-submit when user starts typing (clears previous error state)
2808
+ if (!this.autoSubmitEnabled) {
2809
+ this.autoSubmitEnabled = true;
2810
+ this.setError(null); // Clear any previous error
2811
+ }
2676
2812
  }
2677
2813
  else {
2678
2814
  input.value = '';
2679
2815
  }
2680
2816
  this.updateOtpValue();
2817
+ // Auto-submit when 6 digits are entered (only if auto-submit is enabled)
2818
+ if (this.otp.length === 6 && this.autoSubmitEnabled && !this.loading) {
2819
+ // Small delay to ensure the last digit is properly set
2820
+ setTimeout(() => {
2821
+ this.handleOtpVerify();
2822
+ }, 100);
2823
+ }
2681
2824
  }
2682
2825
  /**
2683
2826
  * Handle OTP keydown - backspace to go to previous field
@@ -2706,7 +2849,13 @@ export class OnboardingUIWeb {
2706
2849
  this.otpInputs[lastFilledIndex].focus();
2707
2850
  }
2708
2851
  this.updateOtpValue();
2709
- // Don't auto-verify, wait for user to click Verify button
2852
+ // Auto-submit when 6 digits are pasted (only if auto-submit is enabled)
2853
+ if (this.otp.length === 6 && this.autoSubmitEnabled && !this.loading) {
2854
+ // Small delay to ensure all digits are properly set
2855
+ setTimeout(() => {
2856
+ this.handleOtpVerify();
2857
+ }, 100);
2858
+ }
2710
2859
  }
2711
2860
  /**
2712
2861
  * Update OTP value from input fields
@@ -2736,7 +2885,9 @@ export class OnboardingUIWeb {
2736
2885
  this.config.onLoginSuccess?.(result);
2737
2886
  }
2738
2887
  else {
2739
- throw new Error(result.error || 'Invalid verification code');
2888
+ // Use the error message from the API response, fallback to generic message only if not provided
2889
+ const errorMessage = result.error || 'Invalid verification code';
2890
+ throw new Error(errorMessage);
2740
2891
  }
2741
2892
  }
2742
2893
  else if (this.config.emailOtpEndpoint) {
@@ -2752,7 +2903,9 @@ export class OnboardingUIWeb {
2752
2903
  this.config.onLoginSuccess?.({ token: data.token });
2753
2904
  }
2754
2905
  else {
2755
- throw new Error(data.message || 'Invalid verification code');
2906
+ // Use the error message from the API response, fallback to generic message only if not provided
2907
+ const errorMessage = data.message || data.error || 'Invalid verification code';
2908
+ throw new Error(errorMessage);
2756
2909
  }
2757
2910
  }
2758
2911
  }
@@ -2760,6 +2913,26 @@ export class OnboardingUIWeb {
2760
2913
  const errorMessage = err instanceof Error ? err.message : 'An error occurred';
2761
2914
  this.setError(errorMessage);
2762
2915
  this.config.onLoginError?.(err instanceof Error ? err : new Error(errorMessage));
2916
+ // Disable auto-submit after failed attempt
2917
+ this.autoSubmitEnabled = false;
2918
+ // Ensure social buttons and login elements remain hidden on OTP screen when error occurs
2919
+ // This prevents them from appearing when an OTP error is shown
2920
+ if (this.otpVerificationScreen) {
2921
+ // Immediately hide login elements
2922
+ this.hideLoginElements();
2923
+ // Also use setTimeout to ensure they stay hidden after any potential re-renders
2924
+ setTimeout(() => {
2925
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
2926
+ this.hideLoginElements();
2927
+ }
2928
+ }, 0);
2929
+ // Additional check after a short delay to catch any late re-renders
2930
+ setTimeout(() => {
2931
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
2932
+ this.hideLoginElements();
2933
+ }
2934
+ }, 100);
2935
+ }
2763
2936
  // Clear OTP inputs on error
2764
2937
  this.otpInputs.forEach(input => {
2765
2938
  input.value = '';
@@ -2781,6 +2954,8 @@ export class OnboardingUIWeb {
2781
2954
  if (this.resendCooldown > 0) {
2782
2955
  return;
2783
2956
  }
2957
+ // Re-enable auto-submit when resending OTP
2958
+ this.autoSubmitEnabled = true;
2784
2959
  this.setError(null);
2785
2960
  this.setLoading(true, 'email');
2786
2961
  try {
@@ -2824,20 +2999,42 @@ export class OnboardingUIWeb {
2824
2999
  * Start resend cooldown timer
2825
3000
  */
2826
3001
  startResendCooldown() {
3002
+ // Clear any existing cooldown timer
3003
+ if (this.resendCooldownTimer) {
3004
+ clearInterval(this.resendCooldownTimer);
3005
+ }
2827
3006
  this.resendCooldown = 60; // 60 seconds cooldown
2828
- const updateCooldown = () => {
2829
- if (this.resendCooldown > 0 && this.resendButton) {
2830
- this.resendButton.textContent = `Resend (${this.resendCooldown}s)`;
2831
- this.resendButton.setAttribute('disabled', 'true');
3007
+ // Update UI immediately
3008
+ if (this.resendButton) {
3009
+ this.resendButton.textContent = `Resend (${this.resendCooldown}s)`;
3010
+ this.resendButton.classList.add('onboarding-otp-resend-link-disabled');
3011
+ this.resendButton.style.pointerEvents = 'none';
3012
+ this.resendButton.style.cursor = 'not-allowed';
3013
+ this.resendButton.style.opacity = '0.6';
3014
+ }
3015
+ // Start countdown timer
3016
+ this.resendCooldownTimer = setInterval(() => {
3017
+ if (this.resendCooldown > 0) {
2832
3018
  this.resendCooldown--;
2833
- setTimeout(updateCooldown, 1000);
3019
+ if (this.resendButton) {
3020
+ this.resendButton.textContent = `Resend (${this.resendCooldown}s)`;
3021
+ }
2834
3022
  }
2835
- else if (this.resendButton) {
2836
- this.resendButton.textContent = 'Resend';
2837
- this.resendButton.removeAttribute('disabled');
3023
+ else {
3024
+ // Cooldown complete, enable resend
3025
+ if (this.resendCooldownTimer) {
3026
+ clearInterval(this.resendCooldownTimer);
3027
+ this.resendCooldownTimer = null;
3028
+ }
3029
+ if (this.resendButton) {
3030
+ this.resendButton.textContent = 'Resend';
3031
+ this.resendButton.classList.remove('onboarding-otp-resend-link-disabled');
3032
+ this.resendButton.style.pointerEvents = 'auto';
3033
+ this.resendButton.style.cursor = 'pointer';
3034
+ this.resendButton.style.opacity = '1';
3035
+ }
2838
3036
  }
2839
- };
2840
- updateCooldown();
3037
+ }, 1000);
2841
3038
  }
2842
3039
  /**
2843
3040
  * Set error message
@@ -2854,6 +3051,24 @@ export class OnboardingUIWeb {
2854
3051
  else {
2855
3052
  otpErrorElement.style.display = 'none';
2856
3053
  }
3054
+ // Ensure social buttons remain hidden when showing error on OTP screen
3055
+ // Call immediately and also with delays to catch any re-renders
3056
+ this.hideLoginElements();
3057
+ setTimeout(() => {
3058
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
3059
+ this.hideLoginElements();
3060
+ }
3061
+ }, 0);
3062
+ setTimeout(() => {
3063
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
3064
+ this.hideLoginElements();
3065
+ }
3066
+ }, 50);
3067
+ setTimeout(() => {
3068
+ if (this.otpVerificationScreen && this.otpVerificationScreen.parentElement) {
3069
+ this.hideLoginElements();
3070
+ }
3071
+ }, 150);
2857
3072
  return;
2858
3073
  }
2859
3074
  }
@@ -3009,16 +3224,23 @@ export class OnboardingUIWeb {
3009
3224
  * Reset OTP screen and show initial login form
3010
3225
  */
3011
3226
  resetToLoginForm() {
3227
+ // Clear resend cooldown timer
3228
+ if (this.resendCooldownTimer) {
3229
+ clearInterval(this.resendCooldownTimer);
3230
+ this.resendCooldownTimer = null;
3231
+ }
3232
+ this.resendCooldown = 0;
3012
3233
  // Clear OTP verification screen
3013
3234
  if (this.otpVerificationScreen) {
3014
3235
  this.otpVerificationScreen.remove();
3015
3236
  this.otpVerificationScreen = null;
3016
3237
  }
3017
- // Remove loading/error mode classes
3238
+ // Remove loading/error mode classes and OTP active class
3018
3239
  const card = this.rootElement?.querySelector('.onboarding-card');
3019
3240
  if (card) {
3020
3241
  card.classList.remove('onboarding-mode-loading');
3021
3242
  card.classList.remove('onboarding-mode-error');
3243
+ card.classList.remove('onboarding-otp-active');
3022
3244
  }
3023
3245
  // Hide loading modal if exists (for inline components)
3024
3246
  this.hideLoadingModal();
@@ -3037,10 +3259,18 @@ export class OnboardingUIWeb {
3037
3259
  this.otpSent = false;
3038
3260
  this.loading = false;
3039
3261
  // Clear timer
3262
+ // Clear resend cooldown timer
3263
+ if (this.resendCooldownTimer) {
3264
+ clearInterval(this.resendCooldownTimer);
3265
+ this.resendCooldownTimer = null;
3266
+ }
3040
3267
  this.resendCooldown = 0;
3041
3268
  if (this.resendButton) {
3042
3269
  this.resendButton.textContent = 'Resend';
3043
- this.resendButton.removeAttribute('disabled');
3270
+ this.resendButton.classList.remove('onboarding-otp-resend-link-disabled');
3271
+ this.resendButton.style.pointerEvents = 'auto';
3272
+ this.resendButton.style.cursor = 'pointer';
3273
+ this.resendButton.style.opacity = '1';
3044
3274
  }
3045
3275
  // Get auth methods
3046
3276
  const authMethods = this.config.authMethods || ['otp', 'google'];