Skip to content

Commit 36c5c25

Browse files
committed
fix(sdk): Better handling of redacted and redaction events in Latest Event.
This patch revisits the way redacted and redaction events are handled in the Latest Event. Previously, all redacted events were considered suitable candidate. It's no longer the case. Redaction and redacted events are no longer considered suitable. This patch also revisits `rfind_map_event_in_memory_by` to return a `&TimelineEvent` instead of an `OwnedEventId`, which could be more performant in the future. The tests have been updated accordingly.
1 parent 238e4e8 commit 36c5c25

2 files changed

Lines changed: 83 additions & 82 deletions

File tree

crates/matrix-sdk/src/event_cache/room/mod.rs

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ impl RoomEventCache {
329329
/// chunk **only**. It doesn't look inside the storage.
330330
pub async fn rfind_map_event_in_memory_by<O, P>(&self, predicate: P) -> Result<Option<O>>
331331
where
332-
P: FnMut(&Event, Option<OwnedEventId>) -> Option<O>,
332+
P: FnMut(&Event, Option<&Event>) -> Option<O>,
333333
{
334334
Ok(self.inner.state.read().await?.rfind_map_event_in_memory_by(predicate))
335335
}
@@ -1117,24 +1117,20 @@ mod private {
11171117
/// **Warning**! It looks into the loaded events from the in-memory
11181118
/// linked chunk **only**. It doesn't look inside the storage,
11191119
/// contrary to [`Self::find_event`].
1120-
pub fn rfind_map_event_in_memory_by<O, P>(&self, mut predicate: P) -> Option<O>
1120+
pub fn rfind_map_event_in_memory_by<'i, O, P>(&'i self, mut predicate: P) -> Option<O>
11211121
where
1122-
P: FnMut(&Event, Option<OwnedEventId>) -> Option<O>,
1122+
P: FnMut(&'i Event, Option<&'i Event>) -> Option<O>,
11231123
{
11241124
self.state
11251125
.room_linked_chunk
11261126
.revents()
11271127
.peekable()
11281128
.batching(|iter| {
11291129
iter.next().map(|(_position, event)| {
1130-
(
1131-
event,
1132-
iter.peek()
1133-
.and_then(|(_next_position, next_event)| next_event.event_id()),
1134-
)
1130+
(event, iter.peek().map(|(_next_position, next_event)| *next_event))
11351131
})
11361132
})
1137-
.find_map(|(event, next_event_id)| predicate(event, next_event_id))
1133+
.find_map(|(event, next_event)| predicate(event, next_event))
11381134
}
11391135

11401136
#[cfg(test)]
@@ -3819,8 +3815,8 @@ mod timed_tests {
38193815
// Look for an event from `BOB`: it must be `event_0`.
38203816
assert_matches!(
38213817
room_event_cache
3822-
.rfind_map_event_in_memory_by(|event, previous_event_id| {
3823-
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*BOB)).then(|| (event.event_id(), previous_event_id))
3818+
.rfind_map_event_in_memory_by(|event, previous_event| {
3819+
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*BOB)).then(|| (event.event_id(), previous_event.and_then(|event| event.event_id())))
38243820
})
38253821
.await,
38263822
Ok(Some((event_id, previous_event_id))) => {
@@ -3833,8 +3829,8 @@ mod timed_tests {
38333829
// because events are looked for in reverse order.
38343830
assert_matches!(
38353831
room_event_cache
3836-
.rfind_map_event_in_memory_by(|event, previous_event_id| {
3837-
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*ALICE)).then(|| (event.event_id(), previous_event_id))
3832+
.rfind_map_event_in_memory_by(|event, previous_event| {
3833+
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*ALICE)).then(|| (event.event_id(), previous_event.and_then(|event| event.event_id())))
38383834
})
38393835
.await,
38403836
Ok(Some((event_id, previous_event_id))) => {

crates/matrix-sdk/src/latest_events/latest_event.rs

Lines changed: 74 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ use ruma::{
3232
relation::Replacement,
3333
room::{
3434
member::MembershipState,
35-
message::{MessageType, Relation},
35+
message::{MessageType, Relation, RoomMessageEventContent},
3636
power_levels::RoomPowerLevels,
3737
},
3838
},
@@ -135,15 +135,16 @@ impl LatestEvent {
135135
self.update(new_value).await;
136136
}
137137

138-
/// Update [`Self::current_value`] if and only if the `new_value` is not
139-
/// [`LatestEventValue::None`].
138+
/// Update [`Self::current_value`], and persist the `new_value` in the
139+
/// store.
140+
///
141+
/// If the `new_value` is [`LatestEventValue::None`], it is accepted: if the
142+
/// previous latest event value has been redacted and no other candidate has
143+
/// been found, we want to latest event value to be `None`, so that it is
144+
/// erased correctly.
140145
async fn update(&mut self, new_value: LatestEventValue) {
141-
if let LatestEventValue::None = new_value {
142-
// Do not update to a `None` value.
143-
} else {
144-
self.current_value.set(new_value.clone()).await;
145-
self.store(new_value).await;
146-
}
146+
self.current_value.set(new_value.clone()).await;
147+
self.store(new_value).await;
147148
}
148149

149150
/// Update the `RoomInfo` associated to this room to set the new
@@ -232,7 +233,7 @@ mod tests_latest_event {
232233
}
233234

234235
#[async_test]
235-
async fn test_update_ignores_none_value() {
236+
async fn test_update_do_not_ignore_none_value() {
236237
let room_id = room_id!("!r0");
237238

238239
let server = MatrixMockServer::new().await;
@@ -262,13 +263,10 @@ mod tests_latest_event {
262263
LatestEventValue::LocalIsSending(_)
263264
);
264265

265-
// Finally, set a new `None` value. It must be ignored.
266+
// Finally, set a new `None` value. It must NOT be ignored.
266267
latest_event.update(LatestEventValue::None).await;
267268

268-
assert_matches!(
269-
latest_event.current_value.get().await,
270-
LatestEventValue::LocalIsSending(_)
271-
);
269+
assert_matches!(latest_event.current_value.get().await, LatestEventValue::None);
272270
}
273271

274272
#[async_test]
@@ -519,8 +517,8 @@ impl LatestEventValueBuilder {
519517
power_levels: Option<&RoomPowerLevels>,
520518
) -> LatestEventValue {
521519
if let Ok(Some(event)) = room_event_cache
522-
.rfind_map_event_in_memory_by(|event, previous_event_id| {
523-
filter_timeline_event(event, previous_event_id, own_user_id, power_levels)
520+
.rfind_map_event_in_memory_by(|event, previous_event| {
521+
filter_timeline_event(event, previous_event, own_user_id, power_levels)
524522
.then(|| event.clone())
525523
})
526524
.await
@@ -931,7 +929,7 @@ impl LatestEventValuesForLocalEvents {
931929

932930
fn filter_timeline_event(
933931
event: &TimelineEvent,
934-
previous_event_id: Option<OwnedEventId>,
932+
previous_event: Option<&TimelineEvent>,
935933
own_user_id: Option<&UserId>,
936934
power_levels: Option<&RoomPowerLevels>,
937935
) -> bool {
@@ -954,11 +952,11 @@ fn filter_timeline_event(
954952
match message_like_event.original_content() {
955953
Some(any_message_like_event_content) => filter_any_message_like_event_content(
956954
any_message_like_event_content,
957-
previous_event_id,
955+
previous_event,
958956
),
959957

960958
// The event has been redacted.
961-
None => true,
959+
None => false,
962960
}
963961
}
964962

@@ -970,34 +968,49 @@ fn filter_timeline_event(
970968

971969
fn filter_any_message_like_event_content(
972970
event: AnyMessageLikeEventContent,
973-
previous_event_id: Option<OwnedEventId>,
971+
previous_event: Option<&TimelineEvent>,
974972
) -> bool {
975973
match event {
976-
AnyMessageLikeEventContent::RoomMessage(message) => {
974+
// `m.room.message`
975+
AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent {
976+
msgtype,
977+
relates_to,
978+
..
979+
}) => {
977980
// Don't show incoming verification requests.
978-
if let MessageType::VerificationRequest(_) = message.msgtype {
981+
if let MessageType::VerificationRequest(_) = msgtype {
979982
return false;
980983
}
981984

982985
// Not all relations are accepted. Let's filter them.
983-
match &message.relates_to {
986+
match relates_to {
984987
Some(Relation::Replacement(Replacement { event_id, .. })) => {
985988
// If the edit relates to the immediate previous event, this is an acceptable
986-
// latest event, otherwise let's ignore it.
987-
Some(event_id) == previous_event_id.as_ref()
989+
// latest event candidate, otherwise let's ignore it.
990+
Some(event_id) == previous_event.and_then(|event| event.event_id())
988991
}
989992

990993
_ => true,
991994
}
992995
}
993996

997+
// `org.matrix.msc3381.poll.start`
998+
// `m.call.invite`
999+
// `m.rtc.notification`
1000+
// `m.sticker`
9941001
AnyMessageLikeEventContent::UnstablePollStart(_)
9951002
| AnyMessageLikeEventContent::CallInvite(_)
9961003
| AnyMessageLikeEventContent::RtcNotification(_)
9971004
| AnyMessageLikeEventContent::Sticker(_) => true,
9981005

999-
// Encrypted events are not suitable.
1000-
AnyMessageLikeEventContent::RoomEncrypted(_) => false,
1006+
// `m.room.redaction`
1007+
AnyMessageLikeEventContent::RoomRedaction(_) => false,
1008+
1009+
// `m.room.encrypted`
1010+
AnyMessageLikeEventContent::RoomEncrypted(_) => {
1011+
// Encrypted events are **explicitly** not suitable.
1012+
false
1013+
}
10011014

10021015
// Everything else is considered not suitable.
10031016
_ => false,
@@ -1098,21 +1111,6 @@ mod tests_latest_event_content {
10981111
);
10991112
}
11001113

1101-
#[test]
1102-
fn test_redacted() {
1103-
assert_latest_event_content!(
1104-
event | event_factory | {
1105-
event_factory
1106-
.redacted(
1107-
user_id!("@mnt_io:matrix.org"),
1108-
ruma::events::room::message::RedactedRoomMessageEventContent::new(),
1109-
)
1110-
.into_event()
1111-
}
1112-
is a candidate
1113-
);
1114-
}
1115-
11161114
#[test]
11171115
fn test_room_message_replacement() {
11181116
let user_id = user_id!("@mnt_io:matrix.org");
@@ -1131,45 +1129,52 @@ mod tests_latest_event_content {
11311129
{
11321130
let previous_event_id = None;
11331131

1134-
assert!(
1135-
filter_timeline_event(
1136-
&event,
1137-
previous_event_id,
1138-
Some(user_id!("@mnt_io:matrix.org")),
1139-
None
1140-
)
1141-
.not()
1142-
);
1132+
assert!(filter_timeline_event(&event, previous_event_id, Some(user_id), None).not());
11431133
}
11441134

1145-
// With a previous event, but the one being replaced.
1135+
// With a previous event, but not the one being replaced.
11461136
{
1147-
let previous_event_id = Some(event_id!("$ev1").to_owned());
1137+
let previous_event =
1138+
Some(event_factory.text_msg("no!").event_id(event_id!("$ev1")).into_event());
11481139

11491140
assert!(
1150-
filter_timeline_event(
1151-
&event,
1152-
previous_event_id,
1153-
Some(user_id!("@mnt_io:matrix.org")),
1154-
None
1155-
)
1156-
.not()
1141+
filter_timeline_event(&event, previous_event.as_ref(), Some(user_id), None).not()
11571142
);
11581143
}
11591144

11601145
// With a previous event, and that's the one being replaced!
11611146
{
1162-
let previous_event_id = Some(event_id!("$ev0").to_owned());
1163-
1164-
assert!(filter_timeline_event(
1165-
&event,
1166-
previous_event_id,
1167-
Some(user_id!("@mnt_io:matrix.org")),
1168-
None
1169-
));
1147+
let previous_event =
1148+
Some(event_factory.text_msg("hello").event_id(event_id!("$ev0")).into_event());
1149+
1150+
assert!(filter_timeline_event(&event, previous_event.as_ref(), Some(user_id), None));
11701151
}
11711152
}
11721153

1154+
#[test]
1155+
fn test_redaction() {
1156+
assert_latest_event_content!(
1157+
event | event_factory | { event_factory.redaction(event_id!("$ev0")).into_event() }
1158+
is not a candidate
1159+
);
1160+
}
1161+
1162+
#[test]
1163+
fn test_redacted() {
1164+
assert_latest_event_content!(
1165+
event | event_factory | {
1166+
event_factory
1167+
.redacted(
1168+
user_id!("@mnt_io:matrix.org"),
1169+
ruma::events::room::message::RedactedRoomMessageEventContent::new(),
1170+
)
1171+
.event_id(event_id!("$ev0"))
1172+
.into_event()
1173+
}
1174+
is not a candidate
1175+
);
1176+
}
1177+
11731178
#[test]
11741179
fn test_poll() {
11751180
assert_latest_event_content!(

0 commit comments

Comments
 (0)