ChannelDropdownMenu.tsx raw
1 import {
2 ExternalLinkIcon,
3 HandCoinsIcon,
4 MoreHorizontalIcon,
5 ScaleIcon,
6 Trash2Icon,
7 } from "lucide-react";
8 import React from "react";
9 import { useSearchParams } from "react-router";
10 import { CloseChannelDialogContent } from "src/components/CloseChannelDialogContent";
11 import ExternalLink from "src/components/ExternalLink";
12 import { RebalanceChannelDialogContent } from "src/components/RebalanceChannelDialogContent";
13 import { RoutingFeeDialogContent } from "src/components/RoutingFeeDialogContent";
14 import {
15 AlertDialog,
16 AlertDialogTrigger,
17 } from "src/components/ui/alert-dialog";
18 import { Button } from "src/components/ui/button.tsx";
19 import {
20 DropdownMenu,
21 DropdownMenuContent,
22 DropdownMenuItem,
23 DropdownMenuTrigger,
24 } from "src/components/ui/dropdown-menu.tsx";
25 import { useInfo } from "src/hooks/useInfo";
26 import { Channel } from "src/types";
27
28 type ChannelDropdownMenuProps = {
29 alias: string;
30 channel: Channel;
31 hasMultipleChannels: boolean;
32 };
33
34 export function ChannelDropdownMenu({
35 alias,
36 channel,
37 hasMultipleChannels,
38 }: ChannelDropdownMenuProps) {
39 const { data: info } = useInfo();
40 const [searchParams] = useSearchParams();
41 const [dialog, setDialog] = React.useState<
42 "closeChannel" | "routingFee" | "rebalance"
43 >();
44
45 React.useEffect(() => {
46 // when opening the swap dialog, close existing dialog
47 if (searchParams.has("swap", "true")) {
48 setDialog(undefined);
49 }
50 }, [searchParams]);
51
52 return (
53 <AlertDialog
54 open={!!dialog}
55 onOpenChange={(open) => {
56 if (!open) {
57 setDialog(undefined);
58 }
59 }}
60 >
61 <DropdownMenu modal={false}>
62 <Button asChild size="icon" variant="ghost">
63 <DropdownMenuTrigger>
64 <MoreHorizontalIcon />
65 </DropdownMenuTrigger>
66 </Button>
67 <DropdownMenuContent align="end" className="w-64">
68 {channel.status == "online" &&
69 channel.remoteBalanceMsat > channel.localSpendableBalanceMsat &&
70 hasMultipleChannels && (
71 <AlertDialogTrigger asChild>
72 <DropdownMenuItem onClick={() => setDialog("rebalance")}>
73 <ScaleIcon />
74 Rebalance In
75 </DropdownMenuItem>
76 </AlertDialogTrigger>
77 )}
78 <DropdownMenuItem>
79 <ExternalLink
80 className="flex flex-1 flex-row items-center gap-2"
81 to={`${info?.mempoolUrl}/tx/${channel.fundingTxId}#flow=&vout=${channel.fundingTxVout}`}
82 >
83 <ExternalLinkIcon />
84 View Funding Transaction
85 </ExternalLink>
86 </DropdownMenuItem>
87 <DropdownMenuItem>
88 <ExternalLink
89 className="flex flex-1 flex-row items-center gap-2"
90 to={`https://amboss.space/node/${channel.remotePubkey}`}
91 >
92 <ExternalLinkIcon />
93 View Node on amboss.space
94 </ExternalLink>
95 </DropdownMenuItem>
96 {channel.public && (
97 <AlertDialogTrigger asChild>
98 <DropdownMenuItem onClick={() => setDialog("routingFee")}>
99 <HandCoinsIcon />
100 Set Routing Fee
101 </DropdownMenuItem>
102 </AlertDialogTrigger>
103 )}
104 <AlertDialogTrigger asChild>
105 <DropdownMenuItem
106 variant="destructive"
107 onClick={() => setDialog("closeChannel")}
108 >
109 <Trash2Icon />
110 Close Channel
111 </DropdownMenuItem>
112 </AlertDialogTrigger>
113 </DropdownMenuContent>
114 </DropdownMenu>
115 {dialog === "closeChannel" && (
116 <CloseChannelDialogContent alias={alias} channel={channel} />
117 )}
118 {dialog === "routingFee" && <RoutingFeeDialogContent channel={channel} />}
119 {dialog === "rebalance" && (
120 <RebalanceChannelDialogContent
121 receiveThroughNodePubkey={channel.remotePubkey}
122 closeDialog={() => setDialog(undefined)}
123 />
124 )}
125 </AlertDialog>
126 );
127 }
128