atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

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

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2025 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for the specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
use std::{fmt::Debug, sync::Arc};
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
use eyeball_im::VectorDiff;
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
use futures_util::{StreamExt, pin_mut};
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
use matrix_sdk_common::{SendOutsideWasm, SyncOutsideWasm};
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
use matrix_sdk_ui::spaces::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
    SpaceFilter as UISpaceFilter, SpaceRoom as UISpaceRoom, SpaceRoomList as UISpaceRoomList,
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
    SpaceService as UISpaceService,
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
    leave::{LeaveSpaceHandle as UILeaveSpaceHandle, LeaveSpaceRoom as UILeaveSpaceRoom},
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
    room_list::SpaceRoomListPaginationState,
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
use ruma::RoomId;
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
    TaskHandle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
    client::JoinRule,
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
    error::ClientError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
    room::{Membership, RoomHero},
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
    room_preview::RoomType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
    runtime::get_runtime_handle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
/// The main entry point into the Spaces facilities.
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
/// The spaces service is responsible for retrieving one's joined rooms,
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
/// building a graph out of their `m.space.parent` and `m.space.child` state
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
/// events, and providing access to the top-level spaces and their children.
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
pub struct SpaceService {
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
    inner: UISpaceService,
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
impl SpaceService {
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
    /// Creates a new `SpaceService` instance.
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
    pub(crate) fn new(inner: UISpaceService) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
        Self { inner }
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
impl SpaceService {
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
    /// Returns a list of all the top-level joined spaces. It will eagerly
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
    /// compute the latest version and also notify subscribers if there were
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
    /// any changes.
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
    pub async fn top_level_joined_spaces(&self) -> Vec<SpaceRoom> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
        self.inner.top_level_joined_spaces().await.into_iter().map(Into::into).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
    /// Subscribes to updates on the joined spaces list. If space rooms are
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
    /// joined or left, the stream will yield diffs that reflect the changes.
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
    pub async fn subscribe_to_top_level_joined_spaces(
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
        listener: Box<dyn SpaceServiceJoinedSpacesListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
        let (initial_values, mut stream) = self.inner.subscribe_to_top_level_joined_spaces().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
        listener.on_update(vec![SpaceListUpdate::Reset {
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
            values: initial_values.into_iter().map(Into::into).collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
        }]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
            while let Some(diffs) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
                listener.on_update(diffs.into_iter().map(Into::into).collect());
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
    /// Space filters provide access to a custom subset of the space graph that
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
    /// can be used in tandem with the [`crate::RoomListService`] to narrow
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
    /// down the presented rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
    /// They are limited to the first 2 levels of the graph, with the first
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
    /// level only containing direct descendants while the second holds the rest
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
    /// of them recursively.
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
    pub async fn space_filters(&self) -> Vec<SpaceFilter> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
        self.inner.space_filters().await.into_iter().map(|s| s.into()).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
    /// Subscribe to changes or updates to the space filters.
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
    pub async fn subscribe_to_space_filters(
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
        listener: Box<dyn SpaceServiceSpaceFiltersListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
        let (initial_values, mut stream) = self.inner.subscribe_to_space_filters().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
        listener.on_update(vec![SpaceFilterUpdate::Reset {
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
            values: initial_values.into_iter().map(Into::into).collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
        }]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
            while let Some(diffs) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
                listener.on_update(diffs.into_iter().map(Into::into).collect());
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
    /// Returns a flattened list containing all the spaces where the user has
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
    /// permission to send `m.space.child` state events.
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
    /// Note: Unlike [`Self::top_level_joined_spaces()`], this method does not
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
    /// recompute the space graph, nor does it notify subscribers about changes.
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
    pub async fn editable_spaces(&self) -> Vec<SpaceRoom> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
        self.inner.editable_spaces().await.into_iter().map(Into::into).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
    /// Returns a `SpaceRoomList` for the given space ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
    pub async fn space_room_list(
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
        space_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
    ) -> Result<Arc<SpaceRoomList>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
        let space_id = RoomId::parse(space_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
        Ok(Arc::new(SpaceRoomList::new(self.inner.space_room_list(space_id).await)))
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    /// Returns all known direct-parents of a given space room ID.
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    pub async fn joined_parents_of_child(
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
        child_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
    ) -> Result<Vec<SpaceRoom>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
        let child_id = RoomId::parse(child_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
        let parents = self.inner.joined_parents_of_child(&child_id).await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
        Ok(parents.into_iter().map(Into::into).collect())
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
    /// Returns the corresponding `SpaceRoom` for the given room ID, or `None`
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
    /// if it isn't known.
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
    pub async fn get_space_room(&self, room_id: String) -> Result<Option<SpaceRoom>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
        let room_id = RoomId::parse(room_id.as_str())?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
        Ok(self.inner.get_space_room(&room_id).await.map(Into::into))
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
    pub async fn add_child_to_space(
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
        child_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
        space_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
        let space_id = RoomId::parse(space_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
        let child_id = RoomId::parse(child_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
        self.inner.add_child_to_space(child_id, space_id).await.map_err(ClientError::from)
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
    pub async fn remove_child_from_space(
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
        child_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
        space_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
    ) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
        let space_id = RoomId::parse(space_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
        let child_id = RoomId::parse(child_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
        self.inner.remove_child_from_space(child_id, space_id).await.map_err(ClientError::from)
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
    /// Start a space leave process returning a [`LeaveSpaceHandle`] from which
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
    /// rooms can be retrieved in reversed BFS order starting from the requested
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
    /// `space_id` graph node. If the room is unknown then an error will be
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    /// returned.
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
    /// Once the rooms to be left are chosen the handle can be used to leave
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
    /// them.
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
    pub async fn leave_space(
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
        space_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
    ) -> Result<Arc<LeaveSpaceHandle>, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
        let space_id = RoomId::parse(space_id)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
        let handle = self.inner.leave_space(&space_id).await.map_err(ClientError::from)?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
        Ok(Arc::new(handle.into()))
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
/// The `SpaceRoomList` represents a paginated list of direct rooms
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
/// that belong to a particular space.
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
/// It can be used to paginate through the list (and have live updates on the
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
/// pagination state) as well as subscribe to changes as rooms are joined or
7ff8adafe9e4atagen2026-10-22 12:30 UTC194
/// left.
7ff8adafe9e4atagen2026-10-22 12:30 UTC195
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC196
/// The `SpaceRoomList` also automatically subscribes to client room changes
7ff8adafe9e4atagen2026-10-22 12:30 UTC197
/// and updates the list accordingly as rooms are joined or left.
7ff8adafe9e4atagen2026-10-22 12:30 UTC198
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC199
pub struct SpaceRoomList {
7ff8adafe9e4atagen2026-10-22 12:30 UTC200
    inner: UISpaceRoomList,
7ff8adafe9e4atagen2026-10-22 12:30 UTC201
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC202
7ff8adafe9e4atagen2026-10-22 12:30 UTC203
impl SpaceRoomList {
7ff8adafe9e4atagen2026-10-22 12:30 UTC204
    /// Creates a new `SpaceRoomList` for the underlying UI crate room list.
7ff8adafe9e4atagen2026-10-22 12:30 UTC205
    fn new(inner: UISpaceRoomList) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC206
        Self { inner }
7ff8adafe9e4atagen2026-10-22 12:30 UTC207
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC208
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC209
7ff8adafe9e4atagen2026-10-22 12:30 UTC210
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC211
impl SpaceRoomList {
7ff8adafe9e4atagen2026-10-22 12:30 UTC212
    /// Returns the space of the room list if known.
7ff8adafe9e4atagen2026-10-22 12:30 UTC213
    pub fn space(&self) -> Option<SpaceRoom> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC214
        self.inner.space().map(Into::into)
7ff8adafe9e4atagen2026-10-22 12:30 UTC215
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC216
7ff8adafe9e4atagen2026-10-22 12:30 UTC217
    /// Subscribe to space updates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC218
    pub fn subscribe_to_space_updates(
7ff8adafe9e4atagen2026-10-22 12:30 UTC219
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC220
        listener: Box<dyn SpaceRoomListSpaceListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC221
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC222
        let space_updates = self.inner.subscribe_to_space_updates();
7ff8adafe9e4atagen2026-10-22 12:30 UTC223
7ff8adafe9e4atagen2026-10-22 12:30 UTC224
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC225
            pin_mut!(space_updates);
7ff8adafe9e4atagen2026-10-22 12:30 UTC226
7ff8adafe9e4atagen2026-10-22 12:30 UTC227
            while let Some(space) = space_updates.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC228
                listener.on_update(space.map(Into::into));
7ff8adafe9e4atagen2026-10-22 12:30 UTC229
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC230
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC231
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC232
7ff8adafe9e4atagen2026-10-22 12:30 UTC233
    /// Returns if the room list is currently paginating or not.
7ff8adafe9e4atagen2026-10-22 12:30 UTC234
    pub fn pagination_state(&self) -> SpaceRoomListPaginationState {
7ff8adafe9e4atagen2026-10-22 12:30 UTC235
        self.inner.pagination_state()
7ff8adafe9e4atagen2026-10-22 12:30 UTC236
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC237
7ff8adafe9e4atagen2026-10-22 12:30 UTC238
    /// Subscribe to pagination updates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC239
    pub fn subscribe_to_pagination_state_updates(
7ff8adafe9e4atagen2026-10-22 12:30 UTC240
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC241
        listener: Box<dyn SpaceRoomListPaginationStateListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC242
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC243
        let pagination_state = self.inner.subscribe_to_pagination_state_updates();
7ff8adafe9e4atagen2026-10-22 12:30 UTC244
7ff8adafe9e4atagen2026-10-22 12:30 UTC245
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC246
            pin_mut!(pagination_state);
7ff8adafe9e4atagen2026-10-22 12:30 UTC247
7ff8adafe9e4atagen2026-10-22 12:30 UTC248
            while let Some(state) = pagination_state.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC249
                listener.on_update(state);
7ff8adafe9e4atagen2026-10-22 12:30 UTC250
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC251
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC252
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC253
7ff8adafe9e4atagen2026-10-22 12:30 UTC254
    /// Return the current list of rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC255
    pub async fn rooms(&self) -> Vec<SpaceRoom> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC256
        self.inner.rooms().await.into_iter().map(Into::into).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC257
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC258
7ff8adafe9e4atagen2026-10-22 12:30 UTC259
    /// Subscribes to room list updates.
7ff8adafe9e4atagen2026-10-22 12:30 UTC260
    pub async fn subscribe_to_room_update(
7ff8adafe9e4atagen2026-10-22 12:30 UTC261
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC262
        listener: Box<dyn SpaceRoomListEntriesListener>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC263
    ) -> Arc<TaskHandle> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC264
        let (initial_values, mut stream) = self.inner.subscribe_to_room_updates().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC265
7ff8adafe9e4atagen2026-10-22 12:30 UTC266
        listener.on_update(vec![SpaceListUpdate::Reset {
7ff8adafe9e4atagen2026-10-22 12:30 UTC267
            values: initial_values.into_iter().map(Into::into).collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC268
        }]);
7ff8adafe9e4atagen2026-10-22 12:30 UTC269
7ff8adafe9e4atagen2026-10-22 12:30 UTC270
        Arc::new(TaskHandle::new(get_runtime_handle().spawn(async move {
7ff8adafe9e4atagen2026-10-22 12:30 UTC271
            while let Some(diffs) = stream.next().await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC272
                listener.on_update(diffs.into_iter().map(Into::into).collect());
7ff8adafe9e4atagen2026-10-22 12:30 UTC273
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC274
        })))
7ff8adafe9e4atagen2026-10-22 12:30 UTC275
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC276
7ff8adafe9e4atagen2026-10-22 12:30 UTC277
    /// Ask the list to retrieve the next page if the end hasn't been reached
7ff8adafe9e4atagen2026-10-22 12:30 UTC278
    /// yet. Otherwise it no-ops.
7ff8adafe9e4atagen2026-10-22 12:30 UTC279
    pub async fn paginate(&self) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC280
        self.inner.paginate().await.map_err(ClientError::from)
7ff8adafe9e4atagen2026-10-22 12:30 UTC281
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC282
7ff8adafe9e4atagen2026-10-22 12:30 UTC283
    /// Clears the room list back to its initial state so that any new changes
7ff8adafe9e4atagen2026-10-22 12:30 UTC284
    /// to the hierarchy will be included the next time [`Self::paginate`] is
7ff8adafe9e4atagen2026-10-22 12:30 UTC285
    /// called.
7ff8adafe9e4atagen2026-10-22 12:30 UTC286
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC287
    /// This is useful when you've added or removed children from the space as
7ff8adafe9e4atagen2026-10-22 12:30 UTC288
    /// the list is based on a cached state that lives server-side, meaning
7ff8adafe9e4atagen2026-10-22 12:30 UTC289
    /// the /hierarchy request needs to be restarted from scratch to pick up
7ff8adafe9e4atagen2026-10-22 12:30 UTC290
    /// the changes.
7ff8adafe9e4atagen2026-10-22 12:30 UTC291
    pub async fn reset(&self) {
7ff8adafe9e4atagen2026-10-22 12:30 UTC292
        self.inner.reset().await;
7ff8adafe9e4atagen2026-10-22 12:30 UTC293
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC294
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC295
7ff8adafe9e4atagen2026-10-22 12:30 UTC296
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC297
pub trait SpaceRoomListSpaceListener: SendOutsideWasm + SyncOutsideWasm + Debug {
7ff8adafe9e4atagen2026-10-22 12:30 UTC298
    fn on_update(&self, space: Option<SpaceRoom>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC299
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC300
7ff8adafe9e4atagen2026-10-22 12:30 UTC301
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC302
pub trait SpaceRoomListPaginationStateListener: SendOutsideWasm + SyncOutsideWasm + Debug {
7ff8adafe9e4atagen2026-10-22 12:30 UTC303
    fn on_update(&self, pagination_state: SpaceRoomListPaginationState);
7ff8adafe9e4atagen2026-10-22 12:30 UTC304
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC305
7ff8adafe9e4atagen2026-10-22 12:30 UTC306
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC307
pub trait SpaceRoomListEntriesListener: SendOutsideWasm + SyncOutsideWasm + Debug {
7ff8adafe9e4atagen2026-10-22 12:30 UTC308
    fn on_update(&self, rooms: Vec<SpaceListUpdate>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC309
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC310
7ff8adafe9e4atagen2026-10-22 12:30 UTC311
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC312
pub trait SpaceServiceJoinedSpacesListener: SendOutsideWasm + SyncOutsideWasm + Debug {
7ff8adafe9e4atagen2026-10-22 12:30 UTC313
    fn on_update(&self, room_updates: Vec<SpaceListUpdate>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC314
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC315
7ff8adafe9e4atagen2026-10-22 12:30 UTC316
#[matrix_sdk_ffi_macros::export(callback_interface)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC317
pub trait SpaceServiceSpaceFiltersListener: SendOutsideWasm + SyncOutsideWasm + Debug {
7ff8adafe9e4atagen2026-10-22 12:30 UTC318
    fn on_update(&self, filter_updates: Vec<SpaceFilterUpdate>);
7ff8adafe9e4atagen2026-10-22 12:30 UTC319
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC320
7ff8adafe9e4atagen2026-10-22 12:30 UTC321
/// Structure representing a room in a space and aggregated information
7ff8adafe9e4atagen2026-10-22 12:30 UTC322
/// relevant to the UI layer.
7ff8adafe9e4atagen2026-10-22 12:30 UTC323
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC324
pub struct SpaceRoom {
7ff8adafe9e4atagen2026-10-22 12:30 UTC325
    /// The ID of the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC326
    pub room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC327
    /// The canonical alias of the room, if any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC328
    pub canonical_alias: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC329
    /// The room's name from the room state event if received from sync, or one
7ff8adafe9e4atagen2026-10-22 12:30 UTC330
    /// that's been computed otherwise.
7ff8adafe9e4atagen2026-10-22 12:30 UTC331
    pub display_name: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC332
    /// Room name as defined by the room state event only.
7ff8adafe9e4atagen2026-10-22 12:30 UTC333
    pub raw_name: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC334
    /// The topic of the room, if any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC335
    pub topic: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC336
    /// The URL for the room's avatar, if one is set.
7ff8adafe9e4atagen2026-10-22 12:30 UTC337
    pub avatar_url: Option<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC338
    /// The type of room from `m.room.create`, if any.
7ff8adafe9e4atagen2026-10-22 12:30 UTC339
    pub room_type: RoomType,
7ff8adafe9e4atagen2026-10-22 12:30 UTC340
    /// The number of members joined to the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC341
    pub num_joined_members: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC342
    /// The join rule of the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC343
    pub join_rule: Option<JoinRule>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC344
    /// Whether the room may be viewed by users without joining.
7ff8adafe9e4atagen2026-10-22 12:30 UTC345
    pub world_readable: Option<bool>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC346
    /// Whether guest users may join the room and participate in it.
7ff8adafe9e4atagen2026-10-22 12:30 UTC347
    pub guest_can_join: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC348
7ff8adafe9e4atagen2026-10-22 12:30 UTC349
    /// Whether this room is a direct room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC350
    ///
7ff8adafe9e4atagen2026-10-22 12:30 UTC351
    /// Only set if the room is known to the client otherwise we
7ff8adafe9e4atagen2026-10-22 12:30 UTC352
    /// assume DMs shouldn't be exposed publicly in spaces.
7ff8adafe9e4atagen2026-10-22 12:30 UTC353
    pub is_direct: Option<bool>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC354
    /// The number of children room this has, if a space.
7ff8adafe9e4atagen2026-10-22 12:30 UTC355
    pub children_count: u64,
7ff8adafe9e4atagen2026-10-22 12:30 UTC356
    /// Whether this room is joined, left etc.
7ff8adafe9e4atagen2026-10-22 12:30 UTC357
    pub state: Option<Membership>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC358
    /// A list of room members considered to be heroes.
7ff8adafe9e4atagen2026-10-22 12:30 UTC359
    pub heroes: Option<Vec<RoomHero>>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC360
    /// The via parameters of the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC361
    pub via: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC362
    /// Whether this room is a DM, if known.
7ff8adafe9e4atagen2026-10-22 12:30 UTC363
    /// Note this value can be calculated following some assumptions and is not
7ff8adafe9e4atagen2026-10-22 12:30 UTC364
    /// guaranteed to be accurate.
7ff8adafe9e4atagen2026-10-22 12:30 UTC365
    pub is_dm: Option<bool>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC366
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC367
7ff8adafe9e4atagen2026-10-22 12:30 UTC368
impl From<UISpaceRoom> for SpaceRoom {
7ff8adafe9e4atagen2026-10-22 12:30 UTC369
    fn from(room: UISpaceRoom) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC370
        Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC371
            room_id: room.room_id.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC372
            canonical_alias: room.canonical_alias.map(|alias| alias.into()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC373
            display_name: room.display_name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC374
            raw_name: room.name,
7ff8adafe9e4atagen2026-10-22 12:30 UTC375
            topic: room.topic,
7ff8adafe9e4atagen2026-10-22 12:30 UTC376
            avatar_url: room.avatar_url.map(|url| url.into()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC377
            room_type: room.room_type.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC378
            num_joined_members: room.num_joined_members,
7ff8adafe9e4atagen2026-10-22 12:30 UTC379
            join_rule: room.join_rule.map(Into::into),
7ff8adafe9e4atagen2026-10-22 12:30 UTC380
            world_readable: room.world_readable,
7ff8adafe9e4atagen2026-10-22 12:30 UTC381
            guest_can_join: room.guest_can_join,
7ff8adafe9e4atagen2026-10-22 12:30 UTC382
            is_direct: room.is_direct,
7ff8adafe9e4atagen2026-10-22 12:30 UTC383
            children_count: room.children_count,
7ff8adafe9e4atagen2026-10-22 12:30 UTC384
            state: room.state.map(Into::into),
7ff8adafe9e4atagen2026-10-22 12:30 UTC385
            heroes: room.heroes.map(|heroes| heroes.into_iter().map(Into::into).collect()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC386
            via: room.via.into_iter().map(Into::into).collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC387
            is_dm: room.is_dm,
7ff8adafe9e4atagen2026-10-22 12:30 UTC388
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC389
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC390
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC391
7ff8adafe9e4atagen2026-10-22 12:30 UTC392
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC393
pub enum SpaceListUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC394
    Append { values: Vec<SpaceRoom> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC395
    Clear,
7ff8adafe9e4atagen2026-10-22 12:30 UTC396
    PushFront { value: SpaceRoom },
7ff8adafe9e4atagen2026-10-22 12:30 UTC397
    PushBack { value: SpaceRoom },
7ff8adafe9e4atagen2026-10-22 12:30 UTC398
    PopFront,
7ff8adafe9e4atagen2026-10-22 12:30 UTC399
    PopBack,
7ff8adafe9e4atagen2026-10-22 12:30 UTC400
    Insert { index: u32, value: SpaceRoom },
7ff8adafe9e4atagen2026-10-22 12:30 UTC401
    Set { index: u32, value: SpaceRoom },
7ff8adafe9e4atagen2026-10-22 12:30 UTC402
    Remove { index: u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC403
    Truncate { length: u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC404
    Reset { values: Vec<SpaceRoom> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC405
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC406
7ff8adafe9e4atagen2026-10-22 12:30 UTC407
impl From<VectorDiff<UISpaceRoom>> for SpaceListUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC408
    fn from(diff: VectorDiff<UISpaceRoom>) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC409
        match diff {
7ff8adafe9e4atagen2026-10-22 12:30 UTC410
            VectorDiff::Append { values } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC411
                Self::Append { values: values.into_iter().map(|v| v.into()).collect() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC412
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC413
            VectorDiff::Clear => Self::Clear,
7ff8adafe9e4atagen2026-10-22 12:30 UTC414
            VectorDiff::PushFront { value } => Self::PushFront { value: value.into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC415
            VectorDiff::PushBack { value } => Self::PushBack { value: value.into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC416
            VectorDiff::PopFront => Self::PopFront,
7ff8adafe9e4atagen2026-10-22 12:30 UTC417
            VectorDiff::PopBack => Self::PopBack,
7ff8adafe9e4atagen2026-10-22 12:30 UTC418
            VectorDiff::Insert { index, value } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC419
                Self::Insert { index: index as u32, value: value.into() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC420
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC421
            VectorDiff::Set { index, value } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC422
                Self::Set { index: index as u32, value: value.into() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC423
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC424
            VectorDiff::Remove { index } => Self::Remove { index: index as u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC425
            VectorDiff::Truncate { length } => Self::Truncate { length: length as u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC426
            VectorDiff::Reset { values } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC427
                Self::Reset { values: values.into_iter().map(|v| v.into()).collect() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC428
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC429
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC430
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC431
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC432
7ff8adafe9e4atagen2026-10-22 12:30 UTC433
#[derive(uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC434
pub enum SpaceFilterUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC435
    Append { values: Vec<SpaceFilter> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC436
    Clear,
7ff8adafe9e4atagen2026-10-22 12:30 UTC437
    PushFront { value: SpaceFilter },
7ff8adafe9e4atagen2026-10-22 12:30 UTC438
    PushBack { value: SpaceFilter },
7ff8adafe9e4atagen2026-10-22 12:30 UTC439
    PopFront,
7ff8adafe9e4atagen2026-10-22 12:30 UTC440
    PopBack,
7ff8adafe9e4atagen2026-10-22 12:30 UTC441
    Insert { index: u32, value: SpaceFilter },
7ff8adafe9e4atagen2026-10-22 12:30 UTC442
    Set { index: u32, value: SpaceFilter },
7ff8adafe9e4atagen2026-10-22 12:30 UTC443
    Remove { index: u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC444
    Truncate { length: u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC445
    Reset { values: Vec<SpaceFilter> },
7ff8adafe9e4atagen2026-10-22 12:30 UTC446
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC447
7ff8adafe9e4atagen2026-10-22 12:30 UTC448
impl From<VectorDiff<UISpaceFilter>> for SpaceFilterUpdate {
7ff8adafe9e4atagen2026-10-22 12:30 UTC449
    fn from(diff: VectorDiff<UISpaceFilter>) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC450
        match diff {
7ff8adafe9e4atagen2026-10-22 12:30 UTC451
            VectorDiff::Append { values } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC452
                Self::Append { values: values.into_iter().map(|v| v.into()).collect() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC453
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC454
            VectorDiff::Clear => Self::Clear,
7ff8adafe9e4atagen2026-10-22 12:30 UTC455
            VectorDiff::PushFront { value } => Self::PushFront { value: value.into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC456
            VectorDiff::PushBack { value } => Self::PushBack { value: value.into() },
7ff8adafe9e4atagen2026-10-22 12:30 UTC457
            VectorDiff::PopFront => Self::PopFront,
7ff8adafe9e4atagen2026-10-22 12:30 UTC458
            VectorDiff::PopBack => Self::PopBack,
7ff8adafe9e4atagen2026-10-22 12:30 UTC459
            VectorDiff::Insert { index, value } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC460
                Self::Insert { index: index as u32, value: value.into() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC461
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC462
            VectorDiff::Set { index, value } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC463
                Self::Set { index: index as u32, value: value.into() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC464
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC465
            VectorDiff::Remove { index } => Self::Remove { index: index as u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC466
            VectorDiff::Truncate { length } => Self::Truncate { length: length as u32 },
7ff8adafe9e4atagen2026-10-22 12:30 UTC467
            VectorDiff::Reset { values } => {
7ff8adafe9e4atagen2026-10-22 12:30 UTC468
                Self::Reset { values: values.into_iter().map(|v| v.into()).collect() }
7ff8adafe9e4atagen2026-10-22 12:30 UTC469
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC470
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC471
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC472
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC473
7ff8adafe9e4atagen2026-10-22 12:30 UTC474
/// The `LeaveSpaceHandle` processes rooms to be left in the order they were
7ff8adafe9e4atagen2026-10-22 12:30 UTC475
/// provided by the [`SpaceService`] and annotates them with extra data to
7ff8adafe9e4atagen2026-10-22 12:30 UTC476
/// inform the leave process e.g. if the current user is the last room admin.
7ff8adafe9e4atagen2026-10-22 12:30 UTC477
///
7ff8adafe9e4atagen2026-10-22 12:30 UTC478
/// Once the upstream client decides what rooms should actually be left, the
7ff8adafe9e4atagen2026-10-22 12:30 UTC479
/// handle provides a method to execute that too.
7ff8adafe9e4atagen2026-10-22 12:30 UTC480
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC481
pub struct LeaveSpaceHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC482
    inner: UILeaveSpaceHandle,
7ff8adafe9e4atagen2026-10-22 12:30 UTC483
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC484
7ff8adafe9e4atagen2026-10-22 12:30 UTC485
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC486
impl LeaveSpaceHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC487
    /// A list of rooms to be left which next to normal [`SpaceRoom`] data also
7ff8adafe9e4atagen2026-10-22 12:30 UTC488
    /// include leave specific information.
7ff8adafe9e4atagen2026-10-22 12:30 UTC489
    pub fn rooms(&self) -> Vec<LeaveSpaceRoom> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC490
        let rooms = self.inner.rooms();
7ff8adafe9e4atagen2026-10-22 12:30 UTC491
        rooms.iter().map(|room| room.clone().into()).collect()
7ff8adafe9e4atagen2026-10-22 12:30 UTC492
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC493
7ff8adafe9e4atagen2026-10-22 12:30 UTC494
    /// Bulk leave the given rooms. Stops when encountering an error.
7ff8adafe9e4atagen2026-10-22 12:30 UTC495
    pub async fn leave(&self, room_ids: Vec<String>) -> Result<(), ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC496
        let room_ids = room_ids.iter().map(RoomId::parse).collect::<Result<Vec<_>, _>>()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC497
7ff8adafe9e4atagen2026-10-22 12:30 UTC498
        self.inner
7ff8adafe9e4atagen2026-10-22 12:30 UTC499
            .leave(|room| room_ids.contains(&room.space_room.room_id))
7ff8adafe9e4atagen2026-10-22 12:30 UTC500
            .await
7ff8adafe9e4atagen2026-10-22 12:30 UTC501
            .map_err(ClientError::from)
7ff8adafe9e4atagen2026-10-22 12:30 UTC502
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC503
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC504
7ff8adafe9e4atagen2026-10-22 12:30 UTC505
impl From<UILeaveSpaceHandle> for LeaveSpaceHandle {
7ff8adafe9e4atagen2026-10-22 12:30 UTC506
    fn from(handle: UILeaveSpaceHandle) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC507
        LeaveSpaceHandle { inner: handle }
7ff8adafe9e4atagen2026-10-22 12:30 UTC508
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC509
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC510
7ff8adafe9e4atagen2026-10-22 12:30 UTC511
/// Space leaving specific room that groups normal [`SpaceRoom`] details with
7ff8adafe9e4atagen2026-10-22 12:30 UTC512
/// information about the leaving user's role.
7ff8adafe9e4atagen2026-10-22 12:30 UTC513
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC514
pub struct LeaveSpaceRoom {
7ff8adafe9e4atagen2026-10-22 12:30 UTC515
    /// The underlying [`SpaceRoom`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC516
    pub space_room: SpaceRoom,
7ff8adafe9e4atagen2026-10-22 12:30 UTC517
    /// Whether the user is the last owner in the room. This helps clients
7ff8adafe9e4atagen2026-10-22 12:30 UTC518
    /// better inform the user about the consequences of leaving the room.
7ff8adafe9e4atagen2026-10-22 12:30 UTC519
    pub is_last_owner: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC520
    /// If the room creators have infinite PL.
7ff8adafe9e4atagen2026-10-22 12:30 UTC521
    pub are_creators_privileged: bool,
7ff8adafe9e4atagen2026-10-22 12:30 UTC522
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC523
7ff8adafe9e4atagen2026-10-22 12:30 UTC524
impl From<UILeaveSpaceRoom> for LeaveSpaceRoom {
7ff8adafe9e4atagen2026-10-22 12:30 UTC525
    fn from(room: UILeaveSpaceRoom) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC526
        LeaveSpaceRoom {
7ff8adafe9e4atagen2026-10-22 12:30 UTC527
            space_room: room.space_room.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC528
            is_last_owner: room.is_last_owner,
7ff8adafe9e4atagen2026-10-22 12:30 UTC529
            are_creators_privileged: room.are_creators_privileged,
7ff8adafe9e4atagen2026-10-22 12:30 UTC530
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC531
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC532
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC533
7ff8adafe9e4atagen2026-10-22 12:30 UTC534
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC535
pub struct SpaceFilter {
7ff8adafe9e4atagen2026-10-22 12:30 UTC536
    /// The underlying [`SpaceRoom`]
7ff8adafe9e4atagen2026-10-22 12:30 UTC537
    space_room: SpaceRoom,
7ff8adafe9e4atagen2026-10-22 12:30 UTC538
    /// The level of the space filter in the tree/hierarchy.
7ff8adafe9e4atagen2026-10-22 12:30 UTC539
    /// At this point in time the filters are limited to the first 2 levels.
7ff8adafe9e4atagen2026-10-22 12:30 UTC540
    level: u8,
7ff8adafe9e4atagen2026-10-22 12:30 UTC541
    /// The room identifiers of the descendants of this space.
7ff8adafe9e4atagen2026-10-22 12:30 UTC542
    /// For top level spaces (level 0) these will be direct descendants while
7ff8adafe9e4atagen2026-10-22 12:30 UTC543
    /// for first level spaces they will be all other descendants, recursively.
7ff8adafe9e4atagen2026-10-22 12:30 UTC544
    descendants: Vec<String>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC545
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC546
7ff8adafe9e4atagen2026-10-22 12:30 UTC547
impl From<UISpaceFilter> for SpaceFilter {
7ff8adafe9e4atagen2026-10-22 12:30 UTC548
    fn from(filter: UISpaceFilter) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC549
        SpaceFilter {
7ff8adafe9e4atagen2026-10-22 12:30 UTC550
            space_room: filter.space_room.into(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC551
            level: filter.level,
7ff8adafe9e4atagen2026-10-22 12:30 UTC552
            descendants: filter.descendants.into_iter().map(|id| id.to_string()).collect(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC553
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC554
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC555
}