From be42941b973b2e9dc145eb1bb2c1f8019d7fd246 Mon Sep 17 00:00:00 2001 From: Mollusk Date: Tue, 16 Jun 2026 14:12:29 -0400 Subject: [PATCH] feat(home): presence selector as a compact dropdown instead of 3 radios Replace the three tooltip'd presence radios in the home Friends card with a pick_list dropdown + a one-line explainer for the current choice, mirroring the Settings NetworkMode picker. Add PresenceMode::ALL + a Display impl (descriptive labels) to back the picker. Tightens the Friends card vertically. clippy --all-targets clean, 256 lib tests green. Co-Authored-By: Claude Opus 4.8 --- src/app/mod.rs | 30 +++++++++++++++--------------- src/presence.rs | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 15 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 7b867a1..0ed6554 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -1453,19 +1453,21 @@ fn friends_panel(state: &AppState) -> Element<'_, AppMessage> { .spacing(0) .width(iced::Length::Fill); - // Presence posture radios (each with a hover explainer). - let presence_radio = |mode: PresenceMode, label: &'static str| -> Element<'_, AppMessage> { - tooltip( - radio(label, mode, Some(state.config.presence_mode), AppMessage::PresenceModeSelected), - container(text(presence_mode_hint(mode)).size(11).color(color_text)) - .padding(8) - .max_width(300.0) - .style(c_style(color_crust, color_surface, 6.0)), - iced::widget::tooltip::Position::Bottom, + // Presence posture: a compact dropdown (mirrors the Settings NetworkMode + // picker) with a one-line explainer for the current choice below it. + let presence_picker = column![ + pick_list( + &PresenceMode::ALL[..], + Some(state.config.presence_mode), + AppMessage::PresenceModeSelected, ) - .gap(8) - .into() - }; + .width(iced::Length::Fill), + text(presence_mode_hint(state.config.presence_mode)) + .size(11) + .color(color_subtext), + ] + .spacing(4) + .width(iced::Length::Fill); container( column![ @@ -1481,9 +1483,7 @@ fn friends_panel(state: &AppState) -> Element<'_, AppMessage> { add_form, vertical_space(14.0), text("Your presence").size(13).color(color_subtext), - presence_radio(PresenceMode::Normal, "Normal — friends only"), - presence_radio(PresenceMode::Invisible, "Invisible — appear offline"), - presence_radio(PresenceMode::Discoverable, "Discoverable — findable after a move"), + presence_picker, ] .spacing(6), ) diff --git a/src/presence.rs b/src/presence.rs index f5dce11..80623d2 100644 --- a/src/presence.rs +++ b/src/presence.rs @@ -35,6 +35,10 @@ pub enum PresenceMode { } impl PresenceMode { + /// All postures, default first — the option list for the Settings/home picker. + pub const ALL: [PresenceMode; 3] = + [PresenceMode::Normal, PresenceMode::Invisible, PresenceMode::Discoverable]; + /// Whether this posture publishes to discovery (the only mode that does). pub fn publishes_to_discovery(self) -> bool { matches!(self, PresenceMode::Discoverable) @@ -46,6 +50,17 @@ impl PresenceMode { } } +impl std::fmt::Display for PresenceMode { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let label = match self { + PresenceMode::Normal => "Normal — friends only", + PresenceMode::Invisible => "Invisible — appear offline", + PresenceMode::Discoverable => "Discoverable — findable after a move", + }; + f.write_str(label) + } +} + /// What a peer advertises about the gathering they're currently in. Both fields /// are peer-supplied and therefore untrusted — see [`interpret_pong`], which /// sanitizes the name and validates the ticket before surfacing them.