Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions docs/iamb.5
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,13 @@ Enable image previews and configure it.
An empty object will enable the feature with default settings, omitting it will disable the feature.
The available fields in this object are:
.Bl -tag -width Ds
.It Sy lazy_load
If
.Sy true
(the default), download and render image previews when viewing a message with an image.
If
.Sy false ,
load previews as soon as a message with an image is received.
.It Sy size
An optional object with
.Sy width
Expand Down Expand Up @@ -538,8 +545,6 @@ Configured as an object under the key
.It Sy cache
Specifies where to store assets and temporary data in.
(For example,
.Sy image_preview
and
.Sy logs
will also go in here by default.)
Defaults to
Expand All @@ -555,11 +560,6 @@ Specifies where to store downloaded files.
Defaults to
.Ev $XDG_DOWNLOAD_DIR .

.It Sy image_previews
Specifies where to store automatically downloaded image previews.
Defaults to
.Ev ${cache}/image_preview_downloads .

.It Sy logs
Specifies where to store log files.
Defaults to
Expand Down
33 changes: 11 additions & 22 deletions src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,8 @@ use modalkit::{
};

use crate::config::ImagePreviewProtocolValues;
use crate::message::ImageStatus;
use crate::notifications::NotificationHandle;
use crate::preview::{source_from_event, spawn_insert_preview};
use crate::preview::{source_from_event, PreviewManager};
use crate::{
message::{Message, MessageEvent, MessageKey, MessageTimeStamp, Messages},
worker::Requester,
Expand Down Expand Up @@ -1233,33 +1232,23 @@ impl RoomInfo {
}
}

/// Insert a new message event, and spawn a task for image-preview if it has an image
/// attachment.
/// Insert a new message event, and prepare for image-preview if it has an image attachment.
pub fn insert_with_preview(
&mut self,
room_id: OwnedRoomId,
store: AsyncProgramStore,
picker: Option<Picker>,
ev: RoomMessageEvent,
settings: &mut ApplicationSettings,
media: matrix_sdk::Media,
settings: &ApplicationSettings,
previews: &mut PreviewManager,
worker: &Requester,
) {
let source = picker.and_then(|_| source_from_event(&ev));
let source = source_from_event(&ev);
self.insert(ev);

if let Some((event_id, source)) = source {
if let (Some(msg), Some(image_preview)) =
(self.get_event_mut(&event_id), &settings.tunables.image_preview)
{
msg.image_preview = ImageStatus::Downloading(image_preview.size.clone());
spawn_insert_preview(
store,
room_id,
event_id,
source,
media,
settings.dirs.image_previews.clone(),
)
msg.image_preview = Some(source.clone());
previews.register_preview(settings, source, image_preview.size, worker)
}
}
}
Expand Down Expand Up @@ -1601,8 +1590,8 @@ pub struct ChatStore {
/// Information gathered by the background thread.
pub sync_info: SyncInfo,

/// Image preview "protocol" picker.
pub picker: Option<Picker>,
/// Rendered image previews.
pub previews: PreviewManager,

/// Last draw time, used to match with RoomInfo's draw_last.
pub draw_curr: Option<Instant>,
Expand All @@ -1628,7 +1617,7 @@ impl ChatStore {
ChatStore {
worker,
settings,
picker,
previews: PreviewManager::new(picker),
cmds: crate::commands::setup_commands(),
emojis: emoji_map(),

Expand Down
26 changes: 6 additions & 20 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -489,26 +489,29 @@ pub struct Notifications {

#[derive(Clone)]
pub struct ImagePreviewValues {
pub lazy_load: bool,
pub size: ImagePreviewSize,
pub protocol: Option<ImagePreviewProtocolValues>,
}

#[derive(Clone, Default, Deserialize)]
pub struct ImagePreview {
pub lazy_load: Option<bool>,
pub size: Option<ImagePreviewSize>,
pub protocol: Option<ImagePreviewProtocolValues>,
}

impl ImagePreview {
fn values(self) -> ImagePreviewValues {
ImagePreviewValues {
lazy_load: self.lazy_load.unwrap_or(true),
size: self.size.unwrap_or_default(),
protocol: self.protocol,
}
}
}

#[derive(Clone, Deserialize)]
#[derive(Clone, Copy, Deserialize, Debug)]
pub struct ImagePreviewSize {
pub width: usize,
pub height: usize,
Expand Down Expand Up @@ -683,19 +686,17 @@ pub struct DirectoryValues {
pub data: PathBuf,
pub logs: PathBuf,
pub downloads: Option<PathBuf>,
pub image_previews: PathBuf,
}

impl DirectoryValues {
fn create_dir_all(&self) -> std::io::Result<()> {
use std::fs::create_dir_all;

let Self { cache, data, logs, downloads, image_previews } = self;
let Self { cache, data, logs, downloads } = self;

create_dir_all(cache)?;
create_dir_all(data)?;
create_dir_all(logs)?;
create_dir_all(image_previews)?;

if let Some(downloads) = downloads {
create_dir_all(downloads)?;
Expand All @@ -711,7 +712,6 @@ pub struct Directories {
pub data: Option<String>,
pub logs: Option<String>,
pub downloads: Option<String>,
pub image_previews: Option<String>,
}

impl Directories {
Expand All @@ -721,7 +721,6 @@ impl Directories {
data: self.data.or(other.data),
logs: self.logs.or(other.logs),
downloads: self.downloads.or(other.downloads),
image_previews: self.image_previews.or(other.image_previews),
}
}

Expand Down Expand Up @@ -776,20 +775,7 @@ impl Directories {
})
.or_else(dirs::download_dir);

let image_previews = self
.image_previews
.map(|dir| {
let dir = shellexpand::full(&dir)
.expect("unable to expand shell variables in dirs.cache");
Path::new(dir.as_ref()).to_owned()
})
.unwrap_or_else(|| {
let mut dir = cache.clone();
dir.push("image_preview_downloads");
dir
});

DirectoryValues { cache, data, logs, downloads, image_previews }
DirectoryValues { cache, data, logs, downloads }
}
}

Expand Down
55 changes: 29 additions & 26 deletions src/message/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::ops::{Deref, DerefMut};
use chrono::{DateTime, Local as LocalTz};
use humansize::{format_size, DECIMAL};
use matrix_sdk::ruma::events::receipt::ReceiptThread;
use matrix_sdk::ruma::events::room::MediaSource;
use matrix_sdk::ruma::room_version_rules::RedactionRules;
use serde_json::json;
use unicode_width::UnicodeWidthStr;
Expand Down Expand Up @@ -58,6 +59,7 @@ use modalkit::prelude::*;
use ratatui_image::protocol::Protocol;

use crate::config::ImagePreviewSize;
use crate::preview::{ImageStatus, PreviewManager};
use crate::{
base::RoomInfo,
config::ApplicationSettings,
Expand Down Expand Up @@ -731,6 +733,7 @@ impl<'a> MessageFormatter<'a> {
text: &mut Text<'a>,
info: &'a RoomInfo,
settings: &'a ApplicationSettings,
previews: &'a PreviewManager,
) -> Option<ProtocolPreview<'a>> {
let reply_style = if settings.tunables.message_user_color {
style.patch(settings.get_user_color(&msg.sender))
Expand All @@ -740,7 +743,7 @@ impl<'a> MessageFormatter<'a> {

let width = self.width();
let w = width.saturating_sub(2);
let (mut replied, proto) = msg.show_msg(w, reply_style, true, settings);
let (mut replied, proto) = msg.show_msg(w, reply_style, true, settings, previews);
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);
Expand Down Expand Up @@ -841,20 +844,13 @@ impl<'a> MessageFormatter<'a> {
}
}

pub enum ImageStatus {
None,
Downloading(ImagePreviewSize),
Loaded(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,
pub image_preview: Option<MediaSource>,
}

impl Message {
Expand All @@ -868,7 +864,7 @@ impl Message {
timestamp,
downloaded,
html,
image_preview: ImageStatus::None,
image_preview: None,
}
}

Expand All @@ -893,7 +889,7 @@ impl Message {
}
}

fn thread_root(&self) -> Option<OwnedEventId> {
pub fn thread_root(&self) -> Option<OwnedEventId> {
let content = match &self.event {
MessageEvent::EncryptedOriginal(_) => return None,
MessageEvent::EncryptedRedacted(_) => return None,
Expand Down Expand Up @@ -1000,6 +996,7 @@ impl Message {
vwctx: &ViewportContext<MessageCursor>,
info: &'a RoomInfo,
settings: &'a ApplicationSettings,
previews: &'a PreviewManager,
) -> (Text<'a>, [Option<ProtocolPreview<'a>>; 2]) {
let width = vwctx.get_width();

Expand All @@ -1015,11 +1012,11 @@ impl Message {
.and_then(|e| info.get_event(&e));
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)
fmt.push_in_reply(r, style, &mut text, info, settings, previews)
});

// Now show the message contents, and the inlined reply if we couldn't find it above.
let (msg, proto) = self.show_msg(width, style, reply.is_some(), settings);
let (msg, proto) = self.show_msg(width, style, reply.is_some(), settings, previews);

// Given our text so far, determine the image offset.
let proto_main = proto.map(|p| {
Expand Down Expand Up @@ -1057,8 +1054,9 @@ impl Message {
vwctx: &ViewportContext<MessageCursor>,
info: &'a RoomInfo,
settings: &'a ApplicationSettings,
previews: &'a PreviewManager,
) -> Text<'a> {
self.show_with_preview(prev, selected, vwctx, info, settings).0
self.show_with_preview(prev, selected, vwctx, info, settings, previews).0
}

fn show_msg<'a>(
Expand All @@ -1067,6 +1065,7 @@ impl Message {
style: Style,
hide_reply: bool,
settings: &'a ApplicationSettings,
previews: &'a PreviewManager,
) -> (Text<'a>, Option<&'a Protocol>) {
if let Some(html) = &self.html {
(html.to_text(width, style, hide_reply, settings), None)
Expand All @@ -1081,17 +1080,21 @@ impl Message {
}

let mut proto = None;
let placeholder = match &self.image_preview {
ImageStatus::None => None,
ImageStatus::Downloading(image_preview_size) => {
placeholder_frame(Some("Downloading..."), width, image_preview_size)
},
ImageStatus::Loaded(backend) => {
proto = Some(backend);
placeholder_frame(Some("No Space..."), width, &backend.area().into())
},
ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")),
};
let placeholder =
match self.image_preview.as_ref().and_then(|source| previews.get(source)) {
None => None,
Some(ImageStatus::Queued(image_preview_size)) => {
placeholder_frame(Some("Queued..."), width, image_preview_size)
},
Some(ImageStatus::Downloading(image_preview_size)) => {
placeholder_frame(Some("Downloading..."), width, image_preview_size)
},
Some(ImageStatus::Loaded(backend)) => {
proto = Some(backend);
placeholder_frame(Some("No Space..."), width, &backend.area().into())
},
Some(ImageStatus::Error(err)) => Some(format!("[Image error: {err}]\n")),
};

if let Some(placeholder) = placeholder {
msg.to_mut().insert_str(0, &placeholder);
Expand Down Expand Up @@ -1143,7 +1146,7 @@ impl Message {
self.event.redact(redaction, rules);
self.html = None;
self.downloaded = false;
self.image_preview = ImageStatus::None;
self.image_preview = None;
}
}

Expand Down
Loading
Loading