diff --git a/src/session/model/mod.rs b/src/session/model/mod.rs index 3d34dcee..35e42a8a 100644 --- a/src/session/model/mod.rs +++ b/src/session/model/mod.rs @@ -3,7 +3,7 @@ mod notifications; mod room; mod room_list; mod session; -mod settings; +mod session_settings; mod sidebar; mod user; mod verification; @@ -19,7 +19,7 @@ pub use self::{ }, room_list::RoomList, session::{Session, SessionState}, - settings::SessionSettings, + session_settings::{SessionSettings, StoredSessionSettings}, sidebar::{ Category, CategoryType, IconItem, ItemList, ItemType, Selection, SidebarItem, SidebarItemImpl, SidebarListModel, diff --git a/src/session/model/session.rs b/src/session/model/session.rs index 23707fb3..d90ee2f8 100644 --- a/src/session/model/session.rs +++ b/src/session/model/session.rs @@ -42,6 +42,7 @@ use crate::{ matrix::{self, ClientSetupError}, TokioDrop, }, + Application, }; /// The state of the session. @@ -99,12 +100,22 @@ mod imp { glib::ParamSpecEnum::builder::("state") .read_only() .build(), + glib::ParamSpecObject::builder::("settings") + .construct_only() + .build(), ] }); PROPERTIES.as_ref() } + fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) { + match pspec.name() { + "settings" => self.settings.set(value.get().unwrap()).unwrap(), + _ => unimplemented!(), + } + } + fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value { let obj = self.obj(); @@ -113,6 +124,7 @@ mod imp { "user" => obj.user().to_value(), "offline" => obj.is_offline().to_value(), "state" => obj.state().to_value(), + "settings" => obj.settings().to_value(), _ => unimplemented!(), } } @@ -121,10 +133,6 @@ mod imp { self.parent_constructed(); let obj = self.obj(); - self.settings - .set(SessionSettings::new(obj.session_id())) - .unwrap(); - self.notifications.set_session(Some(&obj)); let monitor = gio::NetworkMonitor::default(); @@ -166,14 +174,22 @@ impl Session { /// Create a new session. pub async fn new(homeserver: Url, data: MatrixSession) -> Result { let stored_session = StoredSession::with_login_data(homeserver, data); + let settings = Application::default() + .session_list() + .settings() + .get_or_create(stored_session.id()); - Self::restore(stored_session).await + Self::restore(stored_session, settings).await } /// Restore a stored session. - pub async fn restore(stored_session: StoredSession) -> Result { + pub async fn restore( + stored_session: StoredSession, + settings: SessionSettings, + ) -> Result { let obj = glib::Object::builder::() .property("info", BoxedStoredSession(stored_session.clone())) + .property("settings", settings) .build(); let client = diff --git a/src/session/model/settings.rs b/src/session/model/session_settings.rs similarity index 67% rename from src/session/model/settings.rs rename to src/session/model/session_settings.rs index 064775f5..f6d8dfd6 100644 --- a/src/session/model/settings.rs +++ b/src/session/model/session_settings.rs @@ -1,7 +1,5 @@ use gtk::{glib, prelude::*, subclass::prelude::*}; -use indexmap::IndexMap; use serde::{Deserialize, Serialize}; -use tracing::error; use crate::Application; @@ -28,6 +26,10 @@ impl Default for StoredSessionSettings { } } +#[derive(Clone, Debug, glib::Boxed)] +#[boxed_type(name = "BoxedStoredSessionSettings")] +pub struct BoxedStoredSessionSettings(StoredSessionSettings); + mod imp { use std::cell::RefCell; @@ -56,6 +58,10 @@ mod imp { glib::ParamSpecString::builder("session-id") .construct_only() .build(), + glib::ParamSpecBoxed::builder::("stored-settings") + .write_only() + .construct_only() + .build(), glib::ParamSpecBoolean::builder("notifications-enabled") .default_value(true) .explicit_notify() @@ -70,7 +76,11 @@ mod imp { let obj = self.obj(); match pspec.name() { - "session-id" => obj.set_session_id(value.get().ok()), + "session-id" => self.session_id.set(value.get().unwrap()).unwrap(), + "stored-settings" => { + self.stored_settings + .replace(value.get::().unwrap().0); + } "notifications-enabled" => obj.set_notifications_enabled(value.get().unwrap()), _ => unimplemented!(), } @@ -101,34 +111,34 @@ impl SessionSettings { .build() } - /// Save the settings in the GSettings. - fn save(&self) { - let mut sessions = sessions(); - let stored_settings = self.imp().stored_settings.borrow().clone(); + /// Restore existing `SessionSettings` with the given session ID and stored + /// settings. + pub fn restore(session_id: &str, stored_settings: StoredSessionSettings) -> Self { + glib::Object::builder() + .property("session-id", session_id) + .property( + "stored-settings", + &BoxedStoredSessionSettings(stored_settings), + ) + .build() + } - sessions.insert(self.session_id().to_owned(), stored_settings); - let sessions = sessions.into_iter().collect::>(); + /// The stored settings. + pub fn stored_settings(&self) -> StoredSessionSettings { + self.imp().stored_settings.borrow().clone() + } - if let Err(error) = Application::default() - .settings() - .set_string("sessions", &serde_json::to_string(&sessions).unwrap()) - { - error!("Failed to save session settings: {error}"); - } + /// Save the settings in the GSettings. + fn save(&self) { + Application::default().session_list().settings().save(); } /// Delete the settings from the GSettings. pub fn delete(&self) { - let mut sessions = sessions(); - - sessions.remove(self.session_id()); - - if let Err(error) = Application::default() + Application::default() + .session_list() .settings() - .set_string("sessions", &serde_json::to_string(&sessions).unwrap()) - { - error!("Failed to delete session settings: {error}"); - } + .remove(self.session_id()); } /// The ID of the session these settings are for. @@ -136,28 +146,6 @@ impl SessionSettings { self.imp().session_id.get().unwrap() } - /// Set the ID of the session these settings are for. - fn set_session_id(&self, session_id: Option) { - let session_id = match session_id { - Some(s) => s, - None => return, - }; - - let imp = self.imp(); - imp.session_id.set(session_id.clone()).unwrap(); - - if let Some(session_settings) = sessions() - .into_iter() - .find_map(|(s_id, session)| (s_id == session_id).then_some(session)) - { - // Restore the settings. - imp.stored_settings.replace(session_settings); - } else { - // This is a new session, add it to the list of sessions. - self.save(); - } - } - pub fn explore_custom_servers(&self) -> Vec { self.imp() .stored_settings @@ -197,16 +185,3 @@ impl SessionSettings { self.notify("notifications-enabled"); } } - -/// Get map of session stored in the GSettings. -fn sessions() -> IndexMap { - let serialized = Application::default().settings().string("sessions"); - - match serde_json::from_str::>(&serialized) { - Ok(stored_settings) => stored_settings.into_iter().collect(), - Err(error) => { - error!("Failed to load profile settings, fallback to default settings: {error}"); - Default::default() - } - } -} diff --git a/src/session_list/mod.rs b/src/session_list/mod.rs index b7e87615..ecc95f36 100644 --- a/src/session_list/mod.rs +++ b/src/session_list/mod.rs @@ -6,8 +6,9 @@ use tracing::{error, info, warn}; mod failed_session; mod new_session; mod session_info; +mod session_list_settings; -pub use self::{failed_session::*, new_session::*, session_info::*}; +pub use self::{failed_session::*, new_session::*, session_info::*, session_list_settings::*}; use crate::{ prelude::*, secret::{self, StoredSession}, @@ -31,6 +32,8 @@ mod imp { pub error: RefCell>, /// A map of session ID to session. pub list: RefCell>, + /// The settings of the sessions. + pub settings: SessionListSettings, } #[glib::object_subclass] @@ -126,6 +129,11 @@ impl SessionList { self.notify("error"); } + /// The settings of the sessions. + pub fn settings(&self) -> &SessionListSettings { + &self.imp().settings + } + /// Whether this list is empty. pub fn is_empty(&self) -> bool { self.imp().list.borrow().is_empty() @@ -226,6 +234,8 @@ impl SessionList { let handle = spawn_tokio!(secret::restore_sessions()); match handle.await.unwrap() { Ok(sessions) => { + self.settings().load(); + for stored_session in sessions { info!( "Restoring previous session for user: {}", @@ -263,7 +273,8 @@ impl SessionList { /// Restore a stored session. async fn restore_stored_session(&self, session_info: StoredSession) { - match Session::restore(session_info.clone()).await { + let settings = self.settings().get_or_create(session_info.id()); + match Session::restore(session_info.clone(), settings).await { Ok(session) => { session.prepare().await; self.insert(session); diff --git a/src/session_list/session_list_settings.rs b/src/session_list/session_list_settings.rs new file mode 100644 index 00000000..e646c3a6 --- /dev/null +++ b/src/session_list/session_list_settings.rs @@ -0,0 +1,114 @@ +use gtk::{glib, prelude::*, subclass::prelude::*}; +use indexmap::IndexMap; +use tracing::error; + +use crate::{ + session::model::{SessionSettings, StoredSessionSettings}, + Application, +}; + +mod imp { + use std::cell::RefCell; + + use super::*; + use crate::session::model::SessionSettings; + + #[derive(Debug, Default)] + pub struct SessionListSettings { + /// The settings of the sessions. + pub sessions: RefCell>, + } + + #[glib::object_subclass] + impl ObjectSubclass for SessionListSettings { + const NAME: &'static str = "SessionListSettings"; + type Type = super::SessionListSettings; + } + + impl ObjectImpl for SessionListSettings {} +} + +glib::wrapper! { + /// The settings of the list of sessions. + pub struct SessionListSettings(ObjectSubclass); +} + +impl SessionListSettings { + /// Create a new `SessionListSettings`. + pub fn new() -> Self { + glib::Object::new() + } + + /// Load these settings from the GSettings. + pub fn load(&self) { + let serialized = Application::default().settings().string("sessions"); + + let stored_sessions = + match serde_json::from_str::>(&serialized) { + Ok(stored_sessions) => stored_sessions, + Err(error) => { + error!( + "Failed to load sessions settings, fallback to default settings: {error}" + ); + Default::default() + } + }; + + let sessions = stored_sessions + .into_iter() + .map(|(session_id, stored_session)| { + let session = SessionSettings::restore(&session_id, stored_session); + (session_id, session) + }) + .collect(); + + self.imp().sessions.replace(sessions); + } + + /// Save the settings in the GSettings. + pub fn save(&self) { + let stored_sessions = self + .imp() + .sessions + .borrow() + .iter() + .map(|(session_id, session)| (session_id.to_owned(), session.stored_settings())) + .collect::>(); + + if let Err(error) = Application::default().settings().set_string( + "sessions", + &serde_json::to_string(&stored_sessions).unwrap(), + ) { + error!("Failed to save sessions settings: {error}"); + } + } + + /// Get or create the settings for the session with the given ID. + pub fn get_or_create(&self, session_id: &str) -> SessionSettings { + let sessions = &self.imp().sessions; + + if let Some(session) = sessions.borrow().get(session_id) { + return session.clone(); + }; + + let session = SessionSettings::new(session_id); + sessions + .borrow_mut() + .insert(session_id.to_owned(), session.clone()); + self.save(); + + session + } + + /// Remove the settings of the session with the given ID. + pub fn remove(&self, session_id: &str) { + self.imp().sessions.borrow_mut().remove(session_id); + self.save(); + } +} + +impl Default for SessionListSettings { + fn default() -> Self { + Self::new() + } +}