Add command to set per-room notification levels (#305)

This commit is contained in:
Tony 2024-08-17 23:43:19 +02:00 committed by GitHub
parent b4fc574163
commit 2a66496913
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 135 additions and 1 deletions

View file

@ -378,6 +378,9 @@ pub enum RoomField {
/// The room topic.
Topic,
/// Notification level.
NotificationMode,
/// The room's entire list of alternative aliases.
Aliases,
@ -612,6 +615,10 @@ pub type MessageReactions = HashMap<OwnedEventId, (String, OwnedUserId)>;
/// Errors encountered during application use.
#[derive(thiserror::Error, Debug)]
pub enum IambError {
/// An invalid notification level was specified.
#[error("Invalid notification level: {0}")]
InvalidNotificationLevel(String),
/// An invalid user identifier was specified.
#[error("Invalid user identifier: {0}")]
InvalidUserId(String),
@ -691,6 +698,9 @@ pub enum IambError {
#[error("Verification request error: {0}")]
VerificationRequestError(#[from] matrix_sdk::encryption::identities::RequestVerificationError),
#[error("Notification setting error: {0}")]
NotificationSettingError(#[from] matrix_sdk::NotificationSettingsError),
/// A failure related to images.
#[error("Image error: {0}")]
Image(#[from] image::ImageError),

View file

@ -34,7 +34,7 @@ type ProgResult = CommandResult<ProgramCommand>;
/// Convert strings the user types into a tag name.
fn tag_name(name: String) -> Result<TagName, CommandError> {
let tag = match name.as_str() {
let tag = match name.to_lowercase().as_str() {
"fav" | "favorite" | "favourite" | "m.favourite" => TagName::Favorite,
"low" | "lowpriority" | "low_priority" | "low-priority" | "m.lowpriority" => {
TagName::LowPriority
@ -431,6 +431,18 @@ fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult {
("tag", "set", Some(s)) => RoomAction::Set(RoomField::Tag(tag_name(s)?), "".into()).into(),
("tag", "set", None) => return Result::Err(CommandError::InvalidArgument),
// :room notify set <notification-level>
("notify", "set", Some(s)) => RoomAction::Set(RoomField::NotificationMode, s).into(),
("notify", "set", None) => return Result::Err(CommandError::InvalidArgument),
// :room notify unset <notification-level>
("notify", "unset", None) => RoomAction::Unset(RoomField::NotificationMode).into(),
("notify", "unset", Some(_)) => return Result::Err(CommandError::InvalidArgument),
// :room notify show
("notify", "show", None) => RoomAction::Show(RoomField::NotificationMode).into(),
("notify", "show", Some(_)) => return Result::Err(CommandError::InvalidArgument),
// :room tag unset <tag-name>
("tag", "unset", Some(s)) => RoomAction::Unset(RoomField::Tag(tag_name(s)?)).into(),
("tag", "unset", None) => return Result::Err(CommandError::InvalidArgument),
@ -977,6 +989,27 @@ mod tests {
);
}
#[test]
fn test_cmd_room_notification_mode_set() {
let mut cmds = setup_commands();
let ctx = EditContext::default();
let cmd = format!("room notify set mute");
let res = cmds.input_cmd(&cmd, ctx.clone()).unwrap();
let act = RoomAction::Set(RoomField::NotificationMode, "mute".into());
assert_eq!(res, vec![(act.into(), ctx.clone())]);
let cmd = format!("room notify unset");
let res = cmds.input_cmd(&cmd, ctx.clone()).unwrap();
let act = RoomAction::Unset(RoomField::NotificationMode);
assert_eq!(res, vec![(act.into(), ctx.clone())]);
let cmd = format!("room notify show");
let res = cmds.input_cmd(&cmd, ctx.clone()).unwrap();
let act = RoomAction::Show(RoomField::NotificationMode);
assert_eq!(res, vec![(act.into(), ctx.clone())]);
}
#[test]
fn test_cmd_invite() {
let mut cmds = setup_commands();

View file

@ -2,6 +2,7 @@
use std::collections::HashSet;
use matrix_sdk::{
notification_settings::RoomNotificationMode,
room::Room as MatrixRoom,
ruma::{
api::client::{
@ -82,6 +83,19 @@ macro_rules! delegate {
};
}
fn notification_mode(name: impl Into<String>) -> IambResult<RoomNotificationMode> {
let name = name.into();
let mode = match name.to_lowercase().as_str() {
"mute" => RoomNotificationMode::Mute,
"mentions" | "keywords" => RoomNotificationMode::MentionsAndKeywordsOnly,
"all" => RoomNotificationMode::AllMessages,
_ => return Err(IambError::InvalidNotificationLevel(name).into()),
};
Ok(mode)
}
/// State for a Matrix room or space.
///
/// Since spaces function as special rooms within Matrix, we wrap their window state together, so
@ -296,6 +310,16 @@ impl RoomState {
let ev = RoomTopicEventContent::new(value);
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
},
RoomField::NotificationMode => {
let mode = notification_mode(value)?;
let client = &store.application.worker.client;
let notifications = client.notification_settings().await;
notifications
.set_room_notification_mode(self.id(), mode)
.await
.map_err(IambError::from)?;
},
RoomField::CanonicalAlias => {
let client = &mut store.application.worker.client;
@ -399,6 +423,15 @@ impl RoomState {
let ev = RoomTopicEventContent::new("".into());
let _ = room.send_state_event(ev).await.map_err(IambError::from)?;
},
RoomField::NotificationMode => {
let client = &store.application.worker.client;
let notifications = client.notification_settings().await;
notifications
.delete_user_defined_room_rules(self.id())
.await
.map_err(IambError::from)?;
},
RoomField::CanonicalAlias => {
let Some(alias_to_destroy) = room.canonical_alias() else {
let msg = "This room has no canonical alias to unset";
@ -481,6 +514,21 @@ impl RoomState {
Some(topic) => format!("Room topic: {topic:?}"),
}
},
RoomField::NotificationMode => {
let client = &store.application.worker.client;
let notifications = client.notification_settings().await;
let mode =
notifications.get_user_defined_room_notification_mode(self.id()).await;
let level = match mode {
Some(RoomNotificationMode::Mute) => "mute",
Some(RoomNotificationMode::MentionsAndKeywordsOnly) => "keywords",
Some(RoomNotificationMode::AllMessages) => "all",
None => "default",
};
format!("Room notification level: {level:?}")
},
RoomField::Aliases => {
let aliases = room
.alt_aliases()
@ -677,3 +725,27 @@ impl WindowOps<IambInfo> for RoomState {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_room_notification_level() {
let tests = vec![
("mute", RoomNotificationMode::Mute),
("mentions", RoomNotificationMode::MentionsAndKeywordsOnly),
("keywords", RoomNotificationMode::MentionsAndKeywordsOnly),
("all", RoomNotificationMode::AllMessages),
];
for (input, expect) in tests {
let res = notification_mode(input).unwrap();
assert_eq!(expect, res);
}
assert!(notification_mode("invalid").is_err());
assert!(notification_mode("not a level").is_err());
assert!(notification_mode("@user:example.com").is_err());
}
}