notifications: Fix notifications for invites

There was a race condition where we would not be able to show the invite
for a room because we didn't have the room yet in the room list.
This commit is contained in:
Kévin Commaille 2025-05-18 11:53:38 +02:00
parent b84a584995
commit e5ed751c1f
No known key found for this signature in database
GPG Key ID: F26F4BE20A08255B
6 changed files with 72 additions and 13 deletions

View File

@ -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>() {
window.session_view().select_room(local_room);
}

View File

@ -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) => {

View File

@ -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<bool>,
}
@ -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.

View File

@ -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<Option<glib::SourceId>>,
}
#[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<Room> {
pub(crate) async fn get_wait(
&self,
room_id: &RoomId,
timeout: Option<Duration>,
) -> Option<Room> {
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
}

View File

@ -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)

View File

@ -208,7 +208,11 @@ mod imp {
let Some(window) = obj.root().and_downcast::<Window>() 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);
}