@@ -40,13 +40,13 @@ use matrix_sdk_common::executor::spawn;
4040use ruma:: {
4141 OwnedRoomId , RoomId ,
4242 events:: {
43- self , SyncStateEvent ,
43+ self , StateEventType , SyncStateEvent ,
4444 space:: { child:: SpaceChildEventContent , parent:: SpaceParentEventContent } ,
4545 } ,
4646} ;
4747use thiserror:: Error ;
4848use tokio:: sync:: Mutex as AsyncMutex ;
49- use tracing:: error;
49+ use tracing:: { error, warn } ;
5050
5151use crate :: spaces:: { graph:: SpaceGraph , leave:: LeaveSpaceHandle } ;
5252pub 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