From f8562e1d791a5dd6c05cfadceed01e4945b7472c Mon Sep 17 00:00:00 2001 From: Proof of Prints Date: Sun, 7 Jun 2026 00:08:06 -0500 Subject: [PATCH] feat(notifications): warn when Windows notifications are disabled + test button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows' per-app/system notification toggle being OFF silently drops toasts (.show() returns Ok, nothing appears) with no way for users to tell. - get_notification_status command queries WinRT ToastNotifier.Setting (enabled / disabledForApplication / disabledForUser / disabledByGroupPolicy / disabledByManifest). Adds the windows crate UI_Notifications feature (already in the dep tree). - NotificationBanner.tsx shows an inline warning that deep-links to ms-settings:notifications when disabled; re-checks on window focus. Rendered on the Alerts page and in Settings. - 'Send test notification' button in Settings → Desktop Notifications card calls the existing send_desktop_notification command. --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + src-tauri/src/commands/notifications.rs | 43 ++++++++++++++++++ src-tauri/src/lib.rs | 3 +- src/components/NotificationBanner.tsx | 58 +++++++++++++++++++++++++ src/pages/Alerts.tsx | 3 ++ src/pages/Settings.tsx | 46 ++++++++++++++++++++ 7 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 src/components/NotificationBanner.tsx diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index cc488f0..b6e0371 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -3440,6 +3440,7 @@ dependencies = [ "tower", "tower-http", "uuid", + "windows", "windows-sys 0.61.2", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 4a48f2c..d82b6a1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -48,6 +48,7 @@ keyring = { version = "3", features = ["windows-native", "apple-native", "sync-s [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_System_Registry", "Win32_Security"] } +windows = { version = "0.61", features = ["UI_Notifications", "Foundation"] } tauri-winrt-notification = "0.7" [profile.release] diff --git a/src-tauri/src/commands/notifications.rs b/src-tauri/src/commands/notifications.rs index 25d1d18..c5d15b1 100644 --- a/src-tauri/src/commands/notifications.rs +++ b/src-tauri/src/commands/notifications.rs @@ -58,6 +58,49 @@ pub fn setup_windows_aumid(identifier: &str, display_name: &str) { } } +/// Report whether Windows will actually surface our toasts. When the per-app or +/// system notification toggle is OFF, `Toast::show()` returns `Ok` but nothing +/// appears — users have no way to tell. The frontend uses this to show a warning +/// banner that deep-links to `ms-settings:notifications`. +/// +/// Returns one of: `enabled`, `disabledForApplication`, `disabledForUser`, +/// `disabledByGroupPolicy`, `disabledByManifest`, `unknown`. +#[cfg(target_os = "windows")] +#[tauri::command] +pub fn get_notification_status(app: tauri::AppHandle) -> Result { + use windows::core::HSTRING; + use windows::UI::Notifications::{NotificationSetting, ToastNotificationManager}; + + let identifier = app.config().identifier.clone(); + let aumid = HSTRING::from(identifier); + let notifier = ToastNotificationManager::CreateToastNotifierWithId(&aumid) + .map_err(|e| format!("Failed to create toast notifier: {}", e))?; + let setting = notifier + .Setting() + .map_err(|e| format!("Failed to read notification setting: {}", e))?; + + let s = if setting == NotificationSetting::Enabled { + "enabled" + } else if setting == NotificationSetting::DisabledForApplication { + "disabledForApplication" + } else if setting == NotificationSetting::DisabledForUser { + "disabledForUser" + } else if setting == NotificationSetting::DisabledByGroupPolicy { + "disabledByGroupPolicy" + } else if setting == NotificationSetting::DisabledByManifest { + "disabledByManifest" + } else { + "unknown" + }; + Ok(s.to_string()) +} + +#[cfg(not(target_os = "windows"))] +#[tauri::command] +pub fn get_notification_status(_app: tauri::AppHandle) -> Result { + Ok("enabled".to_string()) +} + #[tauri::command] pub fn send_desktop_notification( app: tauri::AppHandle, diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 783d024..a44a68d 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -34,7 +34,7 @@ use commands::alerts::{ }; use commands::coins::{get_coins, add_coin, remove_coin}; use commands::email::{get_smtp_config, save_smtp_config, test_smtp_config, send_alert_email}; -use commands::notifications::send_desktop_notification; +use commands::notifications::{send_desktop_notification, get_notification_status}; use commands::mute::{get_muted_devices, set_device_mute, clear_device_mute}; use commands::uptime::{record_uptime, get_uptime_stats, get_all_uptime_stats, clear_uptime_data}; use commands::export::{export_miners_csv, export_alert_history_csv, export_profitability_csv, export_farm_history_csv}; @@ -322,6 +322,7 @@ pub fn run() { test_smtp_config, send_alert_email, send_desktop_notification, + get_notification_status, add_farm_snapshot, get_farm_history, clear_farm_history, diff --git a/src/components/NotificationBanner.tsx b/src/components/NotificationBanner.tsx new file mode 100644 index 0000000..ab5d9db --- /dev/null +++ b/src/components/NotificationBanner.tsx @@ -0,0 +1,58 @@ +import { useState, useEffect, useCallback } from "react"; +import { invoke } from "@tauri-apps/api/core"; +import { open as openUrl } from "@tauri-apps/plugin-shell"; + +/** + * Warns when Windows notifications are turned off for OverManager. When the + * per-app or system toggle is OFF, toasts silently never appear — this banner + * surfaces that and deep-links to the Windows notification settings page. + * + * Renders nothing while notifications are enabled (or status can't be read). + */ +export default function NotificationBanner() { + const [status, setStatus] = useState("enabled"); + + const check = useCallback(async () => { + try { + const s = await invoke("get_notification_status"); + setStatus(s); + } catch (err) { + console.error("Failed to query notification status:", err); + setStatus("enabled"); // fail open — don't nag on an unexpected error + } + }, []); + + useEffect(() => { + check(); + // Re-check when the user returns to the window (e.g. after toggling the + // setting in the Windows Settings app). + window.addEventListener("focus", check); + return () => window.removeEventListener("focus", check); + }, [check]); + + // "unknown" is treated as enabled to avoid false warnings. + if (status === "enabled" || status === "unknown") return null; + + return ( +
+ + + +
+

+ Windows notifications are off for OverManager — desktop alerts won't appear +

+

+ Turn notifications back on in Windows Settings so alert toasts are delivered. +

+ +
+
+ ); +} diff --git a/src/pages/Alerts.tsx b/src/pages/Alerts.tsx index f657c15..7826828 100644 --- a/src/pages/Alerts.tsx +++ b/src/pages/Alerts.tsx @@ -5,6 +5,7 @@ import { writeTextFile } from "@tauri-apps/plugin-fs"; import type { AlertEvent, AlertRule, RuleType } from "../types/alerts"; import type { SavedMiner, MobileMiner } from "../types/miner"; import { useAlerts } from "../context/AlertContext"; +import NotificationBanner from "../components/NotificationBanner"; // ─── Alert Rules helpers ────────────────────────────────────────────────────── @@ -297,6 +298,8 @@ export default function Alerts() {

Monitor alert history and manage alert rules

+ + {/* Tab toggle */}
)} + {/* Desktop Notifications */} +
+
+

Desktop Notifications

+

+ Alert toasts are delivered through Windows notifications. +

+
+ +
+ + {notifMsg && ( + + {notifMsg} + + )} +
+
+ {/* Data Export */}