Skip to content

Commit e4b17ad

Browse files
committed
spaces: Add support for getting a flattened list of editable spaces.
1 parent 1a6aeaa commit e4b17ad

File tree

2 files changed

+104
-0
lines changed

2 files changed

+104
-0
lines changed

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,15 @@ impl SpaceService {
7878
})))
7979
}
8080

81+
/// Returns a flattened list containing all joined spaces where the
82+
/// user has permission to send `m.space.child` state events.
83+
///
84+
/// Note: Unlike [`Self::joined_spaces()`], this method does not recompute
85+
/// the space graph, nor does it notify subscribers about changes.
86+
pub async fn all_editable_joined_spaces(&self) -> Vec<SpaceRoom> {
87+
self.inner.all_editable_joined_spaces().await.into_iter().map(Into::into).collect()
88+
}
89+
8190
/// Returns a `SpaceRoomList` for the given space ID.
8291
pub async fn space_room_list(
8392
&self,

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

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,33 @@ impl SpaceService {
211211
spaces
212212
}
213213

214+
/// Returns a flattened list containing all joined spaces where the
215+
/// user has permission to send `m.space.child` state events.
216+
///
217+
/// Note: Unlike [`Self::joined_spaces()`], this method does not recompute
218+
/// graph, nor does it notify subscribers about changes.
219+
pub async fn all_editable_joined_spaces(&self) -> Vec<SpaceRoom> {
220+
let Some(user_id) = self.client.user_id() else {
221+
return vec![];
222+
};
223+
224+
let graph = &self.space_state.lock().await.graph;
225+
let rooms = self.client.joined_space_rooms();
226+
227+
let mut editable_spaces = Vec::new();
228+
for room in &rooms {
229+
if let Ok(power_levels) = room.power_levels().await
230+
&& power_levels.user_can_send_state(user_id, StateEventType::SpaceChild)
231+
{
232+
let room_id = room.room_id();
233+
editable_spaces
234+
.push(SpaceRoom::new_from_known(room, graph.children_of(room_id).len() as u64));
235+
}
236+
}
237+
238+
editable_spaces
239+
}
240+
214241
/// Returns a `SpaceRoomList` for the given space ID.
215242
pub async fn space_room_list(&self, space_id: OwnedRoomId) -> SpaceRoomList {
216243
SpaceRoomList::new(self.client.clone(), space_id).await
@@ -738,6 +765,74 @@ mod tests {
738765
);
739766
}
740767

768+
#[async_test]
769+
async fn test_all_editable_spaces() {
770+
// Given a space hierarchy where the user is admin of some spaces and subspaces.
771+
let server = MatrixMockServer::new().await;
772+
let client = server.client_builder().build().await;
773+
let user_id = client.user_id().unwrap();
774+
let factory = EventFactory::new();
775+
776+
server.mock_room_state_encryption().plain().mount().await;
777+
778+
let admin_space_id = room_id!("!admin_space:example.org");
779+
let admin_subspace_id = room_id!("!admin_subspace:example.org");
780+
let regular_space_id = room_id!("!regular_space:example.org");
781+
let regular_subspace_id = room_id!("!regular_subspace:example.org");
782+
783+
add_space_rooms(
784+
vec![
785+
MockSpaceRoomParameters {
786+
room_id: admin_space_id,
787+
order: None,
788+
parents: vec![],
789+
children: vec![regular_subspace_id],
790+
power_level: Some(100),
791+
},
792+
MockSpaceRoomParameters {
793+
room_id: admin_subspace_id,
794+
order: None,
795+
parents: vec![regular_space_id],
796+
children: vec![],
797+
power_level: Some(100),
798+
},
799+
MockSpaceRoomParameters {
800+
room_id: regular_space_id,
801+
order: None,
802+
parents: vec![],
803+
children: vec![admin_subspace_id],
804+
power_level: Some(0),
805+
},
806+
MockSpaceRoomParameters {
807+
room_id: regular_subspace_id,
808+
order: None,
809+
parents: vec![admin_space_id],
810+
children: vec![],
811+
power_level: Some(0),
812+
},
813+
],
814+
&client,
815+
&server,
816+
&factory,
817+
user_id,
818+
)
819+
.await;
820+
821+
let space_service = SpaceService::new(client.clone());
822+
823+
// Wait for the space hierarchy to register.
824+
_ = space_service.joined_spaces().await;
825+
826+
// When retrieving all editable joined spaces.
827+
let editable_spaces = space_service.all_editable_joined_spaces().await;
828+
829+
// Then only the spaces where the user is admin are returned.
830+
assert_eq!(
831+
editable_spaces.iter().map(|room| room.room_id.to_owned()).collect::<Vec<_>>(),
832+
vec![admin_space_id.to_owned(), admin_subspace_id.to_owned()]
833+
);
834+
}
835+
741836
#[async_test]
742837
async fn test_joined_parents_of_child() {
743838
// Given a space with three parent spaces, two of which are joined.

0 commit comments

Comments
 (0)