LR-IMP-LOCAL: Forget deleted files when browsing from the sidebar too

The sidebar reads the folder on a worker thread and skipped the cleanup library.browse does, so the fix for #1392 didn't reach the app.
This commit is contained in:
Michael Anderson
2026-10-14 10:40:41 -05:00
parent 697010d520
commit e8818281d5
4 changed files with 53 additions and 1 deletions
+9
View File
@@ -60,6 +60,15 @@ fn browse(s: &mut Session, p: &Value) -> Result<Value> {
}))
}
impl Session {
/// Forget the untouched Local records of files gone from the folder `dir` (with its subfolders when
/// `subfolders`) that a browse just listed as `files`: what `library.browse` does after reading a folder, for a
/// UI that reads it on a worker thread instead. Returns how many went.
pub fn forget_gone_from(&mut self, dir: &str, subfolders: bool, files: &[String]) -> usize {
forget_gone(self, dir, subfolders, files)
}
}
/// Local records in the browsed folder whose files are no longer on disk (deleted or moved by
/// another app) and that nothing was changed on are forgotten, as `library.forgetLocal` forgets
/// them: the folder shows what is on disk, not photos that fail to load (and log an error) on
+37
View File
@@ -2983,6 +2983,43 @@ mod tests {
let _ = std::fs::remove_dir_all(&dir);
}
/// Browsing a folder again from the sidebar forgets the untouched Local records of files deleted from it by
/// another app, as `library.browse` does (issue #1392).
#[test]
fn browsing_again_from_the_ui_forgets_deleted_files() {
let mut h = demo([1200.0, 900.0]);
let t = Duration::from_secs(10);
let dir = std::env::temp_dir().join(format!("lc-browse-gone-{}", std::process::id()));
std::fs::create_dir_all(&dir).unwrap();
let img = lightcraft_raster::Rgba8::from_fn(8, 8, |x, y| [(x * 9) as u8, (y * 12) as u8, 80, 255]);
let o = lightcraft_engine::export::ExportOptions { format: lightcraft_engine::export::ExportFormat::Png, ..Default::default() };
let png = lightcraft_engine::export::encode_image(&img, &o).unwrap();
std::fs::write(dir.join("a.png"), &png).unwrap();
std::fs::write(dir.join("b.png"), &png).unwrap();
let path = dir.to_string_lossy().to_string();
let browse = |h: &mut Headless| {
let r = h.request("engine.execute", json!({"command": "library.browse", "params": {"path": path}}), t);
assert_eq!(r["result"]["scanning"], true, "{r}");
h.settle(SETTLE);
};
browse(&mut h);
assert_eq!(h.app.session.visible_cloned().len(), 2);
// another app deletes the photo that isn't selected (a selected or open one stays, shown as missing)
let held: Vec<lightcraft_catalog::PhotoId> =
h.app.session.selection.ids.iter().copied().chain(h.app.session.selection.active).chain(h.app.session.previous_active).collect();
let name = |id: &lightcraft_catalog::PhotoId| h.app.session.catalog.photo(*id).map(|p| p.file_name.clone()).unwrap_or_default();
let gone = if held.iter().any(|id| name(id) == "b.png") { "a.png" } else { "b.png" };
std::fs::remove_file(dir.join(gone)).unwrap();
// a later session: the browse's "Add 2 Photos" step is no longer in the undo history (photos it refers to
// are kept, like selected ones)
h.app.session.undo.clear();
h.app.session.redo.clear();
// the folder clicked again in the sidebar
browse(&mut h);
assert_eq!(h.app.session.visible_cloned().len(), 1, "the deleted file's record is forgotten");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn add_folder_scans_in_the_background() {
let mut h = demo([1200.0, 900.0]);
+6
View File
@@ -559,6 +559,12 @@ pub fn poll_scan(app: &mut LightcraftApp, ctx: &egui::Context) {
}
app.session.import_probes = out.probes;
if task.browse {
// records of files gone from the folder (deleted or moved by another app) that nothing was changed on go,
// as `library.browse` forgets them (issue #1392)
if let Some(b) = app.session.browse.clone() {
let listed: Vec<String> = out.candidates.iter().map(|c| c.path.clone()).collect();
app.session.forget_gone_from(&b.path, b.subfolders, &listed);
}
// what the library doesn't know yet joins the Local view
let queue: Vec<String> =
out.candidates.iter().filter(|c| c.duplicate != Some("path".into()) && c.error.is_none()).map(|c| c.path.clone()).collect();
+1 -1
View File
@@ -4,7 +4,7 @@
### Local folders
- Photos deleted (or moved) by another app no longer stay in a Local folder, failing to load and logging an error
each time you look (issue #1392): browsing the folder again leaves them out. A photo you had rated, flagged or
each time you look (issue #1392): the next time you browse the folder, in a later session, they're left out. A photo you had rated, flagged or
edited stays, marked missing, so its changes aren't lost; photos in My Photos are unchanged.
### Hard-edged Heal and Clone spots