From d5f09ab34292bcd282aaf19755730d6ea1339bfd Mon Sep 17 00:00:00 2001 From: Reactor Scram Date: Tue, 16 Jan 2024 11:08:40 -0600 Subject: [PATCH] feat(Windows): #2998, add 'signing in' state to system tray menu (#3157) Depends on the new auth flow in #3147 I couldn't find the UI mockups so I just modified the "Sign In" menu to create this. --- .../src-tauri/src/client/auth.rs | 3 +- .../src-tauri/src/client/gui.rs | 58 +++++++++++++------ .../src/client/gui/system_tray_menu.rs | 10 ++++ 3 files changed, 53 insertions(+), 18 deletions(-) diff --git a/rust/windows-client/src-tauri/src/client/auth.rs b/rust/windows-client/src-tauri/src/client/auth.rs index 096d2017a..8ff4697ea 100644 --- a/rust/windows-client/src-tauri/src/client/auth.rs +++ b/rust/windows-client/src-tauri/src/client/auth.rs @@ -86,6 +86,7 @@ impl Auth { Ok(this) } + /// Returns the session iff we are signed in. pub fn session(&self) -> Option<&Session> { match &self.state { State::SignedIn(x) => Some(x), @@ -175,7 +176,7 @@ impl Auth { Ok(keyring::Entry::new_with_target(self.keyring_key, "", "")?) } - fn ongoing_request(&self) -> Result<&Request> { + pub fn ongoing_request(&self) -> Result<&Request> { match &self.state { State::NeedResponse(x) => Ok(x), _ => Err(Error::NoInflightRequest), diff --git a/rust/windows-client/src-tauri/src/client/gui.rs b/rust/windows-client/src-tauri/src/client/gui.rs index d1cfb4325..fbefb3f19 100644 --- a/rust/windows-client/src-tauri/src/client/gui.rs +++ b/rust/windows-client/src-tauri/src/client/gui.rs @@ -295,7 +295,7 @@ struct Controller { /// Debugging-only settings like API URL, auth URL, log filter advanced_settings: AdvancedSettings, app: tauri::AppHandle, - // Sign-in state + // Sign-in state with the portal / deep links auth: client::auth::Auth, ctlr_tx: CtlrTx, /// connlib session for the currently signed-in user, if there is one @@ -307,6 +307,7 @@ struct Controller { logging_handles: client::logging::Handles, /// Tells us when to wake up and look for a new resource list. Tokio docs say that memory reads and writes are synchronized when notifying, so we don't need an extra mutex on the resources. notify_controller: Arc, + tunnel_ready: bool, } /// Everything related to a signed-in user session @@ -335,6 +336,7 @@ impl Controller { log_counting_task: None, logging_handles, notify_controller, + tunnel_ready: false, }; if let Some(token) = this.auth.token()? { @@ -411,21 +413,38 @@ impl Controller { Ok(()) } - fn reload_resource_list(&mut self) -> Result<()> { - let Some(session) = &self.session else { - tracing::warn!("got notified to update resources but there is no session"); - return Ok(()); - }; - let resources = session.callback_handler.resources.load(); - let actor_name = &self - .auth - .session() - .ok_or_else(|| anyhow!("connlib is signed in but auth module isn't"))? - .actor_name; - self.app + /// Returns a new system tray menu + fn build_system_tray_menu(&self) -> tauri::SystemTrayMenu { + // TODO: Refactor this and the auth module so that "Are we logged in" + // doesn't require such complicated control flow to answer. + if let Some(auth_session) = self.auth.session() { + if let Some(connlib_session) = &self.session { + if self.tunnel_ready { + // Signed in, tunnel ready + let resources = connlib_session.callback_handler.resources.load(); + system_tray_menu::signed_in(&auth_session.actor_name, &resources) + } else { + // Signed in, raising tunnel + system_tray_menu::signing_in() + } + } else { + tracing::error!("We have an auth session but no connlib session"); + system_tray_menu::signed_out() + } + } else if self.auth.ongoing_request().is_ok() { + // Signing in, waiting on deep link callback + system_tray_menu::signing_in() + } else { + system_tray_menu::signed_out() + } + } + + /// Builds a new system tray menu and applies it to the app + fn refresh_system_tray_menu(&self) -> Result<()> { + Ok(self + .app .tray_handle() - .set_menu(system_tray_menu::signed_in(actor_name, &resources))?; - Ok(()) + .set_menu(self.build_system_tray_menu())?) } } @@ -450,7 +469,7 @@ async fn run_controller( loop { tokio::select! { - () = controller.notify_controller.notified() => if let Err(e) = controller.reload_resource_list() { + () = controller.notify_controller.notified() => if let Err(e) = controller.refresh_system_tray_menu() { tracing::error!("couldn't reload resource list: {e:#?}"); }, req = rx.recv() => { @@ -463,6 +482,7 @@ async fn run_controller( } Req::Disconnected => { tracing::debug!("connlib disconnected, tearing down Session"); + controller.tunnel_ready = false; if let Some(mut session) = controller.session.take() { tracing::debug!("disconnecting connlib"); session.connlib.disconnect(None); @@ -478,6 +498,7 @@ async fn run_controller( Req::SignIn => { if let Some(req) = controller.auth.start_sign_in()? { let url = req.to_url(&controller.advanced_settings.auth_base_url); + controller.refresh_system_tray_menu()?; tauri::api::shell::open( &app.shell_scope(), &url.expose_secret().inner, @@ -487,6 +508,7 @@ async fn run_controller( } Req::SignOut => { controller.auth.sign_out()?; + controller.tunnel_ready = false; if let Some(mut session) = controller.session.take() { tracing::debug!("disconnecting connlib"); session.connlib.disconnect(None); @@ -494,7 +516,7 @@ async fn run_controller( else { tracing::error!("tried to sign out but there's no session"); } - app.tray_handle().set_menu(system_tray_menu::signed_out())?; + controller.refresh_system_tray_menu()?; } Req::StartStopLogCounting(enable) => { if enable { @@ -510,6 +532,8 @@ async fn run_controller( } } Req::TunnelReady => { + controller.tunnel_ready = true; + // May say "Windows Powershell" in dev mode // See https://github.com/tauri-apps/tauri/issues/3700 Notification::new(&controller.app.config().tauri.bundle.identifier) diff --git a/rust/windows-client/src-tauri/src/client/gui/system_tray_menu.rs b/rust/windows-client/src-tauri/src/client/gui/system_tray_menu.rs index be0b5dfa8..7fb40a6fe 100644 --- a/rust/windows-client/src-tauri/src/client/gui/system_tray_menu.rs +++ b/rust/windows-client/src-tauri/src/client/gui/system_tray_menu.rs @@ -66,6 +66,16 @@ pub(crate) fn signed_in(user_name: &str, resources: &[ResourceDescription]) -> S menu } +pub(crate) fn signing_in() -> SystemTrayMenu { + // TODO: Check this layout with Jamil, I lost track of the Figma for it. + SystemTrayMenu::new() + .add_item(CustomMenuItem::new("".to_string(), "Signing In...").disabled()) + .add_native_item(SystemTrayMenuItem::Separator) + .add_item(CustomMenuItem::new("/about".to_string(), "About")) + .add_item(CustomMenuItem::new("/settings".to_string(), "Settings")) + .add_item(CustomMenuItem::new("/quit".to_string(), "Quit Firezone").accelerator("Ctrl+Q")) +} + pub(crate) fn signed_out() -> SystemTrayMenu { SystemTrayMenu::new() .add_item(CustomMenuItem::new("/sign_in".to_string(), "Sign In"))