Skip to content

Commit

Permalink
notification client: use the membership state to match an invite
Browse files Browse the repository at this point in the history
  • Loading branch information
bnjbvr committed Sep 16, 2024
1 parent 119bee6 commit 965390c
Showing 1 changed file with 77 additions and 13 deletions.
90 changes: 77 additions & 13 deletions crates/matrix-sdk-ui/src/notification_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ use ruma::{
assign,
directory::RoomTypeFilter,
events::{
room::{member::StrippedRoomMemberEvent, message::SyncRoomMessageEvent},
room::{
member::{MembershipState, StrippedRoomMemberEvent},
message::SyncRoomMessageEvent,
},
AnyFullStateEventContent, AnyStateEvent, AnySyncMessageLikeEvent, AnySyncTimelineEvent,
FullStateEventContent, StateEventType, TimelineEventType,
},
Expand Down Expand Up @@ -280,9 +283,21 @@ impl NotificationClient {
/// Try to run a sliding sync (without encryption) to retrieve the event
/// from the notification.
///
/// This works by requesting explicit state that'll be useful for building
/// the `NotificationItem`, and subscribing to the room which the
/// notification relates to.
/// The event can either be:
/// - an invite event,
/// - or a non-invite event.
///
/// In case it's a non-invite event, it's rather easy: we'll request
/// explicit state that'll be useful for building the
/// `NotificationItem`, and subscribe to the room which the notification
/// relates to.
///
/// In case it's an invite-event, it's trickier because the stripped event
/// may not contain the event id, so we can't just match on it. Rather,
/// we look at stripped room member events that may be fitting (i.e.
/// match the current user and are invites), and if the SDK concludes the
/// room was in the invited state, and we didn't find the event by id,
/// *then* we'll use that stripped room member event.
#[instrument(skip_all)]
async fn try_sliding_sync(
&self,
Expand All @@ -297,9 +312,9 @@ impl NotificationClient {
// notification, so we can figure out the full event and associated
// information.

let notification = Arc::new(Mutex::new(None));
let raw_notification = Arc::new(Mutex::new(None));

let cloned_notif = notification.clone();
let handler_raw_notification = raw_notification.clone();
let target_event_id = event_id.to_owned();

let timeline_event_handler =
Expand All @@ -309,7 +324,7 @@ impl NotificationClient {
if event_id == target_event_id {
// found it! There shouldn't be a previous event before, but if there
// is, that should be ok to just replace it.
*cloned_notif.lock().unwrap() =
*handler_raw_notification.lock().unwrap() =
Some(RawNotificationEvent::Timeline(raw));
}
}
Expand All @@ -322,25 +337,57 @@ impl NotificationClient {
}
});

let cloned_notif = notification.clone();
// We'll only use this event if the room is in the invited state.
let raw_invite = Arc::new(Mutex::new(None));

let target_event_id = event_id.to_owned();
let user_id = self.client.user_id().unwrap().to_owned();
let handler_raw_invite = raw_invite.clone();
let handler_raw_notification = raw_notification.clone();
let stripped_member_handler =
self.client.add_event_handler(move |raw: Raw<StrippedRoomMemberEvent>| async move {
let deserialized = match raw.deserialize() {
Ok(d) => d,
Err(err) => {
warn!("failed to deserialize raw stripped room member event: {err}");
return;
}
};

trace!("received a stripped room member event");

// Try to match the event by event_id, as it's the most precise. In theory, we
// shouldn't receive it, so that's a first attempt.
match raw.get_field::<OwnedEventId>("event_id") {
Ok(Some(event_id)) => {
if event_id == target_event_id {
// found it! There shouldn't be a previous event before, but if there
// is, that should be ok to just replace it.
*cloned_notif.lock().unwrap() = Some(RawNotificationEvent::Invite(raw));
*handler_raw_notification.lock().unwrap() =
Some(RawNotificationEvent::Invite(raw));
return;
}
}
Ok(None) => {
warn!("a room member event had no id");
debug!("a room member event had no id");
}
Err(err) => {
warn!("a room member event id couldn't be decoded: {err}");
debug!("a room member event id couldn't be decoded: {err}");
}
}

// Try to match the event by membership and state_key for the current user.
if deserialized.content.membership == MembershipState::Invite
&& deserialized.state_key == user_id
{
debug!("found an invite event for the current user");
// This could be it! There might be several of these following each other, so
// assume it's the latest one (in sync ordering), and override a previous one if
// present.
*handler_raw_invite.lock().unwrap() = Some(RawNotificationEvent::Invite(raw));
} else {
debug!("not an invite event, or not for the current user");
}
});

// Room power levels are necessary to build the push context.
Expand Down Expand Up @@ -394,7 +441,7 @@ impl NotificationClient {
break;
}

if notification.lock().unwrap().is_some() {
if raw_notification.lock().unwrap().is_some() || raw_invite.lock().unwrap().is_some() {
// We got the event.
break;
}
Expand All @@ -409,7 +456,24 @@ impl NotificationClient {
self.client.remove_event_handler(stripped_member_handler);
self.client.remove_event_handler(timeline_event_handler);

let maybe_event = notification.lock().unwrap().take();
let mut maybe_event = raw_notification.lock().unwrap().take();

if maybe_event.is_none() {
trace!("we didn't have a non-invite event, looking for invited room now");
if let Some(room) = self.client.get_room(room_id) {
if room.state() == RoomState::Invited {
maybe_event = raw_invite.lock().unwrap().take();
} else {
debug!("the room isn't in the invited state");
}
} else {
debug!("the room isn't an invite");
}
}

let found = if maybe_event.is_some() { "" } else { "not " };
trace!("the notification event has been {found}found");

Ok(maybe_event)
}

Expand Down

0 comments on commit 965390c

Please sign in to comment.