diff --git a/cinny b/cinny index d9e7f77..6edfe70 160000 --- a/cinny +++ b/cinny @@ -1 +1 @@ -Subproject commit d9e7f77402ddf26712d7717fccbc0ade16ffb5d6 +Subproject commit 6edfe700204a626d97c62ae2b1c3d0faca49933c diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 841191a..22723ed 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -564,6 +564,53 @@ fn set_tray_unread(unread: bool, state: tauri::State<'_, TrayUnreadState>) -> Re state.tray.set_icon(Some(icon)).map_err(|e| e.to_string()) } +/// cinny-desktop #6: the tray's "Restart to update" item. Inserted at the top +/// of the menu only while an update is available, so it isn't a dead entry the +/// rest of the time. +struct TrayUpdateState { + menu: Menu, + item: MenuItem, + shown: std::sync::Mutex, +} + +/// Show (`Some(version)`) or clear (`None`) the tray's update affordance: the +/// menu item and the tooltip. The web client drives it from its update status, +/// and clicking the item hands the install back to the web flow +/// (`lotus-tray-install-update`) so progress and failures are shown in-app. +#[tauri::command] +fn set_tray_update_ready(app: tauri::AppHandle, version: Option) -> Result<(), String> { + let Some(state) = app.try_state::() else { + return Ok(()); + }; + let unread = app.try_state::(); + let mut shown = state.shown.lock().map_err(|e| e.to_string())?; + match version { + Some(version) => { + state + .item + .set_text(format!("Restart to update (v{version})")) + .map_err(|e| e.to_string())?; + if !*shown { + state.menu.insert(&state.item, 0).map_err(|e| e.to_string())?; + *shown = true; + } + if let Some(u) = unread { + let _ = u.tray.set_tooltip(Some("Lotus Chat — update ready")); + } + } + None => { + if *shown { + state.menu.remove(&state.item).map_err(|e| e.to_string())?; + *shown = false; + } + if let Some(u) = unread { + let _ = u.tray.set_tooltip(Some("Lotus Chat")); + } + } + } + Ok(()) +} + /// Flash the taskbar button to draw attention (e.g. a new mention while the /// window is unfocused). Clears automatically once the window is focused. #[tauri::command] @@ -611,6 +658,7 @@ pub fn run() { set_badge_count, set_tray_unread, get_tray_dnd, + set_tray_update_ready, flash_window, focus_main_window, send_notification, @@ -695,6 +743,12 @@ pub fn run() { .on_menu_event(move |app, event| match event.id.as_ref() { "open" => show_main(app), "quit" => app.exit(0), + "update" => { + // Show the window so the download progress (and any + // failure) is visible; the web side runs the install. + show_main(app); + native::emit_to_web(app, "lotus-tray-install-update", "{}"); + } "dnd" => { let checked = dnd_for_event.is_checked().unwrap_or(false); native::emit_to_web( @@ -739,6 +793,12 @@ pub fn run() { // Keep a handle to the DND CheckMenuItem so `get_tray_dnd` can // report its live checkstate for web re-hydration after reload. app.manage(TrayDndState(dnd_item)); + + app.manage(TrayUpdateState { + menu: tray_menu.clone(), + item: MenuItem::with_id(app, "update", "Restart to update", true, None::<&str>)?, + shown: std::sync::Mutex::new(false), + }); } else { eprintln!("tray: no bundled window icon; skipping system tray setup"); }