Skip to content

Commit b19377a

Browse files
Hywanstefanceriu
andauthored
fix(sdk): Better handling of redacted and redaction events in Latest Event (#5932)
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. --- * Fix #5899 * Address #4112 Signed-off-by: Stefan Ceriu <[email protected]> Co-authored-by: Stefan Ceriu <[email protected]>
1 parent 76cae09 commit b19377a

File tree

2 files changed

+80
-80
lines changed

2 files changed

+80
-80
lines changed

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

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,7 @@ impl RoomEventCache {
334334
/// chunk **only**. It doesn't look inside the storage.
335335
pub async fn rfind_map_event_in_memory_by<O, P>(&self, predicate: P) -> Result<Option<O>>
336336
where
337-
P: FnMut(&Event, Option<OwnedEventId>) -> Option<O>,
337+
P: FnMut(&Event, Option<&Event>) -> Option<O>,
338338
{
339339
Ok(self.inner.state.read().await?.rfind_map_event_in_memory_by(predicate))
340340
}
@@ -1170,24 +1170,20 @@ mod private {
11701170
/// **Warning**! It looks into the loaded events from the in-memory
11711171
/// linked chunk **only**. It doesn't look inside the storage,
11721172
/// contrary to [`Self::find_event`].
1173-
pub fn rfind_map_event_in_memory_by<O, P>(&self, mut predicate: P) -> Option<O>
1173+
pub fn rfind_map_event_in_memory_by<'i, O, P>(&'i self, mut predicate: P) -> Option<O>
11741174
where
1175-
P: FnMut(&Event, Option<OwnedEventId>) -> Option<O>,
1175+
P: FnMut(&'i Event, Option<&'i Event>) -> Option<O>,
11761176
{
11771177
self.state
11781178
.room_linked_chunk
11791179
.revents()
11801180
.peekable()
11811181
.batching(|iter| {
11821182
iter.next().map(|(_position, event)| {
1183-
(
1184-
event,
1185-
iter.peek()
1186-
.and_then(|(_next_position, next_event)| next_event.event_id()),
1187-
)
1183+
(event, iter.peek().map(|(_next_position, next_event)| *next_event))
11881184
})
11891185
})
1190-
.find_map(|(event, next_event_id)| predicate(event, next_event_id))
1186+
.find_map(|(event, next_event)| predicate(event, next_event))
11911187
}
11921188

11931189
#[cfg(test)]
@@ -3911,8 +3907,8 @@ mod timed_tests {
39113907
// Look for an event from `BOB`: it must be `event_0`.
39123908
assert_matches!(
39133909
room_event_cache
3914-
.rfind_map_event_in_memory_by(|event, previous_event_id| {
3915-
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*BOB)).then(|| (event.event_id(), previous_event_id))
3910+
.rfind_map_event_in_memory_by(|event, previous_event| {
3911+
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*BOB)).then(|| (event.event_id(), previous_event.and_then(|event| event.event_id())))
39163912
})
39173913
.await,
39183914
Ok(Some((event_id, previous_event_id))) => {
@@ -3925,8 +3921,8 @@ mod timed_tests {
39253921
// because events are looked for in reverse order.
39263922
assert_matches!(
39273923
room_event_cache
3928-
.rfind_map_event_in_memory_by(|event, previous_event_id| {
3929-
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*ALICE)).then(|| (event.event_id(), previous_event_id))
3924+
.rfind_map_event_in_memory_by(|event, previous_event| {
3925+
(event.raw().get_field::<OwnedUserId>("sender").unwrap().as_deref() == Some(*ALICE)).then(|| (event.event_id(), previous_event.and_then(|event| event.event_id())))
39303926
})
39313927
.await,
39323928
Ok(Some((event_id, previous_event_id))) => {

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

Lines changed: 71 additions & 67 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]
@@ -952,7 +950,7 @@ impl LatestEventValuesForLocalEvents {
952950

953951
fn filter_timeline_event(
954952
event: &TimelineEvent,
955-
previous_event_id: Option<OwnedEventId>,
953+
previous_event: Option<&TimelineEvent>,
956954
own_user_id: Option<&UserId>,
957955
power_levels: Option<&RoomPowerLevels>,
958956
) -> bool {
@@ -975,11 +973,11 @@ fn filter_timeline_event(
975973
match message_like_event.original_content() {
976974
Some(any_message_like_event_content) => filter_any_message_like_event_content(
977975
any_message_like_event_content,
978-
previous_event_id,
976+
previous_event,
979977
),
980978

981979
// The event has been redacted.
982-
None => true,
980+
None => false,
983981
}
984982
}
985983

@@ -991,34 +989,48 @@ fn filter_timeline_event(
991989

992990
fn filter_any_message_like_event_content(
993991
event: AnyMessageLikeEventContent,
994-
previous_event_id: Option<OwnedEventId>,
992+
previous_event: Option<&TimelineEvent>,
995993
) -> bool {
996994
match event {
997-
AnyMessageLikeEventContent::RoomMessage(message) => {
995+
// `m.room.message`
996+
AnyMessageLikeEventContent::RoomMessage(RoomMessageEventContent {
997+
msgtype,
998+
relates_to,
999+
..
1000+
}) => {
9981001
// Don't show incoming verification requests.
999-
if let MessageType::VerificationRequest(_) = message.msgtype {
1002+
if let MessageType::VerificationRequest(_) = msgtype {
10001003
return false;
10011004
}
10021005

10031006
// Not all relations are accepted. Let's filter them.
1004-
match &message.relates_to {
1007+
match relates_to {
10051008
Some(Relation::Replacement(Replacement { event_id, .. })) => {
10061009
// If the edit relates to the immediate previous event, this is an acceptable
1007-
// latest event, otherwise let's ignore it.
1008-
Some(event_id) == previous_event_id.as_ref()
1010+
// latest event candidate, otherwise let's ignore it.
1011+
Some(event_id) == previous_event.and_then(|event| event.event_id())
10091012
}
10101013

10111014
_ => true,
10121015
}
10131016
}
10141017

1018+
// `org.matrix.msc3381.poll.start`
1019+
// `m.call.invite`
1020+
// `m.rtc.notification`
1021+
// `m.sticker`
10151022
AnyMessageLikeEventContent::UnstablePollStart(_)
10161023
| AnyMessageLikeEventContent::CallInvite(_)
10171024
| AnyMessageLikeEventContent::RtcNotification(_)
10181025
| AnyMessageLikeEventContent::Sticker(_) => true,
10191026

1020-
// Encrypted events are not suitable.
1021-
AnyMessageLikeEventContent::RoomEncrypted(_) => false,
1027+
// `m.room.redaction`
1028+
// `m.room.encrypted`
1029+
AnyMessageLikeEventContent::RoomRedaction(_)
1030+
| AnyMessageLikeEventContent::RoomEncrypted(_) => {
1031+
// These events are **explicitly** not suitable.
1032+
false
1033+
}
10221034

10231035
// Everything else is considered not suitable.
10241036
_ => false,
@@ -1119,21 +1131,6 @@ mod tests_latest_event_content {
11191131
);
11201132
}
11211133

1122-
#[test]
1123-
fn test_redacted() {
1124-
assert_latest_event_content!(
1125-
event | event_factory | {
1126-
event_factory
1127-
.redacted(
1128-
user_id!("@mnt_io:matrix.org"),
1129-
ruma::events::room::message::RedactedRoomMessageEventContent::new(),
1130-
)
1131-
.into_event()
1132-
}
1133-
is a candidate
1134-
);
1135-
}
1136-
11371134
#[test]
11381135
fn test_room_message_replacement() {
11391136
let user_id = user_id!("@mnt_io:matrix.org");
@@ -1152,45 +1149,52 @@ mod tests_latest_event_content {
11521149
{
11531150
let previous_event_id = None;
11541151

1155-
assert!(
1156-
filter_timeline_event(
1157-
&event,
1158-
previous_event_id,
1159-
Some(user_id!("@mnt_io:matrix.org")),
1160-
None
1161-
)
1162-
.not()
1163-
);
1152+
assert!(filter_timeline_event(&event, previous_event_id, Some(user_id), None).not());
11641153
}
11651154

1166-
// With a previous event, but the one being replaced.
1155+
// With a previous event, but not the one being replaced.
11671156
{
1168-
let previous_event_id = Some(event_id!("$ev1").to_owned());
1157+
let previous_event =
1158+
Some(event_factory.text_msg("no!").event_id(event_id!("$ev1")).into_event());
11691159

11701160
assert!(
1171-
filter_timeline_event(
1172-
&event,
1173-
previous_event_id,
1174-
Some(user_id!("@mnt_io:matrix.org")),
1175-
None
1176-
)
1177-
.not()
1161+
filter_timeline_event(&event, previous_event.as_ref(), Some(user_id), None).not()
11781162
);
11791163
}
11801164

11811165
// With a previous event, and that's the one being replaced!
11821166
{
1183-
let previous_event_id = Some(event_id!("$ev0").to_owned());
1184-
1185-
assert!(filter_timeline_event(
1186-
&event,
1187-
previous_event_id,
1188-
Some(user_id!("@mnt_io:matrix.org")),
1189-
None
1190-
));
1167+
let previous_event =
1168+
Some(event_factory.text_msg("hello").event_id(event_id!("$ev0")).into_event());
1169+
1170+
assert!(filter_timeline_event(&event, previous_event.as_ref(), Some(user_id), None));
11911171
}
11921172
}
11931173

1174+
#[test]
1175+
fn test_redaction() {
1176+
assert_latest_event_content!(
1177+
event | event_factory | { event_factory.redaction(event_id!("$ev0")).into_event() }
1178+
is not a candidate
1179+
);
1180+
}
1181+
1182+
#[test]
1183+
fn test_redacted() {
1184+
assert_latest_event_content!(
1185+
event | event_factory | {
1186+
event_factory
1187+
.redacted(
1188+
user_id!("@mnt_io:matrix.org"),
1189+
ruma::events::room::message::RedactedRoomMessageEventContent::new(),
1190+
)
1191+
.event_id(event_id!("$ev0"))
1192+
.into_event()
1193+
}
1194+
is not a candidate
1195+
);
1196+
}
1197+
11941198
#[test]
11951199
fn test_poll() {
11961200
assert_latest_event_content!(

0 commit comments

Comments
 (0)