Folder labels: drop stale labels on any move; clear labels of empty folders

Review fixes. folder_records_follow now drops records left at the
destination even when the moved folder carries none, so an unlabelled
folder renamed onto an old path no longer inherits that path's label.
folder.label {label: none} works once the folder's photos are gone (a
label can no longer get stuck); setting a label still needs photos.

Adds tests for both, undo/redo of a labelled move, and labels replayed
from the journal without a snapshot.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nathaniel Ledford
2026-10-09 17:40:20 -04:00
co-authored by Claude Opus 5.5
parent c40a5675bb
commit b26d5271b8
4 changed files with 56 additions and 6 deletions
+6 -2
View File
@@ -392,7 +392,7 @@ pub fn specs() -> Vec<CommandSpec> {
"Set Folder Color Label",
[],
None,
"{path, label: red|yellow|green|blue|purple|none} — give a folder of the library (one its photos were imported from, see library.folders) a colour label, or take it off; one undo step. The label follows the folder when it is renamed or moved with folder.rename / folder.move → {path, label}",
"{path, label: red|yellow|green|blue|purple|none} — give a folder of the library (one its photos were imported from, see library.folders) a colour label, or take it off (also once its photos are gone); one undo step. The label follows the folder when it is renamed or moved with folder.rename / folder.move → {path, label}",
always,
|s, p| {
const C: &str = "folder.label";
@@ -401,10 +401,14 @@ pub fn specs() -> Vec<CommandSpec> {
"none" => None,
x => Some(ColorLabel::parse(x).ok_or_else(|| bad(C, format!("unknown label {x:?}")))?),
};
// taking a label off always works, also once the folder's photos are gone
let f = Filter { library_folder: Some(path.to_string()), ..Default::default() };
if s.catalog.query(&f, &Sort::default()).is_empty() {
if label.is_some() && s.catalog.query(&f, &Sort::default()).is_empty() {
return Err(bad(C, format!("{path}: no photo in the library was imported from it")));
}
if label.is_none() && s.catalog.folder_record(path).is_none() {
return Ok(json!({"path": path, "label": label}));
}
let op = s.catalog.folder_label_op(path, label);
s.commit(if label.is_some() { "Set Folder Color Label" } else { "Remove Folder Color Label" }, op)?;
Ok(json!({"path": path, "label": label}))