fractal/components/
offline_banner.rs1use adw::{prelude::*, subclass::prelude::*};
2use gtk::{gio, glib, glib::clone};
3
4use crate::{session::Session, utils::BoundObjectWeakRef};
5
6mod imp {
7 use std::cell::RefCell;
8
9 use gettextrs::gettext;
10
11 use super::*;
12
13 #[derive(Debug, Default, glib::Properties)]
14 #[properties(wrapper_type = super::OfflineBanner)]
15 pub struct OfflineBanner {
16 banner: adw::Banner,
17 #[property(get, set = Self::set_session, explicit_notify, nullable)]
19 session: BoundObjectWeakRef<Session>,
20 monitor_handler: RefCell<Option<glib::SignalHandlerId>>,
21 }
22
23 #[glib::object_subclass]
24 impl ObjectSubclass for OfflineBanner {
25 const NAME: &'static str = "OfflineBanner";
26 type Type = super::OfflineBanner;
27 type ParentType = adw::Bin;
28 }
29
30 #[glib::derived_properties]
31 impl ObjectImpl for OfflineBanner {
32 fn constructed(&self) {
33 self.parent_constructed();
34
35 self.obj().set_child(Some(&self.banner));
36 self.update();
37 }
38
39 fn dispose(&self) {
40 if let Some(handler) = self.monitor_handler.take() {
41 gio::NetworkMonitor::default().disconnect(handler);
42 }
43 }
44 }
45
46 impl WidgetImpl for OfflineBanner {}
47 impl BinImpl for OfflineBanner {}
48
49 impl OfflineBanner {
50 fn set_session(&self, session: Option<&Session>) {
52 if self.session.obj().as_ref() == session {
53 return;
54 }
55
56 if let Some(handler) = self.monitor_handler.take() {
57 gio::NetworkMonitor::default().disconnect(handler);
58 }
59 self.session.disconnect_signals();
60
61 if let Some(session) = session {
62 let offline_handler = session.connect_is_offline_notify(clone!(
63 #[weak(rename_to = imp)]
64 self,
65 move |_| {
66 imp.update();
67 }
68 ));
69
70 self.session.set(session, vec![offline_handler]);
71 }
72
73 self.update();
74 }
75
76 fn ensure_connection_watched(&self) {
78 if self.session.obj().is_some() {
79 return;
81 }
82
83 if self.monitor_handler.borrow().is_some() {
84 return;
86 }
87
88 let monitor = gio::NetworkMonitor::default();
89 let monitor_handler = monitor.connect_network_changed(clone!(
90 #[weak(rename_to = imp)]
91 self,
92 move |_, _| {
93 imp.update();
94 }
95 ));
96 self.monitor_handler.replace(Some(monitor_handler));
97 }
98
99 fn update(&self) {
101 if let Some(session) = self.session.obj() {
102 self.banner.set_title(&gettext("Offline"));
103 self.banner.set_revealed(session.is_offline());
104 } else {
105 self.ensure_connection_watched();
106 let monitor = gio::NetworkMonitor::default();
107
108 if !monitor.is_network_available() {
109 self.banner.set_title(&gettext("No network connection"));
110 self.banner.set_revealed(true);
111 } else if monitor.connectivity() != gio::NetworkConnectivity::Full {
112 self.banner.set_title(&gettext("No Internet connection"));
113 self.banner.set_revealed(true);
114 } else {
115 self.banner.set_revealed(false);
116 }
117 }
118 }
119 }
120}
121
122glib::wrapper! {
123 pub struct OfflineBanner(ObjectSubclass<imp::OfflineBanner>)
128 @extends gtk::Widget, adw::Bin,
129 @implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
130}
131
132impl OfflineBanner {
133 pub fn new() -> Self {
134 glib::Object::new()
135 }
136}