feat(presence): presence-mode selector in Settings (W7 P5)
Add a 'Presence' section to Settings with three radio options wired to the persisted PresenceMode (W7): Normal (friends only, no beacon; default), Invisible (appear offline to everyone), Discoverable (also publish so friends can find you after a network change). Each has a hover tooltip explainer, mirroring the recording-mode radios. Selecting one saves config.presence_mode. The live friends listener (P4, not yet wired) will read this posture when it lands; no core command until then. 256 lib tests green, clippy clean, release builds. Solo screenshot-verifiable (the live effect needs P4 + 2 machines). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ use crate::network::PeerState;
|
|||||||
use crate::notify::{self, Sound};
|
use crate::notify::{self, Sound};
|
||||||
use crate::audio::pw_cli::{AudioDevice, enumerate_audio_devices};
|
use crate::audio::pw_cli::{AudioDevice, enumerate_audio_devices};
|
||||||
use crate::config::{AppConfig, NetworkMode, RecordingMode, RoomLayout};
|
use crate::config::{AppConfig, NetworkMode, RecordingMode, RoomLayout};
|
||||||
|
use crate::presence::PresenceMode;
|
||||||
use crate::theme::{AppTheme, Palette};
|
use crate::theme::{AppTheme, Palette};
|
||||||
|
|
||||||
use iced::widget::{
|
use iced::widget::{
|
||||||
@@ -135,6 +136,8 @@ pub enum AppMessage {
|
|||||||
NoiseGateDragging(f32),
|
NoiseGateDragging(f32),
|
||||||
NetworkModeSelected(NetworkMode),
|
NetworkModeSelected(NetworkMode),
|
||||||
RecordingModeSelected(RecordingMode),
|
RecordingModeSelected(RecordingMode),
|
||||||
|
/// Choose the friends presence posture (W7): invisible / normal / discoverable.
|
||||||
|
PresenceModeSelected(PresenceMode),
|
||||||
EventOccurred(Event),
|
EventOccurred(Event),
|
||||||
NavigateToSettings,
|
NavigateToSettings,
|
||||||
NavigateBack,
|
NavigateBack,
|
||||||
@@ -733,6 +736,12 @@ fn update(state: &mut AppState, message: AppMessage) -> Task<AppMessage> {
|
|||||||
// Takes effect on the next recording start.
|
// Takes effect on the next recording start.
|
||||||
let _ = state.controller.send(CoreCommand::SetRecordingMode(mode));
|
let _ = state.controller.send(CoreCommand::SetRecordingMode(mode));
|
||||||
}
|
}
|
||||||
|
AppMessage::PresenceModeSelected(mode) => {
|
||||||
|
state.config.presence_mode = mode;
|
||||||
|
state.config.save();
|
||||||
|
// Persisted now; the live friends listener (P4, not yet wired) reads
|
||||||
|
// this posture when it lands. No core command until then.
|
||||||
|
}
|
||||||
AppMessage::ToggleNotifications(enabled) => {
|
AppMessage::ToggleNotifications(enabled) => {
|
||||||
state.config.notifications_enabled = enabled;
|
state.config.notifications_enabled = enabled;
|
||||||
state.config.save();
|
state.config.save();
|
||||||
@@ -1009,6 +1018,21 @@ fn recording_mode_hint(mode: RecordingMode) -> &'static str {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One-line explanation of a presence posture for the settings picker (W7).
|
||||||
|
fn presence_mode_hint(mode: PresenceMode) -> &'static str {
|
||||||
|
match mode {
|
||||||
|
PresenceMode::Invisible => {
|
||||||
|
"Answer no one — appear offline to everyone, even friends."
|
||||||
|
}
|
||||||
|
PresenceMode::Normal => {
|
||||||
|
"Answer friends only, from your saved address. No presence beacon."
|
||||||
|
}
|
||||||
|
PresenceMode::Discoverable => {
|
||||||
|
"Also publish so friends can still find you after you change networks (opt-in)."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Formats a call duration as `m:ss` (or `h:mm:ss` past an hour).
|
/// Formats a call duration as `m:ss` (or `h:mm:ss` past an hour).
|
||||||
fn format_duration(total_secs: u64) -> String {
|
fn format_duration(total_secs: u64) -> String {
|
||||||
let h = total_secs / 3600;
|
let h = total_secs / 3600;
|
||||||
@@ -1475,6 +1499,30 @@ fn view(state: &AppState) -> Element<'_, AppMessage> {
|
|||||||
identity_warning,
|
identity_warning,
|
||||||
].spacing(8).width(iced::Length::Fill);
|
].spacing(8).width(iced::Length::Fill);
|
||||||
|
|
||||||
|
// --- Presence (W7) — how reachable you are to friends while idle. ---
|
||||||
|
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(320.0)
|
||||||
|
.style(c_style(color_crust, color_surface, 6.0)),
|
||||||
|
iced::widget::tooltip::Position::Right,
|
||||||
|
)
|
||||||
|
.gap(8)
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
let presence_section = column![
|
||||||
|
text("Controls whether friends can see you're online (and what room \
|
||||||
|
you're in) while the app is open. Discovery stays off unless you \
|
||||||
|
pick Discoverable.")
|
||||||
|
.size(12)
|
||||||
|
.color(color_subtext),
|
||||||
|
presence_radio(PresenceMode::Normal, "Normal — friends only, no beacon"),
|
||||||
|
presence_radio(PresenceMode::Invisible, "Invisible — appear offline"),
|
||||||
|
presence_radio(PresenceMode::Discoverable, "Discoverable — findable after a network change"),
|
||||||
|
].spacing(8).width(iced::Length::Fill);
|
||||||
|
|
||||||
let settings_content = scrollable(
|
let settings_content = scrollable(
|
||||||
column![
|
column![
|
||||||
// --- Audio Devices ---
|
// --- Audio Devices ---
|
||||||
@@ -1571,6 +1619,11 @@ fn view(state: &AppState) -> Element<'_, AppMessage> {
|
|||||||
identity_section,
|
identity_section,
|
||||||
vertical_space(section_gap),
|
vertical_space(section_gap),
|
||||||
|
|
||||||
|
// --- Presence ---
|
||||||
|
section_header("Presence"),
|
||||||
|
presence_section,
|
||||||
|
vertical_space(section_gap),
|
||||||
|
|
||||||
// --- Notifications & Sounds ---
|
// --- Notifications & Sounds ---
|
||||||
section_header("Notifications & Sounds"),
|
section_header("Notifications & Sounds"),
|
||||||
column![
|
column![
|
||||||
|
|||||||
Reference in New Issue
Block a user