From acd95ff7877b9d7e252b1bd238e64cbbc07758fa Mon Sep 17 00:00:00 2001 From: Adithya Vardhan Date: Mon, 12 Aug 2024 15:50:19 +0530 Subject: [PATCH] chore: add disconnect peer dialog (#434) * feat(lnd): add disconnect peer * chore: add disconnect peer dialog * fix: add break-all to toast text * chore: render dialog component outside table * chore: remove check for node id * chore: minor changes * fix: usage of break all * chore: rename * chore: undo break-all change on the toast component * chore: fix naming * fix: word wrap on toast content * fix: disconnect peer toasts * chore: rename dialog content components * chore: minor copy improvements --------- Co-authored-by: Roland Bewick --- ...alog.tsx => CloseChannelDialogContent.tsx} | 2 +- .../DisconnectPeerDialogContent.tsx | 68 +++++++++++++++++++ .../channels/ChannelDropdownMenu.tsx | 4 +- frontend/src/components/ui/toast.tsx | 23 ++++--- frontend/src/screens/peers/Peers.tsx | 60 +++++++++------- lnclient/lnd/lnd.go | 5 ++ lnclient/lnd/wrapper/lnd.go | 4 ++ 7 files changed, 127 insertions(+), 39 deletions(-) rename frontend/src/components/{CloseChannelDialog.tsx => CloseChannelDialogContent.tsx} (99%) create mode 100644 frontend/src/components/DisconnectPeerDialogContent.tsx diff --git a/frontend/src/components/CloseChannelDialog.tsx b/frontend/src/components/CloseChannelDialogContent.tsx similarity index 99% rename from frontend/src/components/CloseChannelDialog.tsx rename to frontend/src/components/CloseChannelDialogContent.tsx index 88efdb64..e4419eb1 100644 --- a/frontend/src/components/CloseChannelDialog.tsx +++ b/frontend/src/components/CloseChannelDialogContent.tsx @@ -24,7 +24,7 @@ type Props = { channel: Channel; }; -export function CloseChannelDialog({ alias, channel }: Props) { +export function CloseChannelDialogContent({ alias, channel }: Props) { const [closeType, setCloseType] = React.useState("normal"); const [step, setStep] = React.useState(channel.active ? 2 : 1); const [fundingTxId, setFundingTxId] = React.useState(""); diff --git a/frontend/src/components/DisconnectPeerDialogContent.tsx b/frontend/src/components/DisconnectPeerDialogContent.tsx new file mode 100644 index 00000000..5a7cd86e --- /dev/null +++ b/frontend/src/components/DisconnectPeerDialogContent.tsx @@ -0,0 +1,68 @@ +import { toast } from "src/components/ui/use-toast"; +import { usePeers } from "src/hooks/usePeers"; +import { Peer } from "src/types"; +import { request } from "src/utils/request"; +import { + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "./ui/alert-dialog"; + +type Props = { + peer: Peer; + name: string | undefined; +}; + +export function DisconnectPeerDialogContent({ peer, name }: Props) { + const { mutate: reloadPeers } = usePeers(); + + async function disconnectPeer() { + try { + console.info(`Disconnecting from ${peer.nodeId}`); + + await request(`/api/peers/${peer.nodeId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }); + toast({ + title: "Successfully disconnected from peer", + description: peer.nodeId, + }); + await reloadPeers(); + } catch (e) { + toast({ + variant: "destructive", + title: "Failed to disconnect peer", + description: "" + e, + }); + console.error(e); + } + } + + return ( + + + Disconnect Peer + +
+

+ Are you sure you wish to disconnect from {name || "this peer"}? +

+

Peer Pubkey

+

{peer.nodeId}

+
+
+
+ + Cancel + Confirm + +
+ ); +} diff --git a/frontend/src/components/channels/ChannelDropdownMenu.tsx b/frontend/src/components/channels/ChannelDropdownMenu.tsx index 17d58214..bb6fda96 100644 --- a/frontend/src/components/channels/ChannelDropdownMenu.tsx +++ b/frontend/src/components/channels/ChannelDropdownMenu.tsx @@ -4,7 +4,7 @@ import { MoreHorizontal, Trash2, } from "lucide-react"; -import { CloseChannelDialog } from "src/components/CloseChannelDialog"; +import { CloseChannelDialogContent } from "src/components/CloseChannelDialogContent"; import ExternalLink from "src/components/ExternalLink"; import { AlertDialog, @@ -74,7 +74,7 @@ export function ChannelDropdownMenu({ - + ); } diff --git a/frontend/src/components/ui/toast.tsx b/frontend/src/components/ui/toast.tsx index 753f0302..2c8e4045 100644 --- a/frontend/src/components/ui/toast.tsx +++ b/frontend/src/components/ui/toast.tsx @@ -1,7 +1,7 @@ -import * as React from "react"; import { Cross2Icon } from "@radix-ui/react-icons"; import * as ToastPrimitives from "@radix-ui/react-toast"; import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; import { cn } from "src/lib/utils"; @@ -92,7 +92,10 @@ const ToastTitle = React.forwardRef< >(({ className, ...props }, ref) => ( )); @@ -104,7 +107,7 @@ const ToastDescription = React.forwardRef< >(({ className, ...props }, ref) => ( )); @@ -115,13 +118,13 @@ type ToastProps = React.ComponentPropsWithoutRef; type ToastActionElement = React.ReactElement; export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, Toast, - ToastTitle, - ToastDescription, - ToastClose, ToastAction, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, + type ToastActionElement, + type ToastProps, }; diff --git a/frontend/src/screens/peers/Peers.tsx b/frontend/src/screens/peers/Peers.tsx index 00a9b5bf..d28df3c8 100644 --- a/frontend/src/screens/peers/Peers.tsx +++ b/frontend/src/screens/peers/Peers.tsx @@ -2,6 +2,8 @@ import { MoreHorizontal, Trash2 } from "lucide-react"; import React from "react"; import { Link } from "react-router-dom"; import AppHeader from "src/components/AppHeader.tsx"; +import { DisconnectPeerDialogContent } from "src/components/DisconnectPeerDialogContent"; +import { AlertDialog } from "src/components/ui/alert-dialog.tsx"; import { Badge } from "src/components/ui/badge.tsx"; import { Button } from "src/components/ui/button.tsx"; import { @@ -18,18 +20,19 @@ import { TableHeader, TableRow, } from "src/components/ui/table.tsx"; -import { toast } from "src/components/ui/use-toast.ts"; +import { toast } from "src/components/ui/use-toast"; import { useChannels } from "src/hooks/useChannels"; import { usePeers } from "src/hooks/usePeers.ts"; import { useSyncWallet } from "src/hooks/useSyncWallet.ts"; -import { Node } from "src/types"; +import { Node, Peer } from "src/types"; import { request } from "src/utils/request"; export default function Peers() { useSyncWallet(); - const { data: peers, mutate: reloadPeers } = usePeers(); + const { data: peers } = usePeers(); const { data: channels } = useChannels(); const [nodes, setNodes] = React.useState([]); + const [peerToDisconnect, setPeerToDisconnect] = React.useState(); // TODO: move to NWC backend const loadNodeStats = React.useCallback(async () => { @@ -56,35 +59,22 @@ export default function Peers() { loadNodeStats(); }, [loadNodeStats]); - async function disconnectPeer(peerId: string) { + async function checkDisconnectPeer(peer: Peer) { try { - if (!peerId) { - throw new Error("peer missing"); - } if (!channels) { throw new Error("channels not loaded"); } - if (channels.some((channel) => channel.remotePubkey === peerId)) { - throw new Error("you have one or more open channels with " + peerId); - } - if ( - !confirm( - "Are you sure you wish to disconnect with peer " + peerId + "?" - ) - ) { - return; + if (channels.some((channel) => channel.remotePubkey === peer.nodeId)) { + throw new Error( + "you have one or more open channels with " + peer.nodeId + ); } - console.info(`Disconnecting from ${peerId}`); - - await request(`/api/peers/${peerId}`, { - method: "DELETE", - }); - toast({ title: "Successfully disconnected from peer " + peerId }); - await reloadPeers(); + setPeerToDisconnect(peer); } catch (e) { toast({ variant: "destructive", - title: "Failed to disconnect peer: " + e, + title: "Cannot disconnect peer", + description: "" + e, }); console.error(e); } @@ -152,10 +142,10 @@ export default function Peers() { checkDisconnectPeer(peer)} className="flex flex-row items-center gap-2" - onClick={() => disconnectPeer(peer.nodeId)} > - + Disconnect Peer @@ -167,6 +157,24 @@ export default function Peers() { + + { + if (!open) { + setPeerToDisconnect(undefined); + } + }} + > + {peerToDisconnect && ( + n.public_key === peerToDisconnect.nodeId)?.alias + } + /> + )} + ); } diff --git a/lnclient/lnd/lnd.go b/lnclient/lnd/lnd.go index ed3caf09..58e394a4 100644 --- a/lnclient/lnd/lnd.go +++ b/lnclient/lnd/lnd.go @@ -949,6 +949,11 @@ func (svc *LNDService) UpdateChannel(ctx context.Context, updateChannelRequest * } func (svc *LNDService) DisconnectPeer(ctx context.Context, peerId string) error { + _, err := svc.client.DisconnectPeer(ctx, &lnrpc.DisconnectPeerRequest{PubKey: peerId}) + if err != nil { + return err + } + return nil } diff --git a/lnclient/lnd/wrapper/lnd.go b/lnclient/lnd/wrapper/lnd.go index ae1162d8..39fe0b35 100644 --- a/lnclient/lnd/wrapper/lnd.go +++ b/lnclient/lnd/wrapper/lnd.go @@ -188,3 +188,7 @@ func (wrapper *LNDWrapper) GetChanInfo(ctx context.Context, req *lnrpc.ChanInfoR func (wrapper *LNDWrapper) UpdateChannel(ctx context.Context, req *lnrpc.PolicyUpdateRequest, options ...grpc.CallOption) (*lnrpc.PolicyUpdateResponse, error) { return wrapper.client.UpdateChannelPolicy(ctx, req, options...) } + +func (wrapper *LNDWrapper) DisconnectPeer(ctx context.Context, req *lnrpc.DisconnectPeerRequest, options ...grpc.CallOption) (*lnrpc.DisconnectPeerResponse, error) { + return wrapper.client.DisconnectPeer(ctx, req, options...) +}