Add support for previewing images in room scrollback (#108)

This commit is contained in:
Benjamin Grosse 2023-11-16 08:36:22 -08:00 committed by GitHub
parent 974775b29b
commit 221faa828d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 588 additions and 29 deletions

View file

@ -51,7 +51,9 @@ use modalkit::tui::{
};
use modalkit::editing::{base::ViewportContext, cursor::Cursor};
use ratatui_image::protocol::Protocol;
use crate::config::ImagePreviewSize;
use crate::{
base::{IambResult, RoomInfo},
config::ApplicationSettings,
@ -585,12 +587,20 @@ impl<'a> MessageFormatter<'a> {
}
}
pub enum ImageStatus {
None,
Downloading(ImagePreviewSize),
Loaded(Box<dyn Protocol>),
Error(String),
}
pub struct Message {
pub event: MessageEvent,
pub sender: OwnedUserId,
pub timestamp: MessageTimeStamp,
pub downloaded: bool,
pub html: Option<StyleTree>,
pub image_preview: ImageStatus,
}
impl Message {
@ -598,7 +608,14 @@ impl Message {
let html = event.html();
let downloaded = false;
Message { event, sender, timestamp, downloaded, html }
Message {
event,
sender,
timestamp,
downloaded,
html,
image_preview: ImageStatus::None,
}
}
pub fn reply_to(&self) -> Option<OwnedEventId> {
@ -681,6 +698,36 @@ impl Message {
}
}
/// Get the image preview Protocol and x,y offset, based on get_render_format.
pub fn line_preview<'a>(
&'a self,
prev: Option<&Message>,
vwctx: &ViewportContext<MessageCursor>,
info: &'a RoomInfo,
) -> Option<(&dyn Protocol, u16, u16)> {
let width = vwctx.get_width();
// The x position where get_render_format would render the text.
let x = (if USER_GUTTER + MIN_MSG_LEN <= width {
USER_GUTTER
} else {
0
} + 1) as u16;
// See get_render_format; account for possible "date" line.
let date_y = match &prev {
Some(prev) if !prev.timestamp.same_day(&self.timestamp) => 1,
_ => 0,
};
if let ImageStatus::Loaded(backend) = &self.image_preview {
return Some((backend.as_ref(), x, date_y));
} else if let Some(reply) = self.reply_to().and_then(|e| info.get_event(&e)) {
if let ImageStatus::Loaded(backend) = &reply.image_preview {
// The reply should be offset a bit:
return Some((backend.as_ref(), x + 2, date_y + 1));
}
}
None
}
pub fn show<'a>(
&'a self,
prev: Option<&Message>,
@ -791,6 +838,19 @@ impl Message {
msg.to_mut().push_str(" \u{2705}");
}
if let Some(placeholder) = match &self.image_preview {
ImageStatus::None => None,
ImageStatus::Downloading(image_preview_size) => {
Some(Message::placeholder_frame(Some("Downloading..."), image_preview_size))
},
ImageStatus::Loaded(backend) => {
Some(Message::placeholder_frame(None, &backend.rect().into()))
},
ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")),
} {
msg.to_mut().insert_str(0, &placeholder);
}
wrapped_text(msg, width, style)
}
}
@ -803,6 +863,24 @@ impl Message {
settings.get_user_span(self.sender.as_ref(), info)
}
/// Before the image is loaded, already display a placeholder frame of the image size.
fn placeholder_frame(text: Option<&str>, image_preview_size: &ImagePreviewSize) -> String {
let ImagePreviewSize { width, height } = image_preview_size;
let mut placeholder = "\u{230c}".to_string();
placeholder.push_str(&" ".repeat(width - 2));
placeholder.push_str("\u{230d}\n");
placeholder.push(' ');
if let Some(text) = text {
placeholder.push_str(text);
}
placeholder.push_str(&"\n".repeat(height - 2));
placeholder.push('\u{230e}');
placeholder.push_str(&" ".repeat(width - 2));
placeholder.push_str("\u{230f}\n");
placeholder
}
fn show_sender<'a>(
&'a self,
prev: Option<&Message>,