Skip to content

Commit 14197e5

Browse files
committed
fix(sdk): Introduce LatestEventValue::LocalHasBeenSent.
The problem we are trying to solve is the following: - a local event is being sent, - the `LatestEventValue` is `LocalIsSending`, - the local event is finally sent, - the `LatestEventValue` is still `LocalIsSending` purposely, with the hope that an update from the Event Cache will replace it. But sometimes, this update from the Event Cache comes **before** the update from the Send Queue. Why is it problem? Because updates from the Event Cache are ignored until the buffer of local `LatestEventValue`s aren't empty, which means that if an update from the Event Cache is received before `RoomSendQueueUpdate::SentEvent`, it is ignored, and the `LatestEventValue` stays in the `LocalIsSending` state. That's annoying. The idea is to introduce a new state: `LocalHasBeenSent` which mimics `Remote`, but for a local event. It clarifies the state of a sent event, without relying on the Event Cache.
1 parent 1480ede commit 14197e5

File tree

5 files changed

+217
-77
lines changed

5 files changed

+217
-77
lines changed

bindings/matrix-sdk-ffi/src/timeline/mod.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,9 @@ use matrix_sdk_common::{
3131
};
3232
use matrix_sdk_ui::timeline::{
3333
self, AttachmentConfig, AttachmentSource, EventItemOrigin,
34-
LatestEventValue as UiLatestEventValue, MediaUploadProgress as SdkMediaUploadProgress, Profile,
35-
TimelineDetails, TimelineUniqueId as SdkTimelineUniqueId,
34+
LatestEventValue as UiLatestEventValue, LatestEventValueLocalState,
35+
MediaUploadProgress as SdkMediaUploadProgress, Profile, TimelineDetails,
36+
TimelineUniqueId as SdkTimelineUniqueId,
3637
};
3738
use mime::Mime;
3839
use reply::{EmbeddedEventDetails, InReplyToDetails};
@@ -1301,7 +1302,7 @@ impl LazyTimelineItemProvider {
13011302
}
13021303

13031304
/// Mimic the [`UiLatestEventValue`] type.
1304-
#[derive(Clone, uniffi::Enum)]
1305+
#[derive(uniffi::Enum)]
13051306
pub enum LatestEventValue {
13061307
None,
13071308
Remote {
@@ -1316,7 +1317,7 @@ pub enum LatestEventValue {
13161317
sender: String,
13171318
profile: ProfileDetails,
13181319
content: TimelineItemContent,
1319-
is_sending: bool,
1320+
state: LatestEventValueLocalState,
13201321
},
13211322
}
13221323

@@ -1333,13 +1334,13 @@ impl From<UiLatestEventValue> for LatestEventValue {
13331334
content: content.into(),
13341335
}
13351336
}
1336-
UiLatestEventValue::Local { timestamp, sender, profile, content, is_sending } => {
1337+
UiLatestEventValue::Local { timestamp, sender, profile, content, state } => {
13371338
Self::Local {
13381339
timestamp: timestamp.into(),
13391340
sender: sender.to_string(),
13401341
profile: profile.into(),
13411342
content: content.into(),
1342-
is_sending,
1343+
state,
13431344
}
13441345
}
13451346
}

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

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ pub enum LatestEventValue {
1919
/// The latest event represents a local event that is sending.
2020
LocalIsSending(LocalLatestEventValue),
2121

22+
/// The latest event represents a local event that has been sent
23+
/// successfully. It should come quickly as a [`Self::Remote`].
24+
LocalHasBeenSent(LocalLatestEventValue),
25+
2226
/// The latest event represents a local event that cannot be sent, either
2327
/// because a previous local event, or this local event cannot be sent.
2428
LocalCannotBeSent(LocalLatestEventValue),
@@ -28,19 +32,21 @@ impl LatestEventValue {
2832
/// Get the timestamp of the [`LatestEventValue`].
2933
///
3034
/// If it's [`None`], it returns `None`. If it's [`Remote`], it returns the
31-
/// [`TimelineEvent::timestamp`]. If it's [`LocalIsSending`] or
32-
/// [`LocalCannotBeSent`], it returns the
35+
/// [`TimelineEvent::timestamp`]. If it's [`LocalIsSending`],
36+
/// [`LocalHasBeenSent`] or [`LocalCannotBeSent`], it returns the
3337
/// [`LocalLatestEventValue::timestamp`] value.
3438
///
3539
/// [`None`]: LatestEventValue::None
3640
/// [`Remote`]: LatestEventValue::Remote
3741
/// [`LocalIsSending`]: LatestEventValue::LocalIsSending
42+
/// [`LocalHasBeenSent`]: LatestEventValue::LocalHasBeenSent
3843
/// [`LocalCannotBeSent`]: LatestEventValue::LocalCannotBeSent
3944
pub fn timestamp(&self) -> Option<MilliSecondsSinceUnixEpoch> {
4045
match self {
4146
Self::None => None,
4247
Self::Remote(remote_latest_event_value) => remote_latest_event_value.timestamp(),
4348
Self::LocalIsSending(LocalLatestEventValue { timestamp, .. })
49+
| Self::LocalHasBeenSent(LocalLatestEventValue { timestamp, .. })
4450
| Self::LocalCannotBeSent(LocalLatestEventValue { timestamp, .. }) => Some(*timestamp),
4551
}
4652
}
@@ -52,7 +58,9 @@ impl LatestEventValue {
5258
/// [`LocalCannotBeSent`]: LatestEventValue::LocalCannotBeSent
5359
pub fn is_local(&self) -> bool {
5460
match self {
55-
Self::LocalIsSending(_) | Self::LocalCannotBeSent(_) => true,
61+
Self::LocalIsSending(_) | Self::LocalHasBeenSent(_) | Self::LocalCannotBeSent(_) => {
62+
true
63+
}
5664
Self::None | Self::Remote(_) => false,
5765
}
5866
}
@@ -132,6 +140,19 @@ mod tests_latest_event_value {
132140
assert_eq!(value.timestamp(), Some(MilliSecondsSinceUnixEpoch(uint!(42))));
133141
}
134142

143+
#[test]
144+
fn test_timestamp_with_local_has_been_sent() {
145+
let value = LatestEventValue::LocalHasBeenSent(LocalLatestEventValue {
146+
timestamp: MilliSecondsSinceUnixEpoch(uint!(42)),
147+
content: SerializableEventContent::new(&AnyMessageLikeEventContent::RoomMessage(
148+
RoomMessageEventContent::text_plain("raclette"),
149+
))
150+
.unwrap(),
151+
});
152+
153+
assert_eq!(value.timestamp(), Some(MilliSecondsSinceUnixEpoch(uint!(42))));
154+
}
155+
135156
#[test]
136157
fn test_timestamp_with_local_cannot_be_sent() {
137158
let value = LatestEventValue::LocalCannotBeSent(LocalLatestEventValue {

crates/matrix-sdk-ui/src/timeline/latest_event.rs

Lines changed: 68 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,12 +69,19 @@ pub enum LatestEventValue {
6969
/// The content of the local event.
7070
content: TimelineItemContent,
7171

72-
/// Whether the local event is sending if it is set to `true`, otherwise
73-
/// it cannot be sent.
74-
is_sending: bool,
72+
/// Whether the local event is sending, has been sent or cannot be sent.
73+
state: LatestEventValueLocalState,
7574
},
7675
}
7776

77+
#[derive(Debug)]
78+
#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
79+
pub enum LatestEventValueLocalState {
80+
IsSending,
81+
HasBeenSent,
82+
CannotBeSent,
83+
}
84+
7885
impl LatestEventValue {
7986
pub(crate) async fn from_base_latest_event_value(
8087
value: BaseLatestEventValue,
@@ -153,14 +160,11 @@ impl LatestEventValue {
153160
_ => Self::None,
154161
}
155162
}
156-
BaseLatestEventValue::LocalIsSending(LocalLatestEventValue {
157-
timestamp,
158-
content: ref serialized_content,
159-
})
160-
| BaseLatestEventValue::LocalCannotBeSent(LocalLatestEventValue {
161-
timestamp,
162-
content: ref serialized_content,
163-
}) => {
163+
BaseLatestEventValue::LocalIsSending(ref local_value)
164+
| BaseLatestEventValue::LocalHasBeenSent(ref local_value)
165+
| BaseLatestEventValue::LocalCannotBeSent(ref local_value) => {
166+
let LocalLatestEventValue { timestamp, content: serialized_content } = local_value;
167+
164168
let Ok(message_like_event_content) = serialized_content.deserialize() else {
165169
return Self::None;
166170
};
@@ -172,12 +176,28 @@ impl LatestEventValue {
172176
.await
173177
.map(TimelineDetails::Ready)
174178
.unwrap_or(TimelineDetails::Unavailable);
175-
let is_sending = matches!(value, BaseLatestEventValue::LocalIsSending(_));
176179

177180
match TimelineAction::from_content(message_like_event_content, None, None, None) {
178-
TimelineAction::AddItem { content } => {
179-
Self::Local { timestamp, sender, profile, content, is_sending }
180-
}
181+
TimelineAction::AddItem { content } => Self::Local {
182+
timestamp: *timestamp,
183+
sender,
184+
profile,
185+
content,
186+
state: match value {
187+
BaseLatestEventValue::LocalIsSending(_) => {
188+
LatestEventValueLocalState::IsSending
189+
}
190+
BaseLatestEventValue::LocalHasBeenSent(_) => {
191+
LatestEventValueLocalState::HasBeenSent
192+
}
193+
BaseLatestEventValue::LocalCannotBeSent(_) => {
194+
LatestEventValueLocalState::CannotBeSent
195+
}
196+
BaseLatestEventValue::Remote(_) | BaseLatestEventValue::None => {
197+
unreachable!("Only local latest events are supposed to be handled");
198+
}
199+
},
200+
},
181201

182202
TimelineAction::HandleAggregation { kind, .. } => {
183203
// Add some debug logging here to help diagnose issues with the latest
@@ -210,7 +230,7 @@ mod tests {
210230

211231
use super::{
212232
super::{MsgLikeContent, MsgLikeKind, TimelineItemContent},
213-
BaseLatestEventValue, LatestEventValue, TimelineDetails,
233+
BaseLatestEventValue, LatestEventValue, LatestEventValueLocalState, TimelineDetails,
214234
};
215235

216236
#[async_test]
@@ -275,15 +295,43 @@ mod tests {
275295
let value =
276296
LatestEventValue::from_base_latest_event_value(base_value, &room, &client).await;
277297

278-
assert_matches!(value, LatestEventValue::Local { timestamp, sender, profile, content, is_sending } => {
298+
assert_matches!(value, LatestEventValue::Local { timestamp, sender, profile, content, state } => {
299+
assert_eq!(u64::from(timestamp.get()), 42u64);
300+
assert_eq!(sender, "@example:localhost");
301+
assert_matches!(profile, TimelineDetails::Unavailable);
302+
assert_matches!(
303+
content,
304+
TimelineItemContent::MsgLike(MsgLikeContent { kind: MsgLikeKind::Message(_), .. })
305+
);
306+
assert_matches!(state, LatestEventValueLocalState::IsSending);
307+
})
308+
}
309+
310+
#[async_test]
311+
async fn test_local_has_been_sent() {
312+
let server = MatrixMockServer::new().await;
313+
let client = server.client_builder().build().await;
314+
let room = server.sync_room(&client, JoinedRoomBuilder::new(room_id!("!r0"))).await;
315+
316+
let base_value = BaseLatestEventValue::LocalHasBeenSent(LocalLatestEventValue {
317+
timestamp: MilliSecondsSinceUnixEpoch(uint!(42)),
318+
content: SerializableEventContent::new(&AnyMessageLikeEventContent::RoomMessage(
319+
RoomMessageEventContent::text_plain("raclette"),
320+
))
321+
.unwrap(),
322+
});
323+
let value =
324+
LatestEventValue::from_base_latest_event_value(base_value, &room, &client).await;
325+
326+
assert_matches!(value, LatestEventValue::Local { timestamp, sender, profile, content, state } => {
279327
assert_eq!(u64::from(timestamp.get()), 42u64);
280328
assert_eq!(sender, "@example:localhost");
281329
assert_matches!(profile, TimelineDetails::Unavailable);
282330
assert_matches!(
283331
content,
284332
TimelineItemContent::MsgLike(MsgLikeContent { kind: MsgLikeKind::Message(_), .. })
285333
);
286-
assert!(is_sending);
334+
assert_matches!(state, LatestEventValueLocalState::HasBeenSent);
287335
})
288336
}
289337

@@ -303,15 +351,15 @@ mod tests {
303351
let value =
304352
LatestEventValue::from_base_latest_event_value(base_value, &room, &client).await;
305353

306-
assert_matches!(value, LatestEventValue::Local { timestamp, sender, profile, content, is_sending } => {
354+
assert_matches!(value, LatestEventValue::Local { timestamp, sender, profile, content, state } => {
307355
assert_eq!(u64::from(timestamp.get()), 42u64);
308356
assert_eq!(sender, "@example:localhost");
309357
assert_matches!(profile, TimelineDetails::Unavailable);
310358
assert_matches!(
311359
content,
312360
TimelineItemContent::MsgLike(MsgLikeContent { kind: MsgLikeKind::Message(_), .. })
313361
);
314-
assert!(is_sending.not());
362+
assert_matches!(state, LatestEventValueLocalState::CannotBeSent);
315363
})
316364
}
317365

crates/matrix-sdk-ui/src/timeline/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub use self::{
101101
},
102102
event_type_filter::TimelineEventTypeFilter,
103103
item::{TimelineItem, TimelineItemKind, TimelineUniqueId},
104-
latest_event::LatestEventValue,
104+
latest_event::{LatestEventValue, LatestEventValueLocalState},
105105
traits::RoomExt,
106106
virtual_item::VirtualTimelineItem,
107107
};

0 commit comments

Comments
 (0)