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
11 changes: 6 additions & 5 deletions bindings/matrix-sdk-ffi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,12 @@ mod uniffi_types {
},
timeline::{
EmoteMessageContent, EncryptedMessage, 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,
FormattedBody, ImageInfo, ImageMessageContent, InsertAtData,
LocalEventTimelineItemSendState, MembershipChange, Message, MessageFormat, MessageType,
NoticeMessageContent, OtherState, Profile, Reaction, TextMessageContent, ThumbnailInfo,
TimelineChange, TimelineDiff, TimelineItem, TimelineItemContent,
TimelineItemContentKind, UpdateAtData, VideoInfo, VideoMessageContent,
VirtualTimelineItem,
},
};
}
35 changes: 35 additions & 0 deletions bindings/matrix-sdk-ffi/src/timeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,32 @@ impl TimelineItem {
}
}

/// This type represents the “send state” of a local event timeline item.
#[derive(Clone, uniffi::Enum)]
pub enum LocalEventTimelineItemSendState {
/// The local event has not been sent yet.
NotSendYet,
/// The local event has been sent to the server, but unsuccessfully: The
/// sending has failed.
SendingFailed,
/// The local event has been sent successfully to the server.
Sent,
}

impl From<matrix_sdk::room::timeline::LocalEventTimelineItemSendState>
for LocalEventTimelineItemSendState
{
fn from(value: matrix_sdk::room::timeline::LocalEventTimelineItemSendState) -> Self {
use matrix_sdk::room::timeline::LocalEventTimelineItemSendState::*;

match value {
NotSentYet => Self::NotSendYet,
SendingFailed => Self::SendingFailed,
Sent => Self::Sent,
}
}
}

#[derive(uniffi::Object)]
pub struct EventTimelineItem(pub(crate) matrix_sdk::room::timeline::EventTimelineItem);

Expand Down Expand Up @@ -234,6 +260,15 @@ impl EventTimelineItem {
self.0.raw().map(|r| r.json().get().to_owned())
}

pub fn local_send_state(&self) -> Option<LocalEventTimelineItemSendState> {
use matrix_sdk::room::timeline::EventTimelineItem::*;

match &self.0 {
Local(local_event) => Some(local_event.send_state.into()),
Remote(_) => None,
}
}

pub fn fmt_debug(&self) -> String {
format!("{:#?}", self.0)
}
Expand Down
15 changes: 13 additions & 2 deletions crates/matrix-sdk/src/room/timeline/event_handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ use tracing::{debug, error, field::debug, info, instrument, trace, warn};
use super::{
event_item::{
AnyOtherFullStateEventContent, BundledReactions, LocalEventTimelineItem,
MemberProfileChange, OtherState, Profile, RemoteEventTimelineItem, RoomMembershipChange,
Sticker, TimelineDetails,
LocalEventTimelineItemSendState, MemberProfileChange, OtherState, Profile,
RemoteEventTimelineItem, RoomMembershipChange, Sticker, TimelineDetails,
},
find_event_by_id, find_event_by_txn_id, find_read_marker, EventTimelineItem, Message,
TimelineInnerMetadata, TimelineItem, TimelineItemContent, VirtualTimelineItem,
Expand Down Expand Up @@ -240,13 +240,16 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
#[instrument(skip_all, fields(txn_id, event_id, position))]
pub(super) fn handle_event(mut self, event_kind: TimelineEventKind) -> HandleEventResult {
let span = tracing::Span::current();

match &self.flow {
Flow::Local { txn_id, .. } => {
span.record("txn_id", debug(txn_id));
}

Flow::Remote { event_id, txn_id, position, .. } => {
span.record("event_id", debug(event_id));
span.record("position", debug(position));

if let Some(txn_id) = txn_id {
span.record("txn_id", debug(txn_id));
}
Expand Down Expand Up @@ -279,21 +282,27 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
);
}
},

TimelineEventKind::RedactedMessage => {
self.add(NewEventTimelineItem::redacted_message());
}

TimelineEventKind::Redaction { redacts, content } => {
self.handle_redaction(redacts, content);
}

TimelineEventKind::RoomMember { user_id, content, sender } => {
self.add(NewEventTimelineItem::room_member(user_id, content, sender));
}

TimelineEventKind::OtherState { state_key, content } => {
self.add(NewEventTimelineItem::other_state(state_key, content));
}

TimelineEventKind::FailedToParseMessageLike { event_type, error } => {
self.add(NewEventTimelineItem::failed_to_parse_message_like(event_type, error));
}

TimelineEventKind::FailedToParseState { event_type, state_key, error } => {
self.add(NewEventTimelineItem::failed_to_parse_state(event_type, state_key, error));
}
Expand Down Expand Up @@ -488,6 +497,7 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
}
}

/// Add a new event item in the timeline.
fn add(&mut self, item: NewEventTimelineItem) {
self.result.item_added = true;

Expand All @@ -500,6 +510,7 @@ impl<'a, 'i> TimelineEventHandler<'a, 'i> {
match &self.flow {
Flow::Local { txn_id, timestamp } => {
EventTimelineItem::Local(LocalEventTimelineItem {
send_state: LocalEventTimelineItemSendState::NotSentYet,
transaction_id: txn_id.to_owned(),
event_id: None,
sender,
Expand Down
35 changes: 32 additions & 3 deletions crates/matrix-sdk/src/room/timeline/event_item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,22 @@ impl EventTimelineItem {
}
}

#[derive(Clone, Debug)]
/// This type represents the “send state” of a local event timeline item.
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum LocalEventTimelineItemSendState {
/// The local event has not been sent yet.
NotSentYet,
/// The local event has been sent to the server, but unsuccessfully: The
/// sending has failed.
SendingFailed,
/// The local event has been sent successfully to the server.
Sent,
}

#[derive(Debug, Clone)]
pub struct LocalEventTimelineItem {
/// The send state of this local event.
pub send_state: LocalEventTimelineItemSendState,
/// The transaction ID.
pub transaction_id: OwnedTransactionId,
/// The event ID received from the server in the event-sending response.
Expand All @@ -198,8 +212,23 @@ pub struct LocalEventTimelineItem {

impl LocalEventTimelineItem {
/// Clone the current event item, and update its `event_id`.
pub(super) fn with_event_id(&self, event_id: OwnedEventId) -> Self {
Self { event_id: Some(event_id), ..self.clone() }
///
/// `event_id` is optional:
/// * `Some(_)` means the local event has been sent successfully to the
/// server, its send state will be moved to
/// [`LocalEventTimelineItemSendState::Sent`].
/// * `None` means the local event has been failed to be sent to the
/// server, its send state will be moved to
/// [`LocalEventTimelineItemSendState::SendingFailed`].
pub(super) fn with_event_id(&self, event_id: Option<OwnedEventId>) -> Self {
Self {
send_state: match &event_id {
Some(_) => LocalEventTimelineItemSendState::Sent,
None => LocalEventTimelineItemSendState::SendingFailed,
},
event_id,
..self.clone()
}
}
}

Expand Down
87 changes: 64 additions & 23 deletions crates/matrix-sdk/src/room/timeline/inner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use matrix_sdk_base::{
locks::Mutex,
};
use ruma::{
api::client::message::send_message_event::v3::Response as SendMessageEventResponse,
events::{
fully_read::FullyReadEvent, relation::Annotation, AnyMessageLikeEventContent,
AnySyncTimelineEvent,
Expand Down Expand Up @@ -109,6 +110,7 @@ impl<P: ProfileProvider> TimelineInner<P> {
.await;
}

/// Handle the creation of a new local event.
pub(super) async fn handle_local_event(
&self,
txn_id: OwnedTransactionId,
Expand All @@ -134,29 +136,45 @@ impl<P: ProfileProvider> TimelineInner<P> {
.handle_event(kind);
}

/// Handle a back-paginated event.
///
/// Returns the number of timeline updates that were made.
pub(super) async fn handle_back_paginated_event(
/// Handle the response returned by the server when a local event has been
/// sent.
pub(super) fn handle_local_event_send_response(
&self,
event: TimelineEvent,
) -> HandleEventResult {
let mut metadata_lock = self.metadata.lock().await;
handle_remote_event(
event.event.cast(),
event.encryption_info,
TimelineItemPosition::Start,
&self.items,
&mut metadata_lock,
&self.profile_provider,
)
.await
txn_id: &TransactionId,
response: crate::error::Result<SendMessageEventResponse>,
) -> crate::error::Result<()> {
match response {
Ok(response) => {
self.update_event_id_of_local_event(txn_id, Some(response.event_id));

Ok(())
}
Err(error) => {
self.update_event_id_of_local_event(txn_id, None);

Err(error)
}
}
}

/// Update the transaction ID by an event ID.
pub(super) fn add_event_id(&self, txn_id: &TransactionId, event_id: OwnedEventId) {
/// Update the event ID of a local event represented by a transaction ID.
///
/// If the event ID is `None`, it means there is no event ID returned by the
/// server, so the sending has failed. If the event ID is `Some(_)`, it
/// means the sending has been successful.
///
/// If no local event is found, a warning is raised.
pub(super) fn update_event_id_of_local_event(
&self,
txn_id: &TransactionId,
event_id: Option<OwnedEventId>,
) {
let mut lock = self.items.lock_mut();

// Look for the local event by the transaction ID.
if let Some((idx, local_event_item)) = find_event_by_txn_id(&lock, txn_id) {
// An event ID already exists, that's a broken state, let's emit an error but
// also override to the given event ID.
if let Some(existing_event_id) = &local_event_item.event_id {
error!(
?existing_event_id, new_event_id = ?event_id, ?txn_id,
Expand All @@ -168,12 +186,35 @@ impl<P: ProfileProvider> TimelineInner<P> {
idx,
Arc::new(TimelineItem::Event(local_event_item.with_event_id(event_id).into())),
);
} else if find_event_by_id(&lock, &event_id).is_none() {
// Event isn't found by transaction ID, and also not by event ID
// (which it would if the remote echo comes in before the send-event
// response)
warn!(?txn_id, "Timeline item not found, can't add event ID");
}
// No local event has been found.
else if let Some(event_id) = event_id {
if find_event_by_id(&lock, &event_id).is_none() {
// Event isn't found by transaction ID, and also not by event ID
// (which it would if the remote echo comes in before the send-event
// response)
warn!(?txn_id, "Timeline item not found, can't add event ID");
}
}
}

/// Handle a back-paginated event.
///
/// Returns the number of timeline updates that were made.
pub(super) async fn handle_back_paginated_event(
&self,
event: TimelineEvent,
) -> HandleEventResult {
let mut metadata_lock = self.metadata.lock().await;
handle_remote_event(
event.event.cast(),
event.encryption_info,
TimelineItemPosition::Start,
&self.items,
&mut metadata_lock,
&self.profile_provider,
)
.await
}

#[instrument(skip_all)]
Expand Down
13 changes: 6 additions & 7 deletions crates/matrix-sdk/src/room/timeline/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ use self::{
};
pub use self::{
event_item::{
AnyOtherFullStateEventContent, EncryptedMessage, EventTimelineItem, MemberProfileChange,
MembershipChange, Message, OtherState, Profile, ReactionDetails, RoomMembershipChange,
Sticker, TimelineDetails, TimelineItemContent,
AnyOtherFullStateEventContent, EncryptedMessage, EventTimelineItem,
LocalEventTimelineItemSendState, MemberProfileChange, MembershipChange, Message,
OtherState, Profile, ReactionDetails, RoomMembershipChange, Sticker, TimelineDetails,
TimelineItemContent,
},
pagination::{PaginationOptions, PaginationOutcome},
virtual_item::VirtualTimelineItem,
Expand Down Expand Up @@ -371,10 +372,8 @@ impl Timeline {
// Not ideal, but works for now.
let room = Joined { inner: self.room().clone() };

let response = room.send(content, Some(&txn_id)).await?;
self.inner.add_event_id(&txn_id, response.event_id);

Ok(())
let response = room.send(content, Some(&txn_id)).await;
self.inner.handle_local_event_send_response(&txn_id, response)
}
}

Expand Down
Loading