@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 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
- return `Payment is taking longer than expected (status: ${status}). Please try again.`;
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, "").split("").slice(0, 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("div", { style: dividerStyle(tokens), children: [
2132
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: dividerLineStyle(tokens.border) }),
2133
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: "works with" }),
2134
- /* @__PURE__ */ jsxRuntime.jsx("div", { style: dividerLineStyle(tokens.border) })
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(ScreenHeader, { onBack }),
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(IconCircle, { variant: "accent", size: 56, children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", children: /* @__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.65rem",
2242
+ fontSize: "1.45rem",
2183
2243
  fontWeight: 700,
2184
- lineHeight: 1.2,
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 logosRowStyle = {
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: 12,
2237
- marginBottom: 8
2324
+ gap: 16
2238
2325
  };
2239
- var logoCircleStyle = (tokens) => ({
2240
- width: 34,
2241
- height: 34,
2242
- borderRadius: 999,
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.72rem",
2250
- fontWeight: 600
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 = 1;
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 [amount, setAmount] = react.useState(initialAmount);
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 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: detailRowStyle(tokens.textMuted), children: [
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: onDone, children: "Done" }),
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 currentActionType = authExecutor.currentAction?.type;
4089
- const statusLabel = creatingTransfer ? "Creating Transfer" : mobileFlow ? "Waiting for Authorization" : authExecutor.executing ? currentActionType?.replace(/_/g, " ") ?? "Authorizing" : transferSigning.signing ? "Sending transfer" : polling.isPolling ? "Transfer Sent" : "Please wait...";
4090
- return /* @__PURE__ */ jsxRuntime.jsx(ScreenLayout, { children: /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { textAlign: "center", padding: "48px 0", flex: 1, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }, children: [
4091
- /* @__PURE__ */ jsxRuntime.jsx(Spinner, { size: 48 }),
4092
- /* @__PURE__ */ jsxRuntime.jsx("h2", { style: { fontSize: "1.4rem", fontWeight: 700, color: tokens.text, marginTop: 20, marginBottom: 8 }, children: statusLabel }),
4093
- (error || authExecutor.error || transferSigning.error || polling.error) && /* @__PURE__ */ jsxRuntime.jsx("div", { style: {
4094
- background: tokens.errorBg,
4095
- border: `1px solid ${tokens.error}66`,
4096
- borderRadius: 16,
4097
- padding: "11px 14px",
4098
- color: tokens.error,
4099
- fontSize: "0.84rem",
4100
- marginTop: 16,
4101
- lineHeight: 1.5,
4102
- textAlign: "left",
4103
- maxWidth: 340
4104
- }, children: error || authExecutor.error || transferSigning.error || polling.error })
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: oneTapLimit > displayAmount ? oneTapLimit - displayAmount : 0,
4119
- onDone: handleNewPayment,
4120
- onLogout: handleLogout
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,