Merge pull request #238 from DWAA1660/fix/export-missing-folder
Export: clear error when there's no export folder (instead of "Access is denied")
This commit is contained in:
commit
be87e1b6e7
|
|
@ -334,9 +334,16 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context, req: &ControlRequest
|
|||
}
|
||||
}
|
||||
|
||||
/// Default export folder: `~/Pictures/LightCraft Exports` (falls back to the working directory).
|
||||
/// Shown when an export has nowhere to go (no folder typed or chosen, and no home folder to default to).
|
||||
pub const NO_EXPORT_FOLDER: &str = "Choose an export folder first.";
|
||||
|
||||
/// Default export folder: `~/Pictures/LightCraft Exports` (`%USERPROFILE%\Pictures\LightCraft Exports`
|
||||
/// on Windows, where `HOME` usually isn't set). Empty when no home folder is known.
|
||||
pub fn default_export_dir() -> String {
|
||||
std::env::var("HOME").map(|h| format!("{h}/Pictures/LightCraft Exports")).unwrap_or_default()
|
||||
let home = if cfg!(windows) { std::env::var_os("USERPROFILE").or_else(|| std::env::var_os("HOME")) } else { std::env::var_os("HOME") };
|
||||
home.filter(|h| !h.is_empty())
|
||||
.map(|h| std::path::PathBuf::from(h).join("Pictures").join("LightCraft Exports").to_string_lossy().into_owned())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Export the selected photos (UI command `app.export`). Params: see
|
||||
|
|
@ -365,10 +372,23 @@ pub fn export_active(app: &mut LightcraftApp, p: &Value) -> Result<Value, String
|
|||
if ids.is_empty() {
|
||||
return Err("no photo selected".into());
|
||||
}
|
||||
// no folder given (e.g. File → Export with Preset): the last export's, else the default
|
||||
let last_dir = app.session.last_export.as_ref().and_then(|l| l.get("dir")).and_then(Value::as_str).map(str::to_string);
|
||||
let dir = p.get("dir").and_then(Value::as_str).map(str::to_string).or(last_dir).filter(|d| !d.is_empty()).unwrap_or_else(default_export_dir);
|
||||
let to = Destination { dir: dir.clone(), exact: p.get("path").and_then(Value::as_str).map(str::to_string) };
|
||||
// An exact output file needs no folder. A folder given but left blank (the Export dialog's Folder
|
||||
// field cleared) is an error, like Lightroom refusing to export to an unspecified folder, rather
|
||||
// than a silent write into the working directory. No folder at all (e.g. File → Export with
|
||||
// Preset): the last export's, else the default.
|
||||
let exact = p.get("path").and_then(Value::as_str).filter(|s| !s.trim().is_empty()).map(str::to_string);
|
||||
let dir = match p.get("dir").and_then(Value::as_str) {
|
||||
Some(d) => d.trim().to_string(),
|
||||
None => {
|
||||
let last_dir =
|
||||
app.session.last_export.as_ref().and_then(|l| l.get("dir")).and_then(Value::as_str).map(str::trim).filter(|d| !d.is_empty());
|
||||
last_dir.map(str::to_string).unwrap_or_else(default_export_dir)
|
||||
}
|
||||
};
|
||||
if dir.is_empty() && exact.is_none() {
|
||||
return Err(NO_EXPORT_FOLDER.into());
|
||||
}
|
||||
let to = Destination { dir: dir.clone(), exact };
|
||||
let background = p.get("background").and_then(Value::as_bool).unwrap_or(false) && app.services.write_shared.is_some();
|
||||
let out = if background {
|
||||
let items = lightcraft_engine::export::prepare_batch(&mut app.session, &ids, &opts)?;
|
||||
|
|
|
|||
|
|
@ -753,7 +753,13 @@ pub fn show_in_window(app: &mut LightcraftApp, ui: &mut egui::Ui, max_width: f32
|
|||
});
|
||||
}
|
||||
if let Some((id, params)) = clicked {
|
||||
let _ = run_item(app, &id, params);
|
||||
let r = run_item(app, &id, params);
|
||||
// an export that can't start (e.g. no folder) says why instead of doing nothing
|
||||
if let Err(e) = r
|
||||
&& matches!(id.as_str(), "app.export" | "app.exportPrevious")
|
||||
{
|
||||
app.toast(ui.ctx(), e);
|
||||
}
|
||||
}
|
||||
ui.cursor().left() - start
|
||||
}
|
||||
|
|
@ -999,6 +1005,13 @@ mod tests {
|
|||
// remembered (expanded) for Export with Previous, folder included
|
||||
let last = app.session.last_export.clone().unwrap();
|
||||
assert_eq!((last["format"].as_str(), last["width"].as_u64(), last.get("preset")), (Some("png"), Some(40), None));
|
||||
// a blank folder (the Export dialog's Folder field cleared) is refused with a clear message
|
||||
// instead of writing into the working directory
|
||||
let n = w.len();
|
||||
drop(w);
|
||||
let r = run_item(&mut app, "app.export", json!({"preset": "Tiny PNG", "dir": " "}));
|
||||
assert_eq!(r.unwrap_err(), crate::control::NO_EXPORT_FOLDER);
|
||||
assert_eq!(written.lock().unwrap().len(), n, "nothing written without a folder");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -824,8 +824,9 @@ pub fn show(app: &mut LightcraftApp, ctx: &egui::Context) {
|
|||
}
|
||||
if confirm {
|
||||
match confirm_dialog(app, &dlg) {
|
||||
// the import review stays open on an error (e.g. an unusable folder template)
|
||||
Err(e) if matches!(dlg, Dialog::Import { .. }) => app.toast(ctx, e),
|
||||
// the import review stays open on an error (e.g. an unusable folder template), and so
|
||||
// does Export (e.g. no folder chosen) so the choices aren't lost
|
||||
Err(e) if matches!(dlg, Dialog::Import { .. } | Dialog::Export { .. }) => app.toast(ctx, e),
|
||||
// the SAM 3 dialog stays open to show the download (or why it can't start)
|
||||
Err(e) if matches!(dlg, Dialog::SamModel { .. }) => {
|
||||
if let Dialog::SamModel { error, .. } = &mut dlg {
|
||||
|
|
|
|||
|
|
@ -158,8 +158,11 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) {
|
|||
o.remove("advance");
|
||||
}
|
||||
cull(app, id, params, advance);
|
||||
} else {
|
||||
let _ = app.run(id, params);
|
||||
} else if let Err(e) = app.run(id, params)
|
||||
&& matches!(id, "app.export" | "app.exportPrevious")
|
||||
{
|
||||
// an export that can't start (e.g. no folder) says why instead of doing nothing
|
||||
app.toast(ctx, e);
|
||||
}
|
||||
}
|
||||
for f in fire {
|
||||
|
|
@ -255,7 +258,12 @@ pub fn handle(app: &mut LightcraftApp, ctx: &egui::Context) {
|
|||
continue;
|
||||
}
|
||||
}
|
||||
let _ = app.run(&f, json!({}));
|
||||
// an export that can't start (e.g. no folder) says why instead of doing nothing
|
||||
if let Err(e) = app.run(&f, json!({}))
|
||||
&& matches!(f.as_str(), "app.export" | "app.exportPrevious")
|
||||
{
|
||||
app.toast(ctx, e);
|
||||
}
|
||||
match f.as_str() {
|
||||
"photo.pick" => app.toast(ctx, "Flagged as Pick"),
|
||||
"photo.reject" => app.toast(ctx, "Flagged as Reject"),
|
||||
|
|
|
|||
Loading…
Reference in New Issue