Fix image preview in replies and threads (#366)

This commit is contained in:
Andrew Collins 2025-05-15 14:23:39 +10:00 committed by GitHub
parent 3e45ca3d2c
commit a9c1e69a89
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 34 additions and 14 deletions

View file

@ -70,6 +70,8 @@ mod printer;
pub use self::compose::text_to_message; pub use self::compose::text_to_message;
type ProtocolPreview<'a> = (&'a dyn Protocol, u16, u16);
pub type MessageKey = (MessageTimeStamp, OwnedEventId); pub type MessageKey = (MessageTimeStamp, OwnedEventId);
#[derive(Default)] #[derive(Default)]
@ -715,11 +717,12 @@ impl<'a> MessageFormatter<'a> {
style: Style, style: Style,
text: &mut Text<'a>, text: &mut Text<'a>,
info: &'a RoomInfo, info: &'a RoomInfo,
) { settings: &'a ApplicationSettings,
) -> Option<ProtocolPreview<'a>> {
let width = self.width(); let width = self.width();
let w = width.saturating_sub(2); let w = width.saturating_sub(2);
let shortcodes = self.settings.tunables.message_shortcode_display; 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 mut sender = msg.sender_span(info, self.settings);
let sender_width = UnicodeWidthStr::width(sender.content.as_ref()); let sender_width = UnicodeWidthStr::width(sender.content.as_ref());
let trailing = w.saturating_sub(sender_width + 1); let trailing = w.saturating_sub(sender_width + 1);
@ -738,12 +741,22 @@ impl<'a> MessageFormatter<'a> {
text, 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() { for line in replied.lines.iter_mut() {
line.spans.insert(0, Span::styled(THICK_VERTICAL, style)); line.spans.insert(0, Span::styled(THICK_VERTICAL, style));
line.spans.insert(0, Span::styled(" ", style)); line.spans.insert(0, Span::styled(" ", style));
} }
self.push_text(replied, style, text); self.push_text(replied, style, text);
proto
} }
fn push_reactions(&mut self, counts: Vec<(&'a str, usize)>, style: Style, text: &mut Text<'a>) { fn push_reactions(&mut self, counts: Vec<(&'a str, usize)>, style: Style, text: &mut Text<'a>) {
@ -961,7 +974,7 @@ impl Message {
vwctx: &ViewportContext<MessageCursor>, vwctx: &ViewportContext<MessageCursor>,
info: &'a RoomInfo, info: &'a RoomInfo,
settings: &'a ApplicationSettings, settings: &'a ApplicationSettings,
) -> (Text<'a>, Option<(&'a dyn Protocol, u16, u16)>) { ) -> (Text<'a>, [Option<ProtocolPreview<'a>>; 2]) {
let width = vwctx.get_width(); let width = vwctx.get_width();
let style = self.get_render_style(selected, settings); let style = self.get_render_style(selected, settings);
@ -974,10 +987,10 @@ impl Message {
.reply_to() .reply_to()
.or_else(|| self.thread_root()) .or_else(|| self.thread_root())
.and_then(|e| info.get_event(&e)); .and_then(|e| info.get_event(&e));
let proto_reply = reply.as_ref().and_then(|r| {
if let Some(r) = &reply { // Format the reply header, push it into the `Text` buffer, and get any image.
fmt.push_in_reply(r, style, &mut text, info); 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. // Now show the message contents, and the inlined reply if we couldn't find it above.
let (msg, proto) = self.show_msg( let (msg, proto) = self.show_msg(
@ -988,10 +1001,11 @@ impl Message {
); );
// Given our text so far, determine the image offset. // 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 y_off = text.lines.len() as u16;
let x_off = fmt.cols.user_gutter_width(settings); 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 }; let y_off = if fmt.date.is_some() { y_off + 1 } else { y_off };
(p, x_off, y_off) (p, x_off, y_off)
}); });
@ -1012,7 +1026,7 @@ impl Message {
fmt.push_thread_reply_count(thread.len(), &mut text); fmt.push_thread_reply_count(thread.len(), &mut text);
} }
(text, proto) (text, [proto_main, proto_reply])
} }
pub fn show<'a>( pub fn show<'a>(
@ -1053,7 +1067,7 @@ impl Message {
}, },
ImageStatus::Loaded(backend) => { ImageStatus::Loaded(backend) => {
proto = Some(backend.as_ref()); 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")), ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")),
}; };

View file

@ -1340,7 +1340,7 @@ impl<'a> StatefulWidget for Scrollback<'a> {
for (key, item) in thread.range(&corner_key..) { for (key, item) in thread.range(&corner_key..) {
let sel = key == cursor_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); item.show_with_preview(prev, foc && sel, &state.viewctx, info, settings);
let incomplete_ok = !full || !sel; let incomplete_ok = !full || !sel;
@ -1357,11 +1357,17 @@ impl<'a> StatefulWidget for Scrollback<'a> {
continue; continue;
} }
let line_preview = match msg_preview {
// Only take the preview into the matching row number. // 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 {
Some((_, _, y)) if y as usize == row => msg_preview.take(), Some((_, _, y)) if y as usize == row => msg_preview.take(),
_ => None, _ => None,
}; }
.or(match reply_preview {
Some((_, _, y)) if y as usize == row => reply_preview.take(),
_ => None,
});
lines.push((key, row, line, line_preview)); lines.push((key, row, line, line_preview));
sawit |= sel; sawit |= sel;