@swype-org/react-sdk 0.1.63 → 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 +284 -216
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +284 -216
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
}
|
|
975
|
-
|
|
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
|
|
3034
|
-
const
|
|
3035
|
-
const [
|
|
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
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
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
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
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(
|
|
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(
|
|
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,
|
|
@@ -4541,6 +4496,55 @@ function SwypePaymentInner({
|
|
|
4541
4496
|
setConnectingNewAccount(false);
|
|
4542
4497
|
}
|
|
4543
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]);
|
|
4544
4548
|
react.useEffect(() => {
|
|
4545
4549
|
if (depositAmount != null) {
|
|
4546
4550
|
setAmount(depositAmount.toString());
|
|
@@ -4626,19 +4630,70 @@ function SwypePaymentInner({
|
|
|
4626
4630
|
let cancelled = false;
|
|
4627
4631
|
setError(null);
|
|
4628
4632
|
resetHeadlessLogin();
|
|
4629
|
-
const restoreOrDeposit = () => {
|
|
4633
|
+
const restoreOrDeposit = async (credId, token) => {
|
|
4630
4634
|
const persisted = loadMobileFlowState();
|
|
4631
|
-
|
|
4632
|
-
|
|
4633
|
-
|
|
4634
|
-
|
|
4635
|
-
|
|
4636
|
-
|
|
4637
|
-
|
|
4638
|
-
|
|
4639
|
-
|
|
4640
|
-
|
|
4635
|
+
let accts = [];
|
|
4636
|
+
try {
|
|
4637
|
+
accts = await fetchAccounts(apiBaseUrl, token, credId);
|
|
4638
|
+
if (cancelled) return;
|
|
4639
|
+
} catch {
|
|
4640
|
+
}
|
|
4641
|
+
const resolved = resolvePostAuthStep({
|
|
4642
|
+
hasPasskey: true,
|
|
4643
|
+
accounts: accts,
|
|
4644
|
+
persistedMobileFlow: persisted,
|
|
4645
|
+
connectingNewAccount: false
|
|
4646
|
+
});
|
|
4647
|
+
if (resolved.clearPersistedFlow) {
|
|
4648
|
+
clearMobileFlowState();
|
|
4649
|
+
}
|
|
4650
|
+
if (resolved.step === "open-wallet" && persisted) {
|
|
4651
|
+
try {
|
|
4652
|
+
const existingTransfer = await fetchTransfer(apiBaseUrl, token, persisted.transferId);
|
|
4653
|
+
if (cancelled) return;
|
|
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") {
|
|
4667
|
+
clearMobileFlowState();
|
|
4668
|
+
setMobileFlow(false);
|
|
4669
|
+
setDeeplinkUri(null);
|
|
4670
|
+
setTransfer(existingTransfer);
|
|
4671
|
+
setError(null);
|
|
4672
|
+
setStep("success");
|
|
4673
|
+
onComplete?.(existingTransfer);
|
|
4674
|
+
return;
|
|
4675
|
+
}
|
|
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
|
+
);
|
|
4691
|
+
return;
|
|
4692
|
+
}
|
|
4693
|
+
enterPersistedMobileFlow(persisted);
|
|
4694
|
+
return;
|
|
4641
4695
|
}
|
|
4696
|
+
setStep(resolved.step);
|
|
4642
4697
|
};
|
|
4643
4698
|
const checkPasskey = async () => {
|
|
4644
4699
|
try {
|
|
@@ -4655,7 +4710,7 @@ function SwypePaymentInner({
|
|
|
4655
4710
|
return;
|
|
4656
4711
|
}
|
|
4657
4712
|
if (activeCredentialId && allPasskeys.some((p) => p.credentialId === activeCredentialId)) {
|
|
4658
|
-
restoreOrDeposit();
|
|
4713
|
+
await restoreOrDeposit(activeCredentialId, token);
|
|
4659
4714
|
return;
|
|
4660
4715
|
}
|
|
4661
4716
|
if (cancelled) return;
|
|
@@ -4665,7 +4720,7 @@ function SwypePaymentInner({
|
|
|
4665
4720
|
if (matched) {
|
|
4666
4721
|
setActiveCredentialId(matched);
|
|
4667
4722
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, matched);
|
|
4668
|
-
restoreOrDeposit();
|
|
4723
|
+
await restoreOrDeposit(matched, token);
|
|
4669
4724
|
return;
|
|
4670
4725
|
}
|
|
4671
4726
|
setStep("create-passkey");
|
|
@@ -4677,7 +4732,18 @@ function SwypePaymentInner({
|
|
|
4677
4732
|
return () => {
|
|
4678
4733
|
cancelled = true;
|
|
4679
4734
|
};
|
|
4680
|
-
}, [
|
|
4735
|
+
}, [
|
|
4736
|
+
ready,
|
|
4737
|
+
authenticated,
|
|
4738
|
+
step,
|
|
4739
|
+
apiBaseUrl,
|
|
4740
|
+
getAccessToken,
|
|
4741
|
+
activeCredentialId,
|
|
4742
|
+
resetHeadlessLogin,
|
|
4743
|
+
enterPersistedMobileFlow,
|
|
4744
|
+
handleAuthorizedMobileReturn,
|
|
4745
|
+
onComplete
|
|
4746
|
+
]);
|
|
4681
4747
|
const loadingDataRef = react.useRef(false);
|
|
4682
4748
|
react.useEffect(() => {
|
|
4683
4749
|
if (!authenticated) return;
|
|
@@ -4709,11 +4775,22 @@ function SwypePaymentInner({
|
|
|
4709
4775
|
} else if (prov.length > 0 && !connectingNewAccount) {
|
|
4710
4776
|
setSelectedProviderId(prov[0].id);
|
|
4711
4777
|
}
|
|
4712
|
-
const
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4778
|
+
const persisted = loadMobileFlowState();
|
|
4779
|
+
const resolved = resolvePostAuthStep({
|
|
4780
|
+
hasPasskey: !!activeCredentialId,
|
|
4781
|
+
accounts: accts,
|
|
4782
|
+
persistedMobileFlow: persisted,
|
|
4783
|
+
mobileSetupInProgress: mobileSetupFlowRef.current,
|
|
4784
|
+
connectingNewAccount
|
|
4785
|
+
});
|
|
4786
|
+
if (resolved.clearPersistedFlow) {
|
|
4787
|
+
clearMobileFlowState();
|
|
4788
|
+
setMobileFlow(false);
|
|
4789
|
+
setDeeplinkUri(null);
|
|
4790
|
+
}
|
|
4791
|
+
const correctableSteps = ["deposit", "wallet-picker", "open-wallet"];
|
|
4792
|
+
if (correctableSteps.includes(step)) {
|
|
4793
|
+
setStep(resolved.step);
|
|
4717
4794
|
}
|
|
4718
4795
|
} catch (err) {
|
|
4719
4796
|
if (!cancelled) {
|
|
@@ -4776,22 +4853,8 @@ function SwypePaymentInner({
|
|
|
4776
4853
|
if (!mobileFlow) return;
|
|
4777
4854
|
const polledTransfer = polling.transfer;
|
|
4778
4855
|
if (!polledTransfer || polledTransfer.status !== "AUTHORIZED") return;
|
|
4779
|
-
|
|
4780
|
-
|
|
4781
|
-
clearMobileFlowState();
|
|
4782
|
-
setDeeplinkUri(null);
|
|
4783
|
-
polling.stopPolling();
|
|
4784
|
-
setTransfer(polledTransfer);
|
|
4785
|
-
reloadAccounts().catch(() => {
|
|
4786
|
-
}).then(() => {
|
|
4787
|
-
setMobileFlow(false);
|
|
4788
|
-
setStep("deposit");
|
|
4789
|
-
});
|
|
4790
|
-
return;
|
|
4791
|
-
}
|
|
4792
|
-
setTransfer(polledTransfer);
|
|
4793
|
-
setStep("confirm-sign");
|
|
4794
|
-
}, [mobileFlow, polling.transfer, polling.stopPolling, transferSigning, onError, reloadAccounts]);
|
|
4856
|
+
void handleAuthorizedMobileReturn(polledTransfer, mobileSetupFlowRef.current);
|
|
4857
|
+
}, [mobileFlow, polling.transfer, handleAuthorizedMobileReturn]);
|
|
4795
4858
|
react.useEffect(() => {
|
|
4796
4859
|
if (!mobileFlow) return;
|
|
4797
4860
|
const transferIdToResume = pollingTransferIdRef.current ?? transfer?.id;
|
|
@@ -5024,15 +5087,18 @@ function SwypePaymentInner({
|
|
|
5024
5087
|
setActiveCredentialId(credentialId);
|
|
5025
5088
|
window.localStorage.setItem(ACTIVE_CREDENTIAL_STORAGE_KEY, credentialId);
|
|
5026
5089
|
setPasskeyPopupNeeded(false);
|
|
5027
|
-
const
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5090
|
+
const resolved = resolvePostAuthStep({
|
|
5091
|
+
hasPasskey: true,
|
|
5092
|
+
accounts,
|
|
5093
|
+
persistedMobileFlow: loadMobileFlowState(),
|
|
5094
|
+
mobileSetupInProgress: mobileSetupFlowRef.current,
|
|
5095
|
+
connectingNewAccount
|
|
5096
|
+
});
|
|
5097
|
+
if (resolved.clearPersistedFlow) {
|
|
5098
|
+
clearMobileFlowState();
|
|
5034
5099
|
}
|
|
5035
|
-
|
|
5100
|
+
setStep(resolved.step);
|
|
5101
|
+
}, [getAccessToken, apiBaseUrl, accounts, connectingNewAccount]);
|
|
5036
5102
|
const handleRegisterPasskey = react.useCallback(async () => {
|
|
5037
5103
|
setRegisteringPasskey(true);
|
|
5038
5104
|
setError(null);
|
|
@@ -5230,6 +5296,8 @@ function SwypePaymentInner({
|
|
|
5230
5296
|
walletName: providerName,
|
|
5231
5297
|
deeplinkUri: deeplinkUri ?? "",
|
|
5232
5298
|
loading: creatingTransfer || !deeplinkUri,
|
|
5299
|
+
error: error || polling.error,
|
|
5300
|
+
onRetryStatus: handleRetryMobileStatus,
|
|
5233
5301
|
onLogout: handleLogout
|
|
5234
5302
|
}
|
|
5235
5303
|
);
|