From 4c37d45b109c0c412e53c34e5dee7c0a13cf5e2f Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:26:30 +0000 Subject: [PATCH 1/6] refactor: move autostart control out of tray menu --- src-tauri/src/lib.rs | 45 ++++++++++++++++++------------------------- src-tauri/src/menu.rs | 20 ++++--------------- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index a5e9034..47fcb84 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -10,9 +10,24 @@ mod state; mod updater; use tauri::tray::TrayIconBuilder; -use tauri::{Emitter, Manager}; +use tauri::{AppHandle, Emitter, Manager}; use tauri_plugin_autostart::ManagerExt; +#[tauri::command] +pub fn is_autostart_enabled_cmd(app: AppHandle) -> Result { + Ok(app.autolaunch().is_enabled().unwrap_or(false)) +} + +#[tauri::command] +pub fn set_autostart_cmd(app: AppHandle, enabled: bool) -> Result<(), String> { + let result = if enabled { + app.autolaunch().enable() + } else { + app.autolaunch().disable() + }; + result.map_err(|e| format!("Failed to set autostart: {}", e)) +} + #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() @@ -44,7 +59,9 @@ pub fn run() { settings::get_settings_cmd, settings::save_settings_cmd, updater::check_for_update_cmd, - updater::install_update_cmd + updater::install_update_cmd, + is_autostart_enabled_cmd, + set_autostart_cmd ]) .setup(|app| { // Restore saved auth token from disk (if any) @@ -202,30 +219,6 @@ fn handle_menu_event(app: &tauri::AppHandle, id: &str) { }); } - "autostart_toggle" => { - let currently_enabled = app.autolaunch().is_enabled().unwrap_or(false); - let toggle_result = if currently_enabled { - app.autolaunch().disable() - } else { - app.autolaunch().enable() - }; - if let Err(e) = toggle_result { - eprintln!("[autostart] Failed to toggle autostart: {}", e); - } - - let prs = { - let state = app.state::(); - let val = state.prs.lock().unwrap().clone(); - val - }; - let state = app.state::(); - let tray_guard = state.tray.lock().unwrap(); - if let Some(tray) = tray_guard.as_ref() { - if let Ok(new_menu) = menu::build_pr_menu(app, &prs, &state.avatar_cache) { - let _ = tray.set_menu(Some(new_menu)); - } - } - } "sign_in" => { let app = app.clone(); diff --git a/src-tauri/src/menu.rs b/src-tauri/src/menu.rs index bfda46d..cde5643 100644 --- a/src-tauri/src/menu.rs +++ b/src-tauri/src/menu.rs @@ -1,6 +1,5 @@ use tauri::AppHandle; -use tauri::menu::{CheckMenuItem, IconMenuItem, Menu, MenuItem, PredefinedMenuItem}; -use tauri_plugin_autostart::ManagerExt; +use tauri::menu::{IconMenuItem, Menu, MenuItem, PredefinedMenuItem}; use crate::avatars::AvatarCache; use crate::models::{CheckStatus, PrState, PullRequest}; @@ -192,24 +191,13 @@ pub fn build_pr_menu( menu.append(&see_all)?; let refresh = MenuItem::with_id(app, "refresh", "Refresh", true, None::<&str>)?; menu.append(&refresh)?; + let sep2 = PredefinedMenuItem::separator(app)?; + menu.append(&sep2)?; let check_updates = MenuItem::with_id(app, "check_updates", "Check for Updates", true, None::<&str>)?; menu.append(&check_updates)?; - let autostart_enabled = app.autolaunch().is_enabled().unwrap_or(false); - let autostart_toggle = CheckMenuItem::with_id( - app, - "autostart_toggle", - "Launch at login", - true, - autostart_enabled, - None::<&str>, - )?; - menu.append(&autostart_toggle)?; - let settings = - MenuItem::with_id(app, "settings", "Settings...", true, None::<&str>)?; + let settings = MenuItem::with_id(app, "settings", "Settings", true, None::<&str>)?; menu.append(&settings)?; - let sep2 = PredefinedMenuItem::separator(app)?; - menu.append(&sep2)?; let logout = MenuItem::with_id(app, "logout", "Logout", true, None::<&str>)?; menu.append(&logout)?; let quit = MenuItem::with_id(app, "quit", "Quit PR Buddy", true, None::<&str>)?; From 927449e949eaf1b57ec07a40c2f7aab901283ba5 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:26:05 +0000 Subject: [PATCH 2/6] feat: add launch at login toggle in settings general section --- src/lib/SettingsPage.svelte | 40 +++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/lib/SettingsPage.svelte b/src/lib/SettingsPage.svelte index ae02737..4446aed 100644 --- a/src/lib/SettingsPage.svelte +++ b/src/lib/SettingsPage.svelte @@ -28,15 +28,25 @@ let loaded = $state(false); let currentTheme = $state("system"); let setThemePreference: (t: ThemePreference) => void = () => {}; + let launchAtLoginEnabled = $state(false); // Serialize saves so rapid toggles don't race each other let saveChain = Promise.resolve(); onMount(() => { void loadSettings(); + void loadAutostartSetting(); void initTheme(); }); + async function loadAutostartSetting() { + try { + const enabled = await invoke("is_autostart_enabled_cmd"); + launchAtLoginEnabled = Boolean(enabled); + } catch (e) { + console.error("[settings] Failed to load autostart setting:", e); + } + } async function initTheme() { if (typeof window !== "undefined" && typeof window.matchMedia !== "function") { @@ -92,6 +102,15 @@ void save(); } + function toggleLaunchAtLogin() { + const enabled = !launchAtLoginEnabled; + launchAtLoginEnabled = enabled; + void invoke("set_autostart_cmd", { enabled }).catch((e: unknown) => { + console.error("[settings] Failed to set autostart setting:", e); + launchAtLoginEnabled = !enabled; + }); + } + function toggleRepo(repo: string) { const idx = settings.hidden_repos.indexOf(repo); if (idx >= 0) { @@ -143,6 +162,27 @@
{:else} + +
+

General

+ +
+

Theme

From 035b871cf74514af0dc18def031f682f2be33610 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:30:24 +0000 Subject: [PATCH 3/6] fix: move autostart commands to dedicated module --- src-tauri/src/autostart.rs | 17 +++++++++++++++++ src-tauri/src/lib.rs | 23 ++++------------------- 2 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 src-tauri/src/autostart.rs diff --git a/src-tauri/src/autostart.rs b/src-tauri/src/autostart.rs new file mode 100644 index 0000000..0ca9c4b --- /dev/null +++ b/src-tauri/src/autostart.rs @@ -0,0 +1,17 @@ +use tauri::AppHandle; +use tauri_plugin_autostart::ManagerExt; + +#[tauri::command] +pub fn is_autostart_enabled_cmd(app: AppHandle) -> Result { + Ok(app.autolaunch().is_enabled().unwrap_or(false)) +} + +#[tauri::command] +pub fn set_autostart_cmd(app: AppHandle, enabled: bool) -> Result<(), String> { + let result = if enabled { + app.autolaunch().enable() + } else { + app.autolaunch().disable() + }; + result.map_err(|e| format!("Failed to set autostart: {}", e)) +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 47fcb84..bb37efd 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,5 +1,6 @@ mod avatars; mod auth; +mod autostart; mod github; mod menu; mod models; @@ -10,23 +11,7 @@ mod state; mod updater; use tauri::tray::TrayIconBuilder; -use tauri::{AppHandle, Emitter, Manager}; -use tauri_plugin_autostart::ManagerExt; - -#[tauri::command] -pub fn is_autostart_enabled_cmd(app: AppHandle) -> Result { - Ok(app.autolaunch().is_enabled().unwrap_or(false)) -} - -#[tauri::command] -pub fn set_autostart_cmd(app: AppHandle, enabled: bool) -> Result<(), String> { - let result = if enabled { - app.autolaunch().enable() - } else { - app.autolaunch().disable() - }; - result.map_err(|e| format!("Failed to set autostart: {}", e)) -} +use tauri::{Emitter, Manager}; #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { @@ -60,8 +45,8 @@ pub fn run() { settings::save_settings_cmd, updater::check_for_update_cmd, updater::install_update_cmd, - is_autostart_enabled_cmd, - set_autostart_cmd + autostart::is_autostart_enabled_cmd, + autostart::set_autostart_cmd ]) .setup(|app| { // Restore saved auth token from disk (if any) From d488bac24f7747cf925ecab9bc6ab3ee883fc3d9 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 4 Mar 2026 13:57:09 +0000 Subject: [PATCH 4/6] fix: serialize autostart toggle to prevent race conditions --- src/lib/SettingsPage.svelte | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/SettingsPage.svelte b/src/lib/SettingsPage.svelte index 4446aed..bea28cd 100644 --- a/src/lib/SettingsPage.svelte +++ b/src/lib/SettingsPage.svelte @@ -32,6 +32,7 @@ // Serialize saves so rapid toggles don't race each other let saveChain = Promise.resolve(); + let autoStartChain = Promise.resolve(); onMount(() => { void loadSettings(); @@ -105,10 +106,12 @@ function toggleLaunchAtLogin() { const enabled = !launchAtLoginEnabled; launchAtLoginEnabled = enabled; - void invoke("set_autostart_cmd", { enabled }).catch((e: unknown) => { - console.error("[settings] Failed to set autostart setting:", e); - launchAtLoginEnabled = !enabled; - }); + autoStartChain = autoStartChain + .then(async () => { await invoke("set_autostart_cmd", { enabled }); }) + .catch((e: unknown) => { + console.error("[settings] Failed to set autostart setting:", e); + launchAtLoginEnabled = !enabled; + }); } function toggleRepo(repo: string) { From 81fb27e4d35b988e3d169354c77d38cbf8e7eb50 Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:06:10 +0000 Subject: [PATCH 5/6] fix: guard autostart toggle against stale reads and rollbacks --- src/lib/SettingsPage.svelte | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/lib/SettingsPage.svelte b/src/lib/SettingsPage.svelte index bea28cd..7262100 100644 --- a/src/lib/SettingsPage.svelte +++ b/src/lib/SettingsPage.svelte @@ -29,6 +29,7 @@ let currentTheme = $state("system"); let setThemePreference: (t: ThemePreference) => void = () => {}; let launchAtLoginEnabled = $state(false); + let autoStartToggleCount = 0; // generation counter to discard stale reads/rollbacks // Serialize saves so rapid toggles don't race each other let saveChain = Promise.resolve(); @@ -41,9 +42,13 @@ }); async function loadAutostartSetting() { + const gen = autoStartToggleCount; try { const enabled = await invoke("is_autostart_enabled_cmd"); - launchAtLoginEnabled = Boolean(enabled); + // Only apply if user hasn't toggled while we were loading + if (autoStartToggleCount === gen) { + launchAtLoginEnabled = Boolean(enabled); + } } catch (e) { console.error("[settings] Failed to load autostart setting:", e); } @@ -106,11 +111,15 @@ function toggleLaunchAtLogin() { const enabled = !launchAtLoginEnabled; launchAtLoginEnabled = enabled; + const gen = ++autoStartToggleCount; autoStartChain = autoStartChain .then(async () => { await invoke("set_autostart_cmd", { enabled }); }) .catch((e: unknown) => { console.error("[settings] Failed to set autostart setting:", e); - launchAtLoginEnabled = !enabled; + // Only rollback if no newer toggle has happened since + if (autoStartToggleCount === gen) { + launchAtLoginEnabled = !enabled; + } }); } From 2ef591a8276f635c4bd756dff60bf27a629f133f Mon Sep 17 00:00:00 2001 From: Michael Suchacz <203725896+ibetitsmike@users.noreply.github.com> Date: Wed, 4 Mar 2026 14:12:39 +0000 Subject: [PATCH 6/6] fix: disable autostart toggle until initial state is loaded --- src/lib/SettingsPage.svelte | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/lib/SettingsPage.svelte b/src/lib/SettingsPage.svelte index 7262100..8e13e1a 100644 --- a/src/lib/SettingsPage.svelte +++ b/src/lib/SettingsPage.svelte @@ -29,6 +29,7 @@ let currentTheme = $state("system"); let setThemePreference: (t: ThemePreference) => void = () => {}; let launchAtLoginEnabled = $state(false); + let autoStartLoaded = $state(false); let autoStartToggleCount = 0; // generation counter to discard stale reads/rollbacks // Serialize saves so rapid toggles don't race each other @@ -51,6 +52,8 @@ } } catch (e) { console.error("[settings] Failed to load autostart setting:", e); + } finally { + autoStartLoaded = true; } } @@ -179,8 +182,10 @@

General