matrix-rust-sdk icon indicating copy to clipboard operation
matrix-rust-sdk copied to clipboard

Spaces support

Open johannescpk opened this issue 4 years ago • 16 comments

Spaces support was merged into the spec (https://github.com/matrix-org/matrix-doc/blob/master/proposals/1772-groups-as-rooms.md). So it might be nice to have some dedicated spaces support in the SDK as well. Since ruma already provides types for spaces it's already possible to work with spaces generally.

Important

  • Normal messages within a space-room are discouraged (but not blocked by the server): user interfaces are not expected to have a way to enter or display such messages. Space-rooms should be created with a power level for events_default of 100, to prevent the rooms accidentally/maliciously clogging up with messages from random members of the space.

Todo

  • [ ] Should we make sure that rooms with type m.space are created with required PL 100 for events_default? Should this happen transparently in Client::create_room or do we want a dedicated Client::create_space?
  • [x] ~~Fix https://github.com/matrix-org/matrix-rust-sdk/issues/247~~

Related

  • ruma issue: https://github.com/ruma/ruma/issues/445
  • matrix-bot-sdk: https://github.com/turt2live/matrix-bot-sdk/pull/121
  • matrix-js-sdk: https://github.com/matrix-org/matrix-js-sdk/pull/1563
  • matrix-dart-sdk: https://gitlab.com/famedly/famedlysdk/-/merge_requests/683
  • matrix-go-sdk: None yet
  • synapse
    • https://github.com/matrix-org/synapse/pull/10011
    • https://github.com/matrix-org/synapse/pull/9947

johannescpk avatar May 10 '21 21:05 johannescpk

Related ruma PR: https://github.com/ruma/ruma/pull/585

MTRNord avatar May 18 '21 23:05 MTRNord

Any update? Fractal needs this for spaces support.

orowith2os avatar Jul 02 '23 08:07 orowith2os

As far as I know, it's not planned for the next months.

Hywan avatar Jul 05 '23 11:07 Hywan

I got some space support working for a room-sorting bot (pardon the crappy Rust):

                let space_id = if spaces.contains_key(&target_space) {
                    spaces.get(&target_space).unwrap().clone()
                } else {
                    println!(" > Creating space: {}", target_space.name);
                    let mut request = create_room::v3::Request::new();
                    let mut creation_content: CreationContent = Default::default();
                    creation_content.room_type = Some(matrix_sdk::ruma::room::RoomType::Space);
                    request.creation_content =
                        Some(Raw::<CreationContent>::new(&creation_content).unwrap());
                    let creation_state = [
                        {
                            let mut content = RoomAvatarEventContent::new();
                            content.url = Some(OwnedMxcUri::from(target_space.avatar_url.clone()));
                            let initial_event: InitialStateEvent<RoomAvatarEventContent> =
                                InitialStateEvent {
                                    content: content,
                                    state_key: EmptyStateKey {},
                                };
                            // let any_event: AnyInitialStateEvent = AnyInitialStateEvent::RoomAvatar(initial_event);

                            Raw::new(&initial_event)
                                .unwrap()
                                .cast::<AnyInitialStateEvent>()
                        },
                        {
                            let initial_event: InitialStateEvent<RoomNameEventContent> =
                                InitialStateEvent {
                                    content: RoomNameEventContent::new(Some(
                                        target_space.name.clone(),
                                    )),
                                    state_key: EmptyStateKey {},
                                };
                            Raw::new(&initial_event)
                                .unwrap()
                                .cast::<AnyInitialStateEvent>()
                        },
                        {
                            let initial_event: InitialStateEvent<RoomTopicEventContent> =
                                InitialStateEvent {
                                    content: RoomTopicEventContent::new(
                                        "Automatically created by `b_room_sorting.rs`".to_string(),
                                    ),
                                    state_key: EmptyStateKey {},
                                };
                            Raw::new(&initial_event)
                                .unwrap()
                                .cast::<AnyInitialStateEvent>()
                        },
                    ];
                    request.initial_state = &creation_state;
                    let result = client.create_room(request).await.unwrap();
                    spaces.insert(target_space, result.room_id.clone());
                    client
                        .join_room_by_id(&result.room_id)
                        .await
                        .unwrap()
                        .room_id
                };

                let mut in_target_space = false;
                let mut in_external_space = None::<OwnedRoomId>;
                for space in client.joined_rooms() {
                    if space.is_space() {
                        if let Some(SyncSpaceChildEvent::Original(event)) = space
                            .get_state_event_static_for_key(&room.room_id().to_owned())
                            .await
                            .unwrap()
                            .map(|event| event.deserialize().unwrap())
                        {
                            // The "via" field is documented as required,
                            // so its absence indicates a tombstone.
                            match event.content.via {
                                Some(_) => {
                                    if space.room_id() == space_id {
                                        in_target_space = true;
                                    } else if space.room_id().server_name().as_str() != "cy.md" {
                                        in_external_space = Some(space.room_id().to_owned());
                                    } else {
                                        println!(
                                            " > Removing room {} from space {}",
                                            room.room_id(),
                                            space.room_id()
                                        );
                                        if do_move {
                                            space
                                                .redact(&event.event_id, None, None)
                                                .await
                                                .unwrap();
                                        }
                                    }
                                }
                                None => {
                                    println!(
                                        " > (room {} was once in space {})",
                                        room.room_id(),
                                        space.room_id()
                                    );
                                }
                            }
                        }
                    }
                }

                if in_target_space {
                    println!(
                        " > Room {} is already in the correct space {}",
                        room.room_id(),
                        space_id
                    );
                } else if let Some(external_space_id) = in_external_space {
                    println!(
                        " > Room {} is already in the external space {}",
                        room.room_id(),
                        external_space_id
                    );
                } else {
                    println!(" > Adding room {} into space {}", room.room_id(), space_id);
                    if do_move {
                        let space_room = client.get_joined_room(&space_id).unwrap();

                        let mut content = SpaceChildEventContent::new();
                        content.suggested = Some(false);
                        content.via = Some(vec![space_id.server_name().to_owned()]);
                        let result = space_room
                            .send_state_event_for_key(room.room_id(), content)
                            .await
                            .unwrap();
                        println!("   > Moved: event={}", result.event_id);
                    }
                }

It looks like there is some space support already (Room::is_space()) and the rest are achievable with just reading/sending ruma events. Maybe Fractal could do something like this too for the time being? HTH.

CyberShadow avatar Jul 05 '23 15:07 CyberShadow

Maybe Fractal could do something like this too for the time being?

@CyberShadow My guess is that Fractal devs won't be exactly fond of doing this themselves. Fractal used to implement all the Matrix-related things, but now with the fractal-next rewrite, they switched to using matrix-rust-sdk. Making exceptions for certain features is a good way to fall down a rabbit hole where everything is implemented in Fractal again.

But I'm just guessing here, I'm not a Fractal dev (nor a Rust dev).

k8ieone avatar Jul 06 '23 00:07 k8ieone

Having everything done in matrix-rust-sdk would be preferable, although I'm not a Fractal dev.

Implementing Matrix features would best be done here, not in downstreams.

orowith2os avatar Jul 06 '23 01:07 orowith2os

Any updates?

nyabinary avatar Jan 04 '24 14:01 nyabinary

As far as I know, still not planned for the next months by the Element team. Contributions would be appreciated, though :nerd_face:

bnjbvr avatar Jan 04 '24 14:01 bnjbvr

As far as I know, still not planned for the next months by the Element team. Contributions would be appreciated, though 🤓

Oh how is Element X going deal with spaces then?

nyabinary avatar Jan 04 '24 15:01 nyabinary

We do not know yet when and how spaces will be implemented in EX. It will not happen in the 4 next months for sure.

manuroe avatar Jan 05 '24 07:01 manuroe