Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion nmrs-core/src/dbus.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::models::{Device, DeviceState, DeviceType, Network};
use crate::models::{Device, DeviceState, DeviceType, Network, NetworkInfo};
use crate::wifi_builders::build_wifi_connection;
use futures_timer::Delay;
use std::collections::HashMap;
Expand Down Expand Up @@ -415,4 +415,51 @@ impl NetworkManager {
}
None
}

pub async fn show_details(&self, ssid: &str) -> zbus::Result<NetworkInfo> {
let nm = NMProxy::new(&self.conn).await?;
for dp in nm.get_devices().await? {
let dev = NMDeviceProxy::builder(&self.conn)
.path(dp.clone())?
.build()
.await?;
if dev.device_type().await? != 2 {
continue;
}

let wifi = NMWirelessProxy::builder(&self.conn)
.path(dp.clone())?
.build()
.await?;

for ap_path in wifi.get_all_access_points().await? {
let ap = NMAccessPointProxy::builder(&self.conn)
.path(ap_path.clone())?
.build()
.await?;

let ssid_bytes = ap.ssid().await?;
if std::str::from_utf8(&ssid_bytes).unwrap_or("") == ssid {
let strength = ap.strength().await?;
let bssid = ap.hw_address().await?;
let wpa = ap.wpa_flags().await?;
let rsn = ap.rsn_flags().await?;
let security = if wpa != 0 || rsn != 0 {
"secured"
} else {
"open"
};
return Ok(NetworkInfo {
ssid: ssid.to_string(),
bssid,
strength,
freq: None,
security: security.to_string(),
});
}
}
}

Err(zbus::Error::Failure("Network not found".into()))
}
}
12 changes: 10 additions & 2 deletions nmrs-core/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ pub struct Network {
pub is_eap: bool,
}

pub struct NetworkInfo {
pub ssid: String,
pub bssid: String,
pub strength: u8,
pub freq: Option<u32>,
pub security: String,
}

#[derive(Debug, Clone)]
pub struct Device {
pub path: String,
Expand Down Expand Up @@ -113,7 +121,7 @@ impl Display for DeviceType {
DeviceType::Wifi => write!(f, "Wi-Fi"),
DeviceType::WifiP2P => write!(f, "Wi-Fi P2P"),
DeviceType::Loopback => write!(f, "Loopback"),
DeviceType::Other(v) => write!(f, "Other({})", v),
DeviceType::Other(v) => write!(f, "Other({v})"),
}
}
}
Expand All @@ -127,7 +135,7 @@ impl Display for DeviceState {
DeviceState::Prepare => write!(f, "Preparing"),
DeviceState::Config => write!(f, "Configuring"),
DeviceState::Activated => write!(f, "Activated"),
DeviceState::Other(v) => write!(f, "Other({})", v),
DeviceState::Other(v) => write!(f, "Other({v})"),
}
}
}
Expand Down
74 changes: 74 additions & 0 deletions nmrs-ui/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,77 @@ headerbar label:last-child {
opacity: 0.7;
margin-top: 4px;
}

.network-arrow:hover {
opacity: 1.0;
transform: scale(1.1);
transition: transform 120ms ease, opacity 120ms ease;
}

.network-details label {
font-size: 14px;
color: #e0e0e0;
}

.network-title {
font-size: 16px;
font-weight: bold;
margin-bottom: 8px;
}

.network-page {
background: #1e1e1e;
border-radius: 12px;
border: 1px solid #3a3a3a;
padding: 20px;
}

.network-title {
font-size: 18px;
font-weight: bold;
color: #ffffff;
}

.network-detail {
font-size: 14px;
color: #cccccc;
margin-left: 6px;
}

.disconnect-btn {
background: #3a7bd5;
color: #ffffff;
border-radius: 8px;
padding: 6px 14px;
}

.forget-btn {
background: #d54d3a;
color: #ffffff;
border-radius: 8px;
padding: 6px 14px;
}

.network-dialog {
background: #1a1a1a;
border: none;
box-shadow: none;
}

.network-page {
border-radius: 12px;
background: #1a1a1a;
padding: 20px;
}

button.disconnect-btn {
background: #2563eb;
color: white;
border-radius: 8px;
}

button.forget-btn {
background: #b91c1c;
color: white;
border-radius: 8px;
}
1 change: 1 addition & 0 deletions nmrs-ui/src/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
pub mod connect;
pub mod header;
pub mod network_page;
pub mod networks;

use gtk::prelude::*;
Expand Down
43 changes: 43 additions & 0 deletions nmrs-ui/src/ui/network_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use gtk::prelude::*;
use gtk::{Box, Button, Image, Label, Orientation};
use nmrs_core::models::NetworkInfo;
use relm4::RelmWidgetExt;

pub fn network_page(info: &NetworkInfo) -> Box {
let container = Box::new(Orientation::Vertical, 16);
container.add_css_class("network-page");
container.set_margin_all(20);

let header = Box::new(Orientation::Horizontal, 8);
let icon = Image::from_icon_name("network-wireless-signal-excellent-symbolic");
icon.set_pixel_size(32);
let title = Label::new(Some(&info.ssid));
title.add_css_class("network-title");
header.append(&icon);
header.append(&title);
container.append(&header);

let details = Box::new(Orientation::Vertical, 4);
let bssid = Label::new(Some(&format!("BSSID: {}", info.bssid)));
let strength = Label::new(Some(&format!("Signal: {}%", info.strength)));
let security = Label::new(Some(&format!("Security: {}", info.security)));
details.append(&bssid);
details.append(&strength);
details.append(&security);
container.append(&details);

let actions = Box::new(Orientation::Horizontal, 12);
actions.set_halign(gtk::Align::End);

let disconnect = Button::with_label("Disconnect");
disconnect.add_css_class("disconnect-btn");
actions.append(&disconnect);

let forget = Button::with_label("Forget");
forget.add_css_class("forget-btn");
actions.append(&forget);

container.append(&actions);

container
}
44 changes: 43 additions & 1 deletion nmrs-ui/src/ui/networks.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use glib::clone;
use gtk::Align;
use gtk::GestureClick;
use gtk::gdk;
use gtk::prelude::*;
use gtk::{Box, Image, Label, ListBox, ListBoxRow, Orientation};
use nmrs_core::models::WifiSecurity;
use nmrs_core::{NetworkManager, models};

use crate::ui::connect;
use crate::ui::network_page::network_page;

pub fn networks_view(
networks: &[models::Network],
Expand Down Expand Up @@ -59,7 +61,46 @@ pub fn networks_view(
let arrow = Image::from_icon_name("go-next-symbolic");
arrow.set_halign(Align::End);
arrow.add_css_class("network-arrow");
hbox.append(&arrow);

let arrow_click = GestureClick::new();
arrow.set_cursor_from_name(Some("pointer"));
let ssid_clone = net.ssid.clone();
arrow_click.connect_pressed(clone!(
#[weak]
parent_window,
move |_, _, _, _| {
let ssid_clone = ssid_clone.clone();
glib::MainContext::default().spawn_local(async move {
if let Ok(nm) = NetworkManager::new().await
&& let Ok(details) = nm.show_details(&ssid_clone).await
{
let page = network_page(&details);
let dialog = gtk::Window::builder()
.title(&details.ssid)
.child(&page)
.transient_for(&parent_window)
.default_width(300)
.default_height(200)
.build();

let key = gtk::EventControllerKey::new();
{
let dialog = dialog.clone();
key.connect_key_pressed(move |_, key, _, _| {
if key == gdk::Key::Escape {
dialog.close();
return glib::Propagation::Stop;
}
glib::Propagation::Proceed
});
}
dialog.add_controller(key);
dialog.present();
}
});
}
));
arrow.add_controller(arrow_click);

let ssid_str = net.ssid.clone();
let secured = net.secured;
Expand Down Expand Up @@ -93,6 +134,7 @@ pub fn networks_view(

row.add_controller(gesture);

hbox.append(&arrow);
row.set_child(Some(&hbox));
list.append(&row);
}
Expand Down