About.tsx raw
1 import { ExternalLinkIcon } from "lucide-react";
2 import ExternalLink from "src/components/ExternalLink";
3 import Loading from "src/components/Loading";
4 import SettingsHeader from "src/components/SettingsHeader";
5 import { Badge } from "src/components/ui/badge";
6 import { useAlbyMe } from "src/hooks/useAlbyMe";
7 import { useNodeDetails } from "src/hooks/useNodeDetails";
8 import { backendTypeConfigs } from "src/lib/backendType";
9
10 import { useInfo } from "src/hooks/useInfo";
11
12 export function About() {
13 const { data: info } = useInfo();
14 const { data: albyMe, error: albyMeError } = useAlbyMe();
15 const lsps2Source = info?.jitChannelsLiquiditySource;
16 const lsps2Pubkey = lsps2Source?.includes("@")
17 ? lsps2Source.split("@")[0]
18 : undefined;
19 const { data: lsps2NodeDetails } = useNodeDetails(lsps2Pubkey);
20 const lsps2Label =
21 lsps2NodeDetails?.alias ||
22 (lsps2Pubkey ? lsps2Pubkey.slice(0, 8) + "..." : lsps2Source);
23 const lsps2MinPaymentSizeSat = info?.jitChannelsMinPaymentSizeMsat
24 ? Math.ceil(info.jitChannelsMinPaymentSizeMsat / 1000)
25 : undefined;
26 const lsps2MaxPaymentSizeSat = info?.jitChannelsMaxPaymentSizeMsat
27 ? Math.floor(info.jitChannelsMaxPaymentSizeMsat / 1000)
28 : undefined;
29
30 if (!info || (info.albyAccountConnected && !albyMe && !albyMeError)) {
31 return <Loading />;
32 }
33
34 const planCode = albyMe?.subscription.plan_code;
35 let planName: string;
36 if (planCode?.startsWith("buzz_")) {
37 planName = "Pro Cloud";
38 } else if (planCode?.startsWith("pro_")) {
39 planName = "Pro";
40 } else if (planCode) {
41 planName = "Paid";
42 } else {
43 planName = "Free";
44 }
45
46 return (
47 <>
48 <SettingsHeader
49 pageTitle="About"
50 title="About"
51 description="Info about your Alby Hub"
52 />
53 <div className="grid gap-4">
54 <div className="grid gap-2">
55 <p className="font-medium text-sm">Alby Hub Version</p>
56 <p className="text-muted-foreground text-sm slashed-zero">
57 {info.version || "dev"}
58 </p>
59 </div>
60 <div className="grid gap-2">
61 <p className="font-medium text-sm">Lightning Node Backend</p>
62 <div className="flex items-center gap-2 text-muted-foreground text-sm">
63 <div className="h-6 w-6">
64 {backendTypeConfigs[info.backendType].icon}
65 </div>
66 <p>{backendTypeConfigs[info.backendType].title}</p>
67 </div>
68 </div>
69 {info.databaseType && (
70 <div className="grid gap-2">
71 <p className="font-medium text-sm">Database Storage</p>
72 <p className="text-muted-foreground text-sm">
73 {info.databaseType === "postgres"
74 ? "PostgreSQL"
75 : info.databaseType === "sqlite"
76 ? "SQLite"
77 : info.databaseType}
78 </p>
79 </div>
80 )}
81 {info.backendType === "LDK" && (
82 <div className="grid gap-2">
83 <p className="font-medium text-sm">VSS</p>
84 <p className="text-muted-foreground text-sm break-all">
85 {info.ldkVssEnabled ? `Enabled (${info.ldkVssUrl})` : "Disabled"}
86 </p>
87 </div>
88 )}
89 {info.chainDataSourceType && (
90 <div className="grid gap-2">
91 <p className="font-medium text-sm">Chain Data Source</p>
92 <div className="flex flex-col gap-1 text-sm text-muted-foreground">
93 <p className="capitalize">{info.chainDataSourceType}</p>
94 {info.chainDataSourceAddress && (
95 <p className="break-all">{info.chainDataSourceAddress}</p>
96 )}
97 </div>
98 </div>
99 )}
100 {info.jitChannelsLiquiditySource && (
101 <div className="grid gap-2">
102 <p className="font-medium text-sm">
103 Just-in-Time channels Liquidity Source LSPS2
104 </p>
105 <div className="flex flex-col gap-1 text-sm text-muted-foreground">
106 {lsps2Pubkey ? (
107 <ExternalLink
108 to={`${info.mempoolUrl}/lightning/node/${lsps2Pubkey}`}
109 className="inline-flex items-center gap-1 underline w-fit"
110 >
111 {lsps2Label}
112 <ExternalLinkIcon className="size-4" />
113 </ExternalLink>
114 ) : (
115 <p>{lsps2Label}</p>
116 )}
117 <p className="break-all">{info.jitChannelsLiquiditySource}</p>
118 {(lsps2MinPaymentSizeSat || lsps2MaxPaymentSizeSat) && (
119 <p>
120 JIT payment size:{" "}
121 {lsps2MinPaymentSizeSat
122 ? new Intl.NumberFormat().format(lsps2MinPaymentSizeSat)
123 : "?"}
124 {" - "}
125 {lsps2MaxPaymentSizeSat
126 ? new Intl.NumberFormat().format(lsps2MaxPaymentSizeSat)
127 : "?"}{" "}
128 sats
129 </p>
130 )}
131 </div>
132 </div>
133 )}
134 <div className="grid gap-2">
135 <p className="font-medium text-sm">Nostr Relays</p>
136 {info.relays.map((relay) => (
137 <p className="flex items-center gap-2 text-muted-foreground text-sm">
138 {relay.url}
139 <Badge variant={relay.online ? "positive" : "destructive"}>
140 {relay.online ? "online" : "offline"}
141 </Badge>
142 </p>
143 ))}
144 </div>
145 {info.albyAccountConnected && albyMe && (
146 <div className="grid gap-2">
147 <p className="font-medium text-sm">Connected Alby Account</p>
148 <p className="text-muted-foreground text-sm slashed-zero">
149 {albyMe.email || albyMe.lightning_address}
150 </p>
151 </div>
152 )}
153 {info.albyAccountConnected && albyMe?.hub.name && (
154 <div className="grid gap-2">
155 <p className="font-medium text-sm">Alby Hub Name</p>
156 <p className="text-muted-foreground text-sm slashed-zero">
157 {albyMe.hub.name}
158 </p>
159 </div>
160 )}
161 {info.albyAccountConnected && (
162 <div className="grid gap-2">
163 <p className="font-medium text-sm">Alby Account Plan</p>
164 <p className="text-muted-foreground text-sm slashed-zero">
165 {planName}
166 </p>
167 </div>
168 )}
169 </div>
170 </>
171 );
172 }
173