use dioxus::prelude::*;
use matrix_sdk::notification_settings::RoomNotificationMode;
use matrix_sdk::ruma::OwnedRoomId;
use matrix_sdk::Client;
use crate::components::modal::Modal;
use crate::state::app_state::AppState;
use crate::state::room_state::NotificationLevel;
#[derive(Clone, Debug, PartialEq)]
pub enum PushRuleAction {
Default,
AllMessages,
MentionsOnly,
Mute,
}
pub fn notification_level_from_push_action(action: PushRuleAction) -> NotificationLevel {
match action {
PushRuleAction::Default => NotificationLevel::Default,
PushRuleAction::AllMessages => NotificationLevel::AllMessages,
PushRuleAction::MentionsOnly => NotificationLevel::MentionsAndKeywords,
PushRuleAction::Mute => NotificationLevel::Mute,
}
}
pub fn push_rule_action_from_notification_level(level: &NotificationLevel) -> PushRuleAction {
match level {
NotificationLevel::Default => PushRuleAction::Default,
NotificationLevel::AllMessages => PushRuleAction::AllMessages,
NotificationLevel::MentionsAndKeywords => PushRuleAction::MentionsOnly,
NotificationLevel::Mute => PushRuleAction::Mute,
}
}
pub fn notification_level_from_user_defined_mode(
mode: Option<RoomNotificationMode>,
) -> NotificationLevel {
match mode {
Some(RoomNotificationMode::AllMessages) => NotificationLevel::AllMessages,
Some(RoomNotificationMode::MentionsAndKeywordsOnly) => {
NotificationLevel::MentionsAndKeywords
}
Some(RoomNotificationMode::Mute) => NotificationLevel::Mute,
None => NotificationLevel::Default,
}
}
pub async fn load_room_notification_level(
client: &Client,
room_id: &OwnedRoomId,
) -> Result<NotificationLevel, String> {
let settings = client.notification_settings().await;
Ok(notification_level_from_user_defined_mode(
settings.get_user_defined_room_notification_mode(room_id).await,
))
}
pub async fn persist_room_notification_level(
client: &Client,
room_id: &OwnedRoomId,
level: NotificationLevel,
) -> Result<(), String> {
let settings = client.notification_settings().await;
match level {
NotificationLevel::Default => settings
.delete_user_defined_room_rules(room_id)
.await
.map_err(|e| format!("Failed to reset room notifications: {e}"))?,
NotificationLevel::AllMessages => settings
.set_room_notification_mode(room_id, RoomNotificationMode::AllMessages)
.await
.map_err(|e| format!("Failed to enable notifications for all messages: {e}"))?,
NotificationLevel::MentionsAndKeywords => settings
.set_room_notification_mode(
room_id,
RoomNotificationMode::MentionsAndKeywordsOnly,
)
.await
.map_err(|e| format!("Failed to keep only mentions and keywords: {e}"))?,
NotificationLevel::Mute => settings
.set_room_notification_mode(room_id, RoomNotificationMode::Mute)
.await
.map_err(|e| format!("Failed to mute room notifications: {e}"))?,
}
Ok(())
}
#[component]
pub fn PushRulesDialog(room_id: String, room_name: String, on_close: EventHandler<()>) -> Element {
let mut state = use_context::<Signal<AppState>>();
let initial_selection = {
let state_read = state.read();
state_read
.rooms
.values()
.find(|room| room.room_id.to_string() == room_id)
.map(|room| push_rule_action_from_notification_level(&room.notification_level))
.unwrap_or(PushRuleAction::Default)
};
let mut selected = use_signal(move || initial_selection.clone());
let mut saving = use_signal(|| false);
let mut save_error = use_signal(|| Option::<String>::None);
let room_id_for_load = room_id.clone();
use_effect(move || {
let rid = room_id_for_load.clone();
spawn(async move {
let client = { state.read().client.clone() };
let Some(client) = client else {
return;
};
let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) else {
return;
};
match load_room_notification_level(&client, &room_id).await {
Ok(level) => selected.set(push_rule_action_from_notification_level(&level)),
Err(e) => save_error.set(Some(e)),
}
});
});
let room_id_for_save = room_id.clone();
let on_save = move |_| {
let rule = selected.read().clone();
let rid = room_id_for_save.clone();
let next_level = notification_level_from_push_action(rule.clone());
saving.set(true);
save_error.set(None);
spawn(async move {
let client = { state.read().client.clone() };
let Some(client) = client else {
save_error.set(Some("Not logged in".to_string()));
saving.set(false);
return;
};
if let Ok(room_id) = matrix_sdk::ruma::OwnedRoomId::try_from(rid.as_str()) {
match persist_room_notification_level(&client, &room_id, next_level.clone()).await {
Ok(()) => {
if let Some(room) = state.write().rooms.get_mut(&room_id) {
room.notification_level = next_level;
}
}
Err(e) => {
save_error.set(Some(e));
}
}
}
saving.set(false);
});
};
rsx! {
Modal {
title: format!("Notification settings: {room_name}"),
on_close: move |_| on_close.call(()),
div {
class: "push-rules-dialog",
p { class: "push-rules-dialog__description",
"Choose how you want to be notified about new messages in this room."
}
if let Some(err) = save_error.read().as_ref() {
p {
class: "push-rules-dialog__error",
"{err}"
}
}
div {
class: "push-rules-dialog__options",
label {
class: "push-rules-dialog__option",
input {
r#type: "radio",
name: "push_rule",
checked: *selected.read() == PushRuleAction::Default,
oninput: move |_| selected.set(PushRuleAction::Default),
}
div {
strong { "Default" }
p { "Use your global notification settings" }
}
}
label {
class: "push-rules-dialog__option",
input {
r#type: "radio",
name: "push_rule",
checked: *selected.read() == PushRuleAction::AllMessages,
oninput: move |_| selected.set(PushRuleAction::AllMessages),
}
div {
strong { "All messages" }
p { "Notify for every message" }
}
}
label {
class: "push-rules-dialog__option",
input {
r#type: "radio",
name: "push_rule",
checked: *selected.read() == PushRuleAction::MentionsOnly,
oninput: move |_| selected.set(PushRuleAction::MentionsOnly),
}
div {
strong { "Mentions & keywords" }
p { "Only notify when mentioned or keywords match" }
}
}
label {
class: "push-rules-dialog__option",
input {
r#type: "radio",
name: "push_rule",
checked: *selected.read() == PushRuleAction::Mute,
oninput: move |_| selected.set(PushRuleAction::Mute),
}
div {
strong { "Mute" }
p { "No notifications from this room" }
}
}
}
div {
class: "push-rules-dialog__actions",
button {
class: "btn btn--secondary",
onclick: move |_| on_close.call(()),
"Cancel"
}
button {
class: "btn btn--primary",
disabled: *saving.read(),
onclick: on_save,
if *saving.read() { "Saving..." } else { "Save" }
}
}
}
}
}
}