From cd4c613758c2b27e18d829a9f0a51941eedea042 Mon Sep 17 00:00:00 2001 From: Joseph Montanaro Date: Mon, 30 Dec 2024 21:09:45 -0500 Subject: [PATCH] improve start-minimized and start-on-login behavior Previously, when Creddy was configured to start minimized, it would always start minimized, regardless of how it was launched. Really, though, when you use this setting what you probably want is for it to start minimized only when it's being launched automatically, i.e. on login. This update changes its behavior so that it will only start minimized when auto-launching. Additionally, if Creddy detects on startup that its start-on-login configuration doesn't match the system, it will modify its own settings to match the system (unless it's the very first launch, of course.) That way if you disable Creddy's start-on-login behavior from your system dialog, it will respect your change. --- package.json | 2 +- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/creddy_cli/src/cli/mod.rs | 10 ++++- src-tauri/creddy_cli/src/lib.rs | 1 + src-tauri/creddy_cli/src/main.rs | 18 ++++++--- src-tauri/src/app.rs | 24 +++++++----- src-tauri/src/config.rs | 58 +++++++++++++++++++---------- src-tauri/src/main.rs | 15 ++++++-- src-tauri/src/state.rs | 7 ++-- src-tauri/tauri.conf.json | 2 +- src/views/Settings.svelte | 15 ++++---- 12 files changed, 105 insertions(+), 51 deletions(-) diff --git a/package.json b/package.json index 1971987..29cfe02 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "creddy", - "version": "0.6.2", + "version": "0.6.3", "scripts": { "dev": "vite", "build": "vite build", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 90fc83d..c8ccee7 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1217,7 +1217,7 @@ dependencies = [ [[package]] name = "creddy" -version = "0.6.2" +version = "0.6.3" dependencies = [ "argon2", "auto-launch", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 1836bc3..4b51746 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "creddy" -version = "0.6.2" +version = "0.6.3" description = "A friendly AWS credentials manager" authors = ["Joseph Montanaro"] license = "" diff --git a/src-tauri/creddy_cli/src/cli/mod.rs b/src-tauri/creddy_cli/src/cli/mod.rs index 87f14ee..f88a143 100644 --- a/src-tauri/creddy_cli/src/cli/mod.rs +++ b/src-tauri/creddy_cli/src/cli/mod.rs @@ -65,7 +65,7 @@ pub struct GlobalArgs { #[derive(Debug, Subcommand)] pub enum Action { /// Launch Creddy - Run, + Run(RunArgs), /// Request credentials from Creddy and output to stdout Get(GetArgs), /// Inject credentials into the environment of another command @@ -78,6 +78,14 @@ pub enum Action { } +#[derive(Debug, Args)] +pub struct RunArgs { + /// Minimize to system tray on launch + #[arg(long, default_value_t = false)] + pub minimized: bool, +} + + #[derive(Debug, Args)] pub struct GetArgs { /// If unspecified, use default credentials diff --git a/src-tauri/creddy_cli/src/lib.rs b/src-tauri/creddy_cli/src/lib.rs index 94cfce0..c1c993b 100644 --- a/src-tauri/creddy_cli/src/lib.rs +++ b/src-tauri/creddy_cli/src/lib.rs @@ -6,6 +6,7 @@ pub use cli::{ exec, get, GlobalArgs, + RunArgs, invoke_shortcut, }; diff --git a/src-tauri/creddy_cli/src/main.rs b/src-tauri/creddy_cli/src/main.rs index 5aab1e2..0c51d9b 100644 --- a/src-tauri/creddy_cli/src/main.rs +++ b/src-tauri/creddy_cli/src/main.rs @@ -1,13 +1,17 @@ use std::env; use std::process::{self, Command}; -use creddy_cli::{Action, Cli}; - +use creddy_cli::{ + Action, + Cli, + RunArgs, +}; fn main() { let cli = Cli::parse(); let res = match cli.action { - None | Some(Action::Run)=> launch_gui(), + None => launch_gui(RunArgs { minimized: false }), + Some(Action::Run(run_args)) => launch_gui(run_args), Some(Action::Get(args)) => creddy_cli::get(args, cli.global_args), Some(Action::Exec(args)) => creddy_cli::exec(args, cli.global_args), Some(Action::Shortcut(args)) => creddy_cli::invoke_shortcut(args, cli.global_args), @@ -21,7 +25,7 @@ fn main() { } -fn launch_gui() -> anyhow::Result<()> { +fn launch_gui(run_args: RunArgs) -> anyhow::Result<()> { let mut path = env::current_exe()?; path.pop(); // bin dir @@ -31,6 +35,10 @@ fn launch_gui() -> anyhow::Result<()> { path.push("creddy.exe"); // exe in main install dir (aka gui exe) - Command::new(path).spawn()?; + let mut cmd = Command::new(path); + if run_args.minimized { + cmd.arg("--minimized"); + } + cmd.spawn()?; Ok(()) } diff --git a/src-tauri/src/app.rs b/src-tauri/src/app.rs index d7fcdcb..8ff4ae5 100644 --- a/src-tauri/src/app.rs +++ b/src-tauri/src/app.rs @@ -15,7 +15,7 @@ use tauri::{ RunEvent, WindowEvent, }; -use creddy_cli::GlobalArgs; +use creddy_cli::{GlobalArgs, RunArgs}; use crate::{ config::{self, AppConfig}, @@ -32,7 +32,7 @@ use crate::{ pub static APP: OnceCell = OnceCell::new(); -pub fn run(global_args: GlobalArgs) -> tauri::Result<()> { +pub fn run(run_args: RunArgs, global_args: GlobalArgs) -> tauri::Result<()> { if let Ok(_) = creddy_cli::show_window(global_args) { // app is already running, so terminate return Ok(()); @@ -62,7 +62,7 @@ pub fn run(global_args: GlobalArgs) -> tauri::Result<()> { ipc::get_devmode, ipc::exit, ]) - .setup(|app| rt::block_on(setup(app))) + .setup(|app| rt::block_on(setup(app, run_args))) .build(tauri::generate_context!())? .run(|app, run_event| { if let RunEvent::WindowEvent { event, .. } = run_event { @@ -88,11 +88,11 @@ pub async fn connect_db() -> Result { } -async fn setup(app: &mut App) -> Result<(), Box> { +async fn setup(app: &mut App, run_args: RunArgs) -> Result<(), Box> { APP.set(app.handle().clone()).unwrap(); tray::setup(app)?; // get_or_create_db_path doesn't create the actual db file, just the directory - let is_first_launch = !config::get_or_create_db_path()?.exists(); + let is_first_launch = !config::get_or_create_db_path()?.try_exists()?; let pool = connect_db().await?; let mut setup_errors: Vec = vec![]; @@ -111,10 +111,16 @@ async fn setup(app: &mut App) -> Result<(), Box> { creddy_server::serve(app.handle().clone())?; agent::serve(app.handle().clone())?; - config::set_auto_launch(conf.start_on_login)?; - if let Err(_e) = config::set_auto_launch(conf.start_on_login) { - setup_errors.push("Error: Failed to manage autolaunch.".into()); + // if this is the first launch, setup system with default auto-launch settings + if is_first_launch { + if let Err(e) = conf.set_auto_launch() { + setup_errors.push(format!("Failed to manage autolaunch: {e}")); + } } + // otherwise, treat the system as the source of truth and ensure ours matches + else { + conf.match_auto_launch(&pool).await?; + }; // if hotkeys fail to register, disable them so that this error doesn't have to keep showing up if let Err(_e) = shortcuts::register_hotkeys(&conf.hotkeys) { @@ -127,7 +133,7 @@ async fn setup(app: &mut App) -> Result<(), Box> { .map(|names| names.split(':').any(|n| n == "GNOME")) .unwrap_or(false); - if !conf.start_minimized || is_first_launch { + if !run_args.minimized { show_main_window(&app.handle())?; } diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 596cc8c..5ef24d5 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use std::time::Duration; -use auto_launch::AutoLaunchBuilder; +use auto_launch::{AutoLaunch, AutoLaunchBuilder}; use is_terminal::IsTerminal; use serde::{Serialize, Deserialize}; use sqlx::SqlitePool; @@ -89,29 +89,49 @@ impl AppConfig { pub async fn save(&self, pool: &SqlitePool) -> Result<(), sqlx::error::Error> { kv::save(pool, "config", self).await } -} + /// Configure system with auto-launch settings + pub fn set_auto_launch(&self) -> Result<(), SetupError> { + let mgr = self.auto_launch_manager()?; -pub fn set_auto_launch(is_configured: bool) -> Result<(), SetupError> { - let path_buf = std::env::current_exe() - .map_err(|e| auto_launch::Error::Io(e))?; - let path = path_buf - .to_string_lossy(); + // if enabled, disabled regardless of desired end state because either: + // a) we are just going to leave it disabled, or + // b) we need to disable-and-reenable in case args are different + if mgr.is_enabled()? { + mgr.disable()?; + } + if self.start_on_login { + mgr.enable()?; + } - let auto = AutoLaunchBuilder::new() - .set_app_name("Creddy") - .set_app_path(&path) - .build()?; - - let is_enabled = auto.is_enabled()?; - if is_configured && !is_enabled { - auto.enable()?; - } - else if !is_configured && is_enabled { - auto.disable()?; + Ok(()) } - Ok(()) + /// Match own auto-launch settings to system + pub async fn match_auto_launch(&mut self, pool: &SqlitePool) -> Result<(), SetupError> { + let mgr = self.auto_launch_manager()?; + let is_enabled = mgr.is_enabled()?; + if is_enabled != self.start_on_login { + self.start_on_login = is_enabled; + self.save(pool).await?; + } + Ok(()) + } + + fn auto_launch_manager(&self) -> Result { + let path_buf = std::env::current_exe() + .map_err(|e| auto_launch::Error::Io(e))?; + + let name = if cfg!(debug_assertions) { "Creddy" } else { "Creddy (dev)" }; + let mut builder = AutoLaunchBuilder::new(); + builder.set_app_name(name); + builder.set_app_path(&path_buf.to_string_lossy()); + if self.start_minimized { + builder.set_args(&["run", "--minimized"]); + } + + Ok(builder.build()?) + } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 5ff4a5c..cf984c0 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -8,14 +8,23 @@ use creddy::{ app, errors::ShowError, }; -use creddy_cli::{Action, Cli}; +use creddy_cli::{ + Action, + Cli, + RunArgs, +}; fn main() { let cli = Cli::parse(); let res = match cli.action { - None | Some(Action::Run) => { - app::run(cli.global_args).error_popup("Creddy encountered an error"); + None => { + let run_args = RunArgs { minimized: false }; + app::run(run_args, cli.global_args).error_popup("Creddy encountered an error"); + Ok(()) + } + Some(Action::Run(run_args)) => { + app::run(run_args, cli.global_args).error_popup("Creddy encountered an error"); Ok(()) }, Some(Action::Get(args)) => creddy_cli::get(args, cli.global_args), diff --git a/src-tauri/src/state.rs b/src-tauri/src/state.rs index 15d11ff..e43d1db 100644 --- a/src-tauri/src/state.rs +++ b/src-tauri/src/state.rs @@ -22,7 +22,7 @@ use crate::credentials::{ DockerCredential, SshKey, }; -use crate::{config, config::AppConfig}; +use crate::config::AppConfig; use crate::credentials::{ AwsBaseCredential, Credential, @@ -204,8 +204,9 @@ impl AppState { let mut live_config = self.config.write().await; // update autostart if necessary - if new_config.start_on_login != live_config.start_on_login { - config::set_auto_launch(new_config.start_on_login)?; + if new_config.start_on_login != live_config.start_on_login + || new_config.start_minimized != live_config.start_minimized { + new_config.set_auto_launch()?; } // re-register hotkeys if necessary diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index eacef81..bfec188 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -50,7 +50,7 @@ } }, "productName": "creddy", - "version": "0.6.2", + "version": "0.6.3", "identifier": "creddy", "plugins": {}, "app": { diff --git a/src/views/Settings.svelte b/src/views/Settings.svelte index 70c4e07..7b6e95b 100644 --- a/src/views/Settings.svelte +++ b/src/views/Settings.svelte @@ -20,7 +20,6 @@ let error = null; async function save() { try { - throw('wtf'); await invoke('save_config', {config}); $appState.config = await invoke('get_config'); } @@ -41,18 +40,20 @@
- + Start Creddy when you log in to your computer. - - - Minimize to the system tray at startup. - - + {#if config.start_on_login} + + + Minimize to the system tray when starting on login. + + + {/if}