diff --git a/bindings/matrix-sdk-ffi/src/error.rs b/bindings/matrix-sdk-ffi/src/error.rs index 5ce1bf48e33..9f630a767c1 100644 --- a/bindings/matrix-sdk-ffi/src/error.rs +++ b/bindings/matrix-sdk-ffi/src/error.rs @@ -9,7 +9,7 @@ use matrix_sdk::{ use matrix_sdk_ui::{encryption_sync_service, notification_client, sync_service, timeline}; use uniffi::UnexpectedUniFFICallbackError; -use crate::room_list::RoomListError; +use crate::{room_list::RoomListError, timeline::FocusEventError}; #[derive(Debug, thiserror::Error)] pub enum ClientError { @@ -161,6 +161,12 @@ impl From for ClientError { } } +impl From for ClientError { + fn from(e: FocusEventError) -> Self { + Self::new(e) + } +} + /// Bindings version of the sdk type replacing OwnedUserId/DeviceIds with simple /// String. /// diff --git a/bindings/matrix-sdk-ffi/src/room.rs b/bindings/matrix-sdk-ffi/src/room.rs index 62f7b678021..08e9fdbee02 100644 --- a/bindings/matrix-sdk-ffi/src/room.rs +++ b/bindings/matrix-sdk-ffi/src/room.rs @@ -4,14 +4,13 @@ use anyhow::{Context, Result}; use futures_util::{pin_mut, StreamExt}; use matrix_sdk::{ crypto::LocalTrust, - event_cache::paginator::PaginatorError, room::{ edit::EditedContent, power_levels::RoomPowerLevelChanges, Room as SdkRoom, RoomMemberRole, }, ComposerDraft as SdkComposerDraft, ComposerDraftType as SdkComposerDraftType, RoomHero as SdkRoomHero, RoomMemberships, RoomState, }; -use matrix_sdk_ui::timeline::{default_event_filter, PaginationError, RoomExt, TimelineFocus}; +use matrix_sdk_ui::timeline::{default_event_filter, RoomExt}; use mime::Mime; use ruma::{ api::client::room::report_content, @@ -36,12 +35,15 @@ use crate::{ chunk_iterator::ChunkIterator, client::{JoinRule, RoomVisibility}, error::{ClientError, MediaInfoError, NotYetImplemented, RoomError}, - event::{MessageLikeEventType, RoomMessageEventMessageType, StateEventType}, + event::{MessageLikeEventType, StateEventType}, identity_status_change::IdentityStatusChange, room_info::RoomInfo, room_member::RoomMember, ruma::{ImageInfo, Mentions, NotifyType}, - timeline::{DateDividerMode, FocusEventError, ReceiptType, SendHandle, Timeline}, + timeline::{ + configuration::{AllowedMessageTypes, TimelineConfiguration}, + ReceiptType, SendHandle, Timeline, + }, utils::u64_to_uint, TaskHandle, }; @@ -87,10 +89,6 @@ impl Room { #[matrix_sdk_ffi_macros::export] impl Room { - pub fn id(&self) -> String { - self.inner.room_id().to_string() - } - /// Returns the room's name from the state event if available, otherwise /// compute a room name based on the room's nature (DM or not) and number of /// members. @@ -200,115 +198,44 @@ impl Room { } } - /// Returns a timeline focused on the given event. - /// - /// Note: this timeline is independent from that returned with - /// [`Self::timeline`], and as such it is not cached. - pub async fn timeline_focused_on_event( - &self, - event_id: String, - num_context_events: u16, - internal_id_prefix: Option, - ) -> Result, FocusEventError> { - let parsed_event_id = EventId::parse(&event_id).map_err(|err| { - FocusEventError::InvalidEventId { event_id: event_id.clone(), err: err.to_string() } - })?; - - let room = &self.inner; - - let mut builder = matrix_sdk_ui::timeline::Timeline::builder(room); - - if let Some(internal_id_prefix) = internal_id_prefix { - builder = builder.with_internal_id_prefix(internal_id_prefix); - } - - let timeline = match builder - .with_focus(TimelineFocus::Event { target: parsed_event_id, num_context_events }) - .build() - .await - { - Ok(t) => t, - Err(err) => { - if let matrix_sdk_ui::timeline::Error::PaginationError( - PaginationError::Paginator(PaginatorError::EventNotFound(..)), - ) = err - { - return Err(FocusEventError::EventNotFound { event_id: event_id.to_string() }); - } - return Err(FocusEventError::Other { msg: err.to_string() }); - } - }; - - Ok(Timeline::new(timeline)) - } - - pub async fn pinned_events_timeline( + /// Build a new timeline instance with the given configuration. + pub async fn timeline_with_configuration( &self, - internal_id_prefix: Option, - max_events_to_load: u16, - max_concurrent_requests: u16, + configuration: TimelineConfiguration, ) -> Result, ClientError> { - let room = &self.inner; - - let mut builder = matrix_sdk_ui::timeline::Timeline::builder(room); + let mut builder = matrix_sdk_ui::timeline::Timeline::builder(&self.inner); - if let Some(internal_id_prefix) = internal_id_prefix { - builder = builder.with_internal_id_prefix(internal_id_prefix); + builder = builder.with_focus(configuration.focus.try_into()?); + + if let AllowedMessageTypes::Only { types } = configuration.allowed_message_types { + builder = builder.event_filter(move |event, room_version_id| { + default_event_filter(event, room_version_id) + && match event { + AnySyncTimelineEvent::MessageLike(msg) => match msg.original_content() { + Some(AnyMessageLikeEventContent::RoomMessage(content)) => { + types.contains(&content.msgtype.into()) + } + _ => false, + }, + _ => false, + } + }); } - let timeline = builder - .with_focus(TimelineFocus::PinnedEvents { max_events_to_load, max_concurrent_requests }) - .build() - .await?; - - Ok(Timeline::new(timeline)) - } - - /// A timeline instance that can be configured to only include RoomMessage - /// type events and filter those further based on their message type. - /// - /// Virtual timeline items will still be provided and the - /// `default_event_filter` will be applied before everything else. - /// - /// # Arguments - /// - /// * `internal_id_prefix` - An optional String that will be prepended to - /// all the timeline item's internal IDs, making it possible to - /// distinguish different timeline instances from each other. - /// - /// * `allowed_message_types` - A list of `RoomMessageEventMessageType` that - /// will be allowed to appear in the timeline - pub async fn message_filtered_timeline( - &self, - internal_id_prefix: Option, - allowed_message_types: Vec, - date_divider_mode: DateDividerMode, - ) -> Result, ClientError> { - let mut builder = matrix_sdk_ui::timeline::Timeline::builder(&self.inner); - - if let Some(internal_id_prefix) = internal_id_prefix { + if let Some(internal_id_prefix) = configuration.internal_id_prefix { builder = builder.with_internal_id_prefix(internal_id_prefix); } - builder = builder.with_date_divider_mode(date_divider_mode.into()); - - builder = builder.event_filter(move |event, room_version_id| { - default_event_filter(event, room_version_id) - && match event { - AnySyncTimelineEvent::MessageLike(msg) => match msg.original_content() { - Some(AnyMessageLikeEventContent::RoomMessage(content)) => { - allowed_message_types.contains(&content.msgtype.into()) - } - _ => false, - }, - _ => false, - } - }); + builder = builder.with_date_divider_mode(configuration.date_divider_mode.into()); let timeline = builder.build().await?; Ok(Timeline::new(timeline)) } + pub fn id(&self) -> String { + self.inner.room_id().to_string() + } + pub fn is_encrypted(&self) -> Result { Ok(RUNTIME.block_on(self.inner.is_encrypted())?) } diff --git a/bindings/matrix-sdk-ffi/src/timeline/configuration.rs b/bindings/matrix-sdk-ffi/src/timeline/configuration.rs new file mode 100644 index 00000000000..9b20f99e960 --- /dev/null +++ b/bindings/matrix-sdk-ffi/src/timeline/configuration.rs @@ -0,0 +1,85 @@ +use ruma::EventId; + +use super::FocusEventError; +use crate::{error::ClientError, event::RoomMessageEventMessageType}; + +#[derive(uniffi::Enum)] +pub enum TimelineFocus { + Live, + Event { event_id: String, num_context_events: u16 }, + PinnedEvents { max_events_to_load: u16, max_concurrent_requests: u16 }, +} + +impl TryFrom for matrix_sdk_ui::timeline::TimelineFocus { + type Error = ClientError; + + fn try_from( + value: TimelineFocus, + ) -> Result { + match value { + TimelineFocus::Live => Ok(Self::Live), + TimelineFocus::Event { event_id, num_context_events } => { + let parsed_event_id = + EventId::parse(&event_id).map_err(|err| FocusEventError::InvalidEventId { + event_id: event_id.clone(), + err: err.to_string(), + })?; + + Ok(Self::Event { target: parsed_event_id, num_context_events }) + } + TimelineFocus::PinnedEvents { max_events_to_load, max_concurrent_requests } => { + Ok(Self::PinnedEvents { max_events_to_load, max_concurrent_requests }) + } + } + } +} + +/// Changes how date dividers get inserted, either in between each day or in +/// between each month +#[derive(uniffi::Enum)] +pub enum DateDividerMode { + Daily, + Monthly, +} + +impl From for matrix_sdk_ui::timeline::DateDividerMode { + fn from(value: DateDividerMode) -> Self { + match value { + DateDividerMode::Daily => Self::Daily, + DateDividerMode::Monthly => Self::Monthly, + } + } +} + +#[derive(uniffi::Enum)] +pub enum AllowedMessageTypes { + All, + Only { types: Vec }, +} + +/// Various options used to configure the timeline's behavior. +/// +/// # Arguments +/// +/// * `internal_id_prefix` - +/// +/// * `allowed_message_types` - +/// +/// * `date_divider_mode` - +#[derive(uniffi::Record)] +pub struct TimelineConfiguration { + /// What should the timeline focus on? + pub focus: TimelineFocus, + + /// A list of [`RoomMessageEventMessageType`] that will be allowed to appear + /// in the timeline + pub allowed_message_types: AllowedMessageTypes, + + /// An optional String that will be prepended to + /// all the timeline item's internal IDs, making it possible to + /// distinguish different timeline instances from each other. + pub internal_id_prefix: Option, + + /// How often to insert date dividers + pub date_divider_mode: DateDividerMode, +} diff --git a/bindings/matrix-sdk-ffi/src/timeline/mod.rs b/bindings/matrix-sdk-ffi/src/timeline/mod.rs index 3520ce64c8a..3635e81cef2 100644 --- a/bindings/matrix-sdk-ffi/src/timeline/mod.rs +++ b/bindings/matrix-sdk-ffi/src/timeline/mod.rs @@ -79,6 +79,7 @@ use crate::{ RUNTIME, }; +pub mod configuration; mod content; pub use content::MessageContent; @@ -1322,20 +1323,3 @@ impl LazyTimelineItemProvider { self.0.local_echo_send_handle().map(|handle| Arc::new(SendHandle::new(handle))) } } - -/// Changes how date dividers get inserted, either in between each day or in -/// between each month -#[derive(Debug, Clone, uniffi::Enum)] -pub enum DateDividerMode { - Daily, - Monthly, -} - -impl From for matrix_sdk_ui::timeline::DateDividerMode { - fn from(value: DateDividerMode) -> Self { - match value { - DateDividerMode::Daily => Self::Daily, - DateDividerMode::Monthly => Self::Monthly, - } - } -}