@livo-build/kit 0.1.1 → 0.1.6
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/auth/SignInWithEthereum.d.ts +8 -0
- package/dist/auth/SignInWithEthereum.js +18 -0
- package/dist/auth/index.d.ts +2 -0
- package/dist/auth/index.js +2 -0
- package/dist/auth/useSiwe.d.ts +18 -0
- package/dist/auth/useSiwe.js +72 -0
- package/dist/data/DataTable.d.ts +26 -0
- package/dist/data/DataTable.js +15 -0
- package/dist/data/data.css +22 -0
- package/dist/data/index.d.ts +4 -0
- package/dist/data/index.js +4 -0
- package/dist/data/useCollection.d.ts +42 -0
- package/dist/data/useCollection.js +67 -0
- package/dist/data/useCollection.test.d.ts +1 -0
- package/dist/data/useCollection.test.js +27 -0
- package/dist/data/useEventSource.d.ts +15 -0
- package/dist/data/useEventSource.js +37 -0
- package/dist/data/useWebSocket.d.ts +17 -0
- package/dist/data/useWebSocket.js +61 -0
- package/dist/dataviz/Stat.d.ts +18 -0
- package/dist/dataviz/Stat.js +14 -0
- package/dist/dataviz/dataviz.css +15 -0
- package/dist/dataviz/index.d.ts +2 -0
- package/dist/dataviz/index.js +2 -0
- package/dist/dataviz/sparkline.d.ts +15 -0
- package/dist/dataviz/sparkline.js +31 -0
- package/dist/dataviz/sparkline.test.d.ts +1 -0
- package/dist/dataviz/sparkline.test.js +14 -0
- package/dist/format.d.ts +3 -0
- package/dist/format.js +9 -0
- package/dist/hooks/index.d.ts +12 -0
- package/dist/hooks/index.js +30 -0
- package/dist/hyperliquid/PriceTicker.d.ts +11 -0
- package/dist/hyperliquid/PriceTicker.js +24 -0
- package/dist/hyperliquid/client.d.ts +9 -0
- package/dist/hyperliquid/client.js +17 -0
- package/dist/hyperliquid/hl.css +10 -0
- package/dist/hyperliquid/index.d.ts +3 -0
- package/dist/hyperliquid/index.js +3 -0
- package/dist/hyperliquid/useHyperliquid.d.ts +76 -0
- package/dist/hyperliquid/useHyperliquid.js +52 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +18 -7
- package/dist/nft/MintButton.d.ts +20 -0
- package/dist/nft/MintButton.js +9 -0
- package/dist/nft/NFTCard.d.ts +15 -0
- package/dist/nft/NFTCard.js +14 -0
- package/dist/nft/NFTMedia.d.ts +11 -0
- package/dist/nft/NFTMedia.js +16 -0
- package/dist/nft/index.d.ts +5 -0
- package/dist/nft/index.js +5 -0
- package/dist/nft/nft.css +16 -0
- package/dist/nft/resolveUri.d.ts +1 -0
- package/dist/nft/resolveUri.js +15 -0
- package/dist/nft/resolveUri.test.d.ts +1 -0
- package/dist/nft/resolveUri.test.js +13 -0
- package/dist/nft/useNFT.d.ts +34 -0
- package/dist/nft/useNFT.js +65 -0
- package/dist/telegram/index.d.ts +2 -0
- package/dist/telegram/index.js +2 -0
- package/dist/telegram/miniapp.d.ts +15 -0
- package/dist/telegram/miniapp.js +63 -0
- package/dist/telegram/useTelegramTheme.d.ts +2 -0
- package/dist/telegram/useTelegramTheme.js +20 -0
- package/dist/tx/TxProgress.d.ts +13 -0
- package/dist/tx/TxProgress.js +22 -0
- package/dist/tx/index.d.ts +1 -0
- package/dist/tx/index.js +1 -0
- package/dist/tx/tx.css +9 -0
- package/dist/ui/index.d.ts +1 -1
- package/dist/ui/index.js +1 -1
- package/dist/ui/ui.css +7 -0
- package/dist/ui/ui.d.ts +13 -0
- package/dist/ui/ui.js +24 -0
- package/dist/user/UserMenu.d.ts +10 -0
- package/dist/user/UserMenu.js +18 -0
- package/dist/user/index.d.ts +2 -0
- package/dist/user/index.js +2 -0
- package/dist/user/useUser.d.ts +32 -0
- package/dist/user/useUser.js +36 -0
- package/dist/user/user.css +15 -0
- package/dist/web3/TokenAmount.d.ts +16 -0
- package/dist/web3/TokenAmount.js +17 -0
- package/dist/web3/index.d.ts +1 -0
- package/dist/web3/index.js +1 -0
- package/dist/web3/web3.css +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export interface MainButtonOptions {
|
|
2
|
+
text: string;
|
|
3
|
+
onClick: () => void;
|
|
4
|
+
show?: boolean;
|
|
5
|
+
enabled?: boolean;
|
|
6
|
+
progress?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function useTelegramMainButton(opts: MainButtonOptions): void;
|
|
9
|
+
export declare function useTelegramBackButton(onClick: () => void, show?: boolean): void;
|
|
10
|
+
export interface Haptics {
|
|
11
|
+
impact: (style?: "light" | "medium" | "heavy" | "rigid" | "soft") => void;
|
|
12
|
+
notification: (type?: "error" | "success" | "warning") => void;
|
|
13
|
+
selection: () => void;
|
|
14
|
+
}
|
|
15
|
+
export declare function useTelegramHaptics(): Haptics;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
function webApp() {
|
|
3
|
+
if (typeof window === "undefined")
|
|
4
|
+
return undefined;
|
|
5
|
+
return window.Telegram?.WebApp;
|
|
6
|
+
}
|
|
7
|
+
// Drive the Telegram Mini App MainButton from React. No-op outside Telegram.
|
|
8
|
+
export function useTelegramMainButton(opts) {
|
|
9
|
+
const cbRef = useRef(opts.onClick);
|
|
10
|
+
cbRef.current = opts.onClick;
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
const mb = webApp()?.MainButton;
|
|
13
|
+
if (!mb)
|
|
14
|
+
return;
|
|
15
|
+
const handler = () => cbRef.current();
|
|
16
|
+
mb.setText(opts.text);
|
|
17
|
+
if (opts.enabled === false)
|
|
18
|
+
mb.disable();
|
|
19
|
+
else
|
|
20
|
+
mb.enable();
|
|
21
|
+
if (opts.progress)
|
|
22
|
+
mb.showProgress?.();
|
|
23
|
+
else
|
|
24
|
+
mb.hideProgress?.();
|
|
25
|
+
if (opts.show === false)
|
|
26
|
+
mb.hide();
|
|
27
|
+
else
|
|
28
|
+
mb.show();
|
|
29
|
+
mb.onClick(handler);
|
|
30
|
+
return () => {
|
|
31
|
+
mb.offClick(handler);
|
|
32
|
+
mb.hide();
|
|
33
|
+
};
|
|
34
|
+
}, [opts.text, opts.show, opts.enabled, opts.progress]);
|
|
35
|
+
}
|
|
36
|
+
// Drive the Telegram Mini App BackButton. No-op outside Telegram.
|
|
37
|
+
export function useTelegramBackButton(onClick, show = true) {
|
|
38
|
+
const cbRef = useRef(onClick);
|
|
39
|
+
cbRef.current = onClick;
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const bb = webApp()?.BackButton;
|
|
42
|
+
if (!bb)
|
|
43
|
+
return;
|
|
44
|
+
const handler = () => cbRef.current();
|
|
45
|
+
bb.onClick(handler);
|
|
46
|
+
if (show)
|
|
47
|
+
bb.show();
|
|
48
|
+
else
|
|
49
|
+
bb.hide();
|
|
50
|
+
return () => {
|
|
51
|
+
bb.offClick(handler);
|
|
52
|
+
bb.hide();
|
|
53
|
+
};
|
|
54
|
+
}, [show]);
|
|
55
|
+
}
|
|
56
|
+
// Telegram haptic feedback helpers. Safely no-op outside Telegram.
|
|
57
|
+
export function useTelegramHaptics() {
|
|
58
|
+
return {
|
|
59
|
+
impact: (style = "medium") => webApp()?.HapticFeedback?.impactOccurred(style),
|
|
60
|
+
notification: (type = "success") => webApp()?.HapticFeedback?.notificationOccurred(type),
|
|
61
|
+
selection: () => webApp()?.HapticFeedback?.selectionChanged(),
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {} from "../theme";
|
|
2
|
+
// A KitTheme derived from the host Telegram Mini App's theme, or undefined outside
|
|
3
|
+
// Telegram. Pass it to <LivoApp theme={…}> / <KitThemeProvider> so the kit matches
|
|
4
|
+
// the user's Telegram colours automatically.
|
|
5
|
+
export function useTelegramTheme() {
|
|
6
|
+
if (typeof window === "undefined")
|
|
7
|
+
return undefined;
|
|
8
|
+
const wa = window.Telegram?.WebApp;
|
|
9
|
+
const tp = wa?.themeParams;
|
|
10
|
+
if (!tp)
|
|
11
|
+
return undefined;
|
|
12
|
+
return {
|
|
13
|
+
accent: tp.button_color,
|
|
14
|
+
accentFg: tp.button_text_color,
|
|
15
|
+
bg: tp.bg_color,
|
|
16
|
+
fg: tp.text_color,
|
|
17
|
+
muted: tp.hint_color,
|
|
18
|
+
border: tp.secondary_bg_color,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import "./tx.css";
|
|
2
|
+
import { type TxStatus } from "./useTx";
|
|
3
|
+
export interface TxProgressProps {
|
|
4
|
+
status: TxStatus;
|
|
5
|
+
hash?: `0x${string}`;
|
|
6
|
+
error?: Error | null;
|
|
7
|
+
/** Block explorer base, e.g. "https://sepolia.etherscan.io". Links the hash when set. */
|
|
8
|
+
explorer?: string;
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Hide when idle (default true). */
|
|
11
|
+
hideIdle?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function TxProgress({ status, hash, error, explorer, className, hideIdle }: TxProgressProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "./tx.css";
|
|
3
|
+
import {} from "./useTx";
|
|
4
|
+
import { shortHash } from "../format";
|
|
5
|
+
import { cx } from "../util";
|
|
6
|
+
const LABELS = {
|
|
7
|
+
idle: "",
|
|
8
|
+
pending: "Confirm in your wallet…",
|
|
9
|
+
confirming: "Transaction submitted, confirming…",
|
|
10
|
+
success: "Confirmed",
|
|
11
|
+
error: "Failed",
|
|
12
|
+
};
|
|
13
|
+
// A neutral inline status line for a transaction lifecycle (pairs with useTx). Style
|
|
14
|
+
// it via the .kit-txp* classes — see STYLING.md.
|
|
15
|
+
export function TxProgress({ status, hash, error, explorer, className, hideIdle = true }) {
|
|
16
|
+
if (status === "idle" && hideIdle)
|
|
17
|
+
return null;
|
|
18
|
+
const label = status === "error" ? error?.message ?? LABELS.error : LABELS[status];
|
|
19
|
+
const busy = status === "pending" || status === "confirming";
|
|
20
|
+
return (_jsxs("div", { className: cx("kit-txp", `kit-txp-${status}`, className), role: "status", "aria-live": "polite", children: [busy && _jsx("span", { className: "kit-txp-spinner", "aria-hidden": "true" }), _jsx("span", { className: "kit-txp-label", children: label }), hash &&
|
|
21
|
+
(explorer ? (_jsx("a", { className: "kit-txp-hash", href: `${explorer}/tx/${hash}`, target: "_blank", rel: "noreferrer", children: shortHash(hash) })) : (_jsx("span", { className: "kit-txp-hash", children: shortHash(hash) })))] }));
|
|
22
|
+
}
|
package/dist/tx/index.d.ts
CHANGED
package/dist/tx/index.js
CHANGED
package/dist/tx/tx.css
CHANGED
|
@@ -11,3 +11,12 @@
|
|
|
11
11
|
.kit-btn:hover:not(:disabled){opacity:.9}
|
|
12
12
|
.kit-btn:active:not(:disabled){transform:translateY(1px)}
|
|
13
13
|
@media (prefers-color-scheme:dark){.kit-btn{background:var(--kit-accent,#f5f5f7);color:var(--kit-accent-fg,#111827)}}
|
|
14
|
+
|
|
15
|
+
/* Transaction status line (TxProgress). Neutral; restyle via these classes. */
|
|
16
|
+
.kit-txp{display:inline-flex;align-items:center;gap:8px;font:inherit;font-size:13px;color:var(--kit-muted,#6b7280)}
|
|
17
|
+
.kit-txp-success{color:var(--kit-success,#15803d)}
|
|
18
|
+
.kit-txp-error{color:var(--kit-danger,#b91c1c)}
|
|
19
|
+
.kit-txp-hash{color:var(--kit-accent,#2563eb);text-decoration:none;font-variant-numeric:tabular-nums}
|
|
20
|
+
.kit-txp-hash:hover{text-decoration:underline}
|
|
21
|
+
.kit-txp-spinner{width:12px;height:12px;border-radius:50%;border:2px solid currentColor;border-top-color:transparent;animation:kit-txp-spin .7s linear infinite}
|
|
22
|
+
@keyframes kit-txp-spin{to{transform:rotate(360deg)}}
|
package/dist/ui/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Dialog, Card, Skeleton, Spinner, EmptyState, CopyButton, Button, Badge, type DialogProps, type SkeletonProps, type EmptyStateProps, type CopyButtonProps, type ButtonProps, } from "./ui";
|
|
1
|
+
export { Dialog, Card, Skeleton, Spinner, EmptyState, CopyButton, Button, Badge, Countdown, type DialogProps, type SkeletonProps, type EmptyStateProps, type CopyButtonProps, type ButtonProps, type CountdownProps, } from "./ui";
|
package/dist/ui/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Dialog, Card, Skeleton, Spinner, EmptyState, CopyButton, Button, Badge, } from "./ui";
|
|
1
|
+
export { Dialog, Card, Skeleton, Spinner, EmptyState, CopyButton, Button, Badge, Countdown, } from "./ui";
|
package/dist/ui/ui.css
CHANGED
|
@@ -32,3 +32,10 @@
|
|
|
32
32
|
@media (max-width:480px){.kit-dialog-root{align-items:flex-end}.kit-dialog{width:100%;max-width:100%;border-radius:16px 16px 0 0;animation:kit-up .22s cubic-bezier(.2,.9,.25,1)}}
|
|
33
33
|
@keyframes kit-up{from{transform:translateY(100%)}to{transform:none}}
|
|
34
34
|
@media (prefers-reduced-motion:reduce){.kit-dialog,.kit-dialog-overlay,.kit-skeleton,.kit-spinner{animation:none}}
|
|
35
|
+
|
|
36
|
+
/* Countdown — neutral inline D:H:M:S. Restyle via these classes. */
|
|
37
|
+
.kit-countdown{display:inline-flex;gap:10px;font:inherit;font-variant-numeric:tabular-nums}
|
|
38
|
+
.kit-countdown-unit{display:inline-flex;flex-direction:column;align-items:center;line-height:1.1}
|
|
39
|
+
.kit-countdown-num{font-weight:700;font-size:20px;color:var(--kit-fg,#111827)}
|
|
40
|
+
.kit-countdown-label{font-size:11px;color:var(--kit-muted,#6b7280);text-transform:uppercase;letter-spacing:.04em}
|
|
41
|
+
@media (prefers-color-scheme:dark){.kit-countdown-num{color:var(--kit-fg,#f5f5f7)}}
|
package/dist/ui/ui.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import "./ui.css";
|
|
2
2
|
import { type ReactNode, type CSSProperties, type HTMLAttributes, type ButtonHTMLAttributes } from "react";
|
|
3
|
+
import { type CountdownState } from "../hooks";
|
|
3
4
|
export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
4
5
|
variant?: "primary" | "secondary" | "ghost";
|
|
5
6
|
}
|
|
@@ -43,3 +44,15 @@ export interface CopyButtonProps {
|
|
|
43
44
|
className?: string;
|
|
44
45
|
}
|
|
45
46
|
export declare function CopyButton({ value, children, copiedLabel, className }: CopyButtonProps): import("react").JSX.Element;
|
|
47
|
+
export interface CountdownProps {
|
|
48
|
+
/** Target time — Date, unix seconds, or ms. */
|
|
49
|
+
to: Date | number;
|
|
50
|
+
/** Hide leading zero days/hours. Default false (always D:H:M:S). */
|
|
51
|
+
trim?: boolean;
|
|
52
|
+
/** Rendered when the countdown reaches zero. */
|
|
53
|
+
whenDone?: ReactNode;
|
|
54
|
+
className?: string;
|
|
55
|
+
/** Custom render given the live countdown parts. */
|
|
56
|
+
children?: (c: CountdownState) => ReactNode;
|
|
57
|
+
}
|
|
58
|
+
export declare function Countdown({ to, trim, whenDone, className, children }: CountdownProps): import("react").JSX.Element;
|
package/dist/ui/ui.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import "./ui.css";
|
|
3
3
|
import { useEffect, useRef, useState, } from "react";
|
|
4
4
|
import { cx } from "../util";
|
|
5
|
+
import { useCountdown } from "../hooks";
|
|
5
6
|
export function Button({ variant = "secondary", className, ...rest }) {
|
|
6
7
|
return _jsx("button", { className: cx("kit-button", "kit-button-" + variant, className), ...rest });
|
|
7
8
|
}
|
|
@@ -61,3 +62,26 @@ export function CopyButton({ value, children, copiedLabel = "Copied", className
|
|
|
61
62
|
}
|
|
62
63
|
}, children: copied ? copiedLabel : (children ?? "Copy") }));
|
|
63
64
|
}
|
|
65
|
+
const pad = (n) => String(n).padStart(2, "0");
|
|
66
|
+
// Live D:H:M:S countdown to a target time (e.g. a mint/sale window). Neutral —
|
|
67
|
+
// style via .kit-countdown*, or pass `children` for a custom layout.
|
|
68
|
+
export function Countdown({ to, trim = false, whenDone, className, children }) {
|
|
69
|
+
const c = useCountdown(to);
|
|
70
|
+
if (children)
|
|
71
|
+
return _jsx("span", { className: cx("kit-countdown", className), children: children(c) });
|
|
72
|
+
if (c.done && whenDone !== undefined)
|
|
73
|
+
return _jsx("span", { className: cx("kit-countdown", className), children: whenDone });
|
|
74
|
+
const parts = [
|
|
75
|
+
[c.days, "d"],
|
|
76
|
+
[c.hours, "h"],
|
|
77
|
+
[c.minutes, "m"],
|
|
78
|
+
[c.seconds, "s"],
|
|
79
|
+
];
|
|
80
|
+
let started = !trim;
|
|
81
|
+
return (_jsx("span", { className: cx("kit-countdown", className), "data-done": c.done || undefined, role: "timer", children: parts.map(([v, label], i) => {
|
|
82
|
+
if (!started && v === 0 && i < 3)
|
|
83
|
+
return null; // trim leading zero units (never the seconds)
|
|
84
|
+
started = true;
|
|
85
|
+
return (_jsxs("span", { className: "kit-countdown-unit", children: [_jsx("span", { className: "kit-countdown-num", children: pad(v) }), _jsx("span", { className: "kit-countdown-label", children: label })] }, label));
|
|
86
|
+
}) }));
|
|
87
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import "./user.css";
|
|
2
|
+
import { type UseUserOptions } from "./useUser";
|
|
3
|
+
export interface UserMenuProps extends UseUserOptions {
|
|
4
|
+
/** Show the Telegram link control in the menu. Default true. */
|
|
5
|
+
telegram?: boolean;
|
|
6
|
+
/** Show the Sign-In With Ethereum control. Default true. */
|
|
7
|
+
auth?: boolean;
|
|
8
|
+
className?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function UserMenu({ telegram, auth, className, ...opts }: UserMenuProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import "./user.css";
|
|
3
|
+
import { useState } from "react";
|
|
4
|
+
import { useUser } from "./useUser";
|
|
5
|
+
import { Identity } from "../account/Identity";
|
|
6
|
+
import { ConnectWallet } from "../wallet";
|
|
7
|
+
import { LinkTelegramButton } from "../telegram/LinkTelegramButton";
|
|
8
|
+
import { cx } from "../util";
|
|
9
|
+
// A compact account control: a wallet-connect button when disconnected, otherwise
|
|
10
|
+
// an identity pill that opens a menu with SIWE sign-in/out + Telegram linking +
|
|
11
|
+
// disconnect. Neutral — style via .kit-user*. The one-stop user-management widget.
|
|
12
|
+
export function UserMenu({ telegram = true, auth = true, className, ...opts }) {
|
|
13
|
+
const user = useUser(opts);
|
|
14
|
+
const [open, setOpen] = useState(false);
|
|
15
|
+
if (!user.isConnected || !user.address)
|
|
16
|
+
return _jsx(ConnectWallet, { className: className });
|
|
17
|
+
return (_jsxs("div", { className: cx("kit-user", className), "data-open": open || undefined, children: [_jsx("button", { className: "kit-user-trigger", onClick: () => setOpen((o) => !o), children: _jsx(Identity, { address: user.address, size: 24 }) }), open && (_jsxs(_Fragment, { children: [_jsx("div", { className: "kit-user-scrim", onClick: () => setOpen(false) }), _jsxs("div", { className: "kit-user-panel", role: "menu", children: [auth && (_jsxs("div", { className: "kit-user-row", children: [_jsx("span", { className: "kit-user-label", children: "Session" }), user.isSignedIn ? (_jsx("span", { className: "kit-user-action", "data-state": "on", children: "Signed in \u2713" })) : (_jsx("button", { className: "kit-user-action", onClick: () => void user.signIn(), children: "Sign in" }))] })), telegram && (_jsxs("div", { className: "kit-user-row", children: [_jsx("span", { className: "kit-user-label", children: "Telegram" }), _jsx(LinkTelegramButton, { endpoint: opts.telegramEndpoint })] })), _jsx("button", { className: "kit-user-disconnect", onClick: () => void user.signOut(), children: "Disconnect" })] })] }))] }));
|
|
18
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type SiweUser } from "../auth/useSiwe";
|
|
2
|
+
import { type LinkedTelegram } from "../telegram/useTelegramLink";
|
|
3
|
+
export interface UseUserOptions {
|
|
4
|
+
/** SIWE auth API base (default "/api/auth"). */
|
|
5
|
+
authEndpoint?: string;
|
|
6
|
+
/** Telegram link API base (default "/api/telegram"). */
|
|
7
|
+
telegramEndpoint?: string;
|
|
8
|
+
/** Skip the SIWE session check (wallet-only identity). Default false. */
|
|
9
|
+
noAuth?: boolean;
|
|
10
|
+
/** Skip the Telegram link check. Default false. */
|
|
11
|
+
noTelegram?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface KitUser {
|
|
14
|
+
/** Connected wallet address (undefined if no wallet). */
|
|
15
|
+
address?: `0x${string}`;
|
|
16
|
+
isConnected: boolean;
|
|
17
|
+
/** Server-verified SIWE session user (undefined until signed in). */
|
|
18
|
+
session?: SiweUser;
|
|
19
|
+
isSignedIn: boolean;
|
|
20
|
+
/** Linked Telegram identity (undefined if not linked). */
|
|
21
|
+
telegram?: LinkedTelegram;
|
|
22
|
+
isTelegramLinked: boolean;
|
|
23
|
+
/** True while any underlying identity check is still loading. */
|
|
24
|
+
isLoading: boolean;
|
|
25
|
+
/** Run the SIWE sign-in flow. */
|
|
26
|
+
signIn: () => Promise<void>;
|
|
27
|
+
/** Sign out of the session AND disconnect the wallet. */
|
|
28
|
+
signOut: () => Promise<void>;
|
|
29
|
+
/** Re-check session + telegram link. */
|
|
30
|
+
refresh: () => void;
|
|
31
|
+
}
|
|
32
|
+
export declare function useUser(opts?: UseUserOptions): KitUser;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useConnection, useDisconnect } from "wagmi";
|
|
2
|
+
import { useSiwe } from "../auth/useSiwe";
|
|
3
|
+
import { useTelegramLink } from "../telegram/useTelegramLink";
|
|
4
|
+
// One hook for "who is the current user", unifying the three identity surfaces a
|
|
5
|
+
// Livo app has: the connected wallet, the SIWE session (your /api/auth), and the
|
|
6
|
+
// linked Telegram account (your /api/telegram). The backbone of user management for
|
|
7
|
+
// Telegram-connected apps.
|
|
8
|
+
export function useUser(opts = {}) {
|
|
9
|
+
const { address, isConnected } = useConnection();
|
|
10
|
+
const { disconnect } = useDisconnect();
|
|
11
|
+
const siwe = useSiwe({ endpoint: opts.authEndpoint });
|
|
12
|
+
const tg = useTelegramLink({ endpoint: opts.telegramEndpoint });
|
|
13
|
+
const useAuth = !opts.noAuth;
|
|
14
|
+
const useTg = !opts.noTelegram;
|
|
15
|
+
return {
|
|
16
|
+
address,
|
|
17
|
+
isConnected: Boolean(isConnected),
|
|
18
|
+
session: useAuth ? siwe.user : undefined,
|
|
19
|
+
isSignedIn: useAuth ? siwe.status === "signed-in" : Boolean(isConnected),
|
|
20
|
+
telegram: useTg ? tg.telegram : undefined,
|
|
21
|
+
isTelegramLinked: useTg ? tg.status === "linked" : false,
|
|
22
|
+
isLoading: (useAuth && siwe.status === "loading") || (useTg && tg.status === "loading"),
|
|
23
|
+
signIn: siwe.signIn,
|
|
24
|
+
signOut: async () => {
|
|
25
|
+
if (useAuth)
|
|
26
|
+
await siwe.signOut();
|
|
27
|
+
disconnect();
|
|
28
|
+
},
|
|
29
|
+
refresh: () => {
|
|
30
|
+
if (useAuth)
|
|
31
|
+
siwe.refresh();
|
|
32
|
+
if (useTg)
|
|
33
|
+
tg.refresh();
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* UserMenu — neutral account control + dropdown. Restyle via .kit-user*. */
|
|
2
|
+
.kit-user{position:relative;display:inline-block;font:inherit}
|
|
3
|
+
.kit-user-trigger{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--kit-border,#e5e7eb);background:transparent;color:inherit;border-radius:999px;padding:4px 12px 4px 4px;font:inherit;cursor:pointer}
|
|
4
|
+
.kit-user-trigger:hover{border-color:var(--kit-muted,#9ca3af)}
|
|
5
|
+
.kit-user-scrim{position:fixed;inset:0;z-index:40}
|
|
6
|
+
.kit-user-panel{position:absolute;right:0;top:calc(100% + 8px);z-index:41;min-width:240px;display:flex;flex-direction:column;gap:4px;padding:10px;border:1px solid var(--kit-border,#e5e7eb);border-radius:14px;background:var(--kit-bg,#fff);box-shadow:0 12px 32px rgba(0,0,0,.12)}
|
|
7
|
+
.kit-user-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:6px 6px}
|
|
8
|
+
.kit-user-label{color:var(--kit-muted,#6b7280);font-size:13px}
|
|
9
|
+
.kit-user-action{font:inherit;font-size:13px;font-weight:600;color:var(--kit-accent,#111827);background:transparent;border:0;cursor:pointer;padding:0}
|
|
10
|
+
.kit-user-action[data-state="on"]{color:var(--kit-up,#15803d);cursor:default}
|
|
11
|
+
.kit-user-disconnect{margin-top:4px;border:0;border-top:1px solid var(--kit-border,#eceef1);background:transparent;color:var(--kit-down,#b91c1c);font:inherit;font-size:13px;text-align:left;padding:10px 6px 4px;cursor:pointer}
|
|
12
|
+
@media (prefers-color-scheme:dark){
|
|
13
|
+
.kit-user-panel{--kit-border:#2c2e36;--kit-bg:#15171c}
|
|
14
|
+
.kit-user-trigger{--kit-border:#2c2e36}
|
|
15
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import "./web3.css";
|
|
2
|
+
import { type ReactNode } from "react";
|
|
3
|
+
export interface TokenAmountProps {
|
|
4
|
+
/** Base-unit amount (e.g. wei). */
|
|
5
|
+
amount: bigint;
|
|
6
|
+
decimals?: number;
|
|
7
|
+
symbol?: string;
|
|
8
|
+
/** Max fractional digits to show (default 6). */
|
|
9
|
+
maxFractionDigits?: number;
|
|
10
|
+
/** USD price per whole token — when set, renders a secondary USD value. */
|
|
11
|
+
usdPrice?: number;
|
|
12
|
+
className?: string;
|
|
13
|
+
/** Render the symbol before the number (e.g. for "$"). */
|
|
14
|
+
symbolFirst?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function TokenAmount({ amount, decimals, symbol, maxFractionDigits, usdPrice, className, symbolFirst, }: TokenAmountProps): ReactNode;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import "./web3.css";
|
|
3
|
+
import {} from "react";
|
|
4
|
+
import { formatToken, formatUsd } from "../format";
|
|
5
|
+
import { cx } from "../util";
|
|
6
|
+
// Correctly-formatted on-chain amount display (handles BigInt + decimals). Neutral
|
|
7
|
+
// markup — style via .kit-amt* classes.
|
|
8
|
+
export function TokenAmount({ amount, decimals = 18, symbol, maxFractionDigits = 6, usdPrice, className, symbolFirst, }) {
|
|
9
|
+
const formatted = formatToken(amount, decimals, maxFractionDigits);
|
|
10
|
+
const sym = symbol ? _jsx("span", { className: "kit-amt-sym", children: symbol }) : null;
|
|
11
|
+
let usd = null;
|
|
12
|
+
if (usdPrice != null) {
|
|
13
|
+
const whole = Number(formatToken(amount, decimals, 18));
|
|
14
|
+
usd = _jsx("span", { className: "kit-amt-usd", children: formatUsd(whole * usdPrice) });
|
|
15
|
+
}
|
|
16
|
+
return (_jsxs("span", { className: cx("kit-amt", className), children: [symbolFirst && sym, _jsx("span", { className: "kit-amt-num", children: formatted }), !symbolFirst && sym, usd] }));
|
|
17
|
+
}
|
package/dist/web3/index.d.ts
CHANGED
|
@@ -4,3 +4,4 @@ export { NetworkGuard, type NetworkGuardProps } from "./NetworkGuard";
|
|
|
4
4
|
export { TokenAmountInput, type TokenAmountInputProps } from "./TokenAmountInput";
|
|
5
5
|
export { ChainSwitcher, type ChainSwitcherProps } from "./ChainSwitcher";
|
|
6
6
|
export { AddressInput, type AddressInputProps } from "./AddressInput";
|
|
7
|
+
export { TokenAmount, type TokenAmountProps } from "./TokenAmount";
|
package/dist/web3/index.js
CHANGED
package/dist/web3/web3.css
CHANGED
|
@@ -19,4 +19,8 @@
|
|
|
19
19
|
.kit-addr-input input{flex:1;border:0;outline:0;background:transparent;color:inherit;font:inherit;font-size:15px;min-width:0;font-variant-numeric:tabular-nums}
|
|
20
20
|
.kit-addr-input-resolved{color:var(--kit-muted,#6b7280);font-size:13px;font-variant-numeric:tabular-nums}
|
|
21
21
|
.kit-async-error{color:var(--kit-error,#dc2626);font:inherit;font-size:14px}
|
|
22
|
+
.kit-amt{display:inline-flex;align-items:baseline;gap:4px;font:inherit;font-variant-numeric:tabular-nums}
|
|
23
|
+
.kit-amt-num{font-weight:600}
|
|
24
|
+
.kit-amt-sym{color:var(--kit-muted,#6b7280)}
|
|
25
|
+
.kit-amt-usd{color:var(--kit-muted,#6b7280);font-size:.85em}
|
|
22
26
|
@media (prefers-color-scheme:dark){.kit-net,.kit-amount,.kit-chain,.kit-addr-input{--kit-border:#2c2e36}}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livo-build/kit",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Livo frontend kit — reusable React + wagmi v3 building blocks (wallet connect modal, providers, hooks) for web3 apps built on Livo.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "UNLICENSED",
|