From 4fee9f635e0276560e69269e44583b79bb63742c Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Fri, 10 Jul 2026 08:31:06 -0400 Subject: [PATCH] Advanced Privacy: lock amounts/names toggles under Hide all details When the Notifications "Hide all details" switch is on, the finer "Hide amounts" and "Hide names" switches now render forced visually ON, dimmed, and non-interactive (a new toggle_locked widget plus a settings_row_toggle_locked helper) instead of misleadingly showing OFF. The locked rows only render; they never write the hide_amounts / hide_names config, so the stored values are preserved. Turning "Hide all details" back off restores the user's real per-toggle choices and makes the switches interactive again. No copy/locale changes. --- src/gui/views/goblin/helpers.rs | 31 +++++++++++++++++++++++++++++++ src/gui/views/goblin/privacy.rs | 22 ++++++++++++++++++++-- src/gui/views/goblin/widgets.rs | 19 +++++++++++++++++++ 3 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/gui/views/goblin/helpers.rs b/src/gui/views/goblin/helpers.rs index 2a49cf5f..6a2ff1a1 100644 --- a/src/gui/views/goblin/helpers.rs +++ b/src/gui/views/goblin/helpers.rs @@ -119,6 +119,37 @@ pub(super) fn settings_row_toggle( toggled } +/// Like [`settings_row_toggle`] but the switch is forced visually ON, dimmed and +/// non-interactive, and the label/subtitle are dimmed to match — for a toggle +/// that is superseded by a higher-priority setting (e.g. "Hide all details" +/// locking on "Hide amounts"/"Hide names"). Renders only; it never reports a +/// toggle and never touches the underlying stored value, so the row snaps back +/// to the user's real choice once the governing toggle is turned off. +pub(super) fn settings_row_toggle_locked(ui: &mut egui::Ui, label: &str, sub: &str) { + let t = theme::tokens(); + ui.horizontal(|ui| { + let toggle_w = 58.0; + let text_w = (ui.available_width() - toggle_w).max(0.0); + ui.vertical(|ui| { + ui.set_width(text_w); + ui.label( + RichText::new(label) + .font(FontId::new(15.0, fonts::medium())) + .color(t.surface_text.gamma_multiply(0.5)), + ); + ui.label( + RichText::new(sub) + .font(FontId::new(13.0, fonts::regular())) + .color(t.surface_text_dim.gamma_multiply(0.7)), + ); + }); + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + w::toggle_locked(ui); + }); + }); + ui.add_space(10.0); +} + pub(super) fn settings_row(ui: &mut egui::Ui, label: &str, value: &str) { settings_row_ink(ui, label, value, theme::tokens().surface_text_dim); } diff --git a/src/gui/views/goblin/privacy.rs b/src/gui/views/goblin/privacy.rs index 17210be3..a4e63f40 100644 --- a/src/gui/views/goblin/privacy.rs +++ b/src/gui/views/goblin/privacy.rs @@ -235,7 +235,19 @@ impl GoblinWalletView { ); ui.add_space(16.0); settings_group(ui, &t!("goblin.advprivacy.notifications"), |ui| { - if let Some(v) = settings_row_toggle( + // "Hide all details" supersedes the two finer toggles: while it + // is on, they render forced-on, dimmed and locked (non-interactive) + // via settings_row_toggle_locked, which never writes their stored + // values — so turning it back off restores the user's real + // hide_amounts / hide_names choices. + let hide_all = crate::AppConfig::notif_hide_details(); + if hide_all { + settings_row_toggle_locked( + ui, + &t!("goblin.settings.hide_amounts"), + &t!("goblin.settings.hide_amounts_sub"), + ); + } else if let Some(v) = settings_row_toggle( ui, &t!("goblin.settings.hide_amounts"), &t!("goblin.settings.hide_amounts_sub"), @@ -243,7 +255,13 @@ impl GoblinWalletView { ) { crate::AppConfig::set_hide_amounts(v); } - if let Some(v) = settings_row_toggle( + if hide_all { + settings_row_toggle_locked( + ui, + &t!("goblin.advprivacy.hide_names"), + &t!("goblin.advprivacy.hide_names_sub"), + ); + } else if let Some(v) = settings_row_toggle( ui, &t!("goblin.advprivacy.hide_names"), &t!("goblin.advprivacy.hide_names_sub"), diff --git a/src/gui/views/goblin/widgets.rs b/src/gui/views/goblin/widgets.rs index 62cd15b0..da576432 100644 --- a/src/gui/views/goblin/widgets.rs +++ b/src/gui/views/goblin/widgets.rs @@ -238,6 +238,25 @@ pub fn toggle(ui: &mut Ui, on: bool) -> Response { resp.on_hover_cursor(egui::CursorIcon::PointingHand) } +/// A locked variant of [`toggle`]: always drawn in the ON state but dimmed and +/// non-interactive. Used when a higher-priority setting supersedes this one +/// (e.g. "Hide all details" forces "Hide amounts"/"Hide names" on). Same 46×28 +/// footprint as [`toggle`] so locked rows line up with live ones; allocated with +/// `Sense::hover()` (no click) and no pointing-hand cursor so taps do nothing. +pub fn toggle_locked(ui: &mut Ui) -> Response { + let t = theme::tokens(); + let (rect, resp) = ui.allocate_exact_size(Vec2::new(46.0, 28.0), Sense::hover()); + let track = t.accent.gamma_multiply(0.45); + ui.painter() + .rect_filled(rect, CornerRadius::same(14), track); + let knob_r = 11.0; + let knob_x = rect.right() - knob_r - 3.0; + let knob = t.accent_ink.gamma_multiply(0.6); + ui.painter() + .circle_filled(egui::pos2(knob_x, rect.center().y), knob_r, knob); + resp +} + /// A large on/off switch for the Network-privacy Tor toggle. Unlike [`toggle`] /// (brand yellow when on), this is dormant GRAY when OFF and blueviolet ("tor /// purple") when ON — never yellow — so the privacy state reads distinctly from