Skip to content

Commit 8c866a4

Browse files
committed
feat(room_list): make RoomListService updates subscriptions non-destructively
Signed-off-by: Johannes Marbach <n0-0ne+github@mailbox.org>
1 parent 306b00e commit 8c866a4

4 files changed

Lines changed: 185 additions & 8 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`RoomListService::subscribe_to_rooms` now updates subscriptions non-destructively.

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

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,8 @@ impl RoomListService {
474474
/// room in `room_ids`, so that the [`LatestEventValue`] will automatically
475475
/// be calculated and updated for these rooms, for free.
476476
///
477-
/// All previous room subscriptions will be forgotten.
477+
/// Previous room subscriptions that are not contained in the specified room
478+
/// IDs will be forgotten.
478479
///
479480
/// [listen_to_room]: matrix_sdk::latest_events::LatestEvents::listen_to_room
480481
/// [`LatestEventValue`]: matrix_sdk::latest_events::LatestEventValue
@@ -516,11 +517,7 @@ impl RoomListService {
516517
}
517518

518519
// Subscribe to the rooms.
519-
self.sliding_sync.clear_and_subscribe_to_rooms(
520-
room_ids,
521-
Some(settings),
522-
cancel_in_flight_request,
523-
)
520+
self.sliding_sync.resubscribe_to_rooms(room_ids, Some(settings), cancel_in_flight_request)
524521
}
525522

526523
#[cfg(test)]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `SlidingSync::resubscribe_to_rooms` for updating room subscriptions non-destructively.

crates/matrix-sdk/src/sliding_sync/mod.rs

Lines changed: 180 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,42 @@ impl SlidingSync {
175175
}
176176
}
177177

178+
/// Add subscriptions to the specified rooms if they don't already exist and
179+
/// remove any existing subscriptions to other rooms.
180+
pub fn resubscribe_to_rooms(
181+
&self,
182+
room_ids: &[&RoomId],
183+
settings: Option<http::request::RoomSubscription>,
184+
cancel_in_flight_request: bool,
185+
) {
186+
let mut room_subscriptions = self.inner.room_subscriptions.write().unwrap();
187+
let mut skip_over_current_sync_loop_iteration = false;
188+
189+
// Remove rooms that are no longer subscribed.
190+
let to_remove: Vec<_> = room_subscriptions
191+
.keys()
192+
.filter(|room_id| !room_ids.contains(&room_id.as_ref())).cloned()
193+
.collect();
194+
for room_id in to_remove {
195+
println!("unsub {:?}", room_id);
196+
if room_subscriptions.remove(&room_id).is_some() {
197+
skip_over_current_sync_loop_iteration = true;
198+
}
199+
}
200+
201+
if subscribe_to_rooms(
202+
room_subscriptions,
203+
&self.inner.client,
204+
room_ids,
205+
settings,
206+
cancel_in_flight_request && skip_over_current_sync_loop_iteration,
207+
) {
208+
self.inner.internal_channel_send_if_possible(
209+
SlidingSyncInternalMessage::SyncLoopSkipOverCurrentIteration,
210+
);
211+
}
212+
}
213+
178214
/// Replace all subscriptions to rooms by other ones.
179215
///
180216
/// If the associated `Room`s exist, they will be marked as members are
@@ -186,15 +222,27 @@ impl SlidingSync {
186222
cancel_in_flight_request: bool,
187223
) {
188224
let mut room_subscriptions = self.inner.room_subscriptions.write().unwrap();
189-
room_subscriptions.clear();
225+
226+
// Remove rooms that are no longer subscribed.
227+
let to_remove: Vec<_> = room_ids
228+
.iter()
229+
.filter(|room_id| room_subscriptions.contains_key(*room_id.to_owned()))
230+
.collect();
231+
let mut any_removed = false;
232+
for room_id in to_remove {
233+
if room_subscriptions.remove(*room_id).is_some() {
234+
any_removed = true;
235+
}
236+
}
190237

191238
if subscribe_to_rooms(
192239
room_subscriptions,
193240
&self.inner.client,
194241
room_ids,
195242
settings,
196243
cancel_in_flight_request,
197-
) {
244+
) || any_removed
245+
{
198246
self.inner.internal_channel_send_if_possible(
199247
SlidingSyncInternalMessage::SyncLoopSkipOverCurrentIteration,
200248
);
@@ -1205,6 +1253,136 @@ mod tests {
12051253
Ok(())
12061254
}
12071255

1256+
#[async_test]
1257+
async fn test_resubscribe_to_rooms() -> Result<()> {
1258+
let (server, sliding_sync) = new_sliding_sync(vec![
1259+
SlidingSyncList::builder("foo")
1260+
.sync_mode(SlidingSyncMode::new_selective().add_range(0..=10)),
1261+
])
1262+
.await?;
1263+
1264+
let stream = sliding_sync.sync();
1265+
pin_mut!(stream);
1266+
1267+
let room_id_0 = room_id!("!r0:bar.org");
1268+
let room_id_1 = room_id!("!r1:bar.org");
1269+
let room_id_2 = room_id!("!r2:bar.org");
1270+
1271+
{
1272+
let _mock_guard = Mock::given(SlidingSyncMatcher)
1273+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
1274+
"pos": "1",
1275+
"lists": {},
1276+
"rooms": {
1277+
room_id_0: {
1278+
"name": "Room #0",
1279+
"initial": true,
1280+
},
1281+
room_id_1: {
1282+
"name": "Room #1",
1283+
"initial": true,
1284+
},
1285+
room_id_2: {
1286+
"name": "Room #2",
1287+
"initial": true,
1288+
},
1289+
}
1290+
})))
1291+
.mount_as_scoped(&server)
1292+
.await;
1293+
1294+
let _ = stream.next().await.unwrap()?;
1295+
}
1296+
1297+
let room0 = sliding_sync.inner.client.get_room(room_id_0).unwrap();
1298+
1299+
// Members aren't synced.
1300+
// We need to make them synced, so that we can test that subscribing to a room
1301+
// make members not synced. That's a desired feature.
1302+
assert!(room0.are_members_synced().not());
1303+
1304+
{
1305+
struct MemberMatcher(OwnedRoomId);
1306+
1307+
impl Match for MemberMatcher {
1308+
fn matches(&self, request: &Request) -> bool {
1309+
request.url.path()
1310+
== format!("/_matrix/client/r0/rooms/{room_id}/members", room_id = self.0)
1311+
&& request.method == Method::GET
1312+
}
1313+
}
1314+
1315+
let _mock_guard = Mock::given(MemberMatcher(room_id_0.to_owned()))
1316+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
1317+
"chunk": [],
1318+
})))
1319+
.mount_as_scoped(&server)
1320+
.await;
1321+
1322+
assert_matches!(room0.request_members().await, Ok(()));
1323+
}
1324+
1325+
// Members are now synced! We can start subscribing and see how it goes.
1326+
assert!(room0.are_members_synced());
1327+
1328+
sliding_sync.resubscribe_to_rooms(&[room_id_0, room_id_1], None, true);
1329+
1330+
// OK, we have subscribed to some rooms. Let's check on `room0` if members are
1331+
// now marked as not synced.
1332+
assert!(room0.are_members_synced().not());
1333+
1334+
// Both resubscribed rooms are subscribed.
1335+
{
1336+
let room_subscriptions = sliding_sync.inner.room_subscriptions.read().unwrap();
1337+
1338+
assert!(room_subscriptions.contains_key(room_id_0));
1339+
assert!(room_subscriptions.contains_key(room_id_1));
1340+
assert!(!room_subscriptions.contains_key(room_id_2));
1341+
}
1342+
1343+
// Subscribing to the same room doesn't reset the member sync state.
1344+
1345+
{
1346+
struct MemberMatcher(OwnedRoomId);
1347+
1348+
impl Match for MemberMatcher {
1349+
fn matches(&self, request: &Request) -> bool {
1350+
request.url.path()
1351+
== format!("/_matrix/client/r0/rooms/{room_id}/members", room_id = self.0)
1352+
&& request.method == Method::GET
1353+
}
1354+
}
1355+
1356+
let _mock_guard = Mock::given(MemberMatcher(room_id_0.to_owned()))
1357+
.respond_with(ResponseTemplate::new(200).set_body_json(json!({
1358+
"chunk": [],
1359+
})))
1360+
.mount_as_scoped(&server)
1361+
.await;
1362+
1363+
assert_matches!(room0.request_members().await, Ok(()));
1364+
}
1365+
1366+
// Members are synced, good, good.
1367+
assert!(room0.are_members_synced());
1368+
1369+
sliding_sync.resubscribe_to_rooms(&[room_id_0], None, false);
1370+
1371+
// Members are still synced: because we have already subscribed to the
1372+
// room, the members aren't marked as unsynced.
1373+
assert!(room0.are_members_synced());
1374+
1375+
// Only the resubscribed room is subscribed.
1376+
{
1377+
let room_subscriptions = sliding_sync.inner.room_subscriptions.read().unwrap();
1378+
1379+
assert!(room_subscriptions.contains_key(room_id_0));
1380+
assert!(!room_subscriptions.contains_key(room_id_1));
1381+
assert!(!room_subscriptions.contains_key(room_id_2));
1382+
}
1383+
Ok(())
1384+
}
1385+
12081386
#[async_test]
12091387
async fn test_room_subscriptions_are_reset_when_session_expires() -> Result<()> {
12101388
let (_server, sliding_sync) = new_sliding_sync(vec![

0 commit comments

Comments
 (0)