PendingClosedChannelsAlert.tsx raw

   1  import { ExternalLinkIcon, HourglassIcon } from "lucide-react";
   2  import ExternalLink from "src/components/ExternalLink";
   3  import { FormattedBitcoinAmount } from "src/components/FormattedBitcoinAmount";
   4  import { Alert, AlertDescription, AlertTitle } from "src/components/ui/alert";
   5  import { useInfo } from "src/hooks/useInfo";
   6  import { useNodeDetails } from "src/hooks/useNodeDetails";
   7  import { OnchainBalanceResponse, PendingBalancesDetails } from "src/types";
   8  
   9  type PendingClosedChannelsAlertProps = {
  10    balance: OnchainBalanceResponse;
  11  };
  12  
  13  export function PendingClosedChannelsAlert({
  14    balance,
  15  }: PendingClosedChannelsAlertProps) {
  16    if (balance.pendingBalancesFromChannelClosuresSat <= 0) {
  17      return null;
  18    }
  19  
  20    const pendingDetails = [
  21      ...balance.pendingBalancesDetails,
  22      ...balance.pendingSweepBalancesDetails,
  23    ];
  24  
  25    return (
  26      <Alert>
  27        <HourglassIcon />
  28        <AlertTitle>Pending Closed Channels</AlertTitle>
  29        <AlertDescription className="block">
  30          You have{" "}
  31          <FormattedBitcoinAmount
  32            amountMsat={balance.pendingBalancesFromChannelClosuresSat * 1000}
  33          />{" "}
  34          pending from closed channels
  35          {pendingDetails.length > 0 && (
  36            <>
  37              {" "}
  38              with
  39              {pendingDetails.map((details, index) => (
  40                <PendingBalancesDetailsItem
  41                  key={`${details.fundingTxId}:${details.fundingTxVout}`}
  42                  details={details}
  43                  showSeparator={index < pendingDetails.length - 1}
  44                />
  45              ))}
  46            </>
  47          )}
  48          . Once spendable again these will become available in your on-chain
  49          balance. Funds from channels that were force closed may take up to 2
  50          weeks to become available.{" "}
  51          <ExternalLink
  52            to="https://guides.getalby.com/user-guide/alby-hub/faq/why-was-my-lightning-channel-closed-and-what-to-do-next"
  53            className="underline"
  54          >
  55            Learn more
  56          </ExternalLink>
  57        </AlertDescription>
  58      </Alert>
  59    );
  60  }
  61  
  62  type PendingBalancesDetailsItemProps = {
  63    details: PendingBalancesDetails;
  64    showSeparator: boolean;
  65  };
  66  
  67  function PendingBalancesDetailsItem({
  68    details,
  69    showSeparator,
  70  }: PendingBalancesDetailsItemProps) {
  71    const { data: info } = useInfo();
  72    const { data: nodeDetails } = useNodeDetails(details.nodeId);
  73  
  74    return (
  75      <span className="inline">
  76        &nbsp;
  77        <ExternalLink
  78          to={`https://amboss.space/node/${details.nodeId}`}
  79          className="underline"
  80        >
  81          {nodeDetails?.alias || "Unknown"}
  82          <ExternalLinkIcon className="ml-1 inline h-4 w-4" />
  83        </ExternalLink>{" "}
  84        (<FormattedBitcoinAmount amountMsat={details.amountSat * 1000} />
  85        )&nbsp;
  86        <ExternalLink
  87          to={`${info?.mempoolUrl}/tx/${details.fundingTxId}#flow=&vout=${details.fundingTxVout}`}
  88          className="underline"
  89        >
  90          funding tx
  91          <ExternalLinkIcon className="ml-1 inline h-4 w-4" />
  92        </ExternalLink>
  93        {showSeparator && ","}
  94      </span>
  95    );
  96  }
  97