mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 18:18:55 +00:00
feat(windows): add CLI flag to test a clickable update notification (#3526)
Looks a little odd in the Windows Server VM cause of the minimal desktop environment, but it does open the browser, same as the "Sign In" button  --------- Signed-off-by: Reactor Scram <ReactorScram@users.noreply.github.com>
This commit is contained in:
28
rust/Cargo.lock
generated
28
rust/Cargo.lock
generated
@@ -2167,6 +2167,7 @@ dependencies = [
|
||||
"tauri-build",
|
||||
"tauri-runtime",
|
||||
"tauri-utils",
|
||||
"tauri-winrt-notification",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
@@ -3657,19 +3658,6 @@ version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4"
|
||||
|
||||
[[package]]
|
||||
name = "mac-notification-sys"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51fca4d74ff9dbaac16a01b924bc3693fa2bba0862c2c633abc73f9a8ea21f64"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"dirs-next",
|
||||
"objc-foundation",
|
||||
"objc_id",
|
||||
"time",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach2"
|
||||
version = "0.4.2"
|
||||
@@ -4053,19 +4041,6 @@ dependencies = [
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "notify-rust"
|
||||
version = "4.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "827c5edfa80235ded4ab3fe8e9dc619b4f866ef16fe9b1c6b8a7f8692c0f2226"
|
||||
dependencies = [
|
||||
"log",
|
||||
"mac-notification-sys",
|
||||
"serde",
|
||||
"tauri-winrt-notification",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@@ -6539,7 +6514,6 @@ dependencies = [
|
||||
"heck 0.4.1",
|
||||
"http 0.2.11",
|
||||
"ignore",
|
||||
"notify-rust",
|
||||
"objc",
|
||||
"once_cell",
|
||||
"open",
|
||||
|
||||
@@ -13,6 +13,10 @@ use std::path::PathBuf;
|
||||
/// This should be identical to the `tauri.bundle.identifier` over in `tauri.conf.json`,
|
||||
/// but sometimes I need to use this before Tauri has booted up, or in a place where
|
||||
/// getting the Tauri app handle would be awkward.
|
||||
///
|
||||
/// Luckily this is also the AppUserModelId that Windows uses to label notifications,
|
||||
/// so if your dev system has Firezone installed by MSI, the notifications will look right.
|
||||
/// <https://learn.microsoft.com/en-us/windows/configuration/find-the-application-user-model-id-of-an-installed-app>
|
||||
pub const BUNDLE_ID: &str = "dev.firezone.client";
|
||||
|
||||
/// Returns e.g. `C:/Users/User/AppData/Local/dev.firezone.client
|
||||
|
||||
@@ -52,9 +52,10 @@ native-dialog = "0.7.0"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
# Tauri works fine on Linux, but it requires a lot of build-time deps like glib and gdk, so I've blocked it out for now.
|
||||
tauri = { version = "1.5", features = [ "dialog", "notification", "shell-open-api", "system-tray" ] }
|
||||
tauri = { version = "1.5", features = [ "dialog", "shell-open-api", "system-tray" ] }
|
||||
tauri-runtime = "0.14.2"
|
||||
tauri-utils = "1.5.1"
|
||||
tauri-winrt-notification = "0.1.3"
|
||||
winreg = "0.52.0"
|
||||
wintun = "0.4.0"
|
||||
|
||||
|
||||
@@ -124,15 +124,23 @@ fn run_gui(cli: Cli) -> Result<()> {
|
||||
Ok(result?)
|
||||
}
|
||||
|
||||
/// The debug / test flags like `crash_on_purpose` and `test_update_notification`
|
||||
/// don't propagate when we use `RunAs` to elevate ourselves. So those must be run
|
||||
/// from an admin terminal, or with "Run as administrator" in the right-click menu.
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, about, long_about = None)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Option<Cmd>,
|
||||
/// If true, purposely crash the program to test the crash handler
|
||||
#[arg(long, hide = true)]
|
||||
crash_on_purpose: bool,
|
||||
/// If true, slow down I/O operations to test how the GUI handles slow I/O
|
||||
#[arg(long, hide = true)]
|
||||
inject_faults: bool,
|
||||
/// If true, show a fake update notification that opens the Firezone release page when clicked
|
||||
#[arg(long, hide = true)]
|
||||
test_update_notification: bool,
|
||||
}
|
||||
|
||||
#[derive(clap::Subcommand)]
|
||||
|
||||
@@ -14,7 +14,7 @@ use connlib_shared::{messages::ResourceId, windows::BUNDLE_ID};
|
||||
use secrecy::{ExposeSecret, SecretString};
|
||||
use std::{net::IpAddr, path::PathBuf, str::FromStr, sync::Arc, time::Duration};
|
||||
use system_tray_menu::Event as TrayMenuEvent;
|
||||
use tauri::{api::notification::Notification, Manager, SystemTray, SystemTrayEvent};
|
||||
use tauri::{Manager, SystemTray, SystemTrayEvent};
|
||||
use tokio::sync::{mpsc, oneshot, Notify};
|
||||
use ControllerRequest as Req;
|
||||
|
||||
@@ -53,12 +53,16 @@ impl Managed {
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub(crate) enum Error {
|
||||
#[error(r#"Couldn't show clickable notification titled "{0}""#)]
|
||||
ClickableNotification(String),
|
||||
#[error("Deep-link module error: {0}")]
|
||||
DeepLink(#[from] deep_link::Error),
|
||||
#[error("Can't show log filter error dialog: {0}")]
|
||||
LogFilterErrorDialog(native_dialog::Error),
|
||||
#[error("Logging module error: {0}")]
|
||||
Logging(#[from] logging::Error),
|
||||
#[error(r#"Couldn't show notification titled "{0}""#)]
|
||||
Notification(String),
|
||||
#[error(transparent)]
|
||||
Tauri(#[from] tauri::Error),
|
||||
#[error("tokio::runtime::Runtime::new failed: {0}")]
|
||||
@@ -117,6 +121,9 @@ pub(crate) fn run(cli: client::Cli) -> Result<(), Error> {
|
||||
let rt = tokio::runtime::Runtime::new().map_err(Error::TokioRuntimeNew)?;
|
||||
let _guard = rt.enter();
|
||||
|
||||
let (ctlr_tx, ctlr_rx) = mpsc::channel(5);
|
||||
let notify_controller = Arc::new(Notify::new());
|
||||
|
||||
if cli.crash_on_purpose {
|
||||
tokio::spawn(async {
|
||||
let delay = 10;
|
||||
@@ -129,8 +136,17 @@ pub(crate) fn run(cli: client::Cli) -> Result<(), Error> {
|
||||
});
|
||||
}
|
||||
|
||||
let (ctlr_tx, ctlr_rx) = mpsc::channel(5);
|
||||
let notify_controller = Arc::new(Notify::new());
|
||||
if cli.test_update_notification {
|
||||
// TODO: Clicking doesn't work if the notification times out and hides first.
|
||||
// See docs for `show_clickable_notification`.
|
||||
|
||||
show_clickable_notification(
|
||||
"Firezone update",
|
||||
"Click here to open the release page.",
|
||||
ctlr_tx.clone(),
|
||||
Req::NotificationClicked,
|
||||
)?;
|
||||
}
|
||||
|
||||
// Make sure we're single-instance
|
||||
// We register our deep links to call the `open-deep-link` subcommand,
|
||||
@@ -278,6 +294,7 @@ pub(crate) enum ControllerRequest {
|
||||
DisconnectedTokenExpired,
|
||||
ExportLogs { path: PathBuf, stem: PathBuf },
|
||||
GetAdvancedSettings(oneshot::Sender<AdvancedSettings>),
|
||||
NotificationClicked,
|
||||
SchemeRequest(url::Url),
|
||||
SystemTrayMenu(TrayMenuEvent),
|
||||
TunnelReady,
|
||||
@@ -597,6 +614,14 @@ async fn run_controller(
|
||||
Req::GetAdvancedSettings(tx) => {
|
||||
tx.send(controller.advanced_settings.clone()).ok();
|
||||
}
|
||||
Req::NotificationClicked => {
|
||||
tracing::info!("NotificationClicked in run_controller!");
|
||||
tauri::api::shell::open(
|
||||
&app.shell_scope(),
|
||||
"https://example.com/notification_clicked",
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
Req::SchemeRequest(url) => if let Err(e) = controller.handle_deep_link(&url).await {
|
||||
tracing::error!("couldn't handle deep link: {e:#?}");
|
||||
}
|
||||
@@ -644,10 +669,57 @@ async fn run_controller(
|
||||
///
|
||||
/// May say "Windows Powershell" and have the wrong icon in dev mode
|
||||
/// See <https://github.com/tauri-apps/tauri/issues/3700>
|
||||
fn show_notification(title: &str, body: &str) -> Result<()> {
|
||||
Notification::new(BUNDLE_ID)
|
||||
fn show_notification(title: &str, body: &str) -> Result<(), Error> {
|
||||
tauri_winrt_notification::Toast::new(BUNDLE_ID)
|
||||
.title(title)
|
||||
.body(body)
|
||||
.show()?;
|
||||
.text1(body)
|
||||
.show()
|
||||
.map_err(|_| Error::Notification(title.to_string()))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Show a notification that signals `Controller` when clicked
|
||||
///
|
||||
/// May say "Windows Powershell" and have the wrong icon in dev mode
|
||||
/// See <https://github.com/tauri-apps/tauri/issues/3700>
|
||||
///
|
||||
/// Known issue: If the notification times out and goes into the notification center
|
||||
/// (the little thing that pops up when you click the bell icon), then we may not get the
|
||||
/// click signal.
|
||||
///
|
||||
/// I've seen this reported by people using Powershell, C#, etc., so I think it might
|
||||
/// be a Windows bug?
|
||||
/// - <https://superuser.com/questions/1488763/windows-10-notifications-not-activating-the-associated-app-when-clicking-on-it>
|
||||
/// - <https://stackoverflow.com/questions/65835196/windows-toast-notification-com-not-working>
|
||||
/// - <https://answers.microsoft.com/en-us/windows/forum/all/notifications-not-activating-the-associated-app/7a3b31b0-3a20-4426-9c88-c6e3f2ac62c6>
|
||||
///
|
||||
/// Firefox doesn't have this problem. Maybe they're using a different API.
|
||||
fn show_clickable_notification(
|
||||
title: &str,
|
||||
body: &str,
|
||||
tx: CtlrTx,
|
||||
req: ControllerRequest,
|
||||
) -> Result<(), Error> {
|
||||
// For some reason `on_activated` is FnMut
|
||||
let mut req = Some(req);
|
||||
|
||||
tauri_winrt_notification::Toast::new(BUNDLE_ID)
|
||||
.title(title)
|
||||
.text1(body)
|
||||
.scenario(tauri_winrt_notification::Scenario::Reminder)
|
||||
.on_activated(move || {
|
||||
if let Some(req) = req.take() {
|
||||
if let Err(error) = tx.blocking_send(req) {
|
||||
tracing::error!(
|
||||
?error,
|
||||
"User clicked on notification, but we couldn't tell `Controller`"
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.show()
|
||||
.map_err(|_| Error::ClickableNotification(title.to_string()))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
//! Code for the Windows notification area
|
||||
//!
|
||||
//! "Notification Area" is Microsoft's official name instead of "System tray":
|
||||
//! <https://learn.microsoft.com/en-us/windows/win32/shell/notification-area?redirectedfrom=MSDN#notifications-and-the-notification-area>
|
||||
|
||||
use connlib_client_shared::ResourceDescription;
|
||||
use std::str::FromStr;
|
||||
use tauri::{CustomMenuItem, SystemTrayMenu, SystemTrayMenuItem, SystemTraySubmenu};
|
||||
|
||||
Reference in New Issue
Block a user