@swype-org/react-sdk 0.1.64 → 0.1.67

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
@@ -657,6 +657,24 @@ function normalizeSignature(sig) {
657
657
  );
658
658
  }
659
659
 
660
+ // src/transferPolling.ts
661
+ async function pollTransferTick(params) {
662
+ const fetchTransfer2 = params.fetchTransfer ?? fetchTransfer;
663
+ const token = await params.getAccessToken();
664
+ if (!token) {
665
+ return { kind: "retry" };
666
+ }
667
+ try {
668
+ const transfer = await fetchTransfer2(params.apiBaseUrl, token, params.transferId);
669
+ return { kind: "success", transfer };
670
+ } catch (err) {
671
+ return {
672
+ kind: "error",
673
+ message: err instanceof Error ? err.message : "Polling error"
674
+ };
675
+ }
676
+ }
677
+
660
678
  // src/passkey-delegation.ts
661
679
  var PasskeyIframeBlockedError = class extends Error {
662
680
  constructor(message = "Passkey creation is not supported in this browser context.") {
@@ -959,20 +977,22 @@ function useTransferPolling(intervalMs = 3e3) {
959
977
  }, []);
960
978
  const poll = react.useCallback(async () => {
961
979
  if (!transferIdRef.current) return;
962
- try {
963
- const token = await getAccessToken();
964
- if (!token) {
965
- setError("Could not get access token");
966
- stopPolling();
967
- return;
968
- }
969
- const t = await fetchTransfer(apiBaseUrl, token, transferIdRef.current);
970
- setTransfer(t);
971
- if (t.status === "COMPLETED" || t.status === "FAILED") {
972
- stopPolling();
973
- }
974
- } catch (err) {
975
- setError(err instanceof Error ? err.message : "Polling error");
980
+ const result = await pollTransferTick({
981
+ apiBaseUrl,
982
+ transferId: transferIdRef.current,
983
+ getAccessToken
984
+ });
985
+ if (result.kind === "retry") {
986
+ return;
987
+ }
988
+ if (result.kind === "error") {
989
+ setError(result.message);
990
+ stopPolling();
991
+ return;
992
+ }
993
+ setError(null);
994
+ setTransfer(result.transfer);
995
+ if (result.transfer.status === "COMPLETED" || result.transfer.status === "FAILED") {
976
996
  stopPolling();
977
997
  }
978
998
  }, [apiBaseUrl, getAccessToken, stopPolling]);
@@ -1583,6 +1603,35 @@ function isMobileUserAgent(userAgent) {
1583
1603
  function shouldUseWalletConnector(options) {
1584
1604
  return options.useWalletConnector ?? !isMobileUserAgent(options.userAgent);
1585
1605
  }
1606
+
1607
+ // src/mobileFlow.ts
1608
+ function hasActiveWallet(accounts) {
1609
+ return accounts.some((account) => account.wallets.some((wallet) => wallet.status === "ACTIVE"));
1610
+ }
1611
+ function resolvePostAuthStep(state) {
1612
+ if (!state.hasPasskey) {
1613
+ return { step: "create-passkey", clearPersistedFlow: false };
1614
+ }
1615
+ if (state.persistedMobileFlow) {
1616
+ return { step: "open-wallet", clearPersistedFlow: false };
1617
+ }
1618
+ if ((state.accounts.length === 0 || !hasActiveWallet(state.accounts)) && !state.connectingNewAccount) {
1619
+ return { step: "wallet-picker", clearPersistedFlow: false };
1620
+ }
1621
+ return { step: "deposit", clearPersistedFlow: false };
1622
+ }
1623
+ function resolveRestoredMobileFlow(transferStatus, isSetup) {
1624
+ if (transferStatus === "AUTHORIZED") {
1625
+ return isSetup ? { kind: "resume-setup-deposit", step: "deposit", clearPersistedFlow: true } : { kind: "resume-confirm-sign", step: "confirm-sign", clearPersistedFlow: true };
1626
+ }
1627
+ if (transferStatus === "COMPLETED") {
1628
+ return { kind: "resume-success", step: "success", clearPersistedFlow: true };
1629
+ }
1630
+ if (transferStatus === "FAILED") {
1631
+ return { kind: "resume-failed", step: "success", clearPersistedFlow: true };
1632
+ }
1633
+ return { kind: "resume-open-wallet", step: "open-wallet", clearPersistedFlow: false };
1634
+ }
1586
1635
  var FOOTER_CSS = `
1587
1636
  .swype-screen-footer {
1588
1637
  padding-bottom: max(24px, env(safe-area-inset-bottom, 24px));
@@ -1911,137 +1960,6 @@ var inputStyle = (tokens, filled) => ({
1911
1960
  caretColor: tokens.borderFocus,
1912
1961
  transition: "border-color 0.15s ease"
1913
1962
  });
1914
- function LimitSlider({
1915
- value,
1916
- min,
1917
- max,
1918
- step = 1,
1919
- onChange,
1920
- ticks,
1921
- disabled
1922
- }) {
1923
- const { tokens } = useSwypeConfig();
1924
- const pct = (value - min) / (max - min) * 100;
1925
- const handleChange = react.useCallback(
1926
- (e) => onChange(Number(e.target.value)),
1927
- [onChange]
1928
- );
1929
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "swype-slider-wrap", style: wrapperStyle, children: [
1930
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: trackContainerStyle, children: [
1931
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: trackBgStyle(tokens.border) }),
1932
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: trackFillStyle(tokens.accent, pct) }),
1933
- /* @__PURE__ */ jsxRuntime.jsx(
1934
- "input",
1935
- {
1936
- type: "range",
1937
- min,
1938
- max,
1939
- step,
1940
- value,
1941
- onChange: handleChange,
1942
- disabled,
1943
- style: rangeInputStyle
1944
- }
1945
- )
1946
- ] }),
1947
- ticks && ticks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: ticksStyle, children: ticks.map((tick, i) => {
1948
- const pctPos = (tick - min) / (max - min) * 100;
1949
- const isFirst = i === 0;
1950
- const isLast = i === ticks.length - 1;
1951
- const label = tick % 1 === 0 ? `$${tick}` : `$${tick.toFixed(2)}`;
1952
- return /* @__PURE__ */ jsxRuntime.jsx(
1953
- "span",
1954
- {
1955
- style: tickLabelStyle(tokens.textMuted, pctPos, isFirst, isLast),
1956
- children: label
1957
- },
1958
- tick
1959
- );
1960
- }) }),
1961
- /* @__PURE__ */ jsxRuntime.jsx("style", { children: sliderThumbCss(tokens.accent) })
1962
- ] });
1963
- }
1964
- var wrapperStyle = { width: "100%" };
1965
- var trackContainerStyle = {
1966
- position: "relative",
1967
- height: 28,
1968
- display: "flex",
1969
- alignItems: "center"
1970
- };
1971
- var trackBgStyle = (color) => ({
1972
- position: "absolute",
1973
- left: 0,
1974
- right: 0,
1975
- height: 4,
1976
- borderRadius: 2,
1977
- background: color
1978
- });
1979
- var trackFillStyle = (color, pct) => ({
1980
- position: "absolute",
1981
- left: 0,
1982
- width: `${pct}%`,
1983
- height: 4,
1984
- borderRadius: 2,
1985
- background: color
1986
- });
1987
- var rangeInputStyle = {
1988
- position: "absolute",
1989
- left: 0,
1990
- right: 0,
1991
- width: "100%",
1992
- height: 28,
1993
- margin: 0,
1994
- cursor: "pointer",
1995
- zIndex: 2,
1996
- WebkitAppearance: "none",
1997
- appearance: "none",
1998
- background: "transparent"
1999
- };
2000
- var ticksStyle = {
2001
- position: "relative",
2002
- height: 18,
2003
- marginTop: 6
2004
- };
2005
- var tickLabelStyle = (color, pct, isFirst, isLast) => ({
2006
- position: "absolute",
2007
- left: `${pct}%`,
2008
- transform: isFirst ? "none" : isLast ? "translateX(-100%)" : "translateX(-50%)",
2009
- fontSize: "0.72rem",
2010
- fontWeight: 500,
2011
- color,
2012
- whiteSpace: "nowrap"
2013
- });
2014
- var sliderThumbCss = (accent) => `
2015
- .swype-slider-wrap input[type="range"]::-webkit-slider-runnable-track {
2016
- height: 4px;
2017
- background: transparent;
2018
- }
2019
- .swype-slider-wrap input[type="range"]::-webkit-slider-thumb {
2020
- -webkit-appearance: none;
2021
- width: 20px;
2022
- height: 20px;
2023
- border-radius: 50%;
2024
- background: ${accent};
2025
- border: 3px solid #fff;
2026
- box-shadow: 0 2px 6px rgba(0,0,0,0.15);
2027
- cursor: pointer;
2028
- margin-top: -8px;
2029
- }
2030
- .swype-slider-wrap input[type="range"]::-moz-range-track {
2031
- height: 4px;
2032
- background: transparent;
2033
- border: none;
2034
- }
2035
- .swype-slider-wrap input[type="range"]::-moz-range-thumb {
2036
- width: 14px;
2037
- height: 14px;
2038
- border-radius: 50%;
2039
- background: ${accent};
2040
- border: 3px solid #fff;
2041
- box-shadow: 0 2px 6px rgba(0,0,0,0.15);
2042
- cursor: pointer;
2043
- }
2044
- `;
2045
1963
 
2046
1964
  // src/assets/logos.ts
2047
1965
  function svgToDataUri(svg) {
@@ -3003,18 +2921,6 @@ var dividerTextStyle = (color) => ({
3003
2921
  });
3004
2922
  var DEFAULT_MAX = 500;
3005
2923
  var ABSOLUTE_MIN = 1;
3006
- function buildTicks(min, max) {
3007
- if (max <= min) return [min];
3008
- const range = max - min;
3009
- const candidates = [1, 2, 5, 10, 25, 50, 100, 250];
3010
- const step = candidates.find((s) => range / s <= 5) ?? Math.ceil(range / 4);
3011
- const ticks = [];
3012
- for (let v = min; v <= max; v += step) {
3013
- ticks.push(Math.round(v * 100) / 100);
3014
- }
3015
- if (ticks[ticks.length - 1] !== max) ticks.push(max);
3016
- return ticks;
3017
- }
3018
2924
  function SetupScreen({
3019
2925
  availableBalance,
3020
2926
  tokenCount,
@@ -3030,9 +2936,22 @@ function SetupScreen({
3030
2936
  const { tokens } = useSwypeConfig();
3031
2937
  const effectiveMax = Math.floor(Math.min(DEFAULT_MAX, availableBalance > 0 ? availableBalance : DEFAULT_MAX) * 100) / 100;
3032
2938
  const effectiveMin = Math.min(ABSOLUTE_MIN, effectiveMax);
3033
- const sliderStep = effectiveMax <= 10 ? 0.5 : effectiveMax <= 50 ? 1 : 5;
3034
- const ticks = buildTicks(effectiveMin, effectiveMax);
3035
- const [limit, setLimit] = react.useState(() => Math.min(100, effectiveMax));
2939
+ const [limit, setLimit] = react.useState(() => effectiveMax);
2940
+ const [editing, setEditing] = react.useState(false);
2941
+ const [inputValue, setInputValue] = react.useState("");
2942
+ const inputRef = react.useRef(null);
2943
+ const startEditing = react.useCallback(() => {
2944
+ setInputValue(limit.toFixed(2));
2945
+ setEditing(true);
2946
+ requestAnimationFrame(() => inputRef.current?.select());
2947
+ }, [limit]);
2948
+ const commitEdit = react.useCallback(() => {
2949
+ const parsed = parseFloat(inputValue);
2950
+ if (!isNaN(parsed)) {
2951
+ setLimit(Math.min(effectiveMax, Math.max(effectiveMin, Math.round(parsed * 100) / 100)));
2952
+ }
2953
+ setEditing(false);
2954
+ }, [inputValue, effectiveMax, effectiveMin]);
3036
2955
  return /* @__PURE__ */ jsxRuntime.jsxs(
3037
2956
  ScreenLayout,
3038
2957
  {
@@ -3088,19 +3007,33 @@ function SetupScreen({
3088
3007
  ] }),
3089
3008
  /* @__PURE__ */ jsxRuntime.jsxs("div", { style: limitSectionStyle, children: [
3090
3009
  /* @__PURE__ */ jsxRuntime.jsx("div", { style: limitLabelStyle(tokens.textMuted), children: "Your One-Tap limit" }),
3091
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: limitValueStyle(tokens.text), children: [
3010
+ editing ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: limitValueStyle(tokens.text), children: [
3092
3011
  "$",
3093
- limit.toFixed(2)
3094
- ] }),
3095
- /* @__PURE__ */ jsxRuntime.jsx(
3096
- LimitSlider,
3012
+ /* @__PURE__ */ jsxRuntime.jsx(
3013
+ "input",
3014
+ {
3015
+ ref: inputRef,
3016
+ type: "text",
3017
+ inputMode: "decimal",
3018
+ pattern: "[0-9]*",
3019
+ value: inputValue,
3020
+ onChange: (e) => setInputValue(e.target.value),
3021
+ onBlur: commitEdit,
3022
+ onKeyDown: (e) => {
3023
+ if (e.key === "Enter") commitEdit();
3024
+ },
3025
+ style: limitInputStyle(tokens.text)
3026
+ }
3027
+ )
3028
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(
3029
+ "div",
3097
3030
  {
3098
- value: limit,
3099
- min: effectiveMin,
3100
- max: effectiveMax,
3101
- step: sliderStep,
3102
- ticks,
3103
- onChange: setLimit
3031
+ style: { ...limitValueStyle(tokens.text), cursor: "pointer" },
3032
+ onClick: startEditing,
3033
+ children: [
3034
+ "$",
3035
+ limit.toFixed(2)
3036
+ ]
3104
3037
  }
3105
3038
  )
3106
3039
  ] }),
@@ -3207,6 +3140,19 @@ var limitValueStyle = (color) => ({
3207
3140
  color,
3208
3141
  marginBottom: 12
3209
3142
  });
3143
+ var limitInputStyle = (color) => ({
3144
+ fontSize: "2.2rem",
3145
+ fontWeight: 700,
3146
+ color,
3147
+ background: "transparent",
3148
+ border: "none",
3149
+ borderBottom: "2px solid currentColor",
3150
+ outline: "none",
3151
+ textAlign: "center",
3152
+ width: "5ch",
3153
+ fontFamily: "inherit",
3154
+ padding: 0
3155
+ });
3210
3156
  var bannerWrapStyle = { marginBottom: 16 };
3211
3157
  var linkStyle = (color) => ({
3212
3158
  background: "transparent",
@@ -4112,6 +4058,8 @@ function OpenWalletScreen({
4112
4058
  walletName,
4113
4059
  deeplinkUri,
4114
4060
  loading,
4061
+ error,
4062
+ onRetryStatus,
4115
4063
  onLogout
4116
4064
  }) {
4117
4065
  const { tokens } = useSwypeConfig();
@@ -4132,12 +4080,14 @@ function OpenWalletScreen({
4132
4080
  return /* @__PURE__ */ jsxRuntime.jsxs(
4133
4081
  ScreenLayout,
4134
4082
  {
4135
- footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
4083
+ footer: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: footerContentStyle, children: [
4084
+ error && /* @__PURE__ */ jsxRuntime.jsx(InfoBanner, { children: error }),
4136
4085
  !loading && /* @__PURE__ */ jsxRuntime.jsxs(PrimaryButton, { onClick: handleOpen, children: [
4137
4086
  "Open ",
4138
4087
  displayName
4139
4088
  ] }),
4140
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: hintStyle3(tokens.textMuted), children: loading ? "Preparing authorization..." : "If your wallet didn't open automatically, tap the button above" })
4089
+ error && onRetryStatus && /* @__PURE__ */ jsxRuntime.jsx(OutlineButton, { onClick: onRetryStatus, children: "Retry status check" }),
4090
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: hintStyle3(tokens.textMuted), children: loading ? "Preparing authorization..." : error ? "Retry the status check after returning to the browser, or reopen your wallet if needed." : "If your wallet didn't open automatically, tap the button above" })
4141
4091
  ] }),
4142
4092
  children: [
4143
4093
  /* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
@@ -4163,6 +4113,11 @@ var contentStyle6 = {
4163
4113
  textAlign: "center",
4164
4114
  padding: "0 24px"
4165
4115
  };
4116
+ var footerContentStyle = {
4117
+ display: "flex",
4118
+ flexDirection: "column",
4119
+ gap: 12
4120
+ };
4166
4121
  var logoStyle = {
4167
4122
  width: 56,
4168
4123
  height: 56,
@@ -4452,24 +4407,6 @@ function buildSelectSourceChoices(options) {
4452
4407
  }
4453
4408
  return chainChoices;
4454
4409
  }
4455
- function resolvePostAuthStep(state) {
4456
- if (!state.hasPasskey) {
4457
- return { step: "create-passkey", clearPersistedFlow: false };
4458
- }
4459
- const hasActiveWallet = state.accounts.some(
4460
- (a) => a.wallets.some((w) => w.status === "ACTIVE")
4461
- );
4462
- if (state.persistedMobileFlow) {
4463
- if (hasActiveWallet && !state.mobileSetupInProgress) {
4464
- return { step: "deposit", clearPersistedFlow: true };
4465
- }
4466
- return { step: "open-wallet", clearPersistedFlow: false };
4467
- }
4468
- if ((state.accounts.length === 0 || !hasActiveWallet) && !state.connectingNewAccount) {
4469
- return { step: "wallet-picker", clearPersistedFlow: false };
4470
- }
4471
- return { step: "deposit", clearPersistedFlow: false };
4472
- }
4473
4410
  function SwypePayment(props) {
4474
4411
  const resetKey = react.useRef(0);
4475
4412
  const handleBoundaryReset = react.useCallback(() => {
@@ -4559,6 +4496,55 @@ function SwypePaymentInner({
4559
4496
  setConnectingNewAccount(false);
4560
4497
  }
4561
4498
  }, [getAccessToken, activeCredentialId, apiBaseUrl, depositAmount]);
4499
+ const enterPersistedMobileFlow = react.useCallback((persisted, errorMessage) => {
4500
+ setMobileFlow(true);
4501
+ setDeeplinkUri(persisted.deeplinkUri);
4502
+ setSelectedProviderId(persisted.providerId);
4503
+ pollingTransferIdRef.current = persisted.transferId;
4504
+ mobileSetupFlowRef.current = persisted.isSetup;
4505
+ setError(errorMessage ?? null);
4506
+ setStep("open-wallet");
4507
+ polling.startPolling(persisted.transferId);
4508
+ }, [polling]);
4509
+ const handleAuthorizedMobileReturn = react.useCallback(async (authorizedTransfer, isSetup) => {
4510
+ setTransfer(authorizedTransfer);
4511
+ polling.stopPolling();
4512
+ if (isSetup) {
4513
+ mobileSetupFlowRef.current = false;
4514
+ clearMobileFlowState();
4515
+ try {
4516
+ await reloadAccounts();
4517
+ setError(null);
4518
+ setDeeplinkUri(null);
4519
+ setMobileFlow(false);
4520
+ setStep("deposit");
4521
+ } catch (err) {
4522
+ setError(
4523
+ err instanceof Error ? err.message : "Wallet authorized, but we could not refresh your account yet."
4524
+ );
4525
+ setStep("open-wallet");
4526
+ }
4527
+ return;
4528
+ }
4529
+ mobileSetupFlowRef.current = false;
4530
+ clearMobileFlowState();
4531
+ setError(null);
4532
+ setDeeplinkUri(null);
4533
+ setMobileFlow(false);
4534
+ setStep("confirm-sign");
4535
+ }, [polling.stopPolling, reloadAccounts]);
4536
+ const handleRetryMobileStatus = react.useCallback(() => {
4537
+ setError(null);
4538
+ const currentTransfer = polling.transfer ?? transfer;
4539
+ if (currentTransfer?.status === "AUTHORIZED") {
4540
+ void handleAuthorizedMobileReturn(currentTransfer, mobileSetupFlowRef.current);
4541
+ return;
4542
+ }
4543
+ const transferIdToResume = pollingTransferIdRef.current ?? currentTransfer?.id;
4544
+ if (transferIdToResume) {
4545
+ polling.startPolling(transferIdToResume);
4546
+ }
4547
+ }, [handleAuthorizedMobileReturn, polling, transfer]);
4562
4548
  react.useEffect(() => {
4563
4549
  if (depositAmount != null) {
4564
4550
  setAmount(depositAmount.toString());
@@ -4656,7 +4642,6 @@ function SwypePaymentInner({
4656
4642
  hasPasskey: true,
4657
4643
  accounts: accts,
4658
4644
  persistedMobileFlow: persisted,
4659
- mobileSetupInProgress: false,
4660
4645
  connectingNewAccount: false
4661
4646
  });
4662
4647
  if (resolved.clearPersistedFlow) {
@@ -4666,23 +4651,47 @@ function SwypePaymentInner({
4666
4651
  try {
4667
4652
  const existingTransfer = await fetchTransfer(apiBaseUrl, token, persisted.transferId);
4668
4653
  if (cancelled) return;
4669
- const terminalStatuses = ["AUTHORIZED", "COMPLETED", "FAILED"];
4670
- if (terminalStatuses.includes(existingTransfer.status)) {
4654
+ const mobileResolution = resolveRestoredMobileFlow(
4655
+ existingTransfer.status,
4656
+ persisted.isSetup
4657
+ );
4658
+ if (mobileResolution.kind === "resume-setup-deposit") {
4659
+ await handleAuthorizedMobileReturn(existingTransfer, true);
4660
+ return;
4661
+ }
4662
+ if (mobileResolution.kind === "resume-confirm-sign") {
4663
+ await handleAuthorizedMobileReturn(existingTransfer, false);
4664
+ return;
4665
+ }
4666
+ if (mobileResolution.kind === "resume-success") {
4671
4667
  clearMobileFlowState();
4672
- setStep("deposit");
4668
+ setMobileFlow(false);
4669
+ setDeeplinkUri(null);
4670
+ setTransfer(existingTransfer);
4671
+ setError(null);
4672
+ setStep("success");
4673
+ onComplete?.(existingTransfer);
4673
4674
  return;
4674
4675
  }
4675
- } catch {
4676
- clearMobileFlowState();
4677
- setStep("deposit");
4676
+ if (mobileResolution.kind === "resume-failed") {
4677
+ clearMobileFlowState();
4678
+ setMobileFlow(false);
4679
+ setDeeplinkUri(null);
4680
+ setTransfer(existingTransfer);
4681
+ setError("Transfer failed.");
4682
+ setStep("success");
4683
+ return;
4684
+ }
4685
+ } catch (err) {
4686
+ if (cancelled) return;
4687
+ enterPersistedMobileFlow(
4688
+ persisted,
4689
+ err instanceof Error ? err.message : "Unable to refresh wallet authorization status."
4690
+ );
4678
4691
  return;
4679
4692
  }
4680
- setMobileFlow(true);
4681
- setDeeplinkUri(persisted.deeplinkUri);
4682
- setSelectedProviderId(persisted.providerId);
4683
- pollingTransferIdRef.current = persisted.transferId;
4684
- mobileSetupFlowRef.current = persisted.isSetup;
4685
- polling.startPolling(persisted.transferId);
4693
+ enterPersistedMobileFlow(persisted);
4694
+ return;
4686
4695
  }
4687
4696
  setStep(resolved.step);
4688
4697
  };
@@ -4723,7 +4732,18 @@ function SwypePaymentInner({
4723
4732
  return () => {
4724
4733
  cancelled = true;
4725
4734
  };
4726
- }, [ready, authenticated, step, apiBaseUrl, getAccessToken, activeCredentialId, resetHeadlessLogin]);
4735
+ }, [
4736
+ ready,
4737
+ authenticated,
4738
+ step,
4739
+ apiBaseUrl,
4740
+ getAccessToken,
4741
+ activeCredentialId,
4742
+ resetHeadlessLogin,
4743
+ enterPersistedMobileFlow,
4744
+ handleAuthorizedMobileReturn,
4745
+ onComplete
4746
+ ]);
4727
4747
  const loadingDataRef = react.useRef(false);
4728
4748
  react.useEffect(() => {
4729
4749
  if (!authenticated) return;
@@ -4833,22 +4853,8 @@ function SwypePaymentInner({
4833
4853
  if (!mobileFlow) return;
4834
4854
  const polledTransfer = polling.transfer;
4835
4855
  if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
4836
- if (mobileSetupFlowRef.current) {
4837
- mobileSetupFlowRef.current = false;
4838
- clearMobileFlowState();
4839
- setDeeplinkUri(null);
4840
- polling.stopPolling();
4841
- setTransfer(polledTransfer);
4842
- reloadAccounts().catch(() => {
4843
- }).then(() => {
4844
- setMobileFlow(false);
4845
- setStep("deposit");
4846
- });
4847
- return;
4848
- }
4849
- setTransfer(polledTransfer);
4850
- setStep("confirm-sign");
4851
- }, [mobileFlow, polling.transfer, polling.stopPolling, transferSigning, onError, reloadAccounts]);
4856
+ void handleAuthorizedMobileReturn(polledTransfer, mobileSetupFlowRef.current);
4857
+ }, [mobileFlow, polling.transfer, handleAuthorizedMobileReturn]);
4852
4858
  react.useEffect(() => {
4853
4859
  if (!mobileFlow) return;
4854
4860
  const transferIdToResume = pollingTransferIdRef.current ?? transfer?.id;
@@ -5290,6 +5296,8 @@ function SwypePaymentInner({
5290
5296
  walletName: providerName,
5291
5297
  deeplinkUri: deeplinkUri ?? "",
5292
5298
  loading: creatingTransfer || !deeplinkUri,
5299
+ error: error || polling.error,
5300
+ onRetryStatus: handleRetryMobileStatus,
5293
5301
  onLogout: handleLogout
5294
5302
  }
5295
5303
  );