diff --git a/src/message/mod.rs b/src/message/mod.rs index a200140..d9c0716 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -70,6 +70,8 @@ mod printer; pub use self::compose::text_to_message; +type ProtocolPreview<'a> = (&'a dyn Protocol, u16, u16); + pub type MessageKey = (MessageTimeStamp, OwnedEventId); #[derive(Default)] @@ -715,11 +717,12 @@ impl<'a> MessageFormatter<'a> { style: Style, text: &mut Text<'a>, info: &'a RoomInfo, - ) { + settings: &'a ApplicationSettings, + ) -> Option> { 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 replied, proto) = 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); @@ -738,12 +741,22 @@ impl<'a> MessageFormatter<'a> { text, ); + // Determine the image offset of the reply header, taking into account the formatting + let proto = proto.map(|p| { + let y_off = text.lines.len() as u16; + // Adjust x_off by 2 to account for the vertical line and indent + let x_off = self.cols.user_gutter_width(settings) + 2; + (p, x_off, y_off) + }); + 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); + + proto } fn push_reactions(&mut self, counts: Vec<(&'a str, usize)>, style: Style, text: &mut Text<'a>) { @@ -961,7 +974,7 @@ impl Message { vwctx: &ViewportContext, info: &'a RoomInfo, settings: &'a ApplicationSettings, - ) -> (Text<'a>, Option<(&'a dyn Protocol, u16, u16)>) { + ) -> (Text<'a>, [Option>; 2]) { let width = vwctx.get_width(); let style = self.get_render_style(selected, settings); @@ -974,10 +987,10 @@ impl Message { .reply_to() .or_else(|| self.thread_root()) .and_then(|e| info.get_event(&e)); - - if let Some(r) = &reply { - fmt.push_in_reply(r, style, &mut text, info); - } + let proto_reply = reply.as_ref().and_then(|r| { + // Format the reply header, push it into the `Text` buffer, and get any image. + fmt.push_in_reply(r, style, &mut text, info, settings) + }); // Now show the message contents, and the inlined reply if we couldn't find it above. let (msg, proto) = self.show_msg( @@ -988,10 +1001,11 @@ impl Message { ); // Given our text so far, determine the image offset. - let proto = proto.map(|p| { + let proto_main = proto.map(|p| { let y_off = text.lines.len() as u16; let x_off = fmt.cols.user_gutter_width(settings); - // Adjust y_off by 1 if a date was printed before the message to account for the extra line. + // Adjust y_off by 1 if a date was printed before the message to account for + // the extra line we're going to print. let y_off = if fmt.date.is_some() { y_off + 1 } else { y_off }; (p, x_off, y_off) }); @@ -1012,7 +1026,7 @@ impl Message { fmt.push_thread_reply_count(thread.len(), &mut text); } - (text, proto) + (text, [proto_main, proto_reply]) } pub fn show<'a>( @@ -1053,7 +1067,7 @@ impl Message { }, ImageStatus::Loaded(backend) => { proto = Some(backend.as_ref()); - placeholder_frame(None, width, &backend.rect().into()) + placeholder_frame(Some("Loading..."), width, &backend.rect().into()) }, ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")), }; diff --git a/src/windows/room/scrollback.rs b/src/windows/room/scrollback.rs index eeb6c2d..cc0e3bd 100644 --- a/src/windows/room/scrollback.rs +++ b/src/windows/room/scrollback.rs @@ -1340,7 +1340,7 @@ impl<'a> StatefulWidget for Scrollback<'a> { for (key, item) in thread.range(&corner_key..) { let sel = key == cursor_key; - let (txt, mut msg_preview) = + let (txt, [mut msg_preview, mut reply_preview]) = item.show_with_preview(prev, foc && sel, &state.viewctx, info, settings); let incomplete_ok = !full || !sel; @@ -1357,11 +1357,17 @@ impl<'a> StatefulWidget for Scrollback<'a> { continue; } + // Only take the preview into the matching row number. + // `reply` and `msg` previews are on rows, + // so an `or` works to pick the one that matches (if any) let line_preview = match msg_preview { - // Only take the preview into the matching row number. Some((_, _, y)) if y as usize == row => msg_preview.take(), _ => None, - }; + } + .or(match reply_preview { + Some((_, _, y)) if y as usize == row => reply_preview.take(), + _ => None, + }); lines.push((key, row, line, line_preview)); sawit |= sel;