From bdedcba4985b53b2b074e8341b5167fd64f90e17 Mon Sep 17 00:00:00 2001 From: 2ro <17595647+2ro@users.noreply.github.com> Date: Sun, 5 Jul 2026 13:11:23 -0400 Subject: [PATCH] goblin: appearance settings additions for Build 140 Three settings-surface additions: - Hide amounts: a toggle (same switch widget as the incoming-requests toggle) under the amount-privacy rows, persisted in the app config (serde-default off so old configs load). When on, the received-payment notification masks the numeric grin with dots. - Language: a picker under Appearance beside the theme row. Lists the six shipped locales in their own names; tapping one switches and persists the active locale. - Update available: a badge pinned to the right of the settings profile panel, shown only when the release check has found a newer build (reuses the existing AppConfig::app_update state); tapping it opens the release download page. New user-facing strings added to all six locales. --- locales/de.yml | 4 ++ locales/en.yml | 4 ++ locales/fr.yml | 4 ++ locales/ru.yml | 4 ++ locales/tr.yml | 4 ++ locales/zh-CN.yml | 4 ++ src/gui/views/goblin/mod.rs | 96 +++++++++++++++++++++++++++++++++++++ src/nostr/client.rs | 8 +++- src/settings/config.rs | 18 +++++++ 9 files changed, 145 insertions(+), 1 deletion(-) diff --git a/locales/de.yml b/locales/de.yml index 48431a53..22f81766 100644 --- a/locales/de.yml +++ b/locales/de.yml @@ -487,6 +487,10 @@ goblin: requests: "Anfragen" incoming_requests: "Eingehende Anfragen" incoming_requests_sub: "Erlaube anderen, Geld von dir anzufordern" + hide_amounts: "Beträge verbergen" + hide_amounts_sub: "Empfangene Beträge in Benachrichtigungen verbergen" + language: "Sprache" + update_available: "Update verfügbar" appearance: "Erscheinungsbild" theme: "Design" theme_light: "Hell" diff --git a/locales/en.yml b/locales/en.yml index 293d7e7a..a8ba4839 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -487,6 +487,10 @@ goblin: requests: "Requests" incoming_requests: "Incoming requests" incoming_requests_sub: "Let others request money from you" + hide_amounts: "Hide amounts" + hide_amounts_sub: "Hide received amounts in notifications" + language: "Language" + update_available: "Update available" appearance: "Appearance" theme: "Theme" theme_light: "Light" diff --git a/locales/fr.yml b/locales/fr.yml index 96c24e07..27423c43 100644 --- a/locales/fr.yml +++ b/locales/fr.yml @@ -487,6 +487,10 @@ goblin: requests: "Demandes" incoming_requests: "Demandes entrantes" incoming_requests_sub: "Laisser les autres vous demander de l'argent" + hide_amounts: "Masquer les montants" + hide_amounts_sub: "Masquer les montants reçus dans les notifications" + language: "Langue" + update_available: "Mise à jour disponible" appearance: "Apparence" theme: "Thème" theme_light: "Clair" diff --git a/locales/ru.yml b/locales/ru.yml index 847b2b64..67465321 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -487,6 +487,10 @@ goblin: requests: "Запросы" incoming_requests: "Входящие запросы" incoming_requests_sub: "Разрешить другим запрашивать у вас деньги" + hide_amounts: "Скрыть суммы" + hide_amounts_sub: "Скрывать полученные суммы в уведомлениях" + language: "Язык" + update_available: "Доступно обновление" appearance: "Внешний вид" theme: "Тема" theme_light: "Светлая" diff --git a/locales/tr.yml b/locales/tr.yml index 800e8492..d1ba5c61 100644 --- a/locales/tr.yml +++ b/locales/tr.yml @@ -487,6 +487,10 @@ goblin: requests: "İstekler" incoming_requests: "Gelen istekler" incoming_requests_sub: "Başkalarının senden para istemesine izin ver" + hide_amounts: "Tutarları gizle" + hide_amounts_sub: "Bildirimlerde alınan tutarları gizle" + language: "Dil" + update_available: "Güncelleme mevcut" appearance: "Görünüm" theme: "Tema" theme_light: "Açık" diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index fb16e383..efa2f397 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -487,6 +487,10 @@ goblin: requests: "请求" incoming_requests: "收到的请求" incoming_requests_sub: "允许他人向你请求付款" + hide_amounts: "隐藏金额" + hide_amounts_sub: "在通知中隐藏收到的金额" + language: "语言" + update_available: "有可用更新" appearance: "外观" theme: "主题" theme_light: "浅色" diff --git a/src/gui/views/goblin/mod.rs b/src/gui/views/goblin/mod.rs index 28c64996..46127f22 100644 --- a/src/gui/views/goblin/mod.rs +++ b/src/gui/views/goblin/mod.rs @@ -138,6 +138,7 @@ enum SettingsPage { Relays, Nips, Pairing, + Language, Slatepack, Privacy, Advanced, @@ -2430,6 +2431,7 @@ impl GoblinWalletView { SettingsPage::Relays => return self.relays_ui(ui, wallet, cb), SettingsPage::Nips => return self.nips_ui(ui), SettingsPage::Pairing => return self.pairing_settings_ui(ui), + SettingsPage::Language => return self.language_settings_ui(ui), SettingsPage::Slatepack => return self.slatepack_ui(ui, wallet, cb), SettingsPage::Privacy => return self.privacy_ui(ui), SettingsPage::Advanced => return self.advanced_ui(ui, wallet, cb), @@ -2535,6 +2537,31 @@ impl GoblinWalletView { .request_repaint_after(std::time::Duration::from_millis(600)); } }); + // Update-available badge, pinned to the right of the profile + // panel. Shown only when the release check found a newer build + // (reuses the AppConfig::app_update state the wallet list uses); + // tapping it opens the release download page. + if let Some(update) = crate::AppConfig::app_update() { + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + let (rect, resp) = + ui.allocate_exact_size(Vec2::splat(36.0), Sense::click()); + ui.painter().circle_filled(rect.center(), 18.0, t.accent); + ui.painter().text( + rect.center(), + egui::Align2::CENTER_CENTER, + crate::gui::icons::CLOUD_ARROW_DOWN, + FontId::new(18.0, fonts::regular()), + t.accent_ink, + ); + if resp + .on_hover_cursor(egui::CursorIcon::PointingHand) + .on_hover_text(t!("goblin.settings.update_available").to_string()) + .clicked() + { + open_url(ui, &update.url); + } + }); + } }); }); @@ -2759,6 +2786,16 @@ impl GoblinWalletView { ) { open_pairing = true; } + // Hide received amounts in payment notifications/alerts. Same + // switch widget as the incoming-requests toggle below. + if let Some(v) = settings_row_toggle( + ui, + &t!("goblin.settings.hide_amounts"), + &t!("goblin.settings.hide_amounts_sub"), + crate::AppConfig::hide_amounts(), + ) { + crate::AppConfig::set_hide_amounts(v); + } }); if open_pairing { self.settings_page = SettingsPage::Pairing; @@ -2790,6 +2827,7 @@ impl GoblinWalletView { ui.add_space(16.0); w::kicker(ui, &t!("goblin.settings.appearance")); ui.add_space(8.0); + let mut open_language = false; w::card(ui, |ui| { let theme_label = match crate::AppConfig::theme() { crate::gui::theme::ThemeKind::Light => t!("goblin.settings.theme_light"), @@ -2799,7 +2837,21 @@ impl GoblinWalletView { if settings_row_btn(ui, &t!("goblin.settings.theme"), &theme_label) { cycle_theme(ui.ctx()); } + // Language sits beside theme under Appearance; the value is the + // active language in its own name (e.g. "Deutsch"). + let current = crate::AppConfig::locale() + .unwrap_or_else(|| rust_i18n::locale().to_string()); + if settings_row_nav( + ui, + &t!("goblin.settings.language"), + &t!("lang_name", locale = current.as_str()), + ) { + open_language = true; + } }); + if open_language { + self.settings_page = SettingsPage::Language; + } ui.add_space(16.0); w::kicker(ui, &t!("goblin.settings.archive")); @@ -2999,6 +3051,50 @@ impl GoblinWalletView { }); } + /// Language picker: the six shipped locales, each in its own name. Tapping one + /// switches the active locale and persists it (mirrors the GRIM interface + /// settings, but in Goblin's row style like the pairing picker). + fn language_settings_ui(&mut self, ui: &mut egui::Ui) { + let t = theme::tokens(); + if self.sub_header(ui, &t!("goblin.settings.language")) { + self.settings_page = SettingsPage::Main; + return; + } + let current = crate::AppConfig::locale().unwrap_or_else(|| rust_i18n::locale().to_string()); + ScrollArea::vertical() + .auto_shrink([false; 2]) + .scroll_bar_visibility(egui::scroll_area::ScrollBarVisibility::AlwaysHidden) + .show(ui, |ui| { + settings_group(ui, &t!("goblin.settings.language"), |ui| { + for locale in rust_i18n::available_locales!() { + let active = current == locale; + let row = ui.horizontal(|ui| { + ui.label( + RichText::new(t!("lang_name", locale = locale)) + .font(FontId::new(15.0, fonts::medium())) + .color(t.surface_text), + ); + ui.with_layout(Layout::right_to_left(Align::Center), |ui| { + if active { + ui.label( + RichText::new(crate::gui::icons::CHECK) + .font(FontId::new(16.0, fonts::regular())) + .color(t.pos), + ); + } + }); + }); + ui.add_space(10.0); + if !active && row.response.interact(Sense::click()).clicked() { + rust_i18n::set_locale(locale); + crate::AppConfig::save_locale(locale); + } + } + }); + ui.add_space(16.0); + }); + } + /// Network-privacy breakdown: what rides the Nym mixnet versus what connects /// directly. Honest by design — no claim that node traffic is mixed, and no /// toggle to route it (chain sync is heavy and not tied to your identity). diff --git a/src/nostr/client.rs b/src/nostr/client.rs index 90747905..0c27ef9b 100644 --- a/src/nostr/client.rs +++ b/src/nostr/client.rs @@ -2144,7 +2144,13 @@ async fn handle_wrap(svc: &Arc, wallet: &Wallet, event: Event) { { let name = crate::gui::views::goblin::data::contact_title(&svc.store, &sender_hex); - let amount = amount_to_hr_string(slate.amount, true); + // Honor the "hide amounts" setting: keep the numeric grin + // out of the received-payment alert when the user opted in. + let amount = if crate::AppConfig::hide_amounts() { + "•••".to_string() + } else { + amount_to_hr_string(slate.amount, true) + }; crate::notify_payment_received(&name, &amount); } match svc diff --git a/src/settings/config.rs b/src/settings/config.rs index 3e25b38c..0e417561 100644 --- a/src/settings/config.rs +++ b/src/settings/config.rs @@ -94,6 +94,10 @@ pub struct AppConfig { /// Application update information. app_update: Option, + /// Hide received grin amounts in payment notifications/alerts. Default false + /// so existing configs (and new wallets) keep showing the amount. + hide_amounts: Option, + /// Last-known-good Nym ENTRY gateway (base58 identity). Only the gateway /// CHOICE is remembered — the mixnet keys stay ephemeral — so a warm reconnect /// can skip re-picking a (possibly dead) random first hop. @@ -212,6 +216,7 @@ impl Default for AppConfig { // update check — payments, relays and identity still stay mixnet-only. check_updates: Some(true), app_update: None, + hide_amounts: None, nym_entry_gateway: None, nym_last_ipr: None, } @@ -624,6 +629,19 @@ impl AppConfig { } w_config.save(); } + + /// Whether received grin amounts are hidden in payment notifications/alerts. + pub fn hide_amounts() -> bool { + let r_config = Settings::app_config_to_read(); + r_config.hide_amounts.unwrap_or(false) + } + + /// Set whether received grin amounts are hidden in notifications/alerts. + pub fn set_hide_amounts(hide: bool) { + let mut w_config = Settings::app_config_to_update(); + w_config.hide_amounts = Some(hide); + w_config.save(); + } } #[cfg(test)]