@pollar/react 0.10.1-rc.0 → 0.10.1-rc.1

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.js CHANGED
@@ -1035,7 +1035,7 @@ var PollarModalFooter = () => {
1035
1035
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1036
1036
  /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "pollar-footer-version", children: [
1037
1037
  "v",
1038
- "0.10.1-rc.0"
1038
+ "0.10.1-rc.1"
1039
1039
  ] })
1040
1040
  ] })
1041
1041
  ] });
@@ -2459,14 +2459,12 @@ function RouteDisplay({ quote, onSelect }) {
2459
2459
  );
2460
2460
  }
2461
2461
  var LOADING_STEPS = ["Detecting your country\u2026", "Consulting providers\u2026", "Route found!"];
2462
- var COUNTRY_CURRENCIES = {
2463
- MX: "MXN",
2464
- BR: "BRL",
2465
- CO: "COP",
2466
- CL: "CLP",
2467
- PE: "PEN",
2468
- AR: "ARS"
2469
- };
2462
+ function flagEmoji(code) {
2463
+ const cc = code.trim().toUpperCase();
2464
+ if (!/^[A-Z]{2}$/.test(cc)) return "";
2465
+ const base = 127462;
2466
+ return String.fromCodePoint(base + (cc.charCodeAt(0) - 65), base + (cc.charCodeAt(1) - 65));
2467
+ }
2470
2468
  var STATUS_LABEL = {
2471
2469
  pending: "Pending",
2472
2470
  processing: "Processing",
@@ -2481,6 +2479,9 @@ function RampWidgetTemplate({
2481
2479
  amount,
2482
2480
  currency,
2483
2481
  country,
2482
+ countries,
2483
+ countriesLoading,
2484
+ refreshing,
2484
2485
  quotes,
2485
2486
  isLoading,
2486
2487
  provider,
@@ -2499,6 +2500,7 @@ function RampWidgetTemplate({
2499
2500
  onOpenKyc,
2500
2501
  onCompleteWithdraw,
2501
2502
  onRetry,
2503
+ onRefresh,
2502
2504
  onClose
2503
2505
  }) {
2504
2506
  const isDark = theme === "dark";
@@ -2537,9 +2539,35 @@ function RampWidgetTemplate({
2537
2539
  error: "Please try again"
2538
2540
  };
2539
2541
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-card pollar-ramp-modal", style: cssVars, onClick: (e) => e.stopPropagation(), children: [
2540
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-header", children: [
2541
- /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-ramp-title", children: stepTitle[step] }),
2542
- /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-subtitle", children: stepSubtitle[step] })
2542
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header", children: [
2543
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-header-text", children: [
2544
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "pollar-modal-title", children: stepTitle[step] }),
2545
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "pollar-ramp-subtitle", children: stepSubtitle[step] })
2546
+ ] }),
2547
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-header-actions", children: [
2548
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onRefresh, disabled: refreshing, "aria-label": "Refresh", children: /* @__PURE__ */ jsxRuntime.jsx(
2549
+ "svg",
2550
+ {
2551
+ className: refreshing ? "pollar-modal-refresh-icon spinning" : "pollar-modal-refresh-icon",
2552
+ width: "16",
2553
+ height: "16",
2554
+ viewBox: "0 0 16 16",
2555
+ fill: "none",
2556
+ "aria-hidden": true,
2557
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2558
+ "path",
2559
+ {
2560
+ d: "M13.5 8a5.5 5.5 0 1 1-1.6-3.9M13.5 2v3h-3",
2561
+ stroke: "currentColor",
2562
+ strokeWidth: "1.6",
2563
+ strokeLinecap: "round",
2564
+ strokeLinejoin: "round"
2565
+ }
2566
+ )
2567
+ }
2568
+ ) }),
2569
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2570
+ ] })
2543
2571
  ] }),
2544
2572
  step === "input" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2545
2573
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-tabs", children: [
@@ -2596,30 +2624,25 @@ function RampWidgetTemplate({
2596
2624
  ] }),
2597
2625
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-field", children: [
2598
2626
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-ramp-label", children: "Country" }),
2599
- /* @__PURE__ */ jsxRuntime.jsxs(
2600
- "select",
2601
- {
2602
- className: "pollar-ramp-input",
2603
- value: country,
2604
- onChange: (e) => {
2605
- const c = e.target.value;
2606
- onCountryChange(c);
2607
- if (COUNTRY_CURRENCIES[c]) onCurrencyChange(COUNTRY_CURRENCIES[c]);
2608
- },
2609
- children: [
2610
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "MX", children: "\u{1F1F2}\u{1F1FD} Mexico" }),
2611
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "BR", children: "\u{1F1E7}\u{1F1F7} Brazil" }),
2612
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "CO", children: "\u{1F1E8}\u{1F1F4} Colombia" }),
2613
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "CL", children: "\u{1F1E8}\u{1F1F1} Chile" }),
2614
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "PE", children: "\u{1F1F5}\u{1F1EA} Peru" }),
2615
- /* @__PURE__ */ jsxRuntime.jsx("option", { value: "AR", children: "\u{1F1E6}\u{1F1F7} Argentina" })
2616
- ]
2617
- }
2618
- )
2627
+ countriesLoading ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-input pollar-ramp-input-loading", children: "Loading countries\u2026" }) : countries.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: "No ramp providers available on this network yet." }) : /* @__PURE__ */ jsxRuntime.jsx("select", { className: "pollar-ramp-input", value: country, onChange: (e) => onCountryChange(e.target.value), children: countries.map((c) => /* @__PURE__ */ jsxRuntime.jsxs("option", { value: c.code, children: [
2628
+ flagEmoji(c.code),
2629
+ " ",
2630
+ c.code,
2631
+ c.currency ? ` \u2014 ${c.currency}` : ""
2632
+ ] }, c.code)) })
2619
2633
  ] }),
2620
2634
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-modal-actions", children: [
2621
2635
  /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-secondary", onClick: onClose, children: "Cancel" }),
2622
- /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", disabled: !amount || isLoading, onClick: onFindRoute, children: "Find best route" })
2636
+ /* @__PURE__ */ jsxRuntime.jsx(
2637
+ "button",
2638
+ {
2639
+ type: "button",
2640
+ className: "pollar-btn-primary",
2641
+ disabled: !amount || isLoading || countriesLoading || countries.length === 0,
2642
+ onClick: onFindRoute,
2643
+ children: "Find best route"
2644
+ }
2645
+ )
2623
2646
  ] })
2624
2647
  ] }),
2625
2648
  step === "loading_quote" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-loading", children: LOADING_STEPS.map((text, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-loading-row", children: [
@@ -2637,13 +2660,7 @@ function RampWidgetTemplate({
2637
2660
  ] }),
2638
2661
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2639
2662
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Status" }),
2640
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx(
2641
- "code",
2642
- {
2643
- style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 },
2644
- children: txStatus ? STATUS_LABEL[txStatus] : "Processing"
2645
- }
2646
- ) })
2663
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-ramp-payment-value", children: /* @__PURE__ */ jsxRuntime.jsx("code", { style: { color: txStatus === "completed" ? "var(--pollar-success-text)" : void 0 }, children: txStatus ? STATUS_LABEL[txStatus] : "Processing" }) })
2647
2664
  ] }),
2648
2665
  stellarTxHash && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-ramp-payment-field", children: [
2649
2666
  /* @__PURE__ */ jsxRuntime.jsx("span", { className: "pollar-ramp-payment-label", children: "Stellar tx" }),
@@ -2679,8 +2696,11 @@ function RampWidget({ onClose }) {
2679
2696
  const [step, setStep] = react.useState("input");
2680
2697
  const [direction, setDirection] = react.useState("onramp");
2681
2698
  const [amount, setAmount] = react.useState("");
2682
- const [currency, setCurrency] = react.useState("ARS");
2683
- const [country, setCountry] = react.useState("AR");
2699
+ const [currency, setCurrency] = react.useState("");
2700
+ const [country, setCountry] = react.useState("");
2701
+ const [countries, setCountries] = react.useState([]);
2702
+ const [countriesLoading, setCountriesLoading] = react.useState(true);
2703
+ const [refreshing, setRefreshing] = react.useState(false);
2684
2704
  const [quotes, setQuotes] = react.useState([]);
2685
2705
  const [isLoading, setIsLoading] = react.useState(false);
2686
2706
  const [txId, setTxId] = react.useState(null);
@@ -2712,6 +2732,50 @@ function RampWidget({ onClose }) {
2712
2732
  clearInterval(id);
2713
2733
  };
2714
2734
  }, [step, txId, txStatus, client]);
2735
+ async function loadCountries(resetSelection) {
2736
+ setCountriesLoading(true);
2737
+ try {
2738
+ const { countries: list } = await client.getRampCountries();
2739
+ setCountries(list);
2740
+ const first = list[0];
2741
+ const stillValid = list.some((c) => c.code === country);
2742
+ if (first && (resetSelection || !stillValid)) {
2743
+ setCountry(first.code);
2744
+ if (first.currency) setCurrency(first.currency);
2745
+ }
2746
+ } catch {
2747
+ setCountries([]);
2748
+ } finally {
2749
+ setCountriesLoading(false);
2750
+ }
2751
+ }
2752
+ react.useEffect(() => {
2753
+ loadCountries(true);
2754
+ }, []);
2755
+ function handleCountryChange(code) {
2756
+ setCountry(code);
2757
+ const match = countries.find((c) => c.code === code);
2758
+ if (match?.currency) setCurrency(match.currency);
2759
+ }
2760
+ async function handleRefresh() {
2761
+ if (refreshing) return;
2762
+ setRefreshing(true);
2763
+ try {
2764
+ await loadCountries(false);
2765
+ if (step === "select_route") {
2766
+ const result = await client.getRampsQuote({ country, amount: Number(amount), currency, direction });
2767
+ setQuotes(result.quotes ?? []);
2768
+ } else if (step === "status" && txId) {
2769
+ const tx = await client.getRampTransaction(txId);
2770
+ setTxStatus(tx.status);
2771
+ if (tx.stellarTxHash) setStellarTxHash(tx.stellarTxHash);
2772
+ if (tx.kycUrl) setKycUrl(tx.kycUrl);
2773
+ }
2774
+ } catch {
2775
+ } finally {
2776
+ setRefreshing(false);
2777
+ }
2778
+ }
2715
2779
  function resetToInput() {
2716
2780
  setStep("input");
2717
2781
  setQuotes([]);
@@ -2817,6 +2881,9 @@ function RampWidget({ onClose }) {
2817
2881
  amount,
2818
2882
  currency,
2819
2883
  country,
2884
+ countries,
2885
+ countriesLoading,
2886
+ refreshing,
2820
2887
  quotes,
2821
2888
  isLoading,
2822
2889
  provider,
@@ -2829,12 +2896,13 @@ function RampWidget({ onClose }) {
2829
2896
  onDirectionChange: setDirection,
2830
2897
  onAmountChange: setAmount,
2831
2898
  onCurrencyChange: setCurrency,
2832
- onCountryChange: setCountry,
2899
+ onCountryChange: handleCountryChange,
2833
2900
  onFindRoute: handleFindRoute,
2834
2901
  onSelectQuote: handleSelectQuote,
2835
2902
  onOpenKyc: handleOpenKyc,
2836
2903
  onCompleteWithdraw: handleCompleteWithdraw,
2837
2904
  onRetry: resetToInput,
2905
+ onRefresh: handleRefresh,
2838
2906
  onClose
2839
2907
  }
2840
2908
  ) });
@@ -3544,6 +3612,7 @@ function SwapModalTemplate({
3544
3612
  onRefresh,
3545
3613
  onSelectSell,
3546
3614
  onSelectBuy,
3615
+ onAddCustomToken,
3547
3616
  onAmountChange,
3548
3617
  onProviderChange,
3549
3618
  onSwap,
@@ -3553,6 +3622,21 @@ function SwapModalTemplate({
3553
3622
  onDone
3554
3623
  }) {
3555
3624
  const isDark = theme === "dark";
3625
+ const [customOpen, setCustomOpen] = react.useState(false);
3626
+ const [customCode, setCustomCode] = react.useState("");
3627
+ const [customIssuer, setCustomIssuer] = react.useState("");
3628
+ const [customError, setCustomError] = react.useState("");
3629
+ const submitCustomToken = () => {
3630
+ const err = onAddCustomToken(customCode, customIssuer);
3631
+ if (err) {
3632
+ setCustomError(err);
3633
+ return;
3634
+ }
3635
+ setCustomCode("");
3636
+ setCustomIssuer("");
3637
+ setCustomError("");
3638
+ setCustomOpen(false);
3639
+ };
3556
3640
  const cssVars = {
3557
3641
  "--pollar-accent": accentColor,
3558
3642
  "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
@@ -3671,7 +3755,45 @@ function SwapModalTemplate({
3671
3755
  ] }, assetOptionKey(o)))
3672
3756
  ]
3673
3757
  }
3674
- )
3758
+ ),
3759
+ !isLoadingData && (customOpen ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-custom", children: [
3760
+ /* @__PURE__ */ jsxRuntime.jsx(
3761
+ "input",
3762
+ {
3763
+ className: "pollar-input",
3764
+ type: "text",
3765
+ placeholder: "Code (e.g. USDC)",
3766
+ value: customCode,
3767
+ onChange: (e) => setCustomCode(e.target.value)
3768
+ }
3769
+ ),
3770
+ /* @__PURE__ */ jsxRuntime.jsx(
3771
+ "input",
3772
+ {
3773
+ className: "pollar-input",
3774
+ type: "text",
3775
+ placeholder: "Issuer (G...)",
3776
+ value: customIssuer,
3777
+ onChange: (e) => setCustomIssuer(e.target.value)
3778
+ }
3779
+ ),
3780
+ customError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "pollar-modal-error", children: customError }),
3781
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-swap-custom-actions", children: [
3782
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-btn-primary", onClick: submitCustomToken, children: "Add token" }),
3783
+ /* @__PURE__ */ jsxRuntime.jsx(
3784
+ "button",
3785
+ {
3786
+ type: "button",
3787
+ className: "pollar-swap-custom-cancel",
3788
+ onClick: () => {
3789
+ setCustomOpen(false);
3790
+ setCustomError("");
3791
+ },
3792
+ children: "Cancel"
3793
+ }
3794
+ )
3795
+ ] })
3796
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", className: "pollar-swap-custom-toggle", onClick: () => setCustomOpen(true), children: "+ Add a token by code / issuer" }))
3675
3797
  ] }),
3676
3798
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pollar-send-field", children: [
3677
3799
  /* @__PURE__ */ jsxRuntime.jsx("label", { className: "pollar-send-label", children: "Route" }),
@@ -3779,6 +3901,7 @@ function SwapModal({ onClose }) {
3779
3901
  const [provider, setProvider] = react.useState("auto");
3780
3902
  const [venues, setVenues] = react.useState(null);
3781
3903
  const [catalogTokens, setCatalogTokens] = react.useState([]);
3904
+ const [customTokens, setCustomTokens] = react.useState([]);
3782
3905
  const [quotes, setQuotes] = react.useState([]);
3783
3906
  const [quoteLoading, setQuoteLoading] = react.useState(false);
3784
3907
  const [quoteError, setQuoteError] = react.useState("");
@@ -3836,7 +3959,21 @@ function SwapModal({ onClose }) {
3836
3959
  issuer: tk.issuer,
3837
3960
  enabledInApp: false
3838
3961
  })).filter((o) => !enabledKeys.has(optKey(o)));
3839
- const buyOptions = [...enabledBuy, ...catalogBuy].filter((o) => optKey(o) !== buyKeyOfSell);
3962
+ const knownKeys = /* @__PURE__ */ new Set([...enabledKeys, ...catalogBuy.map(optKey)]);
3963
+ const customBuy = customTokens.filter((o) => !knownKeys.has(optKey(o)));
3964
+ const buyOptions = [...enabledBuy, ...catalogBuy, ...customBuy].filter(
3965
+ (o) => optKey(o) !== buyKeyOfSell
3966
+ );
3967
+ function addCustomToken(code, issuer) {
3968
+ const c = code.trim().toUpperCase();
3969
+ const i = issuer.trim();
3970
+ if (c.length < 1 || c.length > 12) return "Code must be 1-12 characters";
3971
+ if (i.length !== 56 || !i.startsWith("G")) return "Issuer must be a Stellar address (G..., 56 chars)";
3972
+ const opt = { ref: catalogRef(c, i), code: c, issuer: i, enabledInApp: false };
3973
+ setCustomTokens((prev) => prev.some((o) => optKey(o) === optKey(opt)) ? prev : [...prev, opt]);
3974
+ setSelectedBuy(opt);
3975
+ return null;
3976
+ }
3840
3977
  const buyNeedsTrustline = (() => {
3841
3978
  if (!selectedBuy || smartUnsupported) return false;
3842
3979
  if (selectedBuy.ref.type === "native") return false;
@@ -3976,6 +4113,7 @@ function SwapModal({ onClose }) {
3976
4113
  onRefresh: handleRefresh,
3977
4114
  onSelectSell: setSelectedSell,
3978
4115
  onSelectBuy: setSelectedBuy,
4116
+ onAddCustomToken: addCustomToken,
3979
4117
  onAmountChange: setAmount,
3980
4118
  onProviderChange: setProvider,
3981
4119
  onSwap: () => void handleSwap(),