PaymentSuccess.tsx raw

   1  import type { Invoice } from "@getalby/lightning-tools/bolt11";
   2  import {
   3    ArrowLeftIcon,
   4    CopyIcon,
   5    ExternalLinkIcon,
   6    HandCoinsIcon,
   7  } from "lucide-react";
   8  import { useEffect } from "react";
   9  import { useLocation, useNavigate } from "react-router";
  10  import ExternalLink from "src/components/ExternalLink";
  11  import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount";
  12  import FormattedFiatAmount from "src/components/FormattedFiatAmount";
  13  import { Button } from "src/components/ui/button";
  14  import {
  15    Card,
  16    CardContent,
  17    CardFooter,
  18    CardHeader,
  19    CardTitle,
  20  } from "src/components/ui/card";
  21  import { copyToClipboard } from "src/lib/clipboard";
  22  
  23  import AppHeader from "src/components/AppHeader";
  24  import LottieSuccess from "src/components/LottieSuccess";
  25  import { LinkButton } from "src/components/ui/custom/link-button";
  26  
  27  export default function PaymentSuccess() {
  28    const { state } = useLocation();
  29    const navigate = useNavigate();
  30  
  31    useEffect(() => {
  32      if (!state?.preimage) {
  33        navigate("/wallet/send");
  34      }
  35    }, [state, navigate]);
  36  
  37    if (!state?.preimage || !state?.invoice) {
  38      return null;
  39    }
  40  
  41    const to = state?.to as string;
  42    const pageTitle = state?.pageTitle as string;
  43    const invoice = state?.invoice as Invoice;
  44    const amountSat = state?.amountSat as number;
  45  
  46    const copy = () => {
  47      copyToClipboard(state.preimage as string);
  48    };
  49  
  50    return (
  51      <div className="grid gap-4">
  52        <AppHeader pageTitle={pageTitle || "Send"} title={pageTitle || "Send"} />
  53        <div className="w-full md:max-w-lg">
  54          <Card className="w-full">
  55            <CardHeader>
  56              <CardTitle className="text-center">Payment Successful</CardTitle>
  57            </CardHeader>
  58            <CardContent className="flex flex-col items-center gap-6">
  59              <LottieSuccess />
  60              <div className="flex flex-col gap-1 items-center">
  61                <p className="text-2xl font-medium slashed-zero">
  62                  <FormattedBitcoinAmount
  63                    amountMsat={(invoice.satoshi || amountSat) * 1000}
  64                  />
  65                </p>
  66                <FormattedFiatAmount
  67                  amountSat={invoice.satoshi || amountSat}
  68                  className="text-xl"
  69                />
  70              </div>
  71              {(to || invoice.description || invoice.successAction) && (
  72                <div className="flex flex-col items-center w-full gap-4">
  73                  {to && (
  74                    <p className="text-muted-foreground">
  75                      to <span className="text-foreground">{to}</span>
  76                    </p>
  77                  )}
  78                  {invoice.description && (
  79                    <p className="text-muted-foreground">{invoice.description}</p>
  80                  )}
  81                  <SuccessAction action={invoice.successAction} />
  82                </div>
  83              )}
  84            </CardContent>
  85            <CardFooter className="flex flex-col gap-3 pt-2">
  86              <Button onClick={copy} variant="outline" className="w-full">
  87                <CopyIcon className="size-4" />
  88                Copy Preimage
  89              </Button>
  90              <LinkButton to="/wallet/send" variant="outline" className="w-full">
  91                <HandCoinsIcon className="size-4" />
  92                Make Another Payment
  93              </LinkButton>
  94              <LinkButton to="/wallet" variant="link" className="w-full">
  95                <ArrowLeftIcon className="size-4" />
  96                Back to Wallet
  97              </LinkButton>
  98            </CardFooter>
  99          </Card>
 100        </div>
 101      </div>
 102    );
 103  }
 104  
 105  function SuccessAction({ action }: { action: Invoice["successAction"] }) {
 106    if (!action) {
 107      return null;
 108    }
 109  
 110    switch (action.tag) {
 111      case "message":
 112        return (
 113          <div className="w-full p-4 border rounded-lg">
 114            <div className="font-medium">Message</div>
 115            <p className="text-sm">{action.message}</p>
 116          </div>
 117        );
 118      case "url":
 119        return (
 120          <>
 121            <div className="w-full p-4 border rounded-lg">
 122              <div className="font-medium">Description</div>
 123              <p className="text-sm">{action.description}</p>
 124            </div>
 125            <div className="w-full p-4 border rounded-lg">
 126              <div className="font-medium">URL</div>
 127              <ExternalLink
 128                to={action.url}
 129                className="underline flex items-center"
 130              >
 131                <p className="text-sm">{action.url}</p>
 132                <ExternalLinkIcon className="w-4 h-4 ml-2" />
 133              </ExternalLink>
 134            </div>
 135          </>
 136        );
 137    }
 138  }
 139