Add forward mail option
Forward email with shortcut 'forward' (default ctrl+f) This opens a composing tab letting you to select receiver etc. "composing" config setting "forward_as_attachment" selects the forwarding behavior: - "ask" asks you ever time - true always forwards by attaching the entire email as a single attachment - false always forwards by inlining the email, like most email clients do. Closes #120master
parent
e090c31f96
commit
505adca54d
|
@ -520,6 +520,11 @@ with the replied envelope's date.
|
|||
Whether the strftime call for the attribution string uses the POSIX locale instead of the user's active locale.
|
||||
.\" default value
|
||||
.Pq Em true
|
||||
.It Ic forward_as_attachment Ar boolean or "ask"
|
||||
.Pq Em optional
|
||||
Forward emails as attachment? (Alternative is inline).
|
||||
.\" default value
|
||||
.Pq Em ask
|
||||
.El
|
||||
.Sh SHORTCUTS
|
||||
Shortcuts can take the following values:
|
||||
|
@ -751,6 +756,18 @@ View raw envelope source in a pager.
|
|||
Reply to envelope.
|
||||
.\" default value
|
||||
.Pq Em R
|
||||
.It Ic reply_to_author
|
||||
Reply to author.
|
||||
.\" default value
|
||||
.Pq Em Ctrl-r
|
||||
.It Ic reply_to_all
|
||||
Reply to all/Reply to list/Follow up.
|
||||
.\" default value
|
||||
.Pq Em Ctrl-g
|
||||
.It Ic forward
|
||||
Forward email.
|
||||
.\" default value
|
||||
.Pq Em Ctrl-f
|
||||
.It Ic edit
|
||||
Open envelope in composer.
|
||||
.\" default value
|
||||
|
|
|
@ -564,3 +564,12 @@ impl From<&[u8]> for ContentDisposition {
|
|||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ContentDispositionKind> for ContentDisposition {
|
||||
fn from(kind: ContentDispositionKind) -> ContentDisposition {
|
||||
ContentDisposition {
|
||||
kind,
|
||||
..ContentDisposition::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -408,6 +408,55 @@ impl Composer {
|
|||
Composer::reply_to(coordinates, reply_body, context, true)
|
||||
}
|
||||
|
||||
pub fn forward(
|
||||
coordinates: (AccountHash, MailboxHash, EnvelopeHash),
|
||||
bytes: &[u8],
|
||||
env: &Envelope,
|
||||
as_attachment: bool,
|
||||
context: &mut Context,
|
||||
) -> Self {
|
||||
let mut composer = Composer::with_account(coordinates.0, context);
|
||||
let mut draft: Draft = Draft::default();
|
||||
draft.set_header("Subject", format!("Fwd: {}", env.subject()));
|
||||
let preamble = format!(
|
||||
r#"
|
||||
---------- Forwarded message ---------
|
||||
From: {}
|
||||
Date: {}
|
||||
Subject: {}
|
||||
To: {}
|
||||
|
||||
"#,
|
||||
env.field_from_to_string(),
|
||||
env.date_as_str(),
|
||||
env.subject(),
|
||||
env.field_to_to_string()
|
||||
);
|
||||
if as_attachment {
|
||||
let mut attachment = AttachmentBuilder::new(b"");
|
||||
let mut disposition: ContentDisposition = ContentDispositionKind::Attachment.into();
|
||||
{
|
||||
disposition.filename = Some(format!("{}.eml", env.message_id_raw()));
|
||||
}
|
||||
attachment
|
||||
.set_raw(bytes.to_vec())
|
||||
.set_body_to_raw()
|
||||
.set_content_type(ContentType::MessageRfc822)
|
||||
.set_content_transfer_encoding(ContentTransferEncoding::_8Bit)
|
||||
.set_content_disposition(disposition);
|
||||
draft.attachments.push(attachment);
|
||||
draft.body = preamble;
|
||||
} else {
|
||||
let content_type = ContentType::default();
|
||||
let preamble: AttachmentBuilder =
|
||||
Attachment::new(content_type, Default::default(), preamble.into_bytes()).into();
|
||||
draft.attachments.push(preamble);
|
||||
draft.attachments.push(env.body_bytes(bytes).into());
|
||||
}
|
||||
composer.set_draft(draft);
|
||||
composer
|
||||
}
|
||||
|
||||
pub fn set_draft(&mut self, draft: Draft) {
|
||||
self.draft = draft;
|
||||
self.update_form();
|
||||
|
|
|
@ -168,11 +168,13 @@ pub struct MailView {
|
|||
id: ComponentId,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub enum PendingReplyAction {
|
||||
Reply,
|
||||
ReplyToAuthor,
|
||||
ReplyToAll,
|
||||
ForwardAttachment,
|
||||
ForwardInline,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -189,6 +191,7 @@ enum MailViewState {
|
|||
},
|
||||
Loaded {
|
||||
bytes: Vec<u8>,
|
||||
env: Envelope,
|
||||
body: Attachment,
|
||||
display: Vec<AttachmentDisplay>,
|
||||
body_text: String,
|
||||
|
@ -310,6 +313,8 @@ impl MailView {
|
|||
.get_env_mut(self.coordinates.2)
|
||||
.populate_headers(&bytes);
|
||||
}
|
||||
let env =
|
||||
account.collection.get_env(self.coordinates.2).clone();
|
||||
let body = AttachmentBuilder::new(&bytes).build();
|
||||
let display = Self::attachment_to(
|
||||
&body,
|
||||
|
@ -325,6 +330,7 @@ impl MailView {
|
|||
self.attachment_displays_to_text(&display, context, true);
|
||||
self.state = MailViewState::Loaded {
|
||||
display,
|
||||
env,
|
||||
body,
|
||||
bytes,
|
||||
body_text,
|
||||
|
@ -388,7 +394,7 @@ impl MailView {
|
|||
}
|
||||
|
||||
fn perform_action(&mut self, action: PendingReplyAction, context: &mut Context) {
|
||||
let reply_body = match self.state {
|
||||
let (bytes, reply_body, env) = match self.state {
|
||||
MailViewState::Init {
|
||||
ref mut pending_action,
|
||||
..
|
||||
|
@ -402,9 +408,16 @@ impl MailView {
|
|||
}
|
||||
return;
|
||||
}
|
||||
MailViewState::Loaded { ref display, .. } => {
|
||||
self.attachment_displays_to_text(&display, context, false)
|
||||
}
|
||||
MailViewState::Loaded {
|
||||
ref bytes,
|
||||
ref display,
|
||||
ref env,
|
||||
..
|
||||
} => (
|
||||
bytes,
|
||||
self.attachment_displays_to_text(&display, context, false),
|
||||
env,
|
||||
),
|
||||
MailViewState::Error { .. } => {
|
||||
return;
|
||||
}
|
||||
|
@ -425,6 +438,20 @@ impl MailView {
|
|||
reply_body,
|
||||
context,
|
||||
)),
|
||||
PendingReplyAction::ForwardAttachment => Box::new(Composer::forward(
|
||||
self.coordinates,
|
||||
bytes,
|
||||
env,
|
||||
true,
|
||||
context,
|
||||
)),
|
||||
PendingReplyAction::ForwardInline => Box::new(Composer::forward(
|
||||
self.coordinates,
|
||||
bytes,
|
||||
env,
|
||||
false,
|
||||
context,
|
||||
)),
|
||||
};
|
||||
|
||||
context
|
||||
|
@ -1737,6 +1764,10 @@ impl Component for MailView {
|
|||
.get_env_mut(self.coordinates.2)
|
||||
.populate_headers(&bytes);
|
||||
}
|
||||
let env = context.accounts[&self.coordinates.0]
|
||||
.collection
|
||||
.get_env(self.coordinates.2)
|
||||
.clone();
|
||||
let body = AttachmentBuilder::new(&bytes).build();
|
||||
let display = Self::attachment_to(
|
||||
&body,
|
||||
|
@ -1752,6 +1783,7 @@ impl Component for MailView {
|
|||
self.attachment_displays_to_text(&display, context, true);
|
||||
self.state = MailViewState::Loaded {
|
||||
bytes,
|
||||
env,
|
||||
body,
|
||||
display,
|
||||
links: vec![],
|
||||
|
@ -1907,6 +1939,53 @@ impl Component for MailView {
|
|||
self.perform_action(PendingReplyAction::ReplyToAuthor, context);
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["forward"]) =>
|
||||
{
|
||||
match mailbox_settings!(
|
||||
context[self.coordinates.0][&self.coordinates.1]
|
||||
.composing
|
||||
.forward_as_attachment
|
||||
) {
|
||||
f if f.is_ask() => {
|
||||
let id = self.id;
|
||||
context.replies.push_back(UIEvent::GlobalUIDialog(Box::new(
|
||||
UIConfirmationDialog::new(
|
||||
"How do you want the email to be forwarded?",
|
||||
vec![
|
||||
(true, "inline".to_string()),
|
||||
(false, "as attachment".to_string()),
|
||||
],
|
||||
true,
|
||||
Some(Box::new(move |_: ComponentId, result: bool| {
|
||||
Some(UIEvent::FinishedUIDialog(
|
||||
id,
|
||||
Box::new(if result {
|
||||
PendingReplyAction::ForwardInline
|
||||
} else {
|
||||
PendingReplyAction::ForwardAttachment
|
||||
}),
|
||||
))
|
||||
})),
|
||||
context,
|
||||
),
|
||||
)));
|
||||
}
|
||||
f if f.is_true() => {
|
||||
self.perform_action(PendingReplyAction::ForwardAttachment, context);
|
||||
}
|
||||
_ => {
|
||||
self.perform_action(PendingReplyAction::ForwardInline, context);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::FinishedUIDialog(id, ref result) if id == self.id() => {
|
||||
if let Some(result) = result.downcast_ref::<PendingReplyAction>() {
|
||||
self.perform_action(*result, context);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
UIEvent::Input(ref key)
|
||||
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["edit"]) =>
|
||||
{
|
||||
|
@ -2227,6 +2306,7 @@ impl Component for MailView {
|
|||
body: _,
|
||||
bytes: _,
|
||||
display: _,
|
||||
env: _,
|
||||
ref body_text,
|
||||
ref links,
|
||||
} => {
|
||||
|
|
|
@ -593,6 +593,10 @@ mod default_vals {
|
|||
pub(in crate::conf) fn internal_value_true<T: std::convert::From<super::ToggleFlag>>() -> T {
|
||||
super::ToggleFlag::InternalVal(true).into()
|
||||
}
|
||||
|
||||
pub(in crate::conf) fn ask<T: std::convert::From<super::ToggleFlag>>() -> T {
|
||||
super::ToggleFlag::Ask.into()
|
||||
}
|
||||
}
|
||||
|
||||
mod deserializers {
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
*/
|
||||
|
||||
//! Configuration for composing email.
|
||||
use super::default_vals::{false_val, none, true_val};
|
||||
use super::default_vals::{ask, false_val, none, true_val};
|
||||
use melib::ToggleFlag;
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// Settings for writing and sending new e-mail
|
||||
|
@ -72,6 +73,10 @@ pub struct ComposingSettings {
|
|||
/// Default: true
|
||||
#[serde(default = "true_val")]
|
||||
pub attribution_use_posix_locale: bool,
|
||||
/// Forward emails as attachment? (Alternative is inline)
|
||||
/// Default: ask
|
||||
#[serde(default = "ask", alias = "forward-as-attachment")]
|
||||
pub forward_as_attachment: ToggleFlag,
|
||||
}
|
||||
|
||||
impl Default for ComposingSettings {
|
||||
|
@ -86,6 +91,7 @@ impl Default for ComposingSettings {
|
|||
store_sent_mail: true,
|
||||
attribution_format_string: None,
|
||||
attribution_use_posix_locale: true,
|
||||
forward_as_attachment: ToggleFlag::Ask,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,6 +312,11 @@ pub struct ComposingSettingsOverride {
|
|||
#[doc = " Default: true"]
|
||||
#[serde(default)]
|
||||
pub attribution_use_posix_locale: Option<bool>,
|
||||
#[doc = " Forward emails as attachment? (Alternative is inline)"]
|
||||
#[doc = " Default: ask"]
|
||||
#[serde(alias = "forward-as-attachment")]
|
||||
#[serde(default)]
|
||||
pub forward_as_attachment: Option<ToggleFlag>,
|
||||
}
|
||||
impl Default for ComposingSettingsOverride {
|
||||
fn default() -> Self {
|
||||
|
@ -325,6 +330,7 @@ impl Default for ComposingSettingsOverride {
|
|||
store_sent_mail: None,
|
||||
attribution_format_string: None,
|
||||
attribution_use_posix_locale: None,
|
||||
forward_as_attachment: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -247,6 +247,7 @@ shortcut_key_values! { "envelope-view",
|
|||
reply |> "Reply to envelope." |> Key::Char('R'),
|
||||
reply_to_author |> "Reply to author." |> Key::Ctrl('r'),
|
||||
reply_to_all |> "Reply to all/Reply to list/Follow up." |> Key::Ctrl('g'),
|
||||
forward |> "Forward email." |> Key::Ctrl('f'),
|
||||
return_to_normal_view |> "Return to envelope if viewing raw source or attachment." |> Key::Char('r'),
|
||||
toggle_expand_headers |> "Expand extra headers (References and others)." |> Key::Char('h'),
|
||||
toggle_url_mode |> "Toggles url open mode." |> Key::Char('u'),
|
||||
|
|
10
src/state.rs
10
src/state.rs
|
@ -1167,15 +1167,15 @@ impl State {
|
|||
}
|
||||
Some(false)
|
||||
}
|
||||
/// Switch back to the terminal's main screen (The command line the user sees before opening
|
||||
/// the application)
|
||||
/// Switch back to the terminal's main screen (The command line the user sees before opening
|
||||
/// the application)
|
||||
pub fn switch_to_main_screen(&mut self) {
|
||||
self.screen.switch_to_main_screen();
|
||||
}
|
||||
|
||||
pub fn switch_to_alternate_screen(&mut self){
|
||||
self.screen.switch_to_alternate_screen(&mut self.context);
|
||||
}
|
||||
pub fn switch_to_alternate_screen(&mut self) {
|
||||
self.screen.switch_to_alternate_screen(&mut self.context);
|
||||
}
|
||||
|
||||
fn flush(&mut self) {
|
||||
self.screen.flush();
|
||||
|
|
Loading…
Reference in New Issue