diff --git a/src/components/dialogs/room_preview.rs b/src/components/dialogs/room_preview.rs index 0e089314..652ae1eb 100644 --- a/src/components/dialogs/room_preview.rs +++ b/src/components/dialogs/room_preview.rs @@ -387,7 +387,7 @@ mod imp { Ok(room_id) => { let obj = self.obj(); - if let Some(local_room) = room_list.get_wait(&room_id).await { + if let Some(local_room) = room_list.get_wait(&room_id, None).await { if let Some(window) = obj.root().and_downcast_ref::() { window.session_view().select_room(local_room); } diff --git a/src/session/model/notifications/mod.rs b/src/session/model/notifications/mod.rs index b8f23a67..eb1e60e0 100644 --- a/src/session/model/notifications/mod.rs +++ b/src/session/model/notifications/mod.rs @@ -1,4 +1,4 @@ -use std::borrow::Cow; +use std::{borrow::Cow, cell::Cell, time::Duration}; use gettextrs::gettext; use gtk::{gdk, gio, glib, prelude::*, subclass::prelude::*}; @@ -182,11 +182,31 @@ impl Notifications { return; } - let Some(room) = session.room_list().get(room_id) else { + let Some(room) = session + .room_list() + .get_wait(room_id, Some(Duration::from_secs(10))) + .await + else { warn!("Could not display notification for missing room {room_id}",); return; }; + if !room.is_room_info_initialized() { + // Wait for the room to finish initializing, otherwise we will not have the + // display name or the avatar. + let (sender, receiver) = futures_channel::oneshot::channel(); + + let sender_cell = Cell::new(Some(sender)); + let handler_id = room.connect_is_room_info_initialized_notify(move |_| { + if let Some(sender) = sender_cell.take() { + let _ = sender.send(()); + } + }); + + let _ = receiver.await; + room.disconnect(handler_id); + } + let event = match AnySyncOrStrippedTimelineEvent::from_raw(&matrix_notification.event) { Ok(event) => event, Err(error) => { diff --git a/src/session/model/room/mod.rs b/src/session/model/room/mod.rs index 26eae8cf..21edbb06 100644 --- a/src/session/model/room/mod.rs +++ b/src/session/model/room/mod.rs @@ -224,6 +224,7 @@ mod imp { /// Whether the room info is initialized. /// /// Used to silence logs during initialization. + #[property(get)] is_room_info_initialized: Cell, } @@ -277,7 +278,9 @@ mod imp { imp.update_with_room_info(imp.matrix_room().clone_info()) .await; imp.watch_room_info(); + imp.is_room_info_initialized.set(true); + imp.obj().notify_is_room_info_initialized(); // Only initialize the following after we have loaded the category of the // room since we only load them for some categories. diff --git a/src/session/model/room_list/mod.rs b/src/session/model/room_list/mod.rs index fcbe2453..4d52d038 100644 --- a/src/session/model/room_list/mod.rs +++ b/src/session/model/room_list/mod.rs @@ -1,6 +1,8 @@ use std::{ cell::Cell, collections::{HashMap, HashSet}, + rc::Rc, + time::Duration, }; use gtk::{ @@ -49,6 +51,7 @@ mod imp { /// The rooms metainfo that allow to restore this `RoomList` from its /// previous state. metainfo: RoomListMetainfo, + pub(super) get_wait_source: RefCell>, } #[glib::object_subclass] @@ -70,6 +73,12 @@ mod imp { self.parent_constructed(); self.metainfo.set_room_list(&self.obj()); } + + fn dispose(&self) { + if let Some(source) = self.get_wait_source.take() { + source.remove(); + } + } } impl ListModelImpl for RoomList { @@ -405,28 +414,51 @@ impl RoomList { } /// Wait till the room with the given ID becomes available. - pub(crate) async fn get_wait(&self, room_id: &RoomId) -> Option { + pub(crate) async fn get_wait( + &self, + room_id: &RoomId, + timeout: Option, + ) -> Option { if let Some(room) = self.get(room_id) { return Some(room); } + let imp = self.imp(); let (sender, receiver) = futures_channel::oneshot::channel(); let room_id = room_id.to_owned(); - let sender = Cell::new(Some(sender)); - // FIXME: add a timeout - let handler_id = self.connect_items_changed(move |obj, _, _, _| { - if let Some(room) = obj.get(&room_id) { - if let Some(sender) = sender.take() { - let _ = sender.send(Some(room)); + let sender_cell = Rc::new(Cell::new(Some(sender))); + + let handler_id = self.connect_items_changed(clone!( + #[strong] + sender_cell, + move |obj, _, _, _| { + if let Some(room) = obj.get(&room_id) { + if let Some(sender) = sender_cell.take() { + let _ = sender.send(Some(room)); + } } } - }); + )); + + if let Some(timeout) = timeout { + let get_wait_source = glib::timeout_add_local_once(timeout, move || { + if let Some(sender) = sender_cell.take() { + let _ = sender.send(None); + } + }); + imp.get_wait_source.replace(Some(get_wait_source)); + } let room = receiver.await.ok().flatten(); self.disconnect(handler_id); + // Remove the source if we got a room. + if let Some(source) = imp.get_wait_source.take().filter(|_| room.is_some()) { + source.remove(); + } + room } diff --git a/src/session/model/user.rs b/src/session/model/user.rs index d5d24f61..77481834 100644 --- a/src/session/model/user.rs +++ b/src/session/model/user.rs @@ -185,7 +185,7 @@ mod imp { let room = self .session() .room_list() - .get_wait(matrix_room.room_id()) + .get_wait(matrix_room.room_id(), None) .await .expect("The newly created room was not found"); Ok(room) diff --git a/src/session/view/create_room_dialog.rs b/src/session/view/create_room_dialog.rs index c1f23077..3de6b7cd 100644 --- a/src/session/view/create_room_dialog.rs +++ b/src/session/view/create_room_dialog.rs @@ -208,7 +208,11 @@ mod imp { let Some(window) = obj.root().and_downcast::() else { return; }; - if let Some(room) = session.room_list().get_wait(matrix_room.room_id()).await { + if let Some(room) = session + .room_list() + .get_wait(matrix_room.room_id(), None) + .await + { window.session_view().select_room(room); }