feat: retry opening network locations after mounting
This commit is contained in:
parent
7711b4bcc0
commit
c7e9828d7b
9 changed files with 333 additions and 56 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1482,6 +1482,7 @@ dependencies = [
|
|||
name = "cosmic-files"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bzip2",
|
||||
"chrono",
|
||||
"compio",
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ rust-version = "1.85.0"
|
|||
vergen = { version = "8", features = ["git", "gitcl"] }
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
chrono = { version = "0.4", features = ["unstable-locales"] }
|
||||
icu = { version = "1.5.0", features = [
|
||||
"experimental",
|
||||
|
|
|
|||
216
src/app.rs
216
src/app.rs
|
|
@ -334,6 +334,12 @@ pub enum Message {
|
|||
NavMenuAction(NavMenuAction),
|
||||
NetworkAuth(MounterKey, String, MounterAuth, mpsc::Sender<MounterAuth>),
|
||||
NetworkDriveInput(String),
|
||||
NetworkDriveOpenEntityAfterMount {
|
||||
entity: Entity,
|
||||
},
|
||||
NetworkDriveOpenTabAfterMount {
|
||||
location: Location,
|
||||
},
|
||||
NetworkDriveSubmit,
|
||||
NetworkResult(MounterKey, String, Result<bool, String>),
|
||||
NewItem(Option<Entity>, bool),
|
||||
|
|
@ -885,6 +891,58 @@ impl App {
|
|||
activate: bool,
|
||||
selection_paths: Option<Vec<PathBuf>>,
|
||||
) -> (Entity, Task<Message>) {
|
||||
#[cfg(feature = "gvfs")]
|
||||
if let Location::Network(ref uri, ref name, Some(ref path)) = location {
|
||||
dbg!(path, self.mounter_items.len());
|
||||
let mut found = false;
|
||||
|
||||
if let Some(key) = self
|
||||
.mounter_items
|
||||
.iter()
|
||||
.find_map(|(k, items)| {
|
||||
items.iter().find_map(|item| {
|
||||
found |= item.path().is_some_and(|p| path.starts_with(p))
|
||||
|| item.name() == *name
|
||||
|| item.uri() == *uri;
|
||||
dbg!(
|
||||
item.is_mounted(),
|
||||
item.path(),
|
||||
path,
|
||||
name,
|
||||
item.name(),
|
||||
item.uri(),
|
||||
uri
|
||||
);
|
||||
(!item.is_mounted() && found).then(|| *k)
|
||||
})
|
||||
})
|
||||
.or(if found {
|
||||
dbg!("found so skip...");
|
||||
None
|
||||
} else {
|
||||
dbg!("not found...");
|
||||
// TODO do we need to choose the correct mounter?
|
||||
self.mounter_items.iter().map(|(k, _)| *k).next()
|
||||
})
|
||||
{
|
||||
if let Some(mounter) = MOUNTERS.get(&key) {
|
||||
let location = location.clone();
|
||||
dbg!(&location);
|
||||
return (
|
||||
Entity::null(),
|
||||
mounter.network_drive(uri.clone()).map(move |_| {
|
||||
cosmic::Action::App(Message::NetworkDriveOpenTabAfterMount {
|
||||
location: location.clone(),
|
||||
})
|
||||
}),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
dbg!("huh...");
|
||||
}
|
||||
}
|
||||
|
||||
dbg!(&location);
|
||||
let mut tab = Tab::new(
|
||||
location.clone(),
|
||||
self.config.tab,
|
||||
|
|
@ -1061,6 +1119,7 @@ impl App {
|
|||
location: Location,
|
||||
selection_paths: Option<Vec<PathBuf>>,
|
||||
) -> Task<Message> {
|
||||
dbg!("rescan", &location);
|
||||
log::info!("rescan_tab {entity:?} {location:?} {selection_paths:?}");
|
||||
let icon_sizes = self.config.tab.icon_sizes;
|
||||
let mounter_items = self.mounter_items.clone();
|
||||
|
|
@ -1303,7 +1362,12 @@ impl App {
|
|||
})
|
||||
.size(16),
|
||||
)
|
||||
.data(Location::Path(path.clone()))
|
||||
.data(match favorite {
|
||||
Favorite::Network { uri, name, path } => {
|
||||
Location::Network(uri.clone(), name.clone(), Some(path.to_owned()))
|
||||
}
|
||||
_ => Location::Path(path.clone()),
|
||||
})
|
||||
.data(FavoriteIndex(favorite_i))
|
||||
});
|
||||
}
|
||||
|
|
@ -1327,6 +1391,7 @@ impl App {
|
|||
.data(Location::Network(
|
||||
"network:///".to_string(),
|
||||
fl!("networks"),
|
||||
None,
|
||||
))
|
||||
.divider_above()
|
||||
});
|
||||
|
|
@ -1346,7 +1411,13 @@ impl App {
|
|||
nav_model = nav_model.insert(|mut b| {
|
||||
b = b.text(item.name()).data(MounterData(key, item.clone()));
|
||||
if let Some(path) = item.path() {
|
||||
b = b.data(Location::Path(path.clone()));
|
||||
b = b.data(Location::Network(
|
||||
item.uri().to_string(),
|
||||
item.name(),
|
||||
Some(path),
|
||||
));
|
||||
} else {
|
||||
b = b.data(Location::Network(item.uri().to_string(), item.name(), None));
|
||||
}
|
||||
if let Some(icon) = item.icon(true) {
|
||||
b = b.icon(widget::icon::icon(icon).size(16));
|
||||
|
|
@ -2155,35 +2226,85 @@ impl Application for App {
|
|||
fn on_nav_select(&mut self, entity: Entity) -> Task<Self::Message> {
|
||||
self.nav_model.activate(entity);
|
||||
if let Some(location) = self.nav_model.data::<Location>(entity) {
|
||||
dbg!(&location);
|
||||
|
||||
let should_open = match location {
|
||||
Location::Path(path) => match path.try_exists() {
|
||||
Ok(true) => true,
|
||||
Ok(false) => {
|
||||
log::warn!("failed to open favorite, path does not exist: {:?}", path);
|
||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||
path: path.clone(),
|
||||
entity,
|
||||
});
|
||||
false
|
||||
#[cfg(feature = "gvfs")]
|
||||
Location::Network(uri, name, Some(path))
|
||||
if !path.try_exists().unwrap_or_default() =>
|
||||
{
|
||||
dbg!(&location, path, self.mounter_items.len(), uri, path);
|
||||
let mut found = false;
|
||||
|
||||
if let Some(key) = self
|
||||
.mounter_items
|
||||
.iter()
|
||||
.find_map(|(k, items)| {
|
||||
items.iter().find_map(|item| {
|
||||
found |= item.path().is_some_and(|p| path.starts_with(&p))
|
||||
|| item.name() == *name
|
||||
|| item.uri() == *uri;
|
||||
dbg!(item.is_mounted(), item.path());
|
||||
(!item.is_mounted() && found).then(|| *k)
|
||||
})
|
||||
})
|
||||
.or(if found {
|
||||
None
|
||||
} else {
|
||||
// TODO do we need to choose the correct mounter?
|
||||
self.mounter_items.iter().map(|(k, _)| *k).next()
|
||||
})
|
||||
{
|
||||
if let Some(mounter) = MOUNTERS.get(&key) {
|
||||
dbg!(&location);
|
||||
// TODO can we detect an error and handle failure?
|
||||
let location = location.clone();
|
||||
return mounter.network_drive(uri.clone()).map(move |_| {
|
||||
cosmic::Action::App(Message::NetworkDriveOpenEntityAfterMount {
|
||||
entity,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("failed to open favorite for path: {:?}, {}", path, err);
|
||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||
path: path.clone(),
|
||||
entity,
|
||||
});
|
||||
false
|
||||
|
||||
log::warn!("failed to open favorite, path does not exist: {:?}", path);
|
||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||
path: path.clone(),
|
||||
entity,
|
||||
});
|
||||
false
|
||||
}
|
||||
Location::Path(path) | Location::Network(_, _, Some(path)) => {
|
||||
match path.try_exists() {
|
||||
Ok(true) => true,
|
||||
Ok(false) => {
|
||||
log::warn!("failed to open favorite, path does not exist: {:?}", path);
|
||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||
path: path.clone(),
|
||||
entity,
|
||||
});
|
||||
false
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("failed to open favorite for path: {:?}, {}", path, err);
|
||||
self.dialog_pages.push_back(DialogPage::FavoritePathError {
|
||||
path: path.clone(),
|
||||
entity,
|
||||
});
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_ => true,
|
||||
};
|
||||
|
||||
dbg!(should_open);
|
||||
if should_open {
|
||||
let message = Message::TabMessage(None, tab::Message::Location(location.clone()));
|
||||
return self.update(message);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(data) = self.nav_model.data::<MounterData>(entity) {
|
||||
if let Some(mounter) = MOUNTERS.get(&data.0) {
|
||||
return mounter
|
||||
|
|
@ -2296,10 +2417,45 @@ impl Application for App {
|
|||
match message {
|
||||
Message::AddToSidebar(entity_opt) => {
|
||||
let mut favorites = self.config.favorites.clone();
|
||||
// check if the selected entity is in the current tab
|
||||
// else just use the selected entity and check its location
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
|
||||
for path in self.selected_paths(entity_opt) {
|
||||
let favorite = Favorite::from_path(path);
|
||||
dbg!(&path);
|
||||
let is_network = self.tab_model.data::<Tab>(entity).and_then(|tab| {
|
||||
let in_current_tab =
|
||||
tab.location.path_opt().zip(path.parent()).is_some_and(
|
||||
|(t_path, parent)| {
|
||||
dbg!(&parent, &t_path);
|
||||
parent == t_path
|
||||
},
|
||||
);
|
||||
let tab = if in_current_tab {
|
||||
self.tab_model
|
||||
.data::<Tab>(self.tab_model.active())
|
||||
.unwrap_or(tab)
|
||||
} else {
|
||||
tab
|
||||
};
|
||||
dbg!(in_current_tab, &tab.location);
|
||||
let name = Location::Path(path.clone()).title();
|
||||
if let Location::Network(uri, _, _) = tab.location.clone() {
|
||||
Some((uri, name, path.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
dbg!(&is_network);
|
||||
let name = Location::Path(path.clone()).title();
|
||||
let favorite = if let Some((uri, _, _)) = is_network.clone() {
|
||||
Favorite::Network { uri, name, path }
|
||||
} else {
|
||||
Favorite::from_path(path)
|
||||
};
|
||||
if !favorites.iter().any(|f| f == &favorite) {
|
||||
favorites.push(favorite);
|
||||
dbg!(&favorites);
|
||||
}
|
||||
}
|
||||
config_set!(favorites, favorites);
|
||||
|
|
@ -2991,6 +3147,7 @@ impl Application for App {
|
|||
Message::OpenInNewTab(entity_opt) => {
|
||||
return Task::batch(self.selected_paths(entity_opt).into_iter().filter_map(
|
||||
|path| {
|
||||
dbg!(&path);
|
||||
if path.is_dir() {
|
||||
Some(self.open_tab(Location::Path(path), false, None))
|
||||
} else {
|
||||
|
|
@ -3510,6 +3667,7 @@ impl Application for App {
|
|||
config.show_hidden = !config.show_hidden;
|
||||
return self.update(Message::TabConfig(config));
|
||||
}
|
||||
|
||||
Message::TabMessage(entity_opt, tab_message) => {
|
||||
let entity = entity_opt.unwrap_or_else(|| self.tab_model.active());
|
||||
|
||||
|
|
@ -3528,6 +3686,7 @@ impl Application for App {
|
|||
for tab_command in tab_commands {
|
||||
match tab_command {
|
||||
tab::Command::Action(action) => {
|
||||
dbg!(&action);
|
||||
commands.push(self.update(action.message(Some(entity))));
|
||||
}
|
||||
tab::Command::AddNetworkDrive => {
|
||||
|
|
@ -3535,6 +3694,7 @@ impl Application for App {
|
|||
self.set_show_context(true);
|
||||
}
|
||||
tab::Command::AddToSidebar(path) => {
|
||||
dbg!("add to sidebar");
|
||||
let mut favorites = self.config.favorites.clone();
|
||||
let favorite = Favorite::from_path(path);
|
||||
if !favorites.iter().any(|f| f == &favorite) {
|
||||
|
|
@ -3580,6 +3740,7 @@ impl Application for App {
|
|||
}
|
||||
tab::Command::OpenFile(paths) => self.open_file(&paths),
|
||||
tab::Command::OpenInNewTab(path) => {
|
||||
dbg!(&path);
|
||||
commands.push(self.open_tab(Location::Path(path.clone()), false, None));
|
||||
}
|
||||
tab::Command::OpenInNewWindow(path) => match env::current_exe() {
|
||||
|
|
@ -4037,14 +4198,15 @@ impl Application for App {
|
|||
}
|
||||
NavMenuAction::OpenInNewTab(entity) => {
|
||||
match self.nav_model.data::<Location>(entity) {
|
||||
Some(Location::Network(ref uri, ref display_name)) => {
|
||||
Some(Location::Network(ref uri, ref display_name, path)) => {
|
||||
return self.open_tab(
|
||||
Location::Network(uri.clone(), display_name.clone()),
|
||||
Location::Network(uri.clone(), display_name.clone(), path.clone()),
|
||||
false,
|
||||
None,
|
||||
);
|
||||
}
|
||||
Some(Location::Path(ref path)) => {
|
||||
dbg!(path);
|
||||
return self.open_tab(Location::Path(path.clone()), false, None);
|
||||
}
|
||||
Some(Location::Recents) => {
|
||||
|
|
@ -4304,6 +4466,13 @@ impl Application for App {
|
|||
Message::DragId(id) => {
|
||||
return window::drag(id);
|
||||
}
|
||||
Message::NetworkDriveOpenEntityAfterMount { entity } => {
|
||||
return self.on_nav_select(entity);
|
||||
}
|
||||
Message::NetworkDriveOpenTabAfterMount { location } => {
|
||||
dbg!("location");
|
||||
return self.open_tab(location, false, None);
|
||||
}
|
||||
}
|
||||
|
||||
Task::none()
|
||||
|
|
@ -5880,6 +6049,7 @@ pub(crate) mod test_utils {
|
|||
nested: usize,
|
||||
name_len: usize,
|
||||
) -> io::Result<(TempDir, Tab)> {
|
||||
dbg!("new tab...");
|
||||
let fs = simple_fs(files, hidden, dirs, nested, name_len)?;
|
||||
let path = fs.path();
|
||||
|
||||
|
|
|
|||
|
|
@ -64,6 +64,11 @@ pub enum Favorite {
|
|||
Pictures,
|
||||
Videos,
|
||||
Path(PathBuf),
|
||||
Network {
|
||||
uri: String,
|
||||
name: String,
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl Favorite {
|
||||
|
|
@ -95,6 +100,7 @@ impl Favorite {
|
|||
Self::Pictures => dirs::picture_dir(),
|
||||
Self::Videos => dirs::video_dir(),
|
||||
Self::Path(path) => Some(path.clone()),
|
||||
Self::Network { path, .. } => Some(path.clone()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
} else if &arg == "--recents" {
|
||||
Location::Recents
|
||||
} else if &arg == "--network" {
|
||||
Location::Network("network:///".to_string(), fl!("networks"))
|
||||
Location::Network("network:///".to_string(), fl!("networks"), None)
|
||||
} else {
|
||||
//TODO: support more URLs
|
||||
let path = match url::Url::parse(&arg) {
|
||||
|
|
|
|||
13
src/menu.rs
13
src/menu.rs
|
|
@ -152,7 +152,11 @@ pub fn context_menu<'a>(
|
|||
match (&tab.mode, &tab.location) {
|
||||
(
|
||||
tab::Mode::App | tab::Mode::Desktop,
|
||||
Location::Desktop(..) | Location::Path(..) | Location::Search(..) | Location::Recents,
|
||||
Location::Desktop(..)
|
||||
| Location::Path(..)
|
||||
| Location::Search(..)
|
||||
| Location::Recents
|
||||
| Location::Network(_, _, Some(_)),
|
||||
) => {
|
||||
if selected_trash_only {
|
||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||
|
|
@ -298,7 +302,11 @@ pub fn context_menu<'a>(
|
|||
}
|
||||
(
|
||||
tab::Mode::Dialog(dialog_kind),
|
||||
Location::Desktop(..) | Location::Path(..) | Location::Search(..) | Location::Recents,
|
||||
Location::Desktop(..)
|
||||
| Location::Path(..)
|
||||
| Location::Search(..)
|
||||
| Location::Recents
|
||||
| Location::Network(_, _, Some(_)),
|
||||
) => {
|
||||
if selected > 0 {
|
||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||
|
|
@ -327,6 +335,7 @@ pub fn context_menu<'a>(
|
|||
}
|
||||
}
|
||||
(_, Location::Network(..)) => {
|
||||
dbg!(selected, &tab.mode);
|
||||
if selected > 0 {
|
||||
if selected_dir == 1 && selected == 1 || selected_dir == 0 {
|
||||
children.push(menu_item(fl!("open"), Action::Open).into());
|
||||
|
|
|
|||
|
|
@ -3,7 +3,13 @@ use cosmic::{
|
|||
widget, Task,
|
||||
};
|
||||
use gio::{glib, prelude::*};
|
||||
use std::{any::TypeId, cell::Cell, future::pending, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
cell::Cell,
|
||||
future::pending,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::{mpsc, Mutex};
|
||||
|
||||
use super::{Mounter, MounterAuth, MounterItem, MounterItems, MounterMessage};
|
||||
|
|
@ -30,6 +36,7 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
|||
let mut items = MounterItems::new();
|
||||
for (i, mount) in monitor.mounts().into_iter().enumerate() {
|
||||
items.push(MounterItem::Gvfs(Item {
|
||||
uri: MountExt::root(&mount).uri().to_string(),
|
||||
kind: ItemKind::Mount,
|
||||
index: i,
|
||||
name: MountExt::name(&mount).to_string(),
|
||||
|
|
@ -44,7 +51,12 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
|||
// Volumes with mounts are already listed by mount
|
||||
continue;
|
||||
}
|
||||
let uri = VolumeExt::activation_root(&volume)
|
||||
.map(|f| f.uri().to_string())
|
||||
.unwrap_or_default();
|
||||
items.push(MounterItem::Gvfs(Item {
|
||||
// TODO can we get URI for volumes with no mount?
|
||||
uri,
|
||||
kind: ItemKind::Volume,
|
||||
index: i,
|
||||
name: VolumeExt::name(&volume).to_string(),
|
||||
|
|
@ -57,7 +69,11 @@ fn items(monitor: &gio::VolumeMonitor, sizes: IconSizes) -> MounterItems {
|
|||
items
|
||||
}
|
||||
|
||||
fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
||||
fn network_scan(
|
||||
uri: &str,
|
||||
sizes: IconSizes,
|
||||
path: Option<&Path>,
|
||||
) -> Result<Vec<tab::Item>, String> {
|
||||
let file = gio::File::for_uri(uri);
|
||||
let mut items = Vec::new();
|
||||
for info_res in file
|
||||
|
|
@ -77,6 +93,7 @@ fn network_scan(uri: &str, sizes: IconSizes) -> Result<Vec<tab::Item>, String> {
|
|||
file.child(info.name()).uri().to_string()
|
||||
},
|
||||
display_name.clone(),
|
||||
file.child(info.name()).path(),
|
||||
);
|
||||
|
||||
//TODO: support dir or file
|
||||
|
|
@ -193,11 +210,15 @@ fn mount_op(uri: String, event_tx: mpsc::UnboundedSender<Event>) -> gio::MountOp
|
|||
enum Cmd {
|
||||
Items(IconSizes, mpsc::Sender<MounterItems>),
|
||||
Rescan,
|
||||
Mount(MounterItem),
|
||||
NetworkDrive(String),
|
||||
Mount(
|
||||
MounterItem,
|
||||
tokio::sync::oneshot::Sender<anyhow::Result<()>>,
|
||||
),
|
||||
NetworkDrive(String, tokio::sync::oneshot::Sender<anyhow::Result<()>>),
|
||||
NetworkScan(
|
||||
String,
|
||||
IconSizes,
|
||||
Option<PathBuf>,
|
||||
mpsc::Sender<Result<Vec<tab::Item>, String>>,
|
||||
),
|
||||
Unmount(MounterItem),
|
||||
|
|
@ -220,6 +241,7 @@ enum ItemKind {
|
|||
//TODO: better method of matching items
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Item {
|
||||
uri: String,
|
||||
kind: ItemKind,
|
||||
index: usize,
|
||||
name: String,
|
||||
|
|
@ -238,6 +260,10 @@ impl Item {
|
|||
self.is_mounted
|
||||
}
|
||||
|
||||
pub fn uri(&self) -> String {
|
||||
self.uri.clone()
|
||||
}
|
||||
|
||||
pub fn icon(&self, symbolic: bool) -> Option<widget::icon::Handle> {
|
||||
if symbolic {
|
||||
self.icon_symbolic_opt.as_ref()
|
||||
|
|
@ -318,9 +344,15 @@ impl Gvfs {
|
|||
Cmd::Rescan => {
|
||||
event_tx.send(Event::Items(items(&monitor, IconSizes::default()))).unwrap();
|
||||
}
|
||||
Cmd::Mount(mounter_item) => {
|
||||
let MounterItem::Gvfs(ref item) = mounter_item else { continue };
|
||||
let ItemKind::Volume = item.kind else { continue };
|
||||
Cmd::Mount(mounter_item, complete_tx) => {
|
||||
let MounterItem::Gvfs(ref item) = mounter_item else {
|
||||
_ = complete_tx.send(Err(anyhow::anyhow!("No mounter item")));
|
||||
continue
|
||||
};
|
||||
let ItemKind::Volume = item.kind else {
|
||||
_ = complete_tx.send(Err(anyhow::anyhow!("No mounter volume")));
|
||||
continue
|
||||
};
|
||||
for (i, volume) in monitor.volumes().into_iter().enumerate() {
|
||||
if i != item.index {
|
||||
continue;
|
||||
|
|
@ -345,17 +377,23 @@ impl Gvfs {
|
|||
move |res| {
|
||||
log::info!("mount {}: result {:?}", name, res);
|
||||
event_tx.send(Event::MountResult(mounter_item, match res {
|
||||
Ok(()) => Ok(true),
|
||||
Err(err) => match err.kind::<gio::IOErrorEnum>() {
|
||||
Ok(()) => {
|
||||
_ = complete_tx.send(Ok(()));
|
||||
Ok(true)
|
||||
},
|
||||
Err(err) => {
|
||||
_ = complete_tx.send(Err(anyhow::anyhow!("{err:?}")));
|
||||
match err.kind::<gio::IOErrorEnum>() {
|
||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||
_ => Err(format!("{}", err))
|
||||
}
|
||||
}}
|
||||
})).unwrap();
|
||||
},
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
Cmd::NetworkDrive(uri) => {
|
||||
Cmd::NetworkDrive(uri, result_tx) => {
|
||||
let file = gio::File::for_uri(&uri);
|
||||
let mount_op = mount_op(uri.clone(), event_tx.clone());
|
||||
let event_tx = event_tx.clone();
|
||||
|
|
@ -366,16 +404,20 @@ impl Gvfs {
|
|||
move |res| {
|
||||
log::info!("network drive {}: result {:?}", uri, res);
|
||||
event_tx.send(Event::NetworkResult(uri, match res {
|
||||
Ok(()) => Ok(true),
|
||||
Err(err) => match err.kind::<gio::IOErrorEnum>() {
|
||||
Ok(()) => {
|
||||
_ = result_tx.send(Ok(()));
|
||||
Ok(true)},
|
||||
Err(err) => {
|
||||
_ = result_tx.send(Err(anyhow::anyhow!("{err:?}")));
|
||||
match err.kind::<gio::IOErrorEnum>() {
|
||||
Some(gio::IOErrorEnum::FailedHandled) => Ok(false),
|
||||
_ => Err(format!("{}", err))
|
||||
}
|
||||
}}
|
||||
})).unwrap();
|
||||
}
|
||||
);
|
||||
}
|
||||
Cmd::NetworkScan(uri, sizes, items_tx) => {
|
||||
Cmd::NetworkScan(uri, sizes, path, items_tx) => {
|
||||
let file = gio::File::for_uri(&uri);
|
||||
let needs_mount = match file.find_enclosing_mount(gio::Cancellable::NONE) {
|
||||
Ok(_) => false,
|
||||
|
|
@ -468,9 +510,16 @@ impl Mounter for Gvfs {
|
|||
let command_tx = self.command_tx.clone();
|
||||
Task::perform(
|
||||
async move {
|
||||
command_tx.send(Cmd::Mount(item)).unwrap();
|
||||
let (res_tx, res_rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
command_tx.send(Cmd::Mount(item, res_tx)).unwrap();
|
||||
res_rx.await
|
||||
},
|
||||
|x| {
|
||||
if let Err(err) = x {
|
||||
log::error!("{err:?}");
|
||||
}
|
||||
},
|
||||
|x| x,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -478,16 +527,33 @@ impl Mounter for Gvfs {
|
|||
let command_tx = self.command_tx.clone();
|
||||
Task::perform(
|
||||
async move {
|
||||
command_tx.send(Cmd::NetworkDrive(uri)).unwrap();
|
||||
let (res_tx, res_rx) = tokio::sync::oneshot::channel();
|
||||
|
||||
command_tx.send(Cmd::NetworkDrive(uri, res_tx)).unwrap();
|
||||
res_rx.await
|
||||
},
|
||||
|x| {
|
||||
if let Err(err) = x {
|
||||
log::error!("{err:?}");
|
||||
}
|
||||
},
|
||||
|x| x,
|
||||
)
|
||||
}
|
||||
|
||||
fn network_scan(&self, uri: &str, sizes: IconSizes) -> Option<Result<Vec<tab::Item>, String>> {
|
||||
fn network_scan(
|
||||
&self,
|
||||
uri: &str,
|
||||
sizes: IconSizes,
|
||||
path: Option<&Path>,
|
||||
) -> Option<Result<Vec<tab::Item>, String>> {
|
||||
let (items_tx, mut items_rx) = mpsc::channel(1);
|
||||
self.command_tx
|
||||
.send(Cmd::NetworkScan(uri.to_string(), sizes, items_tx))
|
||||
.send(Cmd::NetworkScan(
|
||||
uri.to_string(),
|
||||
sizes,
|
||||
path.map(|p| p.to_owned()),
|
||||
items_tx,
|
||||
))
|
||||
.unwrap();
|
||||
items_rx.blocking_recv()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
use cosmic::{iced::Subscription, widget, Task};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::{collections::BTreeMap, fmt, path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fmt,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use tokio::sync::mpsc;
|
||||
|
||||
use crate::{config::IconSizes, tab};
|
||||
|
|
@ -55,6 +60,14 @@ impl MounterItem {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn uri(&self) -> String {
|
||||
match self {
|
||||
#[cfg(feature = "gvfs")]
|
||||
Self::Gvfs(item) => item.uri(),
|
||||
Self::None => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mounted(&self) -> bool {
|
||||
match self {
|
||||
#[cfg(feature = "gvfs")]
|
||||
|
|
@ -95,7 +108,12 @@ pub trait Mounter: Send + Sync {
|
|||
//TODO: send result
|
||||
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 network_scan(
|
||||
&self,
|
||||
uri: &str,
|
||||
sizes: IconSizes,
|
||||
path: Option<&Path>,
|
||||
) -> Option<Result<Vec<tab::Item>, String>>;
|
||||
fn unmount(&self, item: MounterItem) -> Task<()>;
|
||||
fn subscription(&self) -> Subscription<MounterMessage>;
|
||||
}
|
||||
|
|
|
|||
20
src/tab.rs
20
src/tab.rs
|
|
@ -1205,9 +1205,9 @@ pub fn scan_recents(sizes: IconSizes) -> Vec<Item> {
|
|||
recents.into_iter().take(50).map(|(item, _)| item).collect()
|
||||
}
|
||||
|
||||
pub fn scan_network(uri: &str, sizes: IconSizes) -> Vec<Item> {
|
||||
pub fn scan_network(uri: &str, sizes: IconSizes, path: Option<&Path>) -> Vec<Item> {
|
||||
for (_key, mounter) in MOUNTERS.iter() {
|
||||
match mounter.network_scan(uri, sizes) {
|
||||
match mounter.network_scan(uri, sizes, path) {
|
||||
Some(Ok(items)) => return items,
|
||||
Some(Err(err)) => {
|
||||
log::warn!("failed to scan {:?}: {}", uri, err);
|
||||
|
|
@ -1361,7 +1361,7 @@ impl From<Location> for EditLocation {
|
|||
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
|
||||
pub enum Location {
|
||||
Desktop(PathBuf, String, DesktopConfig),
|
||||
Network(String, String),
|
||||
Network(String, String, Option<PathBuf>),
|
||||
Path(PathBuf),
|
||||
Recents,
|
||||
Search(PathBuf, String, bool, Instant),
|
||||
|
|
@ -1413,6 +1413,7 @@ impl Location {
|
|||
Self::Desktop(path, ..) => Some(path),
|
||||
Self::Path(path) => Some(path),
|
||||
Self::Search(path, ..) => Some(path),
|
||||
Self::Network(_, _, path) => path.as_ref(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
|
@ -1426,6 +1427,8 @@ impl Location {
|
|||
Self::Search(_, term, show_hidden, time) => {
|
||||
Self::Search(path, term.clone(), *show_hidden, *time)
|
||||
}
|
||||
Self::Network(id, name, path) => Self::Network(id.clone(), name.clone(), path.clone()),
|
||||
|
||||
other => other.clone(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1442,7 +1445,7 @@ impl Location {
|
|||
}
|
||||
Self::Trash => scan_trash(sizes),
|
||||
Self::Recents => scan_recents(sizes),
|
||||
Self::Network(uri, _) => scan_network(uri, sizes),
|
||||
Self::Network(uri, _, path) => scan_network(uri, sizes, path.as_deref()),
|
||||
};
|
||||
let parent_item_opt = match self.path_opt() {
|
||||
Some(path) => match item_from_path(path, sizes) {
|
||||
|
|
@ -1479,7 +1482,7 @@ impl Location {
|
|||
Self::Recents => {
|
||||
fl!("recents")
|
||||
}
|
||||
Self::Network(_uri, display_name) => display_name.clone(),
|
||||
Self::Network(display_name, ..) => display_name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3033,6 +3036,7 @@ impl Tab {
|
|||
}
|
||||
if !item.selected {
|
||||
self.clicked = click_i_opt;
|
||||
dbg!(&item.location_opt);
|
||||
item.selected = true;
|
||||
}
|
||||
self.select_range = Some((i, i));
|
||||
|
|
@ -3133,6 +3137,7 @@ impl Tab {
|
|||
}
|
||||
LocationMenuAction::AddToSidebar(ancestor_index) => {
|
||||
if let Some(path) = path_for_index(ancestor_index) {
|
||||
dbg!(&path);
|
||||
commands.push(Command::AddToSidebar(path));
|
||||
} else {
|
||||
log::warn!(
|
||||
|
|
@ -4512,13 +4517,14 @@ impl Tab {
|
|||
.into(),
|
||||
);
|
||||
}
|
||||
Location::Network(uri, display_name) => {
|
||||
Location::Network(uri, display_name, path) => {
|
||||
children.push(
|
||||
widget::button::custom(widget::text::heading(display_name))
|
||||
.padding(space_xxxs)
|
||||
.on_press(Message::Location(Location::Network(
|
||||
uri.clone(),
|
||||
display_name.clone(),
|
||||
path.clone(),
|
||||
)))
|
||||
.class(theme::Button::Text)
|
||||
.into(),
|
||||
|
|
@ -5421,7 +5427,7 @@ impl Tab {
|
|||
}
|
||||
}
|
||||
}
|
||||
Location::Network(uri, _display_name) if uri == "network:///" => {
|
||||
Location::Network(uri, _display_name, path) if uri == "network:///" => {
|
||||
tab_column = tab_column.push(
|
||||
widget::layer_container(widget::row::with_children(vec![
|
||||
widget::horizontal_space().into(),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue