fix(peer): serve full browse listings + harden the listener; TUI transfer/UX work

Peer layer:
- Stream peer messages with a partial-write queue instead of write_all, so a
  large SharedFileListResponse over the non-blocking socket is no longer
  truncated (third-party clients like SoulseekQt can now browse our shares).
- Bound the peer-init handshake with a read timeout and handle each accepted
  connection on its own thread, so one stalled peer can't wedge the listener.
- e2e: third-party direct/brokered/large-share browse + stalled-peer coverage.

TUI:
- Uploads share the downloads table columns (real Speed) and the info pane now
  renders details for a selected upload.
- Download progress bar counts only in-progress transfers and hides when idle.
- Always-visible unread badge (private messages + rooms) in the shortcuts bar,
  self-drawn blink so it works under tmux.
- Persist the unread private-message count across restarts.
This commit is contained in:
Michel de Graaf
2026-07-24 21:18:20 +02:00
parent 105c798855
commit be5dc6c064
24 changed files with 1503 additions and 159 deletions
+7 -2
View File
@@ -1,3 +1,4 @@
use chrono::{DateTime, Local};
use soulseek_rs::types::{RoomEvent, RoomInfo};
/// One line in a room's chat log. A `None` username marks a system line
@@ -6,19 +7,23 @@ use soulseek_rs::types::{RoomEvent, RoomInfo};
pub struct RoomLine {
pub username: Option<String>,
pub text: String,
/// Local wall-clock time the line was appended.
pub at: DateTime<Local>,
}
impl RoomLine {
const fn chat(username: String, text: String) -> Self {
fn chat(username: String, text: String) -> Self {
Self {
username: Some(username),
text,
at: Local::now(),
}
}
const fn system(text: String) -> Self {
fn system(text: String) -> Self {
Self {
username: None,
text,
at: Local::now(),
}
}
}