feat(tab): allow entering of network uri as path

This commit is contained in:
Cheong Lau 2025-11-22 12:29:02 +10:00 committed by Jacob Kauffmann
parent e053db3bf9
commit 66733b7fda
3 changed files with 159 additions and 94 deletions

View file

@ -16,6 +16,24 @@ use crate::{
const TARGET_URI_ATTRIBUTE: &str = "standard::target-uri";
fn resolve_uri(uri: &str) -> (String, gio::File) {
let file = gio::File::for_uri(uri);
// Resolve the target-uri if it exists
if let Ok(file_info) = file.query_info(
TARGET_URI_ATTRIBUTE,
gio::FileQueryInfoFlags::NONE,
gio::Cancellable::NONE,
) {
if let Some(resolved_uri) = file_info.attribute_as_string(TARGET_URI_ATTRIBUTE) {
let resolved_uri = String::from(resolved_uri);
let file = gio::File::for_uri(&resolved_uri);
return (resolved_uri, file);
}
}
(uri.to_string(), file)
}
fn gio_icon_to_path(icon: &gio::Icon, size: u16) -> Option<PathBuf> {
if let Some(themed_icon) = icon.downcast_ref::<gio::ThemedIcon>() {
for name in themed_icon.names() {
@ -83,19 +101,8 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
}
fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
let mut file = gio::File::for_uri(uri);
let force_dir = uri.starts_with("network:///");
// Resolve the target-uri if it exists
if let Ok(file_info) = file.query_info(
TARGET_URI_ATTRIBUTE,
gio::FileQueryInfoFlags::NONE,
gio::Cancellable::NONE,
) {
if let Some(resolved_uri) = file_info.attribute_as_string(TARGET_URI_ATTRIBUTE) {
file = gio::File::for_uri(resolved_uri.as_str());
}
}
let (_, file) = resolve_uri(uri);
// Read .hidden file if present
let hidden_files: Box<[String]> = if let Some(path) = file.path() {
@ -210,6 +217,17 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
Ok(items)
}
fn dir_info(uri: &str) -> Result<(String, String), glib::Error> {
let (resolved_uri, file) = resolve_uri(uri);
let info = file.query_info(
gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
gio::FileQueryInfoFlags::NONE,
gio::Cancellable::NONE,
)?;
Ok((resolved_uri, info.display_name().into()))
}
fn mount_op(uri: String, event_tx: mpsc::UnboundedSender<Event>) -> gio::MountOperation {
let mount_op = gio::MountOperation::new();
mount_op.connect_ask_password(
@ -270,6 +288,7 @@ enum Cmd {
IconSizes,
mpsc::Sender<Result<Vec<tab::Item>, String>>,
),
DirInfo(String, mpsc::Sender<Result<(String, String), glib::Error>>),
Unmount(MounterItem),
}
@ -494,38 +513,27 @@ impl Gvfs {
}
);
}
Cmd::NetworkScan(mut uri, sizes, items_tx) => {
let original_uri = uri.clone();
let mut file = gio::File::for_uri(&uri);
if let Ok(file_info) = file.query_info(
TARGET_URI_ATTRIBUTE,
gio::FileQueryInfoFlags::NONE,
gio::Cancellable::NONE,
) {
if let Some(resolved_uri) = file_info.attribute_as_string(TARGET_URI_ATTRIBUTE) {
uri = resolved_uri.into();
file = gio::File::for_uri(&uri);
}
}
Cmd::NetworkScan(uri, sizes, items_tx) => {
let (resolved_uri, file) = resolve_uri(&uri);
let needs_mount = uri != "network:///" && match file.find_enclosing_mount(gio::Cancellable::NONE) {
let needs_mount = resolved_uri != "network:///" && match file.find_enclosing_mount(gio::Cancellable::NONE) {
Ok(_) => false,
Err(err) => matches!(err.kind::<gio::IOErrorEnum>(), Some(gio::IOErrorEnum::NotMounted))
};
if needs_mount {
let mount_op = mount_op(uri.clone(), event_tx.clone());
let mount_op = mount_op(resolved_uri.clone(), event_tx.clone());
let event_tx = event_tx.clone();
file.mount_enclosing_volume(
gio::MountMountFlags::empty(),
Some(&mount_op),
gio::Cancellable::NONE,
move |res| {
log::info!("network scan mounted {uri}: result {res:?}");
log::info!("network scan mounted {resolved_uri}: result {res:?}");
// FIXME sometimes a uri can be mounted and then not recognized as mounted...
// seems to be related to uri with a path
items_tx.blocking_send(network_scan(&original_uri, sizes)).unwrap();
event_tx.send(Event::NetworkResult(uri, match res {
items_tx.blocking_send(network_scan(&uri, sizes)).unwrap();
event_tx.send(Event::NetworkResult(resolved_uri, match res {
Ok(()) => {
Ok(true)
},
@ -537,9 +545,12 @@ impl Gvfs {
}
);
} else {
items_tx.send(network_scan(&original_uri, sizes)).await.unwrap();
items_tx.send(network_scan(&uri, sizes)).await.unwrap();
}
}
Cmd::DirInfo(uri, result_tx) => {
result_tx.send(dir_info(&uri)).await.unwrap();
}
Cmd::Unmount(mounter_item) => {
let MounterItem::Gvfs(item) = mounter_item else { continue };
let ItemKind::Mount = item.kind else { continue };
@ -640,6 +651,14 @@ impl Mounter for Gvfs {
items_rx.blocking_recv()
}
fn dir_info(&self, uri: &str) -> Option<(String, String)> {
let (result_tx, mut result_rx) = mpsc::channel(1);
self.command_tx
.send(Cmd::DirInfo(uri.to_string(), result_tx))
.unwrap();
result_rx.blocking_recv().and_then(|res| res.ok())
}
fn unmount(&self, item: MounterItem) -> Task<()> {
let command_tx = self.command_tx.clone();
Task::future(async move {

View file

@ -116,6 +116,7 @@ pub trait Mounter: Send + Sync {
fn mount(&self, item: MounterItem) -> Task<()>;
fn network_drive(&self, uri: String) -> Task<()>;
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>>;
fn dir_info(&self, uri: &str) -> Option<(String, String)>;
fn unmount(&self, item: MounterItem) -> Task<()>;
fn subscription(&self) -> Subscription<MounterMessage>;
}