@swype-org/react-sdk 0.1.32 → 0.1.34
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 +473 -75
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +474 -76
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -1451,8 +1451,20 @@ function hasProcessingTimedOut(processingStartedAtMs, nowMs) {
|
|
|
1451
1451
|
if (!processingStartedAtMs) return false;
|
|
1452
1452
|
return nowMs - processingStartedAtMs >= PROCESSING_TIMEOUT_MS;
|
|
1453
1453
|
}
|
|
1454
|
+
var STATUS_DISPLAY_LABELS = {
|
|
1455
|
+
CREATED: "created",
|
|
1456
|
+
AUTHORIZED: "authorized",
|
|
1457
|
+
SENDING: "sending",
|
|
1458
|
+
SENT: "confirming delivery",
|
|
1459
|
+
COMPLETED: "completed",
|
|
1460
|
+
FAILED: "failed"
|
|
1461
|
+
};
|
|
1462
|
+
function getStatusDisplayLabel(status) {
|
|
1463
|
+
return STATUS_DISPLAY_LABELS[status] ?? status;
|
|
1464
|
+
}
|
|
1454
1465
|
function buildProcessingTimeoutMessage(status) {
|
|
1455
|
-
|
|
1466
|
+
const label = getStatusDisplayLabel(status);
|
|
1467
|
+
return `Payment is taking longer than expected (status: ${label}). Please try again.`;
|
|
1456
1468
|
}
|
|
1457
1469
|
|
|
1458
1470
|
// src/walletFlow.ts
|
|
@@ -1715,7 +1727,7 @@ var circleStyle = (bg, size) => ({
|
|
|
1715
1727
|
function OtpInput({ value, onChange, length = 6, disabled }) {
|
|
1716
1728
|
const { tokens } = useSwypeConfig();
|
|
1717
1729
|
const inputsRef = react.useRef([]);
|
|
1718
|
-
const digits = value.padEnd(length
|
|
1730
|
+
const digits = value.padEnd(length).split("").slice(0, length);
|
|
1719
1731
|
const focusInput = react.useCallback((index) => {
|
|
1720
1732
|
const clamped = Math.max(0, Math.min(index, length - 1));
|
|
1721
1733
|
inputsRef.current[clamped]?.focus();
|
|
@@ -2118,34 +2130,50 @@ function LoginScreen({
|
|
|
2118
2130
|
onSubmit,
|
|
2119
2131
|
sending,
|
|
2120
2132
|
error,
|
|
2121
|
-
onBack
|
|
2133
|
+
onBack,
|
|
2134
|
+
merchantInitials,
|
|
2135
|
+
onSocialLogin
|
|
2122
2136
|
}) {
|
|
2123
2137
|
const { tokens } = useSwypeConfig();
|
|
2124
2138
|
const disabled = authInput.trim().length === 0 || sending;
|
|
2125
|
-
const walletLogos = ["MM", "R", "O", "P", "+"];
|
|
2126
2139
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2127
2140
|
ScreenLayout,
|
|
2128
2141
|
{
|
|
2129
2142
|
footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2130
2143
|
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onSubmit, disabled, loading: sending, children: "Continue" }),
|
|
2131
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2132
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2133
|
-
|
|
2134
|
-
|
|
2144
|
+
onSocialLogin && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2145
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: dividerStyle(tokens), children: [
|
|
2146
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: dividerLineStyle(tokens.border) }),
|
|
2147
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "or continue with" }),
|
|
2148
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: dividerLineStyle(tokens.border) })
|
|
2149
|
+
] }),
|
|
2150
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: socialRowStyle, children: ["google", "apple", "twitter"].map((provider) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
2151
|
+
"button",
|
|
2152
|
+
{
|
|
2153
|
+
type: "button",
|
|
2154
|
+
onClick: () => onSocialLogin(provider),
|
|
2155
|
+
style: socialButtonStyle(tokens),
|
|
2156
|
+
children: socialLabel(provider)
|
|
2157
|
+
},
|
|
2158
|
+
provider
|
|
2159
|
+
)) })
|
|
2160
|
+
] }),
|
|
2161
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: walletSectionStyle, children: [
|
|
2162
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: walletLabelStyle(tokens.textMuted), children: "Works with" }),
|
|
2163
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: walletLogosStyle, children: walletIcons.map(({ key, emoji }) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: walletEmojiStyle, children: emoji }, key)) })
|
|
2135
2164
|
] }),
|
|
2136
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: logosRowStyle, children: walletLogos.map((label) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: logoCircleStyle(tokens), children: label }, label)) }),
|
|
2137
2165
|
/* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
|
|
2138
2166
|
] }),
|
|
2139
2167
|
children: [
|
|
2140
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2168
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2169
|
+
ScreenHeader,
|
|
2170
|
+
{
|
|
2171
|
+
onBack,
|
|
2172
|
+
right: merchantInitials ? /* @__PURE__ */ jsxRuntime.jsx("div", { style: avatarStyle(tokens), children: merchantInitials }) : void 0
|
|
2173
|
+
}
|
|
2174
|
+
),
|
|
2141
2175
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle, children: [
|
|
2142
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2143
|
-
"path",
|
|
2144
|
-
{
|
|
2145
|
-
d: "M12 1L3 5v6c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V5l-9-4z",
|
|
2146
|
-
fill: tokens.accent
|
|
2147
|
-
}
|
|
2148
|
-
) }) }),
|
|
2176
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: iconBoxStyle(tokens.accent), children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: iconLetterStyle, children: "S" }) }),
|
|
2149
2177
|
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle(tokens.text), children: "One-time setup.\nOne-tap deposits after." }),
|
|
2150
2178
|
/* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle(tokens.textSecondary), children: "Protected by Face ID." }),
|
|
2151
2179
|
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorStyle(tokens), children: error }),
|
|
@@ -2170,6 +2198,23 @@ function LoginScreen({
|
|
|
2170
2198
|
}
|
|
2171
2199
|
);
|
|
2172
2200
|
}
|
|
2201
|
+
var walletIcons = [
|
|
2202
|
+
{ key: "metamask", emoji: "\u{1F98A}" },
|
|
2203
|
+
{ key: "rabby", emoji: "\u{1F430}" },
|
|
2204
|
+
{ key: "phantom", emoji: "\u25C6" },
|
|
2205
|
+
{ key: "rainbow", emoji: "\u{1F439}" },
|
|
2206
|
+
{ key: "coinbase", emoji: "\u{1F535}" }
|
|
2207
|
+
];
|
|
2208
|
+
function socialLabel(provider) {
|
|
2209
|
+
switch (provider) {
|
|
2210
|
+
case "google":
|
|
2211
|
+
return "Google";
|
|
2212
|
+
case "apple":
|
|
2213
|
+
return "Apple";
|
|
2214
|
+
case "twitter":
|
|
2215
|
+
return "X";
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2173
2218
|
var contentStyle = {
|
|
2174
2219
|
textAlign: "center",
|
|
2175
2220
|
flex: 1,
|
|
@@ -2178,10 +2223,25 @@ var contentStyle = {
|
|
|
2178
2223
|
alignItems: "center",
|
|
2179
2224
|
paddingTop: 24
|
|
2180
2225
|
};
|
|
2226
|
+
var iconBoxStyle = (accent) => ({
|
|
2227
|
+
width: 56,
|
|
2228
|
+
height: 56,
|
|
2229
|
+
borderRadius: 16,
|
|
2230
|
+
background: accent,
|
|
2231
|
+
display: "flex",
|
|
2232
|
+
alignItems: "center",
|
|
2233
|
+
justifyContent: "center"
|
|
2234
|
+
});
|
|
2235
|
+
var iconLetterStyle = {
|
|
2236
|
+
color: "#fff",
|
|
2237
|
+
fontSize: "1.5rem",
|
|
2238
|
+
fontWeight: 700,
|
|
2239
|
+
lineHeight: 1
|
|
2240
|
+
};
|
|
2181
2241
|
var headingStyle = (color) => ({
|
|
2182
|
-
fontSize: "1.
|
|
2242
|
+
fontSize: "1.45rem",
|
|
2183
2243
|
fontWeight: 700,
|
|
2184
|
-
lineHeight: 1.
|
|
2244
|
+
lineHeight: 1.25,
|
|
2185
2245
|
letterSpacing: "-0.02em",
|
|
2186
2246
|
color,
|
|
2187
2247
|
margin: "20px 0 8px",
|
|
@@ -2230,24 +2290,55 @@ var dividerLineStyle = (color) => ({
|
|
|
2230
2290
|
height: 1,
|
|
2231
2291
|
background: color
|
|
2232
2292
|
});
|
|
2233
|
-
var
|
|
2293
|
+
var socialRowStyle = {
|
|
2294
|
+
display: "flex",
|
|
2295
|
+
gap: 10,
|
|
2296
|
+
marginBottom: 20
|
|
2297
|
+
};
|
|
2298
|
+
var socialButtonStyle = (tokens) => ({
|
|
2299
|
+
flex: 1,
|
|
2300
|
+
padding: "12px 0",
|
|
2301
|
+
borderRadius: 12,
|
|
2302
|
+
border: `1px solid ${tokens.border}`,
|
|
2303
|
+
background: "transparent",
|
|
2304
|
+
color: tokens.text,
|
|
2305
|
+
fontSize: "0.88rem",
|
|
2306
|
+
fontWeight: 600,
|
|
2307
|
+
fontFamily: "inherit",
|
|
2308
|
+
cursor: "pointer"
|
|
2309
|
+
});
|
|
2310
|
+
var walletSectionStyle = {
|
|
2311
|
+
textAlign: "center",
|
|
2312
|
+
marginBottom: 8,
|
|
2313
|
+
marginTop: 4
|
|
2314
|
+
};
|
|
2315
|
+
var walletLabelStyle = (color) => ({
|
|
2316
|
+
fontSize: "0.78rem",
|
|
2317
|
+
color,
|
|
2318
|
+
display: "block",
|
|
2319
|
+
marginBottom: 10
|
|
2320
|
+
});
|
|
2321
|
+
var walletLogosStyle = {
|
|
2234
2322
|
display: "flex",
|
|
2235
2323
|
justifyContent: "center",
|
|
2236
|
-
gap:
|
|
2237
|
-
marginBottom: 8
|
|
2324
|
+
gap: 16
|
|
2238
2325
|
};
|
|
2239
|
-
var
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2326
|
+
var walletEmojiStyle = {
|
|
2327
|
+
fontSize: "1.4rem",
|
|
2328
|
+
lineHeight: 1
|
|
2329
|
+
};
|
|
2330
|
+
var avatarStyle = (tokens) => ({
|
|
2331
|
+
width: 28,
|
|
2332
|
+
height: 28,
|
|
2333
|
+
borderRadius: "50%",
|
|
2243
2334
|
border: `1px solid ${tokens.border}`,
|
|
2244
|
-
background: tokens.bgInput,
|
|
2245
|
-
color: tokens.textMuted,
|
|
2246
2335
|
display: "flex",
|
|
2247
2336
|
alignItems: "center",
|
|
2248
2337
|
justifyContent: "center",
|
|
2249
|
-
fontSize: "0.
|
|
2250
|
-
fontWeight:
|
|
2338
|
+
fontSize: "0.6rem",
|
|
2339
|
+
fontWeight: 700,
|
|
2340
|
+
color: tokens.textMuted,
|
|
2341
|
+
background: "transparent"
|
|
2251
2342
|
});
|
|
2252
2343
|
var RESEND_COOLDOWN_SECONDS = 30;
|
|
2253
2344
|
function OtpVerifyScreen({
|
|
@@ -2899,7 +2990,7 @@ var swipeHintStyle = (color) => ({
|
|
|
2899
2990
|
color,
|
|
2900
2991
|
margin: "12px 0 0"
|
|
2901
2992
|
});
|
|
2902
|
-
var MIN_DEPOSIT =
|
|
2993
|
+
var MIN_DEPOSIT = 0.25;
|
|
2903
2994
|
function DepositScreen({
|
|
2904
2995
|
merchantName,
|
|
2905
2996
|
sourceName,
|
|
@@ -2920,8 +3011,7 @@ function DepositScreen({
|
|
|
2920
3011
|
onLogout
|
|
2921
3012
|
}) {
|
|
2922
3013
|
const { tokens } = useSwypeConfig();
|
|
2923
|
-
const
|
|
2924
|
-
const sliderMax = Math.min(remainingLimit, availableBalance, 500);
|
|
3014
|
+
const amount = initialAmount;
|
|
2925
3015
|
const isLowBalance = availableBalance < MIN_DEPOSIT;
|
|
2926
3016
|
const canDeposit = amount >= MIN_DEPOSIT && amount <= remainingLimit && !isLowBalance && !processing;
|
|
2927
3017
|
const headerTitle = merchantName ? `Deposit to ${merchantName}` : "Deposit";
|
|
@@ -3033,17 +3123,6 @@ function DepositScreen({
|
|
|
3033
3123
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: chevronStyle2, children: ">" })
|
|
3034
3124
|
] })
|
|
3035
3125
|
] }),
|
|
3036
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3037
|
-
LimitSlider,
|
|
3038
|
-
{
|
|
3039
|
-
value: amount,
|
|
3040
|
-
min: MIN_DEPOSIT,
|
|
3041
|
-
max: sliderMax > MIN_DEPOSIT ? sliderMax : 20,
|
|
3042
|
-
step: 0.5,
|
|
3043
|
-
ticks: [MIN_DEPOSIT, 5, 10, 20].filter((t) => t <= sliderMax || t <= 20),
|
|
3044
|
-
onChange: setAmount
|
|
3045
|
-
}
|
|
3046
|
-
),
|
|
3047
3126
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: detailsStyle, children: [
|
|
3048
3127
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: detailRowStyle(tokens.textMuted), children: [
|
|
3049
3128
|
"Remaining limit: ",
|
|
@@ -3052,13 +3131,13 @@ function DepositScreen({
|
|
|
3052
3131
|
remainingLimit.toFixed(2)
|
|
3053
3132
|
] })
|
|
3054
3133
|
] }),
|
|
3055
|
-
estimatedFeeUsd != null && estimatedFeePct != null
|
|
3134
|
+
estimatedFeeUsd != null && estimatedFeePct != null ? /* @__PURE__ */ jsxRuntime.jsxs("div", { style: detailRowStyle(tokens.textMuted), children: [
|
|
3056
3135
|
"Fee: ~$",
|
|
3057
3136
|
estimatedFeeUsd.toFixed(2),
|
|
3058
3137
|
" (",
|
|
3059
3138
|
estimatedFeePct.toFixed(1),
|
|
3060
3139
|
"%)"
|
|
3061
|
-
] })
|
|
3140
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: detailRowStyle(tokens.textMuted), children: "Fees calculated at time of transfer" })
|
|
3062
3141
|
] }),
|
|
3063
3142
|
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle5(tokens), children: error })
|
|
3064
3143
|
]
|
|
@@ -3168,14 +3247,45 @@ function SuccessScreen({
|
|
|
3168
3247
|
onDone,
|
|
3169
3248
|
onLogout,
|
|
3170
3249
|
onIncreaseLimits,
|
|
3171
|
-
onManageAccount
|
|
3250
|
+
onManageAccount,
|
|
3251
|
+
autoCloseSeconds
|
|
3172
3252
|
}) {
|
|
3173
3253
|
const { tokens } = useSwypeConfig();
|
|
3254
|
+
const [countdown, setCountdown] = react.useState(autoCloseSeconds ?? 0);
|
|
3255
|
+
const doneCalledRef = react.useRef(false);
|
|
3256
|
+
const handleDone = react.useCallback(() => {
|
|
3257
|
+
if (doneCalledRef.current) return;
|
|
3258
|
+
doneCalledRef.current = true;
|
|
3259
|
+
onDone();
|
|
3260
|
+
}, [onDone]);
|
|
3261
|
+
react.useEffect(() => {
|
|
3262
|
+
if (!autoCloseSeconds || autoCloseSeconds <= 0) return;
|
|
3263
|
+
const intervalId = window.setInterval(() => {
|
|
3264
|
+
setCountdown((prev) => {
|
|
3265
|
+
if (prev <= 1) {
|
|
3266
|
+
window.clearInterval(intervalId);
|
|
3267
|
+
return 0;
|
|
3268
|
+
}
|
|
3269
|
+
return prev - 1;
|
|
3270
|
+
});
|
|
3271
|
+
}, 1e3);
|
|
3272
|
+
return () => window.clearInterval(intervalId);
|
|
3273
|
+
}, [autoCloseSeconds]);
|
|
3274
|
+
react.useEffect(() => {
|
|
3275
|
+
if (autoCloseSeconds && countdown === 0) {
|
|
3276
|
+
handleDone();
|
|
3277
|
+
}
|
|
3278
|
+
}, [autoCloseSeconds, countdown, handleDone]);
|
|
3174
3279
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3175
3280
|
ScreenLayout,
|
|
3176
3281
|
{
|
|
3177
3282
|
footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3178
|
-
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick:
|
|
3283
|
+
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: handleDone, children: "Done" }),
|
|
3284
|
+
autoCloseSeconds != null && autoCloseSeconds > 0 && /* @__PURE__ */ jsxRuntime.jsxs("p", { style: countdownStyle(tokens.textMuted), children: [
|
|
3285
|
+
"Returning to app in ",
|
|
3286
|
+
countdown,
|
|
3287
|
+
"s\u2026"
|
|
3288
|
+
] }),
|
|
3179
3289
|
onManageAccount && /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onManageAccount, style: manageStyle(tokens.textMuted), children: "Manage Swype account \u2192" }),
|
|
3180
3290
|
/* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
|
|
3181
3291
|
] }),
|
|
@@ -3299,6 +3409,12 @@ var upsellLinkStyle = (color) => ({
|
|
|
3299
3409
|
fontFamily: "inherit",
|
|
3300
3410
|
padding: 0
|
|
3301
3411
|
});
|
|
3412
|
+
var countdownStyle = (color) => ({
|
|
3413
|
+
fontSize: "0.82rem",
|
|
3414
|
+
color,
|
|
3415
|
+
margin: "10px 0 0",
|
|
3416
|
+
textAlign: "center"
|
|
3417
|
+
});
|
|
3302
3418
|
var manageStyle = (color) => ({
|
|
3303
3419
|
background: "transparent",
|
|
3304
3420
|
border: "none",
|
|
@@ -3312,6 +3428,233 @@ var manageStyle = (color) => ({
|
|
|
3312
3428
|
textAlign: "center",
|
|
3313
3429
|
padding: "12px 0 0"
|
|
3314
3430
|
});
|
|
3431
|
+
function SelectSourceScreen({
|
|
3432
|
+
choices,
|
|
3433
|
+
selectedChainName,
|
|
3434
|
+
selectedTokenSymbol,
|
|
3435
|
+
recommended,
|
|
3436
|
+
onChainChange,
|
|
3437
|
+
onTokenChange,
|
|
3438
|
+
onConfirm,
|
|
3439
|
+
onLogout
|
|
3440
|
+
}) {
|
|
3441
|
+
const { tokens } = useSwypeConfig();
|
|
3442
|
+
const selectedChain = choices.find((c) => c.chainName === selectedChainName) ?? choices[0];
|
|
3443
|
+
const tokenChoices = selectedChain?.tokens ?? [];
|
|
3444
|
+
const canConfirm = !!selectedChainName && !!selectedTokenSymbol;
|
|
3445
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3446
|
+
ScreenLayout,
|
|
3447
|
+
{
|
|
3448
|
+
footer: /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3449
|
+
/* @__PURE__ */ jsxRuntime.jsx(PrimaryButton, { onClick: onConfirm, disabled: !canConfirm, children: "Confirm source" }),
|
|
3450
|
+
/* @__PURE__ */ jsxRuntime.jsx(PoweredByFooter, {})
|
|
3451
|
+
] }),
|
|
3452
|
+
children: [
|
|
3453
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3454
|
+
ScreenHeader,
|
|
3455
|
+
{
|
|
3456
|
+
title: "Select Source",
|
|
3457
|
+
right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout })
|
|
3458
|
+
}
|
|
3459
|
+
),
|
|
3460
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: subtitleStyle7(tokens.textMuted), children: "Choose which chain and token to pay from." }),
|
|
3461
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle2(tokens.textSecondary), children: "Chain" }),
|
|
3462
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: optionListStyle, children: choices.map((chain) => {
|
|
3463
|
+
const isSelected = chain.chainName === selectedChainName;
|
|
3464
|
+
const isRecommended = chain.chainName === recommended?.chainName;
|
|
3465
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3466
|
+
"button",
|
|
3467
|
+
{
|
|
3468
|
+
type: "button",
|
|
3469
|
+
onClick: () => onChainChange(chain.chainName),
|
|
3470
|
+
style: optionButtonStyle(tokens, isSelected),
|
|
3471
|
+
children: [
|
|
3472
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: optionContentStyle, children: [
|
|
3473
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: optionNameStyle(tokens.text), children: [
|
|
3474
|
+
chain.chainName,
|
|
3475
|
+
isRecommended && /* @__PURE__ */ jsxRuntime.jsx("span", { style: recommendedBadgeStyle(tokens.accent), children: " recommended" })
|
|
3476
|
+
] }),
|
|
3477
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: optionBalanceStyle(tokens.textMuted), children: [
|
|
3478
|
+
"$",
|
|
3479
|
+
chain.balance.toFixed(2)
|
|
3480
|
+
] })
|
|
3481
|
+
] }),
|
|
3482
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: radioStyle(tokens, isSelected), children: isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { style: radioDotStyle(tokens.accent) }) })
|
|
3483
|
+
]
|
|
3484
|
+
},
|
|
3485
|
+
chain.chainName
|
|
3486
|
+
);
|
|
3487
|
+
}) }),
|
|
3488
|
+
tokenChoices.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3489
|
+
/* @__PURE__ */ jsxRuntime.jsx("label", { style: labelStyle2(tokens.textSecondary), children: "Token" }),
|
|
3490
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: optionListStyle, children: tokenChoices.map((token) => {
|
|
3491
|
+
const isSelected = token.tokenSymbol === selectedTokenSymbol;
|
|
3492
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3493
|
+
"button",
|
|
3494
|
+
{
|
|
3495
|
+
type: "button",
|
|
3496
|
+
onClick: () => onTokenChange(token.tokenSymbol),
|
|
3497
|
+
style: optionButtonStyle(tokens, isSelected),
|
|
3498
|
+
children: [
|
|
3499
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: optionContentStyle, children: [
|
|
3500
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: optionNameStyle(tokens.text), children: token.tokenSymbol }),
|
|
3501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: optionBalanceStyle(tokens.textMuted), children: [
|
|
3502
|
+
"$",
|
|
3503
|
+
token.balance.toFixed(2)
|
|
3504
|
+
] })
|
|
3505
|
+
] }),
|
|
3506
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: radioStyle(tokens, isSelected), children: isSelected && /* @__PURE__ */ jsxRuntime.jsx("div", { style: radioDotStyle(tokens.accent) }) })
|
|
3507
|
+
]
|
|
3508
|
+
},
|
|
3509
|
+
token.tokenSymbol
|
|
3510
|
+
);
|
|
3511
|
+
}) })
|
|
3512
|
+
] })
|
|
3513
|
+
]
|
|
3514
|
+
}
|
|
3515
|
+
);
|
|
3516
|
+
}
|
|
3517
|
+
var subtitleStyle7 = (color) => ({
|
|
3518
|
+
fontSize: "0.85rem",
|
|
3519
|
+
color,
|
|
3520
|
+
margin: "0 0 20px",
|
|
3521
|
+
lineHeight: 1.5
|
|
3522
|
+
});
|
|
3523
|
+
var labelStyle2 = (color) => ({
|
|
3524
|
+
display: "block",
|
|
3525
|
+
fontSize: "0.75rem",
|
|
3526
|
+
fontWeight: 600,
|
|
3527
|
+
color,
|
|
3528
|
+
textTransform: "uppercase",
|
|
3529
|
+
letterSpacing: "0.05em",
|
|
3530
|
+
marginBottom: 8
|
|
3531
|
+
});
|
|
3532
|
+
var optionListStyle = {
|
|
3533
|
+
display: "flex",
|
|
3534
|
+
flexDirection: "column",
|
|
3535
|
+
gap: 8,
|
|
3536
|
+
marginBottom: 20
|
|
3537
|
+
};
|
|
3538
|
+
var optionButtonStyle = (tokens, selected) => ({
|
|
3539
|
+
display: "flex",
|
|
3540
|
+
alignItems: "center",
|
|
3541
|
+
justifyContent: "space-between",
|
|
3542
|
+
padding: "14px 16px",
|
|
3543
|
+
background: tokens.bgInput,
|
|
3544
|
+
border: `1.5px solid ${selected ? tokens.accent : tokens.border}`,
|
|
3545
|
+
borderRadius: 14,
|
|
3546
|
+
cursor: "pointer",
|
|
3547
|
+
fontFamily: "inherit",
|
|
3548
|
+
textAlign: "left",
|
|
3549
|
+
transition: "border-color 0.15s ease"
|
|
3550
|
+
});
|
|
3551
|
+
var optionContentStyle = {
|
|
3552
|
+
display: "flex",
|
|
3553
|
+
flexDirection: "column",
|
|
3554
|
+
gap: 2
|
|
3555
|
+
};
|
|
3556
|
+
var optionNameStyle = (color) => ({
|
|
3557
|
+
fontSize: "0.9rem",
|
|
3558
|
+
fontWeight: 600,
|
|
3559
|
+
color
|
|
3560
|
+
});
|
|
3561
|
+
var recommendedBadgeStyle = (color) => ({
|
|
3562
|
+
fontSize: "0.7rem",
|
|
3563
|
+
fontWeight: 500,
|
|
3564
|
+
color,
|
|
3565
|
+
marginLeft: 6
|
|
3566
|
+
});
|
|
3567
|
+
var optionBalanceStyle = (color) => ({
|
|
3568
|
+
fontSize: "0.78rem",
|
|
3569
|
+
color
|
|
3570
|
+
});
|
|
3571
|
+
var radioStyle = (tokens, selected) => ({
|
|
3572
|
+
width: 20,
|
|
3573
|
+
height: 20,
|
|
3574
|
+
borderRadius: "50%",
|
|
3575
|
+
border: `2px solid ${selected ? tokens.accent : tokens.border}`,
|
|
3576
|
+
display: "flex",
|
|
3577
|
+
alignItems: "center",
|
|
3578
|
+
justifyContent: "center",
|
|
3579
|
+
flexShrink: 0
|
|
3580
|
+
});
|
|
3581
|
+
var radioDotStyle = (color) => ({
|
|
3582
|
+
width: 10,
|
|
3583
|
+
height: 10,
|
|
3584
|
+
borderRadius: "50%",
|
|
3585
|
+
background: color
|
|
3586
|
+
});
|
|
3587
|
+
var STEP_LABELS = ["Creating transfer", "Verifying", "Sent", "Done"];
|
|
3588
|
+
var PHASE_ACTIVE_INDEX = {
|
|
3589
|
+
creating: 0,
|
|
3590
|
+
verifying: 1,
|
|
3591
|
+
sent: 2,
|
|
3592
|
+
done: 4
|
|
3593
|
+
};
|
|
3594
|
+
function buildSteps(phase) {
|
|
3595
|
+
const activeIdx = PHASE_ACTIVE_INDEX[phase];
|
|
3596
|
+
return STEP_LABELS.map((label, i) => ({
|
|
3597
|
+
label,
|
|
3598
|
+
status: i < activeIdx ? "complete" : i === activeIdx ? "active" : "pending"
|
|
3599
|
+
}));
|
|
3600
|
+
}
|
|
3601
|
+
function TransferStatusScreen({
|
|
3602
|
+
phase,
|
|
3603
|
+
error,
|
|
3604
|
+
onLogout
|
|
3605
|
+
}) {
|
|
3606
|
+
const { tokens } = useSwypeConfig();
|
|
3607
|
+
const steps = buildSteps(phase);
|
|
3608
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ScreenLayout, { children: [
|
|
3609
|
+
/* @__PURE__ */ jsxRuntime.jsx(ScreenHeader, { right: /* @__PURE__ */ jsxRuntime.jsx(SettingsMenu, { onLogout }) }),
|
|
3610
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: contentStyle6, children: [
|
|
3611
|
+
/* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
|
|
3612
|
+
/* @__PURE__ */ jsxRuntime.jsx("h2", { style: headingStyle8(tokens.text), children: "Processing Transfer..." }),
|
|
3613
|
+
error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: errorBannerStyle6(tokens), children: error }),
|
|
3614
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: stepsWrapStyle2, children: /* @__PURE__ */ jsxRuntime.jsx(StepList, { steps }) }),
|
|
3615
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: waitHintStyle2(tokens.textMuted), children: "Usually takes a few seconds" })
|
|
3616
|
+
] })
|
|
3617
|
+
] });
|
|
3618
|
+
}
|
|
3619
|
+
var contentStyle6 = {
|
|
3620
|
+
flex: 1,
|
|
3621
|
+
display: "flex",
|
|
3622
|
+
flexDirection: "column",
|
|
3623
|
+
alignItems: "center",
|
|
3624
|
+
justifyContent: "center",
|
|
3625
|
+
textAlign: "center",
|
|
3626
|
+
padding: "0 24px"
|
|
3627
|
+
};
|
|
3628
|
+
var headingStyle8 = (color) => ({
|
|
3629
|
+
fontSize: "1.45rem",
|
|
3630
|
+
fontWeight: 700,
|
|
3631
|
+
letterSpacing: "-0.02em",
|
|
3632
|
+
color,
|
|
3633
|
+
margin: "20px 0 16px"
|
|
3634
|
+
});
|
|
3635
|
+
var errorBannerStyle6 = (tokens) => ({
|
|
3636
|
+
background: tokens.errorBg,
|
|
3637
|
+
border: `1px solid ${tokens.error}66`,
|
|
3638
|
+
borderRadius: 16,
|
|
3639
|
+
padding: "11px 14px",
|
|
3640
|
+
color: tokens.error,
|
|
3641
|
+
fontSize: "0.84rem",
|
|
3642
|
+
marginBottom: 14,
|
|
3643
|
+
lineHeight: 1.5,
|
|
3644
|
+
width: "100%",
|
|
3645
|
+
textAlign: "left"
|
|
3646
|
+
});
|
|
3647
|
+
var stepsWrapStyle2 = {
|
|
3648
|
+
width: "100%",
|
|
3649
|
+
maxWidth: 280,
|
|
3650
|
+
textAlign: "left",
|
|
3651
|
+
marginBottom: 16
|
|
3652
|
+
};
|
|
3653
|
+
var waitHintStyle2 = (color) => ({
|
|
3654
|
+
fontSize: "0.82rem",
|
|
3655
|
+
color,
|
|
3656
|
+
margin: 0
|
|
3657
|
+
});
|
|
3315
3658
|
var ACTIVE_CREDENTIAL_STORAGE_KEY = "swype_active_credential_id";
|
|
3316
3659
|
var MIN_SEND_AMOUNT_USD = 0.25;
|
|
3317
3660
|
function computeSmartDefaults(accts, transferAmount) {
|
|
@@ -3390,7 +3733,9 @@ function SwypePayment({
|
|
|
3390
3733
|
idempotencyKey,
|
|
3391
3734
|
merchantAuthorization,
|
|
3392
3735
|
merchantName,
|
|
3393
|
-
onBack
|
|
3736
|
+
onBack,
|
|
3737
|
+
onDismiss,
|
|
3738
|
+
autoCloseSeconds
|
|
3394
3739
|
}) {
|
|
3395
3740
|
const { apiBaseUrl, tokens, depositAmount } = useSwypeConfig();
|
|
3396
3741
|
const { ready, authenticated, user, logout, getAccessToken } = reactAuth.usePrivy();
|
|
@@ -3404,6 +3749,7 @@ function SwypePayment({
|
|
|
3404
3749
|
loginWithCode: loginWithSmsCode,
|
|
3405
3750
|
state: smsLoginState
|
|
3406
3751
|
} = reactAuth.useLoginWithSms();
|
|
3752
|
+
const { initOAuth } = reactAuth.useLoginWithOAuth();
|
|
3407
3753
|
const [step, setStep] = react.useState("login");
|
|
3408
3754
|
const [error, setError] = react.useState(null);
|
|
3409
3755
|
const [providers, setProviders] = react.useState([]);
|
|
@@ -3452,6 +3798,14 @@ function SwypePayment({
|
|
|
3452
3798
|
setVerificationTarget(null);
|
|
3453
3799
|
setOtpCode("");
|
|
3454
3800
|
}, []);
|
|
3801
|
+
const handleSocialLogin = react.useCallback(async (provider) => {
|
|
3802
|
+
setError(null);
|
|
3803
|
+
try {
|
|
3804
|
+
await initOAuth({ provider });
|
|
3805
|
+
} catch (err) {
|
|
3806
|
+
setError(err instanceof Error ? err.message : "Social login failed");
|
|
3807
|
+
}
|
|
3808
|
+
}, [initOAuth]);
|
|
3455
3809
|
const activeOtpStatus = verificationTarget?.kind === "email" ? emailLoginState.status : verificationTarget?.kind === "phone" ? smsLoginState.status : "initial";
|
|
3456
3810
|
const activeOtpErrorMessage = verificationTarget?.kind === "email" && emailLoginState.status === "error" ? emailLoginState.error?.message ?? "Failed to continue with email." : verificationTarget?.kind === "phone" && smsLoginState.status === "error" ? smsLoginState.error?.message ?? "Failed to continue with phone number." : null;
|
|
3457
3811
|
react.useEffect(() => {
|
|
@@ -3520,6 +3874,9 @@ function SwypePayment({
|
|
|
3520
3874
|
if (!token || cancelled) return;
|
|
3521
3875
|
const { config } = await fetchUserConfig(apiBaseUrl, token);
|
|
3522
3876
|
if (cancelled) return;
|
|
3877
|
+
if (config.defaultAllowance != null) {
|
|
3878
|
+
setOneTapLimit(config.defaultAllowance);
|
|
3879
|
+
}
|
|
3523
3880
|
const allPasskeys = config.passkeys ?? (config.passkey ? [config.passkey] : []);
|
|
3524
3881
|
if (allPasskeys.length === 0) {
|
|
3525
3882
|
setStep("create-passkey");
|
|
@@ -3704,6 +4061,26 @@ function SwypePayment({
|
|
|
3704
4061
|
}
|
|
3705
4062
|
initializedSelectSourceActionRef.current = pendingSelectSourceAction.id;
|
|
3706
4063
|
}, [pendingSelectSourceAction, selectSourceChoices, selectSourceRecommended]);
|
|
4064
|
+
react.useEffect(() => {
|
|
4065
|
+
if (pendingSelectSourceAction && step === "processing") {
|
|
4066
|
+
setStep("select-source");
|
|
4067
|
+
} else if (!pendingSelectSourceAction && step === "select-source") {
|
|
4068
|
+
setStep("processing");
|
|
4069
|
+
}
|
|
4070
|
+
}, [pendingSelectSourceAction, step]);
|
|
4071
|
+
const handleSelectSourceChainChange = react.useCallback(
|
|
4072
|
+
(chainName) => {
|
|
4073
|
+
setSelectSourceChainName(chainName);
|
|
4074
|
+
const chain = selectSourceChoices.find((c) => c.chainName === chainName);
|
|
4075
|
+
if (!chain || chain.tokens.length === 0) return;
|
|
4076
|
+
const recommendedToken = selectSourceRecommended?.chainName === chainName ? selectSourceRecommended.tokenSymbol : null;
|
|
4077
|
+
const hasRecommended = !!recommendedToken && chain.tokens.some((t) => t.tokenSymbol === recommendedToken);
|
|
4078
|
+
setSelectSourceTokenSymbol(
|
|
4079
|
+
hasRecommended ? recommendedToken : chain.tokens[0].tokenSymbol
|
|
4080
|
+
);
|
|
4081
|
+
},
|
|
4082
|
+
[selectSourceChoices, selectSourceRecommended]
|
|
4083
|
+
);
|
|
3707
4084
|
const selectedAccount = accounts.find((a) => a.id === selectedAccountId);
|
|
3708
4085
|
const selectedWallet = selectedAccount?.wallets.find((w) => w.id === selectedWalletId);
|
|
3709
4086
|
const sourceName = selectedAccount?.name ?? selectedWallet?.chain.name ?? "Wallet";
|
|
@@ -3879,6 +4256,7 @@ function SwypePayment({
|
|
|
3879
4256
|
}
|
|
3880
4257
|
const setupAmount = depositAmount ?? MIN_SEND_AMOUNT_USD;
|
|
3881
4258
|
const t = await createTransfer(apiBaseUrl, token, {
|
|
4259
|
+
id: idempotencyKey,
|
|
3882
4260
|
credentialId: activeCredentialId ?? "",
|
|
3883
4261
|
merchantAuthorization,
|
|
3884
4262
|
sourceType,
|
|
@@ -3907,6 +4285,7 @@ function SwypePayment({
|
|
|
3907
4285
|
}
|
|
3908
4286
|
}, [
|
|
3909
4287
|
getAccessToken,
|
|
4288
|
+
idempotencyKey,
|
|
3910
4289
|
sourceId,
|
|
3911
4290
|
sourceType,
|
|
3912
4291
|
activeCredentialId,
|
|
@@ -3977,7 +4356,9 @@ function SwypePayment({
|
|
|
3977
4356
|
onSubmit: handleSendLoginCode,
|
|
3978
4357
|
sending: activeOtpStatus === "sending-code",
|
|
3979
4358
|
error,
|
|
3980
|
-
onBack
|
|
4359
|
+
onBack,
|
|
4360
|
+
merchantInitials: merchantName ? merchantName.slice(0, 2).toUpperCase() : void 0,
|
|
4361
|
+
onSocialLogin: handleSocialLogin
|
|
3981
4362
|
}
|
|
3982
4363
|
);
|
|
3983
4364
|
}
|
|
@@ -4004,6 +4385,9 @@ function SwypePayment({
|
|
|
4004
4385
|
}
|
|
4005
4386
|
);
|
|
4006
4387
|
}
|
|
4388
|
+
if ((step === "login" || step === "otp-verify") && authenticated) {
|
|
4389
|
+
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: "Verifying your passkey..." }) }) });
|
|
4390
|
+
}
|
|
4007
4391
|
if (step === "create-passkey") {
|
|
4008
4392
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4009
4393
|
CreatePasskeyScreen,
|
|
@@ -4069,11 +4453,9 @@ function SwypePayment({
|
|
|
4069
4453
|
sourceAddress,
|
|
4070
4454
|
sourceVerified,
|
|
4071
4455
|
availableBalance: maxSourceBalance,
|
|
4072
|
-
remainingLimit: oneTapLimit,
|
|
4456
|
+
remainingLimit: selectedAccount?.remainingAllowance ?? oneTapLimit,
|
|
4073
4457
|
tokenCount,
|
|
4074
4458
|
initialAmount: parsedAmt,
|
|
4075
|
-
estimatedFeePct: 0.6,
|
|
4076
|
-
estimatedFeeUsd: parsedAmt * 6e-3,
|
|
4077
4459
|
processing: creatingTransfer,
|
|
4078
4460
|
error,
|
|
4079
4461
|
onDeposit: handlePay,
|
|
@@ -4085,24 +4467,36 @@ function SwypePayment({
|
|
|
4085
4467
|
);
|
|
4086
4468
|
}
|
|
4087
4469
|
if (step === "processing") {
|
|
4088
|
-
const
|
|
4089
|
-
const
|
|
4090
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
4096
|
-
|
|
4097
|
-
|
|
4098
|
-
|
|
4099
|
-
|
|
4100
|
-
|
|
4101
|
-
|
|
4102
|
-
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4470
|
+
const polledStatus = polling.transfer?.status;
|
|
4471
|
+
const transferPhase = creatingTransfer ? "creating" : mobileFlow || authExecutor.executing || transferSigning.signing ? "verifying" : polledStatus === "SENDING" || polledStatus === "SENT" || polling.isPolling ? "sent" : "creating";
|
|
4472
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4473
|
+
TransferStatusScreen,
|
|
4474
|
+
{
|
|
4475
|
+
phase: transferPhase,
|
|
4476
|
+
error: error || authExecutor.error || transferSigning.error || polling.error,
|
|
4477
|
+
onLogout: handleLogout
|
|
4478
|
+
}
|
|
4479
|
+
);
|
|
4480
|
+
}
|
|
4481
|
+
if (step === "select-source") {
|
|
4482
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4483
|
+
SelectSourceScreen,
|
|
4484
|
+
{
|
|
4485
|
+
choices: selectSourceChoices,
|
|
4486
|
+
selectedChainName: selectSourceChainName,
|
|
4487
|
+
selectedTokenSymbol: selectSourceTokenSymbol,
|
|
4488
|
+
recommended: selectSourceRecommended,
|
|
4489
|
+
onChainChange: handleSelectSourceChainChange,
|
|
4490
|
+
onTokenChange: setSelectSourceTokenSymbol,
|
|
4491
|
+
onConfirm: () => {
|
|
4492
|
+
authExecutor.resolveSelectSource({
|
|
4493
|
+
chainName: selectSourceChainName,
|
|
4494
|
+
tokenSymbol: selectSourceTokenSymbol
|
|
4495
|
+
});
|
|
4496
|
+
},
|
|
4497
|
+
onLogout: handleLogout
|
|
4498
|
+
}
|
|
4499
|
+
);
|
|
4106
4500
|
}
|
|
4107
4501
|
if (step === "success") {
|
|
4108
4502
|
transfer?.status === "COMPLETED";
|
|
@@ -4115,9 +4509,13 @@ function SwypePayment({
|
|
|
4115
4509
|
currency: displayCurrency,
|
|
4116
4510
|
merchantName,
|
|
4117
4511
|
sourceName,
|
|
4118
|
-
remainingLimit:
|
|
4119
|
-
|
|
4120
|
-
|
|
4512
|
+
remainingLimit: (() => {
|
|
4513
|
+
const limit = selectedAccount?.remainingAllowance ?? oneTapLimit;
|
|
4514
|
+
return limit > displayAmount ? limit - displayAmount : 0;
|
|
4515
|
+
})(),
|
|
4516
|
+
onDone: onDismiss ?? handleNewPayment,
|
|
4517
|
+
onLogout: handleLogout,
|
|
4518
|
+
autoCloseSeconds
|
|
4121
4519
|
}
|
|
4122
4520
|
);
|
|
4123
4521
|
}
|
|
@@ -4130,7 +4528,7 @@ function SwypePayment({
|
|
|
4130
4528
|
sourceAddress,
|
|
4131
4529
|
sourceVerified,
|
|
4132
4530
|
availableBalance: 0,
|
|
4133
|
-
remainingLimit: oneTapLimit,
|
|
4531
|
+
remainingLimit: selectedAccount?.remainingAllowance ?? oneTapLimit,
|
|
4134
4532
|
tokenCount,
|
|
4135
4533
|
initialAmount: depositAmount ?? 5,
|
|
4136
4534
|
processing: false,
|