use std::sync::{Arc, RwLock};
use matrix_sdk::{
event_handler::EventHandlerHandle,
notification_settings::{
NotificationSettings as SdkNotificationSettings,
RoomNotificationMode as SdkRoomNotificationMode,
},
ruma::events::push_rules::PushRulesEvent,
Client as MatrixClient,
};
use ruma::{
push::{PredefinedOverrideRuleId, PredefinedUnderrideRuleId, RuleKind},
RoomId,
};
use tokio::sync::RwLock as AsyncRwLock;
use crate::error::NotificationSettingsError;
#[derive(Clone, uniffi::Enum)]
pub enum RoomNotificationMode {
AllMessages,
MentionsAndKeywordsOnly,
Mute,
}
impl From<SdkRoomNotificationMode> for RoomNotificationMode {
fn from(value: SdkRoomNotificationMode) -> Self {
match value {
SdkRoomNotificationMode::AllMessages => Self::AllMessages,
SdkRoomNotificationMode::MentionsAndKeywordsOnly => Self::MentionsAndKeywordsOnly,
SdkRoomNotificationMode::Mute => Self::Mute,
}
}
}
impl From<RoomNotificationMode> for SdkRoomNotificationMode {
fn from(value: RoomNotificationMode) -> Self {
match value {
RoomNotificationMode::AllMessages => Self::AllMessages,
RoomNotificationMode::MentionsAndKeywordsOnly => Self::MentionsAndKeywordsOnly,
RoomNotificationMode::Mute => Self::Mute,
}
}
}
#[matrix_sdk_ffi_macros::export(callback_interface)]
pub trait NotificationSettingsDelegate: Sync + Send {
fn settings_did_change(&self);
}
#[derive(Clone, uniffi::Record)]
pub struct RoomNotificationSettings {
mode: RoomNotificationMode,
is_default: bool,
}
impl RoomNotificationSettings {
fn new(mode: RoomNotificationMode, is_default: bool) -> Self {
RoomNotificationSettings { mode, is_default }
}
}
#[derive(Clone, uniffi::Object)]
pub struct NotificationSettings {
sdk_client: MatrixClient,
sdk_notification_settings: Arc<AsyncRwLock<SdkNotificationSettings>>,
pushrules_event_handler: Arc<RwLock<Option<EventHandlerHandle>>>,
}
impl NotificationSettings {
pub(crate) fn new(
sdk_client: MatrixClient,
sdk_notification_settings: SdkNotificationSettings,
) -> Self {
Self {
sdk_client,
sdk_notification_settings: Arc::new(AsyncRwLock::new(sdk_notification_settings)),
pushrules_event_handler: Arc::new(RwLock::new(None)),
}
}
}
impl Drop for NotificationSettings {
fn drop(&mut self) {
if let Some(event_handler) = self.pushrules_event_handler.read().unwrap().as_ref() {
self.sdk_client.remove_event_handler(event_handler.clone());
}
}
}
#[matrix_sdk_ffi_macros::export]
impl NotificationSettings {
pub fn set_delegate(&self, delegate: Option<Box<dyn NotificationSettingsDelegate>>) {
if let Some(delegate) = delegate {
let delegate: Arc<dyn NotificationSettingsDelegate> = Arc::from(delegate);
let event_handler =
self.sdk_client.add_event_handler(move |_: PushRulesEvent| async move {
delegate.settings_did_change();
});
*self.pushrules_event_handler.write().unwrap() = Some(event_handler);
} else {
let event_handler = &mut *self.pushrules_event_handler.write().unwrap();
if let Some(event_handler) = event_handler {
self.sdk_client.remove_event_handler(event_handler.clone());
}
*event_handler = None;
}
}
pub async fn get_room_notification_settings(
&self,
room_id: String,
is_encrypted: bool,
is_one_to_one: bool,
) -> Result<RoomNotificationSettings, NotificationSettingsError> {
let parsed_room_id = RoomId::parse(&room_id)
.map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
let notification_settings = self.sdk_notification_settings.read().await;
if let Some(mode) =
notification_settings.get_user_defined_room_notification_mode(&parsed_room_id).await
{
return Ok(RoomNotificationSettings::new(mode.into(), false));
}
let mode = notification_settings
.get_default_room_notification_mode(is_encrypted.into(), is_one_to_one.into())
.await;
Ok(RoomNotificationSettings::new(mode.into(), true))
}
pub async fn set_room_notification_mode(
&self,
room_id: String,
mode: RoomNotificationMode,
) -> Result<(), NotificationSettingsError> {
let parsed_room_id = RoomId::parse(&room_id)
.map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
self.sdk_notification_settings
.read()
.await
.set_room_notification_mode(&parsed_room_id, mode.into())
.await?;
Ok(())
}
pub async fn get_user_defined_room_notification_mode(
&self,
room_id: String,
) -> Result<Option<RoomNotificationMode>, NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let parsed_room_id = RoomId::parse(&room_id)
.map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
if let Some(mode) =
notification_settings.get_user_defined_room_notification_mode(&parsed_room_id).await
{
Ok(Some(mode.into()))
} else {
Ok(None)
}
}
pub async fn get_default_room_notification_mode(
&self,
is_encrypted: bool,
is_one_to_one: bool,
) -> RoomNotificationMode {
let notification_settings = self.sdk_notification_settings.read().await;
let mode = notification_settings
.get_default_room_notification_mode(is_encrypted.into(), is_one_to_one.into())
.await;
mode.into()
}
pub async fn set_default_room_notification_mode(
&self,
is_encrypted: bool,
is_one_to_one: bool,
mode: RoomNotificationMode,
) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings
.set_default_room_notification_mode(
is_encrypted.into(),
is_one_to_one.into(),
mode.into(),
)
.await?;
Ok(())
}
pub async fn restore_default_room_notification_mode(
&self,
room_id: String,
) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let parsed_room_id = RoomId::parse(&room_id)
.map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
notification_settings.delete_user_defined_room_rules(&parsed_room_id).await?;
Ok(())
}
pub async fn get_rooms_with_user_defined_rules(&self, enabled: Option<bool>) -> Vec<String> {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings.get_rooms_with_user_defined_rules(enabled).await
}
pub async fn contains_keywords_rules(&self) -> bool {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings.contains_keyword_rules().await
}
pub async fn is_room_mention_enabled(&self) -> Result<bool, NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let enabled = notification_settings
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
.await?;
Ok(enabled)
}
pub async fn set_room_mention_enabled(
&self,
enabled: bool,
) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings
.set_push_rule_enabled(
RuleKind::Override,
PredefinedOverrideRuleId::IsRoomMention,
enabled,
)
.await?;
Ok(())
}
pub async fn is_user_mention_enabled(&self) -> Result<bool, NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let enabled = notification_settings
.is_push_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
.await?;
Ok(enabled)
}
pub async fn can_push_encrypted_event_to_device(&self) -> bool {
let notification_settings = self.sdk_notification_settings.read().await;
if let Ok(enabled) = notification_settings
.is_push_rule_enabled(RuleKind::Override, ".m.rule.encrypted_event")
.await
{
enabled
} else {
notification_settings
.is_push_rule_enabled(RuleKind::Override, ".org.matrix.msc4028.encrypted_event")
.await
.unwrap_or(false)
}
}
pub async fn can_homeserver_push_encrypted_event_to_device(&self) -> bool {
self.sdk_client.can_homeserver_push_encrypted_event_to_device().await.unwrap()
}
pub async fn set_user_mention_enabled(
&self,
enabled: bool,
) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings
.set_push_rule_enabled(
RuleKind::Override,
PredefinedOverrideRuleId::IsUserMention,
enabled,
)
.await?;
Ok(())
}
pub async fn is_call_enabled(&self) -> Result<bool, NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let enabled = notification_settings
.is_push_rule_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::Call)
.await?;
Ok(enabled)
}
pub async fn set_call_enabled(&self, enabled: bool) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings
.set_push_rule_enabled(RuleKind::Underride, PredefinedUnderrideRuleId::Call, enabled)
.await?;
Ok(())
}
pub async fn is_invite_for_me_enabled(&self) -> Result<bool, NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let enabled = notification_settings
.is_push_rule_enabled(
RuleKind::Override,
PredefinedOverrideRuleId::InviteForMe.as_str(),
)
.await?;
Ok(enabled)
}
pub async fn set_invite_for_me_enabled(
&self,
enabled: bool,
) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
notification_settings
.set_push_rule_enabled(
RuleKind::Override,
PredefinedOverrideRuleId::InviteForMe.as_str(),
enabled,
)
.await?;
Ok(())
}
pub async fn unmute_room(
&self,
room_id: String,
is_encrypted: bool,
is_one_to_one: bool,
) -> Result<(), NotificationSettingsError> {
let notification_settings = self.sdk_notification_settings.read().await;
let parsed_room_id = RoomId::parse(&room_id)
.map_err(|_e| NotificationSettingsError::InvalidRoomId { room_id })?;
notification_settings
.unmute_room(&parsed_room_id, is_encrypted.into(), is_one_to_one.into())
.await?;
Ok(())
}
}