feat(ffi): expose Client::register_notification_handler

Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
This commit is contained in:
Johannes Marbach
2025-11-04 15:55:41 +01:00
committed by Damir Jelić
parent 81ff96d569
commit 82c583b5bc
2 changed files with 83 additions and 1 deletions
+2
View File
@@ -62,6 +62,8 @@ All notable changes to this project will be documented in this file.
### Features
- Add `Client::register_notification_handler` for observing notifications generated from sync responses.
([#5831](https://github.com/matrix-org/matrix-rust-sdk/pull/5831))
- Add `Room::mark_as_fully_read_unchecked` so clients can mark a room as read without needing a `Timeline` instance. Note this method is not recommended as it can potentially cause incorrect read receipts, but it can needed in certain cases.
- Add `Timeline::latest_event_id` to be able to fetch the event id of the latest event of the timeline.
- Add `Room::load_or_fetch_event` so we can get a `TimelineEvent` given its event id ([#5678](https://github.com/matrix-org/matrix-rust-sdk/pull/5678)).
+81 -1
View File
@@ -16,6 +16,7 @@ use matrix_sdk::{
authentication::oauth::{
AccountManagementActionFull, ClientId, OAuthAuthorizationData, OAuthSession,
},
deserialized_responses::RawAnySyncOrStrippedTimelineEvent,
media::{MediaFormat, MediaRequestParameters, MediaRetentionPolicy, MediaThumbnailSettings},
ruma::{
api::client::{
@@ -101,7 +102,7 @@ use crate::{
authentication::{HomeserverLoginDetails, OidcConfiguration, OidcError, SsoError, SsoHandler},
client,
encryption::Encryption,
notification::NotificationClient,
notification::{NotificationClient, NotificationEvent},
notification_settings::NotificationSettings,
qr_code::LoginWithQrCodeHandler,
room::{RoomHistoryVisibility, RoomInfoListener, RoomSendQueueUpdate},
@@ -225,6 +226,25 @@ pub trait RoomAccountDataListener: SyncOutsideWasm + SendOutsideWasm {
fn on_change(&self, event: RoomAccountDataEvent, room_id: String);
}
/// A listener for notifications generated from sync responses.
///
/// This is called during sync for each event that triggers a notification
/// based on the user's push rules.
#[matrix_sdk_ffi_macros::export(callback_interface)]
pub trait SyncNotificationListener: SyncOutsideWasm + SendOutsideWasm {
/// Called when a notifying event is received during sync.
fn on_notification(&self, notification: SyncNotification, room_id: String);
}
/// A notification generated from a sync response.
#[derive(uniffi::Record)]
pub struct SyncNotification {
/// The push actions for this notification (notify, sound, highlight, etc.)
pub actions: Vec<crate::notification_settings::Action>,
/// The event that triggered the notification
pub event: NotificationEvent,
}
#[derive(Clone, Copy, uniffi::Record)]
pub struct TransmissionProgress {
pub current: u64,
@@ -818,6 +838,66 @@ impl Client {
}
}
/// Register a handler for notifications generated from sync responses.
///
/// The handler will be called during sync for each event that triggers
/// a notification based on the user's push rules.
///
/// The handler receives:
/// - The notification with push actions and event data
/// - The room ID where the notification occurred
///
/// This is useful for implementing custom notification logic, such as
/// displaying local notifications or updating notification badges.
pub async fn register_notification_handler(&self, listener: Box<dyn SyncNotificationListener>) {
let listener = Arc::new(listener);
self.inner
.register_notification_handler(move |notification, room, _client| {
let listener = listener.clone();
let room_id = room.room_id().to_string();
async move {
// Convert SDK actions to FFI type
let actions: Vec<crate::notification_settings::Action> = notification
.actions
.into_iter()
.filter_map(|action| action.try_into().ok())
.collect();
// Convert SDK event to FFI type
let event = match notification.event {
RawAnySyncOrStrippedTimelineEvent::Sync(raw) => match raw.deserialize() {
Ok(deserialized) => NotificationEvent::Timeline {
event: Arc::new(crate::event::TimelineEvent(Box::new(
deserialized,
))),
},
Err(err) => {
tracing::warn!("Failed to deserialize timeline event: {err}");
return;
}
},
RawAnySyncOrStrippedTimelineEvent::Stripped(raw) => {
match raw.deserialize() {
Ok(deserialized) => NotificationEvent::Invite {
sender: deserialized.sender().to_string(),
},
Err(err) => {
tracing::warn!(
"Failed to deserialize stripped state event: {err}"
);
return;
}
}
}
};
listener.on_notification(SyncNotification { actions, event }, room_id);
}
})
.await;
}
/// Allows generic GET requests to be made through the SDK's internal HTTP
/// client. This is useful when the caller's native HTTP client wouldn't
/// have the same configuration (such as certificates, proxies, etc.) This