1use std::{
2 borrow::{Borrow, BorrowMut},
3 collections::{HashMap, HashSet, VecDeque},
4 sync::Arc,
5};
6use tracing::{debug, error, info, warn};
7
8use crossbeam_queue::SegQueue;
9use eyeball::Subscriber;
10use matrix_sdk::{
11 RoomDisplayName, RoomHero, RoomState,
12 ruma::{
13 MilliSecondsSinceUnixEpoch, OwnedMxcUri, OwnedRoomAliasId, OwnedRoomId, OwnedUserId,
14 events::tag::Tags,
15 },
16};
17use matrix_sdk_ui::room_list_service::RoomListLoadingState;
18use serde::Serialize;
19use tokio::{
20 runtime::Handle,
21 sync::oneshot::{self, Sender},
22};
23
24use crate::{
25 events::timeline::TimelineKind,
26 init::singletons::{ALL_ROOMS_LOADED, UIUpdateMessage, broadcast_event},
27 models::{
28 events::{ToastNotificationRequest, ToastNotificationVariant},
29 room_display_name::FrontendRoomDisplayName,
30 state_updater::StateUpdater,
31 },
32 room::{
33 invited_room::InvitedRoomInfo,
34 joined_room::UnreadMessageCount,
35 notifications::enqueue_toast_notification,
36 room_filter::{RoomDisplayFilterBuilder, RoomFilterCriteria, SortFn},
37 },
38 utils::VecDiff,
39};
40
41use super::{room_filter::RoomDisplayFilter, room_screen::RoomScreen};
42
43#[derive(Debug)]
49pub enum RoomsListUpdate {
50 NotLoaded,
52 LoadedRooms { max_rooms: Option<u32> },
55 AddInvitedRoom(InvitedRoomInfo),
58 AddJoinedRoom(JoinedRoomInfo),
60 ClearRooms,
62 UpdateLatestEvent {
64 room_id: OwnedRoomId,
65 timestamp: MilliSecondsSinceUnixEpoch,
66 latest_message_text: String,
68 },
69 UpdateNumUnreadMessages {
71 room_id: OwnedRoomId,
72 is_marked_unread: bool,
73 unread_messages: UnreadMessageCount,
74 unread_mentions: u64,
75 },
76 UpdateRoomName {
78 room_id: OwnedRoomId,
79 new_room_name: RoomDisplayName,
80 },
81 UpdateTopic {
83 room_id: OwnedRoomId,
84 new_topic: String,
85 },
86 UpdateRoomAvatar {
88 room_id: OwnedRoomId,
89 avatar: OwnedMxcUri,
90 },
91 UpdateIsDirect {
93 room_id: OwnedRoomId,
94 is_direct: bool,
95 },
96 RemoveRoom {
98 room_id: OwnedRoomId,
99 _new_state: RoomState,
101 },
102 Tags {
104 room_id: OwnedRoomId,
105 new_tags: Tags,
106 },
107 Status { status: RoomsCollectionStatus },
109 TombstonedRoom { room_id: OwnedRoomId },
111 _HideRoom { room_id: OwnedRoomId },
117 RoomOrderUpdate(VecDiff<OwnedRoomId>),
119 ApplyFilter { keywords: String },
121}
122
123static PENDING_ROOM_UPDATES: SegQueue<RoomsListUpdate> = SegQueue::new();
124
125pub fn enqueue_rooms_list_update(update: RoomsListUpdate) {
128 PENDING_ROOM_UPDATES.push(update);
129 broadcast_event(UIUpdateMessage::RefreshUI);
130}
131
132#[derive(Debug, Clone, Serialize)]
137#[serde(rename_all = "camelCase")]
138pub struct JoinedRoomInfo {
139 pub(crate) room_id: OwnedRoomId,
141 pub(crate) room_name: FrontendRoomDisplayName,
143 pub(crate) num_unread_messages: u64,
145 pub(crate) num_unread_mentions: u64,
147 pub(crate) is_marked_unread: bool,
149 pub(crate) canonical_alias: Option<OwnedRoomAliasId>,
151 pub(crate) alt_aliases: Vec<OwnedRoomAliasId>,
153 pub(crate) tags: Tags,
157 pub(crate) topic: Option<String>,
159 pub(crate) latest: Option<(MilliSecondsSinceUnixEpoch, String)>,
161 pub(crate) avatar: Option<OwnedMxcUri>,
163 pub(crate) has_been_paginated: bool,
168 pub(crate) is_selected: bool,
170 pub(crate) is_direct: bool,
172 pub(crate) direct_user_id: Option<OwnedUserId>,
174 pub(crate) is_tombstoned: bool,
176 pub(crate) heroes: Vec<RoomHero>,
178}
179
180pub fn handle_rooms_loading_state(mut loading_state: Subscriber<RoomListLoadingState>) {
181 debug!(
182 "Initial room list loading state is {:?}",
183 loading_state.get()
184 );
185 Handle::current().spawn(async move {
186 while let Some(state) = loading_state.next().await {
187 debug!("Received a room list loading state update: {state:?}");
188 match state {
189 RoomListLoadingState::NotLoaded => {
190 enqueue_rooms_list_update(RoomsListUpdate::NotLoaded);
191 }
192 RoomListLoadingState::Loaded {
193 maximum_number_of_rooms,
194 } => {
195 ALL_ROOMS_LOADED.get_or_init(|| true);
196 enqueue_rooms_list_update(RoomsListUpdate::LoadedRooms {
197 max_rooms: maximum_number_of_rooms,
198 });
199 }
200 }
201 }
202 });
203}
204
205#[derive(Debug, Serialize)]
208#[serde(rename_all = "camelCase")]
209pub struct RoomsList {
210 invited_rooms: HashMap<OwnedRoomId, InvitedRoomInfo>,
213
214 all_joined_rooms: HashMap<OwnedRoomId, JoinedRoomInfo>,
217
218 all_known_rooms_order: VecDeque<OwnedRoomId>,
220
221 hidden_rooms: HashSet<OwnedRoomId>,
223
224 #[serde(skip)]
230 display_filter: RoomDisplayFilter,
231
232 filter_keywords: String,
236
237 displayed_invited_rooms: Vec<OwnedRoomId>,
241
242 displayed_direct_rooms: Vec<OwnedRoomId>,
247
248 displayed_regular_rooms: Vec<OwnedRoomId>,
256
257 status: RoomsCollectionStatus,
259 current_active_room: Option<TimelineKind>,
261 #[serde(skip)]
264 current_active_room_killer: Option<Sender<()>>,
265 max_known_rooms: Option<u32>,
267 #[serde(skip)]
269 state_updaters: Arc<Box<dyn StateUpdater>>,
270}
271
272#[derive(Debug, Clone, Serialize)]
273#[serde(
274 rename_all = "camelCase",
275 rename_all_fields = "camelCase",
276 tag = "status",
277 content = "message"
278)]
279pub enum RoomsCollectionStatus {
280 NotLoaded(String),
281 Loading(String),
282 Loaded(String),
283 Error(String),
284}
285
286impl RoomsList {
287 pub(crate) fn new(updaters: Arc<Box<dyn StateUpdater>>) -> Self {
288 Self {
289 invited_rooms: HashMap::default(),
290 all_joined_rooms: HashMap::default(),
291 display_filter: RoomDisplayFilter::default(),
292 filter_keywords: "".to_owned(),
293 displayed_regular_rooms: Vec::new(),
294 all_known_rooms_order: VecDeque::new(),
295 hidden_rooms: HashSet::new(),
296 displayed_direct_rooms: Vec::new(),
297 displayed_invited_rooms: Vec::new(),
298 status: RoomsCollectionStatus::NotLoaded("Initiating".to_owned()),
299 current_active_room: None,
300 current_active_room_killer: None,
301 max_known_rooms: None,
302 state_updaters: updaters,
303 }
304 }
305
306 fn update_frontend_state(&self) {
307 if let Err(e) = self.state_updaters.update_rooms_list(self) {
308 enqueue_toast_notification(ToastNotificationRequest::new(
309 format!("Cannot update room list store. Error: {e}"),
310 None,
311 ToastNotificationVariant::Error,
312 ))
313 }
314 }
315
316 pub(crate) async fn handle_rooms_list_updates(&mut self) {
318 let mut num_updates: usize = 0;
319 let mut needs_sort = false;
320
321 while let Some(update) = PENDING_ROOM_UPDATES.pop() {
322 num_updates += 1;
323
324 debug!("Processing update type: {update:?}");
325
326 match update {
327 RoomsListUpdate::AddInvitedRoom(invited_room) => {
328 let room_id = invited_room.room_id.clone();
329 let should_display = (self.display_filter)(&invited_room);
330 let _replaced = self
331 .invited_rooms
332 .borrow_mut()
333 .insert(room_id.clone(), invited_room);
334 if let Some(_old_room) = _replaced {
335 error!("BUG: Added invited room {room_id} that already existed");
336 } else if should_display {
337 self.displayed_invited_rooms.push(room_id);
338 }
339 self.update_status_rooms_count();
340 }
341 RoomsListUpdate::AddJoinedRoom(joined_room) => {
342 let room_id = joined_room.room_id.clone();
343 let should_display = (self.display_filter)(&joined_room);
344 let is_direct = joined_room.is_direct;
345
346 let replaced = self.all_joined_rooms.insert(room_id.clone(), joined_room);
347
348 if let Some(_old_room) = replaced {
349 error!("BUG: Added joined room {room_id} that already existed");
350 } else if should_display {
351 if is_direct {
352 self.displayed_direct_rooms.push(room_id.clone());
353 } else {
354 self.displayed_regular_rooms.push(room_id.clone());
355 }
356 }
357 if let Some(_accepted_invite) = self.invited_rooms.borrow_mut().remove(&room_id)
361 {
362 info!("Removed room {room_id} from the list of invited rooms");
363 self.displayed_invited_rooms
364 .iter()
365 .position(|r| r == &room_id)
366 .map(|index| self.displayed_invited_rooms.remove(index));
367 }
368 self.update_status_rooms_count();
369 }
370 RoomsListUpdate::UpdateRoomAvatar { room_id, avatar } => {
371 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
372 room.avatar = Some(avatar.clone());
373 } else {
374 error!("Error: couldn't find room {room_id} to update avatar");
375 }
376 }
377 RoomsListUpdate::UpdateLatestEvent {
378 room_id,
379 timestamp,
380 latest_message_text,
381 } => {
382 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
383 room.latest = Some((timestamp, latest_message_text.clone()));
384 } else {
385 warn!("Error: couldn't find room {room_id} to update latest event");
386 }
387 }
388 RoomsListUpdate::UpdateNumUnreadMessages {
389 room_id,
390 is_marked_unread,
391 unread_messages,
392 unread_mentions,
393 } => {
394 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
395 room.num_unread_messages = match unread_messages {
396 UnreadMessageCount::_Unknown => 0,
397 UnreadMessageCount::Known(count) => count,
398 };
399 room.num_unread_mentions = unread_mentions;
400 room.is_marked_unread = is_marked_unread;
401 } else {
402 warn!(
403 "Warning: couldn't find room {} to update unread messages count",
404 room_id
405 );
406 }
407 }
408 RoomsListUpdate::UpdateRoomName {
409 room_id,
410 new_room_name,
411 } => {
412 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
414 let was_displayed = (self.display_filter)(room);
415 room.room_name = new_room_name.into();
417 let should_display = (self.display_filter)(room);
418 match (was_displayed, should_display) {
419 (true, true) | (false, false) => {}
421 (true, false) => {
423 if room.is_direct {
424 self.displayed_direct_rooms
425 .iter()
426 .position(|r| r == &room_id)
427 .map(|index| self.displayed_direct_rooms.remove(index));
428 } else {
429 self.displayed_regular_rooms
430 .iter()
431 .position(|r| r == &room_id)
432 .map(|index| self.displayed_regular_rooms.remove(index));
433 }
434 }
435 (false, true) => {
437 if room.is_direct {
438 self.displayed_direct_rooms.push(room_id);
439 } else {
440 self.displayed_regular_rooms.push(room_id);
441 }
442 }
443 }
444 }
445 else {
447 let invited_rooms = self.invited_rooms.borrow_mut();
448 if let Some(invited_room) = invited_rooms.get_mut(&room_id) {
449 let was_displayed = (self.display_filter)(invited_room);
450 invited_room.room_name = new_room_name.into();
451 let should_display = (self.display_filter)(invited_room);
452 match (was_displayed, should_display) {
453 (true, true) | (false, false) => {}
454 (true, false) => {
455 self.displayed_invited_rooms
456 .iter()
457 .position(|r| r == &room_id)
458 .map(|index| self.displayed_invited_rooms.remove(index));
459 }
460 (false, true) => {
461 self.displayed_invited_rooms.push(room_id.clone());
462 }
463 }
464 } else {
465 warn!(
466 "Warning: couldn't find invited room {} to update room name",
467 room_id
468 );
469 }
470 }
471 }
472 RoomsListUpdate::UpdateTopic { room_id, new_topic } => {
473 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
474 room.topic = Some(new_topic);
475 }
476 }
477 RoomsListUpdate::UpdateIsDirect { room_id, is_direct } => {
478 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
479 if room.is_direct == is_direct {
480 continue;
481 }
482 enqueue_toast_notification(ToastNotificationRequest::new(
483 format!(
484 "{} was changed from {} to {}.",
485 room.room_id,
486 if room.is_direct { "direct" } else { "regular" },
487 if is_direct { "direct" } else { "regular" }
488 ),
489 None,
490 ToastNotificationVariant::Info,
491 ));
492 if (self.display_filter)(room) {
494 let list_to_remove_from = if room.is_direct {
495 &mut self.displayed_direct_rooms
496 } else {
497 &mut self.displayed_regular_rooms
498 };
499 list_to_remove_from
500 .iter()
501 .position(|r| r == &room_id)
502 .map(|index| list_to_remove_from.remove(index));
503 }
504 room.is_direct = is_direct;
506 if (self.display_filter)(room) {
507 if is_direct {
508 self.displayed_direct_rooms.push(room_id);
509 } else {
510 self.displayed_regular_rooms.push(room_id);
511 }
512 }
513 } else {
514 error!("Error: couldn't find room {room_id} to update is_direct");
515 }
516 }
517 RoomsListUpdate::RemoveRoom {
518 room_id,
519 _new_state: _,
520 } => {
521 if let Some(removed) = self.all_joined_rooms.remove(&room_id) {
522 info!("Removed room {room_id} from the list of all joined rooms");
523 if removed.is_direct {
524 self.displayed_direct_rooms
525 .iter()
526 .position(|r| r == &room_id)
527 .map(|index| self.displayed_direct_rooms.remove(index));
528 } else {
529 self.displayed_regular_rooms
530 .iter()
531 .position(|r| r == &room_id)
532 .map(|index| self.displayed_regular_rooms.remove(index));
533 }
534 } else if let Some(_removed) = self.invited_rooms.borrow_mut().remove(&room_id)
535 {
536 info!("Removed room {room_id} from the list of all invited rooms");
537 self.displayed_invited_rooms
538 .iter()
539 .position(|r| r == &room_id)
540 .map(|index| self.displayed_invited_rooms.remove(index));
541 }
542
543 self.update_status_rooms_count();
544 }
545 RoomsListUpdate::ClearRooms => {
546 self.all_joined_rooms.clear();
547 self.displayed_direct_rooms.clear();
548 self.displayed_regular_rooms.clear();
549 self.invited_rooms.borrow_mut().clear();
550 self.displayed_invited_rooms.clear();
551 self.update_status_rooms_count();
552 }
553 RoomsListUpdate::NotLoaded => {
554 self.status = RoomsCollectionStatus::Loading(
555 "Loading rooms (waiting for homeserver)...".to_owned(),
556 );
557 }
558 RoomsListUpdate::LoadedRooms { max_rooms } => {
559 self.max_known_rooms = max_rooms;
560 self.update_status_rooms_count();
561 }
562 RoomsListUpdate::Tags { room_id, new_tags } => {
563 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
564 room.tags = new_tags;
565 } else if let Some(_room) = self.invited_rooms.borrow().get(&room_id) {
566 debug!("Ignoring updated tags update for invited room {room_id}");
567 } else {
568 warn!("Error: skipping updated Tags for unknown room {room_id}.");
569 }
570 }
571 RoomsListUpdate::Status { status } => {
572 self.status = status;
573 }
574 RoomsListUpdate::TombstonedRoom { room_id } => {
575 if let Some(room) = self.all_joined_rooms.get_mut(&room_id) {
576 let was_displayed = (self.display_filter)(room);
577 room.is_tombstoned = true;
578 let should_display = (self.display_filter)(room);
579 match (was_displayed, should_display) {
580 (true, true) | (false, false) => {}
582 (true, false) => {
584 if room.is_direct {
585 self.displayed_direct_rooms
586 .iter()
587 .position(|r| r == &room_id)
588 .map(|index| self.displayed_direct_rooms.remove(index));
589 } else {
590 self.displayed_regular_rooms
591 .iter()
592 .position(|r| r == &room_id)
593 .map(|index| self.displayed_regular_rooms.remove(index));
594 }
595 }
596 (false, true) => {
598 if room.is_direct {
599 self.displayed_direct_rooms.push(room_id);
600 } else {
601 self.displayed_regular_rooms.push(room_id);
602 }
603 }
604 }
605 } else {
606 warn!(
607 "Warning: couldn't find room {room_id} to update the tombstone status"
608 );
609 }
610 }
611 RoomsListUpdate::_HideRoom { room_id } => {
612 self.hidden_rooms.insert(room_id.clone());
613 if let Some(i) = self
616 .displayed_regular_rooms
617 .iter()
618 .position(|r| r == &room_id)
619 {
620 self.displayed_regular_rooms.remove(i);
621 } else if let Some(i) = self
622 .displayed_direct_rooms
623 .iter()
624 .position(|r| r == &room_id)
625 {
626 self.displayed_direct_rooms.remove(i);
627 } else if let Some(i) = self
628 .displayed_invited_rooms
629 .iter()
630 .position(|r| r == &room_id)
631 {
632 self.displayed_invited_rooms.remove(i);
633 }
634 }
635 RoomsListUpdate::RoomOrderUpdate(diff) => match diff {
636 VecDiff::Append { values } => {
637 self.all_known_rooms_order.extend(values);
638 needs_sort = true;
639 }
640 VecDiff::Clear => {
641 self.all_known_rooms_order.clear();
642 needs_sort = true;
643 }
644 VecDiff::PushFront { value } => {
645 self.all_known_rooms_order.push_front(value);
646 needs_sort = true;
647 }
648 VecDiff::PushBack { value } => {
649 self.all_known_rooms_order.push_back(value);
650 needs_sort = true;
651 }
652 VecDiff::PopFront => {
653 self.all_known_rooms_order.pop_front();
654 needs_sort = true;
655 }
656 VecDiff::PopBack => {
657 self.all_known_rooms_order.pop_back();
658 needs_sort = true;
659 }
660 VecDiff::Insert { index, value } => {
661 if index <= self.all_known_rooms_order.len() {
662 self.all_known_rooms_order.insert(index, value);
663 needs_sort = true;
664 }
665 }
666 VecDiff::Set { index, value } => {
667 if let Some(existing) = self.all_known_rooms_order.get_mut(index)
668 && *existing != value
669 {
670 *existing = value;
671 needs_sort = true;
672 }
673 }
674 VecDiff::Remove { index } => {
675 if index < self.all_known_rooms_order.len() {
676 self.all_known_rooms_order.remove(index);
677 needs_sort = true;
678 }
679 }
680 VecDiff::Truncate { length } => {
681 self.all_known_rooms_order.truncate(length);
682 needs_sort = true;
683 }
684 },
685 RoomsListUpdate::ApplyFilter { keywords } => {
686 self.filter_keywords = keywords;
687 }
689 }
690 }
691 if needs_sort {
692 if self.filter_keywords.is_empty() {
694 self.update_displayed_rooms();
695 }
696 }
697 if num_updates > 0 {
698 debug!(
699 "RoomsList: processed {} updates to the list of all rooms",
700 num_updates
701 );
702 self.update_frontend_state();
703 }
704 }
705
706 pub(crate) fn handle_current_active_room(
707 &mut self,
708 updated_current_active_timeline: TimelineKind,
709 room_name: String,
710 ) -> anyhow::Result<()> {
711 if self
713 .current_active_room
714 .as_ref()
715 .is_some_and(|id| id == &updated_current_active_timeline)
716 {
717 debug!("Ignored room screen change.");
718 return Ok(());
719 } else if let Some(sender) = self.current_active_room_killer.take() {
720 sender
721 .send(())
722 .expect("Error while sending message to terminate RoomScreen thread {e:?}")
723 } else {
724 debug!("First time setting a room");
725 }
726
727 debug!("Current active room: {updated_current_active_timeline:?}");
728
729 self.current_active_room = Some(updated_current_active_timeline.clone());
730
731 let mut ui_subscriber = crate::init::singletons::subscribe_to_events()
732 .expect("Couldn't get UI subscriber event");
733 let (tx, mut rx) = oneshot::channel::<()>();
734 self.current_active_room_killer = Some(tx);
735 let updaters = self.state_updaters.clone();
736 Handle::current().spawn(async move {
737 let mut room_screen =
738 RoomScreen::new(updaters, Some(updated_current_active_timeline), room_name);
739 room_screen.show_timeline();
740
741 loop {
742 tokio::select! {
743 _ = ui_subscriber.recv() => {
744 room_screen.process_timeline_updates();
745 }
746 _ = &mut rx => {
747 break;
748 }
749 }
750 }
751 });
755 Ok(())
756
757 }
760
761 fn update_status_rooms_count(&mut self) {
763 let num_rooms = self.all_joined_rooms.len() + self.invited_rooms.borrow().len();
764 self.status = if let Some(max_rooms) = self.max_known_rooms {
765 let message = format!("Loaded {num_rooms} of {max_rooms} total rooms.");
766 if num_rooms as u32 == max_rooms {
767 RoomsCollectionStatus::Loaded(message)
768 } else {
769 RoomsCollectionStatus::Loading(message)
770 }
771 } else {
772 RoomsCollectionStatus::Loaded(format!("Loaded {num_rooms} rooms."))
773 };
774 }
775
776 fn set_status_to_matching_rooms(&mut self) {
779 let num_rooms = self.displayed_invited_rooms.len()
780 + self.displayed_direct_rooms.len()
781 + self.displayed_regular_rooms.len();
782 self.status = match num_rooms {
783 0 => RoomsCollectionStatus::Loaded("No matching rooms found.".to_owned()),
784 1 => RoomsCollectionStatus::Loaded("Found 1 matching room.".to_owned()),
785 n => RoomsCollectionStatus::Loaded(format!("Found {} matching rooms.", n)),
786 }
787 }
788
789 fn update_displayed_rooms(&mut self) {
792 let (filter, sort_fn) = if self.filter_keywords.is_empty() {
793 RoomDisplayFilterBuilder::default()
794 .sort_by_latest_ts()
795 .build()
796 } else {
797 RoomDisplayFilterBuilder::new()
798 .set_keywords(self.filter_keywords.clone())
799 .set_filter_criteria(RoomFilterCriteria::All)
800 .sort_by_latest_ts()
801 .build()
802 };
803 self.display_filter = filter;
804
805 self.displayed_invited_rooms = self.generate_displayed_invited_rooms(sort_fn.as_deref());
806
807 let (new_displayed_regular_rooms, new_displayed_direct_rooms) =
808 self.generate_displayed_joined_rooms(sort_fn.as_deref());
809
810 self.displayed_regular_rooms = new_displayed_regular_rooms;
811 self.displayed_direct_rooms = new_displayed_direct_rooms;
812
813 if self.filter_keywords.is_empty() {
814 self.update_status_rooms_count();
815 } else {
816 self.set_status_to_matching_rooms();
817 }
818 }
819
820 fn generate_displayed_invited_rooms(&self, sort_fn: Option<&SortFn>) -> Vec<OwnedRoomId> {
823 let invited_rooms_ref = self.invited_rooms.borrow();
824 let filtered_invited_rooms_iter = invited_rooms_ref
825 .iter()
826 .filter(|(_room_id, room)| (self.display_filter)(*room));
827
828 if let Some(sort_fn) = sort_fn {
829 let mut filtered_invited_rooms = filtered_invited_rooms_iter.collect::<Vec<_>>();
830 filtered_invited_rooms.sort_by(|(_, room_a), (_, room_b)| sort_fn(*room_a, *room_b));
831 filtered_invited_rooms
832 .into_iter()
833 .map(|(room_id, _)| room_id.clone())
834 .collect()
835 } else {
836 filtered_invited_rooms_iter
837 .map(|(room_id, _)| room_id.clone())
838 .collect()
839 }
840 }
841
842 fn generate_displayed_joined_rooms(
845 &self,
846 sort_fn: Option<&SortFn>,
847 ) -> (Vec<OwnedRoomId>, Vec<OwnedRoomId>) {
848 let mut new_displayed_regular_rooms = Vec::new();
849 let mut new_displayed_direct_rooms = Vec::new();
850 let mut push_room = |room_id: &OwnedRoomId, jr: &JoinedRoomInfo| {
851 let room_id = room_id.clone();
852 if jr.is_direct {
853 new_displayed_direct_rooms.push(room_id);
854 } else {
855 new_displayed_regular_rooms.push(room_id);
856 }
857 };
858
859 let filtered_joined_rooms_iter = self
860 .all_joined_rooms
861 .iter()
862 .filter(|(_room_id, room)| (self.display_filter)(*room));
863
864 if let Some(sort_fn) = sort_fn {
865 let mut filtered_rooms = filtered_joined_rooms_iter.collect::<Vec<_>>();
866 filtered_rooms.sort_by(|(_, room_a), (_, room_b)| sort_fn(*room_a, *room_b));
867 for (room_id, jr) in filtered_rooms.into_iter() {
868 push_room(room_id, jr)
869 }
870 } else {
871 for (room_id, jr) in filtered_joined_rooms_iter {
872 push_room(room_id, jr)
873 }
874 }
875
876 (new_displayed_regular_rooms, new_displayed_direct_rooms)
877 }
878}