@pollar/react 0.6.0 → 0.7.0

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.mjs CHANGED
@@ -1028,7 +1028,7 @@ var PollarModalFooter = () => {
1028
1028
  /* @__PURE__ */ jsx("span", { className: "pollar-footer-name", children: "Pollar" }),
1029
1029
  /* @__PURE__ */ jsxs("span", { className: "pollar-footer-version", children: [
1030
1030
  "v",
1031
- "0.6.0"
1031
+ "0.7.0"
1032
1032
  ] })
1033
1033
  ] })
1034
1034
  ] });
@@ -2499,6 +2499,254 @@ function SendModal({ onClose }) {
2499
2499
  }
2500
2500
  ) });
2501
2501
  }
2502
+ function describeDevice(s) {
2503
+ if (s.deviceLabel) return s.deviceLabel;
2504
+ if (!s.userAgent) return "Unknown device";
2505
+ return parseUserAgent(s.userAgent);
2506
+ }
2507
+ function detectBrowser(ua) {
2508
+ if (/Edg\//.test(ua)) return "Edge";
2509
+ if (/OPR\//.test(ua)) return "Opera";
2510
+ if (/(Chrome|CriOS)\//.test(ua)) return "Chrome";
2511
+ if (/(Firefox|FxiOS)\//.test(ua)) return "Firefox";
2512
+ if (/Safari\//.test(ua)) return "Safari";
2513
+ return null;
2514
+ }
2515
+ function detectOS(ua) {
2516
+ if (/iPhone|iPad|iPod/.test(ua)) return "iOS";
2517
+ if (/Android/.test(ua)) return "Android";
2518
+ if (/Mac OS X/.test(ua)) return "macOS";
2519
+ if (/Windows NT/.test(ua)) return "Windows";
2520
+ if (/Linux/.test(ua)) return "Linux";
2521
+ return null;
2522
+ }
2523
+ function parseUserAgent(ua) {
2524
+ const browser = detectBrowser(ua);
2525
+ const os = detectOS(ua);
2526
+ if (browser && os) return `${os} \u2014 ${browser}`;
2527
+ if (os) return os;
2528
+ if (browser) return browser;
2529
+ return ua.slice(0, 48);
2530
+ }
2531
+ function formatRelative(iso) {
2532
+ if (!iso) return "\u2014";
2533
+ const ts = new Date(iso).getTime();
2534
+ if (!Number.isFinite(ts)) return "\u2014";
2535
+ const diffSec = Math.round((Date.now() - ts) / 1e3);
2536
+ if (diffSec < 0) return "just now";
2537
+ if (diffSec < 60) return `${diffSec}s ago`;
2538
+ const diffMin = Math.round(diffSec / 60);
2539
+ if (diffMin < 60) return `${diffMin}m ago`;
2540
+ const diffHr = Math.round(diffMin / 60);
2541
+ if (diffHr < 24) return `${diffHr}h ago`;
2542
+ const diffDay = Math.round(diffHr / 24);
2543
+ if (diffDay < 30) return `${diffDay}d ago`;
2544
+ return new Date(iso).toLocaleDateString(void 0, { month: "short", day: "numeric", year: "numeric" });
2545
+ }
2546
+ function shortIp(hash) {
2547
+ if (!hash) return "";
2548
+ return hash.slice(0, 8);
2549
+ }
2550
+ function SessionsModalTemplate({
2551
+ theme,
2552
+ accentColor,
2553
+ state,
2554
+ revokingFamilyId,
2555
+ signingOutEverywhere,
2556
+ onRefresh,
2557
+ onRevoke,
2558
+ onLogoutEverywhere,
2559
+ onClose
2560
+ }) {
2561
+ const isDark = theme === "dark";
2562
+ const cssVars = {
2563
+ "--pollar-accent": accentColor,
2564
+ "--pollar-bg": isDark ? "#1a1a1a" : "#ffffff",
2565
+ "--pollar-border": isDark ? "#374151" : "#e5e7eb",
2566
+ "--pollar-text": isDark ? "#ffffff" : "#111827",
2567
+ "--pollar-muted": isDark ? "#9ca3af" : "#6b7280",
2568
+ "--pollar-input-bg": isDark ? "#374151" : "#f9fafb",
2569
+ "--pollar-error-bg": isDark ? "#2a1515" : "#fef2f2",
2570
+ "--pollar-error-border": isDark ? "#7f1d1d" : "#fecaca",
2571
+ "--pollar-error-text": isDark ? "#f87171" : "#dc2626",
2572
+ "--pollar-success-text": isDark ? "#4ade80" : "#16a34a",
2573
+ "--pollar-buttons-border-radius": "6px",
2574
+ "--pollar-buttons-height": "44px",
2575
+ "--pollar-input-height": "44px",
2576
+ "--pollar-input-border-radius": "0.5rem",
2577
+ "--pollar-card-border-radius": "10px"
2578
+ };
2579
+ const isLoading = state.step === "loading";
2580
+ const sessions = state.step === "loaded" ? state.sessions : [];
2581
+ const otherCount = sessions.filter((s) => !s.current).length;
2582
+ return /* @__PURE__ */ jsxs(
2583
+ "div",
2584
+ {
2585
+ className: "pollar-modal-card pollar-sessions-modal",
2586
+ "data-theme": theme,
2587
+ style: cssVars,
2588
+ onClick: (e) => e.stopPropagation(),
2589
+ children: [
2590
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header", children: [
2591
+ /* @__PURE__ */ jsx("h2", { className: "pollar-modal-title", children: "Active sessions" }),
2592
+ /* @__PURE__ */ jsxs("div", { className: "pollar-modal-header-actions", children: [
2593
+ /* @__PURE__ */ jsxs("button", { className: "pollar-modal-refresh-btn", onClick: onRefresh, disabled: isLoading, children: [
2594
+ /* @__PURE__ */ jsxs(
2595
+ "svg",
2596
+ {
2597
+ className: `pollar-modal-refresh-icon${isLoading ? " spinning" : ""}`,
2598
+ width: "13",
2599
+ height: "13",
2600
+ viewBox: "0 0 13 13",
2601
+ fill: "none",
2602
+ "aria-hidden": true,
2603
+ children: [
2604
+ /* @__PURE__ */ jsx("path", { d: "M11.5 6.5a5 5 0 11-1.5-3.536", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round" }),
2605
+ /* @__PURE__ */ jsx(
2606
+ "path",
2607
+ {
2608
+ d: "M10 1v3h-3",
2609
+ stroke: "currentColor",
2610
+ strokeWidth: "1.5",
2611
+ strokeLinecap: "round",
2612
+ strokeLinejoin: "round"
2613
+ }
2614
+ )
2615
+ ]
2616
+ }
2617
+ ),
2618
+ "Refresh"
2619
+ ] }),
2620
+ /* @__PURE__ */ jsx("button", { className: "pollar-modal-close", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx("path", { d: "M2 2l12 12M14 2L2 14", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }) }) })
2621
+ ] })
2622
+ ] }),
2623
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-list", children: [
2624
+ state.step === "idle" && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "Loading\u2026" }),
2625
+ isLoading && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "Loading\u2026" }),
2626
+ state.step === "error" && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: state.message }),
2627
+ state.step === "loaded" && sessions.length === 0 && /* @__PURE__ */ jsx("div", { className: "pollar-modal-empty", children: "No active sessions." }),
2628
+ sessions.map((s) => {
2629
+ const isRevoking = revokingFamilyId === s.familyId;
2630
+ return /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item", "data-current": s.current || void 0, children: [
2631
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item-main", children: [
2632
+ /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-device", children: describeDevice(s) }),
2633
+ s.current && /* @__PURE__ */ jsx("span", { className: "pollar-sessions-item-badge", children: "This device" })
2634
+ ] }),
2635
+ /* @__PURE__ */ jsxs("div", { className: "pollar-sessions-item-meta", children: [
2636
+ /* @__PURE__ */ jsxs("span", { children: [
2637
+ "Last used ",
2638
+ formatRelative(s.lastUsedAt ?? s.createdAt)
2639
+ ] }),
2640
+ s.ipHash && /* @__PURE__ */ jsxs(Fragment, { children: [
2641
+ /* @__PURE__ */ jsx("span", { children: "\xB7" }),
2642
+ /* @__PURE__ */ jsxs("span", { title: `ip-hash ${s.ipHash}`, children: [
2643
+ "ip ",
2644
+ shortIp(s.ipHash)
2645
+ ] })
2646
+ ] })
2647
+ ] }),
2648
+ !s.current && /* @__PURE__ */ jsx(
2649
+ "button",
2650
+ {
2651
+ className: "pollar-sessions-item-revoke",
2652
+ onClick: () => onRevoke(s.familyId),
2653
+ disabled: isRevoking || signingOutEverywhere,
2654
+ children: isRevoking ? "Revoking\u2026" : "Revoke"
2655
+ }
2656
+ )
2657
+ ] }, s.familyId);
2658
+ })
2659
+ ] }),
2660
+ state.step === "loaded" && sessions.length > 0 && /* @__PURE__ */ jsx("div", { className: "pollar-sessions-actions", children: /* @__PURE__ */ jsx(
2661
+ "button",
2662
+ {
2663
+ className: "pollar-sessions-logout-all",
2664
+ onClick: onLogoutEverywhere,
2665
+ disabled: signingOutEverywhere || otherCount === 0,
2666
+ title: otherCount === 0 ? "No other devices to sign out" : void 0,
2667
+ children: signingOutEverywhere ? "Signing out\u2026" : "Sign out everywhere"
2668
+ }
2669
+ ) }),
2670
+ /* @__PURE__ */ jsx(PollarModalFooter, {})
2671
+ ]
2672
+ }
2673
+ );
2674
+ }
2675
+ function SessionsModal({ onClose }) {
2676
+ const { getClient, styles } = usePollar();
2677
+ const { theme = "light", accentColor = "#005DB4" } = styles;
2678
+ const [state, setState] = useState({ step: "idle" });
2679
+ const [revokingFamilyId, setRevokingFamilyId] = useState(null);
2680
+ const [signingOutEverywhere, setSigningOutEverywhere] = useState(false);
2681
+ const mountedRef = useRef(true);
2682
+ useEffect(() => () => {
2683
+ mountedRef.current = false;
2684
+ }, []);
2685
+ const onCloseRef = useRef(onClose);
2686
+ onCloseRef.current = onClose;
2687
+ useEffect(() => {
2688
+ return getClient().onAuthStateChange((authState) => {
2689
+ if (authState.step === "idle") onCloseRef.current();
2690
+ });
2691
+ }, [getClient]);
2692
+ const load = useCallback(async () => {
2693
+ setState({ step: "loading" });
2694
+ try {
2695
+ const sessions = await getClient().listSessions();
2696
+ if (!mountedRef.current) return;
2697
+ setState({ step: "loaded", sessions });
2698
+ } catch (err) {
2699
+ if (!mountedRef.current) return;
2700
+ const message = err instanceof Error ? err.message : "Failed to load sessions";
2701
+ setState({ step: "error", message });
2702
+ }
2703
+ }, [getClient]);
2704
+ useEffect(() => {
2705
+ void load();
2706
+ }, [load]);
2707
+ const handleRevoke = useCallback(
2708
+ async (familyId) => {
2709
+ setRevokingFamilyId(familyId);
2710
+ try {
2711
+ await getClient().revokeSession(familyId);
2712
+ if (!mountedRef.current) return;
2713
+ await load();
2714
+ } catch {
2715
+ if (!mountedRef.current) return;
2716
+ setState(
2717
+ (prev) => prev.step === "loaded" ? { step: "error", message: "Failed to revoke session" } : prev
2718
+ );
2719
+ } finally {
2720
+ if (mountedRef.current) setRevokingFamilyId(null);
2721
+ }
2722
+ },
2723
+ [getClient, load]
2724
+ );
2725
+ const handleLogoutEverywhere = useCallback(async () => {
2726
+ setSigningOutEverywhere(true);
2727
+ try {
2728
+ await getClient().logoutEverywhere();
2729
+ onClose();
2730
+ } catch {
2731
+ if (!mountedRef.current) return;
2732
+ setSigningOutEverywhere(false);
2733
+ }
2734
+ }, [getClient, onClose]);
2735
+ return /* @__PURE__ */ jsx("div", { className: "pollar-overlay", onClick: onClose, children: /* @__PURE__ */ jsx(
2736
+ SessionsModalTemplate,
2737
+ {
2738
+ theme,
2739
+ accentColor,
2740
+ state,
2741
+ revokingFamilyId,
2742
+ signingOutEverywhere,
2743
+ onRefresh: () => void load(),
2744
+ onRevoke: (familyId) => void handleRevoke(familyId),
2745
+ onLogoutEverywhere: () => void handleLogoutEverywhere(),
2746
+ onClose
2747
+ }
2748
+ ) });
2749
+ }
2502
2750
  function TransactionModalTemplate({
2503
2751
  theme,
2504
2752
  accentColor,
@@ -2940,9 +3188,10 @@ function PollarProvider({ config, styles: propStyles, adapters, children }) {
2940
3188
  const [walletBalanceModalOpen, setWalletBalanceModalOpen] = useState(false);
2941
3189
  const [sendModalOpen, setSendModalOpen] = useState(false);
2942
3190
  const [receiveModalOpen, setReceiveModalOpen] = useState(false);
3191
+ const [sessionsModalOpen, setSessionsModalOpen] = useState(false);
2943
3192
  const adaptersRef = useRef(adapters);
2944
3193
  adaptersRef.current = adapters;
2945
- const walletAddress = sessionState?.data?.providers?.wallet?.address || sessionState?.wallet?.publicKey || "";
3194
+ const walletAddress = sessionState?.wallet?.publicKey || "";
2946
3195
  const getClient = useCallback(() => pollarClient, [pollarClient]);
2947
3196
  const refreshWalletBalance = useCallback(() => pollarClient.refreshBalance(walletAddress), [pollarClient, walletAddress]);
2948
3197
  const contextValue = useMemo(
@@ -2972,6 +3221,8 @@ function PollarProvider({ config, styles: propStyles, adapters, children }) {
2972
3221
  // send / receive
2973
3222
  openSendModal: () => setSendModalOpen(true),
2974
3223
  openReceiveModal: () => setReceiveModalOpen(true),
3224
+ // sessions
3225
+ openSessionsModal: () => setSessionsModalOpen(true),
2975
3226
  // network
2976
3227
  network: networkState.step === "connected" ? networkState.network : "testnet",
2977
3228
  setNetwork: (network) => pollarClient.setNetwork(network),
@@ -3006,7 +3257,8 @@ function PollarProvider({ config, styles: propStyles, adapters, children }) {
3006
3257
  txHistoryModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setTxHistoryModalOpen(false), children: /* @__PURE__ */ jsx(TxHistoryModal, { onClose: () => setTxHistoryModalOpen(false) }) }),
3007
3258
  walletBalanceModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setWalletBalanceModalOpen(false), children: /* @__PURE__ */ jsx(WalletBalanceModal, { onClose: () => setWalletBalanceModalOpen(false) }) }),
3008
3259
  sendModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setSendModalOpen(false), children: /* @__PURE__ */ jsx(SendModal, { onClose: () => setSendModalOpen(false) }) }),
3009
- receiveModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) })
3260
+ receiveModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setReceiveModalOpen(false), children: /* @__PURE__ */ jsx(ReceiveModal, { onClose: () => setReceiveModalOpen(false) }) }),
3261
+ sessionsModalOpen && /* @__PURE__ */ jsx(ModalErrorBoundary, { onClose: () => setSessionsModalOpen(false), children: /* @__PURE__ */ jsx(SessionsModal, { onClose: () => setSessionsModalOpen(false) }) })
3010
3262
  ] });
3011
3263
  }
3012
3264
  function usePollar() {
@@ -3257,6 +3509,6 @@ function WalletButton() {
3257
3509
  );
3258
3510
  }
3259
3511
 
3260
- export { KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RouteDisplay, SendModal, SendModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, createPollarAdapterHook, usePollar };
3512
+ export { KycModal, KycModalTemplate, KycStatus, LoginModalTemplate, PollarProvider, RampWidget, RampWidgetTemplate, ReceiveModal, ReceiveModalTemplate, RouteDisplay, SendModal, SendModalTemplate, SessionsModal, SessionsModalTemplate, TransactionModalTemplate, TxHistoryModalTemplate, TxStatusView, WalletBalanceModal, WalletBalanceModalTemplate, WalletButton, createPollarAdapterHook, usePollar };
3261
3513
  //# sourceMappingURL=index.mjs.map
3262
3514
  //# sourceMappingURL=index.mjs.map