diff --git a/apps/lightcraft/src/session.rs b/apps/lightcraft/src/session.rs index 3cd66317b..082e1a757 100644 --- a/apps/lightcraft/src/session.rs +++ b/apps/lightcraft/src/session.rs @@ -280,12 +280,9 @@ fn open_library_session(in_memory: bool, dir: Option, rememb }; if remembered && !lightcraft_engine::library::is_library(&dir) { log::error!("library {} is missing", dir.display()); - let why = if dir.exists() { - "There is no LightCraft library in this folder any more." - } else { - "The library isn't there: it was moved or renamed, or its drive or network share isn't connected." - }; - return (unopened(), Some(LibraryProblem::new(dir.to_string_lossy(), why))); + let why = lightcraft_ui_egui::panels::library_problem::missing_library_reason(&dir); + // Try Again must not make one there either (an empty mount point, issue #3239) + return (unopened(), Some(LibraryProblem { existing_only: true, ..LibraryProblem::new(dir.to_string_lossy(), why) })); } let t0 = std::time::Instant::now(); let mut s = Session::new().with_fs().with_default_denoise_models().with_system_clock(); diff --git a/apps/lightcraft/src/tests.rs b/apps/lightcraft/src/tests.rs index 518c7b8fe..e54deed04 100644 --- a/apps/lightcraft/src/tests.rs +++ b/apps/lightcraft/src/tests.rs @@ -76,12 +76,13 @@ fn a_remembered_library_missing_at_launch_is_reported_not_made_anew() { let problem = problem.expect("the missing library is reported"); assert_eq!(problem.path, lib.to_string_lossy()); assert!(problem.can_retry, "Try Again once the drive is back"); + assert!(problem.existing_only, "Try Again makes no library there either"); assert!(!gone.exists(), "nothing is created where the library was"); assert!(s.library.is_none() && s.catalog.is_empty(), "no library, no demo photos"); // a remembered folder that is there but holds no library is reported too std::fs::create_dir_all(&lib).unwrap(); let (s, problem) = open_session(false, &launch, true); - assert!(problem.is_some() && s.library.is_none() && s.catalog.is_empty()); + assert!(problem.as_ref().is_some_and(|p| p.existing_only) && s.library.is_none() && s.catalog.is_empty(), "{problem:?}"); assert!(!lightcraft_engine::library::is_library(&lib), "nothing written in it"); // the default location (or --library) is still made when it's missing, with the demo photos let fresh = d.join("fresh").join("Lib"); diff --git a/crates/ui-egui/src/panels/library_problem.rs b/crates/ui-egui/src/panels/library_problem.rs index 9e2b084f4..1d83a2ff5 100644 --- a/crates/ui-egui/src/panels/library_problem.rs +++ b/crates/ui-egui/src/panels/library_problem.rs @@ -345,6 +345,11 @@ pub struct LibraryProblem { /// Try Again reopens `path` as a library folder (`false` in the browser, whose storage isn't /// a folder: reloading the page tries again). pub can_retry: bool, + /// Try Again opens `path` only once it holds a library again, and never makes one there: set + /// for the library remembered in Settings that was missing at launch, whose folder can be + /// the empty mount point of a share that isn't connected yet (issue #3239). Choose Another + /// Library… still creates one where asked. + pub existing_only: bool, /// The window lists the recent libraries to choose from instead (issue #1290); `error` then /// says why the last one tried didn't open. pub chooser: Option, @@ -714,6 +719,31 @@ fn unavailable(path: &str) -> Option { None } +/// Why the library at `dir` that has to exist already (a remembered one, issue #3239) can't be +/// opened: the folder is there without a catalog, or the folder itself is missing. +pub fn missing_library_reason(dir: &Path) -> &'static str { + if dir.exists() { + "There is no LightCraft library in this folder any more." + } else { + "The library isn't there: it was moved or renamed, or its drive or network share isn't connected." + } +} + +/// Try Again: reopen the problem's library. One that must exist already (`existing_only`) is +/// opened only once its folder holds a library again (a share remounted): until then the window +/// stays with the reason, and nothing is created there (issue #3239). +fn retry(app: &mut LightcraftApp, problem: LibraryProblem) { + let dir = Path::new(&problem.path); + if problem.existing_only && !lightcraft_engine::library::is_library(dir) { + if let Some(p) = app.library_problem.as_mut() { + p.error = missing_library_reason(dir).to_string(); + p.dismissed = false; + } + return; + } + open(app, Some(problem.path)); +} + /// What the user chose. #[derive(Clone, Copy, Debug, PartialEq)] enum Choice { @@ -811,6 +841,10 @@ fn open(app: &mut LightcraftApp, path: Option) { } } if let Some(path) = path { + // another library failed: Try Again may make that one, as it always could + if p.path != path { + p.existing_only = false; + } p.path = path; } p.error = e; @@ -1077,7 +1111,7 @@ fn act(app: &mut LightcraftApp, choice: Choice) { let Some(problem) = app.library_problem.clone() else { return }; let chosen = problem.chooser.as_ref().and_then(|c| c.selected.and_then(|i| c.libraries.get(i)).map(|l| (l.clone(), c.over))); match choice { - Choice::Retry => open(app, Some(problem.path)), + Choice::Retry => retry(app, problem), Choice::Choose => open(app, None), Choice::Open => { let Some((library, over)) = chosen.filter(|(l, _)| l.unavailable.is_none()) else { return }; diff --git a/crates/ui-egui/src/tests/launch_library.rs b/crates/ui-egui/src/tests/launch_library.rs index b0b7abf7f..9d71b7978 100644 --- a/crates/ui-egui/src/tests/launch_library.rs +++ b/crates/ui-egui/src/tests/launch_library.rs @@ -1032,3 +1032,34 @@ fn found_again(multipass: bool) { drop(h); let _ = std::fs::remove_dir_all(&dir); } + +/// Issue #3239: Try Again on a remembered library that was missing at launch makes no library in +/// its folder when the folder is there but empty (a share's mount point before the share is +/// connected): the window stays and says so. Once the folder holds the library again, it opens. +#[test] +fn try_again_on_a_missing_remembered_library_makes_nothing_in_an_empty_folder() { + let dir = temp("retry-existing"); + let mount = dir.join("Share"); + std::fs::create_dir_all(&mount).unwrap(); + let path = mount.to_string_lossy().into_owned(); + let mut app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), Services::default()); + app.library_problem = Some(LibraryProblem { existing_only: true, ..LibraryProblem::new(path.clone(), "missing") }); + let mut h = Headless::new(app, [1300.0, 900.0], 1.0); + settle(&mut h); + assert_eq!(h.request("ui.clickWidget", json!({"id": "button:libraryRetry"}), T)["ok"], true); + settle(&mut h); + assert!(!lightcraft_engine::library::is_library(&mount), "no catalog is made in the empty folder"); + assert_eq!(std::fs::read_dir(&mount).unwrap().count(), 0, "nothing is written there"); + assert!(h.app.session.library.is_none()); + let p = h.app.library_problem.as_ref().expect("the window stays"); + assert_eq!(p.error, "There is no LightCraft library in this folder any more."); + assert!(has(&h, "button:libraryRetry")); + // the share is connected again: Try Again opens the library in it + make_library(&mount); + assert_eq!(h.request("ui.clickWidget", json!({"id": "button:libraryRetry"}), T)["ok"], true); + settle(&mut h); + assert_eq!(h.app.session.library.as_ref().map(|l| l.dir.to_string_lossy().into_owned()), Some(path)); + assert!(h.app.library_problem.is_none()); + drop(h); + let _ = std::fs::remove_dir_all(&dir); +} diff --git a/docs/whats-new.md b/docs/whats-new.md index 88ce76a2b..fa3564385 100644 --- a/docs/whats-new.md +++ b/docs/whats-new.md @@ -7,8 +7,10 @@ yet or it was moved, the window now says it couldn't open the library and offers Try Again, Choose Another Library…, Continue Without Saving and Quit. Before, LightCraft quietly made a new, empty library at that path, filled it with the demo photos and opened it, so it looked as if the photos and edits were gone. Nothing is - written there now. A library given with `--library` or `LIGHTCRAFT_LIBRARY`, and the default location on first - launch, are still created as before. + written there now, and Try Again opens the library once its drive or share is back: on an empty folder left in + its place, such as a share's mount point, it says so again instead of making a new library there. A library + given with `--library` or `LIGHTCRAFT_LIBRARY`, and the default location on first launch, are still created as + before. ## 2026-10-24