1use std::{cmp::Ordering, collections::HashMap, sync::Arc};
16
17use eyeball::{ObservableWriteGuard, SharedObservable, Subscriber};
18use eyeball_im::{ObservableVector, VectorSubscriberBatchedStream};
19use futures_util::pin_mut;
20use imbl::Vector;
21use itertools::Itertools;
22use matrix_sdk::{Client, Error, executor::AbortOnDrop, locks::Mutex, paginators::PaginationToken};
23use matrix_sdk_common::executor::spawn;
24use ruma::{
25 OwnedRoomId,
26 api::client::space::get_hierarchy,
27 events::space::child::{HierarchySpaceChildEvent, SpaceChildEventContent},
28 uint,
29};
30use tokio::sync::Mutex as AsyncMutex;
31use tracing::{error, warn};
32
33use crate::spaces::SpaceRoom;
34
35#[cfg_attr(feature = "uniffi", derive(uniffi::Enum))]
36#[derive(Clone, Debug, Eq, PartialEq)]
37pub enum SpaceRoomListPaginationState {
38 Idle { end_reached: bool },
39 Loading,
40}
41
42pub struct SpaceRoomList {
104 client: Client,
105
106 space_id: OwnedRoomId,
107
108 space: SharedObservable<Option<SpaceRoom>>,
109
110 children_state: Mutex<Option<HashMap<OwnedRoomId, HierarchySpaceChildEvent>>>,
111
112 token: AsyncMutex<PaginationToken>,
113
114 pagination_state: SharedObservable<SpaceRoomListPaginationState>,
115
116 rooms: Arc<Mutex<ObservableVector<SpaceRoom>>>,
117
118 _space_update_handle: Option<AbortOnDrop<()>>,
119
120 _room_update_handle: AbortOnDrop<()>,
121}
122
123impl SpaceRoomList {
124 pub async fn new(client: Client, space_id: OwnedRoomId) -> Self {
126 let rooms = Arc::new(Mutex::new(ObservableVector::<SpaceRoom>::new()));
127
128 let all_room_updates_receiver = client.subscribe_to_all_room_updates();
129
130 let room_update_handle = spawn({
131 let client = client.clone();
132 let rooms = rooms.clone();
133
134 async move {
135 pin_mut!(all_room_updates_receiver);
136
137 loop {
138 match all_room_updates_receiver.recv().await {
139 Ok(updates) => {
140 if updates.is_empty() {
141 continue;
142 }
143
144 let mut mutable_rooms = rooms.lock();
145
146 updates.iter_all_room_ids().for_each(|updated_room_id| {
147 if let Some((position, room)) = mutable_rooms
148 .clone()
149 .iter()
150 .find_position(|room| &room.room_id == updated_room_id)
151 && let Some(updated_room) = client.get_room(updated_room_id)
152 {
153 mutable_rooms.set(
154 position,
155 SpaceRoom::new_from_known(
156 &updated_room,
157 room.children_count,
158 ),
159 );
160 }
161 })
162 }
163 Err(err) => {
164 error!("error when listening to room updates: {err}");
165 }
166 }
167 }
168 }
169 });
170
171 let space_observable = SharedObservable::new(None);
172
173 let (space_room, space_update_handle) = if let Some(parent) = client.get_room(&space_id) {
174 let children_count = parent
175 .get_state_events_static::<SpaceChildEventContent>()
176 .await
177 .map_or(0, |c| c.len() as u64);
178
179 let mut subscriber = parent.subscribe_info();
180 let space_update_handle = spawn({
181 let client = client.clone();
182 let space_id = space_id.clone();
183 let space_observable = space_observable.clone();
184 async move {
185 while subscriber.next().await.is_some() {
186 if let Some(room) = client.get_room(&space_id) {
187 space_observable
188 .set(Some(SpaceRoom::new_from_known(&room, children_count)));
189 }
190 }
191 }
192 });
193
194 (
195 Some(SpaceRoom::new_from_known(&parent, children_count)),
196 Some(AbortOnDrop::new(space_update_handle)),
197 )
198 } else {
199 (None, None)
200 };
201
202 space_observable.set(space_room);
203
204 Self {
205 client,
206 space_id,
207 space: space_observable,
208 children_state: Mutex::new(None),
209 token: AsyncMutex::new(None.into()),
210 pagination_state: SharedObservable::new(SpaceRoomListPaginationState::Idle {
211 end_reached: false,
212 }),
213 rooms,
214 _space_update_handle: space_update_handle,
215 _room_update_handle: AbortOnDrop::new(room_update_handle),
216 }
217 }
218
219 pub fn space(&self) -> Option<SpaceRoom> {
221 self.space.get()
222 }
223
224 pub fn subscribe_to_space_updates(&self) -> Subscriber<Option<SpaceRoom>> {
226 self.space.subscribe()
227 }
228
229 pub fn pagination_state(&self) -> SpaceRoomListPaginationState {
231 self.pagination_state.get()
232 }
233
234 pub fn subscribe_to_pagination_state_updates(
236 &self,
237 ) -> Subscriber<SpaceRoomListPaginationState> {
238 self.pagination_state.subscribe()
239 }
240
241 pub fn rooms(&self) -> Vec<SpaceRoom> {
243 self.rooms.lock().iter().cloned().collect_vec()
244 }
245
246 pub fn subscribe_to_room_updates(
248 &self,
249 ) -> (Vector<SpaceRoom>, VectorSubscriberBatchedStream<SpaceRoom>) {
250 self.rooms.lock().subscribe().into_values_and_batched_stream()
251 }
252
253 pub async fn paginate(&self) -> Result<(), Error> {
256 {
257 let mut pagination_state = self.pagination_state.write();
258
259 match *pagination_state {
260 SpaceRoomListPaginationState::Idle { end_reached } if end_reached => {
261 return Ok(());
262 }
263 SpaceRoomListPaginationState::Loading => {
264 return Ok(());
265 }
266 _ => {}
267 }
268
269 ObservableWriteGuard::set(&mut pagination_state, SpaceRoomListPaginationState::Loading);
270 }
271
272 let mut request = get_hierarchy::v1::Request::new(self.space_id.clone());
273 request.max_depth = Some(uint!(1)); let mut pagination_token = self.token.lock().await;
276
277 if let PaginationToken::HasMore(ref token) = *pagination_token {
278 request.from = Some(token.clone());
279 }
280
281 match self.client.send(request).await {
282 Ok(result) => {
283 *pagination_token = match &result.next_batch {
284 Some(val) => PaginationToken::HasMore(val.clone()),
285 None => PaginationToken::HitEnd,
286 };
287
288 let mut rooms = self.rooms.lock();
289
290 let (space, children): (Vec<_>, Vec<_>) =
293 result.rooms.into_iter().partition(|f| f.summary.room_id == self.space_id);
294
295 if let Some(room) = space.first() {
296 let mut children_state =
297 HashMap::<OwnedRoomId, HierarchySpaceChildEvent>::new();
298 for child_state in &room.children_state {
299 match child_state.deserialize() {
300 Ok(child) => {
301 children_state.insert(child.state_key.clone(), child.clone());
302 }
303 Err(error) => {
304 warn!("Failed deserializing space child event: {error}");
305 }
306 }
307 }
308 *self.children_state.lock() = Some(children_state);
309
310 let mut space = self.space.write();
311 if space.is_none() {
312 ObservableWriteGuard::set(
313 &mut space,
314 Some(SpaceRoom::new_from_summary(
315 &room.summary,
316 self.client.get_room(&room.summary.room_id),
317 room.children_state.len() as u64,
318 vec![],
319 )),
320 );
321 }
322 }
323
324 let children_state = (*self.children_state.lock()).clone().unwrap_or_default();
325
326 children
327 .iter()
328 .map(|room| {
329 let via = children_state
330 .get(&room.summary.room_id)
331 .map(|state| state.content.via.clone());
332
333 SpaceRoom::new_from_summary(
334 &room.summary,
335 self.client.get_room(&room.summary.room_id),
336 room.children_state.len() as u64,
337 via.unwrap_or_default(),
338 )
339 })
340 .sorted_by(|a, b| Self::compare_rooms(a, b, &children_state))
341 .for_each(|room| rooms.push_back(room));
342
343 self.pagination_state.set(SpaceRoomListPaginationState::Idle {
344 end_reached: result.next_batch.is_none(),
345 });
346
347 Ok(())
348 }
349 Err(err) => {
350 self.pagination_state
351 .set(SpaceRoomListPaginationState::Idle { end_reached: false });
352 Err(err.into())
353 }
354 }
355 }
356
357 pub async fn reset(&self) {
366 let mut pagination_token = self.token.lock().await;
367 *pagination_token = None.into();
368
369 self.rooms.lock().clear();
370 self.children_state.lock().take();
371
372 self.pagination_state.set(SpaceRoomListPaginationState::Idle { end_reached: false });
373 }
374
375 fn compare_rooms(
378 a: &SpaceRoom,
379 b: &SpaceRoom,
380 children_state: &HashMap<OwnedRoomId, HierarchySpaceChildEvent>,
381 ) -> Ordering {
382 let a_state = children_state.get(&a.room_id);
383 let b_state = children_state.get(&b.room_id);
384
385 SpaceRoom::compare_rooms(a, b, a_state.map(Into::into), b_state.map(Into::into))
386 }
387}
388
389#[cfg(test)]
390mod tests {
391 use std::{cmp::Ordering, collections::HashMap};
392
393 use assert_matches2::{assert_let, assert_matches};
394 use eyeball_im::VectorDiff;
395 use futures_util::pin_mut;
396 use matrix_sdk::{RoomState, test_utils::mocks::MatrixMockServer};
397 use matrix_sdk_test::{
398 JoinedRoomBuilder, LeftRoomBuilder, async_test, event_factory::EventFactory,
399 };
400 use ruma::{
401 OwnedRoomId, RoomId,
402 events::space::child::HierarchySpaceChildEvent,
403 owned_room_id, owned_server_name,
404 room::{JoinRuleSummary, RoomSummary},
405 room_id, server_name, uint,
406 };
407 use serde_json::{from_value, json};
408 use stream_assert::{assert_next_eq, assert_next_matches, assert_pending, assert_ready};
409
410 use crate::spaces::{
411 SpaceRoom, SpaceRoomList, SpaceService, room_list::SpaceRoomListPaginationState,
412 };
413
414 #[async_test]
415 async fn test_room_list_pagination() {
416 let server = MatrixMockServer::new().await;
417 let client = server.client_builder().build().await;
418 let user_id = client.user_id().unwrap();
419 let space_service = SpaceService::new(client.clone()).await;
420 let factory = EventFactory::new();
421
422 server.mock_room_state_encryption().plain().mount().await;
423
424 let parent_space_id = room_id!("!parent_space:example.org");
425 let child_space_id_1 = room_id!("!1:example.org");
426 let child_space_id_2 = room_id!("!2:example.org");
427
428 server
429 .sync_room(
430 &client,
431 JoinedRoomBuilder::new(parent_space_id)
432 .add_state_event(
433 factory
434 .space_child(parent_space_id.to_owned(), child_space_id_1.to_owned())
435 .sender(user_id),
436 )
437 .add_state_event(
438 factory
439 .space_child(parent_space_id.to_owned(), child_space_id_2.to_owned())
440 .sender(user_id),
441 ),
442 )
443 .await;
444
445 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
446
447 assert_let!(Some(parent_space) = room_list.space());
449 assert_eq!(parent_space.children_count, 2);
450
451 assert_matches!(
453 room_list.pagination_state(),
454 SpaceRoomListPaginationState::Idle { end_reached: false }
455 );
456
457 assert_eq!(room_list.rooms(), vec![]);
459
460 let pagination_state_subscriber = room_list.subscribe_to_pagination_state_updates();
463 pin_mut!(pagination_state_subscriber);
464 assert_pending!(pagination_state_subscriber);
465
466 let (_, rooms_subscriber) = room_list.subscribe_to_room_updates();
467 pin_mut!(rooms_subscriber);
468 assert_pending!(rooms_subscriber);
469
470 server
472 .mock_get_hierarchy()
473 .ok_with_room_ids_and_children_state(
474 vec![child_space_id_1, child_space_id_2],
475 vec![(room_id!("!child:example.org"), vec![])],
476 )
477 .mount()
478 .await;
479
480 room_list.paginate().await.unwrap();
481
482 assert_next_matches!(
484 pagination_state_subscriber,
485 SpaceRoomListPaginationState::Idle { end_reached: true }
486 );
487
488 assert_next_eq!(
490 rooms_subscriber,
491 vec![
492 VectorDiff::PushBack {
493 value: SpaceRoom::new_from_summary(
494 &RoomSummary::new(
495 child_space_id_1.to_owned(),
496 JoinRuleSummary::Public,
497 false,
498 uint!(1),
499 false,
500 ),
501 None,
502 1,
503 vec![],
504 )
505 },
506 VectorDiff::PushBack {
507 value: SpaceRoom::new_from_summary(
508 &RoomSummary::new(
509 child_space_id_2.to_owned(),
510 JoinRuleSummary::Public,
511 false,
512 uint!(1),
513 false,
514 ),
515 None,
516 1,
517 vec![],
518 ),
519 }
520 ]
521 );
522 }
523
524 #[async_test]
525 async fn test_room_state_updates() {
526 let server = MatrixMockServer::new().await;
527 let client = server.client_builder().build().await;
528 let space_service = SpaceService::new(client.clone()).await;
529
530 let parent_space_id = room_id!("!parent_space:example.org");
531 let child_room_id_1 = room_id!("!1:example.org");
532 let child_room_id_2 = room_id!("!2:example.org");
533
534 server
535 .mock_get_hierarchy()
536 .ok_with_room_ids(vec![child_room_id_1, child_room_id_2])
537 .mount()
538 .await;
539
540 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
541
542 room_list.paginate().await.unwrap();
543
544 assert_eq!(room_list.rooms().first().unwrap().room_id, child_room_id_1);
546 assert_eq!(room_list.rooms().last().unwrap().room_id, child_room_id_2);
547
548 assert_eq!(room_list.rooms().first().unwrap().state, None);
550 assert_eq!(room_list.rooms().last().unwrap().state, None);
551
552 let (_, rooms_subscriber) = room_list.subscribe_to_room_updates();
553 pin_mut!(rooms_subscriber);
554 assert_pending!(rooms_subscriber);
555
556 server.sync_room(&client, JoinedRoomBuilder::new(child_room_id_1)).await;
558
559 assert_ready!(rooms_subscriber);
561 assert_eq!(room_list.rooms().first().unwrap().state, Some(RoomState::Joined));
562 assert_eq!(room_list.rooms().last().unwrap().state, None);
563
564 server.sync_room(&client, JoinedRoomBuilder::new(child_room_id_2)).await;
566 assert_ready!(rooms_subscriber);
567 assert_eq!(room_list.rooms().first().unwrap().state, Some(RoomState::Joined));
568 assert_eq!(room_list.rooms().last().unwrap().state, Some(RoomState::Joined));
569
570 server.sync_room(&client, LeftRoomBuilder::new(child_room_id_1)).await;
572 server.sync_room(&client, LeftRoomBuilder::new(child_room_id_2)).await;
573 assert_ready!(rooms_subscriber);
574 assert_eq!(room_list.rooms().first().unwrap().state, Some(RoomState::Left));
575 assert_eq!(room_list.rooms().last().unwrap().state, Some(RoomState::Left));
576 }
577
578 #[async_test]
579 async fn test_parent_space_updates() {
580 let server = MatrixMockServer::new().await;
581 let client = server.client_builder().build().await;
582 let user_id = client.user_id().unwrap();
583 let space_service = SpaceService::new(client.clone()).await;
584 let factory = EventFactory::new();
585
586 server.mock_room_state_encryption().plain().mount().await;
587
588 let parent_space_id = room_id!("!parent_space:example.org");
589 let child_space_id_1 = room_id!("!1:example.org");
590 let child_space_id_2 = room_id!("!2:example.org");
591
592 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
594 assert!(room_list.space().is_none());
595
596 let parent_space_subscriber = room_list.subscribe_to_space_updates();
597 pin_mut!(parent_space_subscriber);
598 assert_pending!(parent_space_subscriber);
599
600 server
601 .mock_get_hierarchy()
602 .ok_with_room_ids_and_children_state(
603 vec![parent_space_id, child_space_id_1, child_space_id_2],
604 vec![(
605 room_id!("!child:example.org"),
606 vec![server_name!("matrix-client.example.org")],
607 )],
608 )
609 .mount()
610 .await;
611
612 room_list.paginate().await.unwrap();
614 assert_let!(Some(parent_space) = room_list.space());
615 assert_eq!(parent_space.room_id, parent_space_id);
616
617 assert_next_eq!(parent_space_subscriber, Some(parent_space));
619
620 server
623 .sync_room(
624 &client,
625 JoinedRoomBuilder::new(parent_space_id)
626 .add_state_event(
627 factory
628 .space_child(parent_space_id.to_owned(), child_space_id_1.to_owned())
629 .sender(user_id),
630 )
631 .add_state_event(
632 factory
633 .space_child(parent_space_id.to_owned(), child_space_id_2.to_owned())
634 .sender(user_id),
635 ),
636 )
637 .await;
638
639 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
640
641 assert_let!(Some(parent_space) = room_list.space());
643 assert_eq!(parent_space.children_count, 2);
644 }
645
646 #[async_test]
647 async fn test_parent_space_room_info_update() {
648 let server = MatrixMockServer::new().await;
649 let client = server.client_builder().build().await;
650 let user_id = client.user_id().unwrap();
651 let space_service = SpaceService::new(client.clone()).await;
652 let factory = EventFactory::new();
653
654 server.mock_room_state_encryption().plain().mount().await;
655
656 let parent_space_id = room_id!("!parent_space:example.org");
657
658 server.sync_room(&client, JoinedRoomBuilder::new(parent_space_id)).await;
659
660 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
661 assert_let!(Some(parent_space) = room_list.space());
662
663 let parent_space_subscriber = room_list.subscribe_to_space_updates();
665 pin_mut!(parent_space_subscriber);
666 assert_pending!(parent_space_subscriber);
667
668 server
670 .sync_room(
671 &client,
672 JoinedRoomBuilder::new(parent_space_id)
673 .add_state_event(factory.room_topic("New room topic").sender(user_id))
674 .add_state_event(factory.room_name("New room name").sender(user_id)),
675 )
676 .await;
677
678 let mut updated_parent_space = parent_space.clone();
679 updated_parent_space.topic = Some("New room topic".to_owned());
680 updated_parent_space.name = Some("New room name".to_owned());
681 updated_parent_space.display_name = "New room name".to_owned();
682
683 assert_next_eq!(parent_space_subscriber, Some(updated_parent_space));
685 }
686
687 #[async_test]
688 async fn test_via_retrieval() {
689 let server = MatrixMockServer::new().await;
690 let client = server.client_builder().build().await;
691 let space_service = SpaceService::new(client.clone()).await;
692
693 server.mock_room_state_encryption().plain().mount().await;
694
695 let parent_space_id = room_id!("!parent_space:example.org");
696 let child_space_id_1 = room_id!("!1:example.org");
697 let child_space_id_2 = room_id!("!2:example.org");
698
699 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
700
701 let (_, rooms_subscriber) = room_list.subscribe_to_room_updates();
702 pin_mut!(rooms_subscriber);
703
704 server
706 .mock_get_hierarchy()
707 .ok_with_room_ids_and_children_state(
708 vec![parent_space_id, child_space_id_1, child_space_id_2],
709 vec![
710 (child_space_id_1, vec![server_name!("matrix-client.example.org")]),
711 (child_space_id_2, vec![server_name!("other-matrix-client.example.org")]),
712 ],
713 )
714 .mount()
715 .await;
716
717 room_list.paginate().await.unwrap();
718
719 assert_next_eq!(
721 rooms_subscriber,
722 vec![
723 VectorDiff::PushBack {
724 value: SpaceRoom::new_from_summary(
725 &RoomSummary::new(
726 child_space_id_1.to_owned(),
727 JoinRuleSummary::Public,
728 false,
729 uint!(1),
730 false,
731 ),
732 None,
733 2,
734 vec![owned_server_name!("matrix-client.example.org")],
735 )
736 },
737 VectorDiff::PushBack {
738 value: SpaceRoom::new_from_summary(
739 &RoomSummary::new(
740 child_space_id_2.to_owned(),
741 JoinRuleSummary::Public,
742 false,
743 uint!(1),
744 false,
745 ),
746 None,
747 2,
748 vec![owned_server_name!("other-matrix-client.example.org")],
749 ),
750 }
751 ]
752 );
753 }
754
755 #[async_test]
756 async fn test_room_list_sorting() {
757 let mut children_state = HashMap::<OwnedRoomId, HierarchySpaceChildEvent>::new();
758
759 assert_eq!(
761 SpaceRoomList::compare_rooms(
762 &make_space_room(owned_room_id!("!Luana:a.b"), None, None, &mut children_state),
763 &make_space_room(owned_room_id!("!Marțolea:a.b"), None, None, &mut children_state),
764 &children_state,
765 ),
766 Ordering::Less
767 );
768
769 assert_eq!(
770 SpaceRoomList::compare_rooms(
771 &make_space_room(owned_room_id!("!Marțolea:a.b"), None, None, &mut children_state),
772 &make_space_room(owned_room_id!("!Luana:a.b"), None, None, &mut children_state),
773 &children_state,
774 ),
775 Ordering::Greater
776 );
777
778 assert_eq!(
781 SpaceRoomList::compare_rooms(
782 &make_space_room(owned_room_id!("!Luana:a.b"), None, Some(1), &mut children_state),
783 &make_space_room(
784 owned_room_id!("!Marțolea:a.b"),
785 None,
786 Some(0),
787 &mut children_state
788 ),
789 &children_state,
790 ),
791 Ordering::Greater
792 );
793
794 assert_eq!(
796 SpaceRoomList::compare_rooms(
797 &make_space_room(
798 owned_room_id!("!Joiana:a.b"),
799 Some("last"),
800 Some(123),
801 &mut children_state
802 ),
803 &make_space_room(
804 owned_room_id!("!Mioara:a.b"),
805 Some("first"),
806 Some(234),
807 &mut children_state
808 ),
809 &children_state,
810 ),
811 Ordering::Greater
812 );
813
814 assert_eq!(
816 SpaceRoomList::compare_rooms(
817 &make_space_room(
818 owned_room_id!("!Joiana:a.b"),
819 Some("Same pasture"),
820 Some(1),
821 &mut children_state
822 ),
823 &make_space_room(
824 owned_room_id!("!Mioara:a.b"),
825 Some("Same pasture"),
826 Some(0),
827 &mut children_state
828 ),
829 &children_state,
830 ),
831 Ordering::Greater
832 );
833
834 assert_eq!(
837 SpaceRoomList::compare_rooms(
838 &make_space_room(
839 owned_room_id!("!Joiana:a.b"),
840 Some("same_pasture"),
841 Some(0),
842 &mut children_state
843 ),
844 &make_space_room(
845 owned_room_id!("!Mioara:a.b"),
846 Some("same_pasture"),
847 Some(0),
848 &mut children_state
849 ),
850 &children_state,
851 ),
852 Ordering::Less
853 );
854
855 assert_eq!(
858 SpaceRoomList::compare_rooms(
859 &make_space_room(owned_room_id!("!Viola:a.b"), None, None, &mut children_state),
860 &make_space_room(
861 owned_room_id!("!Sâmbotina:a.b"),
862 None,
863 Some(0),
864 &mut children_state
865 ),
866 &children_state,
867 ),
868 Ordering::Greater
869 );
870
871 assert_eq!(
874 SpaceRoomList::compare_rooms(
875 &make_space_room(
876 owned_room_id!("!Sâmbotina:a.b"),
877 None,
878 Some(1),
879 &mut children_state
880 ),
881 &make_space_room(
882 owned_room_id!("!Dumana:a.b"),
883 Some("Some pasture"),
884 Some(1),
885 &mut children_state
886 ),
887 &children_state,
888 ),
889 Ordering::Greater
890 );
891 }
892
893 #[async_test]
894 async fn test_reset() {
895 let server = MatrixMockServer::new().await;
896 let client = server.client_builder().build().await;
897 let space_service = SpaceService::new(client.clone()).await;
898
899 let parent_space_id = room_id!("!parent_space:example.org");
900 let child_space_id_1 = room_id!("!1:example.org");
901
902 server
903 .mock_get_hierarchy()
904 .ok_with_room_ids(vec![child_space_id_1])
905 .expect(2)
906 .mount()
907 .await;
908
909 let room_list = space_service.space_room_list(parent_space_id.to_owned()).await;
910
911 room_list.paginate().await.unwrap();
912
913 assert_eq!(room_list.rooms().len(), 1);
915
916 room_list.reset().await;
918
919 assert_eq!(room_list.rooms().len(), 0);
921 assert_matches!(
922 room_list.pagination_state(),
923 SpaceRoomListPaginationState::Idle { end_reached: false }
924 );
925
926 room_list.paginate().await.unwrap();
928 assert_eq!(room_list.rooms().len(), 1);
929 }
930
931 fn make_space_room(
932 room_id: OwnedRoomId,
933 order: Option<&str>,
934 origin_server_ts: Option<u32>,
935 children_state: &mut HashMap<OwnedRoomId, HierarchySpaceChildEvent>,
936 ) -> SpaceRoom {
937 if let Some(origin_server_ts) = origin_server_ts {
938 children_state.insert(
939 room_id.clone(),
940 hierarchy_space_child_event(&room_id, order, origin_server_ts),
941 );
942 }
943 SpaceRoom {
944 room_id,
945 canonical_alias: None,
946 name: Some("New room name".to_owned()),
947 display_name: "Empty room".to_owned(),
948 topic: None,
949 avatar_url: None,
950 room_type: None,
951 num_joined_members: 0,
952 join_rule: None,
953 world_readable: None,
954 guest_can_join: false,
955 is_direct: None,
956 children_count: 0,
957 state: None,
958 heroes: None,
959 via: vec![],
960 }
961 }
962
963 fn hierarchy_space_child_event(
964 room_id: &RoomId,
965 order: Option<&str>,
966 origin_server_ts: u32,
967 ) -> HierarchySpaceChildEvent {
968 let mut json = json!({
969 "content": {
970 "via": []
971 },
972 "origin_server_ts": origin_server_ts,
973 "sender": "@bob:a.b",
974 "state_key": room_id.to_string(),
975 "type": "m.space.child"
976 });
977
978 if let Some(order) = order {
979 json["content"]["order"] = json!(order);
980 }
981
982 from_value::<HierarchySpaceChildEvent>(json).unwrap()
983 }
984}