azalea_shell/factory/network/
device.rs

1use azalea_service::StaticServiceManager;
2use gtk::prelude::*;
3use relm4::{
4    FactorySender,
5    prelude::{DynamicIndex, FactoryComponent},
6};
7use zbus::zvariant::OwnedObjectPath;
8
9use crate::{
10    icon,
11    service::{
12        self,
13        dbus::network_manager::proxy::{NMDeviceState, NetworkManagerDeviceProxyBlocking},
14    },
15};
16
17#[derive(Debug)]
18pub struct Model {
19    device: OwnedObjectPath,
20    name: String,
21    proxy: Option<NetworkManagerDeviceProxyBlocking<'static>>,
22    is_activated: bool,
23    visible: bool,
24    #[allow(unused)]
25    connection: Option<zbus::blocking::Connection>,
26}
27
28#[derive(Debug)]
29pub enum Input {
30    Toggle,
31    Connect,
32    Disconnect,
33}
34
35#[derive(Debug)]
36pub enum CommandOutput {
37    State(service::dbus::network_manager::proxy::NMDeviceState),
38}
39
40#[derive(Debug)]
41pub enum Output {}
42
43#[relm4::factory(pub)]
44impl FactoryComponent for Model {
45    type Init = OwnedObjectPath;
46    type Input = Input;
47    type Output = Output;
48    type CommandOutput = CommandOutput;
49    type ParentWidget = gtk::Box;
50
51    view! {
52        #[root]
53        gtk::Box {
54            set_visible: self.visible,
55            set_spacing: 12,
56
57            gtk::Label {
58                set_halign: gtk::Align::Start,
59                set_hexpand: true,
60                set_label: &self.name,
61            },
62
63            gtk::Button {
64                set_halign: gtk::Align::End,
65
66                #[watch]
67                set_icon_name: if self.is_activated {
68                    icon::PLUG_CONNECTED
69                } else {
70                    icon::PLUG_DISCONNECTED
71                },
72
73                #[watch]
74                set_css_classes: if self.is_activated {
75                    &[ "azalea-primary-container" ]
76                } else {
77                    &[]
78                },
79
80                connect_clicked => Input::Toggle
81            }
82        }
83    }
84
85    fn init_model(device: Self::Init, _index: &DynamicIndex, sender: FactorySender<Self>) -> Self {
86        let connection = zbus::blocking::Connection::system().ok();
87
88        let proxy = connection
89            .as_ref()
90            .and_then(|conn| NetworkManagerDeviceProxyBlocking::new(&conn, device.clone()).ok());
91
92        let state_stream = proxy.as_ref().map(|p| p.receive_state_changed());
93        if let Some(mut state_stream) = state_stream {
94            let cmd_sender = sender.command_sender().clone();
95            relm4::spawn_blocking(move || {
96                while let Some(prop) = state_stream.next() {
97                    if let Ok(state) = prop.get() {
98                        if let Err(_) = cmd_sender.send(CommandOutput::State(state)) {
99                            break;
100                        }
101                    }
102                }
103            });
104        }
105
106        Self {
107            connection,
108            device,
109            name: proxy
110                .as_ref()
111                .and_then(|p| p.interface().ok())
112                .unwrap_or(format!("unknown")),
113            is_activated: proxy
114                .as_ref()
115                .and_then(|p| p.state().ok())
116                .map(|s| s == NMDeviceState::NMDeviceStateActivated)
117                .unwrap_or(false),
118            visible: proxy
119                .as_ref()
120                .and_then(|p| p.device_type().ok())
121                .map(|t| match t {
122                    _ => true,
123                })
124                .unwrap_or(false),
125            proxy,
126        }
127    }
128
129    fn update(&mut self, message: Self::Input, sender: FactorySender<Self>) {
130        match message {
131            Input::Toggle => {
132                if self.is_activated {
133                    self.update(Input::Disconnect, sender);
134                } else {
135                    self.update(Input::Connect, sender);
136                }
137            }
138            Input::Connect => {
139                service::dbus::network_manager::Service::send(
140                    service::dbus::network_manager::Input::ActivateConnection {
141                        connection: None,
142                        device: Some(self.device.clone()),
143                        specific_object: None,
144                    },
145                );
146            }
147            Input::Disconnect => {
148                if let Some(proxy) = &self.proxy {
149                    drop(proxy.disconnect());
150                };
151            }
152        }
153    }
154
155    fn update_cmd(&mut self, message: Self::CommandOutput, _sender: FactorySender<Self>) {
156        match message {
157            CommandOutput::State(nmdevice_state) => match nmdevice_state {
158                NMDeviceState::NMDeviceStateActivated => self.is_activated = true,
159                _ => self.is_activated = false,
160            },
161        }
162    }
163}