Browse Source

ui: add send confirmation dialog in compose tab

Confirm before sending mail
tags/pre-alpha-0.4.0
Manos Pitsidianakis 3 months ago
parent
commit
ce646abc7a
WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS. GPG Key ID: 73627C2F690DF710
4 changed files with 144 additions and 143 deletions
  1. +10
    -0
      melib/src/email/attachments.rs
  2. +2
    -4
      melib/src/email/compose.rs
  3. +125
    -139
      ui/src/components/mail/compose.rs
  4. +7
    -0
      ui/src/components/utilities.rs

+ 10
- 0
melib/src/email/attachments.rs View File

@@ -92,6 +92,16 @@ impl AttachmentBuilder {
self
}

/// Set body to the entire raw contents, use this if raw contains only data and no headers
/// If raw contains data and headers pass it through AttachmentBuilder::new().
pub fn set_body_to_raw(&mut self) -> &mut Self {
self.body = StrBuilder {
offset: 0,
length: self.raw.len(),
};
self
}

pub fn set_content_type(&mut self, val: ContentType) -> &mut Self {
self.content_type = val;
self


+ 2
- 4
melib/src/email/compose.rs View File

@@ -272,9 +272,6 @@ impl Draft {
);
ret.push('\n');
ret.push_str(&self.body);
} else if self.attachments.len() == 1 && self.body.is_empty() {
let attachment: Attachment = self.attachments.remove(0).into();
ret.extend(attachment.into_raw().chars());
} else {
let mut parts = Vec::with_capacity(self.attachments.len() + 1);
let attachments = std::mem::replace(&mut self.attachments, Vec::new());
@@ -453,9 +450,10 @@ where
let mut file = std::fs::File::open(&path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
let mut attachment = AttachmentBuilder::new(b"");
let mut attachment = AttachmentBuilder::default();
attachment
.set_raw(contents)
.set_body_to_raw()
.set_content_type(ContentType::Other {
name: path.file_name().map(|s| s.to_string_lossy().into()),
tag: b"application/octet-stream".to_vec(),


+ 125
- 139
ui/src/components/mail/compose.rs View File

@@ -115,6 +115,7 @@ enum ViewMode {
Edit,
Embed,
SelectRecipients(Selector<Address>),
Send(Selector<bool>),
}

impl ViewMode {
@@ -364,6 +365,66 @@ impl Composer {
}
}
}

fn save_draft(&mut self, context: &mut Context, folder_type: SpecialUseMailbox) {
let mut failure = true;
let draft = std::mem::replace(&mut self.draft, Draft::default());

let draft = draft.finalise().unwrap();
for folder in &[
&context.accounts[self.account_cursor].special_use_folder(folder_type),
&context.accounts[self.account_cursor].special_use_folder(SpecialUseMailbox::Inbox),
&context.accounts[self.account_cursor].special_use_folder(SpecialUseMailbox::Normal),
] {
if folder.is_none() {
continue;
}
let folder = folder.unwrap();
if let Err(e) = context.accounts[self.account_cursor].save(
draft.as_bytes(),
folder,
Some(Flag::SEEN | Flag::DRAFT),
) {
debug!("{:?} could not save draft msg", e);
log(
format!(
"Could not save draft in '{}' folder: {}.",
folder,
e.to_string()
),
ERROR,
);
context.replies.push_back(UIEvent::Notification(
Some(format!("Could not save draft in '{}' folder.", folder)),
e.into(),
Some(NotificationType::ERROR),
));
} else {
failure = false;
break;
}
}

if failure {
let file = create_temp_file(draft.as_bytes(), None, None, false);
debug!("message saved in {}", file.path.display());
log(
format!(
"Message was stored in {} so that you can restore it manually.",
file.path.display()
),
INFO,
);
context.replies.push_back(UIEvent::Notification(
Some("Could not save in any folder".into()),
format!(
"Message was stored in {} so that you can restore it manually.",
file.path.display()
),
Some(NotificationType::INFO),
));
}
}
}

impl Component for Composer {
@@ -543,6 +604,9 @@ impl Component for Composer {

match self.mode {
ViewMode::Edit | ViewMode::Embed => {}
ViewMode::Send(ref mut s) => {
s.draw(grid, center_area(area, s.content.size()), context);
}
ViewMode::SelectRecipients(ref mut s) => {
s.draw(grid, center_area(area, s.content.size()), context);
}
@@ -565,6 +629,34 @@ impl Component for Composer {
return true;
}
}
(ViewMode::Send(ref mut selector), _, _) => {
if selector.process_event(event, context) {
if selector.is_done() {
let s = match std::mem::replace(&mut self.mode, ViewMode::Edit) {
ViewMode::Send(s) => s,
_ => unreachable!(),
};
let result: bool = s.collect()[0];
if result {
self.update_draft();
if send_draft(
self.sign_mail,
context,
self.account_cursor,
self.draft.clone(),
) {
self.save_draft(context, SpecialUseMailbox::Sent);
context
.replies
.push_back(UIEvent::Action(Tab(Kill(self.id))));
} else {
self.save_draft(context, SpecialUseMailbox::Drafts);
}
}
}
return true;
}
}
(ViewMode::SelectRecipients(ref mut selector), _, _) => {
if selector.process_event(event, context) {
if selector.is_done() {
@@ -601,70 +693,7 @@ impl Component for Composer {
}
'n' => {}
'y' => {
let mut failure = true;
let draft = std::mem::replace(&mut self.draft, Draft::default());

let draft = draft.finalise().unwrap();
for folder in &[
&context.accounts[self.account_cursor]
.special_use_folder(SpecialUseMailbox::Drafts),
&context.accounts[self.account_cursor]
.special_use_folder(SpecialUseMailbox::Inbox),
&context.accounts[self.account_cursor]
.special_use_folder(SpecialUseMailbox::Normal),
] {
if folder.is_none() {
continue;
}
let folder = folder.unwrap();
if let Err(e) = context.accounts[self.account_cursor].save(
draft.as_bytes(),
folder,
Some(Flag::SEEN | Flag::DRAFT),
) {
debug!("{:?} could not save draft msg", e);
log(
format!(
"Could not save draft in '{}' folder: {}.",
folder,
e.to_string()
),
ERROR,
);
context.replies.push_back(UIEvent::Notification(
Some(format!(
"Could not save draft in '{}' folder.",
folder
)),
e.into(),
Some(NotificationType::ERROR),
));
} else {
failure = false;
break;
}
}

if failure {
let file =
create_temp_file(draft.as_bytes(), None, None, false);
debug!("message saved in {}", file.path.display());
log(
format!(
"Message was stored in {} so that you can restore it manually.",
file.path.display()
),
INFO,
);
context.replies.push_back(UIEvent::Notification(
Some("Could not save in any folder".into()),
format!(
"Message was stored in {} so that you can restore it manually.",
file.path.display()
),
Some(NotificationType::INFO),
));
}
self.save_draft(context, SpecialUseMailbox::Drafts);
context.replies.push_back(UIEvent::Action(Tab(Kill(u))));
return true;
}
@@ -720,18 +749,15 @@ impl Component for Composer {
self.cursor = Cursor::Body;
self.dirty = true;
}
UIEvent::Input(Key::Char('s')) => {
UIEvent::Input(Key::Char('s')) if self.mode.is_edit() => {
self.update_draft();
if send_draft(
self.sign_mail,
self.mode = ViewMode::Send(Selector::new(
"send mail?",
vec![(true, "yes".to_string()), (false, "no".to_string())],
/* only one choice */
true,
context,
self.account_cursor,
self.draft.clone(),
) {
context
.replies
.push_back(UIEvent::Action(Tab(Kill(self.id))));
}
));
return true;
}
UIEvent::EmbedInput((Key::Ctrl('z'), _)) => {
@@ -1071,10 +1097,17 @@ pub fn send_draft(
) -> bool {
use std::io::Write;
use std::process::{Command, Stdio};
let mut failure = true;
let settings = &context.settings;
let format_flowed = settings.composing.format_flowed;
let parts = split_command!(settings.composing.mailer_cmd);
if parts.is_empty() {
context.replies.push_back(UIEvent::Notification(
None,
String::from("mailer_cmd configuration value is empty"),
Some(NotificationType::ERROR),
));
return false;
}
let (cmd, args) = (parts[0], &parts[1..]);
let mut msmtp = Command::new(cmd)
.args(args)
@@ -1147,53 +1180,6 @@ pub fn send_draft(
stdin
.write_all(draft.as_bytes())
.expect("Failed to write to stdin");
for folder in &[
&context.accounts[account_cursor].special_use_folder(SpecialUseMailbox::Sent),
&context.accounts[account_cursor].special_use_folder(SpecialUseMailbox::Inbox),
&context.accounts[account_cursor].special_use_folder(SpecialUseMailbox::Normal),
] {
if folder.is_none() {
continue;
}
let folder = folder.unwrap();
if let Err(e) =
context.accounts[account_cursor].save(draft.as_bytes(), folder, Some(Flag::SEEN))
{
debug!("{:?} could not save sent msg", e);
log(
format!("Could not save in '{}' folder: {}.", folder, e.to_string()),
ERROR,
);
context.replies.push_back(UIEvent::Notification(
Some(format!("Could not save in '{}' folder.", folder)),
e.into(),
Some(NotificationType::ERROR),
));
} else {
failure = false;
break;
}
}

if failure {
let file = create_temp_file(draft.as_bytes(), None, None, false);
debug!("message saved in {}", file.path.display());
log(
format!(
"Message was stored in {} so that you can restore it manually.",
file.path.display()
),
INFO,
);
context.replies.push_back(UIEvent::Notification(
Some("Could not save in any folder".into()),
format!(
"Message was stored in {} so that you can restore it manually.",
file.path.display()
),
Some(NotificationType::INFO),
));
}
}
let output = msmtp.wait().expect("Failed to wait on mailer");
if output.success() {
@@ -1203,23 +1189,23 @@ pub fn send_draft(
None,
));
} else {
if let Some(exit_code) = output.code() {
log(
format!(
"Could not send e-mail using `{}`: Process exited with {}",
cmd, exit_code
),
ERROR,
);
let error_message = if let Some(exit_code) = output.code() {
format!(
"Could not send e-mail using `{}`: Process exited with {}",
cmd, exit_code
)
} else {
log(
format!(
"Could not send e-mail using `{}`: Process was killed by signal",
cmd
),
ERROR,
);
}
format!(
"Could not send e-mail using `{}`: Process was killed by signal",
cmd
)
};
context.replies.push_back(UIEvent::Notification(
Some("Message not sent.".into()),
error_message.clone(),
Some(NotificationType::ERROR),
));
log(error_message, ERROR);
}
!failure
true
}

+ 7
- 0
ui/src/components/utilities.rs View File

@@ -1763,6 +1763,13 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
self.done = true;
return true;
}
(UIEvent::Input(Key::Esc), _) => {
for e in self.entries.iter_mut() {
e.1 = false;
}
self.done = true;
return true;
}
(UIEvent::Input(Key::Char('\n')), SelectorCursor::Cancel) if !self.single_only => {
for e in self.entries.iter_mut() {
e.1 = false;


Loading…
Cancel
Save