From 9d1c6fb8eb491757f27a7013fe02a95c4c88e7f8 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 21 Aug 2024 12:56:11 +0300 Subject: [PATCH 1/2] ffi: expose room membership through the RoomListItem and allow invited rooms to be build differently than "full" ones We're finding ourselves in the situation in which we can't interact with invites through normal Room APIs as `full_room`s can't be build from the RoomListItem. Full rooms require the timeline to be configured before use and the timeline can't be configured because encryption cannot be fetched for invited rooms on homeservers that have previews disabled (see #3848 and #3850) In response we now expose the room's membership directly from the `RoomListItem` so that the final client can chose which of the 2 rooms types (invited or full) to ask for before using aforementioned APIs. --- bindings/matrix-sdk-ffi/src/room_list.rs | 31 ++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs index 8b61352b2ad..acf54193dcf 100644 --- a/bindings/matrix-sdk-ffi/src/room_list.rs +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -23,7 +23,7 @@ use tokio::sync::RwLock; use crate::{ error::ClientError, - room::Room, + room::{Membership, Room}, room_info::RoomInfo, timeline::{EventTimelineItem, Timeline}, timeline_event_filter::TimelineEventTypeFilter, @@ -50,6 +50,8 @@ pub enum RoomListError { InitializingTimeline { error: String }, #[error("Event cache ran into an error: {error}")] EventCache { error: String }, + #[error("The requested room doesn't match the membership requirements")] + IncorrectRoomMembership, } impl From for RoomListError { @@ -603,10 +605,35 @@ impl RoomListItem { Ok(RoomInfo::new(self.inner.inner_room()).await?) } + /// The room's current membership state + fn membership(&self) -> Membership { + self.inner.inner_room().state().into() + } + + /// Builds a `Room` FFI from an invited room without initializing its + /// internal timeline + /// + /// An error will be returned if the room is a state different than invited + /// + /// ⚠️ Holding on to this room instance after it has been joined is not + /// safe. Use `full_room` instead + fn invited_room(&self) -> Result, RoomListError> { + if !matches!(self.membership(), Membership::Invited) { + return Err(RoomListError::IncorrectRoomMembership); + } + + Ok(Arc::new(Room::new(self.inner.inner_room().clone()))) + } + /// Build a full `Room` FFI object, filling its associated timeline. /// - /// If its internal timeline hasn't been initialized, it'll fail. + /// An error will be returned if the room is a state different than joined + /// or if its internal timeline hasn't been initialized. fn full_room(&self) -> Result, RoomListError> { + if !matches!(self.membership(), Membership::Joined) { + return Err(RoomListError::IncorrectRoomMembership); + } + if let Some(timeline) = self.inner.timeline() { Ok(Arc::new(Room::with_timeline( self.inner.inner_room().clone(), From 89c65fab6cf3146a8c9dfed63724a086f45c0823 Mon Sep 17 00:00:00 2001 From: Stefan Ceriu Date: Wed, 21 Aug 2024 16:08:46 +0300 Subject: [PATCH 2/2] Address PR comments, add `expected` and `actual` values to the new `RoomListError::IncorrectRoomMembership` error --- bindings/matrix-sdk-ffi/src/room.rs | 2 +- bindings/matrix-sdk-ffi/src/room_list.rs | 16 +++++++++++----- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index 01d8715489b..0cd2d95b5f8 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -41,7 +41,7 @@ use crate::{ TaskHandle, }; -#[derive(uniffi::Enum)] +#[derive(Debug, uniffi::Enum)] pub enum Membership { Invited, Joined, diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs index acf54193dcf..48a6c100397 100644 --- a/bindings/matrix-sdk-ffi/src/room_list.rs +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -50,8 +50,8 @@ pub enum RoomListError { InitializingTimeline { error: String }, #[error("Event cache ran into an error: {error}")] EventCache { error: String }, - #[error("The requested room doesn't match the membership requirements")] - IncorrectRoomMembership, + #[error("The requested room doesn't match the membership requirements {expected:?}, observed {actual:?}")] + IncorrectRoomMembership { expected: Membership, actual: Membership }, } impl From for RoomListError { @@ -605,7 +605,7 @@ impl RoomListItem { Ok(RoomInfo::new(self.inner.inner_room()).await?) } - /// The room's current membership state + /// The room's current membership state. fn membership(&self) -> Membership { self.inner.inner_room().state().into() } @@ -619,7 +619,10 @@ impl RoomListItem { /// safe. Use `full_room` instead fn invited_room(&self) -> Result, RoomListError> { if !matches!(self.membership(), Membership::Invited) { - return Err(RoomListError::IncorrectRoomMembership); + return Err(RoomListError::IncorrectRoomMembership { + expected: Membership::Invited, + actual: self.membership(), + }); } Ok(Arc::new(Room::new(self.inner.inner_room().clone()))) @@ -631,7 +634,10 @@ impl RoomListItem { /// or if its internal timeline hasn't been initialized. fn full_room(&self) -> Result, RoomListError> { if !matches!(self.membership(), Membership::Joined) { - return Err(RoomListError::IncorrectRoomMembership); + return Err(RoomListError::IncorrectRoomMembership { + expected: Membership::Joined, + actual: self.membership(), + }); } if let Some(timeline) = self.inner.timeline() {