AlbyConnectionCard.tsx raw
1 import {
2 CheckCircle2Icon,
3 CircleXIcon,
4 EditIcon,
5 InfoIcon,
6 Link2Icon,
7 User2Icon,
8 ZapIcon,
9 } from "lucide-react";
10 import { FormEvent, useState } from "react";
11 import { Link } from "react-router";
12
13 import BudgetAmountSelect from "src/components/BudgetAmountSelect";
14 import BudgetRenewalSelect from "src/components/BudgetRenewalSelect";
15 import Loading from "src/components/Loading";
16 import UserAvatar from "src/components/UserAvatar";
17 import { AppCardConnectionInfo } from "src/components/connections/AppCardConnectionInfo";
18 import { AppCardNotice } from "src/components/connections/AppCardNotice";
19 import { Button } from "src/components/ui/button";
20 import {
21 Card,
22 CardContent,
23 CardDescription,
24 CardHeader,
25 CardTitle,
26 } from "src/components/ui/card";
27 import { LoadingButton } from "src/components/ui/custom/loading-button";
28 import {
29 Dialog,
30 DialogContent,
31 DialogDescription,
32 DialogFooter,
33 DialogHeader,
34 DialogTrigger,
35 } from "src/components/ui/dialog";
36 import { Separator } from "src/components/ui/separator";
37 import {
38 Tooltip,
39 TooltipContent,
40 TooltipProvider,
41 TooltipTrigger,
42 } from "src/components/ui/tooltip";
43 import { useAlbyMe } from "src/hooks/useAlbyMe";
44 import { LinkStatus, useLinkAccount } from "src/hooks/useLinkAccount";
45 import { BudgetRenewalType } from "src/types";
46
47 import AlbyAccountDarkSVG from "public/images/illustrations/alby-account-dark.svg";
48 import AlbyAccountLightSVG from "public/images/illustrations/alby-account-light.svg";
49 import { LinkButton } from "src/components/ui/custom/link-button";
50 import { ALBY_ACCOUNT_APP_NAME } from "src/constants";
51 import { useApps } from "src/hooks/useApps";
52
53 function AlbyConnectionCard() {
54 const { data: linkedAlbyAccountAppsData, mutate: reloadAlbyAccountApp } =
55 useApps(undefined, undefined, {
56 name: ALBY_ACCOUNT_APP_NAME,
57 });
58 const albyAccountApp = linkedAlbyAccountAppsData?.apps[0];
59 const { data: albyMe } = useAlbyMe();
60 const { loading, linkStatus, loadingLinkStatus, linkAccount } =
61 useLinkAccount(reloadAlbyAccountApp);
62
63 const [maxAmountSat, setMaxAmountSat] = useState(250_000);
64 const [budgetRenewal, setBudgetRenewal] =
65 useState<BudgetRenewalType>("weekly");
66
67 function onSubmit(e: FormEvent) {
68 e.preventDefault();
69
70 linkAccount(maxAmountSat, budgetRenewal);
71 }
72
73 return (
74 <Card>
75 <CardHeader>
76 <CardTitle className="relative">
77 Linked Alby Account
78 {albyAccountApp && <AppCardNotice app={albyAccountApp} />}
79 </CardTitle>
80 <CardDescription>
81 Link Your Alby Account to use your lightning address with Alby Hub and
82 use apps that you connected to your Alby Account.
83 </CardDescription>
84 </CardHeader>
85 <Separator />
86 <CardContent className="group">
87 <div className="grid grid-cols-1 xl:grid-cols-2 gap-3 items-center relative">
88 <div className="flex flex-col gap-4">
89 <div className="flex flex-row gap-4">
90 <UserAvatar className="h-14 w-14" />
91 <div className="flex flex-col justify-center">
92 <div className="text-xl font-semibold">
93 {albyMe?.name || albyMe?.email}
94 </div>
95 <div className="flex flex-row items-center gap-1 text-sm text-muted-foreground">
96 <ZapIcon className="size-4" />
97 {albyMe?.lightning_address}
98 </div>
99 </div>
100 </div>
101 <div className="flex flex-col sm:flex-row gap-3 sm:items-center">
102 {loadingLinkStatus && <Loading />}
103 {!albyAccountApp ||
104 linkStatus === LinkStatus.SharedNode ||
105 linkStatus === LinkStatus.Unlinked ? (
106 <Dialog>
107 <DialogTrigger asChild>
108 <LoadingButton loading={loading}>
109 {!loading && <Link2Icon />}
110 Link your Alby Account
111 </LoadingButton>
112 </DialogTrigger>
113 <DialogContent>
114 <form onSubmit={onSubmit}>
115 <DialogHeader>Link to Alby Account</DialogHeader>
116 <DialogDescription className="flex flex-col gap-4">
117 After you link your account, your lightning address and
118 every app you access through your Alby Account will
119 handle payments via the Hub.
120 <img
121 src={AlbyAccountDarkSVG}
122 className="w-full hidden dark:block"
123 />
124 <img
125 src={AlbyAccountLightSVG}
126 className="w-full dark:hidden"
127 />
128 You can add a budget that will restrict how much can be
129 spent from the Hub with your Alby Account.
130 </DialogDescription>
131 <div className="mt-4">
132 <BudgetRenewalSelect
133 value={budgetRenewal}
134 onChange={setBudgetRenewal}
135 />
136 <BudgetAmountSelect
137 valueSat={maxAmountSat}
138 onChange={setMaxAmountSat}
139 minAmountSat={
140 25000 /* the minimum should be a bit more than the Alby monthly fee */
141 }
142 budgetOptionsSat={{
143 "250k": 250_000,
144 "500k": 500_000,
145 "1M": 1_000_000,
146 }}
147 />
148 </div>
149 <DialogFooter>
150 <LoadingButton loading={loading}>
151 Link to Alby Account
152 </LoadingButton>
153 </DialogFooter>
154 </form>
155 </DialogContent>
156 </Dialog>
157 ) : linkStatus === LinkStatus.ThisNode ? (
158 <Button
159 variant="positive"
160 disabled
161 className="disabled:opacity-100"
162 >
163 <CheckCircle2Icon />
164 Alby Account Linked
165 </Button>
166 ) : (
167 linkStatus === LinkStatus.OtherNode && (
168 <Button variant="destructive" disabled>
169 <CircleXIcon />
170 Linked to another wallet
171 </Button>
172 )
173 )}
174 <LinkButton
175 to="/settings/alby-account"
176 className="w-full sm:w-auto"
177 variant="outline"
178 >
179 <User2Icon />
180 Alby Account Settings
181 </LinkButton>
182 </div>
183 </div>
184 {albyAccountApp && (
185 <div className="slashed-zero">
186 <Link
187 to={`/apps/${albyAccountApp.id}?edit=true`}
188 className="absolute top-0 right-0"
189 >
190 <EditIcon className="size-4 hidden group-hover:inline text-muted-foreground hover:text-card-foreground" />
191 </Link>
192 <AppCardConnectionInfo
193 connection={albyAccountApp}
194 budgetRemainingText={
195 <span className="flex items-center gap-2 justify-end">
196 Left in Alby Account budget
197 <TooltipProvider>
198 <Tooltip>
199 <TooltipTrigger>
200 <div className="flex flex-row items-center">
201 <InfoIcon className="h-4 w-4 shrink-0 text-muted-foreground" />
202 </div>
203 </TooltipTrigger>
204 <TooltipContent>
205 Control what access your Alby Account has to your Hub
206 by editing the budget. Every app you access through
207 your Alby Account (such as your lightning address,
208 Alby Extension, podcasting 2.0 apps) will handle
209 payments via your Hub.
210 </TooltipContent>
211 </Tooltip>
212 </TooltipProvider>
213 </span>
214 }
215 />
216 </div>
217 )}
218 </div>
219 </CardContent>
220 </Card>
221 );
222 }
223
224 export default AlbyConnectionCard;
225