From cf0bea3c4854f77a5f901e417bb12ddafc8f7802 Mon Sep 17 00:00:00 2001 From: Nacho Date: Fri, 21 Aug 2026 20:29:46 +0200 Subject: [PATCH 1/2] fix(progress): seed run_active flag when connecting during a run connect_progress() wires the progress forwarder after start() has already fired startup syncs. The run_active flag introduced in #145 starts false, so every event from the in-flight startup run was dropped and the folder row showed no per-file progress or bar movement. Seed the flag from scheduler.is_running() when connecting, so an in-flight run keeps emitting events. Later runs are still gated by prepare_sync()/finished() as before. Closes #148 --- src/core/account_runtime.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/core/account_runtime.rs b/src/core/account_runtime.rs index 1b8ccbc..bf73ae6 100644 --- a/src/core/account_runtime.rs +++ b/src/core/account_runtime.rs @@ -330,6 +330,13 @@ impl FolderRuntime { // cannot repaint the row. let run_active = std::rc::Rc::new(std::cell::Cell::new(false)); self.scheduler.set_run_active(run_active.clone()); + // Issue #148: startup syncs begin before connect_progress() wires the + // forwarder, so the flag would stay false for the entire first run and + // the UI would show no per-file progress. Seed it from the scheduler + // state when connecting, so an in-flight run keeps emitting events. + if self.scheduler.is_running() { + run_active.set(true); + } glib::spawn_future_local(async move { while let Ok(progress) = progress_rx.recv().await { if !run_active.get() { From 93575bb6e5e8f2fdacfeb600846b260195b55128 Mon Sep 17 00:00:00 2001 From: Nacho Date: Fri, 21 Aug 2026 20:47:46 +0200 Subject: [PATCH 2/2] fix(ui): force folder row redraw after state/progress updates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GTK may not repaint the row when properties are updated from a subscription callback, so after a sync finishes the row could still show "Synchronizing…" with the old file caption until the user clicked elsewhere to force a redraw. Queue an explicit draw on the whole slot after every state render and on the progress label after every progress change, so the icon, spinner, run bar, subtitle and caption all update together without user interaction. Closes #150 --- src/ui/folder_status.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/ui/folder_status.rs b/src/ui/folder_status.rs index 2bc415d..0fd967e 100644 --- a/src/ui/folder_status.rs +++ b/src/ui/folder_status.rs @@ -396,6 +396,7 @@ impl FolderStatusRow { let spinner = this.spinner.clone(); let progress_bar = this.progress_bar.clone(); let row = this.row.clone(); + let slot = this.slot.clone(); let remote_path = this.remote_path.clone(); let format_last_sync = this.format_last_sync.clone(); let subscription = controller.subscribe(move |snapshot: &StateSnapshot| { @@ -408,6 +409,11 @@ impl FolderStatusRow { format_last_sync.as_ref().map(|f| f()), snapshot, ); + // Issue #150: GTK may not repaint the row when properties + // are updated from a subscription callback. Force a redraw + // of the whole slot so state, spinner, bar and caption all + // update together. + slot.queue_draw(); }); this._subscription = Some(subscription); // Live per-file progress (issue #86). Only the widgets are @@ -422,8 +428,12 @@ impl FolderStatusRow { .unwrap_or_else(|| progress.path.clone()); progress_label.set_text(&progress_line_text(progress, &file)); progress_label.set_visible(true); + progress_label.queue_draw(); + } + _ => { + progress_label.set_visible(false); + progress_label.queue_draw(); } - _ => progress_label.set_visible(false), }); this._progress_subscription = Some(progress_subscription); }