[fractal/fractal-next] Fix Session dispose



commit 5878b1abff4cdb8c1385d986c1f6a74df7d9e2bf
Author: Julian Sparber <julian sparber net>
Date:   Fri Oct 8 14:08:17 2021 +0200

    Fix Session dispose
    
    We kept many references to Session, therefore we leaked it when the
    application is closed.
    
    This also removes the loading page from the `Window` and cleans up how
    a Session is restored.

 data/resources/ui/window.ui                        | 28 --------------
 src/components/auth_dialog.rs                      | 40 +++++++-------------
 src/login.rs                                       | 17 +++++++--
 .../account_settings/devices_page/device.rs        | 37 +++++++------------
 .../account_settings/devices_page/device_list.rs   | 43 +++++++---------------
 src/session/account_settings/devices_page/mod.rs   |  2 +-
 src/session/avatar.rs                              | 12 ++++--
 src/session/content/explore/mod.rs                 | 13 +++++--
 src/session/content/explore/public_room.rs         | 14 ++++---
 src/session/content/explore/public_room_list.rs    | 28 +++++++++++---
 src/session/content/mod.rs                         | 26 ++++++++++---
 src/session/room/mod.rs                            | 17 ++++++---
 src/session/room_creation/mod.rs                   | 13 +++++--
 src/session/room_list.rs                           | 20 ++++++----
 src/session/user.rs                                | 14 ++++---
 src/window.rs                                      | 20 ++--------
 16 files changed, 170 insertions(+), 174 deletions(-)
---
diff --git a/data/resources/ui/window.ui b/data/resources/ui/window.ui
index 7cdee88e..c1696d38 100644
--- a/data/resources/ui/window.ui
+++ b/data/resources/ui/window.ui
@@ -26,34 +26,6 @@
                 <property name="transition-type">crossfade</property>
               </object>
             </child>
-            <child>
-              <object class="GtkWindowHandle" id="loading_page">
-                <property name="child">
-                  <object class="GtkBox">
-                    <property name="orientation">vertical</property>
-                    <child>
-                      <object class="GtkHeaderBar">
-                        <property name="show-title-buttons">True</property>
-                        <style>
-                          <class name="flat"/>
-                        </style>
-                      </object>
-                    </child>
-                    <child>
-                      <object class="GtkSpinner">
-                        <property name="spinning">True</property>
-                        <property name="valign">center</property>
-                        <property name="halign">center</property>
-                        <property name="vexpand">True</property>
-                        <style>
-                          <class name="session-loading-spinner"/>
-                        </style>
-                      </object>
-                    </child>
-                  </object>
-                </property>
-              </object>
-            </child>
           </object>
         </child>
       </object>
diff --git a/src/components/auth_dialog.rs b/src/components/auth_dialog.rs
index 42067862..00dbd0c6 100644
--- a/src/components/auth_dialog.rs
+++ b/src/components/auth_dialog.rs
@@ -73,15 +73,16 @@ impl AuthData {
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use glib::subclass::{InitializingObject, Signal};
     use glib::SignalHandlerId;
-    use once_cell::sync::Lazy;
+    use once_cell::sync::{Lazy, OnceCell};
     use std::cell::RefCell;
 
     #[derive(Debug, Default, CompositeTemplate)]
     #[template(resource = "/org/gnome/FractalNext/components-auth-dialog.ui")]
     pub struct AuthDialog {
-        pub session: RefCell<Option<Session>>,
+        pub session: OnceCell<WeakRef<Session>>,
         #[template_child]
         pub stack: TemplateChild<gtk::Stack>,
         #[template_child]
@@ -129,7 +130,7 @@ mod imp {
                     "Session",
                     "The session",
                     Session::static_type(),
-                    glib::ParamFlags::READWRITE | glib::ParamFlags::EXPLICIT_NOTIFY,
+                    glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
                 )]
             });
 
@@ -138,13 +139,16 @@ mod imp {
 
         fn set_property(
             &self,
-            obj: &Self::Type,
+            _obj: &Self::Type,
             _id: usize,
             value: &glib::Value,
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => obj.set_session(value.get().unwrap()),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 _ => unimplemented!(),
             }
         }
@@ -207,21 +211,9 @@ impl AuthDialog {
             .expect("Failed to create AuthDialog")
     }
 
-    pub fn session(&self) -> Option<Session> {
+    pub fn session(&self) -> Session {
         let priv_ = imp::AuthDialog::from_instance(self);
-        priv_.session.borrow().clone()
-    }
-
-    pub fn set_session(&self, session: Option<Session>) {
-        let priv_ = imp::AuthDialog::from_instance(self);
-
-        if self.session() == session {
-            return;
-        };
-
-        priv_.session.replace(session);
-
-        self.notify("session");
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     pub async fn authenticate<
@@ -266,13 +258,7 @@ impl AuthDialog {
                 "m.login.password" => {
                     priv_.stack.set_visible_child_name("m.login.password");
                     if self.show_and_wait_for_response().await {
-                        let user_id = self
-                            .session()
-                            .unwrap()
-                            .user()
-                            .unwrap()
-                            .user_id()
-                            .to_string();
+                        let user_id = self.session().user().unwrap().user_id().to_string();
                         let password = priv_.password.text().to_string();
                         let session = uiaa_info.session;
 
@@ -291,7 +277,7 @@ impl AuthDialog {
                     if let Some(session) = uiaa_info.session {
                         priv_.stack.set_visible_child_name("fallback");
 
-                        let client = self.session()?.client().clone();
+                        let client = self.session().client().clone();
                         let (sender, receiver) = futures::channel::oneshot::channel();
                         RUNTIME.spawn(async move { sender.send(client.homeserver().await) });
                         let homeserver = receiver.await.unwrap();
diff --git a/src/login.rs b/src/login.rs
index fa2accd7..f5a63c3b 100644
--- a/src/login.rs
+++ b/src/login.rs
@@ -14,10 +14,12 @@ mod imp {
     use super::*;
     use glib::subclass::{InitializingObject, Signal};
     use once_cell::sync::Lazy;
+    use std::cell::RefCell;
 
     #[derive(Debug, Default, CompositeTemplate)]
     #[template(resource = "/org/gnome/FractalNext/login.ui")]
     pub struct Login {
+        pub current_session: RefCell<Option<Session>>,
         #[template_child]
         pub next_button: TemplateChild<gtk::Button>,
         #[template_child]
@@ -135,6 +137,8 @@ impl Login {
             username,
             password,
         );
+
+        priv_.current_session.replace(Some(session));
     }
 
     fn clean(&self) {
@@ -163,7 +167,7 @@ impl Login {
         priv_.main_stack.set_sensitive(true);
     }
 
-    pub fn connect_new_session<F: Fn(&Self, &Session) + 'static>(
+    pub fn connect_new_session<F: Fn(&Self, Session) + 'static>(
         &self,
         f: F,
     ) -> glib::SignalHandlerId {
@@ -171,13 +175,19 @@ impl Login {
             let obj = values[0].get::<Self>().unwrap();
             let session = values[1].get::<Session>().unwrap();
 
-            f(&obj, &session);
+            f(&obj, session);
 
             None
         })
         .unwrap()
     }
 
+    fn drop_session_reference(&self) {
+        let priv_ = imp::Login::from_instance(self);
+
+        priv_.current_session.take();
+    }
+
     pub fn default_widget(&self) -> gtk::Widget {
         imp::Login::from_instance(self).next_button.get().upcast()
     }
@@ -188,7 +198,7 @@ impl Login {
         priv_.back_to_session_button.set_visible(show);
     }
 
-    pub fn set_handler_for_prepared_session(&self, session: &Session) {
+    fn set_handler_for_prepared_session(&self, session: &Session) {
         session.connect_prepared(clone!(@weak self as login => move |session| {
             if let Some(error) = session.get_error() {
                 let error_message = &imp::Login::from_instance(&login).error_message;
@@ -202,6 +212,7 @@ impl Login {
                 login.emit_by_name("new-session", &[&session]).unwrap();
                 login.clean();
             }
+            login.drop_session_reference();
         }));
     }
 }
diff --git a/src/session/account_settings/devices_page/device.rs 
b/src/session/account_settings/devices_page/device.rs
index 70862b31..9c470b0f 100644
--- a/src/session/account_settings/devices_page/device.rs
+++ b/src/session/account_settings/devices_page/device.rs
@@ -15,14 +15,14 @@ use log::error;
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use once_cell::sync::{Lazy, OnceCell};
-    use std::cell::RefCell;
 
     #[derive(Debug, Default)]
     pub struct Device {
         pub device: OnceCell<MatrixDevice>,
         pub crypto_device: OnceCell<CryptoDevice>,
-        pub session: RefCell<Option<Session>>,
+        pub session: OnceCell<WeakRef<Session>>,
     }
 
     #[glib::object_subclass]
@@ -41,7 +41,7 @@ mod imp {
                         "Session",
                         "The session",
                         Session::static_type(),
-                        glib::ParamFlags::READWRITE,
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
                     ),
                     glib::ParamSpec::new_string(
                         "device-id",
@@ -84,13 +84,16 @@ mod imp {
 
         fn set_property(
             &self,
-            obj: &Self::Type,
+            _obj: &Self::Type,
             _id: usize,
             value: &glib::Value,
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => obj.set_session(value.get().unwrap()),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 _ => unimplemented!(),
             }
         }
@@ -116,33 +119,21 @@ glib::wrapper! {
 
 impl Device {
     pub fn new(
-        session: Option<&Session>,
+        session: &Session,
         device: MatrixDevice,
         crypto_device: Option<CryptoDevice>,
     ) -> Self {
         let obj: Self =
-            glib::Object::new(&[("session", &session)]).expect("Failed to create Device");
+            glib::Object::new(&[("session", session)]).expect("Failed to create Device");
 
         obj.set_matrix_device(device, crypto_device);
 
         obj
     }
 
-    pub fn session(&self) -> Option<Session> {
-        let priv_ = imp::Device::from_instance(self);
-        priv_.session.borrow().clone()
-    }
-
-    fn set_session(&self, session: Option<Session>) {
+    pub fn session(&self) -> Session {
         let priv_ = imp::Device::from_instance(self);
-
-        if self.session() == session {
-            return;
-        };
-
-        priv_.session.replace(session);
-
-        self.notify("session");
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     fn set_matrix_device(&self, device: MatrixDevice, crypto_device: Option<CryptoDevice>) {
@@ -197,9 +188,7 @@ impl Device {
     ///
     /// Returns `true` for success
     pub async fn delete(&self, transient_for: Option<&impl IsA<gtk::Window>>) -> bool {
-        let session = self
-            .session()
-            .expect("Session needs to be set when removing a device");
+        let session = self.session();
         let client = session.client().clone();
         let device_id = self.device_id().to_owned();
 
diff --git a/src/session/account_settings/devices_page/device_list.rs 
b/src/session/account_settings/devices_page/device_list.rs
index 5ab1ddb7..65396d87 100644
--- a/src/session/account_settings/devices_page/device_list.rs
+++ b/src/session/account_settings/devices_page/device_list.rs
@@ -10,7 +10,8 @@ use crate::{session::Session, utils::do_async};
 use super::{Device, DeviceItem};
 
 mod imp {
-    use once_cell::sync::Lazy;
+    use glib::object::WeakRef;
+    use once_cell::sync::{Lazy, OnceCell};
     use std::cell::{Cell, RefCell};
 
     use super::*;
@@ -18,7 +19,7 @@ mod imp {
     #[derive(Debug, Default)]
     pub struct DeviceList {
         pub list: RefCell<Vec<DeviceItem>>,
-        pub session: RefCell<Option<Session>>,
+        pub session: OnceCell<WeakRef<Session>>,
         pub current_device: RefCell<Option<DeviceItem>>,
         pub loading: Cell<bool>,
     }
@@ -40,7 +41,7 @@ mod imp {
                         "Session",
                         "The session",
                         Session::static_type(),
-                        glib::ParamFlags::READWRITE,
+                        glib::ParamFlags::READWRITE | glib::ParamFlags::CONSTRUCT_ONLY,
                     ),
                     glib::ParamSpec::new_object(
                         "current-device",
@@ -57,13 +58,16 @@ mod imp {
 
         fn set_property(
             &self,
-            obj: &Self::Type,
+            _obj: &Self::Type,
             _id: usize,
             value: &glib::Value,
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => obj.set_session(value.get().unwrap()),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 _ => unimplemented!(),
             }
         }
@@ -105,23 +109,9 @@ impl DeviceList {
         glib::Object::new(&[("session", session)]).expect("Failed to create DeviceList")
     }
 
-    pub fn session(&self) -> Option<Session> {
+    pub fn session(&self) -> Session {
         let priv_ = imp::DeviceList::from_instance(self);
-        priv_.session.borrow().clone()
-    }
-
-    fn set_session(&self, session: Option<Session>) {
-        let priv_ = imp::DeviceList::from_instance(self);
-
-        if self.session() == session {
-            return;
-        };
-
-        priv_.session.replace(session);
-
-        self.load_devices();
-
-        self.notify("session");
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     fn set_loading(&self, loading: bool) {
@@ -176,7 +166,6 @@ impl DeviceList {
         response: Result<(Option<MatrixDevice>, Vec<MatrixDevice>, CryptoDevices), Error>,
     ) {
         let session = self.session();
-        let session = session.as_ref();
 
         match response {
             Ok((current_device, devices, crypto_devices)) => {
@@ -184,7 +173,7 @@ impl DeviceList {
                     .into_iter()
                     .map(|device| {
                         let crypto_device = crypto_devices.get(&device.device_id);
-                        DeviceItem::for_device(Device::new(session, device, crypto_device))
+                        DeviceItem::for_device(Device::new(&session, device, crypto_device))
                     })
                     .collect();
 
@@ -192,7 +181,7 @@ impl DeviceList {
 
                 self.set_current_device(current_device.map(|device| {
                     let crypto_device = crypto_devices.get(&device.device_id);
-                    DeviceItem::for_device(Device::new(session, device, crypto_device))
+                    DeviceItem::for_device(Device::new(&session, device, crypto_device))
                 }));
             }
             Err(error) => {
@@ -206,11 +195,7 @@ impl DeviceList {
     }
 
     pub fn load_devices(&self) {
-        let client = if let Some(session) = self.session() {
-            session.client().clone()
-        } else {
-            return;
-        };
+        let client = self.session().client().clone();
 
         self.set_loading(true);
 
diff --git a/src/session/account_settings/devices_page/mod.rs 
b/src/session/account_settings/devices_page/mod.rs
index cfb98eab..111774a2 100644
--- a/src/session/account_settings/devices_page/mod.rs
+++ b/src/session/account_settings/devices_page/mod.rs
@@ -113,7 +113,7 @@ impl DevicesPage {
         }
 
         if let Some(ref user) = user {
-            let device_list = DeviceList::new(user.session());
+            let device_list = DeviceList::new(&user.session());
             priv_.other_sessions.bind_model(
                 Some(&device_list),
                 clone!(@weak device_list => @default-panic, move |item| {
diff --git a/src/session/avatar.rs b/src/session/avatar.rs
index 90f775b8..d7c62ccc 100644
--- a/src/session/avatar.rs
+++ b/src/session/avatar.rs
@@ -18,6 +18,7 @@ use crate::session::Session;
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use once_cell::sync::{Lazy, OnceCell};
     use std::cell::{Cell, RefCell};
 
@@ -27,7 +28,7 @@ mod imp {
         pub needed: Cell<bool>,
         pub url: RefCell<Option<MxcUri>>,
         pub display_name: RefCell<Option<String>>,
-        pub session: OnceCell<Session>,
+        pub session: OnceCell<WeakRef<Session>>,
     }
 
     #[glib::object_subclass]
@@ -92,7 +93,10 @@ mod imp {
             match pspec.name() {
                 "needed" => obj.set_needed(value.get().unwrap()),
                 "url" => obj.set_url(value.get::<Option<&str>>().unwrap().map(Into::into)),
-                "session" => self.session.set(value.get().unwrap()).unwrap(),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 "display-name" => {
                     let _ = obj.set_display_name(value.get().unwrap());
                 }
@@ -132,9 +136,9 @@ impl Avatar {
         .expect("Failed to create Avatar")
     }
 
-    fn session(&self) -> &Session {
+    fn session(&self) -> Session {
         let priv_ = imp::Avatar::from_instance(self);
-        priv_.session.get().unwrap()
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     pub fn image(&self) -> Option<gdk::Paintable> {
diff --git a/src/session/content/explore/mod.rs b/src/session/content/explore/mod.rs
index 01d190bb..a7de1fed 100644
--- a/src/session/content/explore/mod.rs
+++ b/src/session/content/explore/mod.rs
@@ -16,6 +16,7 @@ use crate::utils::do_async;
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use glib::subclass::InitializingObject;
     use once_cell::sync::Lazy;
     use std::cell::{Cell, RefCell};
@@ -24,7 +25,7 @@ mod imp {
     #[template(resource = "/org/gnome/FractalNext/content-explore.ui")]
     pub struct Explore {
         pub compact: Cell<bool>,
-        pub session: RefCell<Option<Session>>,
+        pub session: RefCell<Option<WeakRef<Session>>>,
         #[template_child]
         pub stack: TemplateChild<gtk::Stack>,
         #[template_child]
@@ -148,7 +149,11 @@ impl Explore {
 
     pub fn session(&self) -> Option<Session> {
         let priv_ = imp::Explore::from_instance(self);
-        priv_.session.borrow().to_owned()
+        priv_
+            .session
+            .borrow()
+            .as_ref()
+            .and_then(|session| session.upgrade())
     }
 
     pub fn init(&self) {
@@ -189,7 +194,9 @@ impl Explore {
             priv_.public_room_list.replace(Some(public_room_list));
         }
 
-        priv_.session.replace(session);
+        priv_
+            .session
+            .replace(session.map(|session| session.downgrade()));
         self.notify("session");
     }
 
diff --git a/src/session/content/explore/public_room.rs b/src/session/content/explore/public_room.rs
index ecf79425..baf747d1 100644
--- a/src/session/content/explore/public_room.rs
+++ b/src/session/content/explore/public_room.rs
@@ -5,13 +5,14 @@ use crate::session::{room::Room, Avatar, Session};
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use glib::signal::SignalHandlerId;
     use once_cell::sync::{Lazy, OnceCell};
     use std::cell::{Cell, RefCell};
 
     #[derive(Debug, Default)]
     pub struct PublicRoom {
-        pub session: OnceCell<Session>,
+        pub session: OnceCell<WeakRef<Session>>,
         pub matrix_public_room: OnceCell<PublicRoomsChunk>,
         pub avatar: OnceCell<Avatar>,
         pub room: OnceCell<Room>,
@@ -72,7 +73,10 @@ mod imp {
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => self.session.set(value.get().unwrap()).unwrap(),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 _ => unimplemented!(),
             }
         }
@@ -90,7 +94,7 @@ mod imp {
         fn constructed(&self, obj: &Self::Type) {
             self.parent_constructed(obj);
 
-            self.avatar.set(Avatar::new(obj.session(), None)).unwrap();
+            self.avatar.set(Avatar::new(&obj.session(), None)).unwrap();
 
             obj.session()
                 .room_list()
@@ -120,9 +124,9 @@ impl PublicRoom {
         glib::Object::new(&[("session", session)]).expect("Failed to create Room")
     }
 
-    pub fn session(&self) -> &Session {
+    pub fn session(&self) -> Session {
         let priv_ = imp::PublicRoom::from_instance(self);
-        priv_.session.get().unwrap()
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     pub fn avatar(&self) -> &Avatar {
diff --git a/src/session/content/explore/public_room_list.rs b/src/session/content/explore/public_room_list.rs
index e94243de..7d530d1d 100644
--- a/src/session/content/explore/public_room_list.rs
+++ b/src/session/content/explore/public_room_list.rs
@@ -17,6 +17,7 @@ use matrix_sdk::ruma::{
 use std::convert::TryFrom;
 
 mod imp {
+    use glib::object::WeakRef;
     use once_cell::sync::Lazy;
     use std::cell::{Cell, RefCell};
 
@@ -32,7 +33,7 @@ mod imp {
         pub loading: Cell<bool>,
         pub request_sent: Cell<bool>,
         pub total_room_count_estimate: Cell<Option<u64>>,
-        pub session: RefCell<Option<Session>>,
+        pub session: RefCell<Option<WeakRef<Session>>>,
     }
 
     #[glib::object_subclass]
@@ -83,15 +84,13 @@ mod imp {
 
         fn set_property(
             &self,
-            _obj: &Self::Type,
+            obj: &Self::Type,
             _id: usize,
             value: &glib::Value,
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => {
-                    let _ = self.session.replace(value.get().unwrap());
-                }
+                "session" => obj.set_session(value.get().unwrap()),
                 _ => unimplemented!(),
             }
         }
@@ -136,7 +135,24 @@ impl PublicRoomList {
 
     pub fn session(&self) -> Option<Session> {
         let priv_ = imp::PublicRoomList::from_instance(self);
-        priv_.session.borrow().to_owned()
+        priv_
+            .session
+            .borrow()
+            .as_ref()
+            .and_then(|session| session.upgrade())
+    }
+
+    pub fn set_session(&self, session: Option<Session>) {
+        let priv_ = imp::PublicRoomList::from_instance(self);
+
+        if session == self.session() {
+            return;
+        }
+
+        priv_
+            .session
+            .replace(session.map(|session| session.downgrade()));
+        self.notify("session");
     }
 
     pub fn loading(&self) -> bool {
diff --git a/src/session/content/mod.rs b/src/session/content/mod.rs
index c1a08974..d07515ee 100644
--- a/src/session/content/mod.rs
+++ b/src/session/content/mod.rs
@@ -28,6 +28,7 @@ use crate::session::Session;
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use glib::{signal::SignalHandlerId, subclass::InitializingObject};
     use once_cell::sync::Lazy;
     use std::cell::{Cell, RefCell};
@@ -36,7 +37,7 @@ mod imp {
     #[template(resource = "/org/gnome/FractalNext/content.ui")]
     pub struct Content {
         pub compact: Cell<bool>,
-        pub session: RefCell<Option<Session>>,
+        pub session: RefCell<Option<WeakRef<Session>>>,
         pub room: RefCell<Option<Room>>,
         pub content_type: Cell<ContentType>,
         pub error_list: RefCell<Option<gio::ListStore>>,
@@ -134,9 +135,7 @@ mod imp {
                     let compact = value.get().unwrap();
                     self.compact.set(compact);
                 }
-                "session" => {
-                    let _ = self.session.replace(value.get().unwrap());
-                }
+                "session" => obj.set_session(value.get().unwrap()),
                 "room" => {
                     let room = value.get().unwrap();
                     obj.set_room(room);
@@ -177,7 +176,24 @@ impl Content {
 
     pub fn session(&self) -> Option<Session> {
         let priv_ = imp::Content::from_instance(self);
-        priv_.session.borrow().to_owned()
+        priv_
+            .session
+            .borrow()
+            .as_ref()
+            .and_then(|session| session.upgrade())
+    }
+
+    pub fn set_session(&self, session: Option<Session>) {
+        let priv_ = imp::Content::from_instance(self);
+
+        if session == self.session() {
+            return;
+        }
+
+        priv_
+            .session
+            .replace(session.map(|session| session.downgrade()));
+        self.notify("session");
     }
 
     pub fn content_type(&self) -> ContentType {
diff --git a/src/session/room/mod.rs b/src/session/room/mod.rs
index 175f42eb..91f285f6 100644
--- a/src/session/room/mod.rs
+++ b/src/session/room/mod.rs
@@ -61,6 +61,7 @@ use crate::RUNTIME;
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use glib::subclass::Signal;
     use once_cell::sync::{Lazy, OnceCell};
     use std::cell::Cell;
@@ -70,7 +71,7 @@ mod imp {
     pub struct Room {
         pub room_id: OnceCell<RoomId>,
         pub matrix_room: RefCell<Option<MatrixRoom>>,
-        pub session: OnceCell<Session>,
+        pub session: OnceCell<WeakRef<Session>>,
         pub name: RefCell<Option<String>>,
         pub avatar: OnceCell<Avatar>,
         pub category: Cell<RoomType>,
@@ -189,7 +190,10 @@ mod imp {
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => self.session.set(value.get().unwrap()).unwrap(),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 "display-name" => {
                     let room_name = value.get().unwrap();
                     obj.store_room_name(room_name)
@@ -252,7 +256,7 @@ mod imp {
             obj.set_matrix_room(obj.session().client().get_room(obj.room_id()).unwrap());
             self.timeline.set(Timeline::new(obj)).unwrap();
             self.avatar
-                .set(Avatar::new(obj.session(), obj.matrix_room().avatar_url()))
+                .set(Avatar::new(&obj.session(), obj.matrix_room().avatar_url()))
                 .unwrap();
 
             obj.load_power_levels();
@@ -278,9 +282,9 @@ impl Room {
             .expect("Failed to create Room")
     }
 
-    pub fn session(&self) -> &Session {
+    pub fn session(&self) -> Session {
         let priv_ = imp::Room::from_instance(self);
-        priv_.session.get().unwrap()
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     pub fn room_id(&self) -> &RoomId {
@@ -854,7 +858,8 @@ impl Room {
 
     /// Creates an expression that is true when the user is allowed the given action.
     pub fn new_allowed_expr(&self, room_action: RoomAction) -> gtk::Expression {
-        let user_id = self.session().user().unwrap().user_id();
+        let session = self.session();
+        let user_id = session.user().unwrap().user_id();
         let member = self.member_by_id(user_id);
         self.power_levels().new_allowed_expr(&member, room_action)
     }
diff --git a/src/session/room_creation/mod.rs b/src/session/room_creation/mod.rs
index d1115d78..7ab1f462 100644
--- a/src/session/room_creation/mod.rs
+++ b/src/session/room_creation/mod.rs
@@ -30,13 +30,14 @@ const MAX_BYTES: usize = 255;
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use glib::subclass::InitializingObject;
     use std::cell::RefCell;
 
     #[derive(Debug, Default, CompositeTemplate)]
     #[template(resource = "/org/gnome/FractalNext/room-creation.ui")]
     pub struct RoomCreation {
-        pub session: RefCell<Option<Session>>,
+        pub session: RefCell<Option<WeakRef<Session>>>,
         #[template_child]
         pub content: TemplateChild<gtk::ListBox>,
         #[template_child]
@@ -171,7 +172,11 @@ impl RoomCreation {
 
     pub fn session(&self) -> Option<Session> {
         let priv_ = imp::RoomCreation::from_instance(self);
-        priv_.session.borrow().clone()
+        priv_
+            .session
+            .borrow()
+            .as_ref()
+            .and_then(|session| session.upgrade())
     }
 
     fn set_session(&self, session: Option<Session>) {
@@ -187,7 +192,9 @@ impl RoomCreation {
                 .set_label(&[":", user.user_id().server_name().as_str()].concat());
         }
 
-        priv_.session.replace(session);
+        priv_
+            .session
+            .replace(session.map(|session| session.downgrade()));
         self.notify("session");
     }
 
diff --git a/src/session/room_list.rs b/src/session/room_list.rs
index ae860808..eae50a0a 100644
--- a/src/session/room_list.rs
+++ b/src/session/room_list.rs
@@ -16,6 +16,7 @@ use std::cell::Cell;
 use std::collections::HashSet;
 
 mod imp {
+    use glib::object::WeakRef;
     use glib::subclass::Signal;
     use once_cell::sync::{Lazy, OnceCell};
     use std::cell::RefCell;
@@ -26,7 +27,7 @@ mod imp {
     pub struct RoomList {
         pub list: RefCell<IndexMap<RoomId, Room>>,
         pub pending_rooms: RefCell<HashSet<RoomIdOrAliasId>>,
-        pub session: OnceCell<Session>,
+        pub session: OnceCell<WeakRef<Session>>,
     }
 
     #[glib::object_subclass]
@@ -60,7 +61,10 @@ mod imp {
             pspec: &glib::ParamSpec,
         ) {
             match pspec.name() {
-                "session" => self.session.set(value.get().unwrap()).unwrap(),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 _ => unimplemented!(),
             }
         }
@@ -119,9 +123,9 @@ impl RoomList {
         glib::Object::new(&[("session", session)]).expect("Failed to create RoomList")
     }
 
-    pub fn session(&self) -> &Session {
+    pub fn session(&self) -> Session {
         let priv_ = imp::RoomList::from_instance(self);
-        priv_.session.get().unwrap()
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     pub fn is_pending_room(&self, identifier: &RoomIdOrAliasId) -> bool {
@@ -244,7 +248,7 @@ impl RoomList {
                 let mut list = priv_.list.borrow_mut();
                 for matrix_room in matrix_rooms {
                     let room_id = matrix_room.room_id().to_owned();
-                    let room = Room::new(session, &room_id);
+                    let room = Room::new(&session, &room_id);
                     list.insert(room_id, room);
                 }
             }
@@ -266,7 +270,7 @@ impl RoomList {
                 .entry(room_id.clone())
                 .or_insert_with(|| {
                     added += 1;
-                    Room::new(session, &room_id)
+                    Room::new(&session, &room_id)
                 })
                 .clone();
 
@@ -281,7 +285,7 @@ impl RoomList {
                 .entry(room_id.clone())
                 .or_insert_with(|| {
                     added += 1;
-                    Room::new(session, &room_id)
+                    Room::new(&session, &room_id)
                 })
                 .clone();
 
@@ -296,7 +300,7 @@ impl RoomList {
                 .entry(room_id.clone())
                 .or_insert_with(|| {
                     added += 1;
-                    Room::new(session, &room_id)
+                    Room::new(&session, &room_id)
                 })
                 .clone();
 
diff --git a/src/session/user.rs b/src/session/user.rs
index 21c56b09..71e83bfa 100644
--- a/src/session/user.rs
+++ b/src/session/user.rs
@@ -5,6 +5,7 @@ use crate::session::{Avatar, Session};
 
 mod imp {
     use super::*;
+    use glib::object::WeakRef;
     use once_cell::sync::{Lazy, OnceCell};
     use std::{cell::RefCell, convert::TryInto};
 
@@ -12,7 +13,7 @@ mod imp {
     pub struct User {
         pub user_id: OnceCell<UserId>,
         pub display_name: RefCell<Option<String>>,
-        pub session: OnceCell<Session>,
+        pub session: OnceCell<WeakRef<Session>>,
         pub avatar: OnceCell<Avatar>,
     }
 
@@ -73,7 +74,10 @@ mod imp {
                     let user_id = value.get::<&str>().unwrap().try_into().unwrap();
                     self.user_id.set(user_id).unwrap();
                 }
-                "session" => self.session.set(value.get().unwrap()).unwrap(),
+                "session" => self
+                    .session
+                    .set(value.get::<Session>().unwrap().downgrade())
+                    .unwrap(),
                 _ => unimplemented!(),
             }
         }
@@ -91,7 +95,7 @@ mod imp {
         fn constructed(&self, obj: &Self::Type) {
             self.parent_constructed(obj);
 
-            let avatar = Avatar::new(obj.session(), None);
+            let avatar = Avatar::new(&obj.session(), None);
             self.avatar.set(avatar).unwrap();
 
             obj.bind_property("display-name", obj.avatar(), "display-name")
@@ -115,9 +119,9 @@ impl User {
 }
 
 pub trait UserExt: IsA<User> {
-    fn session(&self) -> &Session {
+    fn session(&self) -> Session {
         let priv_ = imp::User::from_instance(self.upcast_ref());
-        priv_.session.get().unwrap()
+        priv_.session.get().unwrap().upgrade().unwrap()
     }
 
     fn user_id(&self) -> &UserId {
diff --git a/src/window.rs b/src/window.rs
index 513a8e5c..538698b2 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -26,8 +26,6 @@ mod imp {
         #[template_child]
         pub sessions: TemplateChild<gtk::Stack>,
         #[template_child]
-        pub loading_page: TemplateChild<gtk::WindowHandle>,
-        #[template_child]
         pub error_list: TemplateChild<gio::ListStore>,
     }
 
@@ -66,7 +64,7 @@ mod imp {
 
             self.login
                 .connect_new_session(clone!(@weak obj => move |_login, session| {
-                    obj.add_session(session);
+                    obj.add_session(&session);
                     obj.switch_to_sessions_page();
                 }));
 
@@ -115,16 +113,11 @@ impl Window {
     fn restore_sessions(&self) {
         match secret::restore_sessions() {
             Ok(sessions) => {
-                let login = &imp::Window::from_instance(self).login.get();
-                let n = sessions.len();
                 for stored_session in sessions {
                     let session = Session::new();
-                    login.set_handler_for_prepared_session(&session);
                     session.login_with_previous_session(stored_session);
-                }
-
-                if n > 0 {
-                    self.switch_to_loading_page();
+                    self.add_session(&session);
+                    self.switch_to_sessions_page();
                 }
             }
             Err(error) => warn!("Failed to restore previous sessions: {:?}", error),
@@ -171,13 +164,6 @@ impl Window {
         priv_.main_stack.set_visible_child(&priv_.sessions.get());
     }
 
-    pub fn switch_to_loading_page(&self) {
-        let priv_ = imp::Window::from_instance(self);
-        priv_
-            .main_stack
-            .set_visible_child(&priv_.loading_page.get());
-    }
-
     pub fn switch_to_login_page(&self) {
         let priv_ = imp::Window::from_instance(self);
         priv_


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