diff --git a/bindings/matrix-sdk-ffi/src/api.udl b/bindings/matrix-sdk-ffi/src/api.udl index 6a9d790cbbb..6e1b4b24017 100644 --- a/bindings/matrix-sdk-ffi/src/api.udl +++ b/bindings/matrix-sdk-ffi/src/api.udl @@ -1,19 +1,5 @@ namespace matrix_sdk_ffi {}; -enum SlidingSyncListLoadingState { - /// Sliding Sync has not started to load anything yet. - "NotLoaded", - /// Sliding Sync has been preloaded, i.e. restored from a cache for example. - "Preloaded", - /// We are trying to load all remaining rooms, might be in batches - /// Updates are received from the loaded rooms, and new rooms are being fetched - /// in background - "PartiallyLoaded", - /// Updates are received for all the loaded rooms, and all rooms have been - /// loaded! - "FullyLoaded", -}; - dictionary CreateRoomParameters { string? name; string? topic = null; diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index 55b17efe58c..e70447a002a 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -1,14 +1,12 @@ use std::sync::{Arc, RwLock}; use anyhow::{anyhow, Context}; -use eyeball::SharedObservable; use matrix_sdk::{ media::{MediaFileHandle as SdkMediaFileHandle, MediaFormat, MediaRequest, MediaThumbnailSize}, room::Room as SdkRoom, ruma::{ api::client::{ account::whoami, - error::ErrorKind, media::get_content_thumbnail::v3::Method, push::{EmailPusherData, PusherIds, PusherInit, PusherKind as RumaPusherKind}, room::{create_room, Visibility}, @@ -24,7 +22,7 @@ use matrix_sdk::{ serde::Raw, EventEncryptionAlgorithm, TransactionId, UInt, UserId, }, - Client as MatrixClient, Error, LoopCtrl, + Client as MatrixClient, }; use ruma::{ push::{HttpPusherData as RumaHttpPusherData, PushFormat as RumaPushFormat}, @@ -32,7 +30,7 @@ use ruma::{ }; use serde_json::Value; use tokio::sync::broadcast::error::RecvError; -use tracing::{debug, error, warn}; +use tracing::{debug, error}; use url::Url; use super::{room::Room, session_verification::SessionVerificationController, RUNTIME}; @@ -135,7 +133,6 @@ pub struct Client { notification_delegate: Arc>>>, session_verification_controller: Arc>>, - pub(crate) sliding_sync_reset_broadcast_tx: Arc>, } impl Client { @@ -161,7 +158,6 @@ impl Client { delegate: Arc::new(RwLock::new(None)), notification_delegate: Arc::new(RwLock::new(None)), session_verification_controller, - sliding_sync_reset_broadcast_tx: Default::default(), }; let mut unknown_token_error_receiver = client.inner.subscribe_to_unknown_token_errors(); @@ -674,21 +670,6 @@ impl From<&search_users::v3::User> for UserProfile { } impl Client { - /// Process a sync error and return loop control accordingly - pub(crate) fn process_sync_error(&self, sync_error: Error) -> LoopCtrl { - let client_api_error_kind = sync_error.client_api_error_kind(); - match client_api_error_kind { - Some(ErrorKind::UnknownPos) => { - self.sliding_sync_reset_broadcast_tx.set(()); - LoopCtrl::Continue - } - _ => { - warn!("Ignoring sync error: {sync_error:?}"); - LoopCtrl::Continue - } - } - } - fn process_unknown_token_error(&self, unknown_token: matrix_sdk::UnknownToken) { if let Some(delegate) = &*self.delegate.read().unwrap() { delegate.did_receive_auth_error(unknown_token.soft_logout); diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index 221b17eaa08..1a0925e6ba3 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -33,16 +33,12 @@ mod room; mod room_list; mod room_member; mod session_verification; -mod sliding_sync; mod task_handle; mod timeline; mod tracing; use async_compat::TOKIO1 as RUNTIME; -use matrix_sdk::{ - ruma::events::room::{message::RoomMessageEventContent, MediaSource}, - SlidingSyncListLoadingState, -}; +use matrix_sdk::ruma::events::room::{message::RoomMessageEventContent, MediaSource}; use matrix_sdk_ui::timeline::EventItemOrigin; use self::{ diff --git a/bindings/matrix-sdk-ffi/src/room_list.rs b/bindings/matrix-sdk-ffi/src/room_list.rs index 14b2dc353b9..1b59177ea2b 100644 --- a/bindings/matrix-sdk-ffi/src/room_list.rs +++ b/bindings/matrix-sdk-ffi/src/room_list.rs @@ -2,16 +2,19 @@ use std::{fmt::Debug, sync::Arc}; use eyeball_im::VectorDiff; use futures_util::{pin_mut, StreamExt}; -use ruma::RoomId; +use matrix_sdk::{ + ruma::{ + api::client::sync::sync_events::{ + v4::RoomSubscription as RumaRoomSubscription, + UnreadNotificationsCount as RumaUnreadNotificationsCount, + }, + assign, RoomId, + }, + RoomListEntry as MatrixRoomListEntry, +}; use tokio::sync::RwLock; -use crate::{ - client::Client, - room::Room, - sliding_sync::{RoomListEntry, RoomSubscription, UnreadNotificationsCount}, - timeline::EventTimelineItem, - TaskHandle, RUNTIME, -}; +use crate::{client::Client, room::Room, timeline::EventTimelineItem, TaskHandle, RUNTIME}; #[uniffi::export] impl Client { @@ -384,3 +387,85 @@ impl RoomListItem { Arc::new(self.inner.unread_notifications().into()) } } + +#[derive(Clone, Debug, uniffi::Enum)] +pub enum RoomListEntry { + Empty, + Invalidated { room_id: String }, + Filled { room_id: String }, +} + +impl From for RoomListEntry { + fn from(value: MatrixRoomListEntry) -> Self { + (&value).into() + } +} + +impl From<&MatrixRoomListEntry> for RoomListEntry { + fn from(value: &MatrixRoomListEntry) -> Self { + match value { + MatrixRoomListEntry::Empty => Self::Empty, + MatrixRoomListEntry::Filled(room_id) => Self::Filled { room_id: room_id.to_string() }, + MatrixRoomListEntry::Invalidated(room_id) => { + Self::Invalidated { room_id: room_id.to_string() } + } + } + } +} + +#[derive(uniffi::Record)] +pub struct RequiredState { + pub key: String, + pub value: String, +} + +#[derive(uniffi::Record)] +pub struct RoomSubscription { + pub required_state: Option>, + pub timeline_limit: Option, +} + +impl From for RumaRoomSubscription { + fn from(val: RoomSubscription) -> Self { + assign!(RumaRoomSubscription::default(), { + required_state: val.required_state.map(|r| + r.into_iter().map(|s| (s.key.into(), s.value)).collect() + ).unwrap_or_default(), + timeline_limit: val.timeline_limit.map(|u| u.into()) + }) + } +} + +#[derive(uniffi::Object)] +pub struct UnreadNotificationsCount { + highlight_count: u32, + notification_count: u32, +} + +#[uniffi::export] +impl UnreadNotificationsCount { + pub fn highlight_count(&self) -> u32 { + self.highlight_count + } + pub fn notification_count(&self) -> u32 { + self.notification_count + } + pub fn has_notifications(&self) -> bool { + self.notification_count != 0 || self.highlight_count != 0 + } +} + +impl From for UnreadNotificationsCount { + fn from(inner: RumaUnreadNotificationsCount) -> Self { + UnreadNotificationsCount { + highlight_count: inner + .highlight_count + .and_then(|x| x.try_into().ok()) + .unwrap_or_default(), + notification_count: inner + .notification_count + .and_then(|x| x.try_into().ok()) + .unwrap_or_default(), + } + } +} diff --git a/bindings/matrix-sdk-ffi/src/sliding_sync.rs b/bindings/matrix-sdk-ffi/src/sliding_sync.rs deleted file mode 100644 index c640adeacd7..00000000000 --- a/bindings/matrix-sdk-ffi/src/sliding_sync.rs +++ /dev/null @@ -1,869 +0,0 @@ -use std::sync::{Arc, RwLock}; - -use anyhow::Context; -use eyeball_im::VectorDiff; -use futures_util::{future::join3, pin_mut, StreamExt}; -use matrix_sdk::{ - ruma::{ - api::client::sync::sync_events::{ - v4::{RoomSubscription as RumaRoomSubscription, SyncRequestListFilters}, - UnreadNotificationsCount as RumaUnreadNotificationsCount, - }, - assign, IdParseError, OwnedRoomId, RoomId, - }, - sliding_sync::SlidingSyncSelectiveModeBuilder as MatrixSlidingSyncSelectiveModeBuilder, - LoopCtrl, RoomListEntry as MatrixRoomEntry, SlidingSyncBuilder as MatrixSlidingSyncBuilder, - SlidingSyncListLoadingState, SlidingSyncMode, -}; -use matrix_sdk_ui::timeline::SlidingSyncRoomExt; -use tracing::{error, warn}; - -use crate::{ - client::Client, - error::ClientError, - helpers::unwrap_or_clone_arc, - room::{Room, TimelineLock}, - timeline::{EventTimelineItem, TimelineDiff, TimelineItem, TimelineListener}, - TaskHandle, RUNTIME, -}; - -#[derive(uniffi::Object)] -pub struct UnreadNotificationsCount { - highlight_count: u32, - notification_count: u32, -} - -#[uniffi::export] -impl UnreadNotificationsCount { - pub fn highlight_count(&self) -> u32 { - self.highlight_count - } - pub fn notification_count(&self) -> u32 { - self.notification_count - } - pub fn has_notifications(&self) -> bool { - self.notification_count != 0 || self.highlight_count != 0 - } -} - -impl From for UnreadNotificationsCount { - fn from(inner: RumaUnreadNotificationsCount) -> Self { - UnreadNotificationsCount { - highlight_count: inner - .highlight_count - .and_then(|x| x.try_into().ok()) - .unwrap_or_default(), - notification_count: inner - .notification_count - .and_then(|x| x.try_into().ok()) - .unwrap_or_default(), - } - } -} - -#[derive(uniffi::Error)] -pub enum SlidingSyncError { - /// The response we've received from the server can't be parsed or doesn't - /// match up with the current expectations on the client side. A - /// `sync`-restart might be required. - BadResponse { msg: String }, - /// Called `.build()` on a builder type, but the given required field was - /// missing. - BuildMissingField { msg: String }, - /// A `SlidingSyncListRequestGenerator` has been used without having been - /// initialized. It happens when a response is handled before a request has - /// been sent. It usually happens when testing. - RequestGeneratorHasNotBeenInitialized { msg: String }, - /// Ranges have a `start` bound greater than `end`. - InvalidRange { - /// Start bound. - start: u32, - /// End bound. - end: u32, - }, - /// The SlidingSync internal channel is broken. - InternalChannelIsBroken, - /// Unknown or other error. - Unknown { error: String }, -} - -impl From for SlidingSyncError { - fn from(value: matrix_sdk::sliding_sync::Error) -> Self { - use matrix_sdk::sliding_sync::Error as E; - - match value { - E::BadResponse(msg) => Self::BadResponse { msg }, - E::RequestGeneratorHasNotBeenInitialized(msg) => { - Self::RequestGeneratorHasNotBeenInitialized { msg } - } - E::InvalidRange { start, end } => Self::InvalidRange { start, end }, - E::InternalChannelIsBroken => Self::InternalChannelIsBroken, - error => Self::Unknown { error: error.to_string() }, - } - } -} - -#[derive(uniffi::Object)] -pub struct SlidingSyncRoom { - inner: matrix_sdk::SlidingSyncRoom, - sliding_sync: matrix_sdk::SlidingSync, - timeline: TimelineLock, - client: Client, -} - -#[uniffi::export] -impl SlidingSyncRoom { - pub fn name(&self) -> Option { - self.inner.name() - } - - pub fn room_id(&self) -> String { - self.inner.room_id().to_string() - } - - pub fn is_dm(&self) -> Option { - self.inner.is_dm() - } - - pub fn is_initial(&self) -> Option { - self.inner.is_initial_response() - } - - pub fn has_unread_notifications(&self) -> bool { - self.inner.has_unread_notifications() - } - - pub fn unread_notifications(&self) -> Arc { - Arc::new(self.inner.unread_notifications().into()) - } - - pub fn full_room(&self) -> Option> { - self.client - .inner - .get_room(self.inner.room_id()) - .map(|room| Arc::new(Room::with_timeline(room, self.timeline.clone()))) - } - - pub fn avatar_url(&self) -> Option { - Some(self.client.inner.get_room(self.inner.room_id())?.avatar_url()?.into()) - } - - #[allow(clippy::significant_drop_in_scrutinee)] - pub fn latest_room_message(&self) -> Option> { - let item = RUNTIME.block_on(self.inner.latest_event())?; - Some(Arc::new(EventTimelineItem(item))) - } - - pub fn add_timeline_listener( - &self, - listener: Box, - ) -> Result { - let (items, stoppable_spawn) = self.add_timeline_listener_inner(listener)?; - - Ok(SlidingSyncAddTimelineListenerResult { items, task_handle: Arc::new(stoppable_spawn) }) - } - - pub fn subscribe_to_room(&self, settings: Option) { - let room_id = self.inner.room_id().to_owned(); - - self.sliding_sync.subscribe_to_room(room_id, settings.map(Into::into)); - } - - pub fn unsubscribe_from_room(&self) { - let room_id = self.inner.room_id().to_owned(); - - self.sliding_sync.unsubscribe_from_room(room_id); - } -} - -impl SlidingSyncRoom { - fn add_timeline_listener_inner( - &self, - listener: Box, - ) -> anyhow::Result<(Vec>, TaskHandle)> { - let mut timeline_lock = RUNTIME.block_on(self.timeline.write()); - let timeline = match &*timeline_lock { - Some(timeline) => timeline, - None => { - let timeline = RUNTIME - .block_on(self.inner.timeline()) - .context("Could not set timeline listener: room not found.")?; - timeline_lock.insert(Arc::new(timeline)) - } - }; - - #[allow(unknown_lints, clippy::redundant_async_block)] // false positive - let (timeline_items, timeline_stream) = RUNTIME.block_on(timeline.subscribe()); - - let handle_events = async move { - let listener: Arc = listener.into(); - timeline_stream - .for_each(move |diff| { - let listener = listener.clone(); - let fut = RUNTIME.spawn_blocking(move || { - listener.on_update(Arc::new(TimelineDiff::new(diff))) - }); - - async move { - if let Err(e) = fut.await { - error!("Timeline listener error: {e}"); - } - } - }) - .await; - }; - - let handle_sliding_sync_reset = { - let reset_broadcast_rx = self.client.sliding_sync_reset_broadcast_tx.subscribe(); - let timeline = timeline.to_owned(); - async move { - reset_broadcast_rx.for_each(|_| timeline.clear()).await; - } - }; - - // This in the future could be removed, and the rx handling could be moved - // inside handle_sliding_sync_reset since we want to reset the sliding - // sync for ignore user list events - let handle_ignore_user_list_changes = { - let ignore_user_list_change_rx = - self.client.inner.subscribe_to_ignore_user_list_changes(); - let timeline = timeline.to_owned(); - async move { - ignore_user_list_change_rx.for_each(|_| timeline.clear()).await; - } - }; - - let items = timeline_items.into_iter().map(TimelineItem::from_arc).collect(); - let task_handle = TaskHandle::new(RUNTIME.spawn(async move { - join3(handle_events, handle_sliding_sync_reset, handle_ignore_user_list_changes).await; - })); - - Ok((items, task_handle)) - } -} - -#[derive(uniffi::Record)] -pub struct SlidingSyncAddTimelineListenerResult { - pub items: Vec>, - pub task_handle: Arc, -} - -#[derive(uniffi::Record)] -pub struct UpdateSummary { - /// The lists (according to their name), which have seen an update - pub lists: Vec, - pub rooms: Vec, -} - -#[derive(uniffi::Record)] -pub struct RequiredState { - pub key: String, - pub value: String, -} - -#[derive(uniffi::Record)] -pub struct RoomSubscription { - pub required_state: Option>, - pub timeline_limit: Option, -} - -impl From for RumaRoomSubscription { - fn from(val: RoomSubscription) -> Self { - assign!(RumaRoomSubscription::default(), { - required_state: val.required_state.map(|r| - r.into_iter().map(|s| (s.key.into(), s.value)).collect() - ).unwrap_or_default(), - timeline_limit: val.timeline_limit.map(|u| u.into()) - }) - } -} - -impl From for UpdateSummary { - fn from(other: matrix_sdk::UpdateSummary) -> Self { - Self { - lists: other.lists, - rooms: other.rooms.into_iter().map(|r| r.as_str().to_owned()).collect(), - } - } -} - -#[derive(uniffi::Enum)] -pub enum SlidingSyncListRoomsListDiff { - Append { values: Vec }, - Insert { index: u32, value: RoomListEntry }, - Set { index: u32, value: RoomListEntry }, - Remove { index: u32 }, - PushBack { value: RoomListEntry }, - PushFront { value: RoomListEntry }, - PopBack, - PopFront, - Clear, - Reset { values: Vec }, -} - -impl From> for SlidingSyncListRoomsListDiff { - fn from(other: VectorDiff) -> Self { - match other { - VectorDiff::Append { values } => { - Self::Append { values: values.into_iter().map(|e| (&e).into()).collect() } - } - VectorDiff::Insert { index, value } => { - Self::Insert { index: index as u32, value: (&value).into() } - } - VectorDiff::Set { index, value } => { - Self::Set { index: index as u32, value: (&value).into() } - } - VectorDiff::Remove { index } => Self::Remove { index: index as u32 }, - VectorDiff::PushBack { value } => Self::PushBack { value: (&value).into() }, - VectorDiff::PushFront { value } => Self::PushFront { value: (&value).into() }, - VectorDiff::PopBack => Self::PopBack, - VectorDiff::PopFront => Self::PopFront, - VectorDiff::Clear => Self::Clear, - VectorDiff::Reset { values } => { - warn!("Room list subscriber lagged behind and was reset"); - Self::Reset { values: values.into_iter().map(|e| (&e).into()).collect() } - } - } - } -} - -// Used by `SlidingSync` _and_ `RoomList`. Be careful. -#[derive(Clone, Debug, uniffi::Enum)] -pub enum RoomListEntry { - Empty, - Invalidated { room_id: String }, - Filled { room_id: String }, -} - -impl From for RoomListEntry { - fn from(value: MatrixRoomEntry) -> Self { - (&value).into() - } -} - -impl From<&MatrixRoomEntry> for RoomListEntry { - fn from(value: &MatrixRoomEntry) -> Self { - match value { - MatrixRoomEntry::Empty => Self::Empty, - MatrixRoomEntry::Filled(room_id) => Self::Filled { room_id: room_id.to_string() }, - MatrixRoomEntry::Invalidated(room_id) => { - Self::Invalidated { room_id: room_id.to_string() } - } - } - } -} - -#[uniffi::export(callback_interface)] -pub trait SlidingSyncListRoomItemsObserver: Sync + Send { - fn did_receive_update(&self); -} - -#[uniffi::export(callback_interface)] -pub trait SlidingSyncListRoomListObserver: Sync + Send { - fn did_receive_update(&self, diff: SlidingSyncListRoomsListDiff); -} - -#[uniffi::export(callback_interface)] -pub trait SlidingSyncListRoomsCountObserver: Sync + Send { - fn did_receive_update(&self, new_count: u32); -} - -#[uniffi::export(callback_interface)] -pub trait SlidingSyncListStateObserver: Sync + Send { - fn did_receive_update(&self, new_state: SlidingSyncListLoadingState); -} - -#[derive(Clone, uniffi::Object)] -pub struct SlidingSyncSelectiveModeBuilder { - inner: MatrixSlidingSyncSelectiveModeBuilder, -} - -#[uniffi::export] -impl SlidingSyncSelectiveModeBuilder { - #[uniffi::constructor] - pub fn new() -> Arc { - Arc::new(Self { inner: SlidingSyncMode::new_selective() }) - } - - pub fn add_range(self: Arc, start: u32, end_inclusive: u32) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.add_range(start..=end_inclusive); - Arc::new(builder) - } -} - -#[derive(Clone, uniffi::Object)] -pub struct SlidingSyncListBuilder { - inner: matrix_sdk::SlidingSyncListBuilder, -} - -#[derive(uniffi::Record)] -pub struct SlidingSyncRequestListFilters { - pub is_dm: Option, - pub spaces: Vec, - pub is_encrypted: Option, - pub is_invite: Option, - pub is_tombstoned: Option, - pub room_types: Vec, - pub not_room_types: Vec, - pub room_name_like: Option, - pub tags: Vec, - pub not_tags: Vec, - // pub extensions: BTreeMap, -} - -impl From for SyncRequestListFilters { - fn from(value: SlidingSyncRequestListFilters) -> Self { - let SlidingSyncRequestListFilters { - is_dm, - spaces, - is_encrypted, - is_invite, - is_tombstoned, - room_types, - not_room_types, - room_name_like, - tags, - not_tags, - } = value; - - assign!(SyncRequestListFilters::default(), { - is_dm, spaces, is_encrypted, is_invite, is_tombstoned, room_types, not_room_types, room_name_like, tags, not_tags, - }) - } -} - -#[uniffi::export] -impl SlidingSyncListBuilder { - #[uniffi::constructor] - pub fn new(name: String) -> Arc { - Arc::new(Self { inner: matrix_sdk::SlidingSyncList::builder(name) }) - } - - pub fn sync_mode_selective( - self: Arc, - selective_mode_builder: Arc, - ) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - let selective_mode_builder = unwrap_or_clone_arc(selective_mode_builder); - builder.inner = builder.inner.sync_mode(selective_mode_builder.inner); - Arc::new(builder) - } - - pub fn sync_mode_paging( - self: Arc, - batch_size: u32, - maximum_number_of_rooms_to_fetch: Option, - ) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - let mut mode_builder = SlidingSyncMode::new_paging(batch_size); - if let Some(num) = maximum_number_of_rooms_to_fetch { - mode_builder = mode_builder.maximum_number_of_rooms_to_fetch(num); - } - builder.inner = builder.inner.sync_mode(mode_builder); - Arc::new(builder) - } - - pub fn sync_mode_growing( - self: Arc, - batch_size: u32, - maximum_number_of_rooms_to_fetch: Option, - ) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - let mut mode_builder = SlidingSyncMode::new_growing(batch_size); - if let Some(num) = maximum_number_of_rooms_to_fetch { - mode_builder = mode_builder.maximum_number_of_rooms_to_fetch(num); - } - builder.inner = builder.inner.sync_mode(mode_builder); - Arc::new(builder) - } - - pub fn sort(self: Arc, sort: Vec) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.sort(sort); - Arc::new(builder) - } - - pub fn required_state(self: Arc, required_state: Vec) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder - .inner - .required_state(required_state.into_iter().map(|s| (s.key.into(), s.value)).collect()); - Arc::new(builder) - } - - pub fn filters(self: Arc, filters: SlidingSyncRequestListFilters) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.filters(Some(filters.into())); - Arc::new(builder) - } - - pub fn no_filters(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.filters(None); - Arc::new(builder) - } - - pub fn timeline_limit(self: Arc, limit: u32) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.timeline_limit(limit); - Arc::new(builder) - } - - pub fn no_timeline_limit(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.no_timeline_limit(); - Arc::new(builder) - } - - pub fn once_built(self: Arc, callback: Box) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - - builder.inner = builder.inner.once_built( - move |list: matrix_sdk::SlidingSyncList| -> matrix_sdk::SlidingSyncList { - let list = callback.update_list(Arc::new(list.into())); - - unwrap_or_clone_arc(list).inner - }, - ); - Arc::new(builder) - } - - pub fn bump_event_types(self: Arc, bump_event_types: Vec) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.bump_event_types( - bump_event_types.into_iter().map(Into::into).collect::>().as_slice(), - ); - Arc::new(builder) - } -} - -#[uniffi::export(callback_interface)] -pub trait SlidingSyncListOnceBuilt: Sync + Send { - fn update_list(&self, list: Arc) -> Arc; -} - -#[derive(Clone, uniffi::Object)] -pub struct SlidingSyncList { - inner: matrix_sdk::SlidingSyncList, -} - -impl From for SlidingSyncList { - fn from(inner: matrix_sdk::SlidingSyncList) -> Self { - SlidingSyncList { inner } - } -} - -#[uniffi::export] -impl SlidingSyncList { - pub fn observe_state( - &self, - observer: Box, - ) -> Arc { - let (_, mut state_stream) = self.inner.state_stream(); - - Arc::new(TaskHandle::new(RUNTIME.spawn(async move { - loop { - if let Some(new_state) = state_stream.next().await { - observer.did_receive_update(new_state); - } - } - }))) - } - - pub fn observe_room_list( - &self, - observer: Box, - ) -> Arc { - let (_, mut room_list_stream) = self.inner.room_list_stream(); - - Arc::new(TaskHandle::new(RUNTIME.spawn(async move { - loop { - if let Some(diff) = room_list_stream.next().await { - observer.did_receive_update(diff.into()); - } - } - }))) - } - - pub fn observe_rooms_count( - &self, - observer: Box, - ) -> Arc { - let mut rooms_count_stream = self.inner.maximum_number_of_rooms_stream(); - - Arc::new(TaskHandle::new(RUNTIME.spawn(async move { - loop { - if let Some(Some(new)) = rooms_count_stream.next().await { - observer.did_receive_update(new); - } - } - }))) - } - - /// Get the current list of rooms - pub fn current_room_list(&self) -> Vec { - self.inner.room_list() - } - - /// Total of rooms matching the filter - pub fn current_room_count(&self) -> Option { - self.inner.maximum_number_of_rooms() - } - - /// The current timeline limit - pub fn get_timeline_limit(&self) -> Option { - self.inner.timeline_limit() - } - - /// The current timeline limit - pub fn set_timeline_limit(&self, value: u32) { - self.inner.set_timeline_limit(Some(value)) - } - - /// Unset the current timeline limit - pub fn unset_timeline_limit(&self) { - self.inner.set_timeline_limit(None) - } - - /// Changes the sync mode, and automatically restarts the sliding sync - /// internally. - pub fn set_sync_mode(&self, builder: Arc) { - let builder = unwrap_or_clone_arc(builder); - self.inner.set_sync_mode(builder.inner); - } -} - -#[uniffi::export(callback_interface)] -pub trait SlidingSyncObserver: Sync + Send { - fn did_receive_sync_update(&self, summary: UpdateSummary); -} - -#[derive(uniffi::Object)] -pub struct SlidingSync { - inner: matrix_sdk::SlidingSync, - client: Client, - observer: Arc>>>, -} - -impl SlidingSync { - fn new(inner: matrix_sdk::SlidingSync, client: Client) -> Self { - Self { inner, client, observer: Default::default() } - } -} - -#[uniffi::export] -impl SlidingSync { - pub fn set_observer(&self, observer: Option>) { - *self.observer.write().unwrap() = observer; - } - - pub fn subscribe_to_room( - &self, - room_id: String, - settings: Option, - ) -> Result<(), ClientError> { - let room_id = room_id.try_into()?; - - self.inner.subscribe_to_room(room_id, settings.map(Into::into)); - - Ok(()) - } - - pub fn unsubscribe_from_room(&self, room_id: String) -> Result<(), ClientError> { - let room_id = room_id.try_into()?; - - self.inner.unsubscribe_from_room(room_id); - - Ok(()) - } - - pub fn get_room(&self, room_id: String) -> Result>, ClientError> { - let room_id = <&RoomId>::try_from(room_id.as_str())?; - - Ok(RUNTIME.block_on(async move { - self.inner.get_room(room_id).await.map(|inner| { - Arc::new(SlidingSyncRoom { - inner, - sliding_sync: self.inner.clone(), - client: self.client.clone(), - timeline: Default::default(), - }) - }) - })) - } - - pub fn get_rooms( - &self, - room_ids: Vec, - ) -> Result>>, ClientError> { - let actual_ids = room_ids - .into_iter() - .map(OwnedRoomId::try_from) - .collect::, IdParseError>>()?; - - Ok(RUNTIME.block_on(async move { - self.inner - .get_rooms(actual_ids.into_iter()) - .await - .into_iter() - .map(|o| { - o.map(|inner| { - Arc::new(SlidingSyncRoom { - inner, - sliding_sync: self.inner.clone(), - client: self.client.clone(), - timeline: Default::default(), - }) - }) - }) - .collect() - })) - } - - pub fn add_list(&self, list_builder: Arc) -> Arc { - let this = self.inner.clone(); - - Arc::new(TaskHandle::new(RUNTIME.spawn(async move { - this.add_list(unwrap_or_clone_arc(list_builder).inner).await.unwrap(); - }))) - } - - pub fn add_cached_list( - &self, - list_builder: Arc, - ) -> Result>, ClientError> { - RUNTIME.block_on(async move { - Ok(self - .inner - .add_cached_list(list_builder.inner.clone()) - .await? - .map(|inner| Arc::new(SlidingSyncList { inner }))) - }) - } - - pub fn sync(&self) -> Arc { - let inner = self.inner.clone(); - let client = self.client.clone(); - let observer = self.observer.clone(); - - Arc::new(TaskHandle::new(RUNTIME.spawn(async move { - let stream = inner.sync(); - pin_mut!(stream); - - loop { - let update_summary = match stream.next().await { - Some(Ok(update_summary)) => update_summary, - - Some(Err(error)) => { - if client.process_sync_error(error) == LoopCtrl::Break { - warn!("loop was stopped by client error processing"); - break; - } else { - continue; - } - } - - None => { - warn!("SlidingSync sync-loop ended"); - break; - } - }; - - if let Some(ref observer) = *observer.read().unwrap() { - observer.did_receive_sync_update(update_summary.into()); - } - } - }))) - } - - pub fn stop_sync(&self) -> Result<(), ClientError> { - self.inner.stop_sync().map_err(Into::into) - } -} - -#[derive(Clone, uniffi::Object)] -pub struct SlidingSyncBuilder { - inner: MatrixSlidingSyncBuilder, - client: Client, -} - -#[uniffi::export] -impl SlidingSyncBuilder { - pub fn sliding_sync_proxy(self: Arc, url: String) -> Result, ClientError> { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.sliding_sync_proxy(url.parse()?); - Ok(Arc::new(builder)) - } - - pub fn add_list(self: Arc, list_builder: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.add_list(unwrap_or_clone_arc(list_builder).inner); - Arc::new(builder) - } - - pub fn add_cached_list( - self: Arc, - list_builder: Arc, - ) -> Result, ClientError> { - let mut builder = unwrap_or_clone_arc(self); - let list_builder = unwrap_or_clone_arc(list_builder); - builder.inner = RUNTIME - .block_on(async move { builder.inner.add_cached_list(list_builder.inner).await })?; - Ok(Arc::new(builder)) - } - - pub fn without_e2ee_extension(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.without_e2ee_extension(); - Arc::new(builder) - } - - pub fn without_to_device_extension(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.without_to_device_extension(); - Arc::new(builder) - } - - pub fn without_account_data_extension(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.without_account_data_extension(); - Arc::new(builder) - } - - pub fn without_receipt_extension(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.without_receipt_extension(); - Arc::new(builder) - } - - pub fn without_typing_extension(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.without_typing_extension(); - Arc::new(builder) - } - - pub fn with_all_extensions(self: Arc) -> Arc { - let mut builder = unwrap_or_clone_arc(self); - builder.inner = builder.inner.with_all_extensions(); - Arc::new(builder) - } - - pub fn build(self: Arc) -> Result, ClientError> { - let builder = unwrap_or_clone_arc(self); - RUNTIME.block_on(async move { - Ok(Arc::new(SlidingSync::new(builder.inner.build().await?, builder.client))) - }) - } -} - -#[uniffi::export] -impl Client { - /// Creates a new Sliding Sync instance with the given identifier. - /// - /// Note: the identifier must be less than 16 chars long. - pub fn sliding_sync(&self, id: String) -> Result, ClientError> { - let inner = self.inner.sliding_sync(id)?; - Ok(Arc::new(SlidingSyncBuilder { inner, client: self.clone() })) - } -}