diff --git a/src/session/view/content/room_history/message_toolbar/composer_state.rs b/src/session/view/content/room_history/message_toolbar/composer_state.rs index a9c0d72c..9c8507d4 100644 --- a/src/session/view/content/room_history/message_toolbar/composer_state.rs +++ b/src/session/view/content/room_history/message_toolbar/composer_state.rs @@ -5,7 +5,7 @@ use gtk::{ subclass::prelude::*, }; use matrix_sdk::{deserialized_responses::TimelineEvent, ComposerDraft, ComposerDraftType}; -use matrix_sdk_ui::timeline::Message; +use matrix_sdk_ui::timeline::TimelineItemContent; use ruma::{ events::{ room::message::{MessageFormat, MessageType, OriginalSyncRoomMessageEvent}, @@ -340,11 +340,27 @@ mod imp { } /// Update the buffer for the given edit source. - pub(super) fn set_edit_source(&self, event_id: OwnedEventId, message: &Message) { + pub(super) fn set_edit_source(&self, message_event: &MessageEventSource) { let Some(room) = self.room.upgrade() else { return; }; + let MessageEventSource::Event(event) = message_event else { + warn!("Unsupported event type for edit"); + return; + }; + + let item = event.item(); + + let TimelineItemContent::MsgLike(msg_like) = item.content() else { + warn!("Unsupported event type for edit"); + return; + }; + let Some(message) = msg_like.as_message() else { + warn!("Unsupported event type for edit"); + return; + }; + // We don't support editing non-text messages. let (text, formatted) = match message.msgtype() { MessageType::Emote(emote) => { @@ -354,7 +370,7 @@ mod imp { _ => return, }; - self.set_related_to(Some(RelationInfo::Edit(event_id))); + self.set_related_to(Some(RelationInfo::Edit(message_event.clone()))); // Try to detect rich mentions. let mut mentions = if let Some(html) = @@ -484,8 +500,8 @@ impl ComposerState { } /// Update the buffer for the given edit source. - pub(crate) fn set_edit_source(&self, event_id: OwnedEventId, message: &Message) { - self.imp().set_edit_source(event_id, message); + pub(crate) fn set_edit_source(&self, message_event: &MessageEventSource) { + self.imp().set_edit_source(message_event); } /// Add the given widget at the position of the given iter to this state. @@ -519,16 +535,16 @@ pub(crate) enum RelationInfo { /// Send a reply to the given event. Reply(MessageEventSource), - /// Send an edit to the event with the given ID. - Edit(OwnedEventId), + /// Send an edit to the given event. + Edit(MessageEventSource), } impl RelationInfo { /// Construct a relation info from a composer draft, if possible. pub(crate) async fn from_draft(room: &Room, draft_type: ComposerDraftType) -> Option { - match draft_type { + match &draft_type { ComposerDraftType::NewMessage => None, - ComposerDraftType::Reply { event_id } => { + ComposerDraftType::Reply { event_id } | ComposerDraftType::Edit { event_id } => { // We need to fetch the event and extract its content, so we can display it. let matrix_room = room.matrix_room().clone(); let event_id_clone = event_id.clone(); @@ -551,17 +567,21 @@ impl RelationInfo { return None; }; - Some(RelationInfo::Reply(message_event)) + match draft_type { + ComposerDraftType::Reply { .. } => Some(RelationInfo::Reply(message_event)), + ComposerDraftType::Edit { .. } => Some(RelationInfo::Edit(message_event)), + ComposerDraftType::NewMessage { .. } => None, + } } - ComposerDraftType::Edit { event_id } => Some(RelationInfo::Edit(event_id)), } } /// The unique global identifier of the related event. pub(crate) fn event_id(&self) -> OwnedEventId { match self { - RelationInfo::Reply(message_event) => message_event.event_id(), - RelationInfo::Edit(event_id) => event_id.clone(), + RelationInfo::Reply(message_event) | RelationInfo::Edit(message_event) => { + message_event.event_id() + } } } @@ -571,8 +591,8 @@ impl RelationInfo { Self::Reply(message_event) => ComposerDraftType::Reply { event_id: message_event.event_id(), }, - Self::Edit(event_id) => ComposerDraftType::Edit { - event_id: event_id.clone(), + Self::Edit(message_event) => ComposerDraftType::Edit { + event_id: message_event.event_id(), }, } } diff --git a/src/session/view/content/room_history/message_toolbar/mod.rs b/src/session/view/content/room_history/message_toolbar/mod.rs index 8f94b193..daf9d82a 100644 --- a/src/session/view/content/room_history/message_toolbar/mod.rs +++ b/src/session/view/content/room_history/message_toolbar/mod.rs @@ -448,7 +448,38 @@ mod imp { move |_| { let is_empty = imp.is_buffer_empty(); imp.send_button.set_sensitive(!is_empty); - imp.send_typing_notification(!is_empty); + + if is_empty { + return; + } + + // If we're editing, disable the send button while the body is unchanged + if let Some(RelationInfo::Edit(message)) = + imp.current_composer_state().related_to() + { + if let Some(message_type) = message.msgtype() { + let original_body = match &message_type { + MessageType::Text(text) => text.body.clone(), + MessageType::Emote(emote) => emote.body.clone(), + _ => return, + }; + let body = match &message_type { + MessageType::Emote(_) => { + ComposerParser::new(&imp.current_composer_state(), None) + .into_plain_text() + .replace("/me ", "") + } + MessageType::Text(_) => { + ComposerParser::new(&imp.current_composer_state(), None) + .into_plain_text() + } + _ => return, + }; + let unchanged = original_body == body; + imp.send_button.set_sensitive(!unchanged); + imp.send_typing_notification(!unchanged); + } + } } )); @@ -586,21 +617,22 @@ mod imp { let item = event.item(); - let Some(event_id) = item.event_id() else { - warn!("Cannot send edit for event that is not sent yet"); + let TimelineItemContent::MsgLike(msg_like) = item.content() else { + warn!("Unsupported event type for edit"); return; }; - let TimelineItemContent::MsgLike(msg_like) = item.content() else { + let Some(_message) = msg_like.as_message() else { warn!("Unsupported event type for edit"); return; }; - let Some(message) = msg_like.as_message() else { + + let Some(message_event) = MessageEventSource::from_event(event.clone()) else { warn!("Unsupported event type for edit"); return; }; self.current_composer_state() - .set_edit_source(event_id.to_owned(), &message); + .set_edit_source(&message_event); self.message_entry.grab_focus(); } @@ -669,6 +701,9 @@ mod imp { if !self.can_compose_message() { return; } + if !self.send_button.is_sensitive() { + return; + } let Some(timeline) = self.timeline.upgrade() else { return; }; @@ -703,8 +738,9 @@ mod imp { toast!(self.obj(), gettext("Could not send reply")); } } - Some(RelationInfo::Edit(event_id)) => { + Some(RelationInfo::Edit(message_event)) => { let matrix_room = timeline.room().matrix_room().clone(); + let event_id = message_event.event_id(); let handle = spawn_tokio!(async move { let full_content = matrix_room .make_edit_event(&event_id, EditedContent::RoomMessage(content))