feat(networking): display list of devices on page
This commit is contained in:
parent
2c07dd8bef
commit
c6cd78ec9c
9 changed files with 356 additions and 91 deletions
|
|
@ -447,6 +447,12 @@ impl cosmic::Application for SettingsApp {
|
||||||
return self.activate_page(page);
|
return self.activate_page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
crate::pages::Message::Networking(message) => {
|
||||||
|
if let Some(page) = self.pages.page_mut::<networking::Page>() {
|
||||||
|
return page.update(message).map(Into::into);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
crate::pages::Message::Panel(message) => {
|
crate::pages::Message::Panel(message) => {
|
||||||
if let Some(page) = self.pages.page_mut::<panel::Page>() {
|
if let Some(page) = self.pages.page_mut::<panel::Page>() {
|
||||||
return page.update(message).map(Into::into);
|
return page.update(message).map(Into::into);
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub enum Message {
|
||||||
ManageWindowShortcuts(input::keyboard::shortcuts::ShortcutMessage),
|
ManageWindowShortcuts(input::keyboard::shortcuts::ShortcutMessage),
|
||||||
MoveWindowShortcuts(input::keyboard::shortcuts::ShortcutMessage),
|
MoveWindowShortcuts(input::keyboard::shortcuts::ShortcutMessage),
|
||||||
NavShortcuts(input::keyboard::shortcuts::ShortcutMessage),
|
NavShortcuts(input::keyboard::shortcuts::ShortcutMessage),
|
||||||
|
Networking(networking::Message),
|
||||||
Page(Entity),
|
Page(Entity),
|
||||||
Panel(desktop::panel::Message),
|
Panel(desktop::panel::Message),
|
||||||
PanelApplet(desktop::panel::applets_inner::Message),
|
PanelApplet(desktop::panel::applets_inner::Message),
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,67 @@ pub mod vpn;
|
||||||
pub mod wifi;
|
pub mod wifi;
|
||||||
pub mod wired;
|
pub mod wired;
|
||||||
|
|
||||||
use std::{ffi::OsStr, io, process::ExitStatus};
|
use std::{ffi::OsStr, io, process::ExitStatus, sync::Arc};
|
||||||
|
|
||||||
use cosmic_settings_page as page;
|
use anyhow::Context;
|
||||||
|
use cosmic::{widget, Apply, Command, Element};
|
||||||
|
use cosmic_dbus_networkmanager::{
|
||||||
|
interface::enums::{DeviceState, DeviceType},
|
||||||
|
nm::NetworkManager,
|
||||||
|
};
|
||||||
|
use cosmic_settings_page::{self as page, section, Section};
|
||||||
|
use cosmic_settings_subscriptions::network_manager;
|
||||||
|
use futures::{SinkExt, StreamExt};
|
||||||
|
use slotmap::SlotMap;
|
||||||
|
|
||||||
static NM_CONNECTION_EDITOR: &str = "nm-connection-editor";
|
static NM_CONNECTION_EDITOR: &str = "nm-connection-editor";
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Page;
|
pub struct Page {
|
||||||
|
nm_task: Option<tokio::sync::oneshot::Sender<()>>,
|
||||||
|
devices: Vec<Arc<network_manager::devices::DeviceInfo>>,
|
||||||
|
vpn: page::Entity,
|
||||||
|
wifi: page::Entity,
|
||||||
|
wired: page::Entity,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum Message {
|
||||||
|
/// An error occurred.
|
||||||
|
Error(String),
|
||||||
|
/// Successfully connected to the system dbus.
|
||||||
|
NetworkManagerConnect(
|
||||||
|
(
|
||||||
|
zbus::Connection,
|
||||||
|
tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
/// Open the wifi settings page with the selected device.
|
||||||
|
OpenPage {
|
||||||
|
page: page::Entity,
|
||||||
|
device: Option<DeviceVariant>,
|
||||||
|
},
|
||||||
|
/// Update the devices lists
|
||||||
|
UpdateDevices(Vec<Arc<network_manager::devices::DeviceInfo>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum DeviceVariant {
|
||||||
|
Wired(Arc<network_manager::devices::DeviceInfo>),
|
||||||
|
WiFi(Arc<network_manager::devices::DeviceInfo>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Message> for crate::app::Message {
|
||||||
|
fn from(message: Message) -> Self {
|
||||||
|
crate::pages::Message::Networking(message).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Message> for crate::pages::Message {
|
||||||
|
fn from(message: Message) -> Self {
|
||||||
|
crate::pages::Message::Networking(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl page::Page<crate::pages::Message> for Page {
|
impl page::Page<crate::pages::Message> for Page {
|
||||||
fn info(&self) -> cosmic_settings_page::Info {
|
fn info(&self) -> cosmic_settings_page::Info {
|
||||||
|
|
@ -22,15 +75,248 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
)
|
)
|
||||||
.title(fl!("network-and-wireless"))
|
.title(fl!("network-and-wireless"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn content(
|
||||||
|
&self,
|
||||||
|
sections: &mut SlotMap<section::Entity, Section<crate::pages::Message>>,
|
||||||
|
) -> Option<page::Content> {
|
||||||
|
crate::slab!(descriptions {
|
||||||
|
vpn_txt = fl!("connections-and-profiles", variant = "vpn");
|
||||||
|
});
|
||||||
|
|
||||||
|
let device_list = Section::default().descriptions(descriptions).view::<Self>(
|
||||||
|
move |_binder, page, section| {
|
||||||
|
let descs = §ion.descriptions;
|
||||||
|
|
||||||
|
let wifi_devices = page
|
||||||
|
.devices
|
||||||
|
.iter()
|
||||||
|
.filter(|device| device.device_type == DeviceType::Wifi)
|
||||||
|
.map(|device| {
|
||||||
|
crate::widget::page_list_item(
|
||||||
|
fl!("wifi", "adapter", id = device.interface.as_str()),
|
||||||
|
match device.state {
|
||||||
|
DeviceState::Activated => fl!("network-device-state", "activated"),
|
||||||
|
DeviceState::Config => fl!("network-device-state", "config"),
|
||||||
|
DeviceState::Deactivating => {
|
||||||
|
fl!("network-device-state", "deactivating")
|
||||||
|
}
|
||||||
|
DeviceState::Disconnected => {
|
||||||
|
fl!("network-device-state", "disconnected")
|
||||||
|
}
|
||||||
|
DeviceState::Failed => fl!("network-device-state", "failed"),
|
||||||
|
DeviceState::IpCheck => fl!("network-device-state", "ip-check"),
|
||||||
|
DeviceState::IpConfig => fl!("network-device-state", "ip-config"),
|
||||||
|
DeviceState::NeedAuth => fl!("network-device-state", "need-auth"),
|
||||||
|
DeviceState::Prepare => fl!("network-device-state", "prepare"),
|
||||||
|
DeviceState::Secondaries => {
|
||||||
|
fl!("network-device-state", "secondaries")
|
||||||
|
}
|
||||||
|
DeviceState::Unavailable => {
|
||||||
|
fl!("network-device-state", "unavailable")
|
||||||
|
}
|
||||||
|
DeviceState::Unknown => fl!("network-device-state", "unknown"),
|
||||||
|
DeviceState::Unmanaged => fl!("network-device-state", "unmanaged"),
|
||||||
|
},
|
||||||
|
"preferences-wireless-symbolic",
|
||||||
|
Message::OpenPage {
|
||||||
|
page: page.wifi,
|
||||||
|
device: Some(DeviceVariant::WiFi(device.clone())),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let wired_devices = page
|
||||||
|
.devices
|
||||||
|
.iter()
|
||||||
|
.filter(|device| device.device_type == DeviceType::Ethernet)
|
||||||
|
.map(|device| {
|
||||||
|
crate::widget::page_list_item(
|
||||||
|
fl!("wired", "adapter", id = device.interface.as_str()),
|
||||||
|
match device.state {
|
||||||
|
DeviceState::Activated => fl!("network-device-state", "activated"),
|
||||||
|
DeviceState::Config => fl!("network-device-state", "config"),
|
||||||
|
DeviceState::Deactivating => {
|
||||||
|
fl!("network-device-state", "deactivating")
|
||||||
|
}
|
||||||
|
DeviceState::Disconnected => {
|
||||||
|
fl!("network-device-state", "disconnected")
|
||||||
|
}
|
||||||
|
DeviceState::Failed => fl!("network-device-state", "failed"),
|
||||||
|
DeviceState::IpCheck => fl!("network-device-state", "ip-check"),
|
||||||
|
DeviceState::IpConfig => fl!("network-device-state", "ip-config"),
|
||||||
|
DeviceState::NeedAuth => fl!("network-device-state", "need-auth"),
|
||||||
|
DeviceState::Prepare => fl!("network-device-state", "prepare"),
|
||||||
|
DeviceState::Secondaries => {
|
||||||
|
fl!("network-device-state", "secondaries")
|
||||||
|
}
|
||||||
|
DeviceState::Unavailable => {
|
||||||
|
fl!("network-device-state", "unplugged")
|
||||||
|
}
|
||||||
|
DeviceState::Unknown => fl!("network-device-state", "unknown"),
|
||||||
|
DeviceState::Unmanaged => fl!("network-device-state", "unmanaged"),
|
||||||
|
},
|
||||||
|
"preferences-wired-symbolic",
|
||||||
|
Message::OpenPage {
|
||||||
|
page: page.wired,
|
||||||
|
device: Some(DeviceVariant::Wired(device.clone())),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
let device_list = wifi_devices
|
||||||
|
.chain(wired_devices)
|
||||||
|
.fold(widget::column(), |column, device| column.push(device))
|
||||||
|
.push(crate::widget::page_list_item(
|
||||||
|
fl!("vpn"),
|
||||||
|
&descs[vpn_txt],
|
||||||
|
"preferences-vpn-symbolic",
|
||||||
|
Message::OpenPage {
|
||||||
|
page: page.vpn,
|
||||||
|
device: None,
|
||||||
|
},
|
||||||
|
))
|
||||||
|
.spacing(cosmic::theme::active().cosmic().spacing.space_s);
|
||||||
|
|
||||||
|
Element::from(device_list).map(crate::pages::Message::Networking)
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
Some(vec![sections.insert(device_list)])
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_enter(
|
||||||
|
&mut self,
|
||||||
|
_page: page::Entity,
|
||||||
|
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
|
) -> cosmic::Command<crate::pages::Message> {
|
||||||
|
if self.nm_task.is_none() {
|
||||||
|
return cosmic::command::future(async move {
|
||||||
|
zbus::Connection::system()
|
||||||
|
.await
|
||||||
|
.context("failed to create system dbus connection")
|
||||||
|
.map_or_else(
|
||||||
|
|why| Message::Error(why.to_string()),
|
||||||
|
|conn| Message::NetworkManagerConnect((conn, sender.clone())),
|
||||||
|
)
|
||||||
|
.apply(crate::pages::Message::Networking)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_leave(&mut self) -> Command<crate::pages::Message> {
|
||||||
|
self.devices = Vec::new();
|
||||||
|
|
||||||
|
if let Some(cancel) = self.nm_task.take() {
|
||||||
|
_ = cancel.send(());
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl page::AutoBind<crate::pages::Message> for Page {
|
impl page::AutoBind<crate::pages::Message> for Page {
|
||||||
fn sub_pages(
|
fn sub_pages(
|
||||||
page: cosmic_settings_page::Insert<crate::pages::Message>,
|
mut page: cosmic_settings_page::Insert<crate::pages::Message>,
|
||||||
) -> cosmic_settings_page::Insert<crate::pages::Message> {
|
) -> cosmic_settings_page::Insert<crate::pages::Message> {
|
||||||
page.sub_page::<wired::Page>()
|
let vpn = page.sub_page_with_id::<vpn::Page>();
|
||||||
.sub_page::<wifi::Page>()
|
let wifi = page.sub_page_with_id::<wifi::Page>();
|
||||||
.sub_page::<vpn::Page>()
|
let wired = page.sub_page_with_id::<wired::Page>();
|
||||||
|
|
||||||
|
let model = page.model.page_mut::<Self>().unwrap();
|
||||||
|
model.vpn = vpn;
|
||||||
|
model.wifi = wifi;
|
||||||
|
model.wired = wired;
|
||||||
|
|
||||||
|
page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Page {
|
||||||
|
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
||||||
|
let span = tracing::span!(tracing::Level::INFO, "networking::update");
|
||||||
|
let _span = span.enter();
|
||||||
|
|
||||||
|
match message {
|
||||||
|
Message::NetworkManagerConnect((conn, output)) => {
|
||||||
|
self.connect(conn.clone(), output);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::Error(why) => {
|
||||||
|
tracing::error!(why);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::OpenPage { page, device } => {
|
||||||
|
let mut commands = Vec::<Command<crate::app::Message>>::new();
|
||||||
|
|
||||||
|
commands.push(cosmic::command::message(crate::app::Message::Page(page)));
|
||||||
|
|
||||||
|
if let Some(device) = device {
|
||||||
|
commands.push(cosmic::command::message(crate::app::Message::PageMessage(
|
||||||
|
match device {
|
||||||
|
DeviceVariant::WiFi(device) => {
|
||||||
|
crate::pages::Message::WiFi(wifi::Message::SelectDevice(device))
|
||||||
|
}
|
||||||
|
DeviceVariant::Wired(device) => {
|
||||||
|
crate::pages::Message::Wired(wired::Message::SelectDevice(device))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return cosmic::command::batch(commands);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::UpdateDevices(devices) => {
|
||||||
|
self.devices = devices;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Command::none()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn connect(
|
||||||
|
&mut self,
|
||||||
|
conn: zbus::Connection,
|
||||||
|
sender: tokio::sync::mpsc::Sender<crate::pages::Message>,
|
||||||
|
) {
|
||||||
|
if self.nm_task.is_none() {
|
||||||
|
self.nm_task = Some(crate::utils::forward_event_loop(
|
||||||
|
sender,
|
||||||
|
|event| crate::pages::Message::Networking(event),
|
||||||
|
move |mut tx| async move {
|
||||||
|
let network_manager = match NetworkManager::new(&conn).await {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(why) => {
|
||||||
|
tracing::error!(
|
||||||
|
why = why.to_string(),
|
||||||
|
"failed to connect to network_manager"
|
||||||
|
);
|
||||||
|
|
||||||
|
return futures::future::pending().await;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut devices_changed = std::pin::pin!(network_manager
|
||||||
|
.receive_devices_changed()
|
||||||
|
.await
|
||||||
|
.then(|_| async {
|
||||||
|
match network_manager::devices::list(&conn, |_| true).await {
|
||||||
|
Ok(devices) => Message::UpdateDevices(
|
||||||
|
devices.into_iter().map(Arc::new).collect(),
|
||||||
|
),
|
||||||
|
Err(why) => Message::Error(why.to_string()),
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
while let Some(message) = devices_changed.next().await {
|
||||||
|
_ = tx.send(message).await;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ use cosmic_settings_subscriptions::network_manager::{
|
||||||
use futures::{FutureExt, StreamExt};
|
use futures::{FutureExt, StreamExt};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use secure_string::SecureString;
|
use secure_string::SecureString;
|
||||||
use slab::Slab;
|
|
||||||
|
|
||||||
pub type ConnectionId = Arc<str>;
|
pub type ConnectionId = Arc<str>;
|
||||||
pub type InterfaceId = String;
|
pub type InterfaceId = String;
|
||||||
|
|
@ -285,6 +284,9 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
||||||
|
let span = tracing::span!(tracing::Level::INFO, "vpn::update");
|
||||||
|
let _span = span.enter();
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
Message::NetworkManager(network_manager::Event::RequestResponse {
|
Message::NetworkManager(network_manager::Event::RequestResponse {
|
||||||
req,
|
req,
|
||||||
|
|
@ -490,7 +492,7 @@ impl Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::Error(why) => {
|
Message::Error(why) => {
|
||||||
tracing::error!(why, "error in VPN settings page");
|
tracing::error!(why);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::NetworkManagerConnect((conn, output)) => {
|
Message::NetworkManagerConnect((conn, output)) => {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
// Copyright 2024 System76 <info@system76.com>
|
// Copyright 2024 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: GPL-3.0-only
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::{
|
||||||
|
collections::{BTreeMap, BTreeSet},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use cosmic::{
|
use cosmic::{
|
||||||
|
|
@ -17,7 +20,6 @@ use cosmic_settings_subscriptions::network_manager::{
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use secure_string::SecureString;
|
use secure_string::SecureString;
|
||||||
use slab::Slab;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
|
|
@ -52,6 +54,8 @@ pub enum Message {
|
||||||
PasswordRequest(network_manager::SSID),
|
PasswordRequest(network_manager::SSID),
|
||||||
/// Update the password from the dialog
|
/// Update the password from the dialog
|
||||||
PasswordUpdate(SecureString),
|
PasswordUpdate(SecureString),
|
||||||
|
/// Selects a device to display connections from
|
||||||
|
SelectDevice(Arc<network_manager::devices::DeviceInfo>),
|
||||||
/// Opens settings page for the access point.
|
/// Opens settings page for the access point.
|
||||||
Settings(network_manager::SSID),
|
Settings(network_manager::SSID),
|
||||||
/// Toggles visibility of the password input
|
/// Toggles visibility of the password input
|
||||||
|
|
@ -92,6 +96,8 @@ enum WiFiDialog {
|
||||||
pub struct Page {
|
pub struct Page {
|
||||||
nm_task: Option<tokio::sync::oneshot::Sender<()>>,
|
nm_task: Option<tokio::sync::oneshot::Sender<()>>,
|
||||||
nm_state: Option<NmState>,
|
nm_state: Option<NmState>,
|
||||||
|
/// When defined, displays connections for the specific device.
|
||||||
|
active_device: Option<Arc<network_manager::devices::DeviceInfo>>,
|
||||||
dialog: Option<WiFiDialog>,
|
dialog: Option<WiFiDialog>,
|
||||||
view_more_popup: Option<network_manager::SSID>,
|
view_more_popup: Option<network_manager::SSID>,
|
||||||
connecting: BTreeSet<network_manager::SSID>,
|
connecting: BTreeSet<network_manager::SSID>,
|
||||||
|
|
@ -211,6 +217,7 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn on_leave(&mut self) -> Command<crate::pages::Message> {
|
fn on_leave(&mut self) -> Command<crate::pages::Message> {
|
||||||
|
self.active_device = None;
|
||||||
self.view_more_popup = None;
|
self.view_more_popup = None;
|
||||||
self.nm_state = None;
|
self.nm_state = None;
|
||||||
self.ssid_to_uuid.clear();
|
self.ssid_to_uuid.clear();
|
||||||
|
|
@ -228,6 +235,9 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
||||||
|
let span = tracing::span!(tracing::Level::INFO, "vpn::update");
|
||||||
|
let _span = span.enter();
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
Message::NetworkManager(network_manager::Event::RequestResponse {
|
Message::NetworkManager(network_manager::Event::RequestResponse {
|
||||||
req,
|
req,
|
||||||
|
|
@ -421,7 +431,12 @@ impl Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::Error(why) => {
|
Message::Error(why) => {
|
||||||
tracing::error!(why, "error in wifi settings page");
|
tracing::error!(why);
|
||||||
|
}
|
||||||
|
|
||||||
|
Message::SelectDevice(device) => {
|
||||||
|
// TODO: Per-device wifi connection handling.
|
||||||
|
self.active_device = Some(device);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::NetworkManagerConnect((conn, output)) => {
|
Message::NetworkManagerConnect((conn, output)) => {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ use cosmic_settings_page::{self as page, section, Section};
|
||||||
use cosmic_settings_subscriptions::network_manager::{
|
use cosmic_settings_subscriptions::network_manager::{
|
||||||
self, current_networks::ActiveConnectionInfo, devices::DeviceState, NetworkManagerState,
|
self, current_networks::ActiveConnectionInfo, devices::DeviceState, NetworkManagerState,
|
||||||
};
|
};
|
||||||
use slab::Slab;
|
|
||||||
|
|
||||||
pub type ConnectionId = Arc<str>;
|
pub type ConnectionId = Arc<str>;
|
||||||
|
|
||||||
|
|
@ -196,6 +195,9 @@ impl page::Page<crate::pages::Message> for Page {
|
||||||
|
|
||||||
impl Page {
|
impl Page {
|
||||||
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
pub fn update(&mut self, message: Message) -> Command<crate::app::Message> {
|
||||||
|
let span = tracing::span!(tracing::Level::INFO, "vpn::update");
|
||||||
|
let _span = span.enter();
|
||||||
|
|
||||||
match message {
|
match message {
|
||||||
Message::NetworkManager(network_manager::Event::RequestResponse {
|
Message::NetworkManager(network_manager::Event::RequestResponse {
|
||||||
req,
|
req,
|
||||||
|
|
@ -346,7 +348,7 @@ impl Page {
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::Error(why) => {
|
Message::Error(why) => {
|
||||||
tracing::error!(why, "error in wired settings page");
|
tracing::error!(why);
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::NetworkManagerConnect((conn, output)) => {
|
Message::NetworkManagerConnect((conn, output)) => {
|
||||||
|
|
@ -539,66 +541,6 @@ impl Page {
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn device_list_view<'a>(
|
|
||||||
&'a self,
|
|
||||||
_spacing: &cosmic::cosmic_theme::Spacing,
|
|
||||||
nm_state: &'a NmState,
|
|
||||||
devices_txt: &'a str,
|
|
||||||
) -> Element<'a, Message> {
|
|
||||||
nm_state
|
|
||||||
.devices
|
|
||||||
.iter()
|
|
||||||
.fold(
|
|
||||||
widget::settings::view_section(devices_txt),
|
|
||||||
|section, device| {
|
|
||||||
let is_unplugged = matches!(device.state, DeviceState::Unavailable);
|
|
||||||
|
|
||||||
let device_list =
|
|
||||||
cosmic::widget::settings::item::builder(device.interface.as_str())
|
|
||||||
.description(match device.state {
|
|
||||||
DeviceState::Activated => fl!("network-device-state", "activated"),
|
|
||||||
DeviceState::Config => fl!("network-device-state", "config"),
|
|
||||||
DeviceState::Deactivating => {
|
|
||||||
fl!("network-device-state", "deactivating")
|
|
||||||
}
|
|
||||||
DeviceState::Disconnected => {
|
|
||||||
fl!("network-device-state", "disconnected")
|
|
||||||
}
|
|
||||||
DeviceState::Failed => fl!("network-device-state", "failed"),
|
|
||||||
DeviceState::IpCheck => fl!("network-device-state", "ip-check"),
|
|
||||||
DeviceState::IpConfig => fl!("network-device-state", "ip-config"),
|
|
||||||
DeviceState::NeedAuth => fl!("network-device-state", "need-auth"),
|
|
||||||
DeviceState::Prepare => fl!("network-device-state", "prepare"),
|
|
||||||
DeviceState::Secondaries => {
|
|
||||||
fl!("network-device-state", "secondaries")
|
|
||||||
}
|
|
||||||
DeviceState::Unavailable => {
|
|
||||||
fl!("network-device-state", "unplugged")
|
|
||||||
}
|
|
||||||
DeviceState::Unknown => fl!("network-device-state", "unknown"),
|
|
||||||
DeviceState::Unmanaged => fl!("network-device-state", "unmanaged"),
|
|
||||||
})
|
|
||||||
.icon(icon::from_name("network-wired-symbolic").size(32))
|
|
||||||
.control(icon::from_name("go-next-symbolic").size(20))
|
|
||||||
.spacing(16)
|
|
||||||
.apply(widget::container)
|
|
||||||
.padding([16, 14])
|
|
||||||
.style(cosmic::theme::Container::List)
|
|
||||||
.apply(widget::button)
|
|
||||||
.padding(0)
|
|
||||||
.style(cosmic::theme::Button::Transparent)
|
|
||||||
.on_press_maybe(if is_unplugged {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(Message::SelectDevice(device.clone()))
|
|
||||||
});
|
|
||||||
|
|
||||||
section.add(device_list)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn devices_view() -> Section<crate::pages::Message> {
|
fn devices_view() -> Section<crate::pages::Message> {
|
||||||
|
|
@ -644,11 +586,7 @@ fn devices_view() -> Section<crate::pages::Message> {
|
||||||
device,
|
device,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
None => view.push(page.device_list_view(
|
None => view,
|
||||||
spacing,
|
|
||||||
nm_state,
|
|
||||||
§ion.descriptions[wired_devices_txt],
|
|
||||||
)),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
view.spacing(spacing.space_l)
|
view.spacing(spacing.space_l)
|
||||||
|
|
|
||||||
|
|
@ -51,7 +51,7 @@ pub fn forward_event_loop<M: 'static + Send, T: Future<Output = ()> + Send + 'st
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! slab {
|
macro_rules! slab {
|
||||||
( $descriptions:ident { $( $txt_id:ident = $txt_expr:expr; )+ } ) => {
|
( $descriptions:ident { $( $txt_id:ident = $txt_expr:expr; )+ } ) => {
|
||||||
let mut $descriptions = Slab::new();
|
let mut $descriptions = slab::Slab::new();
|
||||||
|
|
||||||
$(
|
$(
|
||||||
let $txt_id = $descriptions.insert($txt_expr);
|
let $txt_id = $descriptions.insert($txt_expr);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use cosmic::cosmic_theme::Spacing;
|
||||||
use cosmic::iced::{alignment, Length};
|
use cosmic::iced::{alignment, Length};
|
||||||
use cosmic::iced_core::text::Wrap;
|
use cosmic::iced_core::text::Wrap;
|
||||||
use cosmic::prelude::CollectionWidget;
|
use cosmic::prelude::CollectionWidget;
|
||||||
|
|
@ -118,18 +119,32 @@ pub fn display_container<'a, Message: 'a>(widget: Element<'a, Message>) -> Eleme
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn page_list_item<'a, Message: 'static + Clone>(
|
pub fn page_list_item<'a, Message: 'static + Clone>(
|
||||||
title: &'a str,
|
title: impl Into<Cow<'a, str>>,
|
||||||
description: &'a str,
|
description: impl Into<Cow<'a, str>>,
|
||||||
icon: &'a str,
|
icon: &'a str,
|
||||||
message: Message,
|
message: Message,
|
||||||
) -> Element<'a, Message> {
|
) -> Element<'a, Message> {
|
||||||
cosmic::widget::settings::item::builder(title)
|
let Spacing {
|
||||||
.description(description)
|
space_s, space_m, ..
|
||||||
|
} = cosmic::theme::active().cosmic().spacing;
|
||||||
|
|
||||||
|
let mut builder = cosmic::widget::settings::item::builder(title);
|
||||||
|
|
||||||
|
let description = description.into();
|
||||||
|
|
||||||
|
if !description.is_empty() {
|
||||||
|
builder = builder.description(description);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder
|
||||||
.icon(icon::from_name(icon).size(20))
|
.icon(icon::from_name(icon).size(20))
|
||||||
.control(icon::from_name("go-next-symbolic").size(20))
|
.control(icon::from_name("go-next-symbolic").size(20))
|
||||||
.spacing(16)
|
.spacing(space_s)
|
||||||
|
.height(space_s + space_m)
|
||||||
|
.align_items(alignment::Alignment::Center)
|
||||||
.apply(container)
|
.apply(container)
|
||||||
.padding([16, 14])
|
.padding([space_s, space_m])
|
||||||
|
.align_x(alignment::Horizontal::Center)
|
||||||
.style(theme::Container::List)
|
.style(theme::Container::List)
|
||||||
.apply(button)
|
.apply(button)
|
||||||
.padding(0)
|
.padding(0)
|
||||||
|
|
|
||||||
|
|
@ -40,15 +40,15 @@ forget-dialog = Forget this Wi-Fi network?
|
||||||
.description = You'll need to enter a password again to use this Wi-Fi network in the future.
|
.description = You'll need to enter a password again to use this Wi-Fi network in the future.
|
||||||
|
|
||||||
network-device-state =
|
network-device-state =
|
||||||
.activated = Connected to network
|
.activated = Connected
|
||||||
.config = Connecting to network
|
.config = Connecting
|
||||||
.deactivating = Disconnecting from network
|
.deactivating = Disconnecting
|
||||||
.disconnected = Disconnected
|
.disconnected = Disconnected
|
||||||
.failed = Failed to connect
|
.failed = Failed to connect
|
||||||
.ip-check = Checking connection
|
.ip-check = Checking connection
|
||||||
.ip-config = Requesting IP and routing information
|
.ip-config = Requesting IP and routing info
|
||||||
.need-auth = Needs authentication
|
.need-auth = Needs authentication
|
||||||
.prepare = Preparing to connect to network
|
.prepare = Preparing to connect
|
||||||
.secondaries = Waiting for secondary connection
|
.secondaries = Waiting for secondary connection
|
||||||
.unavailable = Unavailable
|
.unavailable = Unavailable
|
||||||
.unknown = Unknown state
|
.unknown = Unknown state
|
||||||
|
|
@ -65,11 +65,13 @@ vpn = VPN
|
||||||
.select-file = Select a VPN configuration file
|
.select-file = Select a VPN configuration file
|
||||||
|
|
||||||
wired = Wired
|
wired = Wired
|
||||||
|
.adapter = Wired adapter { $id }
|
||||||
.connections = Wired Connections
|
.connections = Wired Connections
|
||||||
.devices = Wired Devices
|
.devices = Wired Devices
|
||||||
.remove = Remove connection profile
|
.remove = Remove connection profile
|
||||||
|
|
||||||
wifi = Wi-Fi
|
wifi = Wi-Fi
|
||||||
|
.adapter = Wi-Fi adapter { $id }
|
||||||
.forget = Forget this network
|
.forget = Forget this network
|
||||||
|
|
||||||
## Networking: Online Accounts
|
## Networking: Online Accounts
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue