diff --git a/src/handler.rs b/src/handler.rs index 7ffc0f1..e71fe21 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -6,6 +6,7 @@ use crate::config::Config; use crate::device::Device; use crate::event::Event; use crate::mode::ap::APFocusedSection; +use crate::mode::station::KnownNetworkSelection; use crate::mode::station::share::Share; use crate::mode::station::speed_test::SpeedTest; use crate::nm::{Mode, SecurityType}; @@ -65,57 +66,30 @@ pub async fn toggle_connect(app: &mut App, sender: UnboundedSender) -> Re } } } - FocusedBlock::KnownNetworks => match &station.connected_network { - Some(connected_net) => { - if let Some(selected_net_index) = station.known_networks_state.selected() { - if selected_net_index > station.known_networks.len() - 1 { - // Can not connect to unavailble network - return Ok(()); - } - - let (selected_net, _signal) = &station.known_networks[selected_net_index]; - - if selected_net.name == connected_net.name { + FocusedBlock::KnownNetworks => { + if let Some(KnownNetworkSelection::Network(data_index)) = + station.resolve_known_selection() + { + let (selected_net, _) = &station.known_networks[data_index]; + + let is_connected = station + .connected_network + .as_ref() + .is_some_and(|c| c.name == selected_net.name); + + if is_connected { + station.disconnect(sender.clone()).await?; + } else { + let (net, _) = station.known_networks[data_index].clone(); + if station.connected_network.is_some() { station.disconnect(sender.clone()).await?; - } else { - let net_index = station - .known_networks - .iter() - .position(|(n, _s)| n.name == selected_net.name); - - if let Some(index) = net_index { - let (net, _) = station.known_networks[index].clone(); - station.disconnect(sender.clone()).await?; - tokio::spawn(async move { - // Known networks already have saved credentials - let _ = net.connect(sender.clone(), None).await; - }); - } - } - } - } - None => { - if let Some(selected_net_index) = station.known_networks_state.selected() { - if selected_net_index > station.known_networks.len() - 1 { - // Can not connect to unavailble network - return Ok(()); - } - let (selected_net, _signal) = &station.known_networks[selected_net_index]; - let net_index = station - .known_networks - .iter() - .position(|(n, _s)| n.name == selected_net.name); - - if let Some(index) = net_index { - let (net, _) = station.known_networks[index].clone(); - tokio::spawn(async move { - // Known networks already have saved credentials - let _ = net.connect(sender.clone(), None).await; - }); } + tokio::spawn(async move { + let _ = net.connect(sender.clone(), None).await; + }); } } - }, + } _ => {} } } @@ -561,16 +535,10 @@ pub async fn handle_key_events( KeyCode::Char(c) if c == config.station.known_network.share => { - if let Some(net_index) = - station.known_networks_state.selected() - { - if net_index > station.known_networks.len() - 1 { - let index = net_index.saturating_sub( - station.known_networks.len(), - ); + match station.resolve_known_selection() { + Some(KnownNetworkSelection::Unavailable(index)) => { let network = &station.unavailable_known_networks[index]; - // Check if it's a PSK network (WPA/WPA2/WPA3) if matches!( network.network_type, SecurityType::WPA @@ -587,10 +555,12 @@ pub async fn handle_key_events( app.focused_block = FocusedBlock::ShareNetwork; } - } else { + } + Some(KnownNetworkSelection::Network( + data_index, + )) => { let (network, _) = - &station.known_networks[net_index]; - // Check if it's a PSK network (WPA/WPA2/WPA3) + &station.known_networks[data_index]; if matches!( network.network_type, SecurityType::WPA @@ -609,30 +579,29 @@ pub async fn handle_key_events( FocusedBlock::ShareNetwork; } } + _ => {} } } // Remove a known network KeyCode::Char(c) if c == config.station.known_network.remove => { - if let Some(net_index) = - station.known_networks_state.selected() - { - if net_index > station.known_networks.len() - 1 { - let index = net_index.saturating_sub( - station.known_networks.len(), - ); + match station.resolve_known_selection() { + Some(KnownNetworkSelection::Unavailable(index)) => { let network = &station.unavailable_known_networks[index]; network.forget(sender.clone()).await?; - } else { - let (net, _signal) = - &station.known_networks[net_index]; - + } + Some(KnownNetworkSelection::Network( + data_index, + )) => { + let (net, _) = + &station.known_networks[data_index]; if let Some(known_net) = &net.known_network { known_net.forget(sender.clone()).await?; } } + _ => {} } } @@ -643,13 +612,12 @@ pub async fn handle_key_events( .known_network .toggle_autoconnect => { - if let Some(net_index) = - station.known_networks_state.selected() - && net_index < station.known_networks.len() + if let Some(KnownNetworkSelection::Network( + data_index, + )) = station.resolve_known_selection() { let (net, _) = - &mut station.known_networks[net_index]; - + &mut station.known_networks[data_index]; if let Some(known_net) = &mut net.known_network { known_net .toggle_autoconnect(sender.clone()) @@ -702,38 +670,24 @@ pub async fn handle_key_events( // Scroll down KeyCode::Char('j') | KeyCode::Down => { - if !station.known_networks.is_empty() { + let total = station.known_networks_total_rows(); + if total > 0 { let i = match station.known_networks_state.selected() { - Some(i) => { - let limit = if station - .show_unavailable_known_networks - { - station.known_networks.len() - + station - .unavailable_known_networks - .len() - - 1 - } else { - station.known_networks.len() - 1 - }; - - if i < limit { i + 1 } else { i } - } + Some(i) => (i + 1).min(total - 1), None => 0, }; - station.known_networks_state.select(Some(i)); } } KeyCode::Char('k') | KeyCode::Up => { - if !station.known_networks.is_empty() { + let total = station.known_networks_total_rows(); + if total > 0 { let i = match station.known_networks_state.selected() { Some(i) => i.saturating_sub(1), None => 0, }; - station.known_networks_state.select(Some(i)); } } diff --git a/src/mode/station.rs b/src/mode/station.rs index c585ced..fda9fb1 100644 --- a/src/mode/station.rs +++ b/src/mode/station.rs @@ -28,6 +28,17 @@ use crate::{ use network::Network; +/// Result of resolving the selected known networks table index, +/// accounting for the ethernet row offset. +pub enum KnownNetworkSelection { + /// The ethernet row is selected (no-op for most actions) + Ethernet, + /// A known (visible) network at the given data index + Network(usize), + /// An unavailable (saved but not visible) network at the given index + Unavailable(usize), +} + /// Hidden network representation for NetworkManager #[derive(Debug, Clone)] pub struct HiddenNetwork { @@ -366,6 +377,40 @@ impl Station { Ok(()) } + /// Resolve the currently selected known networks table index to a typed selection, + /// accounting for the ethernet row offset and unavailable networks. + pub fn resolve_known_selection(&self) -> Option { + let selected = self.known_networks_state.selected()?; + let ethernet_offset = usize::from(self.is_ethernet_connected); + + if selected < ethernet_offset { + return Some(KnownNetworkSelection::Ethernet); + } + + let data_index = selected - ethernet_offset; + if data_index < self.known_networks.len() { + Some(KnownNetworkSelection::Network(data_index)) + } else { + let unavail_index = data_index - self.known_networks.len(); + if unavail_index < self.unavailable_known_networks.len() { + Some(KnownNetworkSelection::Unavailable(unavail_index)) + } else { + None + } + } + } + + /// Total number of rows in the known networks table (ethernet + known + unavailable). + pub fn known_networks_total_rows(&self) -> usize { + let ethernet_offset = usize::from(self.is_ethernet_connected); + let unavail = if self.show_unavailable_known_networks { + self.unavailable_known_networks.len() + } else { + 0 + }; + ethernet_offset + self.known_networks.len() + unavail + } + pub fn render( &mut self, frame: &mut Frame,