Skip to content

Commit 6e626f8

Browse files
committed
spaces: Add a method to get the joined parents of a given child.
1 parent e4c0fab commit 6e626f8

File tree

3 files changed

+118
-2
lines changed

3 files changed

+118
-2
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,18 @@ impl SpaceService {
8787
Ok(Arc::new(SpaceRoomList::new(self.inner.space_room_list(space_id).await)))
8888
}
8989

90+
/// Returns all known direct-parents of a given space room ID.
91+
pub async fn joined_parents_of_child(
92+
&self,
93+
child_id: String,
94+
) -> Result<Vec<SpaceRoom>, ClientError> {
95+
let child_id = RoomId::parse(child_id)?;
96+
97+
let parents = self.inner.joined_parents_of_child(&child_id).await;
98+
99+
Ok(parents.into_iter().map(Into::into).collect())
100+
}
101+
90102
pub async fn add_child_to_space(
91103
&self,
92104
child_id: String,

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ impl SpaceGraph {
6767
.map_or(vec![], |node| node.children.iter().map(|id| id.as_ref()).collect())
6868
}
6969

70+
/// Returns the parents of a given node. If the node does not exist, it
71+
/// returns an empty vector.
72+
pub(super) fn parents_of(&self, node_id: &RoomId) -> Vec<&RoomId> {
73+
self.nodes
74+
.get(node_id)
75+
.map_or(vec![], |node| node.parents.iter().map(|id| id.as_ref()).collect())
76+
}
77+
7078
/// Adds a node to the graph. If the node already exists, it does nothing.
7179
pub(super) fn add_node(&mut self, node_id: OwnedRoomId) {
7280
self.nodes.entry(node_id.clone()).or_insert(SpaceGraphNode::new(node_id));
@@ -184,8 +192,8 @@ mod tests {
184192

185193
assert_eq!(graph.root_nodes(), vec![&a]);
186194

187-
assert!(graph.nodes[&b].parents.contains(&a));
188-
assert!(graph.nodes[&c].parents.contains(&a));
195+
assert_eq!(graph.parents_of(&b), vec![&a]);
196+
assert_eq!(graph.parents_of(&c), vec![&a]);
189197

190198
assert_eq!(graph.children_of(&a), vec![&b, &c]);
191199
}

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

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,20 @@ impl SpaceService {
212212
SpaceRoomList::new(self.client.clone(), space_id).await
213213
}
214214

215+
/// Returns all known direct-parents of a given space room ID.
216+
pub async fn joined_parents_of_child(&self, child_id: &RoomId) -> Vec<SpaceRoom> {
217+
let graph = &self.space_state.lock().await.graph;
218+
219+
graph
220+
.parents_of(child_id)
221+
.into_iter()
222+
.filter_map(|parent_id| self.client.get_room(parent_id)) // The graph is built from joined rooms so nothing should be filtered out here.
223+
.map(|room| {
224+
SpaceRoom::new_from_known(&room, graph.children_of(room.room_id()).len() as u64)
225+
})
226+
.collect()
227+
}
228+
215229
pub async fn add_child_to_space(
216230
&self,
217231
child_id: OwnedRoomId,
@@ -708,6 +722,88 @@ mod tests {
708722
);
709723
}
710724

725+
#[async_test]
726+
async fn test_joined_parents_of_child() {
727+
// Given a space with three parent spaces, two of which are joined.
728+
let server = MatrixMockServer::new().await;
729+
let client = server.client_builder().build().await;
730+
let user_id = client.user_id().unwrap();
731+
let factory = EventFactory::new();
732+
733+
server.mock_room_state_encryption().plain().mount().await;
734+
735+
let parent_space_id_1 = room_id!("!parent_space_1:example.org");
736+
let parent_space_id_2 = room_id!("!parent_space_2:example.org");
737+
let unknown_parent_space_id = room_id!("!unknown_parent_space:example.org");
738+
let child_space_id = room_id!("!child_space:example.org");
739+
740+
server
741+
.sync_room(
742+
&client,
743+
JoinedRoomBuilder::new(child_space_id)
744+
.add_state_event(factory.create(user_id, RoomVersionId::V1).with_space_type())
745+
.add_state_event(
746+
factory
747+
.space_parent(parent_space_id_1.to_owned(), child_space_id.to_owned())
748+
.sender(user_id),
749+
)
750+
.add_state_event(
751+
factory
752+
.space_parent(parent_space_id_2.to_owned(), child_space_id.to_owned())
753+
.sender(user_id),
754+
)
755+
.add_state_event(
756+
factory
757+
.space_parent(
758+
unknown_parent_space_id.to_owned(),
759+
child_space_id.to_owned(),
760+
)
761+
.sender(user_id),
762+
),
763+
)
764+
.await;
765+
766+
server
767+
.sync_room(
768+
&client,
769+
JoinedRoomBuilder::new(parent_space_id_1)
770+
.add_state_event(factory.create(user_id, RoomVersionId::V1).with_space_type())
771+
.add_state_event(
772+
factory
773+
.space_child(parent_space_id_1.to_owned(), child_space_id.to_owned())
774+
.sender(user_id),
775+
),
776+
)
777+
.await;
778+
779+
server
780+
.sync_room(
781+
&client,
782+
JoinedRoomBuilder::new(parent_space_id_2)
783+
.add_state_event(factory.create(user_id, RoomVersionId::V1).with_space_type())
784+
.add_state_event(
785+
factory
786+
.space_child(parent_space_id_2.to_owned(), child_space_id.to_owned())
787+
.sender(user_id),
788+
),
789+
)
790+
.await;
791+
792+
let space_service = SpaceService::new(client.clone());
793+
794+
// Wait for the space hierarchy to register.
795+
_ = space_service.joined_spaces().await;
796+
797+
// When retrieving the joined parents of the child space
798+
let parents = space_service.joined_parents_of_child(&child_space_id).await;
799+
800+
// Then both parent spaces are returned
801+
assert_eq!(
802+
parents.iter().map(|space| space.room_id.to_owned()).collect::<Vec<_>>(),
803+
vec![parent_space_id_1, parent_space_id_2]
804+
);
805+
}
806+
711807
#[async_test]
712808
async fn test_add_child_to_space() {
713809
// Given a space and child room where the user is admin of both.

0 commit comments

Comments
 (0)