Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 58 additions & 1 deletion src/Pages/DynamicFeeManagement.razor
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@
<th>Channel</th>
<th class="r">Capacity</th>
<th>Local / Remote</th>
<th class="c">Category</th>
<th class="c">Dynamic fee</th>
</tr>
</thead>
Expand All @@ -203,6 +204,20 @@
@("—")
}
</td>
<td class="c">
@if (ch.RoutingState is { } rs)
{
<span class="cat-badge @CategoryClass(rs.PeerFlowCategory)">@CategoryLabel(rs.PeerFlowCategory)</span>
@if (rs.PendingCategory is { } pending)
{
<span class="cat-pending">→ @CategoryLabel(pending)</span>
}
}
else
{
<span class="cat-badge cat-none">—</span>
}
</td>
<td class="c">
@* Pending channels (ChanId 0) can't be fee-managed yet, so they're not toggleable. *@
<Check TValue="bool"
Expand Down Expand Up @@ -232,6 +247,7 @@

@inject INodeRepository NodeRepository
@inject IChannelRepository ChannelRepository
@inject IChannelRoutingStateRepository ChannelRoutingStateRepository
@inject IFeeEngineStateService FeeEngineStateService
@inject IAuditService AuditService
@inject ILightningService LightningService
Expand All @@ -247,6 +263,7 @@
private List<Channel> _channels = new();
private List<Channel> _nodeChannels = new();
private IReadOnlyDictionary<ulong, ChannelState>? _live;
private Dictionary<int, ChannelRoutingState> _routingStates = new();
private List<PeerOverview> _peers = new();
private readonly HashSet<string> _expanded = new();
private string _search = string.Empty;
Expand All @@ -273,6 +290,8 @@
var allChannels = await ChannelRepository.GetAllManagedByUserNodes(LoggedUser.Id);
_channels = allChannels.Where(c => c.Status == Channel.ChannelStatus.Open).ToList();

await LoadRoutingStatesAsync();

// Live balances are best-effort — structural stats and the toggles still work without LND.
_live = null;
try
Expand Down Expand Up @@ -336,6 +355,8 @@
ChannelState? live = null;
_live?.TryGetValue(channel.ChanId, out live);

_routingStates.TryGetValue(channel.Id, out var routingState);

overview.Channels.Add(new PeerChannelRow
{
Channel = channel,
Expand All @@ -346,6 +367,7 @@
RemoteBalanceSats = live?.RemoteBalance,
Active = live?.Active,
BelowMinSize = channel.SatsAmount < Constants.ROUTING_ENGINE_FEE_MIN_CHANNEL_SIZE_SATS,
RoutingState = routingState,
});
}

Expand All @@ -368,14 +390,32 @@
Rebuild();
}

private void OnNodeChanged()
private async Task OnNodeChanged()
{
_selectedNode = _managedNodes.FirstOrDefault(n => n.Id == _selectedNodeId);
_expanded.Clear();
_search = string.Empty;
await LoadRoutingStatesAsync();
Rebuild();
}

// Routing state is per managed node, so it's re-read whenever the selected node changes.
// Best-effort: categories just render as "—" when it can't be loaded.
private async Task LoadRoutingStatesAsync()
{
_routingStates = new();
if (_selectedNode is null) return;
try
{
var states = await ChannelRoutingStateRepository.GetByManagedNodePubKey(_selectedNode.PubKey);
_routingStates = states.ToDictionary(s => s.ChannelId);
}
catch (Exception ex)
{
Logger.LogWarning(ex, "Could not load routing state for node {PubKey}", _selectedNode.PubKey);
}
}

private IEnumerable<PeerOverview> FilteredPeers()
{
if (string.IsNullOrWhiteSpace(_search)) return _peers;
Expand Down Expand Up @@ -517,6 +557,22 @@
return (updated, failed);
}

private static string CategoryLabel(PeerFlowCategory category) => category switch
{
PeerFlowCategory.Sink => "Sink",
PeerFlowCategory.Source => "Source",
PeerFlowCategory.Bidirectional => "Bidirectional",
_ => "Uncategorized",
};

private static string CategoryClass(PeerFlowCategory category) => category switch
{
PeerFlowCategory.Sink => "cat-sink",
PeerFlowCategory.Source => "cat-source",
PeerFlowCategory.Bidirectional => "cat-bidi",
_ => "cat-uncat",
};

private static string Btc(long sats) => (sats / 100_000_000m).ToString("0.####");
private static string Sats(long sats) => sats.ToString("N0");
private static string F(double d) => d.ToString("0.##", System.Globalization.CultureInfo.InvariantCulture);
Expand Down Expand Up @@ -556,5 +612,6 @@
public long? RemoteBalanceSats { get; init; }
public bool? Active { get; init; }
public bool BelowMinSize { get; init; } // below the engine's minimum size, so it won't be fee-managed
public ChannelRoutingState? RoutingState { get; init; } // null until the engine has evaluated the channel
}
}
24 changes: 24 additions & 0 deletions src/Pages/DynamicFeeManagement.razor.css
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,30 @@ table.peers-tbl tbody tr.peer-row:hover { background: #faf9fe; }
.df-none { background: #eeedf4; color: var(--muted); }
.df-partial { background: rgba(221,164,72,.16); color: #a9791f; }

/* flow category */
.cat-badge {
font-family: var(--mono);
font-size: .68rem;
font-weight: 700;
letter-spacing: .02em;
padding: 3px 9px;
border-radius: 20px;
white-space: nowrap;
}

.cat-sink { background: rgba(221,164,72,.16); color: #a9791f; }
.cat-source { background: rgba(59,119,226,.14); color: #2a5cb8; }
.cat-bidi { background: rgba(151,51,239,.13); color: #7b23c9; }
.cat-uncat { background: #eeedf4; color: var(--muted); }
.cat-none { background: transparent; color: var(--faint); }

.cat-pending {
font-family: var(--mono);
font-size: .62rem;
color: var(--faint);
margin-left: 5px;
}

.gate-warn {
color: #a9791f;
font-size: .72rem;
Expand Down
Loading