Skip to content

Commit 056e90d

Browse files
authored
feat(sdk): Use state_after in sync v2 (#5488)
It is supposed to be an improvement over `state`, since it allows the server to send updates to the state that might not be reflected in the timeline. This is also the same behavior as in Simplified Sliding Sync. This is MSC4222 that was accepted and is about to get merged in the spec. --------- Signed-off-by: Kévin Commaille <[email protected]>
1 parent 787861e commit 056e90d

File tree

13 files changed

+242
-128
lines changed

13 files changed

+242
-128
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ ruma = { git = "https://github.com/ruma/ruma", rev = "a626e6b8521bcaa01bf8b7c416
7474
"unstable-msc4140",
7575
"unstable-msc4143",
7676
"unstable-msc4171",
77+
"unstable-msc4222",
7778
"unstable-msc4278",
7879
"unstable-msc4286",
7980
"unstable-msc4306"

crates/matrix-sdk-base/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ All notable changes to this project will be documented in this file.
77
## [Unreleased] - ReleaseDate
88

99
### Features
10+
- [**breaking**] The `state` field of `JoinedRoomUpdate` and `LeftRoomUpdate`
11+
now uses the `State` enum, depending on whether the state changes were
12+
received in the `state` field or the `state_after` field.
13+
([#5488](https://github.com/matrix-org/matrix-rust-sdk/pull/5488))
1014
- [**breaking**] `RoomCreateWithCreatorEventContent` has a new field
1115
`additional_creators` that allows to specify additional room creators beside
1216
the user sending the `m.room.create` event, introduced with room version 12.

crates/matrix-sdk-base/src/response_processors/room/msc4186/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ use crate::{
4848
Result, Room, RoomHero, RoomInfo, RoomInfoNotableUpdate, RoomInfoNotableUpdateReasons,
4949
RoomState,
5050
store::BaseStateStore,
51-
sync::{InvitedRoomUpdate, JoinedRoomUpdate, KnockedRoomUpdate, LeftRoomUpdate},
51+
sync::{InvitedRoomUpdate, JoinedRoomUpdate, KnockedRoomUpdate, LeftRoomUpdate, State},
5252
};
5353

5454
/// Represent any kind of room updates.
@@ -80,8 +80,8 @@ pub async fn update_any_room(
8080
// Don't read state events from the `timeline` field, because they might be
8181
// incomplete or staled already. We must only read state events from
8282
// `required_state`.
83-
let (raw_state_events, state_events) =
84-
state_events::sync::collect(&room_response.required_state);
83+
let state = State::from_msc4186(room_response.required_state.clone());
84+
let (raw_state_events, state_events) = state.collect(&[]);
8585

8686
let state_store = notification.state_store;
8787

@@ -190,7 +190,7 @@ pub async fn update_any_room(
190190
room_info,
191191
RoomUpdateKind::Joined(JoinedRoomUpdate::new(
192192
timeline,
193-
raw_state_events,
193+
state,
194194
room_account_data.cloned().unwrap_or_default(),
195195
ephemeral,
196196
notification_count,
@@ -203,7 +203,7 @@ pub async fn update_any_room(
203203
room_info,
204204
RoomUpdateKind::Left(LeftRoomUpdate::new(
205205
timeline,
206-
raw_state_events,
206+
state,
207207
room_account_data.cloned().unwrap_or_default(),
208208
ambiguity_changes,
209209
)),
@@ -552,3 +552,11 @@ pub(crate) async fn cache_latest_events(
552552
// the latest is last
553553
room.latest_encrypted_events.write().unwrap().extend(encrypted_events.into_iter().rev());
554554
}
555+
556+
impl State {
557+
/// Construct a [`State`] from the state changes for a joined or left room
558+
/// from a response of the Simplified Sliding Sync endpoint.
559+
fn from_msc4186(events: Vec<Raw<AnySyncStateEvent>>) -> Self {
560+
Self::After(events)
561+
}
562+
}

crates/matrix-sdk-base/src/response_processors/room/sync_v2.rs

Lines changed: 46 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use std::collections::{BTreeMap, BTreeSet};
1616

1717
use ruma::{
1818
OwnedRoomId, OwnedUserId, RoomId,
19-
api::client::sync::sync_events::v3::{InvitedRoom, JoinedRoom, KnockedRoom, LeftRoom, State},
19+
api::client::sync::sync_events::v3::{
20+
InvitedRoom, JoinedRoom, KnockedRoom, LeftRoom, State as RumaState,
21+
},
2022
};
2123
use tokio::sync::broadcast::Sender;
2224
use tracing::error;
@@ -29,7 +31,7 @@ use super::{
2931
};
3032
use crate::{
3133
Result, RoomInfoNotableUpdate, RoomState,
32-
sync::{InvitedRoomUpdate, JoinedRoomUpdate, KnockedRoomUpdate, LeftRoomUpdate},
34+
sync::{InvitedRoomUpdate, JoinedRoomUpdate, KnockedRoomUpdate, LeftRoomUpdate, State},
3335
};
3436

3537
/// Process updates of a joined room.
@@ -64,39 +66,18 @@ pub async fn update_joined_room(
6466

6567
let mut new_user_ids = BTreeSet::new();
6668

67-
let state = match joined_room.state {
68-
State::Before(state) => {
69-
let (raw_state_events, state_events) = state_events::sync::collect(&state.events);
70-
state_events::sync::dispatch(
71-
context,
72-
(&raw_state_events, &state_events),
73-
&mut room_info,
74-
ambiguity_cache,
75-
&mut new_user_ids,
76-
state_store,
77-
)
78-
.await?;
79-
80-
let (raw_state_events_from_timeline, state_events_from_timeline) =
81-
state_events::sync::collect_from_timeline(&joined_room.timeline.events);
82-
state_events::sync::dispatch(
83-
context,
84-
(&raw_state_events_from_timeline, &state_events_from_timeline),
85-
&mut room_info,
86-
ambiguity_cache,
87-
&mut new_user_ids,
88-
state_store,
89-
)
90-
.await?;
91-
92-
state
93-
}
94-
// We shouldn't receive other variants because they are opt-in.
95-
state => {
96-
error!("Unsupported State variant received for joined room: {state:?}");
97-
Default::default()
98-
}
99-
};
69+
let state = State::from_sync_v2(joined_room.state);
70+
let (raw_state_events, state_events) = state.collect(&joined_room.timeline.events);
71+
72+
state_events::sync::dispatch(
73+
context,
74+
(&raw_state_events, &state_events),
75+
&mut room_info,
76+
ambiguity_cache,
77+
&mut new_user_ids,
78+
state_store,
79+
)
80+
.await?;
10081

10182
ephemeral_events::dispatch(context, &joined_room.ephemeral.events, room_id);
10283

@@ -155,7 +136,7 @@ pub async fn update_joined_room(
155136

156137
Ok(JoinedRoomUpdate::new(
157138
timeline,
158-
state.events,
139+
state,
159140
joined_room.account_data.events,
160141
joined_room.ephemeral.events,
161142
notification_count,
@@ -189,39 +170,18 @@ pub async fn update_left_room(
189170
room_info.mark_state_partially_synced();
190171
room_info.handle_encryption_state(requested_required_states.for_room(room_id));
191172

192-
let state = match left_room.state {
193-
State::Before(state) => {
194-
let (raw_state_events, state_events) = state_events::sync::collect(&state.events);
195-
state_events::sync::dispatch(
196-
context,
197-
(&raw_state_events, &state_events),
198-
&mut room_info,
199-
ambiguity_cache,
200-
&mut (),
201-
state_store,
202-
)
203-
.await?;
204-
205-
let (raw_state_events_from_timeline, state_events_from_timeline) =
206-
state_events::sync::collect_from_timeline(&left_room.timeline.events);
207-
state_events::sync::dispatch(
208-
context,
209-
(&raw_state_events_from_timeline, &state_events_from_timeline),
210-
&mut room_info,
211-
ambiguity_cache,
212-
&mut (),
213-
state_store,
214-
)
215-
.await?;
216-
217-
state
218-
}
219-
// We shouldn't receive other variants because they are opt-in.
220-
state => {
221-
error!("Unsupported State variant received for left room: {state:?}");
222-
Default::default()
223-
}
224-
};
173+
let state = State::from_sync_v2(left_room.state);
174+
let (raw_state_events, state_events) = state.collect(&left_room.timeline.events);
175+
176+
state_events::sync::dispatch(
177+
context,
178+
(&raw_state_events, &state_events),
179+
&mut room_info,
180+
ambiguity_cache,
181+
&mut (),
182+
state_store,
183+
)
184+
.await?;
225185

226186
let timeline = timeline::build(
227187
context,
@@ -241,12 +201,7 @@ pub async fn update_left_room(
241201

242202
let ambiguity_changes = ambiguity_cache.changes.remove(room_id).unwrap_or_default();
243203

244-
Ok(LeftRoomUpdate::new(
245-
timeline,
246-
state.events,
247-
left_room.account_data.events,
248-
ambiguity_changes,
249-
))
204+
Ok(LeftRoomUpdate::new(timeline, state, left_room.account_data.events, ambiguity_changes))
250205
}
251206

252207
/// Process updates of an invited room.
@@ -320,3 +275,19 @@ pub async fn update_knocked_room(
320275

321276
Ok(knocked_room)
322277
}
278+
279+
impl State {
280+
/// Construct a [`State`] from the state changes for a joined or left room
281+
/// from a response of the sync v2 endpoint.
282+
fn from_sync_v2(state: RumaState) -> Self {
283+
match state {
284+
RumaState::Before(state) => Self::Before(state.events),
285+
RumaState::After(state) => Self::After(state.events),
286+
// We shouldn't receive other variants because they are opt-in.
287+
state => {
288+
error!("Unsupported State variant received for joined room: {state:?}");
289+
Self::default()
290+
}
291+
}
292+
}
293+
}

crates/matrix-sdk-base/src/response_processors/state_events.rs

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -45,29 +45,31 @@ pub mod sync {
4545
use crate::{
4646
RoomInfo,
4747
store::{BaseStateStore, Result as StoreResult, ambiguity_map::AmbiguityCache},
48+
sync::State,
4849
};
4950

50-
/// Collect [`AnySyncStateEvent`] to [`AnySyncStateEvent`].
51-
pub fn collect(
52-
raw_events: &[Raw<AnySyncStateEvent>],
53-
) -> (Vec<Raw<AnySyncStateEvent>>, Vec<AnySyncStateEvent>) {
54-
super::collect(raw_events)
55-
}
56-
57-
/// Collect [`AnySyncTimelineEvent`] to [`AnySyncStateEvent`].
58-
///
59-
/// A [`AnySyncTimelineEvent`] can represent either message-like events or
60-
/// state events. The message-like events are filtered out.
61-
pub fn collect_from_timeline(
62-
raw_events: &[Raw<AnySyncTimelineEvent>],
63-
) -> (Vec<Raw<AnySyncStateEvent>>, Vec<AnySyncStateEvent>) {
64-
super::collect(raw_events.iter().filter_map(|raw_event| {
65-
// Only state events have a `state_key` field.
66-
match raw_event.get_field::<&str>("state_key") {
67-
Ok(Some(_)) => Some(raw_event.cast_ref_unchecked()),
68-
_ => None,
51+
impl State {
52+
/// Collect all the state changes to update the local state, from this
53+
/// [`State`] and from the given timeline, if necessary.
54+
///
55+
/// The events that fail to deserialize are logged and filtered out.
56+
pub(crate) fn collect(
57+
&self,
58+
timeline: &[Raw<AnySyncTimelineEvent>],
59+
) -> (Vec<Raw<AnySyncStateEvent>>, Vec<AnySyncStateEvent>) {
60+
match self {
61+
Self::Before(events) => {
62+
super::collect(events.iter().chain(timeline.iter().filter_map(|raw_event| {
63+
// Only state events have a `state_key` field.
64+
match raw_event.get_field::<&str>("state_key") {
65+
Ok(Some(_)) => Some(raw_event.cast_ref_unchecked()),
66+
_ => None,
67+
}
68+
})))
69+
}
70+
Self::After(events) => super::collect(events),
6971
}
70-
}))
72+
}
7173
}
7274

7375
/// Dispatch the sync state events.

0 commit comments

Comments
 (0)