diff --git a/src/base.rs b/src/base.rs index 3636a54..a306e16 100644 --- a/src/base.rs +++ b/src/base.rs @@ -45,6 +45,7 @@ use matrix_sdk::{ RoomMessageEventContent, RoomMessageEventContentWithoutRelation, }, + room::redaction::{OriginalSyncRoomRedactionEvent, SyncRoomRedactionEvent}, tag::{TagName, Tags}, MessageLikeEvent, }, @@ -54,6 +55,7 @@ use matrix_sdk::{ OwnedRoomId, OwnedUserId, RoomId, + RoomVersionId, UserId, }, RoomState as MatrixRoomState, @@ -729,7 +731,7 @@ pub struct RoomInfo { pub keys: HashMap, /// The messages loaded for this room. - pub messages: Messages, + messages: Messages, /// A map of read markers to display on different events. pub event_receipts: HashMap>, @@ -745,7 +747,7 @@ pub struct RoomInfo { pub reactions: HashMap, /// A map of message identifiers to thread replies. - pub threads: HashMap, + threads: HashMap, /// Whether the scrollback for this room is currently being fetched. pub fetching: bool, @@ -767,6 +769,48 @@ pub struct RoomInfo { } impl RoomInfo { + pub fn get_thread(&self, root: Option<&EventId>) -> Option<&Messages> { + if let Some(thread_root) = root { + self.threads.get(thread_root) + } else { + Some(&self.messages) + } + } + + pub fn get_thread_mut(&mut self, root: Option) -> &mut Messages { + if let Some(thread_root) = root { + self.threads.entry(thread_root).or_default() + } else { + &mut self.messages + } + } + + /// Get the event for the last message in a thread (or the thread root if there are no + /// in-thread replies yet). + /// + /// This returns `None` if the event identifier isn't in the room. + pub fn get_thread_last<'a>( + &'a self, + thread_root: &OwnedEventId, + ) -> Option<&'a OriginalRoomMessageEvent> { + let last = self.threads.get(thread_root).and_then(|t| Some(t.last_key_value()?.1)); + + let msg = if let Some(last) = last { + &last.event + } else if let EventLocation::Message(_, key) = self.keys.get(thread_root)? { + let msg = self.messages.get(key)?; + &msg.event + } else { + return None; + }; + + if let MessageEvent::Original(ev) = &msg { + Some(ev) + } else { + None + } + } + /// Get the reactions and their counts for a message. pub fn get_reactions(&self, event_id: &EventId) -> Vec<(&str, usize)> { if let Some(reacts) = self.reactions.get(event_id) { @@ -801,6 +845,37 @@ impl RoomInfo { self.messages.get_mut(self.keys.get(event_id)?.to_message_key()?) } + pub fn redact(&mut self, ev: OriginalSyncRoomRedactionEvent, room_version: &RoomVersionId) { + let Some(redacts) = &ev.redacts else { + return; + }; + + match self.keys.get(redacts) { + None => return, + Some(EventLocation::Message(None, key)) => { + if let Some(msg) = self.messages.get_mut(key) { + let ev = SyncRoomRedactionEvent::Original(ev); + msg.redact(ev, room_version); + } + }, + Some(EventLocation::Message(Some(root), key)) => { + if let Some(thread) = self.threads.get_mut(root) { + if let Some(msg) = thread.get_mut(key) { + let ev = SyncRoomRedactionEvent::Original(ev); + msg.redact(ev, room_version); + } + } + }, + Some(EventLocation::Reaction(event_id)) => { + if let Some(reactions) = self.reactions.get_mut(event_id) { + reactions.remove(redacts); + } + + self.keys.remove(redacts); + }, + } + } + /// Insert a reaction to a message. pub fn insert_reaction(&mut self, react: ReactionEvent) { match react { diff --git a/src/message/mod.rs b/src/message/mod.rs index 0cd49f4..9b262aa 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -638,6 +638,106 @@ impl<'a> MessageFormatter<'a> { self.push_spans(line, style, text); } } + + fn push_in_reply( + &mut self, + msg: &'a Message, + style: Style, + text: &mut Text<'a>, + info: &'a RoomInfo, + ) { + let width = self.width(); + let w = width.saturating_sub(2); + let shortcodes = self.settings.tunables.message_shortcode_display; + let mut replied = msg.show_msg(w, style, true, shortcodes); + let mut sender = msg.sender_span(info, self.settings); + let sender_width = UnicodeWidthStr::width(sender.content.as_ref()); + let trailing = w.saturating_sub(sender_width + 1); + + sender.style = sender.style.patch(style); + + self.push_spans( + Line::from(vec![ + Span::styled(" ", style), + Span::styled(THICK_VERTICAL, style), + sender, + Span::styled(":", style), + space_span(trailing, style), + ]), + style, + text, + ); + + for line in replied.lines.iter_mut() { + line.spans.insert(0, Span::styled(THICK_VERTICAL, style)); + line.spans.insert(0, Span::styled(" ", style)); + } + + self.push_text(replied, style, text); + } + + fn push_reactions(&mut self, counts: Vec<(&'a str, usize)>, style: Style, text: &mut Text<'a>) { + let mut emojis = printer::TextPrinter::new(self.width(), style, false, false); + let mut reactions = 0; + + for (key, count) in counts { + if reactions != 0 { + emojis.push_str(" ", style); + } + + let name = if self.settings.tunables.reaction_shortcode_display { + if let Some(emoji) = emojis::get(key) { + if let Some(short) = emoji.shortcode() { + short + } else { + // No ASCII shortcode name to show. + continue; + } + } else if key.chars().all(|c| c.is_ascii_alphanumeric()) { + key + } else { + // Not an Emoji or a printable ASCII string. + continue; + } + } else { + key + }; + + emojis.push_str("[", style); + emojis.push_str(name, style); + emojis.push_str(" ", style); + emojis.push_span_nobreak(Span::styled(count.to_string(), style)); + emojis.push_str("]", style); + + reactions += 1; + } + + if reactions > 0 { + self.push_text(emojis.finish(), style, text); + } + } + + fn push_thread_reply_count(&mut self, len: usize, text: &mut Text<'a>) { + if len == 0 { + return; + } + + // If we have threaded replies to this message, show how many. + let plural = len != 1; + let style = Style::default(); + let mut threaded = + printer::TextPrinter::new(self.width(), style, false, false).literal(true); + let len = Span::styled(len.to_string(), style.add_modifier(StyleModifier::BOLD)); + threaded.push_str(" \u{2937} ", style); + threaded.push_span_nobreak(len); + if plural { + threaded.push_str(" replies in thread", style); + } else { + threaded.push_str(" reply in thread", style); + } + + self.push_text(threaded.finish(), style, text); + } } pub enum ImageStatus { @@ -835,33 +935,7 @@ impl Message { .and_then(|e| info.get_event(&e)); if let Some(r) = &reply { - let w = width.saturating_sub(2); - let mut replied = - r.show_msg(w, style, true, settings.tunables.message_shortcode_display); - let mut sender = r.sender_span(info, settings); - let sender_width = UnicodeWidthStr::width(sender.content.as_ref()); - let trailing = w.saturating_sub(sender_width + 1); - - sender.style = sender.style.patch(style); - - fmt.push_spans( - Line::from(vec![ - Span::styled(" ", style), - Span::styled(THICK_VERTICAL, style), - sender, - Span::styled(":", style), - space_span(trailing, style), - ]), - style, - &mut text, - ); - - for line in replied.lines.iter_mut() { - line.spans.insert(0, Span::styled(THICK_VERTICAL, style)); - line.spans.insert(0, Span::styled(" ", style)); - } - - fmt.push_text(replied, style, &mut text); + fmt.push_in_reply(r, style, &mut text, info); } // Now show the message contents, and the inlined reply if we couldn't find it above. @@ -879,63 +953,12 @@ impl Message { } if settings.tunables.reaction_display { - // Pass false for emoji_shortcodes parameter because we handle shortcodes ourselves - // before pushing to the printer. - let mut emojis = printer::TextPrinter::new(width, style, false, false); - let mut reactions = 0; - - for (key, count) in info.get_reactions(self.event.event_id()).into_iter() { - if reactions != 0 { - emojis.push_str(" ", style); - } - - let name = if settings.tunables.reaction_shortcode_display { - if let Some(emoji) = emojis::get(key) { - if let Some(short) = emoji.shortcode() { - short - } else { - // No ASCII shortcode name to show. - continue; - } - } else if key.chars().all(|c| c.is_ascii_alphanumeric()) { - key - } else { - // Not an Emoji or a printable ASCII string. - continue; - } - } else { - key - }; - - emojis.push_str("[", style); - emojis.push_str(name, style); - emojis.push_str(" ", style); - emojis.push_span_nobreak(Span::styled(count.to_string(), style)); - emojis.push_str("]", style); - - reactions += 1; - } - - if reactions > 0 { - fmt.push_text(emojis.finish(), style, &mut text); - } + let reactions = info.get_reactions(self.event.event_id()); + fmt.push_reactions(reactions, style, &mut text); } - if let Some(thread) = info.threads.get(self.event.event_id()) { - // If we have threaded replies to this message, show how many. - let len = thread.len(); - - if len > 0 { - let style = Style::default(); - let emoji_shortcodes = settings.tunables.message_shortcode_display; - let mut threaded = - printer::TextPrinter::new(width, style, false, emoji_shortcodes).literal(true); - let len = Span::styled(len.to_string(), style.add_modifier(StyleModifier::BOLD)); - threaded.push_str(" \u{2937} ", style); - threaded.push_span_nobreak(len); - threaded.push_str(" replies in thread", style); - fmt.push_text(threaded.finish(), style, &mut text); - } + if let Some(thread) = info.get_thread(Some(self.event.event_id())) { + fmt.push_thread_reply_count(thread.len(), &mut text); } text diff --git a/src/tests.rs b/src/tests.rs index 0adabe3..aa9284b 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -21,7 +21,7 @@ use tracing::Level; use url::Url; use crate::{ - base::{ChatStore, EventLocation, ProgramStore, RoomFetchStatus, RoomInfo}, + base::{ChatStore, EventLocation, ProgramStore, RoomInfo}, config::{ user_color, user_style_from_color, @@ -148,25 +148,11 @@ pub fn mock_messages() -> Messages { } pub fn mock_room() -> RoomInfo { - RoomInfo { - name: Some("Watercooler Discussion".into()), - tags: None, - - keys: mock_keys(), - messages: mock_messages(), - threads: HashMap::default(), - - event_receipts: HashMap::new(), - user_receipts: HashMap::new(), - reactions: HashMap::new(), - - fetching: false, - fetch_id: RoomFetchStatus::NotStarted, - fetch_last: None, - users_typing: None, - display_names: HashMap::new(), - draw_last: None, - } + let mut room = RoomInfo::default(); + room.name = Some("Watercooler Discussion".into()); + room.keys = mock_keys(); + *room.get_thread_mut(None) = mock_messages(); + room } pub fn mock_dirs() -> DirectoryValues { diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index d872f5a..9a873ef 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -28,7 +28,6 @@ use matrix_sdk::{ RoomMessageEventContent, TextMessageEventContent, }, - EventId, OwnedEventId, OwnedRoomId, RoomId, @@ -72,7 +71,6 @@ use modalkit::prelude::*; use crate::base::{ DownloadFlags, - EventLocation, IambAction, IambBufferId, IambError, @@ -148,32 +146,10 @@ impl ChatState { } } - fn get_thread_last<'a>( - &self, - thread_root: &OwnedEventId, - info: &'a RoomInfo, - ) -> Option<&'a OriginalRoomMessageEvent> { - let last = info.threads.get(thread_root).and_then(|t| Some(t.last_key_value()?.1)); - - let msg = if let Some(last) = last { - &last.event - } else if let EventLocation::Message(_, key) = info.keys.get(thread_root)? { - let msg = info.messages.get(key)?; - &msg.event - } else { - return None; - }; - - if let MessageEvent::Original(ev) = &msg { - Some(ev) - } else { - None - } - } - fn get_reply_to<'a>(&self, info: &'a RoomInfo) -> Option<&'a OriginalRoomMessageEvent> { + let thread = self.scrollback.get_thread(info)?; let key = self.reply_to.as_ref()?; - let msg = info.messages.get(key)?; + let msg = thread.get(key)?; if let MessageEvent::Original(ev) = &msg.event { Some(ev) @@ -205,10 +181,7 @@ impl ChatState { let settings = &store.application.settings; let info = store.application.rooms.get_or_default(self.room_id.clone()); - let msg = self - .scrollback - .get_mut(&mut info.messages) - .ok_or(IambError::NoSelectedMessage)?; + let msg = self.scrollback.get_mut(info).ok_or(IambError::NoSelectedMessage)?; match act { MessageAction::Cancel(skip_confirm) => { @@ -441,11 +414,11 @@ impl ChatState { }, MessageAction::Unreact(emoji) => { let room = self.get_joined(&store.application.worker)?; - let event_id: &EventId = match &msg.event { - MessageEvent::EncryptedOriginal(ev) => ev.event_id.as_ref(), - MessageEvent::EncryptedRedacted(ev) => ev.event_id.as_ref(), - MessageEvent::Original(ev) => ev.event_id.as_ref(), - MessageEvent::Local(event_id, _) => event_id.as_ref(), + let event_id = match &msg.event { + MessageEvent::EncryptedOriginal(ev) => ev.event_id.clone(), + MessageEvent::EncryptedRedacted(ev) => ev.event_id.clone(), + MessageEvent::Original(ev) => ev.event_id.clone(), + MessageEvent::Local(event_id, _) => event_id.clone(), MessageEvent::Redacted(_) => { let msg = "Cannot unreact to a redacted message"; let err = UIError::Failure(msg.into()); @@ -454,7 +427,7 @@ impl ChatState { }, }; - let reactions = match info.reactions.get(event_id) { + let reactions = match info.reactions.get(&event_id) { Some(r) => r, None => return Ok(None), }; @@ -518,7 +491,7 @@ impl ChatState { } else if let Some(thread_root) = self.scrollback.thread() { if let Some(m) = self.get_reply_to(info) { msg = msg.make_for_thread(m, ReplyWithinThread::Yes, AddMentions::No); - } else if let Some(m) = self.get_thread_last(thread_root, info) { + } else if let Some(m) = info.get_thread_last(thread_root) { msg = msg.make_for_thread(m, ReplyWithinThread::No, AddMentions::No); } else { // Internal state is wonky? @@ -884,19 +857,21 @@ impl<'a> StatefulWidget for Chat<'a> { fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { // Determine whether we have a description to show for the message bar. - let desc_spans = match (&state.editing, &state.reply_to) { - (None, None) => None, - (Some(_), None) => Some(Line::from("Editing message")), - (editing, Some(_)) => { - state.reply_to.as_ref().and_then(|k| { - let room = self.store.application.rooms.get(state.id())?; - let msg = room.messages.get(k)?; + let desc_spans = match (&state.editing, &state.reply_to, state.thread()) { + (None, None, None) => None, + (None, None, Some(_)) => Some(Line::from("Replying in thread")), + (Some(_), None, None) => Some(Line::from("Editing message")), + (Some(_), None, Some(_)) => Some(Line::from("Editing message in thread")), + (editing, Some(_), thread) => { + self.store.application.rooms.get(state.id()).and_then(|room| { + let msg = state.get_reply_to(room)?; let user = self.store.application.settings.get_user_span(msg.sender.as_ref(), room); - let prefix = if editing.is_some() { - Span::from("Editing reply to ") - } else { - Span::from("Replying to ") + let prefix = match (editing.is_some(), thread.is_some()) { + (true, false) => Span::from("Editing reply to "), + (true, true) => Span::from("Editing reply in thread to "), + (false, false) => Span::from("Replying to "), + (false, true) => Span::from("Replying in thread to "), }; let spans = Line::from(vec![prefix, user]); diff --git a/src/windows/room/scrollback.rs b/src/windows/room/scrollback.rs index d5a4223..8ee327b 100644 --- a/src/windows/room/scrollback.rs +++ b/src/windows/room/scrollback.rs @@ -1,5 +1,4 @@ //! Message scrollback - use ratatui_image::Image; use regex::Regex; @@ -59,6 +58,11 @@ use crate::{ message::{Message, MessageCursor, MessageKey, Messages}, }; +fn no_msgs() -> EditError { + let msg = "No messages to select."; + EditError::Failure(msg.to_string()) +} + fn nth_key_before(pos: MessageKey, n: usize, thread: &Messages) -> MessageKey { let mut end = &pos; let iter = thread.range(..=&pos).rev().enumerate(); @@ -159,14 +163,16 @@ impl ScrollbackState { self.cursor .timestamp .clone() - .or_else(|| self.get_thread(info).last_key_value().map(|kv| kv.0.clone())) + .or_else(|| self.get_thread(info)?.last_key_value().map(|kv| kv.0.clone())) } - pub fn get_mut<'a>(&mut self, messages: &'a mut Messages) -> Option<&'a mut Message> { + pub fn get_mut<'a>(&mut self, info: &'a mut RoomInfo) -> Option<&'a mut Message> { + let thread = self.get_thread_mut(info); + if let Some(k) = &self.cursor.timestamp { - messages.get_mut(k) + thread.get_mut(k) } else { - messages.last_entry().map(|o| o.into_mut()) + thread.last_entry().map(|o| o.into_mut()) } } @@ -174,20 +180,12 @@ impl ScrollbackState { self.thread.as_ref() } - pub fn get_thread<'a>(&self, info: &'a RoomInfo) -> &'a Messages { - if let Some(thread_root) = self.thread.as_ref() { - info.threads.get(thread_root).unwrap_or(&info.messages) - } else { - &info.messages - } + pub fn get_thread<'a>(&self, info: &'a RoomInfo) -> Option<&'a Messages> { + info.get_thread(self.thread.as_deref()) } pub fn get_thread_mut<'a>(&self, info: &'a mut RoomInfo) -> &'a mut Messages { - if let Some(thread_root) = self.thread.as_ref() { - info.threads.get_mut(thread_root).unwrap_or(&mut info.messages) - } else { - &mut info.messages - } + info.get_thread_mut(self.thread.clone()) } pub fn messages<'a>( @@ -195,7 +193,10 @@ impl ScrollbackState { range: EditRange, info: &'a RoomInfo, ) -> impl Iterator { - let thread = self.get_thread(info); + let Some(thread) = self.get_thread(info) else { + return Default::default(); + }; + let start = range.start.to_key(thread); let end = range.end.to_key(thread); @@ -223,7 +224,7 @@ impl ScrollbackState { _ => {}, } - let first_key = self.get_thread(info).first_key_value().map(|(k, _)| k); + let first_key = self.get_thread(info).and_then(|t| t.first_key_value()).map(|(k, _)| k); let at_top = first_key == self.viewctx.corner.timestamp.as_ref(); match (at_top, self.thread.as_ref()) { @@ -254,7 +255,10 @@ impl ScrollbackState { info: &RoomInfo, settings: &ApplicationSettings, ) { - let thread = self.get_thread(info); + let Some(thread) = self.get_thread(info) else { + return; + }; + let selidx = if let Some(key) = self.cursor.to_key(thread) { key } else { @@ -319,7 +323,9 @@ impl ScrollbackState { } fn shift_cursor(&mut self, info: &RoomInfo, settings: &ApplicationSettings) { - let thread = self.get_thread(info); + let Some(thread) = self.get_thread(info) else { + return; + }; let last_key = if let Some(k) = thread.last_key_value() { k.0 @@ -389,7 +395,7 @@ impl ScrollbackState { MoveType::BufferLineOffset => None, MoveType::BufferLinePercent => None, MoveType::BufferPos(MovePosition::Beginning) => { - let start = self.get_thread(info).first_key_value()?.0.clone(); + let start = self.get_thread(info)?.first_key_value()?.0.clone(); Some(start.into()) }, @@ -402,7 +408,7 @@ impl ScrollbackState { MoveType::ParagraphBegin(dir) | MoveType::SectionBegin(dir) | MoveType::SectionEnd(dir) => { - let thread = self.get_thread(info); + let thread = self.get_thread(info)?; match dir { MoveDir1D::Previous => nth_before(pos, count, thread).into(), @@ -455,14 +461,14 @@ impl ScrollbackState { RangeType::XmlTag => None, RangeType::Buffer => { - let thread = self.get_thread(info); + let thread = self.get_thread(info)?; let start = thread.first_key_value()?.0.clone(); let end = thread.last_key_value()?.0.clone(); Some(EditRange::inclusive(start.into(), end.into(), TargetShape::LineWise)) }, RangeType::Line | RangeType::Paragraph | RangeType::Sentence => { - let thread = self.get_thread(info); + let thread = self.get_thread(info)?; let count = ctx.resolve(count); if count == 0 { @@ -496,7 +502,7 @@ impl ScrollbackState { mut count: usize, info: &RoomInfo, ) -> Option { - let thread = self.get_thread(info); + let thread = self.get_thread(info)?; let mut mc = None; for (key, msg) in thread.range(&start..) { @@ -524,9 +530,12 @@ impl ScrollbackState { mut count: usize, info: &RoomInfo, ) -> (Option, bool) { - let thread = self.get_thread(info); let mut mc = None; + let Some(thread) = self.get_thread(info) else { + return (None, false); + }; + for (key, msg) in thread.range(..&end).rev() { if count == 0 { break; @@ -614,14 +623,8 @@ impl EditorActions for ScrollbackState { store: &mut ProgramStore, ) -> EditResult { let info = store.application.rooms.get_or_default(self.room_id.clone()); - let thread = self.get_thread(info); - let key = if let Some(k) = self.cursor.to_key(thread) { - k.clone() - } else { - let msg = "No messages to select."; - let err = EditError::Failure(msg.to_string()); - return Err(err); - }; + let thread = self.get_thread(info).ok_or_else(no_msgs)?; + let key = self.cursor.to_key(thread).ok_or_else(no_msgs)?.clone(); match operation { EditAction::Motion => { @@ -644,7 +647,6 @@ impl EditorActions for ScrollbackState { EditTarget::CharJump(mark) | EditTarget::LineJump(mark) => { let mark = ctx.resolve(mark); let cursor = store.cursors.get_mark(self.id.clone(), mark)?; - let thread = self.get_thread(info); if let Some(mc) = MessageCursor::from_cursor(&cursor, thread) { Some(mc) @@ -723,7 +725,6 @@ impl EditorActions for ScrollbackState { EditTarget::CharJump(mark) | EditTarget::LineJump(mark) => { let mark = ctx.resolve(mark); let cursor = store.cursors.get_mark(self.id.clone(), mark)?; - let thread = self.get_thread(info); if let Some(c) = MessageCursor::from_cursor(&cursor, thread) { self._range_to(c).into() @@ -821,18 +822,11 @@ impl EditorActions for ScrollbackState { store: &mut ProgramStore, ) -> EditResult { let info = store.application.get_room_info(self.room_id.clone()); - let thread = self.get_thread(info); + let thread = self.get_thread(info).ok_or_else(no_msgs)?; + let cursor = self.cursor.to_cursor(thread).ok_or_else(no_msgs)?; + store.cursors.set_mark(self.id.clone(), name, cursor); - if let Some(cursor) = self.cursor.to_cursor(thread) { - store.cursors.set_mark(self.id.clone(), name, cursor); - - Ok(None) - } else { - let msg = "Failed to set mark for message"; - let err = EditError::Failure(msg.into()); - - Err(err) - } + Ok(None) } fn complete( @@ -884,6 +878,7 @@ impl EditorActions for ScrollbackState { store: &mut ProgramStore, ) -> EditResult { let info = store.application.get_room_info(self.room_id.clone()); + let thread = self.get_thread(info).ok_or_else(no_msgs)?; match act { CursorAction::Close(_) => Ok(None), @@ -901,8 +896,6 @@ impl EditorActions for ScrollbackState { self.push_jump(); } - let thread = self.get_thread(info); - if let Some(mc) = MessageCursor::from_cursor(ngroup.leader.cursor(), thread) { self.cursor = mc; @@ -916,7 +909,6 @@ impl EditorActions for ScrollbackState { }, CursorAction::Save(_) => { let reg = ctx.get_register().unwrap_or(Register::UnnamedCursorGroup); - let thread = self.get_thread(info); // Lists don't have groups; override any previously saved group. let cursor = self.cursor.to_cursor(thread).ok_or_else(|| { @@ -1019,7 +1011,7 @@ impl Promptable for ScrollbackState { store: &mut ProgramStore, ) -> EditResult, ProgramContext)>, IambInfo> { let info = store.application.get_room_info(self.room_id.clone()); - let thread = self.get_thread(info); + let thread = self.get_thread(info).ok_or_else(no_msgs)?; let Some(key) = self.cursor.to_key(thread) else { let msg = "No message currently selected"; @@ -1068,7 +1060,7 @@ impl ScrollActions for ScrollbackState { let info = store.application.rooms.get_or_default(self.room_id.clone()); let settings = &store.application.settings; let mut corner = self.viewctx.corner.clone(); - let thread = self.get_thread(info); + let thread = self.get_thread(info).ok_or_else(no_msgs)?; let last_key = if let Some(k) = thread.last_key_value() { k.0 @@ -1182,7 +1174,7 @@ impl ScrollActions for ScrollbackState { Axis::Vertical => { let info = store.application.rooms.get_or_default(self.room_id.clone()); let settings = &store.application.settings; - let thread = self.get_thread(info); + let thread = self.get_thread(info).ok_or_else(no_msgs)?; if let Some(key) = self.cursor.to_key(thread).cloned() { self.scrollview(key, pos, info, settings); @@ -1315,12 +1307,14 @@ impl<'a> StatefulWidget for Scrollback<'a> { return; } + let Some(thread) = state.get_thread(info) else { + return; + }; + if state.cursor.timestamp < state.viewctx.corner.timestamp { state.viewctx.corner = state.cursor.clone(); } - let thread = state.get_thread(info); - let cursor = &state.cursor; let cursor_key = if let Some(k) = cursor.to_key(thread) { k diff --git a/src/worker.rs b/src/worker.rs index 7f14659..a585eab 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -52,7 +52,7 @@ use matrix_sdk::{ member::OriginalSyncRoomMemberEvent, message::{MessageType, RoomMessageEventContent}, name::RoomNameEventContent, - redaction::{OriginalSyncRoomRedactionEvent, SyncRoomRedactionEvent}, + redaction::OriginalSyncRoomRedactionEvent, }, tag::Tags, typing::SyncTypingEvent, @@ -94,7 +94,6 @@ use crate::{ ChatStore, CreateRoomFlags, CreateRoomType, - EventLocation, IambError, IambResult, ProgramStore, @@ -1063,35 +1062,7 @@ impl ClientWorker { let mut locked = store.lock().await; let info = locked.application.get_room_info(room_id.to_owned()); - - let Some(redacts) = &ev.redacts else { - return; - }; - - match info.keys.get(redacts) { - None => return, - Some(EventLocation::Message(None, key)) => { - if let Some(msg) = info.messages.get_mut(key) { - let ev = SyncRoomRedactionEvent::Original(ev); - msg.redact(ev, room_version); - } - }, - Some(EventLocation::Message(Some(root), key)) => { - if let Some(thread) = info.threads.get_mut(root) { - if let Some(msg) = thread.get_mut(key) { - let ev = SyncRoomRedactionEvent::Original(ev); - msg.redact(ev, room_version); - } - } - }, - Some(EventLocation::Reaction(event_id)) => { - if let Some(reactions) = info.reactions.get_mut(event_id) { - reactions.remove(redacts); - } - - info.keys.remove(redacts); - }, - } + info.redact(ev, room_version); } }, );