[fractal] session: Mark offline when homeserver isn't reachable



commit 8a6a209f54283c5416dc66c87ad429dedeaf3d5b
Author: Julian Sparber <julian sparber net>
Date:   Fri Jul 22 19:50:32 2022 +0200

    session: Mark offline when homeserver isn't reachable
    
    This also shows a infobar to the user when offline.
    This completely ignores the connecticity state since it's unrelaibale
    and the server may be reachable even without internet connection.

 data/resources/ui/sidebar.ui | 15 +++++++
 src/session/mod.rs           | 97 +++++++++++++++++++++++++++++++++++++++++++-
 src/session/sidebar/mod.rs   | 30 +++++++++++++-
 3 files changed, 138 insertions(+), 4 deletions(-)
---
diff --git a/data/resources/ui/sidebar.ui b/data/resources/ui/sidebar.ui
index de0353c60..2f3eb9b8f 100644
--- a/data/resources/ui/sidebar.ui
+++ b/data/resources/ui/sidebar.ui
@@ -133,6 +133,20 @@
             </accessibility>
           </object>
         </child>
+        <child>
+          <object class="GtkInfoBar" id="offline_info_bar">
+            <property name="message-type">warning</property>
+            <child>
+              <object class="GtkLabel">
+                <property name="hexpand">True</property>
+                <property name="label" translatable="yes">Offline</property>
+                <style>
+                  <class name="heading"/>
+                </style>
+              </object>
+            </child>
+          </object>
+        </child>
         <child>
           <object class="GtkScrolledWindow">
             <property name="vexpand">True</property>
@@ -156,3 +170,4 @@
     </child>
   </template>
 </interface>
+
diff --git a/src/session/mod.rs b/src/session/mod.rs
index 040d53547..b3e17d58d 100644
--- a/src/session/mod.rs
+++ b/src/session/mod.rs
@@ -15,7 +15,15 @@ use std::{collections::HashSet, convert::TryFrom, fs, path::PathBuf, time::Durat
 use adw::subclass::prelude::BinImpl;
 use futures::StreamExt;
 use gettextrs::gettext;
-use gtk::{self, gdk, glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate};
+use gtk::{
+    self, gdk, gio,
+    gio::prelude::*,
+    glib,
+    glib::{clone, signal::SignalHandlerId},
+    prelude::*,
+    subclass::prelude::*,
+    CompositeTemplate,
+};
 use log::{debug, error, warn};
 use matrix_sdk::{
     config::{RequestConfig, StoreConfig, SyncSettings},
@@ -63,7 +71,9 @@ use crate::{
     secret,
     secret::{Secret, StoredSession},
     session::sidebar::ItemList,
-    spawn, spawn_tokio, toast, UserFacingError, Window,
+    spawn, spawn_tokio, toast,
+    utils::check_if_reachable,
+    UserFacingError, Window,
 };
 
 #[derive(Error, Debug)]
@@ -111,9 +121,12 @@ mod imp {
         pub item_list: OnceCell<ItemList>,
         pub user: OnceCell<User>,
         pub is_ready: Cell<bool>,
+        pub prepared: Cell<bool>,
         pub logout_on_dispose: Cell<bool>,
         pub info: OnceCell<StoredSession>,
         pub sync_tokio_handle: RefCell<Option<JoinHandle<()>>>,
+        pub offline_handler_id: RefCell<Option<SignalHandlerId>>,
+        pub offline: Cell<bool>,
     }
 
     #[glib::object_subclass]
@@ -208,6 +221,13 @@ mod imp {
                         User::static_type(),
                         glib::ParamFlags::READABLE,
                     ),
+                    glib::ParamSpecBoolean::new(
+                        "offline",
+                        "Offline",
+                        "Whether this session has a connection to the homeserver",
+                        false,
+                        glib::ParamFlags::READABLE,
+                    ),
                 ]
             });
 
@@ -218,6 +238,7 @@ mod imp {
             match pspec.name() {
                 "item-list" => obj.item_list().to_value(),
                 "user" => obj.user().to_value(),
+                "offline" => obj.is_offline().to_value(),
                 _ => unimplemented!(),
             }
         }
@@ -253,9 +274,23 @@ mod imp {
                     }
                 }),
             );
+
+            let monitor = gio::NetworkMonitor::default();
+            let handler_id = monitor.connect_network_changed(clone!(@weak obj => move |_, _| {
+                spawn!(clone!(@weak obj => async move {
+                    obj.update_offline().await;
+                }));
+            }));
+
+            self.offline_handler_id.replace(Some(handler_id));
         }
 
         fn dispose(&self, obj: &Self::Type) {
+            // Needs to be disconnected or else it may restart the sync
+            if let Some(handler_id) = self.offline_handler_id.take() {
+                gio::NetworkMonitor::default().disconnect(handler_id);
+            }
+
             if let Some(handle) = self.sync_tokio_handle.take() {
                 handle.abort();
             }
@@ -467,11 +502,13 @@ impl Session {
                 };
 
                 priv_.info.set(session).unwrap();
+                self.update_offline().await;
 
                 self.room_list().load();
                 self.setup_direct_room_handler();
                 self.setup_room_encrypted_changes();
 
+                self.set_is_prepared(true);
                 self.sync();
 
                 None
@@ -489,6 +526,10 @@ impl Session {
     }
 
     fn sync(&self) {
+        if !self.is_prepared() || self.is_offline() {
+            return;
+        }
+
         let client = self.client();
         let session_weak: glib::SendWeakRef<Session> = self.downgrade().into();
 
@@ -587,6 +628,18 @@ impl Session {
         self.imp().is_ready.get()
     }
 
+    fn set_is_prepared(&self, prepared: bool) {
+        if self.is_prepared() == prepared {
+            return;
+        }
+
+        self.imp().prepared.set(prepared);
+    }
+
+    fn is_prepared(&self) -> bool {
+        self.imp().prepared.get()
+    }
+
     pub fn room_list(&self) -> &RoomList {
         self.item_list().room_list()
     }
@@ -634,6 +687,46 @@ impl Session {
             .expect("The session isn't ready")
     }
 
+    pub fn is_offline(&self) -> bool {
+        self.imp().offline.get()
+    }
+
+    async fn update_offline(&self) {
+        let priv_ = self.imp();
+        let monitor = gio::NetworkMonitor::default();
+
+        let is_offline = if monitor.is_network_available() {
+            if let Some(info) = priv_.info.get() {
+                !check_if_reachable(&info.homeserver).await
+            } else {
+                false
+            }
+        } else {
+            true
+        };
+
+        if self.is_offline() == is_offline {
+            return;
+        }
+
+        if is_offline {
+            debug!("This session is now offline");
+        } else {
+            debug!("This session is now online");
+        }
+
+        priv_.offline.set(is_offline);
+
+        if let Some(handle) = priv_.sync_tokio_handle.take() {
+            handle.abort();
+        }
+
+        // Restart the sync loop when online
+        self.sync();
+
+        self.notify("offline");
+    }
+
     /// Connects the prepared signals to the function f given in input
     pub fn connect_prepared<F: Fn(&Self, Option<String>) + 'static>(
         &self,
diff --git a/src/session/sidebar/mod.rs b/src/session/sidebar/mod.rs
index fbca517c5..843951fc6 100644
--- a/src/session/sidebar/mod.rs
+++ b/src/session/sidebar/mod.rs
@@ -34,6 +34,7 @@ use crate::{
     components::Avatar,
     session::{
         room::{Room, RoomType},
+        user::UserExt,
         verification::IdentityVerification,
         User,
     },
@@ -46,7 +47,7 @@ mod imp {
         convert::TryFrom,
     };
 
-    use glib::subclass::InitializingObject;
+    use glib::{signal::SignalHandlerId, subclass::InitializingObject};
     use once_cell::{sync::Lazy, unsync::OnceCell};
 
     use super::*;
@@ -68,11 +69,14 @@ mod imp {
         pub account_switcher_button: TemplateChild<gtk::MenuButton>,
         #[template_child]
         pub room_row_menu: TemplateChild<gio::MenuModel>,
+        #[template_child]
+        pub offline_info_bar: TemplateChild<gtk::InfoBar>,
         pub room_row_popover: OnceCell<gtk::PopoverMenu>,
         pub user: RefCell<Option<User>>,
         /// The type of the source that activated drop mode.
         pub drop_source_type: Cell<Option<RoomType>>,
         pub drop_binding: RefCell<Option<glib::Binding>>,
+        pub offline_handler_id: RefCell<Option<SignalHandlerId>>,
     }
 
     #[glib::object_subclass]
@@ -367,10 +371,32 @@ impl Sidebar {
     }
 
     fn set_user(&self, user: Option<User>) {
-        if self.user() == user {
+        let prev_user = self.user();
+        if prev_user == user {
             return;
         }
 
+        if let Some(prev_user) = prev_user {
+            if let Some(handler_id) = self.imp().offline_handler_id.take() {
+                prev_user.session().disconnect(handler_id);
+            }
+        }
+
+        if let Some(user) = user.as_ref() {
+            let session = user.session();
+            let handler_id = session.connect_notify_local(
+                Some("offline"),
+                clone!(@weak self as obj => move |session, _| {
+                    obj.imp().offline_info_bar.set_revealed(session.is_offline());
+                }),
+            );
+            self.imp()
+                .offline_info_bar
+                .set_revealed(session.is_offline());
+
+            self.imp().offline_handler_id.replace(Some(handler_id));
+        }
+
         self.imp().user.replace(user);
         self.notify("user");
     }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]