Skip to content

Commit ebc0816

Browse files
committed
spaces: Add methods to add/remove space children.
1 parent 9ab886f commit ebc0816

File tree

2 files changed

+110
-2
lines changed

2 files changed

+110
-2
lines changed

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

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

90+
pub async fn add_child_to_space(
91+
&self,
92+
child_id: String,
93+
space_id: String,
94+
) -> Result<(), ClientError> {
95+
let space_id = RoomId::parse(space_id)?;
96+
let child_id = RoomId::parse(child_id)?;
97+
98+
self.inner.add_child_to_space(child_id, space_id).await.map_err(ClientError::from)
99+
}
100+
101+
pub async fn remove_child_from_space(
102+
&self,
103+
child_id: String,
104+
space_id: String,
105+
) -> Result<(), ClientError> {
106+
let space_id = RoomId::parse(space_id)?;
107+
let child_id = RoomId::parse(child_id)?;
108+
109+
self.inner.remove_child_from_space(child_id, space_id).await.map_err(ClientError::from)
110+
}
111+
90112
/// Start a space leave process returning a [`LeaveSpaceHandle`] from which
91113
/// rooms can be retrieved in reversed BFS order starting from the requested
92114
/// `space_id` graph node. If the room is unknown then an error will be

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

Lines changed: 88 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,13 @@ use matrix_sdk_common::executor::spawn;
4040
use ruma::{
4141
OwnedRoomId, RoomId,
4242
events::{
43-
self, SyncStateEvent,
43+
self, StateEventType, SyncStateEvent,
4444
space::{child::SpaceChildEventContent, parent::SpaceParentEventContent},
4545
},
4646
};
4747
use thiserror::Error;
4848
use tokio::sync::Mutex as AsyncMutex;
49-
use tracing::error;
49+
use tracing::{error, warn};
5050

5151
use crate::spaces::{graph::SpaceGraph, leave::LeaveSpaceHandle};
5252
pub use crate::spaces::{room::SpaceRoom, room_list::SpaceRoomList};
@@ -63,6 +63,14 @@ pub enum Error {
6363
#[error("Room `{0}` not found")]
6464
RoomNotFound(OwnedRoomId),
6565

66+
/// The space parent/child state was missing.
67+
#[error("Missing `{0}` for `{1}`")]
68+
MissingState(StateEventType, OwnedRoomId),
69+
70+
/// Failed to set the expected m.space.parent or m.space.child state events.
71+
#[error("Failed updating space parent/child relationship")]
72+
UpdateRelationship(SDKError),
73+
6674
/// Failed to leave a space.
6775
#[error("Failed to leave space")]
6876
LeaveSpace(SDKError),
@@ -204,6 +212,84 @@ impl SpaceService {
204212
SpaceRoomList::new(self.client.clone(), space_id).await
205213
}
206214

215+
pub async fn add_child_to_space(
216+
&self,
217+
child_id: OwnedRoomId,
218+
space_id: OwnedRoomId,
219+
) -> Result<(), Error> {
220+
let space_room =
221+
self.client.get_room(&space_id).ok_or(Error::RoomNotFound(space_id.to_owned()))?;
222+
let child_room =
223+
self.client.get_room(&child_id).ok_or(Error::RoomNotFound(child_id.to_owned()))?;
224+
225+
// Add the child to the space.
226+
let child_route = child_room.route().await.map_err(Error::UpdateRelationship)?;
227+
space_room
228+
.send_state_event_for_key(&child_id, SpaceChildEventContent::new(child_route))
229+
.await
230+
.map_err(Error::UpdateRelationship)?;
231+
232+
// Add the space as parent of the child if allowed.
233+
if let Some(user_id) = self.client.user_id()
234+
&& let Ok(power_levels) = space_room.power_levels().await
235+
&& !power_levels.user_can_send_state(user_id, StateEventType::SpaceParent)
236+
{
237+
warn!("The current user doesn't have permission to set the child's parent.");
238+
return Ok(());
239+
}
240+
let parent_route = space_room.route().await.map_err(Error::UpdateRelationship)?;
241+
child_room
242+
.send_state_event_for_key(&space_id, SpaceParentEventContent::new(parent_route))
243+
.await
244+
.map_err(Error::UpdateRelationship)?;
245+
246+
Ok(())
247+
}
248+
249+
pub async fn remove_child_from_space(
250+
&self,
251+
child_id: OwnedRoomId,
252+
space_id: OwnedRoomId,
253+
) -> Result<(), Error> {
254+
let space_room =
255+
self.client.get_room(&space_id).ok_or(Error::RoomNotFound(space_id.to_owned()))?;
256+
let child_room =
257+
self.client.get_room(&child_id).ok_or(Error::RoomNotFound(child_id.to_owned()))?;
258+
259+
if space_room
260+
.get_state_event_static_for_key::<SpaceChildEventContent, _>(&child_id)
261+
.await
262+
.is_err()
263+
{
264+
return Err(Error::MissingState(StateEventType::SpaceChild, child_id));
265+
}
266+
// Redacting state is a "weird" thing to do, so send {} instead.
267+
//
268+
// Specifically, "The redaction of the state doesn't participate in state
269+
// resolution so behaves quite differently from e.g. sending an empty form of
270+
// that state events".
271+
space_room
272+
.send_state_event_raw("m.space.child", child_id.as_str(), serde_json::json!({}))
273+
.await
274+
.map_err(Error::UpdateRelationship)?;
275+
276+
if child_room
277+
.get_state_event_static_for_key::<SpaceParentEventContent, _>(&space_id)
278+
.await
279+
.is_err()
280+
{
281+
warn!("A space parent event wasn't found on the child room, ignoring.");
282+
return Ok(());
283+
}
284+
// Same as the comment above.
285+
child_room
286+
.send_state_event_raw("m.space.parent", space_id.as_str(), serde_json::json!({}))
287+
.await
288+
.map_err(Error::UpdateRelationship)?;
289+
290+
Ok(())
291+
}
292+
207293
/// Start a space leave process returning a [`LeaveSpaceHandle`] from which
208294
/// rooms can be retrieved in reversed BFS order starting from the requested
209295
/// `space_id` graph node. If the room is unknown then an error will be

0 commit comments

Comments
 (0)