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