diff --git a/melib/src/backends.rs b/melib/src/backends.rs index 28336465..0365db09 100644 --- a/melib/src/backends.rs +++ b/melib/src/backends.rs @@ -436,7 +436,7 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync { /// let operation = Box::new(FooOp {}); /// ``` pub trait BackendOp: ::std::fmt::Debug + ::std::marker::Send { - fn as_bytes(&mut self) -> Result<&[u8]>; + fn as_bytes(&mut self) -> ResultFuture>; fn fetch_flags(&self) -> ResultFuture; fn set_flag(&mut self, flag: Flag, value: bool) -> ResultFuture<()>; fn set_tag(&mut self, tag: String, value: bool) -> ResultFuture<()>; @@ -458,7 +458,7 @@ impl ReadOnlyOp { } impl BackendOp for ReadOnlyOp { - fn as_bytes(&mut self) -> Result<&[u8]> { + fn as_bytes(&mut self) -> ResultFuture> { self.op.as_bytes() } fn fetch_flags(&self) -> ResultFuture { diff --git a/melib/src/backends/imap/operations.rs b/melib/src/backends/imap/operations.rs index da9c1a28..824f486c 100644 --- a/melib/src/backends/imap/operations.rs +++ b/melib/src/backends/imap/operations.rs @@ -31,12 +31,7 @@ use std::sync::{Arc, Mutex}; #[derive(Debug, Clone)] pub struct ImapOp { uid: usize, - bytes: Option, - headers: Option, - body: Option, - mailbox_path: String, mailbox_hash: MailboxHash, - flags: Arc>>, connection: Arc>, uid_store: Arc, } @@ -52,56 +47,43 @@ impl ImapOp { ImapOp { uid, connection, - bytes: None, - headers: None, - body: None, - mailbox_path, mailbox_hash, - flags: Arc::new(FutureMutex::new(None)), uid_store, } } } impl BackendOp for ImapOp { - fn as_bytes(&mut self) -> Result<&[u8]> { - if self.bytes.is_none() { + fn as_bytes(&mut self) -> ResultFuture> { + let mut bytes_cache = self.uid_store.byte_cache.lock()?; + let cache = bytes_cache.entry(self.uid).or_default(); + if cache.bytes.is_none() { + let mut response = String::with_capacity(8 * 1024); + { + let mut conn = try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; + conn.examine_mailbox(self.mailbox_hash, &mut response)?; + conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?; + conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?; + } + debug!( + "fetch response is {} bytes and {} lines", + response.len(), + response.lines().collect::>().len() + ); + let UidFetchResponse { + uid, flags, body, .. + } = protocol_parser::uid_fetch_response(&response)?.1; + assert_eq!(uid, self.uid); + assert!(body.is_some()); let mut bytes_cache = self.uid_store.byte_cache.lock()?; let cache = bytes_cache.entry(self.uid).or_default(); - if cache.bytes.is_some() { - self.bytes = cache.bytes.clone(); - } else { - drop(cache); - drop(bytes_cache); - let mut response = String::with_capacity(8 * 1024); - { - let mut conn = - try_lock(&self.connection, Some(std::time::Duration::new(2, 0)))?; - conn.examine_mailbox(self.mailbox_hash, &mut response, false)?; - conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes())?; - conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED)?; - } - debug!( - "fetch response is {} bytes and {} lines", - response.len(), - response.lines().collect::>().len() - ); - let UidFetchResponse { - uid, flags, body, .. - } = protocol_parser::uid_fetch_response(&response)?.1; - assert_eq!(uid, self.uid); - assert!(body.is_some()); - let mut bytes_cache = self.uid_store.byte_cache.lock()?; - let cache = bytes_cache.entry(self.uid).or_default(); - if let Some((flags, _)) = flags { - cache.flags = Some(flags); - } - cache.bytes = - Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() }); - self.bytes = cache.bytes.clone(); + if let Some((flags, _)) = flags { + cache.flags = Some(flags); } + cache.bytes = Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() }); } - Ok(self.bytes.as_ref().unwrap().as_bytes()) + let ret = cache.bytes.clone().unwrap().into_bytes(); + Ok(Box::pin(async move { Ok(ret) })) } fn fetch_flags(&self) -> ResultFuture { @@ -109,13 +91,9 @@ impl BackendOp for ImapOp { let mailbox_hash = self.mailbox_hash; let uid = self.uid; let uid_store = self.uid_store.clone(); - let flags = self.flags.clone(); let mut response = String::with_capacity(8 * 1024); Ok(Box::pin(async move { - if let Some(val) = *flags.lock().await { - return Ok(val); - } let exists_in_cache = { let mut bytes_cache = uid_store.byte_cache.lock()?; let cache = bytes_cache.entry(uid).or_default(); @@ -158,8 +136,6 @@ impl BackendOp for ImapOp { let val = cache.flags; val }; - let mut f = flags.lock().await; - *f = val; Ok(val.unwrap()) } })) diff --git a/melib/src/backends/imap_async.rs b/melib/src/backends/imap_async.rs index 9a56d3d8..9549e4c6 100644 --- a/melib/src/backends/imap_async.rs +++ b/melib/src/backends/imap_async.rs @@ -376,9 +376,6 @@ impl MailBackend for ImapType { }; Ok(Box::new(ImapOp::new( uid, - self.uid_store.mailboxes.read().unwrap()[&mailbox_hash] - .imap_path() - .to_string(), mailbox_hash, self.connection.clone(), self.uid_store.clone(), diff --git a/melib/src/backends/imap_async/operations.rs b/melib/src/backends/imap_async/operations.rs index 2c5f6840..3be4eb73 100644 --- a/melib/src/backends/imap_async/operations.rs +++ b/melib/src/backends/imap_async/operations.rs @@ -30,12 +30,7 @@ use std::sync::Arc; #[derive(Debug, Clone)] pub struct ImapOp { uid: usize, - bytes: Option, - headers: Option, - body: Option, - mailbox_path: String, mailbox_hash: MailboxHash, - flags: Arc>>, connection: Arc>, uid_store: Arc, } @@ -43,7 +38,6 @@ pub struct ImapOp { impl ImapOp { pub fn new( uid: usize, - mailbox_path: String, mailbox_hash: MailboxHash, connection: Arc>, uid_store: Arc, @@ -51,65 +45,63 @@ impl ImapOp { ImapOp { uid, connection, - bytes: None, - headers: None, - body: None, - mailbox_path, mailbox_hash, - flags: Arc::new(FutureMutex::new(None)), uid_store, } } } impl BackendOp for ImapOp { - fn as_bytes(&mut self) -> Result<&[u8]> { - if self.bytes.is_none() { - let mut bytes_cache = self.uid_store.byte_cache.lock()?; - let cache = bytes_cache.entry(self.uid).or_default(); - if cache.bytes.is_some() { - self.bytes = cache.bytes.clone(); - } else { - drop(cache); - drop(bytes_cache); - let ret: Result<()> = futures::executor::block_on(async { - let mut response = String::with_capacity(8 * 1024); - { - let mut conn = self.connection.lock().await; - conn.examine_mailbox(self.mailbox_hash, &mut response, false) - .await?; - conn.send_command( - format!("UID FETCH {} (FLAGS RFC822)", self.uid).as_bytes(), - ) + fn as_bytes(&mut self) -> ResultFuture> { + let mut response = String::with_capacity(8 * 1024); + let connection = self.connection.clone(); + let mailbox_hash = self.mailbox_hash; + let uid = self.uid; + let uid_store = self.uid_store.clone(); + Ok(Box::pin(async move { + let exists_in_cache = { + let mut bytes_cache = uid_store.byte_cache.lock()?; + let cache = bytes_cache.entry(uid).or_default(); + cache.bytes.is_some() + }; + if !exists_in_cache { + let mut response = String::with_capacity(8 * 1024); + { + let mut conn = connection.lock().await; + conn.examine_mailbox(mailbox_hash, &mut response, false) .await?; - conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED) - .await?; - } - debug!( - "fetch response is {} bytes and {} lines", - response.len(), - response.lines().collect::>().len() - ); - let UidFetchResponse { - uid, flags, body, .. - } = protocol_parser::uid_fetch_response(&response)?.1; - assert_eq!(uid, self.uid); - assert!(body.is_some()); - let mut bytes_cache = self.uid_store.byte_cache.lock()?; - let cache = bytes_cache.entry(self.uid).or_default(); - if let Some((flags, _)) = flags { - //self.flags.set(Some(flags)); - cache.flags = Some(flags); - } - cache.bytes = - Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() }); - self.bytes = cache.bytes.clone(); - Ok(()) - }); - ret?; + conn.send_command(format!("UID FETCH {} (FLAGS RFC822)", uid).as_bytes()) + .await?; + conn.read_response(&mut response, RequiredResponses::FETCH_REQUIRED) + .await?; + } + debug!( + "fetch response is {} bytes and {} lines", + response.len(), + response.lines().collect::>().len() + ); + let UidFetchResponse { + uid: _uid, + flags: _flags, + body, + .. + } = protocol_parser::uid_fetch_response(&response)?.1; + assert_eq!(_uid, uid); + assert!(body.is_some()); + let mut bytes_cache = uid_store.byte_cache.lock()?; + let cache = bytes_cache.entry(uid).or_default(); + if let Some((_flags, _)) = _flags { + //flags.lock().await.set(Some(_flags)); + cache.flags = Some(_flags); + } + cache.bytes = + Some(unsafe { std::str::from_utf8_unchecked(body.unwrap()).to_string() }); } - } - Ok(self.bytes.as_ref().unwrap().as_bytes()) + let mut bytes_cache = uid_store.byte_cache.lock()?; + let cache = bytes_cache.entry(uid).or_default(); + let ret = cache.bytes.clone().unwrap().into_bytes(); + Ok(ret) + })) } fn fetch_flags(&self) -> ResultFuture { @@ -118,12 +110,8 @@ impl BackendOp for ImapOp { let mailbox_hash = self.mailbox_hash; let uid = self.uid; let uid_store = self.uid_store.clone(); - let flags = self.flags.clone(); Ok(Box::pin(async move { - if let Some(val) = *flags.lock().await { - return Ok(val); - } let exists_in_cache = { let mut bytes_cache = uid_store.byte_cache.lock()?; let cache = bytes_cache.entry(uid).or_default(); @@ -158,7 +146,6 @@ impl BackendOp for ImapOp { } let (_uid, (_flags, _)) = v[0]; assert_eq!(uid, uid); - *flags.lock().await = Some(_flags); let mut bytes_cache = uid_store.byte_cache.lock()?; let cache = bytes_cache.entry(uid).or_default(); cache.flags = Some(_flags); @@ -170,8 +157,6 @@ impl BackendOp for ImapOp { let val = cache.flags; val }; - let mut f = flags.lock().await; - *f = val; Ok(val.unwrap()) } })) diff --git a/melib/src/backends/jmap/operations.rs b/melib/src/backends/jmap/operations.rs index 7e8bd751..a9d74ee4 100644 --- a/melib/src/backends/jmap/operations.rs +++ b/melib/src/backends/jmap/operations.rs @@ -56,7 +56,7 @@ impl JmapOp { } impl BackendOp for JmapOp { - fn as_bytes(&mut self) -> Result<&[u8]> { + fn as_bytes(&mut self) -> ResultFuture> { if self.bytes.is_none() { let mut store_lck = self.store.write().unwrap(); if !(store_lck.byte_cache.contains_key(&self.hash) @@ -86,7 +86,8 @@ impl BackendOp for JmapOp { } self.bytes = store_lck.byte_cache[&self.hash].bytes.clone(); } - Ok(&self.bytes.as_ref().unwrap().as_bytes()) + let ret = self.bytes.as_ref().unwrap().as_bytes().to_vec(); + Ok(Box::pin(async move { Ok(ret) })) } fn fetch_flags(&self) -> ResultFuture { diff --git a/melib/src/backends/maildir.rs b/melib/src/backends/maildir.rs index eac719a4..45e87e46 100644 --- a/melib/src/backends/maildir.rs +++ b/melib/src/backends/maildir.rs @@ -90,12 +90,13 @@ impl MaildirOp { } impl<'a> BackendOp for MaildirOp { - fn as_bytes(&mut self) -> Result<&[u8]> { + fn as_bytes(&mut self) -> ResultFuture> { if self.slice.is_none() { self.slice = Some(Mmap::open_path(self.path(), Protection::Read)?); } /* Unwrap is safe since we use ? above. */ - Ok(unsafe { self.slice.as_ref().unwrap().as_slice() }) + let ret = Ok((unsafe { self.slice.as_ref().unwrap().as_slice() }).to_vec()); + Ok(Box::pin(async move { ret })) } fn fetch_flags(&self) -> ResultFuture { diff --git a/melib/src/backends/mbox.rs b/melib/src/backends/mbox.rs index 98d1d097..8771600e 100644 --- a/melib/src/backends/mbox.rs +++ b/melib/src/backends/mbox.rs @@ -182,14 +182,16 @@ impl MboxOp { } impl BackendOp for MboxOp { - fn as_bytes(&mut self) -> Result<&[u8]> { + fn as_bytes(&mut self) -> ResultFuture> { if self.slice.is_none() { self.slice = Some(Mmap::open_path(&self.path, Protection::Read)?); } /* Unwrap is safe since we use ? above. */ - Ok(unsafe { + let ret = Ok((unsafe { &self.slice.as_ref().unwrap().as_slice()[self.offset..self.offset + self.length] }) + .to_vec()); + Ok(Box::pin(async move { ret })) } fn fetch_flags(&self) -> ResultFuture { diff --git a/melib/src/backends/notmuch.rs b/melib/src/backends/notmuch.rs index 519b4d53..25db8ea8 100644 --- a/melib/src/backends/notmuch.rs +++ b/melib/src/backends/notmuch.rs @@ -646,7 +646,7 @@ struct NotmuchOp { } impl BackendOp for NotmuchOp { - fn as_bytes(&mut self) -> Result<&[u8]> { + fn as_bytes(&mut self) -> ResultFuture> { let mut message: *mut notmuch_message_t = std::ptr::null_mut(); let index_lck = self.index.write().unwrap(); unsafe { @@ -662,7 +662,8 @@ impl BackendOp for NotmuchOp { let mut response = String::new(); f.read_to_string(&mut response)?; self.bytes = Some(response); - Ok(self.bytes.as_ref().unwrap().as_bytes()) + let ret = Ok(self.bytes.as_ref().unwrap().as_bytes().to_vec()); + Ok(Box::pin(async move { ret })) } fn fetch_flags(&self) -> ResultFuture { diff --git a/melib/src/email.rs b/melib/src/email.rs index 56472079..c69c61a8 100644 --- a/melib/src/email.rs +++ b/melib/src/email.rs @@ -229,8 +229,8 @@ impl Envelope { pub fn from_token(mut operation: Box, hash: EnvelopeHash) -> Result { let mut e = Envelope::new(hash); e.flags = futures::executor::block_on(operation.fetch_flags()?)?; - let bytes = operation.as_bytes()?; - e.populate_headers(bytes)?; + let bytes = futures::executor::block_on(operation.as_bytes()?)?; + e.populate_headers(&bytes)?; Ok(e) } @@ -413,11 +413,6 @@ impl Envelope { _strings.join(", ") } - /// Requests bytes from backend and thus can fail - pub fn bytes(&self, mut operation: Box) -> Result> { - operation.as_bytes().map(|v| v.to_vec()) - } - pub fn body_bytes(&self, bytes: &[u8]) -> Attachment { let builder = AttachmentBuilder::new(bytes); builder.build() @@ -439,8 +434,8 @@ impl Envelope { /// Requests bytes from backend and thus can fail pub fn body(&self, mut operation: Box) -> Result { debug!("searching body for {:?}", self.message_id_display()); - let file = operation.as_bytes()?; - Ok(self.body_bytes(file)) + let bytes = futures::executor::block_on(operation.as_bytes()?)?; + Ok(self.body_bytes(&bytes)) } pub fn subject(&self) -> Cow { diff --git a/melib/src/email/compose.rs b/melib/src/email/compose.rs index d452fd29..3b820415 100644 --- a/melib/src/email/compose.rs +++ b/melib/src/email/compose.rs @@ -138,8 +138,8 @@ impl Draft { let mut ret = Draft::default(); //TODO: Inform user if error { - let bytes = op.as_bytes().unwrap_or(&[]); - for (k, v) in envelope.headers(bytes).unwrap_or_else(|_| Vec::new()) { + let bytes = futures::executor::block_on(op.as_bytes()?)?; + for (k, v) in envelope.headers(&bytes).unwrap_or_else(|_| Vec::new()) { if ignore_header(k.as_bytes()) { continue; } diff --git a/src/components/mail/compose.rs b/src/components/mail/compose.rs index 5cb22523..6423a36b 100644 --- a/src/components/mail/compose.rs +++ b/src/components/mail/compose.rs @@ -238,9 +238,10 @@ impl Composer { Some(NotificationType::ERROR), )); } - Ok(mut op) => { - let parent_bytes = op.as_bytes(); - ret.draft = Draft::new_reply(&parent_message, parent_bytes.unwrap()); + Ok(op) => { + //FIXME + //let parent_bytes = op.as_bytes(); + //ret.draft = Draft::new_reply(&parent_message, parent_bytes.unwrap()); } } let subject = parent_message.subject(); diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index f27b800e..9fe869cb 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -203,6 +203,8 @@ pub trait MailListingTrait: ListingTrait { } ListingAction::CopyTo(ref mailbox_path) => { drop(envelope); + /* + * FIXME match account .mailbox_by_path(mailbox_path) .and_then(|mailbox_hash| { @@ -219,10 +221,13 @@ pub trait MailListingTrait: ListingTrait { } Ok(fut) => {} } + */ continue; } ListingAction::CopyToOtherAccount(ref account_name, ref mailbox_path) => { drop(envelope); + /* + * FIXME if let Err(err) = op.as_bytes().map(|b| b.to_vec()).and_then(|bytes| { let account_pos = context .accounts @@ -245,10 +250,13 @@ pub trait MailListingTrait: ListingTrait { )); return; } + */ continue; } ListingAction::MoveTo(ref mailbox_path) => { drop(envelope); + /* + * FIXME if let Err(err) = account .mailbox_by_path(mailbox_path) @@ -264,10 +272,12 @@ pub trait MailListingTrait: ListingTrait { )); return; } + */ continue; } ListingAction::MoveToOtherAccount(ref account_name, ref mailbox_path) => { drop(envelope); + /* FIXME if let Err(err) = op .as_bytes() .map(|b| b.to_vec()) @@ -302,26 +312,37 @@ pub trait MailListingTrait: ListingTrait { )); return; } + */ continue; } ListingAction::Tag(Remove(ref tag_str)) => { - if let Err(err) = op.set_tag(tag_str.to_string(), false) { - context.replies.push_back(UIEvent::Notification( - Some("Could not set tag.".to_string()), - err.to_string(), - Some(NotificationType::ERROR), - )); - return; + match op.set_tag(tag_str.to_string(), false) { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("Could not set tag.".to_string()), + err.to_string(), + Some(NotificationType::ERROR), + )); + return; + } + Ok(fut) => { + //FIXME + } } } ListingAction::Tag(Add(ref tag_str)) => { - if let Err(err) = op.set_tag(tag_str.to_string(), true) { - context.replies.push_back(UIEvent::Notification( - Some("Could not set tag.".to_string()), - err.to_string(), - Some(NotificationType::ERROR), - )); - return; + match op.set_tag(tag_str.to_string(), true) { + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("Could not set tag.".to_string()), + err.to_string(), + Some(NotificationType::ERROR), + )); + return; + } + Ok(fut) => { + // FIXME + } } } _ => unreachable!(), diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index bed42fa2..98e5ddcc 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -214,7 +214,7 @@ impl MailListingTrait for PlainListing { let env_hash = self.get_env_under_cursor(self.cursor_pos.2, context); let temp = (self.new_cursor_pos.0, self.new_cursor_pos.1, env_hash); if !force && old_cursor_pos == self.new_cursor_pos { - self.view.update(temp); + self.view.update(temp, context); } else if self.unfocused { self.view = MailView::new(temp, None, None, context); } diff --git a/src/components/mail/listing/thread.rs b/src/components/mail/listing/thread.rs index 243ac747..32eb40ab 100644 --- a/src/components/mail/listing/thread.rs +++ b/src/components/mail/listing/thread.rs @@ -632,7 +632,7 @@ impl Component for ThreadListing { ); if let Some(ref mut v) = self.view { - v.update(coordinates); + v.update(coordinates, context); } else { self.view = Some(MailView::new(coordinates, None, None, context)); } diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index 362164af..77dd48d9 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -20,9 +20,13 @@ */ use super::*; +use crate::conf::accounts::JobRequest; +use crate::jobs::{oneshot, JobId}; use melib::list_management; use melib::parser::BytesExt; use smallvec::SmallVec; +use std::collections::HashSet; +use std::io::Write; use std::convert::TryFrom; use std::process::{Command, Stdio}; @@ -98,11 +102,31 @@ pub struct MailView { headers_cursor: usize, force_draw_headers: bool, theme_default: ThemeAttribute, + active_jobs: HashSet, + state: MailViewState, cmd_buf: String, id: ComponentId, } +#[derive(Debug)] +pub enum MailViewState { + Init, + LoadingBody { + job_id: JobId, + chan: oneshot::Receiver>>, + }, + Loaded { + body: Result>, + }, +} + +impl Default for MailViewState { + fn default() -> Self { + MailViewState::Init + } +} + impl Clone for MailView { fn clone(&self) -> Self { MailView { @@ -111,6 +135,8 @@ impl Clone for MailView { pager: self.pager.clone(), mode: ViewMode::Normal, attachment_tree: self.attachment_tree.clone(), + state: MailViewState::default(), + active_jobs: self.active_jobs.clone(), ..*self } } @@ -129,9 +155,9 @@ impl MailView { coordinates: (usize, MailboxHash, EnvelopeHash), pager: Option, subview: Option>, - context: &Context, + context: &mut Context, ) -> Self { - MailView { + let mut ret = MailView { coordinates, pager: pager.unwrap_or_default(), subview, @@ -146,9 +172,61 @@ impl MailView { force_draw_headers: false, theme_default: crate::conf::value(context, "mail.view.body"), + active_jobs: Default::default(), + state: MailViewState::default(), cmd_buf: String::with_capacity(4), id: ComponentId::new_v4(), + }; + + ret.init_futures(context); + ret + } + + fn init_futures(&mut self, context: &mut Context) { + debug!("init_futures"); + let account = &mut context.accounts[self.coordinates.0]; + if debug!(account.contains_key(self.coordinates.2)) { + { + match account + .operation(self.coordinates.2) + .and_then(|mut op| op.as_bytes()) + { + Ok(fut) => { + let (chan, job_id) = account.job_executor.spawn_specialized(fut); + debug!(&job_id); + self.active_jobs.insert(job_id.clone()); + account.active_jobs.insert(job_id, JobRequest::AsBytes); + self.state = MailViewState::LoadingBody { job_id, chan }; + } + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!("Could not get message: {}", err)), + )); + } + } + } + if !account.collection.get_env(self.coordinates.2).is_seen() { + match account + .operation(self.coordinates.2) + .and_then(|mut op| op.set_flag(Flag::SEEN, true)) + { + Ok(fut) => { + let (rcvr, job_id) = account.job_executor.spawn_specialized(fut); + account + .active_jobs + .insert(job_id, JobRequest::SetFlags(self.coordinates.2, rcvr)); + } + Err(e) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!( + "Could not set message as seen: {}", + e + )), + )); + } + } + } } } @@ -164,8 +242,6 @@ impl MailView { body, Some(Box::new(move |a: &'closure Attachment, v: &mut Vec| { if a.content_type().is_text_html() { - use std::io::Write; - /* FIXME: duplication with view/html.rs */ if let Some(filter_invocation) = mailbox_settings!(context[coordinates.0][&coordinates.1].pager.html_filter) @@ -285,12 +361,187 @@ impl MailView { } } - pub fn update(&mut self, new_coordinates: (usize, MailboxHash, EnvelopeHash)) { + pub fn update( + &mut self, + new_coordinates: (usize, MailboxHash, EnvelopeHash), + context: &mut Context, + ) { self.coordinates = new_coordinates; self.mode = ViewMode::Normal; self.initialised = false; + self.init_futures(context); self.set_dirty(true); } + + fn open_attachment(&mut self, lidx: usize, context: &mut Context, use_mailcap: bool) { + let attachments = if let MailViewState::Loaded { ref body } = self.state { + match body { + Ok(body) => AttachmentBuilder::new(body).build().attachments(), + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + err.to_string(), + Some(NotificationType::ERROR), + )); + log( + format!("Failed to open envelope: {}", err.to_string()), + ERROR, + ); + return; + } + } + } else { + return; + }; + if let Some(u) = attachments.get(lidx) { + if use_mailcap { + if let Ok(()) = crate::mailcap::MailcapEntry::execute(u, context) { + self.set_dirty(true); + } else { + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!( + "no mailcap entry found for {}", + u.content_type() + )))); + } + } else { + match u.content_type() { + ContentType::MessageRfc822 => { + match EnvelopeWrapper::new(u.body().to_vec()) { + Ok(wrapper) => { + context.replies.push_back(UIEvent::Action(Tab(New(Some( + Box::new(EnvelopeView::new( + wrapper, + None, + None, + self.coordinates.0, + )), + ))))); + } + Err(e) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!("{}", e)), + )); + } + } + return; + } + + ContentType::Text { .. } | ContentType::PGPSignature => { + self.mode = ViewMode::Attachment(lidx); + self.initialised = false; + self.dirty = true; + } + ContentType::Multipart { .. } => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage( + "Multipart attachments are not supported yet.".to_string(), + ), + )); + return; + } + ContentType::Other { ref name, .. } => { + let attachment_type = u.mime_type(); + let binary = query_default_app(&attachment_type); + let mut name_opt = name.as_ref().and_then(|n| { + melib::email::parser::encodings::phrase(n.as_bytes(), false) + .map(|(_, v)| v) + .ok() + .and_then(|n| String::from_utf8(n).ok()) + }); + if name_opt.is_none() { + name_opt = name.as_ref().map(|n| n.clone()); + } + if let Ok(binary) = binary { + let p = create_temp_file( + &decode(u, None), + name_opt.as_ref().map(String::as_str), + None, + true, + ); + match debug!(context.plugin_manager.activate_hook( + "attachment-view", + p.path().display().to_string().into_bytes() + )) { + Ok(crate::plugins::FilterResult::Ansi(s)) => { + if let Some(buf) = crate::terminal::ansi::ansi_to_cellbuffer(&s) + { + let raw_buf = RawBuffer::new(buf, name_opt); + self.mode = ViewMode::Ansi(raw_buf); + self.initialised = false; + self.dirty = true; + return; + } + } + Ok(crate::plugins::FilterResult::UiMessage(s)) => { + context.replies.push_back(UIEvent::Notification( + None, + s, + Some(NotificationType::ERROR), + )); + } + _ => {} + } + match Command::new(&binary) + .arg(p.path()) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + { + Ok(child) => { + context.temp_files.push(p); + context.children.push(child); + } + Err(err) => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!( + "Failed to start {}: {}", + binary.display(), + err + )), + )); + } + } + } else { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(if name.is_some() { + format!( + "Couldn't find a default application for file {} (type {})", + name.as_ref().unwrap(), + attachment_type + ) + } else { + format!( + "Couldn't find a default application for type {}", + attachment_type + ) + }), + )); + return; + } + } + ContentType::OctetStream { ref name } => { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!( + "Failed to open {}. application/octet-stream isn't supported yet", + name.as_ref().map(|n| n.as_str()).unwrap_or("file") + )), + )); + return; + } + } + } + } else { + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!( + "Attachment `{}` not found.", + lidx + )))); + return; + } + } } impl Component for MailView { @@ -302,32 +553,13 @@ impl Component for MailView { let bottom_right = bottom_right!(area); let y: usize = { - let account = &mut context.accounts[self.coordinates.0]; + let account = &context.accounts[self.coordinates.0]; if !account.contains_key(self.coordinates.2) { /* The envelope has been renamed or removed, so wait for the appropriate event to * arrive */ self.dirty = false; return; } - let (hash, is_seen) = { - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - (envelope.hash(), envelope.is_seen()) - }; - if !is_seen { - if let Err(e) = account.operation(hash).and_then(|op| { - let mut envelope: EnvelopeRefMut = - account.collection.get_env_mut(self.coordinates.2); - envelope.set_seen(op) - }) { - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::DisplayMessage(format!( - "Could not set message as seen: {}", - e - )))); - } - } - let account = &context.accounts[self.coordinates.0]; let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); let headers = crate::conf::value(context, "mail.view.headers"); @@ -550,14 +782,8 @@ impl Component for MailView { }; if !self.initialised { - self.initialised = true; - let body = { - let account = &mut context.accounts[self.coordinates.0]; - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - match account - .operation(envelope.hash()) - .and_then(|op| envelope.body(op)) - { + let bytes = if let MailViewState::Loaded { ref body } = self.state { + match body { Ok(body) => body, Err(e) => { clear_area( @@ -573,18 +799,23 @@ impl Component for MailView { e.to_string(), Some(NotificationType::ERROR), )); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - e.to_string() - ), - ERROR, - ); + log(format!("Failed to open envelope: {}", e.to_string()), ERROR); return; } } + } else { + clear_area( + grid, + (set_y(upper_left, y), bottom_right), + self.theme_default, + ); + context + .dirty_areas + .push_back((set_y(upper_left, y), bottom_right)); + return; }; + self.initialised = true; + let body = AttachmentBuilder::new(bytes).build(); match self.mode { ViewMode::Attachment(aidx) if body.attachments()[aidx].is_html() => { let attachment = &body.attachments()[aidx]; @@ -631,32 +862,13 @@ impl Component for MailView { ViewMode::Subview | ViewMode::ContactSelector(_) => {} ViewMode::Source(source) => { let text = { - let account = &context.accounts[self.coordinates.0]; - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - let mut op = match account.operation(envelope.hash()) { - Ok(op) => op, - Err(err) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to open e-mail".to_string()), - err.to_string(), - Some(NotificationType::ERROR), - )); - return; - } - }; if source == Source::Raw { - op.as_bytes() - .map(|v| String::from_utf8_lossy(v).into_owned()) - .unwrap_or_else(|e| e.to_string()) + String::from_utf8_lossy(bytes).into_owned() } else { /* Decode each header value */ - let mut ret = op - .as_bytes() - .and_then(|b| { - melib::email::parser::headers::headers(b) - .map(|(_, v)| v) - .map_err(|err| err.into()) - }) + let mut ret = melib::email::parser::headers::headers(bytes) + .map(|(_, v)| v) + .map_err(|err| err.into()) .and_then(|headers| { Ok(headers .into_iter() @@ -675,9 +887,7 @@ impl Component for MailView { .join(&b"\n"[..])) }) .map(|v| String::from_utf8_lossy(&v).into_owned()) - .unwrap_or_else(|e| e.to_string()); - drop(envelope); - drop(account); + .unwrap_or_else(|err: MeliError| err.to_string()); ret.push_str("\n\n"); ret.extend(self.attachment_to_text(&body, context).chars()); ret @@ -810,6 +1020,27 @@ impl Component for MailView { self.pager.set_dirty(true); return true; } + UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id)) + if self.active_jobs.contains(job_id) => + { + match self.state { + MailViewState::LoadingBody { + job_id: ref id, + ref mut chan, + } if job_id == id => { + let bytes_result = chan.try_recv().unwrap().unwrap(); + debug!("bytes_result"); + self.state = MailViewState::Loaded { body: bytes_result }; + } + MailViewState::Init => { + self.init_futures(context); + } + _ => {} + } + self.active_jobs.remove(job_id); + self.set_dirty(true); + return true; + } _ => { if self.pager.process_event(event, context) { return true; @@ -933,55 +1164,16 @@ impl Component for MailView { context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - - { - let account = &mut context.accounts[self.coordinates.0]; - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - let attachments = match account - .operation(envelope.hash()) - .and_then(|op| envelope.body(op)) - { - Ok(body) => body.attachments(), - Err(e) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to open e-mail".to_string()), - e.to_string(), - Some(NotificationType::ERROR), - )); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - e.to_string() - ), - ERROR, - ); - return true; - } - }; - drop(envelope); - drop(account); - if let Some(u) = attachments.get(lidx) { - if let Ok(()) = crate::mailcap::MailcapEntry::execute(u, context) { - self.set_dirty(true); - } else { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!( - "no mailcap entry found for {}", - u.content_type() - )), - )); - } - } else { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!( - "Attachment `{}` not found.", - lidx - )), - )); + match self.state { + MailViewState::LoadingBody { .. } => {} + MailViewState::Loaded { .. } => { + self.open_attachment(lidx, context, true); + } + MailViewState::Init => { + self.init_futures(context); } - return true; } + return true; } UIEvent::Input(ref key) if shortcut!(key == shortcuts[MailView::DESCRIPTION]["open_attachment"]) @@ -993,170 +1185,16 @@ impl Component for MailView { context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - - { - let account = &mut context.accounts[self.coordinates.0]; - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - - let attachments = match account - .operation(envelope.hash()) - .and_then(|op| envelope.body(op)) - { - Ok(body) => body.attachments(), - Err(e) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to open e-mail".to_string()), - e.to_string(), - Some(NotificationType::ERROR), - )); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - e.to_string() - ), - ERROR, - ); - return true; - } - }; - if let Some(u) = attachments.get(lidx) { - match u.content_type() { - ContentType::MessageRfc822 => { - match EnvelopeWrapper::new(u.body().to_vec()) { - Ok(wrapper) => { - context.replies.push_back(UIEvent::Action(Tab(New(Some( - Box::new(EnvelopeView::new( - wrapper, - None, - None, - self.coordinates.0, - )), - ))))); - } - Err(e) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!("{}", e)), - )); - } - } - return true; - } - - ContentType::Text { .. } | ContentType::PGPSignature => { - self.mode = ViewMode::Attachment(lidx); - self.initialised = false; - self.dirty = true; - } - ContentType::Multipart { .. } => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage( - "Multipart attachments are not supported yet.".to_string(), - ), - )); - return true; - } - ContentType::Other { ref name, .. } => { - let attachment_type = u.mime_type(); - let binary = query_default_app(&attachment_type); - let mut name_opt = name.as_ref().and_then(|n| { - melib::email::parser::encodings::phrase(n.as_bytes(), false) - .map(|(_, v)| v) - .ok() - .and_then(|n| String::from_utf8(n).ok()) - }); - if name_opt.is_none() { - name_opt = name.as_ref().map(|n| n.clone()); - } - if let Ok(binary) = binary { - let p = create_temp_file( - &decode(u, None), - name_opt.as_ref().map(String::as_str), - None, - true, - ); - match debug!(context.plugin_manager.activate_hook( - "attachment-view", - p.path().display().to_string().into_bytes() - )) { - Ok(crate::plugins::FilterResult::Ansi(s)) => { - if let Some(buf) = - crate::terminal::ansi::ansi_to_cellbuffer(&s) - { - let raw_buf = RawBuffer::new(buf, name_opt); - self.mode = ViewMode::Ansi(raw_buf); - self.initialised = false; - self.dirty = true; - return true; - } - } - Ok(crate::plugins::FilterResult::UiMessage(s)) => { - context.replies.push_back(UIEvent::Notification( - None, - s, - Some(NotificationType::ERROR), - )); - } - _ => {} - } - match Command::new(&binary) - .arg(p.path()) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - { - Ok(child) => { - context.temp_files.push(p); - context.children.push(child); - } - Err(err) => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!( - "Failed to start {}: {}", - binary.display(), - err - )), - )); - } - } - } else { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(if name.is_some() { - format!( - "Couldn't find a default application for file {} (type {})", - name.as_ref().unwrap(), attachment_type - ) - } else { - format!( "Couldn't find a default application for type {}", attachment_type) - } - - , - ))); - return true; - } - } - ContentType::OctetStream { ref name } => { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage( - format!( - "Failed to open {}. application/octet-stream isn't supported yet", - name.as_ref().map(|n| n.as_str()).unwrap_or("file") - ) - ), - )); - return true; - } - } - } else { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!( - "Attachment `{}` not found.", - lidx - )), - )); - return true; + match self.state { + MailViewState::LoadingBody { .. } => {} + MailViewState::Loaded { .. } => { + self.open_attachment(lidx, context, false); } - }; + MailViewState::Init => { + self.init_futures(context); + } + } + return true; } UIEvent::Input(ref key) if shortcut!(key == shortcuts[MailView::DESCRIPTION]["toggle_expand_headers"]) => @@ -1175,58 +1213,59 @@ impl Component for MailView { context .replies .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); - let url = { - let account = &mut context.accounts[self.coordinates.0]; - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - let finder = LinkFinder::new(); - let t = match account - .operation(envelope.hash()) - .and_then(|op| envelope.body(op)) - { - Ok(body) => body.text().to_string(), - Err(e) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to open e-mail".to_string()), - e.to_string(), - Some(NotificationType::ERROR), - )); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - e.to_string() - ), - ERROR, - ); - return true; - } - }; - let links: Vec = finder.links(&t).collect(); - if let Some(u) = links.get(lidx) { - u.as_str().to_string() - } else { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(format!("Link `{}` not found.", lidx)), - )); - return true; + match self.state { + MailViewState::Init => { + self.init_futures(context); } - }; + MailViewState::LoadingBody { .. } => {} + MailViewState::Loaded { ref body } => { + let url = { + let finder = LinkFinder::new(); + let t = match body { + Ok(body) => { + AttachmentBuilder::new(&body).build().text().to_string() + } + Err(e) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to open e-mail".to_string()), + e.to_string(), + Some(NotificationType::ERROR), + )); + log(e.to_string(), ERROR); + return true; + } + }; + let links: Vec = finder.links(&t).collect(); + if let Some(u) = links.get(lidx) { + u.as_str().to_string() + } else { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(format!( + "Link `{}` not found.", + lidx + )), + )); + return true; + } + }; - match Command::new("xdg-open") - .arg(url) - .stdin(Stdio::piped()) - .stdout(Stdio::piped()) - .spawn() - { - Ok(child) => { - context.children.push(child); - } - Err(err) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to launch xdg-open".to_string()), - err.to_string(), - Some(NotificationType::ERROR), - )); + match Command::new("xdg-open") + .arg(url) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + { + Ok(child) => { + context.children.push(child); + } + Err(err) => { + context.replies.push_back(UIEvent::Notification( + Some("Failed to launch xdg-open".to_string()), + err.to_string(), + Some(NotificationType::ERROR), + )); + } + } } } return true; @@ -1248,37 +1287,15 @@ impl Component for MailView { self.coordinates.2 = new_hash; } UIEvent::Action(View(ViewAction::SaveAttachment(a_i, ref path))) => { - use std::io::Write; - let account = &mut context.accounts[self.coordinates.0]; - let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - let mut op = match account.operation(envelope.hash()) { - Ok(b) => b, - Err(err) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to open e-mail".to_string()), - err.to_string(), - Some(NotificationType::ERROR), - )); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - err.to_string() - ), - ERROR, - ); - return true; - } - }; - - if a_i == 0 { - let mut path = std::path::Path::new(path).to_path_buf(); - // Save entire message as eml - if path.is_dir() { - path.push(format!("{}.eml", envelope.message_id_display())); - } - let bytes = match op.as_bytes() { - Ok(b) => b, + let account = &context.accounts[self.coordinates.0]; + if !account.contains_key(self.coordinates.2) { + /* The envelope has been renamed or removed, so wait for the appropriate event to + * arrive */ + return true; + } + let bytes = if let MailViewState::Loaded { ref body } = self.state { + match body { + Ok(body) => body, Err(err) => { context.replies.push_back(UIEvent::Notification( Some("Failed to open e-mail".to_string()), @@ -1286,16 +1303,23 @@ impl Component for MailView { Some(NotificationType::ERROR), )); log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - err.to_string() - ), + format!("Failed to open envelope: {}", err.to_string()), ERROR, ); return true; } - }; + } + } else { + return true; + }; + + if a_i == 0 { + let mut path = std::path::Path::new(path).to_path_buf(); + let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); + // Save entire message as eml + if path.is_dir() { + path.push(format!("{}.eml", envelope.message_id_display())); + } let mut f = match std::fs::File::create(&path) { Err(err) => { context.replies.push_back(UIEvent::Notification( @@ -1333,25 +1357,7 @@ impl Component for MailView { return true; } - let attachments = match envelope.body(op) { - Ok(body) => body.attachments(), - Err(e) => { - context.replies.push_back(UIEvent::Notification( - Some("Failed to open e-mail".to_string()), - e.to_string(), - Some(NotificationType::ERROR), - )); - log( - format!( - "Failed to open envelope {}: {}", - envelope.message_id_display(), - e.to_string() - ), - ERROR, - ); - return true; - } - }; + let attachments = AttachmentBuilder::new(bytes).build().attachments(); if let Some(u) = attachments.get(a_i) { match u.content_type() { ContentType::MessageRfc822 @@ -1440,7 +1446,6 @@ impl Component for MailView { } } UIEvent::Action(MailingListAction(ref e)) => { - let unsafe_context = context as *mut Context; let account = &context.accounts[self.coordinates.0]; if !account.contains_key(self.coordinates.2) { /* The envelope has been renamed or removed, so wait for the appropriate event to @@ -1448,13 +1453,14 @@ impl Component for MailView { return true; } let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - if let Some(actions) = list_management::ListActions::detect(&envelope) { + let detect = list_management::ListActions::detect(&envelope); + if let Some(ref actions) = detect { match e { MailingListAction::ListPost if actions.post.is_some() => { /* open composer */ let mut failure = true; if let list_management::ListAction::Email(list_post_addr) = - actions.post.unwrap()[0] + actions.post.as_ref().unwrap()[0] { if let Ok(mailto) = Mailto::try_from(list_post_addr) { let draft: Draft = mailto.into(); @@ -1476,12 +1482,12 @@ impl Component for MailView { } MailingListAction::ListUnsubscribe if actions.unsubscribe.is_some() => { /* autosend or open unsubscribe option*/ - let unsubscribe = actions.unsubscribe.unwrap(); - for option in unsubscribe { + let unsubscribe = actions.unsubscribe.as_ref().unwrap(); + for option in unsubscribe.iter() { /* TODO: Ask for confirmation before proceding with an action */ match option { list_management::ListAction::Email(email) => { - if let Ok(mailto) = Mailto::try_from(email) { + if let Ok(mailto) = Mailto::try_from(*email) { let mut draft: Draft = mailto.into(); draft.headers_mut().insert( "From".into(), @@ -1490,15 +1496,14 @@ impl Component for MailView { self.coordinates.0, ), ); + /* Manually drop stuff because borrowck doesn't do it + * on its own */ + drop(detect); + drop(envelope); + drop(account); return super::compose::send_draft( ToggleFlag::False, - /* FIXME: refactor to avoid unsafe. - * - * actions contains byte slices from the envelope's - * headers send_draft only needs a mut ref for - * context to push back replies and save the sent - * message */ - unsafe { &mut *(unsafe_context) }, + context, self.coordinates.0, draft, SpecialUsageMailbox::Sent, @@ -1564,6 +1569,7 @@ impl Component for MailView { } false } + fn is_dirty(&self) -> bool { self.dirty || self.pager.is_dirty() @@ -1576,6 +1582,7 @@ impl Component for MailView { false } } + fn set_dirty(&mut self, value: bool) { self.dirty = value; match self.mode { @@ -1590,6 +1597,7 @@ impl Component for MailView { _ => {} } } + fn get_shortcuts(&self, context: &Context) -> ShortcutMaps { let mut map = if let Some(ref sbv) = self.subview { sbv.get_shortcuts(context) diff --git a/src/components/mail/view/envelope.rs b/src/components/mail/view/envelope.rs index 817c8a22..7659fc98 100644 --- a/src/components/mail/view/envelope.rs +++ b/src/components/mail/view/envelope.rs @@ -95,7 +95,6 @@ impl EnvelopeView { &body, Some(Box::new(|a: &Attachment, v: &mut Vec| { if a.content_type().is_text_html() { - use std::io::Write; let settings = &context.settings; if let Some(filter_invocation) = settings.pager.html_filter.as_ref() { let command_obj = Command::new("sh") diff --git a/src/components/mail/view/thread.rs b/src/components/mail/view/thread.rs index 87ad7ce5..715f44a8 100644 --- a/src/components/mail/view/thread.rs +++ b/src/components/mail/view/thread.rs @@ -961,7 +961,7 @@ impl Component for ThreadView { self.coordinates.1, self.entries[self.current_pos()].msg_hash, ); - self.mailview.update(coordinates); + self.mailview.update(coordinates, context); } if self.entries.len() == 1 { diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index 7b6d9dfc..3222bc7d 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -156,6 +156,7 @@ pub enum JobRequest { DeleteMailbox(oneshot::Receiver>>), //RenameMailbox, Search, + AsBytes, SetMailboxPermissions(MailboxHash, oneshot::Receiver>), SetMailboxSubscription(MailboxHash, oneshot::Receiver>), Watch(JoinHandle), @@ -175,6 +176,7 @@ impl core::fmt::Debug for JobRequest { JobRequest::DeleteMailbox(_) => write!(f, "{}", "JobRequest::DeleteMailbox"), //JobRequest::RenameMailbox, JobRequest::Search => write!(f, "{}", "JobRequest::Search"), + JobRequest::AsBytes => write!(f, "{}", "JobRequest::AsBytes"), JobRequest::SetMailboxPermissions(_, _) => { write!(f, "{}", "JobRequest::SetMailboxPermissions") } @@ -1599,7 +1601,7 @@ impl Account { } } //JobRequest::RenameMailbox, - JobRequest::Search => { + JobRequest::Search | JobRequest::AsBytes => { self.sender .send(ThreadEvent::UIEvent(UIEvent::StatusEvent( StatusEvent::JobFinished(job_id.clone()), diff --git a/src/plugins/backend.rs b/src/plugins/backend.rs index 88e6bf86..90dfa34f 100644 --- a/src/plugins/backend.rs +++ b/src/plugins/backend.rs @@ -296,22 +296,24 @@ struct PluginOp { } impl BackendOp for PluginOp { - fn as_bytes(&mut self) -> Result<&[u8]> { - if let Some(ref bytes) = self.bytes { - return Ok(bytes.as_bytes()); - } - - if let Ok(mut channel) = self.channel.try_lock() { - channel.write_ref(&rmpv::ValueRef::Ext(BACKEND_OP_FN, b"as_bytes"))?; - debug!(channel.expect_ack())?; - channel.write_ref(&rmpv::ValueRef::Integer(self.hash.into()))?; - debug!(channel.expect_ack())?; - let bytes: Result> = channel.from_read(); - self.bytes = Some(bytes.map(Into::into).and_then(std::convert::identity)?); - Ok(self.bytes.as_ref().map(String::as_bytes).unwrap()) - } else { - Err(MeliError::new("busy")) - } + fn as_bytes(&mut self) -> ResultFuture> { + let hash = self.hash; + let channel = self.channel.clone(); + Ok(Box::pin(async move { + if let Ok(mut channel) = channel.try_lock() { + channel.write_ref(&rmpv::ValueRef::Ext(BACKEND_OP_FN, b"as_bytes"))?; + debug!(channel.expect_ack())?; + channel.write_ref(&rmpv::ValueRef::Integer(hash.into()))?; + debug!(channel.expect_ack())?; + let bytes: Result> = channel.from_read(); + Ok(bytes + .map(Into::into) + .and_then(std::convert::identity)? + .into_bytes()) + } else { + Err(MeliError::new("busy")) + } + })) } fn fetch_flags(&self) -> ResultFuture {