RoutingFeeDialogContent.tsx raw

   1  import { ExternalLinkIcon } from "lucide-react";
   2  import React from "react";
   3  import { toast } from "sonner";
   4  import ExternalLink from "src/components/ExternalLink";
   5  import { Input } from "src/components/ui/input";
   6  import { Label } from "src/components/ui/label";
   7  import { useChannels } from "src/hooks/useChannels";
   8  import { Channel, UpdateChannelRequest } from "src/types";
   9  import { request } from "src/utils/request";
  10  import {
  11    AlertDialogAction,
  12    AlertDialogCancel,
  13    AlertDialogContent,
  14    AlertDialogDescription,
  15    AlertDialogFooter,
  16    AlertDialogHeader,
  17    AlertDialogTitle,
  18  } from "./ui/alert-dialog";
  19  
  20  type Props = {
  21    channel: Channel;
  22  };
  23  
  24  export function RoutingFeeDialogContent({ channel }: Props) {
  25    const currentBaseFeeSats: number = Math.floor(
  26      channel.forwardingFeeBaseMsat / 1000
  27    );
  28    const currentFeePPM: number = channel.forwardingFeeProportionalMillionths;
  29  
  30    const [baseFeeSats, setBaseFeeSats] = React.useState(
  31      currentBaseFeeSats !== undefined ? currentBaseFeeSats.toString() : ""
  32    );
  33    const [
  34      forwardingFeeProportionalMillionths,
  35      setForwardingFeeProportionalMillionths,
  36    ] = React.useState(
  37      currentFeePPM !== undefined ? currentFeePPM.toString() : ""
  38    );
  39    const { mutate: reloadChannels } = useChannels();
  40  
  41    async function updateFee() {
  42      try {
  43        const forwardingFeeBaseMsat = +baseFeeSats * 1000;
  44  
  45        console.info(
  46          `🎬 Updating channel ${channel.id} with ${channel.remotePubkey}`
  47        );
  48  
  49        await request(
  50          `/api/peers/${channel.remotePubkey}/channels/${channel.id}`,
  51          {
  52            method: "PATCH",
  53            headers: {
  54              "Content-Type": "application/json",
  55            },
  56            body: JSON.stringify({
  57              forwardingFeeBaseMsat: forwardingFeeBaseMsat,
  58              forwardingFeeProportionalMillionths:
  59                +forwardingFeeProportionalMillionths,
  60            } as UpdateChannelRequest),
  61          }
  62        );
  63  
  64        await reloadChannels();
  65        toast("Successfully updated channel");
  66      } catch (error) {
  67        console.error(error);
  68        toast.error("Something went wrong", {
  69          description: "" + error,
  70        });
  71      }
  72    }
  73  
  74    const handleSubmit = (_e: React.FormEvent) => {
  75      // NOTE: some weird behavior due to this dialog being triggered from a dropdown
  76      //e.preventDefault();
  77      updateFee();
  78    };
  79  
  80    return (
  81      <AlertDialogContent>
  82        <AlertDialogHeader>
  83          <AlertDialogTitle>Update Channel Routing Fee</AlertDialogTitle>
  84          <AlertDialogDescription>
  85            <p className="mb-4">
  86              Adjust the fee you charge for each payment routed through this
  87              channel. A high fee (e.g. 100,000 sats) can be set to prevent
  88              unwanted routing. No matter the fee, you can still receive
  89              payments.{" "}
  90            </p>
  91            <form id="routing-fee-form" onSubmit={handleSubmit}>
  92              <Label htmlFor="baseFee" className="block mb-2">
  93                Base Routing Fee (sats)
  94              </Label>
  95              <Input
  96                id="baseFee"
  97                name="baseFee"
  98                type="number"
  99                required
 100                autoFocus
 101                min={0}
 102                value={baseFeeSats}
 103                onChange={(e) => {
 104                  setBaseFeeSats(e.target.value.trim());
 105                }}
 106              />
 107              <Label htmlFor="ppmFee" className="block mt-4 mb-2">
 108                PPM Fee (1 PPM = 1 per 1 million sats)
 109              </Label>
 110              <Input
 111                id="ppmFee"
 112                name="ppmFee"
 113                type="number"
 114                required
 115                min={0}
 116                value={forwardingFeeProportionalMillionths}
 117                onChange={(e) => {
 118                  setForwardingFeeProportionalMillionths(e.target.value.trim());
 119                }}
 120              />
 121            </form>
 122            <ExternalLink
 123              to="https://guides.getalby.com/user-guide/alby-hub/faq/how-can-i-change-routing-fees#understanding-routing-fees-and-alby-hub"
 124              className="underline flex items-center mt-4"
 125            >
 126              Learn more about routing fees
 127              <ExternalLinkIcon className="size-4 ml-2" />
 128            </ExternalLink>
 129          </AlertDialogDescription>
 130        </AlertDialogHeader>
 131        <AlertDialogFooter>
 132          <AlertDialogCancel>Cancel</AlertDialogCancel>
 133          <AlertDialogAction
 134            disabled={
 135              (parseInt(baseFeeSats) || 0) === currentBaseFeeSats &&
 136              (parseInt(forwardingFeeProportionalMillionths) || 0) ===
 137                currentFeePPM
 138            }
 139            type="submit"
 140            form="routing-fee-form"
 141            onClick={handleSubmit}
 142          >
 143            Confirm
 144          </AlertDialogAction>
 145        </AlertDialogFooter>
 146      </AlertDialogContent>
 147    );
 148  }
 149