From d9d1a5a7309d5b315f9906de3015b6478d800ae2 Mon Sep 17 00:00:00 2001 From: Nigel Ihza Firdausy <106657905+Ni-zav@users.noreply.github.com> Date: Sun, 11 Oct 2026 10:59:36 +0700 Subject: [PATCH] fix(ui): show completion for library maintenance and Auto Sync (#875) --- crates/ui-egui/src/lib.rs | 19 +++++++++++++++++++ crates/ui-egui/src/tests_activity.rs | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index f853fe166..5c801a126 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -427,6 +427,25 @@ impl LightcraftApp { // Keep it hidden after release; O / the overlay eye can show it again. self.ui.mask_overlay = false; } + if r.is_ok() { + // These commands finish synchronously, so their completion would otherwise + // be invisible once the activity stack is gone. Never show success on error. + let completed = match id { + "library.compact" => Some(format!("✓ {}", i18n::tr("Optimize Library"))), + "library.clearPreviews" => Some(format!("✓ {}", i18n::tr("Clear Preview Cache"))), + "develop.autoSync" => Some(format!( + "{}: {}", + i18n::tr("Auto Sync"), + i18n::tr(if self.session.auto_sync { "On" } else { "Off" }) + )), + _ => None, + }; + if let Some(message) = completed { + // Menu clicks can arrive before the next frame updates last_time. + let now = self.tasks.repaint.as_ref().map(|ctx| ctx.input(|i| i.time)).unwrap_or(self.last_time); + self.ui.toast = Some((message, now + TOAST_SECONDS, None)); + } + } if r.is_ok() && id == "photo.label" { let label = params.get("label").and_then(Value::as_str).and_then(lightcraft_catalog::ColorLabel::parse); let text = match label { diff --git a/crates/ui-egui/src/tests_activity.rs b/crates/ui-egui/src/tests_activity.rs index ca2a6de7e..8833ddc79 100644 --- a/crates/ui-egui/src/tests_activity.rs +++ b/crates/ui-egui/src/tests_activity.rs @@ -393,3 +393,22 @@ fn rows_added_later_grow_the_stack() { assert!(stack.contains_rect(r), "row {} at {r:?} is cut off by the stack at {stack:?}", g.id()); } } + +#[test] +fn immediate_library_commands_and_auto_sync_show_completion() { + let mut h = demo(); + assert!(h.app.run("library.clearPreviews", json!({})).is_ok()); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("✓ Clear Preview Cache")); + + assert_eq!(h.app.run("develop.autoSync", json!({"on": true})).unwrap()["on"], true); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Auto Sync: On")); + + assert_eq!(h.app.run("develop.autoSync", json!({"on": false})).unwrap()["on"], false); + assert_eq!(h.app.ui.toast.as_ref().map(|t| t.0.as_str()), Some("Auto Sync: Off")); + + // This demo has no on-disk library to optimize. A failed command must not + // report completion (or accidentally keep its preceding success toast). + h.app.ui.toast = None; + assert!(h.app.run("library.compact", json!({})).is_err()); + assert!(h.app.ui.toast.is_none()); +}