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