diff --git a/Cargo.lock b/Cargo.lock index 4458e67..acb6d6e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1482,6 +1482,7 @@ dependencies = [ name = "cosmic-files" version = "0.1.0" dependencies = [ + "anyhow", "bzip2", "chrono", "compio", diff --git a/Cargo.toml b/Cargo.toml index 23415b6..8d5c446 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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", diff --git a/src/app.rs b/src/app.rs index eafdfe3..ffa0651 100644 --- a/src/app.rs +++ b/src/app.rs @@ -334,6 +334,12 @@ pub enum Message { NavMenuAction(NavMenuAction), NetworkAuth(MounterKey, String, MounterAuth, mpsc::Sender), NetworkDriveInput(String), + NetworkDriveOpenEntityAfterMount { + entity: Entity, + }, + NetworkDriveOpenTabAfterMount { + location: Location, + }, NetworkDriveSubmit, NetworkResult(MounterKey, String, Result), NewItem(Option, bool), @@ -885,6 +891,58 @@ impl App { activate: bool, selection_paths: Option>, ) -> (Entity, Task) { + #[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>, ) -> Task { + 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.nav_model.activate(entity); if let Some(location) = self.nav_model.data::(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::(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::(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::(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::(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(); diff --git a/src/config.rs b/src/config.rs index 9b02dc3..14dc2d0 100644 --- a/src/config.rs +++ b/src/config.rs @@ -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()), } } } diff --git a/src/lib.rs b/src/lib.rs index f9749f7..6fe589f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -107,7 +107,7 @@ pub fn main() -> Result<(), Box> { } 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) { diff --git a/src/menu.rs b/src/menu.rs index c219c61..fa550a1 100644 --- a/src/menu.rs +++ b/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()); diff --git a/src/mounter/gvfs.rs b/src/mounter/gvfs.rs index a728ff6..37603a3 100644 --- a/src/mounter/gvfs.rs +++ b/src/mounter/gvfs.rs @@ -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, String> { +fn network_scan( + uri: &str, + sizes: IconSizes, + path: Option<&Path>, +) -> Result, 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, 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) -> gio::MountOp enum Cmd { Items(IconSizes, mpsc::Sender), Rescan, - Mount(MounterItem), - NetworkDrive(String), + Mount( + MounterItem, + tokio::sync::oneshot::Sender>, + ), + NetworkDrive(String, tokio::sync::oneshot::Sender>), NetworkScan( String, IconSizes, + Option, mpsc::Sender, 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 { 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::() { + Ok(()) => { + _ = complete_tx.send(Ok(())); + Ok(true) + }, + Err(err) => { + _ = complete_tx.send(Err(anyhow::anyhow!("{err:?}"))); + match err.kind::() { 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::() { + Ok(()) => { + _ = result_tx.send(Ok(())); + Ok(true)}, + Err(err) => { + _ = result_tx.send(Err(anyhow::anyhow!("{err:?}"))); + match err.kind::() { 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, String>> { + fn network_scan( + &self, + uri: &str, + sizes: IconSizes, + path: Option<&Path>, + ) -> Option, 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() } diff --git a/src/mounter/mod.rs b/src/mounter/mod.rs index 8b13392..eb0d89d 100644 --- a/src/mounter/mod.rs +++ b/src/mounter/mod.rs @@ -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, String>>; + fn network_scan( + &self, + uri: &str, + sizes: IconSizes, + path: Option<&Path>, + ) -> Option, String>>; fn unmount(&self, item: MounterItem) -> Task<()>; fn subscription(&self) -> Subscription; } diff --git a/src/tab.rs b/src/tab.rs index 48bac2d..af8a49b 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -1205,9 +1205,9 @@ pub fn scan_recents(sizes: IconSizes) -> Vec { recents.into_iter().take(50).map(|(item, _)| item).collect() } -pub fn scan_network(uri: &str, sizes: IconSizes) -> Vec { +pub fn scan_network(uri: &str, sizes: IconSizes, path: Option<&Path>) -> Vec { 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 for EditLocation { #[derive(Clone, Debug, Eq, Hash, PartialEq)] pub enum Location { Desktop(PathBuf, String, DesktopConfig), - Network(String, String), + Network(String, String, Option), 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(),