From 86f94ccf38708d689707b6f9ead223e910dfb547 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Fri, 26 Jul 2024 17:43:24 +0700 Subject: [PATCH 01/10] feat: add channels cards on mobile --- .../src/components/channels/ChannelsCards.tsx | 258 ++++++++++++++++ .../src/components/channels/ChannelsTable.tsx | 285 ++++++++++++++++++ frontend/src/components/ui/credenza.tsx | 6 +- frontend/src/hooks/useMediaQuery.ts | 5 + frontend/src/screens/channels/Channels.tsx | 277 ++--------------- 5 files changed, 571 insertions(+), 260 deletions(-) create mode 100644 frontend/src/components/channels/ChannelsCards.tsx create mode 100644 frontend/src/components/channels/ChannelsTable.tsx diff --git a/frontend/src/components/channels/ChannelsCards.tsx b/frontend/src/components/channels/ChannelsCards.tsx new file mode 100644 index 00000000..9e47d74f --- /dev/null +++ b/frontend/src/components/channels/ChannelsCards.tsx @@ -0,0 +1,258 @@ +import { + AlertTriangle, + ExternalLinkIcon, + HandCoins, + InfoIcon, + MoreHorizontal, + Trash2, +} from "lucide-react"; +import ExternalLink from "src/components/ExternalLink"; +import { Badge } from "src/components/ui/badge.tsx"; +import { Button } from "src/components/ui/button.tsx"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "src/components/ui/card"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "src/components/ui/dropdown-menu.tsx"; +import { Progress } from "src/components/ui/progress.tsx"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "src/components/ui/tooltip.tsx"; +import { formatAmount } from "src/lib/utils.ts"; +import { Channel, Node } from "src/types"; + +type ChannelsCardsProps = { + channels?: Channel[]; + nodes?: Node[]; + closeChannel( + channelId: string, + counterpartyNodeId: string, + isActive: boolean + ): void; + editChannel(channel: Channel): void; +}; + +export function ChannelsCards({ + channels, + nodes, + closeChannel, + editChannel, +}: ChannelsCardsProps) { + if (!channels?.length) { + return null; + } + + return ( + <> +

Channels

+
+ {channels + .sort((a, b) => + a.localBalance + a.remoteBalance > b.localBalance + b.remoteBalance + ? -1 + : 1 + ) + .map((channel) => { + const node = nodes?.find( + (n) => n.public_key === channel.remotePubkey + ); + const alias = node?.alias || "Unknown"; + const capacity = channel.localBalance + channel.remoteBalance; + // TODO: remove duplication + let channelWarning = ""; + if (channel.error) { + channelWarning = channel.error; + } else { + if (channel.localSpendableBalance < capacity * 0.1) { + channelWarning = + "Spending balance low. You may have trouble sending payments through this channel."; + } + if (channel.localSpendableBalance > capacity * 0.9) { + channelWarning = + "Receiving capacity low. You may have trouble receiving payments through this channel."; + } + } + + const channelStatus = channel.active + ? "online" + : channel.confirmationsRequired !== undefined && + channel.confirmations !== undefined && + channel.confirmationsRequired > channel.confirmations + ? "opening" + : "offline"; + if (channelStatus === "opening") { + channelWarning = `Channel is currently being opened (${channel.confirmations} of ${channel.confirmationsRequired} confirmations). Once the required confirmation are reached, you will be able to send and receive on this channel.`; + } + if (channelStatus === "offline") { + channelWarning = + "This channel is currently offline and cannot be used to send or receive payments. Please contact Alby Support for more information."; + } + + return ( + + +
+ +
+ + + + + + + + + +

View Funding Transaction

+
+
+ {channel.public && ( + editChannel(channel)} + > + + Set Routing Fee + + )} + + closeChannel( + channel.id, + channel.remotePubkey, + channel.active + ) + } + > + + Close Channel + +
+
+
+
+ +
+

Status

+
+ {channelStatus == "online" ? ( + Online + ) : channelStatus == "opening" ? ( + Opening + ) : ( + Offline + )} + + {channel.public ? "Public" : "Private"} + +
+
+
+

Capacity

+ {formatAmount(capacity)} sats +
+
+ + + +
+ Reserve + +
+
+ + Funds each participant sets aside to discourage + cheating by ensuring each party has something at + stake. This reserve cannot be spent during the + channel's lifetime and typically amounts to 1% of + the channel capacity. + +
+
+ {channel.localBalance < + channel.unspendablePunishmentReserve * 1000 && ( + <> + {formatAmount( + Math.min( + channel.localBalance, + channel.unspendablePunishmentReserve * 1000 + ) + )}{" "} + /{" "} + + )} + {formatAmount( + channel.unspendablePunishmentReserve * 1000 + )}{" "} + sats +
+
+
+
+ +
+
+ +
+ + {formatAmount(channel.localSpendableBalance)} sats + + + {formatAmount(channel.remoteBalance)} sats + +
+
+ {channelWarning ? ( + + + + + + + {channelWarning} + + + + ) : null} +
+
+
+ ); + })} +
+ + ); +} diff --git a/frontend/src/components/channels/ChannelsTable.tsx b/frontend/src/components/channels/ChannelsTable.tsx new file mode 100644 index 00000000..419f239e --- /dev/null +++ b/frontend/src/components/channels/ChannelsTable.tsx @@ -0,0 +1,285 @@ +import { + AlertTriangle, + ExternalLinkIcon, + HandCoins, + InfoIcon, + MoreHorizontal, + Trash2, +} from "lucide-react"; +import ExternalLink from "src/components/ExternalLink"; +import Loading from "src/components/Loading.tsx"; +import { Badge } from "src/components/ui/badge.tsx"; +import { Button } from "src/components/ui/button.tsx"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "src/components/ui/dropdown-menu.tsx"; +import { Progress } from "src/components/ui/progress.tsx"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "src/components/ui/table.tsx"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "src/components/ui/tooltip.tsx"; +import { formatAmount } from "src/lib/utils.ts"; +import { Channel, Node } from "src/types"; + +type ChannelsTableProps = { + channels?: Channel[]; + nodes?: Node[]; + closeChannel( + channelId: string, + counterpartyNodeId: string, + isActive: boolean + ): void; + editChannel(channel: Channel): void; +}; + +export function ChannelsTable({ + channels, + nodes, + closeChannel, + editChannel, +}: ChannelsTableProps) { + if (channels && !channels.length) { + return null; + } + + return ( +
+ + + + Status + Node + Capacity + + + + +
+ Reserve + +
+
+ + Funds each participant sets aside to discourage cheating by + ensuring each party has something at stake. This reserve + cannot be spent during the channel's lifetime and typically + amounts to 1% of the channel capacity. + +
+
+
+ +
+
Spending
+
Receiving
+
+
+ + +
+
+ + {channels && channels.length > 0 && ( + <> + {channels + .sort((a, b) => + a.localBalance + a.remoteBalance > + b.localBalance + b.remoteBalance + ? -1 + : 1 + ) + .map((channel) => { + const node = nodes?.find( + (n) => n.public_key === channel.remotePubkey + ); + const alias = node?.alias || "Unknown"; + const capacity = channel.localBalance + channel.remoteBalance; + + let channelWarning = ""; + if (channel.error) { + channelWarning = channel.error; + } else { + if (channel.localSpendableBalance < capacity * 0.1) { + channelWarning = + "Spending balance low. You may have trouble sending payments through this channel."; + } + if (channel.localSpendableBalance > capacity * 0.9) { + channelWarning = + "Receiving capacity low. You may have trouble receiving payments through this channel."; + } + } + + const channelStatus = channel.active + ? "online" + : channel.confirmationsRequired !== undefined && + channel.confirmations !== undefined && + channel.confirmationsRequired > channel.confirmations + ? "opening" + : "offline"; + if (channelStatus === "opening") { + channelWarning = `Channel is currently being opened (${channel.confirmations} of ${channel.confirmationsRequired} confirmations). Once the required confirmation are reached, you will be able to send and receive on this channel.`; + } + if (channelStatus === "offline") { + channelWarning = + "This channel is currently offline and cannot be used to send or receive payments. Please contact Alby Support for more information."; + } + + return ( + + + {channelStatus == "online" ? ( + Online + ) : channelStatus == "opening" ? ( + Opening + ) : ( + Offline + )} + + + + + + + {channel.public ? "Public" : "Private"} + + + + {formatAmount(capacity)} sats + + + {channel.localBalance < + channel.unspendablePunishmentReserve * 1000 && ( + <> + {formatAmount( + Math.min( + channel.localBalance, + channel.unspendablePunishmentReserve * 1000 + ) + )}{" "} + /{" "} + + )} + {formatAmount( + channel.unspendablePunishmentReserve * 1000 + )}{" "} + sats + + +
+ +
+ + {formatAmount(channel.localSpendableBalance)} sats + + + {formatAmount(channel.remoteBalance)} sats + +
+
+
+ + {channelWarning ? ( + + + + + + + {channelWarning} + + + + ) : null} + + + + + + + + + + +

View Funding Transaction

+
+
+ {channel.public && ( + editChannel(channel)} + > + + Set Routing Fee + + )} + + closeChannel( + channel.id, + channel.remotePubkey, + channel.active + ) + } + > + + Close Channel + +
+
+
+
+ ); + })} + + )} + {!channels && ( + + + + + + )} +
+
+
+ ); +} diff --git a/frontend/src/components/ui/credenza.tsx b/frontend/src/components/ui/credenza.tsx index 63068eac..53e66bb1 100644 --- a/frontend/src/components/ui/credenza.tsx +++ b/frontend/src/components/ui/credenza.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { useMediaQuery } from "src/hooks/useMediaQuery"; +import { useIsDesktop } from "src/hooks/useMediaQuery"; import { cn } from "src/lib/utils"; import { Dialog, @@ -36,12 +36,10 @@ interface CredenzaProps extends BaseProps { asChild?: true; } -const desktop = "(min-width: 768px)"; - const CredenzaContext = React.createContext(false); const CredenzaProvider = ({ children }: BaseProps) => { - const isDesktop = useMediaQuery(desktop); + const isDesktop = useIsDesktop(); return ( {children} diff --git a/frontend/src/hooks/useMediaQuery.ts b/frontend/src/hooks/useMediaQuery.ts index 48db609e..6c3a6271 100644 --- a/frontend/src/hooks/useMediaQuery.ts +++ b/frontend/src/hooks/useMediaQuery.ts @@ -17,3 +17,8 @@ export function useMediaQuery(query: string) { return value; } + +export function useIsDesktop() { + const desktop = "(min-width: 768px)"; + return useMediaQuery(desktop); +} diff --git a/frontend/src/screens/channels/Channels.tsx b/frontend/src/screens/channels/Channels.tsx index 2bb8424d..4b180b19 100644 --- a/frontend/src/screens/channels/Channels.tsx +++ b/frontend/src/screens/channels/Channels.tsx @@ -5,18 +5,16 @@ import { Bitcoin, ChevronDown, CopyIcon, - ExternalLinkIcon, - HandCoins, Heart, Hotel, InfoIcon, - MoreHorizontal, - Trash2, Unplug, } from "lucide-react"; import React from "react"; import { Link } from "react-router-dom"; import AppHeader from "src/components/AppHeader.tsx"; +import { ChannelsCards } from "src/components/channels/ChannelsCards.tsx"; +import { ChannelsTable } from "src/components/channels/ChannelsTable.tsx"; import EmptyState from "src/components/EmptyState.tsx"; import ExternalLink from "src/components/ExternalLink"; import Loading from "src/components/Loading.tsx"; @@ -25,7 +23,6 @@ import { AlertDescription, AlertTitle, } from "src/components/ui/alert.tsx"; -import { Badge } from "src/components/ui/badge.tsx"; import { Button } from "src/components/ui/button.tsx"; import { Card, @@ -44,15 +41,7 @@ import { DropdownMenuTrigger, } from "src/components/ui/dropdown-menu.tsx"; import { LoadingButton } from "src/components/ui/loading-button.tsx"; -import { CircleProgress, Progress } from "src/components/ui/progress.tsx"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "src/components/ui/table.tsx"; +import { CircleProgress } from "src/components/ui/progress.tsx"; import { Tooltip, TooltipContent, @@ -68,11 +57,12 @@ import { useAlbyBalance } from "src/hooks/useAlbyBalance.ts"; import { useBalances } from "src/hooks/useBalances.ts"; import { useChannels } from "src/hooks/useChannels"; import { useInfo } from "src/hooks/useInfo"; +import { useIsDesktop } from "src/hooks/useMediaQuery.ts"; import { useNodeConnectionInfo } from "src/hooks/useNodeConnectionInfo.ts"; import { useRedeemOnchainFunds } from "src/hooks/useRedeemOnchainFunds.ts"; import { useSyncWallet } from "src/hooks/useSyncWallet.ts"; import { copyToClipboard } from "src/lib/clipboard.ts"; -import { cn, formatAmount } from "src/lib/utils.ts"; +import { cn } from "src/lib/utils.ts"; import { Channel, CloseChannelResponse, @@ -95,6 +85,7 @@ export default function Channels() { const { toast } = useToast(); const [drainingAlbySharedFunds, setDrainingAlbySharedFunds] = React.useState(false); + const isDesktop = useIsDesktop(); const nodeHealth = channels ? getNodeHealth(channels) : 0; @@ -656,247 +647,21 @@ export default function Channels() { /> )} - {!channels || - (channels.length > 0 && ( -
- - - - Status - Node - Capacity - - - - -
- Reserve - -
-
- - Funds each participant sets aside to discourage - cheating by ensuring each party has something at - stake. This reserve cannot be spent during the - channel's lifetime and typically amounts to 1% of the - channel capacity. - -
-
-
- -
-
Spending
-
Receiving
-
-
- - -
-
- - {channels && channels.length > 0 && ( - <> - {channels - .sort((a, b) => - a.localBalance + a.remoteBalance > - b.localBalance + b.remoteBalance - ? -1 - : 1 - ) - .map((channel) => { - const node = nodes.find( - (n) => n.public_key === channel.remotePubkey - ); - const alias = node?.alias || "Unknown"; - const capacity = - channel.localBalance + channel.remoteBalance; - - let channelWarning = ""; - if (channel.error) { - channelWarning = channel.error; - } else { - if (channel.localSpendableBalance < capacity * 0.1) { - channelWarning = - "Spending balance low. You may have trouble sending payments through this channel."; - } - if (channel.localSpendableBalance > capacity * 0.9) { - channelWarning = - "Receiving capacity low. You may have trouble receiving payments through this channel."; - } - } - - const channelStatus = channel.active - ? "online" - : channel.confirmationsRequired !== undefined && - channel.confirmations !== undefined && - channel.confirmationsRequired > - channel.confirmations - ? "opening" - : "offline"; - if (channelStatus === "opening") { - channelWarning = `Channel is currently being opened (${channel.confirmations} of ${channel.confirmationsRequired} confirmations). Once the required confirmation are reached, you will be able to send and receive on this channel.`; - } - if (channelStatus === "offline") { - channelWarning = - "This channel is currently offline and cannot be used to send or receive payments. Please contact Alby Support for more information."; - } - - return ( - - - {channelStatus == "online" ? ( - Online - ) : channelStatus == "opening" ? ( - Opening - ) : ( - Offline - )} - - - - - - - {channel.public ? "Public" : "Private"} - - - - {formatAmount(capacity)} sats - - - {channel.localBalance < - channel.unspendablePunishmentReserve * 1000 && ( - <> - {formatAmount( - Math.min( - channel.localBalance, - channel.unspendablePunishmentReserve * - 1000 - ) - )}{" "} - /{" "} - - )} - {formatAmount( - channel.unspendablePunishmentReserve * 1000 - )}{" "} - sats - - -
- -
- - {formatAmount( - channel.localSpendableBalance - )}{" "} - sats - - - {formatAmount(channel.remoteBalance)} sats - -
-
-
- - {channelWarning ? ( - - - - - - - {channelWarning} - - - - ) : null} - - - - - - - - - - -

View Funding Transaction

-
-
- {channel.public && ( - editChannel(channel)} - > - - Set Routing Fee - - )} - - closeChannel( - channel.id, - channel.remotePubkey, - channel.active - ) - } - > - - Close Channel - -
-
-
-
- ); - })} - - )} - {!channels && ( - - - - - - )} -
-
-
- ))} + {isDesktop ? ( + + ) : ( + + )} ); } From fc81187f20dcbc766b53166abef46b01265fcb11 Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 30 Jul 2024 17:20:56 +0700 Subject: [PATCH 02/10] feat: improve card design, reduce duplication --- api/api.go | 37 +++- api/models.go | 21 ++- .../components/channels/ChannelWarning.tsx | 45 +++++ .../src/components/channels/ChannelsCards.tsx | 159 ++++++++---------- .../src/components/channels/ChannelsTable.tsx | 50 +----- frontend/src/types.ts | 1 + lnclient/ldk/ldk.go | 1 + lnclient/models.go | 30 ++-- 8 files changed, 192 insertions(+), 152 deletions(-) create mode 100644 frontend/src/components/channels/ChannelWarning.tsx diff --git a/api/api.go b/api/api.go index 02320aa9..2520a93c 100644 --- a/api/api.go +++ b/api/api.go @@ -282,11 +282,44 @@ func (api *api) ListApps() ([]App, error) { return apiApps, nil } -func (api *api) ListChannels(ctx context.Context) ([]lnclient.Channel, error) { +func (api *api) ListChannels(ctx context.Context) ([]Channel, error) { if api.svc.GetLNClient() == nil { return nil, errors.New("LNClient not started") } - return api.svc.GetLNClient().ListChannels(ctx) + channels, err := api.svc.GetLNClient().ListChannels(ctx) + if err != nil { + return nil, err + } + + apiChannels := []Channel{} + for _, channel := range channels { + status := "offline" + if channel.Active { + status = "online" + } else if channel.Confirmations != nil && channel.ConfirmationsRequired != nil && *channel.ConfirmationsRequired > *channel.Confirmations { + status = "opening" + } + + apiChannels = append(apiChannels, Channel{ + LocalBalance: channel.LocalBalance, + LocalSpendableBalance: channel.LocalSpendableBalance, + RemoteBalance: channel.RemoteBalance, + Id: channel.Id, + RemotePubkey: channel.RemotePubkey, + FundingTxId: channel.FundingTxId, + Active: channel.Active, + Public: channel.Public, + InternalChannel: channel.InternalChannel, + Confirmations: channel.Confirmations, + ConfirmationsRequired: channel.ConfirmationsRequired, + ForwardingFeeBaseMsat: channel.ForwardingFeeBaseMsat, + UnspendablePunishmentReserve: channel.UnspendablePunishmentReserve, + CounterpartyUnspendablePunishmentReserve: channel.CounterpartyUnspendablePunishmentReserve, + Error: channel.Error, + Status: status, + }) + } + return apiChannels, nil } func (api *api) GetChannelPeerSuggestions(ctx context.Context) ([]alby.ChannelPeerSuggestion, error) { diff --git a/api/models.go b/api/models.go index 8305264e..87a0bc04 100644 --- a/api/models.go +++ b/api/models.go @@ -16,7 +16,7 @@ type API interface { DeleteApp(userApp *db.App) error GetApp(userApp *db.App) *App ListApps() ([]App, error) - ListChannels(ctx context.Context) ([]lnclient.Channel, error) + ListChannels(ctx context.Context) ([]Channel, error) GetChannelPeerSuggestions(ctx context.Context) ([]alby.ChannelPeerSuggestion, error) ResetRouter(key string) error ChangeUnlockPassword(changeUnlockPasswordRequest *ChangeUnlockPasswordRequest) error @@ -285,3 +285,22 @@ type WalletCapabilitiesResponse struct { Methods []string `json:"methods"` NotificationTypes []string `json:"notificationTypes"` } + +type Channel struct { + LocalBalance int64 `json:"localBalance"` + LocalSpendableBalance int64 `json:"localSpendableBalance"` + RemoteBalance int64 `json:"remoteBalance"` + Id string `json:"id"` + RemotePubkey string `json:"remotePubkey"` + FundingTxId string `json:"fundingTxId"` + Active bool `json:"active"` + Public bool `json:"public"` + InternalChannel interface{} `json:"internalChannel"` + Confirmations *uint32 `json:"confirmations"` + ConfirmationsRequired *uint32 `json:"confirmationsRequired"` + ForwardingFeeBaseMsat uint32 `json:"forwardingFeeBaseMsat"` + UnspendablePunishmentReserve uint64 `json:"unspendablePunishmentReserve"` + CounterpartyUnspendablePunishmentReserve uint64 `json:"counterpartyUnspendablePunishmentReserve"` + Error *string `json:"error"` + Status string `json:"status"` +} diff --git a/frontend/src/components/channels/ChannelWarning.tsx b/frontend/src/components/channels/ChannelWarning.tsx new file mode 100644 index 00000000..4cf8244a --- /dev/null +++ b/frontend/src/components/channels/ChannelWarning.tsx @@ -0,0 +1,45 @@ +import { AlertTriangle } from "lucide-react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "src/components/ui/tooltip"; +import { Channel } from "src/types"; + +type ChannelWarningProps = { + channel: Channel; +}; + +export function ChannelWarning({ channel }: ChannelWarningProps) { + const capacity = channel.localBalance + channel.remoteBalance; + let channelWarning = channel.error; + if (!channelWarning && channel.status === "opening") { + channelWarning = `Channel is currently being opened (${channel.confirmations} of ${channel.confirmationsRequired} confirmations). Once the required confirmation are reached, you will be able to send and receive on this channel.`; + } + if (!channelWarning && channel.status === "offline") { + channelWarning = + "This channel is currently offline and cannot be used to send or receive payments. Please contact Alby Support for more information."; + } + if (!channelWarning && channel.localSpendableBalance > capacity * 0.9) { + channelWarning = + "Receiving capacity low. You may have trouble receiving payments through this channel."; + } + + if (!channelWarning) { + return null; + } + + return ( + + + + + + + {channelWarning} + + + + ); +} diff --git a/frontend/src/components/channels/ChannelsCards.tsx b/frontend/src/components/channels/ChannelsCards.tsx index 9e47d74f..d72b99a2 100644 --- a/frontend/src/components/channels/ChannelsCards.tsx +++ b/frontend/src/components/channels/ChannelsCards.tsx @@ -1,14 +1,14 @@ import { - AlertTriangle, ExternalLinkIcon, HandCoins, InfoIcon, MoreHorizontal, Trash2, } from "lucide-react"; +import { ChannelWarning } from "src/components/channels/ChannelWarning"; import ExternalLink from "src/components/ExternalLink"; import { Badge } from "src/components/ui/badge.tsx"; -import { Button } from "src/components/ui/button.tsx"; +import { Button, ExternalLinkButton } from "src/components/ui/button.tsx"; import { Card, CardContent, @@ -23,6 +23,7 @@ import { DropdownMenuTrigger, } from "src/components/ui/dropdown-menu.tsx"; import { Progress } from "src/components/ui/progress.tsx"; +import { Separator } from "src/components/ui/separator"; import { Tooltip, TooltipContent, @@ -56,7 +57,7 @@ export function ChannelsCards({ return ( <>

Channels

-
+
{channels .sort((a, b) => a.localBalance + a.remoteBalance > b.localBalance + b.remoteBalance @@ -69,58 +70,26 @@ export function ChannelsCards({ ); const alias = node?.alias || "Unknown"; const capacity = channel.localBalance + channel.remoteBalance; - // TODO: remove duplication - let channelWarning = ""; - if (channel.error) { - channelWarning = channel.error; - } else { - if (channel.localSpendableBalance < capacity * 0.1) { - channelWarning = - "Spending balance low. You may have trouble sending payments through this channel."; - } - if (channel.localSpendableBalance > capacity * 0.9) { - channelWarning = - "Receiving capacity low. You may have trouble receiving payments through this channel."; - } - } - - const channelStatus = channel.active - ? "online" - : channel.confirmationsRequired !== undefined && - channel.confirmations !== undefined && - channel.confirmationsRequired > channel.confirmations - ? "opening" - : "offline"; - if (channelStatus === "opening") { - channelWarning = `Channel is currently being opened (${channel.confirmations} of ${channel.confirmationsRequired} confirmations). Once the required confirmation are reached, you will be able to send and receive on this channel.`; - } - if (channelStatus === "offline") { - channelWarning = - "This channel is currently offline and cannot be used to send or receive payments. Please contact Alby Support for more information."; - } return ( - +
- - - + {alias} +
@@ -159,32 +128,44 @@ export function ChannelsCards({
+
-

Status

-
- {channelStatus == "online" ? ( - Online - ) : channelStatus == "opening" ? ( - Opening - ) : ( - Offline - )} - - {channel.public ? "Public" : "Private"} - -
+

+ Status +

+ {channel.status == "online" ? ( + Online + ) : channel.status == "opening" ? ( + Opening + ) : ( + Offline + )} +
+
+

+ Type +

+

+ {channel.public ? "Public" : "Private"} +

-

Capacity

- {formatAmount(capacity)} sats +

+ Capacity +

+

+ {formatAmount(capacity)} sats +

- Reserve +

+ Reserve +

@@ -197,28 +178,39 @@ export function ChannelsCards({
- {channel.localBalance < - channel.unspendablePunishmentReserve * 1000 && ( - <> - {formatAmount( - Math.min( - channel.localBalance, - channel.unspendablePunishmentReserve * 1000 - ) - )}{" "} - /{" "} - - )} - {formatAmount( - channel.unspendablePunishmentReserve * 1000 - )}{" "} - sats +

+ {channel.localBalance < + channel.unspendablePunishmentReserve * 1000 && ( + <> + {formatAmount( + Math.min( + channel.localBalance, + channel.unspendablePunishmentReserve * 1000 + ) + )}{" "} + /{" "} + + )} + {formatAmount( + channel.unspendablePunishmentReserve * 1000 + )}{" "} + sats +

- -
+ + +
+

+ Spending +

+

+ Receiving +

+
+
- {channelWarning ? ( - - - - - - - {channelWarning} - - - - ) : null} +
diff --git a/frontend/src/components/channels/ChannelsTable.tsx b/frontend/src/components/channels/ChannelsTable.tsx index 419f239e..a76600f6 100644 --- a/frontend/src/components/channels/ChannelsTable.tsx +++ b/frontend/src/components/channels/ChannelsTable.tsx @@ -1,11 +1,11 @@ import { - AlertTriangle, ExternalLinkIcon, HandCoins, InfoIcon, MoreHorizontal, Trash2, } from "lucide-react"; +import { ChannelWarning } from "src/components/channels/ChannelWarning"; import ExternalLink from "src/components/ExternalLink"; import Loading from "src/components/Loading.tsx"; import { Badge } from "src/components/ui/badge.tsx"; @@ -56,7 +56,7 @@ export function ChannelsTable({ } return ( -
+
@@ -108,41 +108,12 @@ export function ChannelsTable({ const alias = node?.alias || "Unknown"; const capacity = channel.localBalance + channel.remoteBalance; - let channelWarning = ""; - if (channel.error) { - channelWarning = channel.error; - } else { - if (channel.localSpendableBalance < capacity * 0.1) { - channelWarning = - "Spending balance low. You may have trouble sending payments through this channel."; - } - if (channel.localSpendableBalance > capacity * 0.9) { - channelWarning = - "Receiving capacity low. You may have trouble receiving payments through this channel."; - } - } - - const channelStatus = channel.active - ? "online" - : channel.confirmationsRequired !== undefined && - channel.confirmations !== undefined && - channel.confirmationsRequired > channel.confirmations - ? "opening" - : "offline"; - if (channelStatus === "opening") { - channelWarning = `Channel is currently being opened (${channel.confirmations} of ${channel.confirmationsRequired} confirmations). Once the required confirmation are reached, you will be able to send and receive on this channel.`; - } - if (channelStatus === "offline") { - channelWarning = - "This channel is currently offline and cannot be used to send or receive payments. Please contact Alby Support for more information."; - } - return ( - {channelStatus == "online" ? ( + {channel.status == "online" ? ( Online - ) : channelStatus == "opening" ? ( + ) : channel.status == "opening" ? ( Opening ) : ( Offline @@ -211,18 +182,7 @@ export function ChannelsTable({ - {channelWarning ? ( - - - - - - - {channelWarning} - - - - ) : null} + diff --git a/frontend/src/types.ts b/frontend/src/types.ts index b26c4d51..a3eb7962 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -195,6 +195,7 @@ export type Channel = { unspendablePunishmentReserve: number; counterpartyUnspendablePunishmentReserve: number; error?: string; + status: "online" | "opening" | "offline"; }; export type UpdateChannelRequest = { diff --git a/lnclient/ldk/ldk.go b/lnclient/ldk/ldk.go index 35bea447..0146f87e 100644 --- a/lnclient/ldk/ldk.go +++ b/lnclient/ldk/ldk.go @@ -263,6 +263,7 @@ func NewLDKService(ctx context.Context, cfg config.Config, eventPublisher events case <-time.After(MIN_SYNC_INTERVAL): ls.syncing = true // always update fee rates to avoid differences in fee rates with channel partners + logger.Logger.Info("Updating fee estimates") err = node.UpdateFeeEstimates() if err != nil { logger.Logger.WithError(err).Error("Failed to update fee estimates") diff --git a/lnclient/models.go b/lnclient/models.go index 8ff75a12..513bc49a 100644 --- a/lnclient/models.go +++ b/lnclient/models.go @@ -79,21 +79,21 @@ type LNClient interface { } type Channel struct { - LocalBalance int64 `json:"localBalance"` - LocalSpendableBalance int64 `json:"localSpendableBalance"` - RemoteBalance int64 `json:"remoteBalance"` - Id string `json:"id"` - RemotePubkey string `json:"remotePubkey"` - FundingTxId string `json:"fundingTxId"` - Active bool `json:"active"` - Public bool `json:"public"` - InternalChannel interface{} `json:"internalChannel"` - Confirmations *uint32 `json:"confirmations"` - ConfirmationsRequired *uint32 `json:"confirmationsRequired"` - ForwardingFeeBaseMsat uint32 `json:"forwardingFeeBaseMsat"` - UnspendablePunishmentReserve uint64 `json:"unspendablePunishmentReserve"` - CounterpartyUnspendablePunishmentReserve uint64 `json:"counterpartyUnspendablePunishmentReserve"` - Error *string `json:"error"` + LocalBalance int64 + LocalSpendableBalance int64 + RemoteBalance int64 + Id string + RemotePubkey string + FundingTxId string + Active bool + Public bool + InternalChannel interface{} + Confirmations *uint32 + ConfirmationsRequired *uint32 + ForwardingFeeBaseMsat uint32 + UnspendablePunishmentReserve uint64 + CounterpartyUnspendablePunishmentReserve uint64 + Error *string } type NodeStatus struct { From 23f58f09ac6357a41309bc123ebacf109aeaf6af Mon Sep 17 00:00:00 2001 From: Roland Bewick Date: Tue, 30 Jul 2024 17:23:18 +0700 Subject: [PATCH 03/10] fix: border rounding --- frontend/src/components/channels/ChannelsTable.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/channels/ChannelsTable.tsx b/frontend/src/components/channels/ChannelsTable.tsx index a76600f6..4e1527a1 100644 --- a/frontend/src/components/channels/ChannelsTable.tsx +++ b/frontend/src/components/channels/ChannelsTable.tsx @@ -56,7 +56,7 @@ export function ChannelsTable({ } return ( -
+
From 8df8062a493029aecb0f0c8620d0683b25aa6b89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 30 Jul 2024 13:16:35 +0200 Subject: [PATCH 04/10] fix: badge styles --- frontend/src/components/channels/ChannelsCards.tsx | 2 +- frontend/src/components/ui/badge.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/channels/ChannelsCards.tsx b/frontend/src/components/channels/ChannelsCards.tsx index d72b99a2..f803ca6a 100644 --- a/frontend/src/components/channels/ChannelsCards.tsx +++ b/frontend/src/components/channels/ChannelsCards.tsx @@ -77,7 +77,7 @@ export function ChannelsCards({
-
+
Date: Tue, 30 Jul 2024 13:48:23 +0200 Subject: [PATCH 05/10] fix: move link --- .../src/components/channels/ChannelsCards.tsx | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/frontend/src/components/channels/ChannelsCards.tsx b/frontend/src/components/channels/ChannelsCards.tsx index f803ca6a..ed3e99d1 100644 --- a/frontend/src/components/channels/ChannelsCards.tsx +++ b/frontend/src/components/channels/ChannelsCards.tsx @@ -8,7 +8,7 @@ import { import { ChannelWarning } from "src/components/channels/ChannelWarning"; import ExternalLink from "src/components/ExternalLink"; import { Badge } from "src/components/ui/badge.tsx"; -import { Button, ExternalLinkButton } from "src/components/ui/button.tsx"; +import { Button } from "src/components/ui/button.tsx"; import { Card, CardContent, @@ -77,14 +77,8 @@ export function ChannelsCards({
-
- - {alias} - +
+ {alias}
@@ -102,6 +96,15 @@ export function ChannelsCards({

View Funding Transaction

+ + + +

View Node on amboss.space

+
+
{channel.public && ( Date: Tue, 30 Jul 2024 14:03:21 +0200 Subject: [PATCH 06/10] fix: move link to context menu --- .../src/components/channels/ChannelsCards.tsx | 2 +- .../src/components/channels/ChannelsTable.tsx | 22 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/channels/ChannelsCards.tsx b/frontend/src/components/channels/ChannelsCards.tsx index ed3e99d1..37834eb8 100644 --- a/frontend/src/components/channels/ChannelsCards.tsx +++ b/frontend/src/components/channels/ChannelsCards.tsx @@ -83,7 +83,7 @@ export function ChannelsCards({ diff --git a/frontend/src/components/channels/ChannelsTable.tsx b/frontend/src/components/channels/ChannelsTable.tsx index 4e1527a1..acccf1b8 100644 --- a/frontend/src/components/channels/ChannelsTable.tsx +++ b/frontend/src/components/channels/ChannelsTable.tsx @@ -119,17 +119,8 @@ export function ChannelsTable({ Offline )} - - - - + + {alias} {channel.public ? "Public" : "Private"} @@ -201,6 +192,15 @@ export function ChannelsTable({

View Funding Transaction

+ + + +

View Node on amboss.space

+
+
{channel.public && ( Date: Tue, 30 Jul 2024 14:03:40 +0200 Subject: [PATCH 07/10] fix: add positive colors to other themes --- frontend/src/themes/alby.css | 12 ++++++++++++ frontend/src/themes/bitcoin.css | 3 +++ frontend/src/themes/default.css | 13 +++++++++++-- frontend/src/themes/nostr.css | 3 +++ 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/frontend/src/themes/alby.css b/frontend/src/themes/alby.css index db52a422..a7984528 100644 --- a/frontend/src/themes/alby.css +++ b/frontend/src/themes/alby.css @@ -1,23 +1,35 @@ .theme-alby { --background: 0 0% 100%; --foreground: 0 0% 5%; + --card: 0 0% 100%; --card-foreground: 0 0% 5%; + --popover: 0 0% 100%; --popover-foreground: 0 0% 5%; + --primary: 47 100% 72%; --primary-foreground: 0 0% 2%; + --secondary: 0 0% 96%; --secondary-foreground: 0 0% 5%; + --muted: 0 0% 96%; --muted-foreground: 0 0% 45%; + --accent: 0 0% 96%; --accent-foreground: 0 0% 5%; + --destructive: 0 84% 60%; --destructive-foreground: 0 0% 98%; + + --positive: 138, 68%, 96%; + --positive-foreground: 142 76% 36%; + --border: 0 0% 92%; --input: 0 0% 85%; --ring: 0 0% 76%; + --radius: 0.5rem; } diff --git a/frontend/src/themes/bitcoin.css b/frontend/src/themes/bitcoin.css index eb7cd837..e00791d5 100644 --- a/frontend/src/themes/bitcoin.css +++ b/frontend/src/themes/bitcoin.css @@ -23,6 +23,9 @@ --destructive: 0 84% 60%; --destructive-foreground: 0 0% 98%; + --positive: 138, 68%, 96%; + --positive-foreground: 142 76% 36%; + --border: 0 0% 92%; --input: 0 0% 85%; --ring: 0 0% 76%; diff --git a/frontend/src/themes/default.css b/frontend/src/themes/default.css index 64a4a1bd..5c1e4ef3 100644 --- a/frontend/src/themes/default.css +++ b/frontend/src/themes/default.css @@ -1,25 +1,34 @@ .theme-default { --background: 0 0% 100%; --foreground: 240 10% 3.9%; + --card: 0 0% 100%; --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; --popover-foreground: 240 10% 3.9%; + --primary: 240 5.9% 10%; --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; --destructive-foreground: 0 0% 98%; + + --positive: 138, 68%, 96%; + --positive-foreground: 142 76% 36%; + --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 240 5.9% 10%; - --positive: 138, 68%, 96%; - --positive-foreground: 142 76% 36%; --radius: 0.5rem; } diff --git a/frontend/src/themes/nostr.css b/frontend/src/themes/nostr.css index 01b9c732..adb111d3 100644 --- a/frontend/src/themes/nostr.css +++ b/frontend/src/themes/nostr.css @@ -23,6 +23,9 @@ --destructive: 0 84% 60%; --destructive-foreground: 0 0% 98%; + --positive: 138, 68%, 96%; + --positive-foreground: 142 76% 36%; + --border: 270 9% 87%; --input: 267 9% 81%; --ring: 273 36% 72%; From 94063b9dce29dbd626649a0e56bb5f9e14281f31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 30 Jul 2024 14:13:27 +0200 Subject: [PATCH 08/10] fix: logo clipPath --- frontend/src/components/icons/AlbyHubLogo.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/icons/AlbyHubLogo.tsx b/frontend/src/components/icons/AlbyHubLogo.tsx index abd42e0b..48a1783c 100644 --- a/frontend/src/components/icons/AlbyHubLogo.tsx +++ b/frontend/src/components/icons/AlbyHubLogo.tsx @@ -10,7 +10,7 @@ export function AlbyHubLogo(props: SVGAttributes) { xmlns="http://www.w3.org/2000/svg" {...props} > - + Date: Tue, 30 Jul 2024 14:17:56 +0200 Subject: [PATCH 09/10] fix: introduce warning color + badge --- frontend/src/components/channels/ChannelsCards.tsx | 2 +- frontend/src/components/channels/ChannelsTable.tsx | 2 +- frontend/src/components/ui/badge.tsx | 1 + frontend/src/themes/alby.css | 3 +++ frontend/src/themes/bitcoin.css | 3 +++ frontend/src/themes/default.css | 3 +++ frontend/src/themes/nostr.css | 3 +++ frontend/tailwind.config.js | 4 ++++ 8 files changed, 19 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/channels/ChannelsCards.tsx b/frontend/src/components/channels/ChannelsCards.tsx index 37834eb8..164e13d0 100644 --- a/frontend/src/components/channels/ChannelsCards.tsx +++ b/frontend/src/components/channels/ChannelsCards.tsx @@ -142,7 +142,7 @@ export function ChannelsCards({ ) : channel.status == "opening" ? ( Opening ) : ( - Offline + Offline )}
diff --git a/frontend/src/components/channels/ChannelsTable.tsx b/frontend/src/components/channels/ChannelsTable.tsx index acccf1b8..aaf9cec2 100644 --- a/frontend/src/components/channels/ChannelsTable.tsx +++ b/frontend/src/components/channels/ChannelsTable.tsx @@ -116,7 +116,7 @@ export function ChannelsTable({ ) : channel.status == "opening" ? ( Opening ) : ( - Offline + Offline )} diff --git a/frontend/src/components/ui/badge.tsx b/frontend/src/components/ui/badge.tsx index c4a49249..9baa39ac 100644 --- a/frontend/src/components/ui/badge.tsx +++ b/frontend/src/components/ui/badge.tsx @@ -15,6 +15,7 @@ const badgeVariants = cva( destructive: "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", positive: "border-transparent bg-positive text-positive-foreground", + warning: "border-transparent bg-warning text-warning-foreground", outline: "text-foreground", }, }, diff --git a/frontend/src/themes/alby.css b/frontend/src/themes/alby.css index a7984528..117da5ad 100644 --- a/frontend/src/themes/alby.css +++ b/frontend/src/themes/alby.css @@ -58,6 +58,9 @@ --destructive: 0 84% 60%; --destructive-foreground: 0 0% 98%; + --warning: 33 90% 96%; + --warning-foreground: 21 90% 48%; + --border: 0 0% 15%; --input: 0 0% 15%; --ring: 47 100% 40%; diff --git a/frontend/src/themes/bitcoin.css b/frontend/src/themes/bitcoin.css index e00791d5..8799dab1 100644 --- a/frontend/src/themes/bitcoin.css +++ b/frontend/src/themes/bitcoin.css @@ -26,6 +26,9 @@ --positive: 138, 68%, 96%; --positive-foreground: 142 76% 36%; + --warning: 33, 90%, 96%; + --warning-foreground: 21, 90%, 48%; + --border: 0 0% 92%; --input: 0 0% 85%; --ring: 0 0% 76%; diff --git a/frontend/src/themes/default.css b/frontend/src/themes/default.css index 5c1e4ef3..cef648a0 100644 --- a/frontend/src/themes/default.css +++ b/frontend/src/themes/default.css @@ -26,6 +26,9 @@ --positive: 138, 68%, 96%; --positive-foreground: 142 76% 36%; + --warning: 33, 90%, 96%; + --warning-foreground: 21, 90%, 48%; + --border: 240 5.9% 90%; --input: 240 5.9% 90%; --ring: 240 5.9% 10%; diff --git a/frontend/src/themes/nostr.css b/frontend/src/themes/nostr.css index adb111d3..fbe5734a 100644 --- a/frontend/src/themes/nostr.css +++ b/frontend/src/themes/nostr.css @@ -26,6 +26,9 @@ --positive: 138, 68%, 96%; --positive-foreground: 142 76% 36%; + --warning: 33, 90%, 96%; + --warning-foreground: 21, 90%, 48%; + --border: 270 9% 87%; --input: 267 9% 81%; --ring: 273 36% 72%; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index afa21e7a..f2835e2a 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -61,6 +61,10 @@ module.exports = { DEFAULT: "hsl(var(--positive))", foreground: "hsl(var(--positive-foreground))", }, + warning: { + DEFAULT: "hsl(var(--warning))", + foreground: "hsl(var(--warning-foreground))", + }, }, borderRadius: { lg: "var(--radius)", From fcf9760fc5aa18eb175942ac9b806d18f1f1e193 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Aaron?= Date: Tue, 30 Jul 2024 14:27:12 +0200 Subject: [PATCH 10/10] fix: nesting warning --- frontend/src/components/SidebarHint.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/frontend/src/components/SidebarHint.tsx b/frontend/src/components/SidebarHint.tsx index 188e8484..319d8570 100644 --- a/frontend/src/components/SidebarHint.tsx +++ b/frontend/src/components/SidebarHint.tsx @@ -5,7 +5,6 @@ import { Button } from "src/components/ui/button"; import { Card, CardContent, - CardDescription, CardHeader, CardTitle, } from "src/components/ui/card"; @@ -90,9 +89,9 @@ function SidebarHintCard({ {title} - {description} +
{description}