diff --git a/crates/engine/src/library.rs b/crates/engine/src/library.rs index 4382160a..88fa2f3f 100644 --- a/crates/engine/src/library.rs +++ b/crates/engine/src/library.rs @@ -295,7 +295,13 @@ impl Session { { log::error!("library: {e}"); } - let (mut journal, catalog, report) = Journal::open(catalog)?; + let (mut journal, mut catalog, report) = Journal::open(catalog)?; + // A loaded catalog counts revisions from 0, like every other one. Caches (sidebar counts, + // keyword tree, the grid's list…) are keyed on the revision, so give each library loaded + // in this process its own range: otherwise an empty library opened after one that was + // never edited this session would look unchanged and show the old library's numbers. + static LOADS: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0); + catalog.revision = LOADS.fetch_add(1, std::sync::atomic::Ordering::Relaxed).wrapping_add(1) << 32; self.catalog = catalog; self.undo.clear(); self.redo.clear(); diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index a4addbf3..a271bec9 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -42,6 +42,8 @@ mod tests_quit_unsaved; #[cfg(test)] mod tests_scroll; #[cfg(test)] +mod tests_switch_library; +#[cfg(test)] mod tests_unsaved; use std::sync::mpsc::{Receiver, Sender}; diff --git a/crates/ui-egui/src/tests_switch_library.rs b/crates/ui-egui/src/tests_switch_library.rs new file mode 100644 index 00000000..d7bffc0d --- /dev/null +++ b/crates/ui-egui/src/tests_switch_library.rs @@ -0,0 +1,43 @@ +//! Switching libraries must not leave the old library's sidebar numbers, keyword tree or date +//! groups on screen. A library loaded from disk starts at catalog revision 0, and so does a brand +//! new empty one, so caches keyed on the revision alone would see "nothing changed". + +use serde_json::json; + +use crate::{LightcraftApp, Services}; + +#[test] +fn opening_another_library_drops_the_old_librarys_cached_panels() { + let dir = std::env::temp_dir().join(format!("lc-ui-switchlib-{}", std::process::id())); + let _ = std::fs::remove_dir_all(&dir); + let (a, b) = (dir.join("a"), dir.join("b")); + std::fs::create_dir_all(&a).unwrap(); + std::fs::create_dir_all(&b).unwrap(); + + // library A with photos, reopened from disk (as at launch) + let mut session = lightcraft_engine::Session::new().with_fs(); + session.open_library(&a, true).unwrap(); + session.close_library().unwrap(); + session.open_library(&a, false).unwrap(); + assert!(!session.catalog.is_empty()); + + let mut app = LightcraftApp::new(session, Services::default()); + let (cat, caches) = (&app.session.catalog, &mut app.caches); + assert!(caches.counts(cat).total > 0); + assert!(!caches.keyword_tree(cat).is_empty()); + assert!(!caches.date_groups(cat).is_empty()); + + // the grid's own list is cached too + assert!(!app.session.visible().is_empty()); + + app.run("app.openLibrary", json!({"path": b.to_string_lossy()})).unwrap(); + assert!(app.session.catalog.is_empty(), "library B is empty"); + let (cat, caches) = (&app.session.catalog, &mut app.caches); + assert_eq!(caches.counts(cat).total, 0, "All Photos count"); + assert_eq!(caches.counts(cat).picks, 0); + assert!(caches.keyword_tree(cat).is_empty(), "keywords"); + assert!(caches.date_groups(cat).is_empty(), "By Date"); + assert!(app.session.visible().is_empty(), "grid"); + + let _ = std::fs::remove_dir_all(&dir); +}