Skip to content

Commit 3a921ce

Browse files
committed
Create PreviewManager
1 parent 8f21aee commit 3a921ce

File tree

8 files changed

+254
-294
lines changed

8 files changed

+254
-294
lines changed

docs/iamb.5

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,13 @@ Enable image previews and configure it.
138138
An empty object will enable the feature with default settings, omitting it will disable the feature.
139139
The available fields in this object are:
140140
.Bl -tag -width Ds
141+
.It Sy lazy_load
142+
If
143+
.Sy true
144+
(the default), download and render image previews when viewing a message with an image.
145+
If
146+
.Sy false ,
147+
load previews as soon as a message with an image is received.
141148
.It Sy size
142149
An optional object with
143150
.Sy width
@@ -538,8 +545,6 @@ Configured as an object under the key
538545
.It Sy cache
539546
Specifies where to store assets and temporary data in.
540547
(For example,
541-
.Sy image_preview
542-
and
543548
.Sy logs
544549
will also go in here by default.)
545550
Defaults to
@@ -555,11 +560,6 @@ Specifies where to store downloaded files.
555560
Defaults to
556561
.Ev $XDG_DOWNLOAD_DIR .
557562

558-
.It Sy image_previews
559-
Specifies where to store automatically downloaded image previews.
560-
Defaults to
561-
.Ev ${cache}/image_preview_downloads .
562-
563563
.It Sy logs
564564
Specifies where to store log files.
565565
Defaults to

src/base.rs

Lines changed: 11 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,8 @@ use modalkit::{
9191
};
9292

9393
use crate::config::ImagePreviewProtocolValues;
94-
use crate::message::ImageStatus;
9594
use crate::notifications::NotificationHandle;
96-
use crate::preview::{source_from_event, spawn_insert_preview};
95+
use crate::preview::{source_from_event, PreviewManager};
9796
use crate::{
9897
message::{Message, MessageEvent, MessageKey, MessageTimeStamp, Messages},
9998
worker::Requester,
@@ -1230,34 +1229,23 @@ impl RoomInfo {
12301229
}
12311230
}
12321231

1233-
/// Insert a new message event, and spawn a task for image-preview if it has an image
1234-
/// attachment.
1232+
/// Insert a new message event, and prepare for image-preview if it has an image attachment.
12351233
pub fn insert_with_preview(
12361234
&mut self,
1237-
room_id: OwnedRoomId,
1238-
store: AsyncProgramStore,
1239-
picker: Option<Arc<Picker>>,
12401235
ev: RoomMessageEvent,
1241-
settings: &mut ApplicationSettings,
1242-
media: matrix_sdk::Media,
1236+
settings: &ApplicationSettings,
1237+
previews: &mut PreviewManager,
1238+
worker: &Requester,
12431239
) {
1244-
let source = picker.and_then(|_| source_from_event(&ev));
1240+
let source = source_from_event(&ev);
12451241
self.insert(ev);
12461242

12471243
if let Some((event_id, source)) = source {
12481244
if let (Some(msg), Some(image_preview)) =
12491245
(self.get_event_mut(&event_id), &settings.tunables.image_preview)
12501246
{
1251-
msg.image_preview = ImageStatus::Downloading(image_preview.size.clone());
1252-
spawn_insert_preview(
1253-
store,
1254-
room_id,
1255-
event_id,
1256-
source,
1257-
media,
1258-
settings.dirs.image_previews.clone(),
1259-
image_preview.size.clone(),
1260-
)
1247+
msg.image_preview = Some(source.clone());
1248+
previews.register_preview(settings, source, image_preview.size, worker)
12611249
}
12621250
}
12631251
}
@@ -1572,8 +1560,8 @@ pub struct ChatStore {
15721560
/// Information gathered by the background thread.
15731561
pub sync_info: SyncInfo,
15741562

1575-
/// Image preview "protocol" picker.
1576-
pub picker: Option<Arc<Picker>>,
1563+
/// Rendered image previews.
1564+
pub previews: PreviewManager,
15771565

15781566
/// Last draw time, used to match with RoomInfo's draw_last.
15791567
pub draw_curr: Option<Instant>,
@@ -1599,7 +1587,7 @@ impl ChatStore {
15991587
ChatStore {
16001588
worker,
16011589
settings,
1602-
picker: picker.map(Into::into),
1590+
previews: PreviewManager::new(picker),
16031591
cmds: crate::commands::setup_commands(),
16041592
emojis: emoji_map(),
16051593

src/config.rs

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -489,26 +489,29 @@ pub struct Notifications {
489489

490490
#[derive(Clone)]
491491
pub struct ImagePreviewValues {
492+
pub lazy_load: bool,
492493
pub size: ImagePreviewSize,
493494
pub protocol: Option<ImagePreviewProtocolValues>,
494495
}
495496

496497
#[derive(Clone, Default, Deserialize)]
497498
pub struct ImagePreview {
499+
pub lazy_load: Option<bool>,
498500
pub size: Option<ImagePreviewSize>,
499501
pub protocol: Option<ImagePreviewProtocolValues>,
500502
}
501503

502504
impl ImagePreview {
503505
fn values(self) -> ImagePreviewValues {
504506
ImagePreviewValues {
507+
lazy_load: self.lazy_load.unwrap_or(true),
505508
size: self.size.unwrap_or_default(),
506509
protocol: self.protocol,
507510
}
508511
}
509512
}
510513

511-
#[derive(Clone, Deserialize)]
514+
#[derive(Clone, Copy, Deserialize, Debug)]
512515
pub struct ImagePreviewSize {
513516
pub width: usize,
514517
pub height: usize,
@@ -683,19 +686,17 @@ pub struct DirectoryValues {
683686
pub data: PathBuf,
684687
pub logs: PathBuf,
685688
pub downloads: Option<PathBuf>,
686-
pub image_previews: PathBuf,
687689
}
688690

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

693-
let Self { cache, data, logs, downloads, image_previews } = self;
695+
let Self { cache, data, logs, downloads } = self;
694696

695697
create_dir_all(cache)?;
696698
create_dir_all(data)?;
697699
create_dir_all(logs)?;
698-
create_dir_all(image_previews)?;
699700

700701
if let Some(downloads) = downloads {
701702
create_dir_all(downloads)?;
@@ -711,7 +712,6 @@ pub struct Directories {
711712
pub data: Option<String>,
712713
pub logs: Option<String>,
713714
pub downloads: Option<String>,
714-
pub image_previews: Option<String>,
715715
}
716716

717717
impl Directories {
@@ -721,7 +721,6 @@ impl Directories {
721721
data: self.data.or(other.data),
722722
logs: self.logs.or(other.logs),
723723
downloads: self.downloads.or(other.downloads),
724-
image_previews: self.image_previews.or(other.image_previews),
725724
}
726725
}
727726

@@ -776,20 +775,7 @@ impl Directories {
776775
})
777776
.or_else(dirs::download_dir);
778777

779-
let image_previews = self
780-
.image_previews
781-
.map(|dir| {
782-
let dir = shellexpand::full(&dir)
783-
.expect("unable to expand shell variables in dirs.cache");
784-
Path::new(dir.as_ref()).to_owned()
785-
})
786-
.unwrap_or_else(|| {
787-
let mut dir = cache.clone();
788-
dir.push("image_preview_downloads");
789-
dir
790-
});
791-
792-
DirectoryValues { cache, data, logs, downloads, image_previews }
778+
DirectoryValues { cache, data, logs, downloads }
793779
}
794780
}
795781

src/message/mod.rs

Lines changed: 29 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use std::ops::{Deref, DerefMut};
1010

1111
use chrono::{DateTime, Local as LocalTz};
1212
use humansize::{format_size, DECIMAL};
13-
use image::ImageReader;
1413
use matrix_sdk::ruma::events::receipt::ReceiptThread;
14+
use matrix_sdk::ruma::events::room::MediaSource;
1515
use matrix_sdk::ruma::room_version_rules::RedactionRules;
1616
use serde_json::json;
1717
use unicode_width::UnicodeWidthStr;
@@ -59,6 +59,7 @@ use modalkit::prelude::*;
5959
use ratatui_image::protocol::Protocol;
6060

6161
use crate::config::ImagePreviewSize;
62+
use crate::preview::{ImageStatus, PreviewManager};
6263
use crate::{
6364
base::RoomInfo,
6465
config::ApplicationSettings,
@@ -732,6 +733,7 @@ impl<'a> MessageFormatter<'a> {
732733
text: &mut Text<'a>,
733734
info: &'a RoomInfo,
734735
settings: &'a ApplicationSettings,
736+
previews: &'a PreviewManager,
735737
) -> Option<ProtocolPreview<'a>> {
736738
let reply_style = if settings.tunables.message_user_color {
737739
style.patch(settings.get_user_color(&msg.sender))
@@ -741,7 +743,7 @@ impl<'a> MessageFormatter<'a> {
741743

742744
let width = self.width();
743745
let w = width.saturating_sub(2);
744-
let (mut replied, proto) = msg.show_msg(w, reply_style, true, settings);
746+
let (mut replied, proto) = msg.show_msg(w, reply_style, true, settings, previews);
745747
let mut sender = msg.sender_span(info, self.settings);
746748
let sender_width = UnicodeWidthStr::width(sender.content.as_ref());
747749
let trailing = w.saturating_sub(sender_width + 1);
@@ -842,21 +844,13 @@ impl<'a> MessageFormatter<'a> {
842844
}
843845
}
844846

845-
pub enum ImageStatus {
846-
None,
847-
Downloading(ImagePreviewSize),
848-
Loading(Option<ImageReader<std::io::Cursor<Vec<u8>>>>, ImagePreviewSize),
849-
Loaded(Protocol),
850-
Error(String),
851-
}
852-
853847
pub struct Message {
854848
pub event: MessageEvent,
855849
pub sender: OwnedUserId,
856850
pub timestamp: MessageTimeStamp,
857851
pub downloaded: bool,
858852
pub html: Option<StyleTree>,
859-
pub image_preview: ImageStatus,
853+
pub image_preview: Option<MediaSource>,
860854
}
861855

862856
impl Message {
@@ -870,7 +864,7 @@ impl Message {
870864
timestamp,
871865
downloaded,
872866
html,
873-
image_preview: ImageStatus::None,
867+
image_preview: None,
874868
}
875869
}
876870

@@ -895,7 +889,7 @@ impl Message {
895889
}
896890
}
897891

898-
fn thread_root(&self) -> Option<OwnedEventId> {
892+
pub fn thread_root(&self) -> Option<OwnedEventId> {
899893
let content = match &self.event {
900894
MessageEvent::EncryptedOriginal(_) => return None,
901895
MessageEvent::EncryptedRedacted(_) => return None,
@@ -1002,6 +996,7 @@ impl Message {
1002996
vwctx: &ViewportContext<MessageCursor>,
1003997
info: &'a RoomInfo,
1004998
settings: &'a ApplicationSettings,
999+
previews: &'a PreviewManager,
10051000
) -> (Text<'a>, [Option<ProtocolPreview<'a>>; 2]) {
10061001
let width = vwctx.get_width();
10071002

@@ -1017,11 +1012,11 @@ impl Message {
10171012
.and_then(|e| info.get_event(&e));
10181013
let proto_reply = reply.as_ref().and_then(|r| {
10191014
// Format the reply header, push it into the `Text` buffer, and get any image.
1020-
fmt.push_in_reply(r, style, &mut text, info, settings)
1015+
fmt.push_in_reply(r, style, &mut text, info, settings, previews)
10211016
});
10221017

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

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

10661062
fn show_msg<'a>(
@@ -1069,6 +1065,7 @@ impl Message {
10691065
style: Style,
10701066
hide_reply: bool,
10711067
settings: &'a ApplicationSettings,
1068+
previews: &'a PreviewManager,
10721069
) -> (Text<'a>, Option<&'a Protocol>) {
10731070
if let Some(html) = &self.html {
10741071
(html.to_text(width, style, hide_reply, settings), None)
@@ -1083,20 +1080,21 @@ impl Message {
10831080
}
10841081

10851082
let mut proto = None;
1086-
let placeholder = match &self.image_preview {
1087-
ImageStatus::None => None,
1088-
ImageStatus::Downloading(image_preview_size) => {
1089-
placeholder_frame(Some("Downloading..."), width, image_preview_size)
1090-
},
1091-
ImageStatus::Loading(_, image_preview_size) => {
1092-
placeholder_frame(Some("Loading..."), width, image_preview_size)
1093-
},
1094-
ImageStatus::Loaded(backend) => {
1095-
proto = Some(backend);
1096-
placeholder_frame(Some("No Space..."), width, &backend.area().into())
1097-
},
1098-
ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")),
1099-
};
1083+
let placeholder =
1084+
match self.image_preview.as_ref().and_then(|source| previews.get(source)) {
1085+
None => None,
1086+
Some(ImageStatus::Queued(image_preview_size)) => {
1087+
placeholder_frame(Some("Queued..."), width, image_preview_size)
1088+
},
1089+
Some(ImageStatus::Downloading(image_preview_size)) => {
1090+
placeholder_frame(Some("Downloading..."), width, image_preview_size)
1091+
},
1092+
Some(ImageStatus::Loaded(backend)) => {
1093+
proto = Some(backend);
1094+
placeholder_frame(Some("No Space..."), width, &backend.area().into())
1095+
},
1096+
Some(ImageStatus::Error(err)) => Some(format!("[Image error: {err}]\n")),
1097+
};
11001098

11011099
if let Some(placeholder) = placeholder {
11021100
msg.to_mut().insert_str(0, &placeholder);
@@ -1148,7 +1146,7 @@ impl Message {
11481146
self.event.redact(redaction, rules);
11491147
self.html = None;
11501148
self.downloaded = false;
1151-
self.image_preview = ImageStatus::None;
1149+
self.image_preview = None;
11521150
}
11531151
}
11541152

0 commit comments

Comments
 (0)