@@ -175,6 +175,43 @@ 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 ( ) ) )
193+ . cloned ( )
194+ . collect ( ) ;
195+ for room_id in to_remove {
196+ println ! ( "unsub {:?}" , room_id) ;
197+ if room_subscriptions. remove ( & room_id) . is_some ( ) {
198+ skip_over_current_sync_loop_iteration = true ;
199+ }
200+ }
201+
202+ if subscribe_to_rooms (
203+ room_subscriptions,
204+ & self . inner . client ,
205+ room_ids,
206+ settings,
207+ cancel_in_flight_request && skip_over_current_sync_loop_iteration,
208+ ) {
209+ self . inner . internal_channel_send_if_possible (
210+ SlidingSyncInternalMessage :: SyncLoopSkipOverCurrentIteration ,
211+ ) ;
212+ }
213+ }
214+
178215 /// Replace all subscriptions to rooms by other ones.
179216 ///
180217 /// If the associated `Room`s exist, they will be marked as members are
@@ -1205,6 +1242,136 @@ mod tests {
12051242 Ok ( ( ) )
12061243 }
12071244
1245+ #[ async_test]
1246+ async fn test_resubscribe_to_rooms ( ) -> Result < ( ) > {
1247+ let ( server, sliding_sync) = new_sliding_sync ( vec ! [
1248+ SlidingSyncList :: builder( "foo" )
1249+ . sync_mode( SlidingSyncMode :: new_selective( ) . add_range( 0 ..=10 ) ) ,
1250+ ] )
1251+ . await ?;
1252+
1253+ let stream = sliding_sync. sync ( ) ;
1254+ pin_mut ! ( stream) ;
1255+
1256+ let room_id_0 = room_id ! ( "!r0:bar.org" ) ;
1257+ let room_id_1 = room_id ! ( "!r1:bar.org" ) ;
1258+ let room_id_2 = room_id ! ( "!r2:bar.org" ) ;
1259+
1260+ {
1261+ let _mock_guard = Mock :: given ( SlidingSyncMatcher )
1262+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_body_json ( json ! ( {
1263+ "pos" : "1" ,
1264+ "lists" : { } ,
1265+ "rooms" : {
1266+ room_id_0: {
1267+ "name" : "Room #0" ,
1268+ "initial" : true ,
1269+ } ,
1270+ room_id_1: {
1271+ "name" : "Room #1" ,
1272+ "initial" : true ,
1273+ } ,
1274+ room_id_2: {
1275+ "name" : "Room #2" ,
1276+ "initial" : true ,
1277+ } ,
1278+ }
1279+ } ) ) )
1280+ . mount_as_scoped ( & server)
1281+ . await ;
1282+
1283+ let _ = stream. next ( ) . await . unwrap ( ) ?;
1284+ }
1285+
1286+ let room0 = sliding_sync. inner . client . get_room ( room_id_0) . unwrap ( ) ;
1287+
1288+ // Members aren't synced.
1289+ // We need to make them synced, so that we can test that subscribing to a room
1290+ // make members not synced. That's a desired feature.
1291+ assert ! ( room0. are_members_synced( ) . not( ) ) ;
1292+
1293+ {
1294+ struct MemberMatcher ( OwnedRoomId ) ;
1295+
1296+ impl Match for MemberMatcher {
1297+ fn matches ( & self , request : & Request ) -> bool {
1298+ request. url . path ( )
1299+ == format ! ( "/_matrix/client/r0/rooms/{room_id}/members" , room_id = self . 0 )
1300+ && request. method == Method :: GET
1301+ }
1302+ }
1303+
1304+ let _mock_guard = Mock :: given ( MemberMatcher ( room_id_0. to_owned ( ) ) )
1305+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_body_json ( json ! ( {
1306+ "chunk" : [ ] ,
1307+ } ) ) )
1308+ . mount_as_scoped ( & server)
1309+ . await ;
1310+
1311+ assert_matches ! ( room0. request_members( ) . await , Ok ( ( ) ) ) ;
1312+ }
1313+
1314+ // Members are now synced! We can start subscribing and see how it goes.
1315+ assert ! ( room0. are_members_synced( ) ) ;
1316+
1317+ sliding_sync. resubscribe_to_rooms ( & [ room_id_0, room_id_1] , None , true ) ;
1318+
1319+ // OK, we have subscribed to some rooms. Let's check on `room0` if members are
1320+ // now marked as not synced.
1321+ assert ! ( room0. are_members_synced( ) . not( ) ) ;
1322+
1323+ // Both resubscribed rooms are subscribed.
1324+ {
1325+ let room_subscriptions = sliding_sync. inner . room_subscriptions . read ( ) . unwrap ( ) ;
1326+
1327+ assert ! ( room_subscriptions. contains_key( room_id_0) ) ;
1328+ assert ! ( room_subscriptions. contains_key( room_id_1) ) ;
1329+ assert ! ( !room_subscriptions. contains_key( room_id_2) ) ;
1330+ }
1331+
1332+ // Subscribing to the same room doesn't reset the member sync state.
1333+
1334+ {
1335+ struct MemberMatcher ( OwnedRoomId ) ;
1336+
1337+ impl Match for MemberMatcher {
1338+ fn matches ( & self , request : & Request ) -> bool {
1339+ request. url . path ( )
1340+ == format ! ( "/_matrix/client/r0/rooms/{room_id}/members" , room_id = self . 0 )
1341+ && request. method == Method :: GET
1342+ }
1343+ }
1344+
1345+ let _mock_guard = Mock :: given ( MemberMatcher ( room_id_0. to_owned ( ) ) )
1346+ . respond_with ( ResponseTemplate :: new ( 200 ) . set_body_json ( json ! ( {
1347+ "chunk" : [ ] ,
1348+ } ) ) )
1349+ . mount_as_scoped ( & server)
1350+ . await ;
1351+
1352+ assert_matches ! ( room0. request_members( ) . await , Ok ( ( ) ) ) ;
1353+ }
1354+
1355+ // Members are synced, good, good.
1356+ assert ! ( room0. are_members_synced( ) ) ;
1357+
1358+ sliding_sync. resubscribe_to_rooms ( & [ room_id_0] , None , false ) ;
1359+
1360+ // Members are still synced: because we have already subscribed to the
1361+ // room, the members aren't marked as unsynced.
1362+ assert ! ( room0. are_members_synced( ) ) ;
1363+
1364+ // Only the resubscribed room is subscribed.
1365+ {
1366+ let room_subscriptions = sliding_sync. inner . room_subscriptions . read ( ) . unwrap ( ) ;
1367+
1368+ assert ! ( room_subscriptions. contains_key( room_id_0) ) ;
1369+ assert ! ( !room_subscriptions. contains_key( room_id_1) ) ;
1370+ assert ! ( !room_subscriptions. contains_key( room_id_2) ) ;
1371+ }
1372+ Ok ( ( ) )
1373+ }
1374+
12081375 #[ async_test]
12091376 async fn test_room_subscriptions_are_reset_when_session_expires ( ) -> Result < ( ) > {
12101377 let ( _server, sliding_sync) = new_sliding_sync ( vec ! [
0 commit comments