feat(tray): "Restart to update" item and tooltip (#6)
Build Lotus Chat Desktop / prepare (push) Successful in 6s
Build Lotus Chat Desktop / build-windows (push) Successful in 32m18s
Build Lotus Chat Desktop / build-linux (push) Successful in 39m16s
Build Lotus Chat Desktop / build-arch (push) Successful in 17s
Build Lotus Chat Desktop / update-manifest (push) Successful in 4s

`set_tray_update_ready(version | null)` inserts a "Restart to update
(vX)" item at the top of the tray menu and sets the tooltip to "Lotus
Chat — update ready"; null removes both. The item only exists while an
update is pending. Clicking it reveals the window and emits
`lotus-tray-install-update`, and cinny runs its normal install flow
(progress, retries, failure toast). No icon dot: the unread dot already
uses that corner.

Bump cinny to 6edfe700 (the web half).

Closes #6

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PPmy3tPq869XDW4njjVaKA
This commit is contained in:
Lotus CI
2026-09-23 21:32:39 -04:00
co-authored by Claude Opus 5.5
parent 96cdbcd512
commit 687642a096
2 changed files with 61 additions and 1 deletions
+1 -1
Submodule cinny updated: d9e7f77402...6edfe70020
+60
View File
@@ -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<tauri::Wry>,
item: MenuItem<tauri::Wry>,
shown: std::sync::Mutex<bool>,
}
/// 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<String>) -> Result<(), String> {
let Some(state) = app.try_state::<TrayUpdateState>() else {
return Ok(());
};
let unread = app.try_state::<TrayUnreadState>();
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");
}