session: Add struct to manage notifications settings
This commit is contained in:
parent
e510f98b86
commit
59262cc141
|
@ -10,7 +10,7 @@ mod verification;
|
|||
|
||||
pub use self::{
|
||||
avatar_data::{AvatarData, AvatarImage, AvatarUriSource},
|
||||
notifications::Notifications,
|
||||
notifications::{Notifications, NotificationsSettings},
|
||||
room::{
|
||||
Event, EventKey, HighlightFlags, Member, MemberList, MemberRole, Membership, MessageState,
|
||||
PowerLevel, ReactionGroup, ReactionList, Room, RoomType, Timeline, TimelineItem,
|
||||
|
|
|
@ -10,6 +10,9 @@ use ruma::{
|
|||
};
|
||||
use tracing::{debug, error, warn};
|
||||
|
||||
mod notifications_settings;
|
||||
|
||||
pub use self::notifications_settings::NotificationsSettings;
|
||||
use super::{Room, Session};
|
||||
use crate::{
|
||||
application::AppShowRoomPayload, prelude::*, spawn, spawn_tokio, utils::matrix::get_event_body,
|
||||
|
@ -19,17 +22,20 @@ use crate::{
|
|||
mod imp {
|
||||
use std::{cell::RefCell, collections::HashMap};
|
||||
|
||||
use glib::WeakRef;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::Notifications)]
|
||||
pub struct Notifications {
|
||||
pub session: WeakRef<Session>,
|
||||
/// The current session.
|
||||
#[property(get, set = Self::set_session, explicit_notify, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
/// A map of room ID to list of event IDs for which a notification was
|
||||
/// sent to the system.
|
||||
pub list: RefCell<HashMap<OwnedRoomId, Vec<OwnedEventId>>>,
|
||||
/// The notifications settings for this session.
|
||||
#[property(get)]
|
||||
pub settings: NotificationsSettings,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -38,29 +44,20 @@ mod imp {
|
|||
type Type = super::Notifications;
|
||||
}
|
||||
|
||||
impl ObjectImpl for Notifications {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build()]
|
||||
});
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for Notifications {}
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().set_session(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
impl Notifications {
|
||||
/// Set the current session.
|
||||
fn set_session(&self, session: Option<&Session>) {
|
||||
if self.session.upgrade().as_ref() == session {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"session" => self.obj().session().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
self.session.set(session);
|
||||
self.obj().notify_session();
|
||||
|
||||
self.settings.set_session(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,23 +72,6 @@ impl Notifications {
|
|||
glib::Object::new()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<&Session>) {
|
||||
let imp = self.imp();
|
||||
|
||||
if self.session().as_ref() == session {
|
||||
return;
|
||||
}
|
||||
|
||||
imp.session.set(session);
|
||||
self.notify("session");
|
||||
}
|
||||
|
||||
/// Ask the system to show the given notification, if applicable.
|
||||
///
|
||||
/// The notification won't be shown if the application is active and this
|
|
@ -0,0 +1,232 @@
|
|||
use futures_util::StreamExt;
|
||||
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::{
|
||||
notification_settings::NotificationSettings as MatrixNotificationSettings,
|
||||
NotificationSettingsError,
|
||||
};
|
||||
use ruma::push::{PredefinedOverrideRuleId, RuleKind};
|
||||
use tokio::sync::broadcast::error::RecvError;
|
||||
use tracing::{error, warn};
|
||||
|
||||
use crate::{
|
||||
session::model::{Session, SessionState},
|
||||
spawn, spawn_tokio,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, glib::Properties)]
|
||||
#[properties(wrapper_type = super::NotificationsSettings)]
|
||||
pub struct NotificationsSettings {
|
||||
/// The parent `Session`.
|
||||
#[property(get, set = Self::set_session, explicit_notify, nullable)]
|
||||
pub session: glib::WeakRef<Session>,
|
||||
/// The SDK notification settings API.
|
||||
pub api: RefCell<Option<MatrixNotificationSettings>>,
|
||||
/// Whether notifications are enabled for this Matrix account.
|
||||
#[property(get)]
|
||||
pub account_enabled: Cell<bool>,
|
||||
/// Whether notifications are enabled for this session.
|
||||
#[property(get, set = Self::set_session_enabled, explicit_notify)]
|
||||
pub session_enabled: Cell<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for NotificationsSettings {
|
||||
const NAME: &'static str = "NotificationsSettings";
|
||||
type Type = super::NotificationsSettings;
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for NotificationsSettings {}
|
||||
|
||||
impl NotificationsSettings {
|
||||
/// Set the parent `Session`.
|
||||
fn set_session(&self, session: Option<&Session>) {
|
||||
if self.session.upgrade().as_ref() == session {
|
||||
return;
|
||||
}
|
||||
|
||||
let obj = self.obj();
|
||||
|
||||
if let Some(session) = session {
|
||||
session
|
||||
.settings()
|
||||
.bind_property("notifications-enabled", &*obj, "session-enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.build();
|
||||
}
|
||||
|
||||
self.session.set(session);
|
||||
obj.notify_session();
|
||||
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.init_api().await;
|
||||
}));
|
||||
}
|
||||
|
||||
/// Set whether notifications are enabled for this session.
|
||||
fn set_session_enabled(&self, enabled: bool) {
|
||||
if self.session_enabled.get() == enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
if let Some(session) = self.session.upgrade() {
|
||||
session.notifications().clear();
|
||||
}
|
||||
}
|
||||
|
||||
self.session_enabled.set(enabled);
|
||||
self.obj().notify_session_enabled();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// The notifications settings of a `Session`.
|
||||
pub struct NotificationsSettings(ObjectSubclass<imp::NotificationsSettings>);
|
||||
}
|
||||
|
||||
impl NotificationsSettings {
|
||||
/// Create a new `NotificationsSettings`.
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
/// The SDK notification settings API.
|
||||
fn api(&self) -> Option<MatrixNotificationSettings> {
|
||||
self.imp().api.borrow().clone()
|
||||
}
|
||||
|
||||
/// Initialize the SDK notification settings API.
|
||||
async fn init_api(&self) {
|
||||
let Some(session) = self.session() else {
|
||||
self.imp().api.take();
|
||||
return;
|
||||
};
|
||||
|
||||
// If the session is not ready, there is no client so let's wait to initialize
|
||||
// the API.
|
||||
if session.state() != SessionState::Ready {
|
||||
self.imp().api.take();
|
||||
|
||||
session.connect_ready(clone!(@weak self as obj => move |_| {
|
||||
spawn!(clone!(@weak obj => async move {
|
||||
obj.init_api().await;
|
||||
}));
|
||||
}));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let client = session.client();
|
||||
let api = spawn_tokio!(async move { client.notification_settings().await })
|
||||
.await
|
||||
.unwrap();
|
||||
let mut api_receiver = api.subscribe_to_changes();
|
||||
|
||||
self.imp().api.replace(Some(api.clone()));
|
||||
|
||||
let (mut sender, mut receiver) = futures_channel::mpsc::channel(10);
|
||||
spawn_tokio!(async move {
|
||||
loop {
|
||||
match api_receiver.recv().await {
|
||||
Ok(()) => {
|
||||
if let Err(error) = sender.try_send(()) {
|
||||
error!("Error sending notifications settings change: {error}");
|
||||
panic!();
|
||||
}
|
||||
}
|
||||
Err(RecvError::Closed) => {
|
||||
break;
|
||||
}
|
||||
Err(RecvError::Lagged(_)) => {
|
||||
warn!("Some notifications settings changes were dropped");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
spawn!(clone!(@weak self as obj => async move { obj.update().await; }));
|
||||
|
||||
while let Some(()) = receiver.next().await {
|
||||
spawn!(clone!(@weak self as obj => async move { obj.update().await; }));
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the notification settings from the SDK API.
|
||||
async fn update(&self) {
|
||||
let Some(api) = self.api() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let api_clone = api.clone();
|
||||
let handle = spawn_tokio!(async move {
|
||||
api_clone
|
||||
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Master)
|
||||
.await
|
||||
});
|
||||
|
||||
let account_enabled = match handle.await.unwrap() {
|
||||
// The rule disables notifications, so we need to invert the boolean.
|
||||
Ok(enabled) => !enabled,
|
||||
Err(error) => {
|
||||
error!("Failed to get account notifications setting: {error}");
|
||||
true
|
||||
}
|
||||
};
|
||||
self.set_account_enabled_inner(account_enabled);
|
||||
}
|
||||
|
||||
/// Set whether notifications are enabled for this session.
|
||||
pub async fn set_account_enabled(
|
||||
&self,
|
||||
enabled: bool,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let Some(api) = self.api() else {
|
||||
error!("Cannot update notifications settings when API is not initialized");
|
||||
return Err(NotificationSettingsError::UnableToUpdatePushRule);
|
||||
};
|
||||
|
||||
let handle = spawn_tokio!(async move {
|
||||
api.set_push_rule_enabled(
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::Master,
|
||||
// The rule disables notifications, so we need to invert the boolean.
|
||||
!enabled,
|
||||
)
|
||||
.await
|
||||
});
|
||||
|
||||
match handle.await.unwrap() {
|
||||
Ok(()) => {
|
||||
self.set_account_enabled_inner(enabled);
|
||||
Ok(())
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Failed to change account notifications setting: {error}");
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_account_enabled_inner(&self, enabled: bool) {
|
||||
if self.account_enabled() == enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().account_enabled.set(enabled);
|
||||
self.notify_account_enabled();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NotificationsSettings {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
|
@ -90,6 +90,7 @@ mod imp {
|
|||
#[property(get, construct_only)]
|
||||
pub settings: OnceCell<SessionSettings>,
|
||||
/// The notifications API for this session.
|
||||
#[property(get)]
|
||||
pub notifications: Notifications,
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,7 @@ mod imp {
|
|||
self.parent_constructed();
|
||||
let obj = self.obj();
|
||||
|
||||
self.notifications.set_session(Some(&obj));
|
||||
self.notifications.set_session(Some(obj.clone()));
|
||||
|
||||
let monitor = gio::NetworkMonitor::default();
|
||||
let handler_id = monitor.connect_network_changed(clone!(@weak obj => move |_, _| {
|
||||
|
@ -530,9 +531,4 @@ impl Session {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// The notifications API of this session.
|
||||
pub fn notifications(&self) -> &Notifications {
|
||||
&self.imp().notifications
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,13 @@
|
|||
</child>
|
||||
<child>
|
||||
<object class="NotificationsPage">
|
||||
<property name="session" bind-source="AccountSettings" bind-property="session" bind-flags="sync-create"/>
|
||||
<binding name="notifications-settings">
|
||||
<lookup name="settings">
|
||||
<lookup name="notifications">
|
||||
<lookup name="session">AccountSettings</lookup>
|
||||
</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
|
|
@ -1,40 +1,35 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{glib, glib::clone, CompositeTemplate};
|
||||
use matrix_sdk::event_handler::EventHandlerDropGuard;
|
||||
use ruma::{
|
||||
api::client::push::{set_pushrule_enabled, RuleKind, RuleScope},
|
||||
events::push_rules::{PushRulesEvent, PushRulesEventContent},
|
||||
push::{PredefinedOverrideRuleId, Ruleset},
|
||||
};
|
||||
use tracing::{error, warn};
|
||||
|
||||
use crate::{prelude::*, session::model::Session, spawn, spawn_tokio, toast};
|
||||
use crate::{
|
||||
components::Spinner, session::model::NotificationsSettings, spawn, toast,
|
||||
utils::BoundObjectWeakRef,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::cell::Cell;
|
||||
|
||||
use glib::{subclass::InitializingObject, WeakRef};
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate)]
|
||||
#[derive(Debug, Default, CompositeTemplate, glib::Properties)]
|
||||
#[template(
|
||||
resource = "/org/gnome/Fractal/ui/session/view/account_settings/notifications_page.ui"
|
||||
)]
|
||||
#[properties(wrapper_type = super::NotificationsPage)]
|
||||
pub struct NotificationsPage {
|
||||
/// The current session.
|
||||
pub session: WeakRef<Session>,
|
||||
/// Binding to the session settings `notifications-enabled` property.
|
||||
pub settings_binding: RefCell<Option<glib::Binding>>,
|
||||
/// The guard of the event handler for push rules changes.
|
||||
pub event_handler_guard: RefCell<Option<EventHandlerDropGuard>>,
|
||||
/// Whether notifications are enabled for this account.
|
||||
pub account_enabled: Cell<bool>,
|
||||
/// Whether an account notifications change is being processed.
|
||||
#[template_child]
|
||||
pub account_switch: TemplateChild<gtk::Switch>,
|
||||
#[template_child]
|
||||
pub session_row: TemplateChild<adw::SwitchRow>,
|
||||
/// The notifications settings of the current session.
|
||||
#[property(get, set = Self::set_notifications_settings, explicit_notify)]
|
||||
pub notifications_settings: BoundObjectWeakRef<NotificationsSettings>,
|
||||
/// Whether the account section is busy.
|
||||
#[property(get)]
|
||||
pub account_loading: Cell<bool>,
|
||||
/// Whether notifications are enabled for this session.
|
||||
pub session_enabled: Cell<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -44,7 +39,10 @@ mod imp {
|
|||
type ParentType = adw::PreferencesPage;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Spinner::static_type();
|
||||
|
||||
Self::bind_template(klass);
|
||||
Self::Type::bind_template_callbacks(klass);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
|
@ -52,289 +50,130 @@ mod imp {
|
|||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for NotificationsPage {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
use once_cell::sync::Lazy;
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
glib::ParamSpecObject::builder::<Session>("session")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("account-enabled")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("account-loading")
|
||||
.read_only()
|
||||
.build(),
|
||||
glib::ParamSpecBoolean::builder("session-enabled")
|
||||
.explicit_notify()
|
||||
.build(),
|
||||
]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.set_session(value.get().unwrap()),
|
||||
"account-enabled" => obj.sync_account_enabled(value.get().unwrap()),
|
||||
"session-enabled" => obj.set_session_enabled(value.get().unwrap()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
let obj = self.obj();
|
||||
|
||||
match pspec.name() {
|
||||
"session" => obj.session().to_value(),
|
||||
"account-enabled" => obj.account_enabled().to_value(),
|
||||
"account-loading" => obj.account_loading().to_value(),
|
||||
"session-enabled" => obj.session_enabled().to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for NotificationsPage {}
|
||||
|
||||
impl WidgetImpl for NotificationsPage {}
|
||||
impl PreferencesPageImpl for NotificationsPage {}
|
||||
|
||||
impl NotificationsPage {
|
||||
/// Set the notifications settings of the current session.
|
||||
fn set_notifications_settings(
|
||||
&self,
|
||||
notifications_settings: Option<&NotificationsSettings>,
|
||||
) {
|
||||
if self.notifications_settings.obj().as_ref() == notifications_settings {
|
||||
return;
|
||||
}
|
||||
let obj = self.obj();
|
||||
|
||||
self.notifications_settings.disconnect_signals();
|
||||
|
||||
if let Some(settings) = notifications_settings {
|
||||
let account_enabled_handler =
|
||||
settings.connect_account_enabled_notify(clone!(@weak obj => move |_| {
|
||||
obj.update_account();
|
||||
}));
|
||||
let session_enabled_handler =
|
||||
settings.connect_session_enabled_notify(clone!(@weak obj => move |_| {
|
||||
obj.update_session();
|
||||
}));
|
||||
|
||||
self.notifications_settings.set(
|
||||
settings,
|
||||
vec![account_enabled_handler, session_enabled_handler],
|
||||
);
|
||||
}
|
||||
|
||||
obj.update_account();
|
||||
obj.update_session();
|
||||
obj.notify_notifications_settings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
/// Preferences page to edit notification settings.
|
||||
/// Preferences page to edit global notification settings.
|
||||
pub struct NotificationsPage(ObjectSubclass<imp::NotificationsPage>)
|
||||
@extends gtk::Widget, adw::PreferencesPage, @implements gtk::Accessible;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl NotificationsPage {
|
||||
pub fn new(session: &Session) -> Self {
|
||||
glib::Object::builder().property("session", session).build()
|
||||
pub fn new(notifications_settings: &NotificationsSettings) -> Self {
|
||||
glib::Object::builder()
|
||||
.property("notifications-settings", notifications_settings)
|
||||
.build()
|
||||
}
|
||||
|
||||
/// The current session.
|
||||
pub fn session(&self) -> Option<Session> {
|
||||
self.imp().session.upgrade()
|
||||
}
|
||||
|
||||
/// Set the current session.
|
||||
pub fn set_session(&self, session: Option<Session>) {
|
||||
let prev_session = self.session();
|
||||
if prev_session == session {
|
||||
/// Update the section about the account.
|
||||
fn update_account(&self) {
|
||||
let Some(settings) = self.notifications_settings() else {
|
||||
return;
|
||||
}
|
||||
|
||||
};
|
||||
let imp = self.imp();
|
||||
if let Some(binding) = imp.settings_binding.take() {
|
||||
binding.unbind();
|
||||
}
|
||||
imp.event_handler_guard.take();
|
||||
|
||||
if let Some(session) = &session {
|
||||
let binding = session
|
||||
.settings()
|
||||
.bind_property("notifications-enabled", self, "session-enabled")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.build();
|
||||
imp.settings_binding.replace(Some(binding));
|
||||
}
|
||||
imp.account_switch.set_active(settings.account_enabled());
|
||||
imp.account_switch.set_sensitive(!self.account_loading());
|
||||
|
||||
imp.session.set(session.as_ref());
|
||||
self.notify("session");
|
||||
|
||||
spawn!(
|
||||
glib::Priority::DEFAULT_IDLE,
|
||||
clone!(@weak self as obj => async move {
|
||||
obj.init_page().await;
|
||||
})
|
||||
);
|
||||
// Other sessions will be disabled or not.
|
||||
self.update_session();
|
||||
}
|
||||
|
||||
/// Initialize the page.
|
||||
async fn init_page(&self) {
|
||||
let session = match self.session() {
|
||||
Some(session) => session,
|
||||
None => return,
|
||||
/// Update the section about the session.
|
||||
fn update_session(&self) {
|
||||
let Some(settings) = self.notifications_settings() else {
|
||||
return;
|
||||
};
|
||||
let imp = self.imp();
|
||||
|
||||
let client = session.client();
|
||||
let account = client.account();
|
||||
let handle =
|
||||
spawn_tokio!(async move { account.account_data::<PushRulesEventContent>().await });
|
||||
|
||||
match handle.await.unwrap() {
|
||||
Ok(Some(pushrules)) => match pushrules.deserialize() {
|
||||
Ok(pushrules) => {
|
||||
self.update_page(pushrules.global);
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Could not deserialize push rules: {error}");
|
||||
toast!(
|
||||
self,
|
||||
gettext("Could not load notifications settings. Try again later.")
|
||||
);
|
||||
}
|
||||
},
|
||||
Ok(None) => {
|
||||
warn!("Could not find push rules, using the default ruleset instead.");
|
||||
let user_id = session.user_id();
|
||||
self.update_page(Ruleset::server_default(user_id));
|
||||
}
|
||||
Err(error) => {
|
||||
error!("Could not get push rules: {error}");
|
||||
toast!(
|
||||
self,
|
||||
gettext("Could not load notifications settings. Try again later.")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let obj_weak = glib::SendWeakRef::from(self.downgrade());
|
||||
let handler = client.add_event_handler(move |event: PushRulesEvent| {
|
||||
let obj_weak = obj_weak.clone();
|
||||
async move {
|
||||
let ctx = glib::MainContext::default();
|
||||
ctx.spawn(async move {
|
||||
if let Some(obj) = obj_weak.upgrade() {
|
||||
obj.update_page(event.content.global)
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
self.imp()
|
||||
.event_handler_guard
|
||||
.replace(Some(client.event_handler_drop_guard(handler)));
|
||||
imp.session_row.set_active(settings.session_enabled());
|
||||
imp.session_row.set_sensitive(settings.account_enabled());
|
||||
}
|
||||
|
||||
/// Update the page for the given ruleset.
|
||||
fn update_page(&self, rules: Ruleset) {
|
||||
let account_enabled = if let Some(rule) = rules
|
||||
.override_
|
||||
.iter()
|
||||
.find(|r| r.rule_id == PredefinedOverrideRuleId::Master.as_str())
|
||||
{
|
||||
!rule.enabled
|
||||
} else {
|
||||
warn!("Could not find `.m.rule.master` push rule, using the default rule instead.");
|
||||
true
|
||||
fn set_account_loading(&self, loading: bool) {
|
||||
self.imp().account_loading.set(loading);
|
||||
self.notify_account_loading();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn account_switched(&self) {
|
||||
let Some(settings) = self.notifications_settings() else {
|
||||
return;
|
||||
};
|
||||
self.set_account_enabled(account_enabled);
|
||||
}
|
||||
let imp = self.imp();
|
||||
|
||||
/// Whether notifications are enabled for this account.
|
||||
pub fn account_enabled(&self) -> bool {
|
||||
self.imp().account_enabled.get()
|
||||
}
|
||||
|
||||
/// Set whether notifications are enabled for this account.
|
||||
///
|
||||
/// This only sets the property locally.
|
||||
fn set_account_enabled(&self, enabled: bool) {
|
||||
if self.account_enabled() == enabled {
|
||||
let enabled = imp.account_switch.is_active();
|
||||
if enabled == settings.account_enabled() {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
if let Some(session) = self.session() {
|
||||
session.notifications().clear();
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().account_enabled.set(enabled);
|
||||
self.notify("account-enabled");
|
||||
}
|
||||
|
||||
/// Sync whether notifications are enabled for this account.
|
||||
///
|
||||
/// This sets the property locally and synchronizes the change with the
|
||||
/// homeserver.
|
||||
pub fn sync_account_enabled(&self, enabled: bool) {
|
||||
self.set_account_enabled(enabled);
|
||||
|
||||
imp.account_switch.set_sensitive(false);
|
||||
self.set_account_loading(true);
|
||||
|
||||
spawn!(clone!(@weak self as obj => async move {
|
||||
obj.send_account_enabled(enabled).await;
|
||||
}));
|
||||
}
|
||||
|
||||
/// Send whether notifications are enabled for this account.
|
||||
///
|
||||
/// This only changes the setting on the homeserver.
|
||||
async fn send_account_enabled(&self, enabled: bool) {
|
||||
let client = match self.session() {
|
||||
Some(session) => session.client(),
|
||||
None => return,
|
||||
};
|
||||
|
||||
let request = set_pushrule_enabled::v3::Request::new(
|
||||
RuleScope::Global,
|
||||
RuleKind::Override,
|
||||
PredefinedOverrideRuleId::Master.to_string(),
|
||||
!enabled,
|
||||
);
|
||||
|
||||
let handle = spawn_tokio!(async move { client.send(request, None).await });
|
||||
|
||||
match handle.await.unwrap() {
|
||||
Ok(_) => {}
|
||||
Err(error) => {
|
||||
error!(
|
||||
"Could not update `{}` push rule: {error}",
|
||||
PredefinedOverrideRuleId::Master
|
||||
);
|
||||
|
||||
spawn!(clone!(@weak self as obj, @weak settings => async move {
|
||||
if settings.set_account_enabled(enabled).await.is_err() {
|
||||
let msg = if enabled {
|
||||
gettext("Could not enable account notifications")
|
||||
} else {
|
||||
gettext("Could not disable account notifications")
|
||||
};
|
||||
toast!(self, msg);
|
||||
|
||||
// Revert the local change.
|
||||
self.set_account_enabled(!enabled);
|
||||
toast!(obj, msg);
|
||||
}
|
||||
}
|
||||
|
||||
self.set_account_loading(false);
|
||||
obj.set_account_loading(false);
|
||||
obj.update_account();
|
||||
}));
|
||||
}
|
||||
|
||||
/// Whether an account notifications change is being processed.
|
||||
pub fn account_loading(&self) -> bool {
|
||||
self.imp().account_loading.get()
|
||||
}
|
||||
|
||||
/// Set whether an account notifications change is being processed.
|
||||
fn set_account_loading(&self, loading: bool) {
|
||||
if self.account_loading() == loading {
|
||||
#[template_callback]
|
||||
fn session_switched(&self) {
|
||||
let Some(settings) = self.notifications_settings() else {
|
||||
return;
|
||||
}
|
||||
};
|
||||
let imp = self.imp();
|
||||
|
||||
self.imp().account_loading.set(loading);
|
||||
self.notify("account-loading");
|
||||
}
|
||||
|
||||
/// Whether notifications are enabled for this session.
|
||||
pub fn session_enabled(&self) -> bool {
|
||||
self.imp().session_enabled.get()
|
||||
}
|
||||
|
||||
/// Set whether notifications are enabled for this session.
|
||||
pub fn set_session_enabled(&self, enabled: bool) {
|
||||
if self.session_enabled() == enabled {
|
||||
return;
|
||||
}
|
||||
|
||||
if !enabled {
|
||||
if let Some(session) = self.session() {
|
||||
session.notifications().clear();
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().session_enabled.set(enabled);
|
||||
self.notify("session-enabled");
|
||||
settings.set_session_enabled(imp.session_row.is_active());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,20 +9,19 @@
|
|||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">Enable for this account</property>
|
||||
<property name="activatable-widget">account_switch</property>
|
||||
<child type="suffix">
|
||||
<object class="GtkBox">
|
||||
<property name="valign">center</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkSpinner">
|
||||
<object class="Spinner">
|
||||
<property name="visible" bind-source="NotificationsPage" bind-property="account-loading" bind-flags="sync-create"/>
|
||||
<property name="spinning" bind-source="NotificationsPage" bind-property="account-loading" bind-flags="sync-create"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkSwitch">
|
||||
<property name="active" bind-source="NotificationsPage" bind-property="account-enabled" bind-flags="sync-create | bidirectional"/>
|
||||
<property name="sensitive" bind-source="NotificationsPage" bind-property="account-loading" bind-flags="sync-create | invert-boolean"/>
|
||||
<object class="GtkSwitch" id="account_switch">
|
||||
<signal name="notify::active" handler="account_switched" swapped="true"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@ -30,10 +29,9 @@
|
|||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwSwitchRow">
|
||||
<object class="AdwSwitchRow" id="session_row">
|
||||
<property name="title" translatable="yes">Enable for this session</property>
|
||||
<property name="sensitive" bind-source="NotificationsPage" bind-property="account-enabled" bind-flags="sync-create"/>
|
||||
<property name="active" bind-source="NotificationsPage" bind-property="session-enabled" bind-flags="sync-create | bidirectional"/>
|
||||
<signal name="notify::active" handler="session_switched" swapped="true"/>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
|
Loading…
Reference in New Issue