Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions bindings/matrix-sdk-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,10 @@ mod uniffi_types {
EmoteMessageContent, EncryptedMessage, EventSendState, EventTimelineItem, FileInfo,
FileMessageContent, FormattedBody, ImageInfo, ImageMessageContent, InsertAtData,
MembershipChange, Message, MessageFormat, MessageType, NoticeMessageContent,
OtherState, Profile, Reaction, TextMessageContent, ThumbnailInfo, TimelineChange,
TimelineDiff, TimelineItem, TimelineItemContent, TimelineItemContentKind, UpdateAtData,
VideoInfo, VideoMessageContent, VirtualTimelineItem,
OtherState, ProfileTimelineDetails, Reaction, TextMessageContent, ThumbnailInfo,
TimelineChange, TimelineDiff, TimelineItem, TimelineItemContent,
TimelineItemContentKind, UpdateAtData, VideoInfo, VideoMessageContent,
VirtualTimelineItem,
},
};
}
14 changes: 14 additions & 0 deletions bindings/matrix-sdk-ffi/src/room.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,20 @@ impl Room {
timeline.retry_decryption(&session_ids).await;
});
}

pub fn fetch_members(&self) {
let timeline = match &*self.timeline.read().unwrap() {
Some(t) => Arc::clone(t),
None => {
error!("Timeline not set up, can't fetch members");
return;
}
};

RUNTIME.spawn(async move {
timeline.fetch_members().await;
});
}
}

impl Room {
Expand Down
35 changes: 21 additions & 14 deletions bindings/matrix-sdk-ffi/src/timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use std::sync::Arc;

use extension_trait::extension_trait;
use futures_signals::signal_vec::VecDiff;
use matrix_sdk::room::timeline::{Profile, TimelineDetails};
pub use matrix_sdk::ruma::events::room::{message::RoomMessageEventContent, MediaSource};

#[uniffi::export]
Expand Down Expand Up @@ -223,7 +224,7 @@ impl EventTimelineItem {
self.0.sender().to_string()
}

pub fn sender_profile(&self) -> Profile {
pub fn sender_profile(&self) -> ProfileTimelineDetails {
self.0.sender_profile().into()
}

Expand Down Expand Up @@ -279,19 +280,25 @@ impl EventTimelineItem {
}
}

#[derive(uniffi::Record)]
pub struct Profile {
display_name: Option<String>,
display_name_ambiguous: bool,
avatar_url: Option<String>,
}

impl From<&matrix_sdk::room::timeline::Profile> for Profile {
fn from(p: &matrix_sdk::room::timeline::Profile) -> Self {
Self {
display_name: p.display_name.clone(),
display_name_ambiguous: p.display_name_ambiguous,
avatar_url: p.avatar_url.as_ref().map(ToString::to_string),
#[derive(uniffi::Enum)]
pub enum ProfileTimelineDetails {
Unavailable,
Pending,
Ready { display_name: Option<String>, display_name_ambiguous: bool, avatar_url: Option<String> },
Error { message: String },
}

impl From<&TimelineDetails<Profile>> for ProfileTimelineDetails {
fn from(details: &TimelineDetails<Profile>) -> Self {
match details {
TimelineDetails::Unavailable => Self::Unavailable,
TimelineDetails::Pending => Self::Pending,
TimelineDetails::Ready(profile) => Self::Ready {
display_name: profile.display_name.clone(),
display_name_ambiguous: profile.display_name_ambiguous,
avatar_url: profile.avatar_url.as_ref().map(ToString::to_string),
},
TimelineDetails::Error(e) => Self::Error { message: e.to_string() },
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/matrix-sdk/src/room/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ impl Common {
}
}

async fn ensure_members(&self) -> Result<()> {
pub(crate) async fn ensure_members(&self) -> Result<()> {
if !self.are_events_visible() {
return Ok(());
}
Expand Down
71 changes: 33 additions & 38 deletions crates/matrix-sdk/src/room/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ use super::{
Sticker,
},
find_read_marker, rfind_event_by_id, rfind_event_item, EventTimelineItem, InReplyToDetails,
Message, ReactionGroup, TimelineInnerMetadata, TimelineItem, TimelineItemContent,
VirtualTimelineItem,
Message, ReactionGroup, TimelineDetails, TimelineInnerMetadata, TimelineItem,
TimelineItemContent, VirtualTimelineItem,
};
use crate::{events::SyncTimelineEventWithoutContent, room::timeline::MembershipChange};

Expand All @@ -68,7 +68,7 @@ pub(super) enum Flow {

pub(super) struct TimelineEventMetadata {
pub(super) sender: OwnedUserId,
pub(super) sender_profile: Profile,
pub(super) sender_profile: Option<Profile>,
pub(super) is_own_event: bool,
pub(super) relations: BundledRelations,
pub(super) encryption_info: Option<EncryptionInfo>,
Expand Down Expand Up @@ -525,43 +525,38 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
self.result.item_added = true;

let NewEventTimelineItem { content } = item;

let item = {
let sender = self.meta.sender.to_owned();
let sender_profile = self.meta.sender_profile.clone();
let mut reactions = self.pending_reactions().unwrap_or_default();

match &self.flow {
Flow::Local { txn_id, timestamp } => {
EventTimelineItem::Local(LocalEventTimelineItem {
send_state: EventSendState::NotSentYet,
transaction_id: txn_id.to_owned(),
sender,
sender_profile,
timestamp: *timestamp,
content,
})
}
Flow::Remote { event_id, origin_server_ts, raw_event, .. } => {
// Drop pending reactions if the message is redacted.
if let TimelineItemContent::RedactedMessage = content {
if !reactions.is_empty() {
reactions = BundledReactions::default();
}
let sender = self.meta.sender.to_owned();
let sender_profile = TimelineDetails::from_initial_value(self.meta.sender_profile.clone());
let mut reactions = self.pending_reactions().unwrap_or_default();

let item = match &self.flow {
Flow::Local { txn_id, timestamp } => EventTimelineItem::Local(LocalEventTimelineItem {
send_state: EventSendState::NotSentYet,
transaction_id: txn_id.to_owned(),
sender,
sender_profile,
timestamp: *timestamp,
content,
}),
Flow::Remote { event_id, origin_server_ts, raw_event, .. } => {
// Drop pending reactions if the message is redacted.
if let TimelineItemContent::RedactedMessage = content {
if !reactions.is_empty() {
reactions = BundledReactions::default();
}

EventTimelineItem::Remote(RemoteEventTimelineItem {
event_id: event_id.clone(),
sender,
sender_profile,
timestamp: *origin_server_ts,
content,
reactions,
is_own: self.meta.is_own_event,
encryption_info: self.meta.encryption_info.clone(),
raw: raw_event.clone(),
})
}

EventTimelineItem::Remote(RemoteEventTimelineItem {
event_id: event_id.clone(),
sender,
sender_profile,
timestamp: *origin_server_ts,
content,
reactions,
is_own: self.meta.is_own_event,
encryption_info: self.meta.encryption_info.clone(),
raw: raw_event.clone(),
})
}
};

Expand Down
47 changes: 40 additions & 7 deletions crates/matrix-sdk/src/room/timeline/event_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ impl EventTimelineItem {
}

/// Get the profile of the sender.
pub fn sender_profile(&self) -> &Profile {
pub fn sender_profile(&self) -> &TimelineDetails<Profile> {
match self {
Self::Local(local_event) => &local_event.sender_profile,
Self::Remote(remote_event) => &remote_event.sender_profile,
Expand Down Expand Up @@ -209,6 +209,18 @@ impl EventTimelineItem {
}
}
}

/// Clone the current event item, and update its `sender_profile`.
pub(super) fn with_sender_profile(&self, sender_profile: TimelineDetails<Profile>) -> Self {
match self {
EventTimelineItem::Local(item) => {
Self::Local(LocalEventTimelineItem { sender_profile, ..item.clone() })
}
EventTimelineItem::Remote(item) => {
Self::Remote(RemoteEventTimelineItem { sender_profile, ..item.clone() })
}
}
}
}

/// This type represents the "send state" of a local event timeline item.
Expand Down Expand Up @@ -238,7 +250,7 @@ pub struct LocalEventTimelineItem {
/// The sender of the event.
pub sender: OwnedUserId,
/// The sender's profile of the event.
pub sender_profile: Profile,
pub sender_profile: TimelineDetails<Profile>,
/// The timestamp of the event.
pub timestamp: MilliSecondsSinceUnixEpoch,
/// The content of the event.
Expand Down Expand Up @@ -275,7 +287,7 @@ pub struct RemoteEventTimelineItem {
/// The sender of the event.
pub sender: OwnedUserId,
/// The sender's profile of the event.
pub sender_profile: Profile,
pub sender_profile: TimelineDetails<Profile>,
/// The timestamp of the event.
pub timestamp: MilliSecondsSinceUnixEpoch,
/// The content of the event.
Expand Down Expand Up @@ -342,7 +354,7 @@ impl fmt::Debug for RemoteEventTimelineItem {
}

/// The display name and avatar URL of a room member.
#[derive(Clone, Debug)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Profile {
/// The display name, if set.
pub display_name: Option<String>,
Expand Down Expand Up @@ -375,6 +387,26 @@ pub enum TimelineDetails<T> {
Error(Arc<Error>),
}

impl<T> TimelineDetails<T> {
pub(crate) fn from_initial_value(value: Option<T>) -> Self {
match value {
Some(v) => Self::Ready(v),
None => Self::Unavailable,
}
}

pub(crate) fn is_unavailable(&self) -> bool {
matches!(self, Self::Unavailable)
}

pub(crate) fn contains<U>(&self, value: &U) -> bool
where
T: PartialEq<U>,
{
matches!(self, Self::Ready(v) if v == value)
}
}

/// The content of an [`EventTimelineItem`].
#[derive(Clone, Debug)]
pub enum TimelineItemContent {
Expand Down Expand Up @@ -518,7 +550,7 @@ impl InReplyToDetails {
pub struct RepliedToEvent {
pub(super) message: Message,
pub(super) sender: OwnedUserId,
pub(super) sender_profile: Profile,
pub(super) sender_profile: TimelineDetails<Profile>,
}

impl RepliedToEvent {
Expand All @@ -533,7 +565,7 @@ impl RepliedToEvent {
}

/// Get the profile of the sender.
pub fn sender_profile(&self) -> &Profile {
pub fn sender_profile(&self) -> &TimelineDetails<Profile> {
&self.sender_profile
}

Expand All @@ -558,7 +590,8 @@ impl RepliedToEvent {
edited: event.relations().replace.is_some(),
};
let sender = event.sender().to_owned();
let sender_profile = profile_provider.profile(&sender).await;
let sender_profile =
TimelineDetails::from_initial_value(profile_provider.profile(&sender).await);

Ok(Self { message, sender, sender_profile })
}
Expand Down
Loading