notifications-page: Add global rooms setting
This commit is contained in:
parent
59262cc141
commit
cd708d637d
|
@ -10,7 +10,7 @@ mod verification;
|
|||
|
||||
pub use self::{
|
||||
avatar_data::{AvatarData, AvatarImage, AvatarUriSource},
|
||||
notifications::{Notifications, NotificationsSettings},
|
||||
notifications::{Notifications, NotificationsGlobalSetting, NotificationsSettings},
|
||||
room::{
|
||||
Event, EventKey, HighlightFlags, Member, MemberList, MemberRole, Membership, MessageState,
|
||||
PowerLevel, ReactionGroup, ReactionList, Room, RoomType, Timeline, TimelineItem,
|
||||
|
|
|
@ -12,7 +12,7 @@ use tracing::{debug, error, warn};
|
|||
|
||||
mod notifications_settings;
|
||||
|
||||
pub use self::notifications_settings::NotificationsSettings;
|
||||
pub use self::notifications_settings::{NotificationsGlobalSetting, NotificationsSettings};
|
||||
use super::{Room, Session};
|
||||
use crate::{
|
||||
application::AppShowRoomPayload, prelude::*, spawn, spawn_tokio, utils::matrix::get_event_body,
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
use futures_util::StreamExt;
|
||||
use gtk::{glib, glib::clone, prelude::*, subclass::prelude::*};
|
||||
use matrix_sdk::{
|
||||
notification_settings::NotificationSettings as MatrixNotificationSettings,
|
||||
notification_settings::{
|
||||
IsEncrypted, NotificationSettings as MatrixNotificationSettings, RoomNotificationMode,
|
||||
},
|
||||
NotificationSettingsError,
|
||||
};
|
||||
use ruma::push::{PredefinedOverrideRuleId, RuleKind};
|
||||
|
@ -13,6 +15,23 @@ use crate::{
|
|||
spawn, spawn_tokio,
|
||||
};
|
||||
|
||||
/// The possible values for the global notifications setting.
|
||||
#[derive(
|
||||
Debug, Default, Hash, Eq, PartialEq, Clone, Copy, glib::Enum, strum::Display, strum::EnumString,
|
||||
)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "NotificationsGlobalSetting")]
|
||||
#[strum(serialize_all = "kebab-case")]
|
||||
pub enum NotificationsGlobalSetting {
|
||||
/// Every message in every room.
|
||||
#[default]
|
||||
All = 0,
|
||||
/// Every message in 1-to-1 rooms, and mentions and keywords in every room.
|
||||
DirectAndMentions = 1,
|
||||
/// Only mentions and keywords in every room.
|
||||
MentionsOnly = 2,
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
|
@ -32,6 +51,9 @@ mod imp {
|
|||
/// Whether notifications are enabled for this session.
|
||||
#[property(get, set = Self::set_session_enabled, explicit_notify)]
|
||||
pub session_enabled: Cell<bool>,
|
||||
/// The global setting about which messages trigger notifications.
|
||||
#[property(get, builder(NotificationsGlobalSetting::default()))]
|
||||
pub global_setting: Cell<NotificationsGlobalSetting>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -181,6 +203,14 @@ impl NotificationsSettings {
|
|||
}
|
||||
};
|
||||
self.set_account_enabled_inner(account_enabled);
|
||||
|
||||
if default_rooms_notifications_is_all(api.clone(), false).await {
|
||||
self.set_global_setting_inner(NotificationsGlobalSetting::All);
|
||||
} else if default_rooms_notifications_is_all(api.clone(), true).await {
|
||||
self.set_global_setting_inner(NotificationsGlobalSetting::DirectAndMentions);
|
||||
} else {
|
||||
self.set_global_setting_inner(NotificationsGlobalSetting::MentionsOnly);
|
||||
}
|
||||
}
|
||||
|
||||
/// Set whether notifications are enabled for this session.
|
||||
|
@ -223,6 +253,46 @@ impl NotificationsSettings {
|
|||
self.imp().account_enabled.set(enabled);
|
||||
self.notify_account_enabled();
|
||||
}
|
||||
|
||||
/// Set the global setting about which messages trigger notifications.
|
||||
pub async fn set_global_setting(
|
||||
&self,
|
||||
setting: NotificationsGlobalSetting,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let Some(api) = self.api() else {
|
||||
error!("Cannot update notifications settings when API is not initialized");
|
||||
return Err(NotificationSettingsError::UnableToUpdatePushRule);
|
||||
};
|
||||
|
||||
let (group_all, one_to_one_all) = match setting {
|
||||
NotificationsGlobalSetting::All => (true, true),
|
||||
NotificationsGlobalSetting::DirectAndMentions => (false, true),
|
||||
NotificationsGlobalSetting::MentionsOnly => (false, false),
|
||||
};
|
||||
|
||||
if let Err(error) = set_default_rooms_notifications_all(api.clone(), false, group_all).await
|
||||
{
|
||||
error!("Failed to change global group chats notifications setting: {error}");
|
||||
return Err(error);
|
||||
}
|
||||
if let Err(error) = set_default_rooms_notifications_all(api, true, one_to_one_all).await {
|
||||
error!("Failed to change global 1-to-1 chats notifications setting: {error}");
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
self.set_global_setting_inner(setting);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_global_setting_inner(&self, setting: NotificationsGlobalSetting) {
|
||||
if self.global_setting() == setting {
|
||||
return;
|
||||
}
|
||||
|
||||
self.imp().global_setting.set(setting);
|
||||
self.notify_global_setting();
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for NotificationsSettings {
|
||||
|
@ -230,3 +300,36 @@ impl Default for NotificationsSettings {
|
|||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
async fn default_rooms_notifications_is_all(
|
||||
api: MatrixNotificationSettings,
|
||||
is_one_to_one: bool,
|
||||
) -> bool {
|
||||
let mode = spawn_tokio!(async move {
|
||||
api.get_default_room_notification_mode(IsEncrypted::No, is_one_to_one.into())
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
mode == RoomNotificationMode::AllMessages
|
||||
}
|
||||
|
||||
async fn set_default_rooms_notifications_all(
|
||||
api: MatrixNotificationSettings,
|
||||
is_one_to_one: bool,
|
||||
all: bool,
|
||||
) -> Result<(), NotificationSettingsError> {
|
||||
let mode = if all {
|
||||
RoomNotificationMode::AllMessages
|
||||
} else {
|
||||
RoomNotificationMode::MentionsAndKeywordsOnly
|
||||
};
|
||||
|
||||
spawn_tokio!(async move {
|
||||
api.set_default_room_notification_mode(IsEncrypted::No, is_one_to_one.into(), mode)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{glib, glib::clone, CompositeTemplate};
|
||||
use tracing::error;
|
||||
|
||||
use crate::{
|
||||
components::Spinner, session::model::NotificationsSettings, spawn, toast,
|
||||
components::{LoadingBin, Spinner},
|
||||
session::model::{NotificationsGlobalSetting, NotificationsSettings},
|
||||
spawn, toast,
|
||||
utils::BoundObjectWeakRef,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use std::cell::Cell;
|
||||
use std::{cell::Cell, marker::PhantomData};
|
||||
|
||||
use glib::subclass::InitializingObject;
|
||||
|
||||
|
@ -24,12 +27,32 @@ mod imp {
|
|||
pub account_switch: TemplateChild<gtk::Switch>,
|
||||
#[template_child]
|
||||
pub session_row: TemplateChild<adw::SwitchRow>,
|
||||
#[template_child]
|
||||
pub global: TemplateChild<adw::PreferencesGroup>,
|
||||
#[template_child]
|
||||
pub global_all_bin: TemplateChild<LoadingBin>,
|
||||
#[template_child]
|
||||
pub global_all_radio: TemplateChild<gtk::CheckButton>,
|
||||
#[template_child]
|
||||
pub global_direct_bin: TemplateChild<LoadingBin>,
|
||||
#[template_child]
|
||||
pub global_direct_radio: TemplateChild<gtk::CheckButton>,
|
||||
#[template_child]
|
||||
pub global_mentions_bin: TemplateChild<LoadingBin>,
|
||||
#[template_child]
|
||||
pub global_mentions_radio: TemplateChild<gtk::CheckButton>,
|
||||
/// 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 the global section is busy.
|
||||
#[property(get)]
|
||||
pub global_loading: Cell<bool>,
|
||||
/// The global notifications setting, as a string.
|
||||
#[property(get = Self::global_setting, set = Self::set_global_setting)]
|
||||
pub global_setting: PhantomData<String>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
@ -43,6 +66,8 @@ mod imp {
|
|||
|
||||
Self::bind_template(klass);
|
||||
Self::Type::bind_template_callbacks(klass);
|
||||
|
||||
klass.install_property_action("notifications.set-global-default", "global-setting");
|
||||
}
|
||||
|
||||
fn instance_init(obj: &InitializingObject<Self>) {
|
||||
|
@ -78,17 +103,46 @@ mod imp {
|
|||
settings.connect_session_enabled_notify(clone!(@weak obj => move |_| {
|
||||
obj.update_session();
|
||||
}));
|
||||
let global_setting_handler =
|
||||
settings.connect_global_setting_notify(clone!(@weak obj => move |_| {
|
||||
obj.update_global();
|
||||
}));
|
||||
|
||||
self.notifications_settings.set(
|
||||
settings,
|
||||
vec![account_enabled_handler, session_enabled_handler],
|
||||
vec![
|
||||
account_enabled_handler,
|
||||
session_enabled_handler,
|
||||
global_setting_handler,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
obj.update_account();
|
||||
obj.update_session();
|
||||
obj.notify_notifications_settings();
|
||||
}
|
||||
|
||||
/// The global notifications setting, as a string.
|
||||
fn global_setting(&self) -> String {
|
||||
let Some(settings) = self.notifications_settings.obj() else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
settings.global_setting().to_string()
|
||||
}
|
||||
|
||||
/// Set the global notifications setting, as a string.
|
||||
fn set_global_setting(&self, default: String) {
|
||||
let default = match default.parse::<NotificationsGlobalSetting>() {
|
||||
Ok(default) => default,
|
||||
Err(_) => {
|
||||
error!("Invalid value to set global default notifications setting: {default}");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.obj().global_setting_changed(default);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +170,7 @@ impl NotificationsPage {
|
|||
imp.account_switch.set_active(settings.account_enabled());
|
||||
imp.account_switch.set_sensitive(!self.account_loading());
|
||||
|
||||
// Other sessions will be disabled or not.
|
||||
// Other sections will be disabled or not.
|
||||
self.update_session();
|
||||
}
|
||||
|
||||
|
@ -129,6 +183,24 @@ impl NotificationsPage {
|
|||
|
||||
imp.session_row.set_active(settings.session_enabled());
|
||||
imp.session_row.set_sensitive(settings.account_enabled());
|
||||
|
||||
// Other sections will be disabled or not.
|
||||
self.update_global();
|
||||
}
|
||||
|
||||
/// Update the section about global.
|
||||
fn update_global(&self) {
|
||||
let Some(settings) = self.notifications_settings() else {
|
||||
return;
|
||||
};
|
||||
let imp = self.imp();
|
||||
|
||||
// Updates the active radio button.
|
||||
self.notify_global_setting();
|
||||
|
||||
let sensitive =
|
||||
settings.account_enabled() && settings.session_enabled() && !self.global_loading();
|
||||
imp.global.set_sensitive(sensitive);
|
||||
}
|
||||
|
||||
fn set_account_loading(&self, loading: bool) {
|
||||
|
@ -176,4 +248,47 @@ impl NotificationsPage {
|
|||
|
||||
settings.set_session_enabled(imp.session_row.is_active());
|
||||
}
|
||||
|
||||
fn set_global_loading(&self, loading: bool, setting: NotificationsGlobalSetting) {
|
||||
let imp = self.imp();
|
||||
|
||||
// Only show the spinner on the selected one.
|
||||
imp.global_all_bin
|
||||
.set_is_loading(loading && setting == NotificationsGlobalSetting::All);
|
||||
imp.global_direct_bin
|
||||
.set_is_loading(loading && setting == NotificationsGlobalSetting::DirectAndMentions);
|
||||
imp.global_mentions_bin
|
||||
.set_is_loading(loading && setting == NotificationsGlobalSetting::MentionsOnly);
|
||||
|
||||
self.imp().global_loading.set(loading);
|
||||
self.notify_global_loading();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn global_setting_changed(&self, setting: NotificationsGlobalSetting) {
|
||||
let Some(settings) = self.notifications_settings() else {
|
||||
return;
|
||||
};
|
||||
let imp = self.imp();
|
||||
|
||||
if setting == settings.global_setting() {
|
||||
// Nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
imp.global.set_sensitive(false);
|
||||
self.set_global_loading(true, setting);
|
||||
|
||||
spawn!(clone!(@weak self as obj, @weak settings => async move {
|
||||
if settings.set_global_setting(setting).await.is_err() {
|
||||
toast!(
|
||||
obj,
|
||||
gettext("Could not change global notifications setting")
|
||||
);
|
||||
}
|
||||
|
||||
obj.set_global_loading(false, setting);
|
||||
obj.update_global();
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,5 +36,62 @@
|
|||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwPreferencesGroup" id="global">
|
||||
<property name="title" translatable="yes">Global</property>
|
||||
<property name="description" translatable="yes">Which messages trigger notifications in rooms that do not have more specific rules.</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="activatable-widget">global_all_radio</property>
|
||||
<property name="title" translatable="yes">All messages in all rooms</property>
|
||||
<child type="prefix">
|
||||
<object class="LoadingBin" id="global_all_bin">
|
||||
<property name="child">
|
||||
<object class="GtkCheckButton" id="global_all_radio">
|
||||
<property name="valign">center</property>
|
||||
<property name="action-name">notifications.set-global-default</property>
|
||||
<property name="action-target">'all'</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="activatable-widget">global_direct_radio</property>
|
||||
<property name="title" translatable="yes">All messages in direct chats, and mentions and keywords in all rooms</property>
|
||||
<child type="prefix">
|
||||
<object class="LoadingBin" id="global_direct_bin">
|
||||
<property name="child">
|
||||
<object class="GtkCheckButton" id="global_direct_radio">
|
||||
<property name="valign">center</property>
|
||||
<property name="action-name">notifications.set-global-default</property>
|
||||
<property name="action-target">'direct-and-mentions'</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="activatable-widget">global_mentions_radio</property>
|
||||
<property name="title" translatable="yes">Only mentions and keywords in all rooms</property>
|
||||
<child type="prefix">
|
||||
<object class="LoadingBin" id="global_mentions_bin">
|
||||
<property name="child">
|
||||
<object class="GtkCheckButton" id="global_mentions_radio">
|
||||
<property name="valign">center</property>
|
||||
<property name="action-name">notifications.set-global-default</property>
|
||||
<property name="action-target">'mentions-only'</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
Loading…
Reference in New Issue