git.lucas.co / cce-secrets
secrets manager
git clone https://git.lucas.co/cce-secrets.git

commitca35933b2cc55cdc4bd530131c491d680ccf90ca
parent16c21dfa88
authorLucas Galante <[email protected]>
date2026-09-21 12:53
cce-secrets: show the sync result after the reload, not before it

The Sync button's status ("in sync", "synced: …") was sent first and the
list reload that follows overwrote it with "N entries" a frame later, so
it flashed and vanished. run_sync now returns the line and the worker
sends it after load_entries.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

 src/main.rs | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index 4118361..09e35d8 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -137,7 +137,10 @@ async fn poke_sync_daemon() -> bool {
 /// file to record a new run, then show that run's outcome. Without it: the
 /// one-shot binary, whose summary line ("synced: …", "in sync") or
 /// refusal text is the status.
-async fn run_sync(tx: &calloop::channel::Sender<AppMessage>) {
+/// Returns the status line (text, is_error). The caller shows it *after*
+/// the reload that follows a sync, or the reload's "N entries" would wipe
+/// it a frame later.
+async fn run_sync() -> (String, bool) {
     let before = read_sync_state().map(|(t, _)| t).unwrap_or(0);
     if poke_sync_daemon().await {
         // A pass is one `op item list` plus writes; a dialog nobody
@@ -148,13 +151,11 @@ async fn run_sync(tx: &calloop::channel::Sender<AppMessage>) {
                 if t > before {
                     let is_error = result.starts_with("failed");
                     let msg = if result.is_empty() { "synced".to_string() } else { result };
-                    let _ = tx.send(AppMessage::Status(msg, is_error));
-                    return;
+                    return (msg, is_error);
                 }
             }
         }
-        let _ = tx.send(AppMessage::Status("sync daemon did not report within 90s".to_string(), true));
-        return;
+        return ("sync daemon did not report within 90s".to_string(), true);
     }
     let out = tokio::process::Command::new("cce-keyring-sync")
         .arg("sync")
@@ -173,15 +174,15 @@ async fn run_sync(tx: &calloop::channel::Sender<AppMessage>) {
             if out.status.success() {
                 let line = pick(&out.stdout);
                 let msg = if line.is_empty() { "synced".to_string() } else { line };
-                let _ = tx.send(AppMessage::Status(msg, false));
+                return (msg, false);
             } else {
                 let line = pick(&out.stderr);
                 let msg = if line.is_empty() { "sync failed".to_string() } else { line };
-                let _ = tx.send(AppMessage::Status(msg, true));
+                return (msg, true);
             }
         }
         Err(e) => {
-            let _ = tx.send(AppMessage::Status(format!("cce-keyring-sync not runnable: {e}"), true));
+            return (format!("cce-keyring-sync not runnable: {e}"), true);
         }
     }
 }
@@ -218,8 +219,9 @@ fn spawn_worker(rx: std::sync::mpsc::Receiver<Cmd>, tx: calloop::channel::Sender
                 match cmd {
                     Cmd::Reload => load_entries(&ss, &tx).await,
                     Cmd::Sync => {
-                        run_sync(&tx).await;
+                        let (msg, is_error) = run_sync().await;
                         load_entries(&ss, &tx).await;
+                        let _ = tx.send(AppMessage::Status(msg, is_error));
                     }
                     op => {
                         let edits = matches!(op, Cmd::CreateItem { .. } | Cmd::UpdateItem { .. } | Cmd::DeleteItem { .. });