| ... | @@ -441,28 +441,25 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { | ... | @@ -441,28 +441,25 @@ fn build(ui: &mut Ui, facts: &Facts) -> Picked { |
| 441 | let host = facts.place.first().map_or("the notebook", String::as_str); | 441 | let host = facts.place.first().map_or("the notebook", String::as_str); |
| 442 | let waiting = changes(sync.queued); | 442 | let waiting = changes(sync.queued); |
| 443 | let conflict; | 443 | let conflict; |
| 444 | let advice = if facts.offline { | 444 | // A problem's advice stays while working offline, so turning it on moves nothing. |
| 445 | None | 445 | let advice = match state { |
| 446 | } else { | 446 | SyncState::NotConnected => Some(match sync.queued { |
| 447 | match state { | 447 | 0 => format!("Can’t reach {host}. Sync continues when it’s back."), |
| 448 | SyncState::NotConnected => Some(match sync.queued { | 448 | _ => format!("Can’t reach {host}. {waiting} will sync when it’s back."), |
| 449 | 0 => format!("Can’t reach {host}. Sync continues when it’s back."), | 449 | }), |
| 450 | _ => format!("Can’t reach {host}. {waiting} will sync when it’s back."), | 450 | SyncState::InUse => { |
| 451 | }), | 451 | Some("Someone else is saving a section. Sync continues when they finish.".into()) |
| 452 | SyncState::InUse => { | | |
| 453 | Some("Someone else is saving a section. Sync continues when they finish.".into()) | | |
| 454 | } | | |
| 455 | SyncState::ReadOnly => Some(format!( | | |
| 456 | "This location is read-only. Your changes stay on {THIS}." | | |
| 457 | )), | | |
| 458 | SyncState::Protected => { | | |
| 459 | Some("Snowbound can’t open password-protected sections yet.".into()) | | |
| 460 | } | | |
| 461 | SyncState::Unreadable | SyncState::Failed => { | | |
| 462 | Some("Snowbound keeps trying. Sync Now tries again at once.".into()) | | |
| 463 | } | | |
| 464 | _ => None, | | |
| 465 | } | 452 | } |
| | 453 | SyncState::ReadOnly => Some(format!( |
| | 454 | "This location is read-only. Your changes stay on {THIS}." |
| | 455 | )), |
| | 456 | SyncState::Protected => { |
| | 457 | Some("Snowbound can’t open password-protected sections yet.".into()) |
| | 458 | } |
| | 459 | SyncState::Unreadable | SyncState::Failed => { |
| | 460 | Some("Snowbound keeps trying. Sync Now tries again at once.".into()) |
| | 461 | } |
| | 462 | _ => None, |
| 466 | }; | 463 | }; |
| 467 | if !showing && headline == "Up to date" && !facts.conflicts.is_empty() { | 464 | if !showing && headline == "Up to date" && !facts.conflicts.is_empty() { |
| 468 | conflict = match facts.conflicts.as_slice() { | 465 | conflict = match facts.conflicts.as_slice() { |
| ... | @@ -1380,6 +1377,18 @@ mod tests { | ... | @@ -1380,6 +1377,18 @@ mod tests { |
| 1380 | _ => status(true, 0, None), | 1377 | _ => status(true, 0, None), |
| 1381 | })) | 1378 | })) |
| 1382 | }), | 1379 | }), |
| | 1380 | ("protected", || { |
| | 1381 | facts(sections(|index| match index { |
| | 1382 | 1 => status(true, 0, Some((io::ErrorKind::Unsupported, "Protected"))), |
| | 1383 | _ => status(true, 0, None), |
| | 1384 | })) |
| | 1385 | }), |
| | 1386 | ("unreadable", || { |
| | 1387 | facts(sections(|index| match index { |
| | 1388 | 4 => status(true, 0, Some((io::ErrorKind::InvalidData, "Not a section"))), |
| | 1389 | _ => status(true, 0, None), |
| | 1390 | })) |
| | 1391 | }), |
| 1383 | ("in-use", || { | 1392 | ("in-use", || { |
| 1384 | facts(sections(|index| match index { | 1393 | facts(sections(|index| match index { |
| 1385 | 2 => status(true, 2, Some((io::ErrorKind::ResourceBusy, "In use"))), | 1394 | 2 => status(true, 2, Some((io::ErrorKind::ResourceBusy, "In use"))), |
| ... | @@ -1433,17 +1442,12 @@ mod tests { | ... | @@ -1433,17 +1442,12 @@ mod tests { |
| 1433 | ))) | 1442 | ))) |
| 1434 | } | 1443 | } |
| 1435 | | 1444 | |
| 1436 | /// Work offline's switch stays put as it turns on and off, and so do the buttons below | 1445 | /// Work offline's switch, and everything below it, stays put as it turns on and off. |
| 1437 | /// but where a problem's advice, which offline leaves out, goes and comes back. | | |
| 1438 | #[test] | 1446 | #[test] |
| 1439 | fn toggling_offline_moves_nothing() { | 1447 | fn toggling_offline_moves_nothing() { |
| 1440 | let switch = id().child("status").child("header").child("offline"); | 1448 | let switch = id().child("status").child("header").child("offline"); |
| 1441 | let controls = id().child("controls"); | 1449 | let controls = id().child("controls"); |
| 1442 | for (name, made) in states() { | 1450 | for (name, made) in states() { |
| 1443 | let advised = !matches!( | | |
| 1444 | overall(&made().sections).state(), | | |
| 1445 | SyncState::UpToDate | SyncState::Syncing | | |
| 1446 | ); | | |
| 1447 | let mut places = Vec::new(); | 1451 | let mut places = Vec::new(); |
| 1448 | for offline in [false, true, false] { | 1452 | for offline in [false, true, false] { |
| 1449 | let mut ui = ui(Appearance::Light); | 1453 | let mut ui = ui(Appearance::Light); |
| ... | @@ -1452,10 +1456,10 @@ mod tests { | ... | @@ -1452,10 +1456,10 @@ mod tests { |
| 1452 | settle(&mut ui, &mut now, &facts); | 1456 | settle(&mut ui, &mut now, &facts); |
| 1453 | places.push((ui.rect(switch).unwrap(), ui.rect(controls).unwrap())); | 1457 | places.push((ui.rect(switch).unwrap(), ui.rect(controls).unwrap())); |
| 1454 | } | 1458 | } |
| 1455 | for pair in places.windows(2) { | 1459 | assert!( |
| 1456 | assert_eq!(pair[0].0, pair[1].0, "{name}"); | 1460 | places.windows(2).all(|pair| pair[0] == pair[1]), |
| 1457 | assert!(advised || pair[0].1 == pair[1].1, "{name}: {places:?}"); | 1461 | "{name}: {places:?}" |
| 1458 | } | 1462 | ); |
| 1459 | } | 1463 | } |
| 1460 | } | 1464 | } |
| 1461 | | 1465 | |