Skip to content

Commit f52ebdb

Browse files
committed
Have the SpaceServiceRoomList publish updates as known room states change i.e. they get joined or left.
1 parent 86e2c74 commit f52ebdb

File tree

4 files changed

+113
-11
lines changed

4 files changed

+113
-11
lines changed

bindings/matrix-sdk-ffi/src/spaces.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,12 @@ impl SpaceService {
4949
self.inner.joined_spaces().into_iter().map(Into::into).collect()
5050
}
5151

52-
pub fn top_level_children_for(
52+
#[allow(clippy::unused_async)]
53+
// This method doesn't need to be async but if its not the FFI layer panics
54+
// with "there is no no reactor running, must be called from the context
55+
// of a Tokio 1.x runtime" error because the underlying constructor spawns
56+
// an async task.
57+
pub async fn space_room_list(
5358
&self,
5459
space_id: String,
5560
) -> Result<SpaceServiceRoomList, ClientError> {

crates/matrix-sdk-base/src/sync.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ impl RoomUpdates {
9090
/// Iterate over all room IDs, from [`RoomUpdates::left`],
9191
/// [`RoomUpdates::joined`], [`RoomUpdates::invited`] and
9292
/// [`RoomUpdates::knocked`].
93-
pub(crate) fn iter_all_room_ids(&self) -> impl Iterator<Item = &OwnedRoomId> {
93+
pub fn iter_all_room_ids(&self) -> impl Iterator<Item = &OwnedRoomId> {
9494
self.left
9595
.keys()
9696
.chain(self.joined.keys())

crates/matrix-sdk-ui/src/spaces/mod.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ impl SpaceService {
9797
.then(|room| async move {
9898
let ok = if let Ok(parents) = room.parent_spaces().await {
9999
pin_mut!(parents);
100-
parents.any(|p| p.is_ok()).await == false
100+
!parents.any(|p| p.is_ok()).await
101101
} else {
102102
false
103103
};
@@ -115,19 +115,17 @@ impl SpaceService {
115115
#[cfg(test)]
116116
mod tests {
117117
use assert_matches2::assert_let;
118+
use futures_util::pin_mut;
118119
use matrix_sdk::{room::ParentSpace, test_utils::mocks::MatrixMockServer};
119120
use matrix_sdk_test::{
120121
JoinedRoomBuilder, LeftRoomBuilder, async_test, event_factory::EventFactory,
121122
};
122123
use ruma::{RoomVersionId, room::RoomType, room_id};
124+
use stream_assert::{assert_next_eq, assert_pending};
123125
use tokio_stream::StreamExt;
124126

125127
use super::*;
126128

127-
use futures_util::pin_mut;
128-
129-
use stream_assert::{assert_next_eq, assert_pending};
130-
131129
#[async_test]
132130
async fn test_spaces_hierarchy() {
133131
let server = MatrixMockServer::new().await;

crates/matrix-sdk-ui/src/spaces/room_list.rs

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,11 @@
1515
use std::sync::Mutex;
1616

1717
use eyeball::{SharedObservable, Subscriber};
18+
use futures_util::pin_mut;
1819
use matrix_sdk::{Client, Error, paginators::PaginationToken};
1920
use ruma::{OwnedRoomId, api::client::space::get_hierarchy};
21+
use tokio::task::JoinHandle;
22+
use tracing::error;
2023

2124
use crate::spaces::SpaceServiceRoom;
2225

@@ -36,18 +39,60 @@ pub struct SpaceServiceRoomList {
3639
pagination_state: SharedObservable<SpaceServiceRoomListPaginationState>,
3740

3841
rooms: SharedObservable<Vec<SpaceServiceRoom>>,
42+
43+
room_update_handle: JoinHandle<()>,
3944
}
4045

46+
impl Drop for SpaceServiceRoomList {
47+
fn drop(&mut self) {
48+
self.room_update_handle.abort();
49+
}
50+
}
4151
impl SpaceServiceRoomList {
4252
pub fn new(client: Client, parent_space_id: OwnedRoomId) -> Self {
53+
let rooms = SharedObservable::new(Vec::<SpaceServiceRoom>::new());
54+
55+
let client_clone = client.clone();
56+
let rooms_clone = rooms.clone();
57+
let all_room_updates_receiver = client.subscribe_to_all_room_updates();
58+
59+
let handle = tokio::spawn(async move {
60+
pin_mut!(all_room_updates_receiver);
61+
62+
loop {
63+
match all_room_updates_receiver.recv().await {
64+
Ok(updates) => {
65+
let mut new_rooms = rooms_clone.get();
66+
67+
updates.iter_all_room_ids().for_each(|updated_room_id| {
68+
if let Some(room) =
69+
new_rooms.iter_mut().find(|room| &room.room_id == updated_room_id)
70+
&& let Some(update_room) = client_clone.get_room(updated_room_id)
71+
{
72+
*room = SpaceServiceRoom::new_from_known(update_room);
73+
}
74+
});
75+
76+
if new_rooms != rooms_clone.get() {
77+
rooms_clone.set(new_rooms);
78+
}
79+
}
80+
Err(err) => {
81+
error!("error when listening to room updates: {err}");
82+
}
83+
}
84+
}
85+
});
86+
4387
Self {
4488
client,
4589
parent_space_id,
4690
token: Mutex::new(None.into()),
4791
pagination_state: SharedObservable::new(SpaceServiceRoomListPaginationState::Idle {
4892
end_reached: false,
4993
}),
50-
rooms: SharedObservable::new(Vec::new()),
94+
rooms,
95+
room_update_handle: handle,
5196
}
5297
}
5398

@@ -132,13 +177,13 @@ impl SpaceServiceRoomList {
132177
mod tests {
133178
use assert_matches2::assert_matches;
134179
use futures_util::pin_mut;
135-
use matrix_sdk::test_utils::mocks::MatrixMockServer;
136-
use matrix_sdk_test::async_test;
180+
use matrix_sdk::{RoomState, test_utils::mocks::MatrixMockServer};
181+
use matrix_sdk_test::{JoinedRoomBuilder, LeftRoomBuilder, async_test};
137182
use ruma::{
138183
room::{JoinRuleSummary, RoomSummary},
139184
room_id, uint,
140185
};
141-
use stream_assert::{assert_next_eq, assert_next_matches, assert_pending};
186+
use stream_assert::{assert_next_eq, assert_next_matches, assert_pending, assert_ready};
142187

143188
use crate::spaces::{
144189
SpaceService, SpaceServiceRoom, room_list::SpaceServiceRoomListPaginationState,
@@ -220,4 +265,58 @@ mod tests {
220265
]
221266
);
222267
}
268+
269+
#[async_test]
270+
async fn test_room_state_updates() {
271+
let server = MatrixMockServer::new().await;
272+
let client = server.client_builder().build().await;
273+
let space_service = SpaceService::new(client.clone()).await;
274+
275+
let parent_space_id = room_id!("!parent_space:example.org");
276+
let child_room_id_1 = room_id!("!1:example.org");
277+
let child_room_id_2 = room_id!("!2:example.org");
278+
279+
server
280+
.mock_get_hierarchy()
281+
.ok_with_room_ids(vec![child_room_id_1, child_room_id_2])
282+
.mount()
283+
.await;
284+
285+
let room_list = space_service.space_room_list(parent_space_id.to_owned());
286+
287+
room_list.paginate().await.unwrap();
288+
289+
// This space contains 2 rooms
290+
assert_eq!(room_list.rooms().first().unwrap().room_id, child_room_id_1);
291+
assert_eq!(room_list.rooms().last().unwrap().room_id, child_room_id_2);
292+
293+
// and we don't know about either of them
294+
assert_eq!(room_list.rooms().first().unwrap().state, None);
295+
assert_eq!(room_list.rooms().last().unwrap().state, None);
296+
297+
let rooms_subscriber = room_list.subscribe_to_room_updates();
298+
pin_mut!(rooms_subscriber);
299+
assert_pending!(rooms_subscriber);
300+
301+
// Joining one of them though
302+
server.sync_room(&client, JoinedRoomBuilder::new(child_room_id_1)).await;
303+
304+
// Results in an update being pushed through
305+
assert_ready!(rooms_subscriber);
306+
assert_eq!(room_list.rooms().first().unwrap().state, Some(RoomState::Joined));
307+
assert_eq!(room_list.rooms().last().unwrap().state, None);
308+
309+
// Same for the second one
310+
server.sync_room(&client, JoinedRoomBuilder::new(child_room_id_2)).await;
311+
assert_ready!(rooms_subscriber);
312+
assert_eq!(room_list.rooms().first().unwrap().state, Some(RoomState::Joined));
313+
assert_eq!(room_list.rooms().last().unwrap().state, Some(RoomState::Joined));
314+
315+
// And when leaving them
316+
server.sync_room(&client, LeftRoomBuilder::new(child_room_id_1)).await;
317+
server.sync_room(&client, LeftRoomBuilder::new(child_room_id_2)).await;
318+
assert_ready!(rooms_subscriber);
319+
assert_eq!(room_list.rooms().first().unwrap().state, Some(RoomState::Left));
320+
assert_eq!(room_list.rooms().last().unwrap().state, Some(RoomState::Left));
321+
}
223322
}

0 commit comments

Comments
 (0)