From 281db6cd0b5379cc164f586a7eb3a657edbd52b0 Mon Sep 17 00:00:00 2001
From: gnacho
Date: Thu, 17 Sep 2026 20:39:15 +0200
Subject: [PATCH 1/2] fix(sync): re-arm a recovery probe that died while
offline (closes #207)
---
src/core/scheduler.rs | 53 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 53 insertions(+)
diff --git a/src/core/scheduler.rs b/src/core/scheduler.rs
index 03a4752..21255e6 100644
--- a/src/core/scheduler.rs
+++ b/src/core/scheduler.rs
@@ -1192,6 +1192,16 @@ impl SchedulerInner {
self.state
.set(AppState::Offline, t("Waiting for a network connection"));
} else if !was_online {
+ // Issue #207: the recovery probe is a one-shot timer; if it fired
+ // while the machine was offline, `start` aborted on !online and
+ // the timer died. Re-arm it now so the folder recovers by itself
+ // once the network is back, instead of sitting queued behind the
+ // server_unreachable gate until a manual sync. The probe guard
+ // (start_source) makes the re-arm a no-op if one is still pending.
+ if self.server_unreachable {
+ self.queue.add(Trigger::Retry);
+ self.schedule_server_probe();
+ }
if !self.queue.is_empty() || !self.manual_only() {
self.request(Trigger::NetworkRestored);
} else {
@@ -1777,6 +1787,49 @@ mod tests {
assert_eq!(scheduler.state().snapshot().state, AppState::IdleOk);
}
+ /// Issue #207: the recovery probe is a one-shot timer. If it fires while
+ /// the machine is offline, `start` aborts on !online and the timer is
+ /// gone for good. When the network comes back, the folder must still
+ /// recover on its own - the probe is re-armed and the queued retry runs -
+ /// without a manual sync or an app restart.
+ #[test]
+ fn network_restore_rearms_a_probe_that_died_while_offline() {
+ let (scheduler, source, runner) = make_scheduler(None);
+ scheduler.request(Trigger::Startup);
+ run_idle(&source);
+ finish(&runner, SyncOutcome::NetworkError);
+ assert!(scheduler.server_unreachable());
+ let probe_id = source.borrow().only_id();
+
+ // Connectivity drops before the probe fires; the probe fires anyway
+ // and its one-shot start() aborts on !online: the timer is dead.
+ scheduler.set_online(false);
+ fire_timer(&source, probe_id);
+ assert_eq!(
+ runner.0.borrow().start_calls,
+ 1,
+ "an offline probe must not run the engine"
+ );
+ assert_eq!(
+ source.borrow().pending(),
+ 0,
+ "the one-shot probe timer is gone"
+ );
+
+ // Connectivity returns: the folder recovers without a manual sync.
+ scheduler.set_online(true);
+ assert!(
+ source.borrow().pending() >= 1,
+ "the recovery probe is re-armed on network restore"
+ );
+ let rearmed_id = source.borrow().only_id();
+ fire_timer(&source, rearmed_id);
+ assert_eq!(runner.0.borrow().start_calls, 2);
+ finish(&runner, SyncOutcome::Success);
+ assert!(!scheduler.server_unreachable());
+ assert_eq!(scheduler.state().snapshot().state, AppState::IdleOk);
+ }
+
/// Issue #179: after the server recovers (Success clears the gate), a
/// fresh automatic trigger runs the engine again.
#[test]
From eb23e1c0c5939959c175cb195317b0668a44c1a9 Mon Sep 17 00:00:00 2001
From: gnacho
Date: Thu, 17 Sep 2026 20:49:11 +0200
Subject: [PATCH 2/2] chore(release): bump version to 0.2.24
---
CHANGELOG.md | 6 ++++++
Cargo.lock | 2 +-
Cargo.toml | 2 +-
PKGBUILD | 2 +-
README.es.md | 4 ++--
README.md | 4 ++--
data/io.github.gnacho.nextsync.metainfo.xml | 1 +
landing/index.html | 2 +-
version.json | 9 ++++-----
9 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 35f8afd..2744f58 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,12 @@
Todas las versiones notables de NextSync se documentan aquí. El formato sigue [Keep a Changelog](https://keepachangelog.com/es/1.1.0/) y el versionado es **+0.0.2 por release, reiniciado en 0.1.4** (decisión del usuario, 22-Ago-2026; sustituye al +0.02 anterior).
+## [0.2.24] - 2026-09-17
+
+### Fixed
+
+- Folders no longer stay Offline after connectivity returns when the recovery probe fired while the machine was offline: the probe is re-armed on network restore and synchronization resumes on its own, without a manual sync or an app restart (#207).
+
## [0.2.22] - 2026-09-17
### Fixed
diff --git a/Cargo.lock b/Cargo.lock
index 825a8df..61695f9 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1283,7 +1283,7 @@ dependencies = [
[[package]]
name = "nextsync"
-version = "0.2.22"
+version = "0.2.24"
dependencies = [
"async-channel",
"data-encoding",
diff --git a/Cargo.toml b/Cargo.toml
index 785a7cd..97839d0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "nextsync"
-version = "0.2.22"
+version = "0.2.24"
edition = "2021"
rust-version = "1.83"
license = "GPL-3.0-or-later"
diff --git a/PKGBUILD b/PKGBUILD
index 42efb6e..26f2199 100644
--- a/PKGBUILD
+++ b/PKGBUILD
@@ -1,6 +1,6 @@
# Maintainer: gnacho
pkgname=nextsync
-pkgver=0.2.22
+pkgver=0.2.24
pkgrel=1
pkgdesc='Nextcloud desktop synchronization client for GNOME (Rust rewrite)'
arch=('x86_64' 'aarch64')
diff --git a/README.es.md b/README.es.md
index 19f02a6..aad7e6d 100644
--- a/README.es.md
+++ b/README.es.md
@@ -17,7 +17,7 @@
-
+
@@ -84,7 +84,7 @@ Ambos motores se esconden detrás del mismo trait pequeño, así que un proveedo
Descarga el `.pkg.tar.zst` de la [última release](https://github.com/gnacho/nextsync/releases/latest) e instálalo:
```bash
-sudo pacman -U nextsync-0.2.22-1-x86_64.pkg.tar.zst
+sudo pacman -U nextsync-0.2.24-1-x86_64.pkg.tar.zst
```
El paquete depende de `gtk4` y `libadwaita`. Para cuentas Nextcloud instala `nextcloud-client` (aporta `nextcloudcmd`); para cuentas OpenCloud, el `opencloudcmd` oficial.
diff --git a/README.md b/README.md
index 1d3a984..8cf5609 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@
-
+
@@ -84,7 +84,7 @@ Both engines sit behind the same small trait, so a new provider is a command bui
Download the `.pkg.tar.zst` from the [latest release](https://github.com/gnacho/nextsync/releases/latest) and install it:
```bash
-sudo pacman -U nextsync-0.2.22-1-x86_64.pkg.tar.zst
+sudo pacman -U nextsync-0.2.24-1-x86_64.pkg.tar.zst
```
The package depends on `gtk4` and `libadwaita`. For Nextcloud accounts install `nextcloud-client` (it provides `nextcloudcmd`); for OpenCloud accounts, the official `opencloudcmd`.
diff --git a/data/io.github.gnacho.nextsync.metainfo.xml b/data/io.github.gnacho.nextsync.metainfo.xml
index 99728ef..c5b1b1b 100644
--- a/data/io.github.gnacho.nextsync.metainfo.xml
+++ b/data/io.github.gnacho.nextsync.metainfo.xml
@@ -32,6 +32,7 @@
io.github.gnacho.nextsync
+
diff --git a/landing/index.html b/landing/index.html
index bc09ba4..67e79e8 100644
--- a/landing/index.html
+++ b/landing/index.html
@@ -210,7 +210,7 @@
Lo que NextSync no hace (todavía)
Instalación fácil y rápida
En Arch, CachyOS y derivadas hay paquete listo en cada release.
Descarga el paquete .pkg.tar.zst más reciente desde GitHub Releases y ajusta el nombre del fichero.
diff --git a/version.json b/version.json
index f512b8e..4a1bbf8 100644
--- a/version.json
+++ b/version.json
@@ -1,12 +1,11 @@
{
"schema_version": 1,
- "version": "0.2.22",
+ "version": "0.2.24",
"mandatory": false,
- "summary": "Detect a missing sync engine and stop reporting success when nothing can be synchronized.",
+ "summary": "Folders recover on their own after connectivity returns, even when the recovery probe fired while the machine was offline.",
"changelog": [
- "When the sync binary (nextcloudcmd / opencloudcmd) is not installed, folders now show a clear 'sync engine is not installed' state instead of a generic failure - or worse, a misleading 'Synchronization completed' from the unchanged-ETag interval shortcut while nothing was actually synced.",
- "A configured engine path that no longer exists resolves as missing too, so a stale override surfaces the same actionable state.",
- "The missing-engine reason is written to the daily log and raises a desktop notification like other problem outcomes."
+ "After a period without connectivity, folders no longer stay Offline until a manual sync or an app restart: when the network comes back, the server recovery probe is re-armed automatically and synchronization resumes on its own.",
+ "Fixes the case where the one-shot recovery probe fired while the machine was offline, died silently, and the NetworkRestored trigger stayed parked behind the server-unreachable gate forever."
],
"released_at": "2026-09-17T00:00:00Z"
}