feat: add first channel payment invoice variant

This commit is contained in:
saunter 2026-05-19 18:26:45 +02:00
parent a0d3da2803
commit b0d0c4ed5b

View file

@ -1,22 +1,47 @@
import { Invoice, getFiatValue } from "@getalby/lightning-tools";
import { CopyIcon, ExternalLinkIcon } from "lucide-react";
import {
ArrowRightLeftIcon,
CopyIcon,
ExternalLinkIcon,
InfoIcon,
} from "lucide-react";
import React from "react";
import { FixedFloatButton } from "src/components/FixedFloatButton";
import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount";
import { LightningIcon } from "src/components/icons/Lightning";
import Loading from "src/components/Loading";
import QRCode from "src/components/QRCode";
import { LSPTermsDialog } from "src/components/channels/LSPTermsDialog";
import { Button } from "src/components/ui/button";
import { LinkButton } from "src/components/ui/custom/link-button";
import { Separator } from "src/components/ui/separator";
import { copyToClipboard } from "src/lib/clipboard";
type PayLightningInvoiceProps = {
invoice: string;
variant?: "default" | "first-channel";
amountSat?: number;
incomingLiquiditySat?: number;
lspTerms?: {
name: string;
description: string;
contactUrl: string;
terms?: string;
};
};
export function PayLightningInvoice({ invoice }: PayLightningInvoiceProps) {
const amountSat = new Invoice({
pr: invoice,
}).satoshi;
export function PayLightningInvoice({
invoice,
variant = "default",
amountSat: amountSatProp,
incomingLiquiditySat,
lspTerms,
}: PayLightningInvoiceProps) {
const amountSat =
amountSatProp ??
new Invoice({
pr: invoice,
}).satoshi;
const [fiatAmount, setFiatAmount] = React.useState(0);
React.useEffect(() => {
getFiatValue({ satoshi: amountSat, currency: "USD" }).then((fiatAmount) =>
@ -27,6 +52,100 @@ export function PayLightningInvoice({ invoice }: PayLightningInvoiceProps) {
copyToClipboard(invoice);
};
if (variant === "first-channel") {
return (
<div className="flex w-full max-w-md flex-col items-center gap-6 rounded-xl border bg-background p-6">
<div className="flex items-center justify-center gap-2 font-semibold">
<Loading variant="loader" />
<p>Waiting for Lightning Payment</p>
</div>
<div className="relative flex w-64 items-center justify-center">
<QRCode value={invoice} className="w-full" />
<div className="absolute rounded-full bg-white p-2">
<LightningIcon className="h-14 w-14" />
</div>
</div>
<div className="text-center">
<p className="text-lg font-semibold">
<FormattedBitcoinAmount amountMsat={amountSat * 1000} />
</p>
<p className="text-muted-foreground">
{new Intl.NumberFormat("en-US", {
currency: "USD",
style: "currency",
}).format(fiatAmount)}
</p>
</div>
{lspTerms && (
<p className="text-center text-sm text-muted-foreground">
By purchasing, you agree to{" "}
<LSPTermsDialog
contactUrl={lspTerms.contactUrl}
description={lspTerms.description}
name={lspTerms.name}
terms={lspTerms.terms}
trigger={
<span className="underline underline-offset-2">
{lspTerms.name} Terms & Conditions
</span>
}
/>
.
</p>
)}
<div className="flex w-full flex-col gap-3">
<Button
onClick={copy}
variant="secondary"
className="flex items-center justify-center gap-2"
>
<CopyIcon />
Copy Invoice
</Button>
<LinkButton
to="/channels/outgoing"
variant="outline"
className="w-full justify-center gap-2"
>
Open Channel with On-Chain
<ArrowRightLeftIcon className="size-4" />
</LinkButton>
<FixedFloatButton
to="BTCLN"
address={invoice}
className="flex items-center justify-center gap-2"
variant="outline"
>
Pay with Other Crypto
<ExternalLinkIcon className="size-4" />
</FixedFloatButton>
</div>
<Separator />
<div className="grid w-full gap-3 text-sm">
{incomingLiquiditySat !== undefined && (
<div className="flex items-center justify-between gap-4">
<p className="text-muted-foreground">Incoming liquidity</p>
<FormattedBitcoinAmount
amountMsat={incomingLiquiditySat * 1000}
/>
</div>
)}
<div className="flex items-center justify-between gap-4">
<p className="text-muted-foreground">Channel duration</p>
<div className="flex items-center gap-2">
<InfoIcon className="size-3 text-muted-foreground" />
<span>at least 3 months</span>
</div>
</div>
</div>
</div>
);
}
return (
<div className="w-96 flex flex-col gap-6 p-6 items-center justify-center">
<div className="flex items-center justify-center gap-2 text-muted-foreground">