From d3c658cf0094ba3bb256fa5b98ec3670a498ebe8 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 1 Aug 2019 12:28:36 +0300 Subject: [PATCH] ui: add attachments in composer tab --- ui/Cargo.toml | 2 +- ui/src/components/mail/compose.rs | 161 +++++++++++++++++++++++------- ui/src/execute.rs | 36 ++++++- ui/src/execute/actions.rs | 7 ++ 4 files changed, 165 insertions(+), 41 deletions(-) diff --git a/ui/Cargo.toml b/ui/Cargo.toml index 8c732efb..2ee5de96 100644 --- a/ui/Cargo.toml +++ b/ui/Cargo.toml @@ -16,7 +16,7 @@ chan-signal = "0.3.1" fnv = "1.0.3" # >:c linkify = "0.3.1" # >:c melib = { path = "../melib", version = "*" } -mime_apps = { git = "https://github.com/meli/mime_apps", version = "*" } +mime_apps = { git = "https://git.meli.delivery/meli/mime_apps", version = "^0.2.0" } nom = "3.2.0" notify = "4.0.1" # >:c notify-rust = "^3" # >:c diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index fc6637da..014a88fb 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -22,6 +22,7 @@ use super::*; use melib::Draft; +use mime_apps::query_mime_info; use std::str::FromStr; #[derive(Debug, PartialEq)] @@ -216,37 +217,55 @@ impl Composer { } } - /* - let (x, y) = if k == "From" { - write_string_to_grid( - "◀ ", - grid, - Color::Byte(251), - Color::Default, - ((x, y), set_y(bottom_right, y)), - true, - ) - } else { - (x, y) - }; - let (x, y) = write_string_to_grid( - &headers[k], - grid, - Color::Default, - bg_color, - ((x, y), set_y(bottom_right, y)), - true, - ); - if k == "From" { - write_string_to_grid( - " ▶", - grid, - Color::Byte(251), - Color::Default, - ((x, y), set_y(bottom_right, y)), - true, - ) - */ + fn draw_attachments(&self, grid: &mut CellBuffer, area: Area, _context: &mut Context) { + let attachments_no = self.draft.attachments().len(); + if attachments_no == 0 { + write_string_to_grid( + "no attachments", + grid, + Color::Default, + Color::Default, + (pos_inc(upper_left!(area), (0, 1)), bottom_right!(area)), + false, + ); + } else { + write_string_to_grid( + &format!("{} attachments ", attachments_no), + grid, + Color::Default, + Color::Default, + (pos_inc(upper_left!(area), (0, 1)), bottom_right!(area)), + false, + ); + for (i, a) in self.draft.attachments().iter().enumerate() { + if let Some(name) = a.content_type().name() { + write_string_to_grid( + &format!( + "[{}] \"{}\", {} {} bytes", + i, + name, + a.content_type(), + a.raw.len() + ), + grid, + Color::Default, + Color::Default, + (pos_inc(upper_left!(area), (0, 2 + i)), bottom_right!(area)), + false, + ); + } else { + write_string_to_grid( + &format!("[{}] {} {} bytes", i, a.content_type(), a.raw.len()), + grid, + Color::Default, + Color::Default, + (pos_inc(upper_left!(area), (0, 2 + i)), bottom_right!(area)), + false, + ); + } + } + } + } } impl Component for Composer { @@ -257,6 +276,10 @@ impl Component for Composer { let upper_left = set_y(upper_left, get_y(upper_left) + 1); + if height!(area) < 4 { + return; + } + let width = if width!(area) > 80 && self.reply_context.is_some() { width!(area) / 2 } else { @@ -275,7 +298,7 @@ impl Component for Composer { self.update_form(); self.initialized = true; } - let header_height = self.form.len() + 1; + let header_height = self.form.len(); let mid = if width > 80 { let width = width - 80; @@ -320,29 +343,42 @@ impl Component for Composer { let header_area = if self.reply_context.is_some() { ( set_x(upper_left, mid + 1), - set_y(bottom_right, get_y(upper_left) + header_height + 1), + set_y(bottom_right, get_y(upper_left) + header_height), ) } else { ( set_x(upper_left, mid + 1), ( get_x(bottom_right).saturating_sub(mid), - get_y(upper_left) + header_height + 1, + get_y(upper_left) + header_height, ), ) }; - let body_area = if self.reply_context.is_some() { + let attachments_no = self.draft.attachments().len(); + let attachment_area = if self.reply_context.is_some() { ( - (mid + 1, get_y(upper_left) + header_height + 1), + (mid + 1, get_y(bottom_right) - 2 - attachments_no), bottom_right, ) } else { ( - pos_inc(upper_left, (mid + 1, header_height + 2)), + (mid + 1, get_y(bottom_right) - 2 - attachments_no), pos_dec(bottom_right, (mid, 0)), ) }; + let body_area = if self.reply_context.is_some() { + ( + (mid + 1, get_y(upper_left) + header_height + 1), + set_y(bottom_right, get_y(bottom_right) - 3 - attachments_no), + ) + } else { + ( + pos_inc(upper_left, (mid + 1, header_height + 1)), + pos_dec(bottom_right, (mid, 3 + attachments_no)), + ) + }; + let (x, y) = write_string_to_grid( if self.reply_context.is_some() { "COMPOSING REPLY" @@ -431,6 +467,7 @@ impl Component for Composer { } } + self.draw_attachments(grid, attachment_area, context); context.dirty_areas.push_back(area); } @@ -587,13 +624,61 @@ impl Component for Composer { return true; } let result = f.read_to_string(); - self.draft = Draft::from_str(result.as_str()).unwrap(); + let mut new_draft = Draft::from_str(result.as_str()).unwrap(); + std::mem::swap(self.draft.attachments_mut(), new_draft.attachments_mut()); + self.draft = new_draft; self.initialized = false; context.replies.push_back(UIEvent::Fork(ForkType::Finished)); context.restore_input(); self.dirty = true; return true; } + UIEvent::Action(ref a) => { + match a { + Action::Compose(ComposeAction::AddAttachment(ref path)) => { + let mut attachment = match melib::email::attachment_from_file(path) { + Ok(a) => a, + Err(e) => { + context.replies.push_back(UIEvent::Notification( + Some("could not add attachment".to_string()), + e.to_string(), + )); + self.dirty = true; + return true; + } + }; + if let Ok(mime_type) = query_mime_info(path) { + match attachment.content_type { + ContentType::Other { ref mut tag, .. } => { + *tag = mime_type; + } + _ => {} + } + } + self.draft.attachments_mut().push(attachment); + self.dirty = true; + return true; + } + Action::Compose(ComposeAction::RemoveAttachment(idx)) => { + if *idx + 1 > self.draft.attachments().len() { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage( + "attachment with given index does not exist".to_string(), + ), + )); + self.dirty = true; + return true; + } + self.draft.attachments_mut().remove(*idx); + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage("attachment removed".to_string()), + )); + self.dirty = true; + return true; + } + _ => {} + } + } _ => {} } false diff --git a/ui/src/execute.rs b/ui/src/execute.rs index 29a13095..0c459a58 100644 --- a/ui/src/execute.rs +++ b/ui/src/execute.rs @@ -26,10 +26,12 @@ use nom::{digit, not_line_ending}; use std; pub mod actions; pub use crate::actions::Action::{self, *}; +pub use crate::actions::ComposeAction::{self, *}; pub use crate::actions::ListingAction::{self, *}; pub use crate::actions::MailingListAction::{self, *}; pub use crate::actions::PagerAction::{self, *}; pub use crate::actions::TabAction::{self, *}; +use std::str::FromStr; /* Create a const table with every command part that can be auto-completed and its description */ macro_rules! define_commands { @@ -185,6 +187,30 @@ define_commands!([ )) ); ) + }, + { tags: ["add-attachment "], + desc: "add-attachment PATH", + parser:( + named!( add_attachment, + do_parse!( + ws!(tag!("add-attachment")) + >> path: map_res!(call!(not_line_ending), std::str::from_utf8) + >> (Compose(AddAttachment(path.to_string()))) + ) + ); + ) + }, + { tags: ["remove-attachment "], + desc: "remove-attachment INDEX", + parser:( + named!( remove_attachment, + do_parse!( + ws!(tag!("remove-attachment")) + >> idx: map_res!(map_res!(call!(not_line_ending), std::str::from_utf8), usize::from_str) + >> (Compose(RemoveAttachment(idx))) + ) + ); + ) } ]); @@ -230,6 +256,12 @@ named!( listing_action, alt_complete!(toggle | envelope_action | filter | toggle_thread_snooze) ); -named!(pub parse_command, - alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | pipe) + +named!( + compose_action, + alt_complete!(add_attachment | remove_attachment) +); + +named!(pub parse_command, + alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | pipe | compose_action) ); diff --git a/ui/src/execute/actions.rs b/ui/src/execute/actions.rs index 5edd8a50..06aaceb0 100644 --- a/ui/src/execute/actions.rs +++ b/ui/src/execute/actions.rs @@ -64,6 +64,12 @@ pub enum PagerAction { Pipe(String, Vec), } +#[derive(Debug)] +pub enum ComposeAction { + AddAttachment(String), + RemoveAttachment(usize), +} + #[derive(Debug)] pub enum Action { Listing(ListingAction), @@ -76,4 +82,5 @@ pub enum Action { Pager(PagerAction), SetEnv(String, String), PrintEnv(String), + Compose(ComposeAction), }