Skip to content

Commit 76a4724

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 76a4724

2 files changed

Lines changed: 81 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: 72 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,47 @@ 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+
// `m.room.encrypted`
1008+
AnyMessageLikeEventContent::RoomEncrypted(_) => {
1009+
// These events are **explicitly** not suitable.
1010+
false
1011+
}
10011012

10021013
// Everything else is considered not suitable.
10031014
_ => false,
@@ -1098,21 +1109,6 @@ mod tests_latest_event_content {
10981109
);
10991110
}
11001111

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-
11161112
#[test]
11171113
fn test_room_message_replacement() {
11181114
let user_id = user_id!("@mnt_io:matrix.org");
@@ -1131,45 +1127,52 @@ mod tests_latest_event_content {
11311127
{
11321128
let previous_event_id = None;
11331129

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-
);
1130+
assert!(filter_timeline_event(&event, previous_event_id, Some(user_id), None).not());
11431131
}
11441132

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

11491138
assert!(
1150-
filter_timeline_event(
1151-
&event,
1152-
previous_event_id,
1153-
Some(user_id!("@mnt_io:matrix.org")),
1154-
None
1155-
)
1156-
.not()
1139+
filter_timeline_event(&event, previous_event.as_ref(), Some(user_id), None).not()
11571140
);
11581141
}
11591142

11601143
// With a previous event, and that's the one being replaced!
11611144
{
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-
));
1145+
let previous_event =
1146+
Some(event_factory.text_msg("hello").event_id(event_id!("$ev0")).into_event());
1147+
1148+
assert!(filter_timeline_event(&event, previous_event.as_ref(), Some(user_id), None));
11701149
}
11711150
}
11721151

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

0 commit comments

Comments
 (0)