Skip to content

Commit 6dc5b4e

Browse files
committed
feat(push): add experimental support for MSC3768 (in-app-only notifications)
Signed-off-by: Johannes Marbach <[email protected]>
1 parent 92192c5 commit 6dc5b4e

File tree

10 files changed

+76
-34
lines changed

10 files changed

+76
-34
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings/matrix-sdk-ffi/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ crate-type = [
2626
[features]
2727
default = ["bundled-sqlite", "unstable-msc4274"]
2828
bundled-sqlite = ["matrix-sdk/bundled-sqlite"]
29+
unstable-msc3768 = ["matrix-sdk-ui/unstable-msc3768"]
2930
unstable-msc4274 = ["matrix-sdk-ui/unstable-msc4274"]
3031
# Required when targeting a Javascript environment, like Wasm in a browser.
3132
js = ["matrix-sdk-ui/js"]

bindings/matrix-sdk-ffi/src/notification_settings.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,12 @@ impl TryFrom<Tweak> for SdkTweak {
322322
#[derive(Clone, uniffi::Enum)]
323323
/// Enum representing the push notification actions for a rule.
324324
pub enum Action {
325-
/// Causes matching events to generate a notification.
325+
/// Causes matching events to generate a notification (both in-app and
326+
/// remote / push).
326327
Notify,
328+
/// Causes matching events to generate an in-app notification but no remote
329+
/// / push notification.
330+
NotifyInApp,
327331
/// Sets an entry in the 'tweaks' dictionary sent to the push gateway.
328332
SetTweak { value: Tweak },
329333
}
@@ -334,6 +338,7 @@ impl TryFrom<SdkAction> for Action {
334338
fn try_from(value: SdkAction) -> Result<Self, Self::Error> {
335339
Ok(match value {
336340
SdkAction::Notify => Self::Notify,
341+
SdkAction::NotifyInApp => Self::NotifyInApp,
337342
SdkAction::SetTweak(tweak) => Self::SetTweak {
338343
value: tweak.try_into().map_err(|e| format!("Failed to convert tweak: {e}"))?,
339344
},
@@ -348,6 +353,7 @@ impl TryFrom<Action> for SdkAction {
348353
fn try_from(value: Action) -> Result<Self, Self::Error> {
349354
Ok(match value {
350355
Action::Notify => Self::Notify,
356+
Action::NotifyInApp => Self::NotifyInApp,
351357
Action::SetTweak { value } => Self::SetTweak(
352358
value.try_into().map_err(|e| format!("Failed to convert tweak: {e}"))?,
353359
),

crates/matrix-sdk-ui/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ rustls-tls = ["matrix-sdk/rustls-tls"]
1919
js = ["matrix-sdk/js"]
2020
uniffi = ["dep:uniffi", "matrix-sdk/uniffi", "matrix-sdk-base/uniffi"]
2121

22+
# Add support for in-app only notifications
23+
unstable-msc3768 = ["matrix-sdk/unstable-msc3768"]
24+
2225
# Add support for encrypted extensible events.
2326
unstable-msc3956 = ["ruma/unstable-msc3956"]
2427

@@ -58,6 +61,7 @@ tracing = { workspace = true, features = ["attributes"] }
5861
unicode-normalization.workspace = true
5962
uniffi = { workspace = true, optional = true }
6063

64+
cfg-if = "1.0.0"
6165
emojis = "0.6.4"
6266
unicode-segmentation = "1.12.0"
6367

crates/matrix-sdk-ui/src/notification_client.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use std::{
1818
time::Duration,
1919
};
2020

21+
use cfg_if::cfg_if;
2122
use futures_util::{StreamExt as _, pin_mut};
2223
use matrix_sdk::{
2324
Client, ClientBuildError, SlidingSyncList, SlidingSyncMode, room::Room, sleep::sleep,
@@ -677,7 +678,7 @@ impl NotificationClient {
677678

678679
let should_notify = push_actions
679680
.as_ref()
680-
.is_some_and(|actions| actions.iter().any(|a| a.should_notify()));
681+
.is_some_and(|actions| actions.iter().any(should_action_notify_remote));
681682

682683
if !should_notify {
683684
// The event has been filtered out by the user's push rules.
@@ -749,7 +750,7 @@ impl NotificationClient {
749750
}
750751

751752
if let Some(actions) = timeline_event.push_actions()
752-
&& !actions.iter().any(|a| a.should_notify())
753+
&& !actions.iter().any(should_action_notify_remote)
753754
{
754755
return Ok(NotificationStatus::EventFilteredOut);
755756
}
@@ -771,6 +772,17 @@ impl NotificationClient {
771772
}
772773
}
773774

775+
fn should_action_notify_remote(action: &Action) -> bool {
776+
cfg_if! {
777+
if #[cfg(feature = "unstable-msc3768")] {
778+
action.should_notify_remote()
779+
} else {
780+
// Before MSC3768 only combined remote/local notifications existed
781+
action.should_notify()
782+
}
783+
}
784+
}
785+
774786
fn is_event_encrypted(event_type: TimelineEventType) -> bool {
775787
let is_still_encrypted = matches!(event_type, TimelineEventType::RoomEncrypted);
776788

crates/matrix-sdk/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ experimental-widgets = ["dep:uuid", "experimental-send-custom-to-device"]
5959

6060
docsrs = ["e2e-encryption", "sqlite", "indexeddb", "sso-login", "qrcode"]
6161

62+
# Add support for in-app only notifications
63+
unstable-msc3768 = ["ruma/unstable-msc3768"]
64+
6265
# Add support for inline media galleries via msgtypes
6366
unstable-msc4274 = ["ruma/unstable-msc4274", "matrix-sdk-base/unstable-msc4274"]
6467

crates/matrix-sdk/src/notification_settings/command.rs

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ use crate::NotificationSettingsError;
1414
#[derive(Clone, Debug)]
1515
pub(crate) enum Command {
1616
/// Set a new `Room` push rule
17-
SetRoomPushRule { room_id: OwnedRoomId, notify: bool },
17+
SetRoomPushRule { room_id: OwnedRoomId, notify: Notify },
1818
/// Set a new `Override` push rule matching a `RoomId`
19-
SetOverridePushRule { rule_id: String, room_id: OwnedRoomId, notify: bool },
19+
SetOverridePushRule { rule_id: String, room_id: OwnedRoomId, notify: Notify },
2020
/// Set a new push rule for a keyword.
2121
SetKeywordPushRule { keyword: String },
2222
/// Set whether a push rule is enabled
@@ -29,21 +29,13 @@ pub(crate) enum Command {
2929
SetCustomPushRule { rule: NewPushRule },
3030
}
3131

32-
fn get_notify_actions(notify: bool) -> Vec<Action> {
33-
if notify {
34-
vec![Action::Notify, Action::SetTweak(Tweak::Sound("default".into()))]
35-
} else {
36-
vec![]
37-
}
38-
}
39-
4032
impl Command {
4133
/// Tries to create a push rule corresponding to this command
4234
pub(crate) fn to_push_rule(&self) -> Result<NewPushRule, NotificationSettingsError> {
4335
match self {
4436
Self::SetRoomPushRule { room_id, notify } => {
4537
// `Room` push rule for this `room_id`
46-
let new_rule = NewSimplePushRule::new(room_id.clone(), get_notify_actions(*notify));
38+
let new_rule = NewSimplePushRule::new(room_id.clone(), notify.get_actions());
4739
Ok(NewPushRule::Room(new_rule))
4840
}
4941

@@ -55,7 +47,7 @@ impl Command {
5547
key: "room_id".to_owned(),
5648
pattern: room_id.to_string(),
5749
}],
58-
get_notify_actions(*notify),
50+
notify.get_actions(),
5951
);
6052
Ok(NewPushRule::Override(new_rule))
6153
}
@@ -65,7 +57,7 @@ impl Command {
6557
let new_rule = NewPatternedPushRule::new(
6658
keyword.clone(),
6759
keyword.clone(),
68-
get_notify_actions(true),
60+
Notify::All.get_actions(),
6961
);
7062
Ok(NewPushRule::Content(new_rule))
7163
}
@@ -80,3 +72,28 @@ impl Command {
8072
}
8173
}
8274
}
75+
76+
/// Enum describing if and how to deliver a notification.
77+
#[derive(Clone, Debug, Eq, PartialEq)]
78+
pub(crate) enum Notify {
79+
/// Generate a notification both in-app and remote / push.
80+
All,
81+
82+
/// Only generate an in-app notification but no remote / push notification.
83+
#[cfg(feature = "unstable-msc3768")]
84+
InAppOnly,
85+
86+
/// Don't notify at all.
87+
None
88+
}
89+
90+
impl Notify {
91+
fn get_actions(&self) -> Vec<Action> {
92+
match self {
93+
Self::All => vec![Action::Notify, Action::SetTweak(Tweak::Sound("default".into()))],
94+
#[cfg(feature = "unstable-msc3768")]
95+
Self::InAppOnly => vec![Action::NotifyInApp],
96+
Self::None => vec![]
97+
}
98+
}
99+
}

crates/matrix-sdk/src/notification_settings/mod.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ mod rules;
4040
pub use matrix_sdk_base::notification_settings::RoomNotificationMode;
4141

4242
use crate::{
43-
config::RequestConfig, error::NotificationSettingsError, event_handler::EventHandlerDropGuard,
44-
Client, Result,
43+
config::RequestConfig, error::NotificationSettingsError, event_handler::EventHandlerDropGuard, notification_settings::command::Notify, Client, Result
4544
};
4645

4746
/// Whether or not a room is encrypted
@@ -320,15 +319,15 @@ impl NotificationSettings {
320319
let (new_rule_kind, notify) = match mode {
321320
RoomNotificationMode::AllMessages => {
322321
// insert a `Room` rule which notifies
323-
(RuleKind::Room, true)
322+
(RuleKind::Room, Notify::All)
324323
}
325324
RoomNotificationMode::MentionsAndKeywordsOnly => {
326325
// insert a `Room` rule which doesn't notify
327-
(RuleKind::Room, false)
326+
(RuleKind::Room, Notify::None)
328327
}
329328
RoomNotificationMode::Mute => {
330329
// insert an `Override` rule which doesn't notify
331-
(RuleKind::Override, false)
330+
(RuleKind::Override, Notify::None)
332331
}
333332
};
334333

crates/matrix-sdk/src/notification_settings/rule_commands.rs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use ruma::{
77
};
88

99
use super::command::Command;
10-
use crate::NotificationSettingsError;
10+
use crate::{notification_settings::command::Notify, NotificationSettingsError};
1111

1212
/// A `RuleCommand` allows to generate a list of `Command` needed to modify a
1313
/// `Ruleset`
@@ -27,7 +27,7 @@ impl RuleCommands {
2727
&mut self,
2828
kind: RuleKind,
2929
room_id: &RoomId,
30-
notify: bool,
30+
notify: Notify,
3131
) -> Result<(), NotificationSettingsError> {
3232
let command = match kind {
3333
RuleKind::Room => Command::SetRoomPushRule { room_id: room_id.to_owned(), notify },
@@ -210,7 +210,7 @@ mod tests {
210210
};
211211

212212
use super::RuleCommands;
213-
use crate::{error::NotificationSettingsError, notification_settings::command::Command};
213+
use crate::{error::NotificationSettingsError, notification_settings::command::{Command, Notify}};
214214

215215
fn get_server_default_ruleset() -> Ruleset {
216216
let user_id = UserId::parse("@user:matrix.org").unwrap();
@@ -225,7 +225,7 @@ mod tests {
225225
async fn test_insert_rule_room() {
226226
let room_id = get_test_room_id();
227227
let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
228-
rule_commands.insert_rule(RuleKind::Room, &room_id, true).unwrap();
228+
rule_commands.insert_rule(RuleKind::Room, &room_id, Notify::All).unwrap();
229229

230230
// A rule must have been inserted in the ruleset.
231231
assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_some());
@@ -235,7 +235,7 @@ mod tests {
235235
assert_matches!(&rule_commands.commands[0],
236236
Command::SetRoomPushRule { room_id: command_room_id, notify } => {
237237
assert_eq!(command_room_id, &room_id);
238-
assert!(notify);
238+
assert_eq!(&Notify::All, notify);
239239
}
240240
);
241241
}
@@ -244,7 +244,7 @@ mod tests {
244244
async fn test_insert_rule_override() {
245245
let room_id = get_test_room_id();
246246
let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
247-
rule_commands.insert_rule(RuleKind::Override, &room_id, true).unwrap();
247+
rule_commands.insert_rule(RuleKind::Override, &room_id, Notify::All).unwrap();
248248

249249
// A rule must have been inserted in the ruleset.
250250
assert!(rule_commands.rules.get(RuleKind::Override, &room_id).is_some());
@@ -255,7 +255,7 @@ mod tests {
255255
Command::SetOverridePushRule {room_id: command_room_id, rule_id, notify } => {
256256
assert_eq!(command_room_id, &room_id);
257257
assert_eq!(rule_id, room_id.as_str());
258-
assert!(notify);
258+
assert_eq!(&Notify::All, notify);
259259
}
260260
);
261261
}
@@ -266,17 +266,17 @@ mod tests {
266266
let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
267267

268268
assert_matches!(
269-
rule_commands.insert_rule(RuleKind::Underride, &room_id, true),
269+
rule_commands.insert_rule(RuleKind::Underride, &room_id, Notify::All),
270270
Err(NotificationSettingsError::InvalidParameter(_)) => {}
271271
);
272272

273273
assert_matches!(
274-
rule_commands.insert_rule(RuleKind::Content, &room_id, true),
274+
rule_commands.insert_rule(RuleKind::Content, &room_id, Notify::All),
275275
Err(NotificationSettingsError::InvalidParameter(_)) => {}
276276
);
277277

278278
assert_matches!(
279-
rule_commands.insert_rule(RuleKind::Sender, &room_id, true),
279+
rule_commands.insert_rule(RuleKind::Sender, &room_id, Notify::All),
280280
Err(NotificationSettingsError::InvalidParameter(_)) => {}
281281
);
282282
}

crates/matrix-sdk/src/notification_settings/rules.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -330,8 +330,7 @@ pub(crate) mod tests {
330330
use crate::{
331331
error::NotificationSettingsError,
332332
notification_settings::{
333-
rules::{self, Rules},
334-
IsEncrypted, IsOneToOne, RoomNotificationMode,
333+
command::Notify, rules::{self, Rules}, IsEncrypted, IsOneToOne, RoomNotificationMode
335334
},
336335
};
337336

@@ -614,7 +613,7 @@ pub(crate) mod tests {
614613

615614
// Build a `RuleCommands` inserting a rule
616615
let mut rules_commands = RuleCommands::new(rules.ruleset.clone());
617-
rules_commands.insert_rule(RuleKind::Override, &room_id, false).unwrap();
616+
rules_commands.insert_rule(RuleKind::Override, &room_id, Notify::None).unwrap();
618617

619618
rules.apply(rules_commands);
620619

0 commit comments

Comments
 (0)