@swype-org/react-sdk 0.1.69 → 0.1.71

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.cjs CHANGED
@@ -1618,6 +1618,9 @@ function resolvePostAuthStep(state) {
1618
1618
  }
1619
1619
  return { step: "open-wallet", clearPersistedFlow: false };
1620
1620
  }
1621
+ if (state.mobileSetupInProgress && !hasActiveWallet(state.accounts)) {
1622
+ return { step: "open-wallet", clearPersistedFlow: false };
1623
+ }
1621
1624
  if ((state.accounts.length === 0 || !hasActiveWallet(state.accounts)) && !state.connectingNewAccount) {
1622
1625
  return { step: "wallet-picker", clearPersistedFlow: false };
1623
1626
  }
@@ -1633,11 +1636,31 @@ function resolveRestoredMobileFlow(transferStatus, isSetup) {
1633
1636
  if (transferStatus === "FAILED") {
1634
1637
  return { kind: "resume-failed", step: "success", clearPersistedFlow: true };
1635
1638
  }
1639
+ if (transferStatus === "SENDING" || transferStatus === "SENT") {
1640
+ return { kind: "resume-processing", step: "processing", clearPersistedFlow: true };
1641
+ }
1636
1642
  if (isSetup) {
1637
1643
  return { kind: "resume-stale-setup", step: "wallet-picker", clearPersistedFlow: true };
1638
1644
  }
1639
1645
  return { kind: "resume-open-wallet", step: "open-wallet", clearPersistedFlow: false };
1640
1646
  }
1647
+
1648
+ // src/dataLoading.ts
1649
+ function resolveDataLoadAction({
1650
+ authenticated,
1651
+ step,
1652
+ accountsCount,
1653
+ hasActiveCredential,
1654
+ loading
1655
+ }) {
1656
+ if (!authenticated || step === "login" || step === "otp-verify" || accountsCount > 0 || !hasActiveCredential) {
1657
+ return "reset";
1658
+ }
1659
+ if (loading) {
1660
+ return "wait";
1661
+ }
1662
+ return "load";
1663
+ }
1641
1664
  var FOOTER_CSS = `
1642
1665
  .swype-screen-footer {
1643
1666
  padding-bottom: max(24px, env(safe-area-inset-bottom, 24px));
@@ -3424,6 +3447,8 @@ var outlineBtnWrapStyle = {
3424
3447
  function SuccessScreen({
3425
3448
  amount,
3426
3449
  currency,
3450
+ succeeded,
3451
+ error,
3427
3452
  merchantName,
3428
3453
  sourceName,
3429
3454
  remainingLimit,
@@ -3434,7 +3459,8 @@ function SuccessScreen({
3434
3459
  autoCloseSeconds
3435
3460
  }) {
3436
3461
  const { tokens } = useSwypeConfig();
3437
- const [countdown, setCountdown] = react.useState(autoCloseSeconds ?? 0);
3462
+ const effectiveAutoClose = succeeded ? autoCloseSeconds : void 0;
3463
+ const [countdown, setCountdown] = react.useState(effectiveAutoClose ?? 0);
3438
3464
  const doneCalledRef = react.useRef(false);
3439
3465
  const handleDone = react.useCallback(() => {
3440
3466
  if (doneCalledRef.current) return;
@@ -3442,7 +3468,7 @@ function SuccessScreen({
3442
3468
  onDone();
3443
3469
  }, [onDone]);
3444
3470
  react.useEffect(() => {
3445
- if (!autoCloseSeconds || autoCloseSeconds <= 0) return;
3471
+ if (!effectiveAutoClose || effectiveAutoClose <= 0) return;
3446
3472
  const intervalId = window.setInterval(() => {
3447
3473
  setCountdown((prev) => {
3448
3474
  if (prev <= 1) {
@@ -3453,18 +3479,18 @@ function SuccessScreen({
3453
3479
  });
3454
3480
  }, 1e3);
3455
3481
  return () => window.clearInterval(intervalId);
3456
- }, [autoCloseSeconds]);
3482
+ }, [effectiveAutoClose]);
3457
3483
  react.useEffect(() => {
3458
- if (autoCloseSeconds && countdown === 0) {
3484
+ if (effectiveAutoClose && countdown === 0) {
3459
3485
  handleDone();
3460
3486
  }
3461
- }, [autoCloseSeconds, countdown, handleDone]);
3487
+ }, [effectiveAutoClose, countdown, handleDone]);
3462
3488
  return /* @__PURE__ */ jsxRuntime.jsxs(
3463
3489
  ScreenLayout,
3464
3490
  {
3465
3491
  footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3466
- /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: handleDone, children: "Done" }),
3467
- autoCloseSeconds != null && autoCloseSeconds > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: countdownStyle(tokens.textMuted), children: [
3492
+ /* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: handleDone, children: succeeded ? "Done" : "Try again" }),
3493
+ effectiveAutoClose != null && effectiveAutoClose > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: countdownStyle(tokens.textMuted), children: [
3468
3494
  "Returning to app in ",
3469
3495
  countdown,
3470
3496
  "s\u2026"
@@ -3480,26 +3506,32 @@ function SuccessScreen({
3480
3506
  }
3481
3507
  ),
3482
3508
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle4, children: [
3483
- /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z", fill: tokens.success }) }) }),
3484
- /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle6(tokens.text), children: [
3485
- "$",
3486
- amount.toFixed(2),
3487
- " deposited"
3488
- ] }),
3489
- merchantName && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle6(tokens.textSecondary), children: [
3490
- "to ",
3491
- merchantName
3509
+ succeeded ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3510
+ /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "success", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41L9 16.17z", fill: tokens.success }) }) }),
3511
+ /* @__PURE__ */ jsxRuntime.jsxs("h2", { style: headingStyle6(tokens.text), children: [
3512
+ "$",
3513
+ amount.toFixed(2),
3514
+ " deposited"
3515
+ ] }),
3516
+ merchantName && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: subtitleStyle6(tokens.textSecondary), children: [
3517
+ "to ",
3518
+ merchantName
3519
+ ] })
3520
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
3521
+ /* @__PURE__ */ jsxRuntime.jsx(IconCircle, { variant: "error", size: 64, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z", fill: tokens.error }) }) }),
3522
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle6(tokens.text), children: "Transfer failed" }),
3523
+ error && /* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle6(tokens.error), children: error })
3492
3524
  ] }),
3493
3525
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryCardStyle(tokens), children: [
3494
3526
  sourceName && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
3495
3527
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryLabelStyle(tokens.textMuted), children: "From" }),
3496
3528
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryValueStyle(tokens.text), children: sourceName })
3497
3529
  ] }),
3498
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
3530
+ succeeded && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
3499
3531
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryLabelStyle(tokens.textMuted), children: "Time" }),
3500
3532
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryValueStyle(tokens.text), children: "just now" })
3501
3533
  ] }),
3502
- remainingLimit != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
3534
+ succeeded && remainingLimit != null && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: summaryRowStyle, children: [
3503
3535
  /* @__PURE__ */ jsxRuntime.jsx("span", { style: summaryLabelStyle(tokens.textMuted), children: "Remaining limit" }),
3504
3536
  /* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...summaryValueStyle(tokens.text), color: tokens.accent }, children: [
3505
3537
  "$",
@@ -3507,7 +3539,7 @@ function SuccessScreen({
3507
3539
  ] })
3508
3540
  ] })
3509
3541
  ] }),
3510
- onIncreaseLimits && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellCardStyle(tokens), children: [
3542
+ succeeded && onIncreaseLimits && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellCardStyle(tokens), children: [
3511
3543
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: upsellHeaderStyle, children: [
3512
3544
  /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", style: { marginRight: 6 }, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 14l5-5 5 5", stroke: tokens.accent, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }),
3513
3545
  /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Want higher limits?" })
@@ -3989,12 +4021,11 @@ var radioDotStyle2 = (color) => ({
3989
4021
  borderRadius: "50%",
3990
4022
  background: color
3991
4023
  });
3992
- var STEP_LABELS = ["Creating transfer", "Verifying", "Sent", "Done"];
4024
+ var STEP_LABELS = ["Creating transfer", "Verifying", "Sent"];
3993
4025
  var PHASE_ACTIVE_INDEX = {
3994
4026
  creating: 0,
3995
4027
  verifying: 1,
3996
- sent: 2,
3997
- done: 4
4028
+ sent: 2
3998
4029
  };
3999
4030
  function buildSteps(phase) {
4000
4031
  const activeIdx = PHASE_ACTIVE_INDEX[phase];
@@ -4472,6 +4503,7 @@ function SwypePaymentInner({
4472
4503
  const [oneTapLimit, setOneTapLimit] = react.useState(100);
4473
4504
  const [mobileFlow, setMobileFlow] = react.useState(false);
4474
4505
  const [deeplinkUri, setDeeplinkUri] = react.useState(null);
4506
+ const loadingDataRef = react.useRef(false);
4475
4507
  const pollingTransferIdRef = react.useRef(null);
4476
4508
  const mobileSigningTransferIdRef = react.useRef(null);
4477
4509
  const mobileSetupFlowRef = react.useRef(false);
@@ -4502,6 +4534,10 @@ function SwypePaymentInner({
4502
4534
  setConnectingNewAccount(false);
4503
4535
  }
4504
4536
  }, [getAccessToken, activeCredentialId, apiBaseUrl, depositAmount]);
4537
+ const resetDataLoadingState = react.useCallback(() => {
4538
+ loadingDataRef.current = false;
4539
+ setLoadingData(false);
4540
+ }, []);
4505
4541
  const enterPersistedMobileFlow = react.useCallback((persisted, errorMessage) => {
4506
4542
  setMobileFlow(true);
4507
4543
  setDeeplinkUri(persisted.deeplinkUri);
@@ -4519,6 +4555,7 @@ function SwypePaymentInner({
4519
4555
  clearMobileFlowState();
4520
4556
  try {
4521
4557
  await reloadAccounts();
4558
+ resetDataLoadingState();
4522
4559
  setTransfer(null);
4523
4560
  setError(null);
4524
4561
  setDeeplinkUri(null);
@@ -4539,7 +4576,7 @@ function SwypePaymentInner({
4539
4576
  setDeeplinkUri(null);
4540
4577
  setMobileFlow(false);
4541
4578
  setStep("confirm-sign");
4542
- }, [polling.stopPolling, reloadAccounts]);
4579
+ }, [polling.stopPolling, reloadAccounts, resetDataLoadingState]);
4543
4580
  const handleRetryMobileStatus = react.useCallback(() => {
4544
4581
  setError(null);
4545
4582
  const currentTransfer = polling.transfer ?? transfer;
@@ -4649,6 +4686,7 @@ function SwypePaymentInner({
4649
4686
  hasPasskey: true,
4650
4687
  accounts: accts,
4651
4688
  persistedMobileFlow: persisted,
4689
+ mobileSetupInProgress: false,
4652
4690
  connectingNewAccount: false
4653
4691
  });
4654
4692
  if (resolved.clearPersistedFlow) {
@@ -4689,6 +4727,16 @@ function SwypePaymentInner({
4689
4727
  setStep("success");
4690
4728
  return;
4691
4729
  }
4730
+ if (mobileResolution.kind === "resume-processing") {
4731
+ clearMobileFlowState();
4732
+ setMobileFlow(false);
4733
+ setDeeplinkUri(null);
4734
+ setTransfer(existingTransfer);
4735
+ setError(null);
4736
+ setStep("processing");
4737
+ polling.startPolling(existingTransfer.id);
4738
+ return;
4739
+ }
4692
4740
  if (mobileResolution.kind === "resume-stale-setup") {
4693
4741
  clearMobileFlowState();
4694
4742
  if (!cancelled) setStep("wallet-picker");
@@ -4756,12 +4804,26 @@ function SwypePaymentInner({
4756
4804
  handleAuthorizedMobileReturn,
4757
4805
  onComplete
4758
4806
  ]);
4759
- const loadingDataRef = react.useRef(false);
4760
4807
  react.useEffect(() => {
4761
- if (!authenticated) return;
4762
- if (step === "login" || step === "otp-verify") return;
4763
- if (accounts.length > 0 || loadingDataRef.current) return;
4764
- if (!activeCredentialId) return;
4808
+ const loadAction = resolveDataLoadAction({
4809
+ authenticated,
4810
+ step,
4811
+ accountsCount: accounts.length,
4812
+ hasActiveCredential: !!activeCredentialId,
4813
+ loading: loadingDataRef.current
4814
+ });
4815
+ if (loadAction === "reset") {
4816
+ resetDataLoadingState();
4817
+ return;
4818
+ }
4819
+ if (loadAction === "wait") {
4820
+ return;
4821
+ }
4822
+ const credentialId = activeCredentialId;
4823
+ if (!credentialId) {
4824
+ resetDataLoadingState();
4825
+ return;
4826
+ }
4765
4827
  let cancelled = false;
4766
4828
  loadingDataRef.current = true;
4767
4829
  const load = async () => {
@@ -4772,7 +4834,7 @@ function SwypePaymentInner({
4772
4834
  if (!token) throw new Error("Not authenticated");
4773
4835
  const [prov, accts, chn] = await Promise.all([
4774
4836
  fetchProviders(apiBaseUrl, token),
4775
- fetchAccounts(apiBaseUrl, token, activeCredentialId),
4837
+ fetchAccounts(apiBaseUrl, token, credentialId),
4776
4838
  fetchChains(apiBaseUrl, token)
4777
4839
  ]);
4778
4840
  if (cancelled) return;
@@ -4810,8 +4872,7 @@ function SwypePaymentInner({
4810
4872
  }
4811
4873
  } finally {
4812
4874
  if (!cancelled) {
4813
- setLoadingData(false);
4814
- loadingDataRef.current = false;
4875
+ resetDataLoadingState();
4815
4876
  }
4816
4877
  }
4817
4878
  };
@@ -4820,7 +4881,7 @@ function SwypePaymentInner({
4820
4881
  cancelled = true;
4821
4882
  loadingDataRef.current = false;
4822
4883
  };
4823
- }, [authenticated, step, accounts.length, apiBaseUrl, getAccessToken, activeCredentialId, depositAmount, connectingNewAccount]);
4884
+ }, [authenticated, step, accounts.length, apiBaseUrl, getAccessToken, activeCredentialId, depositAmount, connectingNewAccount, resetDataLoadingState]);
4824
4885
  react.useEffect(() => {
4825
4886
  if (!polling.transfer) return;
4826
4887
  if (polling.transfer.status === "COMPLETED") {
@@ -5185,6 +5246,7 @@ function SwypePaymentInner({
5185
5246
  processingStartedAtRef.current = null;
5186
5247
  pollingTransferIdRef.current = null;
5187
5248
  mobileSigningTransferIdRef.current = null;
5249
+ preSelectSourceStepRef.current = null;
5188
5250
  setConnectingNewAccount(false);
5189
5251
  setSelectedWalletId(null);
5190
5252
  if (accounts.length > 0) setSelectedAccountId(accounts[0].id);
@@ -5215,6 +5277,7 @@ function SwypePaymentInner({
5215
5277
  setAmount(depositAmount != null ? depositAmount.toString() : "");
5216
5278
  setMobileFlow(false);
5217
5279
  setDeeplinkUri(null);
5280
+ preSelectSourceStepRef.current = null;
5218
5281
  resetHeadlessLogin();
5219
5282
  }, [logout, polling, depositAmount, resetHeadlessLogin]);
5220
5283
  const handleConfirmSign = react.useCallback(async () => {
@@ -5225,12 +5288,13 @@ function SwypePaymentInner({
5225
5288
  setTransfer(signedTransfer);
5226
5289
  clearMobileFlowState();
5227
5290
  setStep("processing");
5291
+ polling.startPolling(t.id);
5228
5292
  } catch (err) {
5229
5293
  const msg = err instanceof Error ? err.message : "Failed to sign transfer";
5230
5294
  setError(msg);
5231
5295
  onError?.(msg);
5232
5296
  }
5233
- }, [transfer, polling.transfer, transferSigning, onError]);
5297
+ }, [transfer, polling.transfer, polling.startPolling, transferSigning, onError]);
5234
5298
  if (!ready) {
5235
5299
  return /* @__PURE__ */ jsxRuntime.jsx(ScreenLayout, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", alignItems: "center", justifyContent: "center" }, children: /* @__PURE__ */ jsxRuntime.jsx(Spinner, { label: "Initializing..." }) }) });
5236
5300
  }
@@ -5355,7 +5419,7 @@ function SwypePaymentInner({
5355
5419
  }
5356
5420
  if (step === "processing") {
5357
5421
  const polledStatus = polling.transfer?.status;
5358
- const transferPhase = creatingTransfer ? "creating" : mobileFlow || authExecutor.executing || transferSigning.signing ? "verifying" : polledStatus === "SENDING" || polledStatus === "SENT" || polling.isPolling ? "sent" : "creating";
5422
+ const transferPhase = creatingTransfer ? "creating" : polledStatus === "SENDING" || polledStatus === "SENT" ? "sent" : "verifying";
5359
5423
  return /* @__PURE__ */ jsxRuntime.jsx(
5360
5424
  TransferStatusScreen,
5361
5425
  {
@@ -5386,7 +5450,7 @@ function SwypePaymentInner({
5386
5450
  );
5387
5451
  }
5388
5452
  if (step === "success") {
5389
- transfer?.status === "COMPLETED";
5453
+ const succeeded = transfer?.status === "COMPLETED";
5390
5454
  const displayAmount = transfer?.amount?.amount ?? 0;
5391
5455
  const displayCurrency = transfer?.amount?.currency ?? "USD";
5392
5456
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -5394,12 +5458,14 @@ function SwypePaymentInner({
5394
5458
  {
5395
5459
  amount: displayAmount,
5396
5460
  currency: displayCurrency,
5461
+ succeeded,
5462
+ error,
5397
5463
  merchantName,
5398
5464
  sourceName,
5399
- remainingLimit: (() => {
5465
+ remainingLimit: succeeded ? (() => {
5400
5466
  const limit = selectedAccount?.remainingAllowance ?? oneTapLimit;
5401
5467
  return limit > displayAmount ? limit - displayAmount : 0;
5402
- })(),
5468
+ })() : void 0,
5403
5469
  onDone: onDismiss ?? handleNewPayment,
5404
5470
  onLogout: handleLogout,
5405
5471
  autoCloseSeconds