ui: add attachments in composer tab

embed
Manos Pitsidianakis 2019-08-01 12:28:36 +03:00
parent 131b4abfbe
commit d3c658cf00
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
4 changed files with 165 additions and 41 deletions

View File

@ -16,7 +16,7 @@ chan-signal = "0.3.1"
fnv = "1.0.3" # >:c fnv = "1.0.3" # >:c
linkify = "0.3.1" # >:c linkify = "0.3.1" # >:c
melib = { path = "../melib", version = "*" } 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" nom = "3.2.0"
notify = "4.0.1" # >:c notify = "4.0.1" # >:c
notify-rust = "^3" # >:c notify-rust = "^3" # >:c

View File

@ -22,6 +22,7 @@
use super::*; use super::*;
use melib::Draft; use melib::Draft;
use mime_apps::query_mime_info;
use std::str::FromStr; use std::str::FromStr;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
@ -216,37 +217,55 @@ impl Composer {
} }
} }
/* fn draw_attachments(&self, grid: &mut CellBuffer, area: Area, _context: &mut Context) {
let (x, y) = if k == "From" { let attachments_no = self.draft.attachments().len();
write_string_to_grid( if attachments_no == 0 {
"", write_string_to_grid(
grid, "no attachments",
Color::Byte(251), grid,
Color::Default, Color::Default,
((x, y), set_y(bottom_right, y)), Color::Default,
true, (pos_inc(upper_left!(area), (0, 1)), bottom_right!(area)),
) false,
} else { );
(x, y) } else {
}; write_string_to_grid(
let (x, y) = write_string_to_grid( &format!("{} attachments ", attachments_no),
&headers[k], grid,
grid, Color::Default,
Color::Default, Color::Default,
bg_color, (pos_inc(upper_left!(area), (0, 1)), bottom_right!(area)),
((x, y), set_y(bottom_right, y)), false,
true, );
); for (i, a) in self.draft.attachments().iter().enumerate() {
if k == "From" { if let Some(name) = a.content_type().name() {
write_string_to_grid( write_string_to_grid(
"", &format!(
grid, "[{}] \"{}\", {} {} bytes",
Color::Byte(251), i,
Color::Default, name,
((x, y), set_y(bottom_right, y)), a.content_type(),
true, 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 { impl Component for Composer {
@ -257,6 +276,10 @@ impl Component for Composer {
let upper_left = set_y(upper_left, get_y(upper_left) + 1); 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() { let width = if width!(area) > 80 && self.reply_context.is_some() {
width!(area) / 2 width!(area) / 2
} else { } else {
@ -275,7 +298,7 @@ impl Component for Composer {
self.update_form(); self.update_form();
self.initialized = true; self.initialized = true;
} }
let header_height = self.form.len() + 1; let header_height = self.form.len();
let mid = if width > 80 { let mid = if width > 80 {
let width = width - 80; let width = width - 80;
@ -320,29 +343,42 @@ impl Component for Composer {
let header_area = if self.reply_context.is_some() { let header_area = if self.reply_context.is_some() {
( (
set_x(upper_left, mid + 1), 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 { } else {
( (
set_x(upper_left, mid + 1), set_x(upper_left, mid + 1),
( (
get_x(bottom_right).saturating_sub(mid), 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, bottom_right,
) )
} else { } 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)), 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( let (x, y) = write_string_to_grid(
if self.reply_context.is_some() { if self.reply_context.is_some() {
"COMPOSING REPLY" "COMPOSING REPLY"
@ -431,6 +467,7 @@ impl Component for Composer {
} }
} }
self.draw_attachments(grid, attachment_area, context);
context.dirty_areas.push_back(area); context.dirty_areas.push_back(area);
} }
@ -587,13 +624,61 @@ impl Component for Composer {
return true; return true;
} }
let result = f.read_to_string(); 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; self.initialized = false;
context.replies.push_back(UIEvent::Fork(ForkType::Finished)); context.replies.push_back(UIEvent::Fork(ForkType::Finished));
context.restore_input(); context.restore_input();
self.dirty = true; self.dirty = true;
return 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 false

View File

@ -26,10 +26,12 @@ use nom::{digit, not_line_ending};
use std; use std;
pub mod actions; pub mod actions;
pub use crate::actions::Action::{self, *}; pub use crate::actions::Action::{self, *};
pub use crate::actions::ComposeAction::{self, *};
pub use crate::actions::ListingAction::{self, *}; pub use crate::actions::ListingAction::{self, *};
pub use crate::actions::MailingListAction::{self, *}; pub use crate::actions::MailingListAction::{self, *};
pub use crate::actions::PagerAction::{self, *}; pub use crate::actions::PagerAction::{self, *};
pub use crate::actions::TabAction::{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 */ /* Create a const table with every command part that can be auto-completed and its description */
macro_rules! define_commands { macro_rules! define_commands {
@ -185,6 +187,30 @@ define_commands!([
)) ))
); );
) )
},
{ tags: ["add-attachment "],
desc: "add-attachment PATH",
parser:(
named!( add_attachment<Action>,
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<Action>,
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<Action>, listing_action<Action>,
alt_complete!(toggle | envelope_action | filter | toggle_thread_snooze) alt_complete!(toggle | envelope_action | filter | toggle_thread_snooze)
); );
named!(pub parse_command<Action>,
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | pipe) named!(
compose_action<Action>,
alt_complete!(add_attachment | remove_attachment)
);
named!(pub parse_command<Action>,
alt_complete!( goto | listing_action | sort | subsort | close | mailinglist | setenv | printenv | pipe | compose_action)
); );

View File

@ -64,6 +64,12 @@ pub enum PagerAction {
Pipe(String, Vec<String>), Pipe(String, Vec<String>),
} }
#[derive(Debug)]
pub enum ComposeAction {
AddAttachment(String),
RemoveAttachment(usize),
}
#[derive(Debug)] #[derive(Debug)]
pub enum Action { pub enum Action {
Listing(ListingAction), Listing(ListingAction),
@ -76,4 +82,5 @@ pub enum Action {
Pager(PagerAction), Pager(PagerAction),
SetEnv(String, String), SetEnv(String, String),
PrintEnv(String), PrintEnv(String),
Compose(ComposeAction),
} }