From 68c19f412906700126a3711b293f8cf146f07b47 Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Wed, 7 Jun 2023 17:38:11 +0200 Subject: [PATCH] feat(ffi): First step for `RoomList` in FFI bindings. --- bindings/matrix-sdk-ffi/Cargo.toml | 2 +- bindings/matrix-sdk-ffi/src/api.udl | 31 ++++ bindings/matrix-sdk-ffi/src/lib.rs | 6 +- bindings/matrix-sdk-ffi/src/room_list.rs | 170 ++++++++++++++++++++ bindings/matrix-sdk-ffi/src/sliding_sync.rs | 16 +- 5 files changed, 218 insertions(+), 7 deletions(-) create mode 100644 bindings/matrix-sdk-ffi/src/room_list.rs diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index 8870789eb..5aa5052a9 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -29,7 +29,7 @@ eyeball-im = { workspace = true } extension-trait = "1.0.1" futures-core = { workspace = true } futures-util = { workspace = true } -matrix-sdk-ui = { path = "../../crates/matrix-sdk-ui", default-features = false, features = ["e2e-encryption", "experimental-sliding-sync"] } +matrix-sdk-ui = { path = "../../crates/matrix-sdk-ui" } mime = "0.3.16" # FIXME: we currently can't feature flag anything in the api.udl, therefore we must enforce experimental-sliding-sync being exposed here.. # see https://github.com/matrix-org/matrix-rust-sdk/issues/1014 diff --git a/bindings/matrix-sdk-ffi/src/api.udl b/bindings/matrix-sdk-ffi/src/api.udl index fc469477e..8c43e6165 100644 --- a/bindings/matrix-sdk-ffi/src/api.udl +++ b/bindings/matrix-sdk-ffi/src/api.udl @@ -50,6 +50,7 @@ callback interface SlidingSyncListStateObserver { void did_receive_update(SlidingSyncState new_state); }; +// Used by `SlidingSync` _and_ `RoomList`. Be careful. [Enum] interface RoomListEntry { Empty(); @@ -167,3 +168,33 @@ callback interface SessionVerificationControllerDelegate { void did_cancel(); void did_finish(); }; + +enum RoomListState { + "Init", + "FirstRooms", + "AllRooms", + "CarryOn", + "Terminated", +}; + +callback interface RoomListStateObserver { + void on_update(RoomListState state); +}; + +[Enum] +interface RoomListEntriesUpdate { + Append(sequence values); + Clear(); + PushFront(RoomListEntry value); + PushBack(RoomListEntry value); + PopFront(); + PopBack(); + Insert(u32 index, RoomListEntry value); + Set(u32 index, RoomListEntry value); + Remove(u32 index); + Reset(sequence values); +}; + +callback interface RoomListEntriesObserver { + void on_update(RoomListEntriesUpdate room_entries_update); +}; diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index 228bb4598..b00f3b672 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -30,6 +30,7 @@ pub mod event; mod helpers; pub mod notification; pub mod room; +pub mod room_list; pub mod room_member; pub mod session_verification; pub mod sliding_sync; @@ -45,8 +46,9 @@ pub use platform::*; // Re-exports for more convenient use inside other submodules use self::error::ClientError; pub use self::{ - authentication_service::*, client::*, event::*, notification::*, room::*, room_member::*, - session_verification::*, sliding_sync::*, task_handle::TaskHandle, timeline::*, tracing::*, + authentication_service::*, client::*, event::*, notification::*, room::*, room_list::*, + room_member::*, session_verification::*, sliding_sync::*, task_handle::TaskHandle, timeline::*, + tracing::*, }; uniffi::include_scaffolding!("api"); diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs new file mode 100644 index 000000000..c786ffa46 --- /dev/null +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -0,0 +1,170 @@ +use std::{fmt::Debug, sync::Arc}; + +use eyeball_im::VectorDiff; +use futures_util::{pin_mut, StreamExt}; + +use crate::{Client, RoomListEntry, TaskHandle, RUNTIME}; + +#[uniffi::export] +impl Client { + /// Get a new `RoomList` instance. + pub fn room_list(&self) -> Result, RoomListError> { + Ok(Arc::new(RoomList { + inner: Arc::new( + RUNTIME + .block_on(async { matrix_sdk_ui::RoomList::new(self.inner.clone()).await }) + .map_err(RoomListError::from)?, + ), + })) + } +} + +#[derive(uniffi::Error)] +pub enum RoomListError { + SlidingSync { error: String }, + UnknownList { list_name: String }, + InputHasNotBeenApplied, + RoomNotFound { room_name: String }, +} + +impl From for RoomListError { + fn from(value: matrix_sdk_ui::room_list::Error) -> Self { + use matrix_sdk_ui::room_list::Error::*; + + match value { + SlidingSync(error) => Self::SlidingSync { error: error.to_string() }, + UnknownList(list_name) => Self::UnknownList { list_name }, + InputHasNotBeenApplied(_) => Self::InputHasNotBeenApplied, + RoomNotFound(room_id) => Self::RoomNotFound { room_name: room_id.to_string() }, + } + } +} + +#[derive(uniffi::Object)] +pub struct RoomList { + inner: Arc, +} + +#[uniffi::export] +impl RoomList { + fn sync(&self) -> Arc { + let this = self.inner.clone(); + + Arc::new(TaskHandle::new(RUNTIME.spawn(async move { + let sync_stream = this.sync(); + pin_mut!(sync_stream); + + while let Some(_) = sync_stream.next().await { + // keep going! + } + }))) + } + + fn state(&self, observer: Box) -> Arc { + let state_stream = self.inner.state(); + + Arc::new(TaskHandle::new(RUNTIME.spawn(async move { + pin_mut!(state_stream); + + while let Some(state) = state_stream.next().await { + observer.on_update(state.into()); + } + }))) + } + + fn entries( + &self, + observer: Box, + ) -> Result { + let (entries, entries_stream) = + RUNTIME.block_on(async { self.inner.entries().await.map_err(RoomListError::from) })?; + + Ok(RoomListEntriesResult { + entries: entries.iter().map(Into::into).collect(), + entries_stream: Arc::new(TaskHandle::new(RUNTIME.spawn(async move { + pin_mut!(entries_stream); + + while let Some(diff) = entries_stream.next().await { + observer.on_update(diff.into()); + } + }))), + }) + } +} + +#[derive(uniffi::Record)] +pub struct RoomListEntriesResult { + pub entries: Vec, + pub entries_stream: Arc, +} + +// Also declared in the UDL file. +pub enum RoomListState { + Init, + FirstRooms, + AllRooms, + CarryOn, + Terminated, +} + +impl From for RoomListState { + fn from(value: matrix_sdk_ui::room_list::State) -> Self { + use matrix_sdk_ui::room_list::State::*; + + match value { + Init => Self::Init, + FirstRooms => Self::FirstRooms, + AllRooms => Self::AllRooms, + CarryOn => Self::CarryOn, + Terminated { .. } => Self::Terminated, + } + } +} + +// Also declared in the UDL file. +pub trait RoomListStateObserver: Send + Sync + Debug { + fn on_update(&self, state: RoomListState); +} + +pub enum RoomListEntriesUpdate { + Append { values: Vec }, + Clear, + PushFront { value: RoomListEntry }, + PushBack { value: RoomListEntry }, + PopFront, + PopBack, + Insert { index: u32, value: RoomListEntry }, + Set { index: u32, value: RoomListEntry }, + Remove { index: u32 }, + Reset { values: Vec }, +} + +impl From> for RoomListEntriesUpdate { + fn from(other: VectorDiff) -> Self { + match other { + VectorDiff::Append { values } => { + Self::Append { values: values.into_iter().map(Into::into).collect() } + } + VectorDiff::Clear => Self::Clear, + VectorDiff::PushFront { value } => Self::PushFront { value: value.into() }, + VectorDiff::PushBack { value } => Self::PushBack { value: value.into() }, + VectorDiff::PopFront => Self::PopFront, + VectorDiff::PopBack => Self::PopBack, + VectorDiff::Insert { index, value } => { + Self::Insert { index: u32::try_from(index).unwrap(), value: value.into() } + } + VectorDiff::Set { index, value } => { + Self::Set { index: u32::try_from(index).unwrap(), value: value.into() } + } + VectorDiff::Remove { index } => Self::Remove { index: u32::try_from(index).unwrap() }, + VectorDiff::Reset { values } => { + Self::Reset { values: values.into_iter().map(Into::into).collect() } + } + } + } +} + +// Also declared in the UDL file. +pub trait RoomListEntriesObserver: Send + Sync + Debug { + fn on_update(&self, room_entries_update: RoomListEntriesUpdate); +} diff --git a/bindings/matrix-sdk-ffi/src/sliding_sync.rs b/bindings/matrix-sdk-ffi/src/sliding_sync.rs index 61f02286a..9a6e518ab 100644 --- a/bindings/matrix-sdk-ffi/src/sliding_sync.rs +++ b/bindings/matrix-sdk-ffi/src/sliding_sync.rs @@ -332,12 +332,20 @@ pub enum RoomListEntry { Filled { room_id: String }, } +impl From for RoomListEntry { + fn from(value: MatrixRoomEntry) -> Self { + (&value).into() + } +} + impl From<&MatrixRoomEntry> for RoomListEntry { - fn from(other: &MatrixRoomEntry) -> Self { - match other { + fn from(value: &MatrixRoomEntry) -> Self { + match value { MatrixRoomEntry::Empty => Self::Empty, - MatrixRoomEntry::Filled(b) => Self::Filled { room_id: b.to_string() }, - MatrixRoomEntry::Invalidated(b) => Self::Invalidated { room_id: b.to_string() }, + MatrixRoomEntry::Filled(room_id) => Self::Filled { room_id: room_id.to_string() }, + MatrixRoomEntry::Invalidated(room_id) => { + Self::Invalidated { room_id: room_id.to_string() } + } } } }