ui: Add Timeline::retry_send

This commit is contained in:
Jonas Platte
2023-06-06 17:10:20 +02:00
committed by Jonas Platte
parent f1e62b0bb8
commit df7beb4afd
3 changed files with 100 additions and 0 deletions

View File

@@ -18,11 +18,13 @@ use imbl::{vector, Vector};
use indexmap::IndexMap;
use matrix_sdk::{deserialized_responses::TimelineEvent, Result};
use ruma::{
assign,
events::{
policy::rule::{
room::PolicyRuleRoomEventContent, server::PolicyRuleServerEventContent,
user::PolicyRuleUserEventContent,
},
relation::InReplyTo,
room::{
aliases::RoomAliasesEventContent,
avatar::RoomAvatarEventContent,
@@ -232,6 +234,15 @@ impl Message {
}
}
impl From<Message> for RoomMessageEventContent {
fn from(msg: Message) -> Self {
let relates_to = msg.in_reply_to.map(|details| message::Relation::Reply {
in_reply_to: InReplyTo::new(details.event_id),
});
assign!(Self::new(msg.msgtype), { relates_to })
}
}
#[cfg(not(tarpaulin_include))]
impl fmt::Debug for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@@ -321,6 +321,30 @@ impl<P: RoomDataProvider> TimelineInner<P> {
state.items.set(idx, Arc::new(new_item));
}
pub(super) async fn prepare_retry(
&self,
txn_id: &TransactionId,
) -> Option<TimelineItemContent> {
let mut state = self.state.lock().await;
let (idx, item) = rfind_event_item(&state.items, |it| it.transaction_id() == Some(txn_id))?;
let local_item = item.as_local()?;
if !matches!(&local_item.send_state, EventSendState::SendingFailed { .. }) {
debug!("Attempted to retry sending of an item that is not in failed state");
return None;
}
let new_item = TimelineItem::Event(
item.with_kind(local_item.with_send_state(EventSendState::NotSentYet)),
);
let content = item.content.clone();
state.items.set(idx, Arc::new(new_item));
Some(content)
}
/// Handle a back-paginated event.
///
/// Returns the number of timeline updates that were made.

View File

@@ -334,6 +334,67 @@ impl Timeline {
Ok(())
}
/// Retry sending a message that previously failed to send.
///
/// # Arguments
///
/// * `txn_id` - The transaction ID of a local echo timeline item that has a
/// `send_state()` of `SendState::FailedToSend { .. }`
pub async fn retry_send(&self, txn_id: &TransactionId) -> Result<(), Error> {
macro_rules! error_return {
($msg:literal) => {{
error!($msg);
return Ok(());
}};
}
let item = self.inner.prepare_retry(txn_id).await.ok_or(Error::RetryEventNotInTimeline)?;
let content = match item {
TimelineItemContent::Message(msg) => {
AnyMessageLikeEventContent::RoomMessage(msg.into())
}
TimelineItemContent::RedactedMessage => {
error_return!("Invalid state: attempting to retry a redacted message");
}
TimelineItemContent::Sticker(sticker) => {
AnyMessageLikeEventContent::Sticker(sticker.content)
}
TimelineItemContent::UnableToDecrypt(_) => {
error_return!("Invalid state: attempting to retry a UTD item");
}
TimelineItemContent::MembershipChange(_)
| TimelineItemContent::ProfileChange(_)
| TimelineItemContent::OtherState(_) => {
error_return!("Retrying state events is not currently supported");
}
TimelineItemContent::FailedToParseMessageLike { .. }
| TimelineItemContent::FailedToParseState { .. } => {
error_return!("Invalid state: attempting to retry a failed-to-parse item");
}
};
let send_state = match Room::from(self.room().clone()) {
Room::Joined(room) => {
let response = room.send(content, Some(txn_id)).await;
match response {
Ok(response) => EventSendState::Sent { event_id: response.event_id },
Err(error) => EventSendState::SendingFailed { error: Arc::new(error) },
}
}
_ => {
EventSendState::SendingFailed {
// FIXME: Probably not exactly right
error: Arc::new(matrix_sdk::Error::InconsistentState),
}
}
};
self.inner.update_event_send_state(txn_id, send_state).await;
Ok(())
}
/// Fetch unavailable details about the event with the given ID.
///
/// This method only works for IDs of remote [`EventTimelineItem`]s,
@@ -629,6 +690,10 @@ pub enum Error {
#[error("Event with remote echo not found in timeline")]
RemoteEventNotInTimeline,
/// Can't find an event with the given transaction ID, can't retry.
#[error("Event not found, can't retry sending")]
RetryEventNotInTimeline,
/// The event is currently unsupported for this use case.
#[error("Unsupported event")]
UnsupportedEvent,