Waiting files: retried from the background logic (also while hidden); no library switch while they wait

This commit is contained in:
Bruno-Alumn
2026-10-30 19:21:07 +01:00
parent 410b19a334
commit 6dbaa1c42c
4 changed files with 33 additions and 6 deletions
+1
View File
@@ -66,6 +66,7 @@ impl LightcraftApp {
if import_tick_allowed(ctx) {
import::tick(self, ctx);
}
panels::library_problem::import_waiting(self, ctx);
lightroom_import::tick(self, ctx);
recheck::tick(self);
tasks::poll(self, ctx);
@@ -11,6 +11,10 @@ pub(crate) fn can_switch_library(app: &LightcraftApp) -> Result<(), String> {
if app.import.is_some() || app.scan.is_some() {
return Err("wait for the import to finish before switching libraries".into());
}
// they were meant for the library open now (issue #2779)
if !app.waiting_import.files.is_empty() {
return Err("wait for the photos waiting to be imported before switching libraries".into());
}
Ok(())
}
+3 -3
View File
@@ -823,8 +823,9 @@ fn open(app: &mut LightcraftApp, path: Option<String>) {
/// Import the files that waited for a library, in the background like files dropped on the window
/// (issue #374). While another import or a Synchronize Folder runs they keep waiting, and a toast
/// says why once (issue #2779).
fn import_waiting(app: &mut LightcraftApp, ctx: &egui::Context) {
/// says why once (issue #2779). Called from the background logic, so they go in as soon as that
/// ends even while the window is hidden.
pub(crate) fn import_waiting(app: &mut LightcraftApp, ctx: &egui::Context) {
if app.waiting_import.files.is_empty() {
return;
}
@@ -854,7 +855,6 @@ pub fn logic(app: &mut LightcraftApp, ctx: &egui::Context) {
let files = app.library_problem.take().map(|p| p.pending_import).unwrap_or_default();
app.waiting_import.files.extend(files);
}
import_waiting(app, ctx);
let batch_in_flight = app.synthetic_in_flight;
alt_at_launch(app, ctx);
// Alt/Option can create the launch list here, after the frame's input was accepted. Drop
+25 -3
View File
@@ -817,8 +817,13 @@ fn waiting_files_wait_for_a_running_import_instead_of_being_dropped() {
let img = lightcraft_raster::Rgba8 { width: 8, height: 8, data: vec![[v, 3, 9, 255]; 64] };
lightcraft_codecs::encode_png(&lightcraft_codecs::EncodeImage::rgba8(&img), &lightcraft_codecs::EncodeMeta::default()).unwrap()
};
let (busy, waiting) = (dir.join("busy.png"), dir.join("waiting.png"));
std::fs::write(&busy, png(10)).unwrap();
// enough photos that the first import is still running two frames later
let busy = dir.join("busy");
std::fs::create_dir_all(&busy).unwrap();
for i in 0..100u8 {
std::fs::write(busy.join(format!("{i}.png")), png(i)).unwrap();
}
let waiting = dir.join("waiting.png");
std::fs::write(&waiting, png(200)).unwrap();
let app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), Services { png: None, ..Default::default() });
@@ -832,13 +837,30 @@ fn waiting_files_wait_for_a_running_import_instead_of_being_dropped() {
// a library opens another way (File ▸ Open Library…) while an import is running
h.app.session.open_library(&lib, false).unwrap();
crate::import::start_paths(&mut h.app, vec![busy.to_string_lossy().to_string()]).unwrap();
// the UI pass takes the files; the next background tick tries to import them
h.step();
h.step();
assert!(h.app.library_problem.is_none(), "the problem is over");
assert_eq!(h.app.waiting_import.files, vec![waiting.to_string_lossy().to_string()], "the waiting file is kept");
let toast = h.app.ui.toast.as_ref().map(|t| t.0.clone()).unwrap_or_default();
assert!(toast.contains("An import is running") && toast.contains("once it ends"), "{toast}");
// the files belong to this library: no switch while they wait, also when what they wait for
// is a Synchronize Folder, which doesn't hold switching back by itself (the import set aside)
let running = h.app.import.take();
let r = h.app.run("app.openLibrary", json!({"path": dir.join("other").to_string_lossy()}));
h.app.import = running;
assert!(r.as_ref().is_err_and(|e| e.contains("waiting")), "{r:?}");
assert_eq!(h.app.session.library.as_ref().map(|l| l.dir.clone()), Some(lib.clone()));
assert!(h.step_until(T, |h| h.app.import.is_none() && h.app.session.catalog.len() == 2), "{} photos", h.app.session.catalog.len());
// the window is hidden from here on: only the background logic runs, and the waiting file
// still goes in once the first import ends
let t0 = std::time::Instant::now();
while !(h.app.import.is_none() && h.app.session.catalog.len() == 101) && t0.elapsed() < T {
let ctx = h.view.ctx.clone();
h.app.logic(&ctx);
std::thread::sleep(Duration::from_millis(2));
}
assert_eq!(h.app.session.catalog.len(), 101, "both imports went in while hidden");
assert!(h.app.waiting_import.files.is_empty());
let _ = h.app.session.close_library();
let _ = std::fs::remove_dir_all(&dir);