Make a folder from the server browser

The in-app browser that chooses a library folder on the server could
only open folders that already existed, so a library, or an export
destination, that was not on the server yet had to be made in the
Nextcloud web page first. It now has "New folder": a name, then MKCOL,
then the parent listed again and the new folder walked into — a folder
somebody has just named is the one they mean to choose.

The listing is the server's rather than the name inserted locally: the
server may have normalised or refused it. A name with a slash, "..",
or nothing at all is refused before any request, because a folder
typed with a slash in it is a path the user did not mean.

remote_folders holds the two WebDAV round trips (list, make) off the UI
thread, with the answer delivered through a Slint timer, so the album
sheet can use the same browser.
This commit is contained in:
2026-09-26 14:13:53 -04:00
parent 6683c14b40
commit 2eb06b1064
5 changed files with 272 additions and 4 deletions
+50
View File
@@ -342,6 +342,56 @@ fn wire_folder_picker_navigation(window: &AppWindow, controller: &Rc<LaunchContr
render(&w, &ctl);
});
}
{
let weak = window.as_weak();
let ctl = controller.clone();
window.on_launch_browse_make(move |name| {
let Some(w) = weak.upgrade() else { return };
let (account, parent) = {
let m = ctl.model.borrow();
let Some(b) = m.browser.as_ref() else { return };
let Some(account) = m.session().cloned() else { return };
(account, b.path.clone())
};
let conn = match ctl
.store
.connection(&account, crate::remote::needs_secret(&account))
{
Ok(c) => c,
Err(e) => {
ctl.model.borrow_mut().fail(format!("credentials: {e}"));
render(&w, &ctl);
return;
}
};
let name = name.trim().to_string();
let child = if parent.is_empty() {
name.clone()
} else {
format!("{parent}/{name}")
};
ctl.model.borrow_mut().browse_to(parent.clone());
render(&w, &ctl);
// Made, then walked into: a folder somebody has just named is
// the one they mean to choose.
let (weak, ctl) = (weak.clone(), ctl.clone());
crate::remote_folders::make(conn, parent, name, move |result| {
let Some(w) = weak.upgrade() else { return };
match result {
Ok(_) => {
ctl.model.borrow_mut().browse_to(child.clone());
render(&w, &ctl);
spawn_folder_list(w.as_weak(), ctl.clone(), child);
}
Err(e) => {
ctl.model.borrow_mut().close_browser();
ctl.model.borrow_mut().fail(e);
render(&w, &ctl);
}
}
});
});
}
{
let weak = window.as_weak();
let ctl = controller.clone();