From 426cd8cd8b10a8ebc02af363750ce3144677e062 Mon Sep 17 00:00:00 2001 From: clover caruso Date: Thu, 1 Oct 2026 22:36:38 -0700 Subject: [PATCH] fix: keep sync advice while working offline Turning Work Offline on during a problem state dropped the advice line, shifting everything below it. The advice now stays, and the toggle test covers every state, password-protected and unreadable sections included. Assisted-by: claude-opus-5.5 --- crates/snowbound/src/sync.rs | 66 +++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 31 deletions(-) diff --git a/crates/snowbound/src/sync.rs b/crates/snowbound/src/sync.rs index 0e2f86d2f954069ccf18db42dc0f4bc7a66f509a..1056de58714a2f08de9a1f9f3cf5ed053d500b40 100644 --- a/crates/snowbound/src/sync.rs +++ b/crates/snowbound/src/sync.rs @@ -441,28 +441,25 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { let host = facts.place.first().map_or("the notebook", String::as_str); let waiting = changes(sync.queued); let conflict; - let advice = if facts.offline { - None - } else { - match state { - SyncState::NotConnected => Some(match sync.queued { - 0 => format!("Can’t reach {host}. Sync continues when it’s back."), - _ => format!("Can’t reach {host}. {waiting} will sync when it’s back."), - }), - SyncState::InUse => { - Some("Someone else is saving a section. Sync continues when they finish.".into()) - } - SyncState::ReadOnly => Some(format!( - "This location is read-only. Your changes stay on {THIS}." - )), - SyncState::Protected => { - Some("Snowbound can’t open password-protected sections yet.".into()) - } - SyncState::Unreadable | SyncState::Failed => { - Some("Snowbound keeps trying. Sync Now tries again at once.".into()) - } - _ => None, + // A problem's advice stays while working offline, so turning it on moves nothing. + let advice = match state { + SyncState::NotConnected => Some(match sync.queued { + 0 => format!("Can’t reach {host}. Sync continues when it’s back."), + _ => format!("Can’t reach {host}. {waiting} will sync when it’s back."), + }), + SyncState::InUse => { + Some("Someone else is saving a section. Sync continues when they finish.".into()) } + SyncState::ReadOnly => Some(format!( + "This location is read-only. Your changes stay on {THIS}." + )), + SyncState::Protected => { + Some("Snowbound can’t open password-protected sections yet.".into()) + } + SyncState::Unreadable | SyncState::Failed => { + Some("Snowbound keeps trying. Sync Now tries again at once.".into()) + } + _ => None, }; if !showing && headline == "Up to date" && !facts.conflicts.is_empty() { conflict = match facts.conflicts.as_slice() { @@ -1380,6 +1377,18 @@ mod tests { _ => status(true, 0, None), })) }), + ("protected", || { + facts(sections(|index| match index { + 1 => status(true, 0, Some((io::ErrorKind::Unsupported, "Protected"))), + _ => status(true, 0, None), + })) + }), + ("unreadable", || { + facts(sections(|index| match index { + 4 => status(true, 0, Some((io::ErrorKind::InvalidData, "Not a section"))), + _ => status(true, 0, None), + })) + }), ("in-use", || { facts(sections(|index| match index { 2 => status(true, 2, Some((io::ErrorKind::ResourceBusy, "In use"))), @@ -1433,17 +1442,12 @@ mod tests { ))) } - /// Work offline's switch stays put as it turns on and off, and so do the buttons below - /// but where a problem's advice, which offline leaves out, goes and comes back. + /// Work offline's switch, and everything below it, stays put as it turns on and off. #[test] fn toggling_offline_moves_nothing() { let switch = id().child("status").child("header").child("offline"); let controls = id().child("controls"); for (name, made) in states() { - let advised = !matches!( - overall(&made().sections).state(), - SyncState::UpToDate | SyncState::Syncing - ); let mut places = Vec::new(); for offline in [false, true, false] { let mut ui = ui(Appearance::Light); @@ -1452,10 +1456,10 @@ mod tests { settle(&mut ui, &mut now, &facts); places.push((ui.rect(switch).unwrap(), ui.rect(controls).unwrap())); } - for pair in places.windows(2) { - assert_eq!(pair[0].0, pair[1].0, "{name}"); - assert!(advised || pair[0].1 == pair[1].1, "{name}: {places:?}"); - } + assert!( + places.windows(2).all(|pair| pair[0] == pair[1]), + "{name}: {places:?}" + ); } } -- 2.54.0