feat(tui): say so in the status bar when the listener took another port

With the login screen's own attempts no longer holding the port, a
session listens somewhere other than the configured port mainly when
another program already holds it. soulseek-rs still falls back to an
ephemeral port there, which is what lets several clients on one machine
share a configured port, and it advertises the port it really bound. A
manual port forward on the router then points at nothing, though, and
the TUI never said so; the CLI already warns.

After login the TUI compares the bound port with the configured one,
using the CLI's rule, and the status bar's title reads "port X in use,
listening on Y" for the rest of the session. A window attached to a
daemon shows nothing: the daemon owns its listener.

Nicotine+ keeps retrying the configured port every five seconds and
Soulseek.NET refuses it; neither falls back. Keeping the fallback and
making it visible is a deliberate difference.
This commit is contained in:
Michel de Graaf
2026-09-14 13:44:57 +02:00
parent bd502c216a
commit deb846be14
5 changed files with 35 additions and 6 deletions
+7 -1
View File
@@ -12,6 +12,7 @@ use ratatui::{
widgets::Paragraph,
};
use soulseek_rs::DownloadStatus;
use std::fmt::Write as _;
const VERSION: &str = env!("CARGO_PKG_VERSION");
@@ -54,6 +55,7 @@ pub fn render_download_stats(
downloads: &[DownloadEntry],
active_count: usize,
daemon: Option<&str>,
listener_fallback: Option<(u16, u16)>,
) {
let completed = downloads
.iter()
@@ -79,10 +81,14 @@ pub fn render_download_stats(
let active = active_progress(downloads);
let title = match daemon {
let mut title = match daemon {
Some(endpoint) => format!("Status · daemon {endpoint}"),
None => "Status".to_string(),
};
if let Some((configured, bound)) = listener_fallback {
let _ =
write!(title, " · port {configured} in use, listening on {bound}");
}
let block = pane_block(false).title(plain_title(&title, false));
let inner_area = block.inner(area);