Skip to content

Commit

Permalink
Add command to set per-room notification levels (#305)
Browse files Browse the repository at this point in the history
  • Loading branch information
rmsthebest authored Aug 17, 2024
1 parent b4fc574 commit 2a66496
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 1 deletion.
19 changes: 19 additions & 0 deletions docs/iamb.1
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,25 @@ View a list of members of the currently focused room.
Set the name of the currently focused room.
.It Sy ":room name unset"
Unset the name of the currently focused room.
.It Sy ":room notify set [level]"
Set a notification level for the currently focused room.
Valid levels are
.Dq mute ,
.Dq mentions ,
.Dq keywords ,
and
.Dq all .
Note that
.Dq mentions
and
.Dq keywords
are aliases for the same behaviour.
.It Sy ":room notify unset"
Unset any room-level notification configuration.
.It Sy ":room notify show"
Show the current room-level notification configuration.
If the room is using the account-level default, then this will print
.Dq default .
.It Sy ":room tag set [tag]"
Add a tag to the currently focused room.
.It Sy ":room tag unset [tag]"
Expand Down
10 changes: 10 additions & 0 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,9 @@ pub enum RoomField {
/// The room topic.
Topic,

/// Notification level.
NotificationMode,

/// The room's entire list of alternative aliases.
Aliases,

Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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),
Expand Down
35 changes: 34 additions & 1 deletion src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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();
Expand Down
72 changes: 72 additions & 0 deletions src/windows/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use std::collections::HashSet;

use matrix_sdk::{
notification_settings::RoomNotificationMode,
room::Room as MatrixRoom,
ruma::{
api::client::{
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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());
}
}

0 comments on commit 2a66496

Please sign in to comment.