From a20e7ac5c26fc488afd06b125a7aaf6889c54332 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 30 Aug 2018 15:54:30 +0300 Subject: [PATCH] ui: select `From` identities in compose tab Tracking issue #24 --- melib/src/conf/mod.rs | 11 ++ melib/src/mailbox/email/compose/mod.rs | 3 + ui/src/components/mail/compose.rs | 153 +++++++++++++++++++------ ui/src/components/mail/mod.rs | 4 +- ui/src/conf/mod.rs | 57 +++++---- 5 files changed, 169 insertions(+), 59 deletions(-) diff --git a/melib/src/conf/mod.rs b/melib/src/conf/mod.rs index 390684bc..360cd730 100644 --- a/melib/src/conf/mod.rs +++ b/melib/src/conf/mod.rs @@ -25,6 +25,8 @@ pub struct AccountSettings { pub root_folder: String, pub format: String, pub sent_folder: String, + pub identity: String, + pub display_name: Option, } impl AccountSettings { @@ -34,7 +36,16 @@ impl AccountSettings { pub fn name(&self) -> &str { &self.name } + pub fn set_name(&mut self, s: String) { + self.name = s; + } pub fn root_folder(&self) -> &str { &self.root_folder } + pub fn identity(&self) -> &str { + &self.identity + } + pub fn display_name(&self) -> Option<&String> { + self.display_name.as_ref() + } } diff --git a/melib/src/mailbox/email/compose/mod.rs b/melib/src/mailbox/email/compose/mod.rs index beae6db7..b4c4e71a 100644 --- a/melib/src/mailbox/email/compose/mod.rs +++ b/melib/src/mailbox/email/compose/mod.rs @@ -40,6 +40,9 @@ impl Default for Draft { } impl Draft { + pub fn headers_mut(&mut self) -> &mut FnvHashMap { + &mut self.headers + } pub fn headers(&self) -> &FnvHashMap { &self.headers } diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 6ec80dac..3a95cff7 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -25,10 +25,13 @@ use melib::Draft; #[derive(Debug)] pub struct Composer { - dirty: bool, mode: ViewMode, pager: Pager, + draft: Draft, + account_cursor: usize, + + dirty: bool, } impl Default for Composer { @@ -36,8 +39,9 @@ impl Default for Composer { Composer { dirty: true, mode: ViewMode::Overview, - pager: Pager::from_str("", None), + pager: Pager::default(), draft: Draft::default(), + account_cursor: 0, } } } @@ -55,9 +59,79 @@ impl fmt::Display for Composer { } } +impl Composer { + fn draw_header_table(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { + let upper_left = upper_left!(area); + let bottom_right = bottom_right!(area); + + let headers = self.draft.headers(); + { + let (mut x, mut y) = upper_left; + for k in &["Date", "From", "To", "Subject"] { + let update = { + let (x, y) = write_string_to_grid( + k, + grid, + Color::Default, + Color::Default, + ((x, y), set_y(bottom_right, y)), + true, + ); + let (x, y) = write_string_to_grid( + ": ", + grid, + Color::Default, + Color::Default, + ((x, y), set_y(bottom_right, y)), + true, + ); + 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, + Color::Default, + ((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, + ) + } else { + (x, y) + } + }; + x = get_x(upper_left); + y = update.1 + 1; + } + } + } +} + impl Component for Composer { fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) { if self.dirty { + self.draft.headers_mut().insert( + "From".into(), + get_display_name(context, self.account_cursor), + ); clear_area(grid, area); } let upper_left = upper_left!(area); @@ -93,6 +167,7 @@ impl Component for Composer { } } + let header_area = (set_x(upper_left, mid + 1), (mid + 78, header_height + 1)); let body_area = ( (mid + 1, header_height + 2), (mid + 78, get_y(bottom_right)), @@ -104,40 +179,7 @@ impl Component for Composer { } match self.mode { ViewMode::Overview => { - let headers = self.draft.headers(); - { - let (mut x, mut y) = set_x(upper_left, mid + 1); - for k in &["Date", "From", "To", "Subject"] { - let update = { - let (x, y) = write_string_to_grid( - k, - grid, - Color::Default, - Color::Default, - ((x, y), (mid + 78, y)), - true, - ); - let (x, y) = write_string_to_grid( - ": ", - grid, - Color::Default, - Color::Default, - ((x, y), (mid + 78, y)), - true, - ); - write_string_to_grid( - &headers[*k], - grid, - Color::Default, - Color::Default, - ((x, y), (mid + 78, y)), - true, - ) - }; - x = mid + 1; - y = update.1 + 1; - } - } + self.draw_header_table(grid, header_area, context); self.pager.draw(grid, body_area, context); } } @@ -152,6 +194,26 @@ impl Component for Composer { UIEventType::Resize => { self.dirty = true; } + UIEventType::Input(Key::Left) => { + self.account_cursor = self.account_cursor.saturating_sub(1); + self.draft.headers_mut().insert( + "From".into(), + get_display_name(context, self.account_cursor), + ); + self.dirty = true; + return true; + } + UIEventType::Input(Key::Right) => { + if self.account_cursor + 1 < context.accounts.len() { + self.account_cursor += 1; + self.draft.headers_mut().insert( + "From".into(), + get_display_name(context, self.account_cursor), + ); + self.dirty = true; + } + return true; + } UIEventType::Input(Key::Char('\n')) => { use std::process::{Command, Stdio}; /* Kill input thread so that spawned command can be sole receiver of stdin */ @@ -177,6 +239,16 @@ impl Component for Composer { self.dirty = true; return true; } + UIEventType::Input(Key::Char('m')) => { + let mut f = + create_temp_file(self.draft.to_string().unwrap().as_str().as_bytes(), None); + context.replies.push_back(UIEvent { + id: 0, + event_type: UIEventType::EditDraft(f), + }); + self.draft = Draft::default(); + return true; + } _ => {} } false @@ -191,3 +263,12 @@ impl Component for Composer { self.pager.set_dirty(); } } + +fn get_display_name(context: &Context, idx: usize) -> String { + let settings = context.accounts[idx].runtime_settings.account(); + if let Some(d) = settings.display_name.as_ref() { + format!("{} <{}>", d, settings.identity) + } else { + settings.identity.to_string() + } +} diff --git a/ui/src/components/mail/mod.rs b/ui/src/components/mail/mod.rs index fc8acff1..56fcb590 100644 --- a/ui/src/components/mail/mod.rs +++ b/ui/src/components/mail/mod.rs @@ -206,11 +206,11 @@ impl AccountMenu { cell.set_fg(Color::Byte(243)); x += 1; continue; - }, + } c if c.is_whitespace() => { x += 1; continue; - }, + } _ => { break; } diff --git a/ui/src/conf/mod.rs b/ui/src/conf/mod.rs index 9fe82090..22737a0b 100644 --- a/ui/src/conf/mod.rs +++ b/ui/src/conf/mod.rs @@ -25,6 +25,7 @@ extern crate xdg; pub mod pager; use melib::conf::AccountSettings; +use melib::error::*; use pager::PagerSettings; use std::collections::HashMap; @@ -34,10 +35,36 @@ pub struct FileAccount { root_folder: String, format: String, sent_folder: String, + identity: String, + display_name: Option, threaded: bool, folders: Option>, } +impl From for AccountConf { + fn from(x: FileAccount) -> Self { + let format = x.format.to_lowercase(); + let sent_folder = x.sent_folder.clone(); + let root_folder = x.root_folder.clone(); + let identity = x.identity.clone(); + let display_name = x.display_name.clone(); + + let acc = AccountSettings { + name: String::new(), + root_folder, + format, + sent_folder, + identity, + display_name, + }; + + AccountConf { + account: acc, + conf: x, + } + } +} + impl FileAccount { pub fn folders(&self) -> Option<&HashMap> { self.folders.as_ref() @@ -85,45 +112,33 @@ pub struct Settings { use self::config::{Config, File, FileFormat}; impl FileSettings { - pub fn new() -> FileSettings { + pub fn new() -> Result { let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap(); let config_path = xdg_dirs .place_config_file("config") .expect("cannot create configuration directory"); - //let setts = Config::default().merge(File::new(config_path.to_str().unwrap_or_default(), config::FileFormat::Toml)).unwrap(); let mut s = Config::new(); let s = s.merge(File::new(config_path.to_str().unwrap(), FileFormat::Toml)); /* No point in returning without a config file. TODO: Error and exit instead of panic. */ - s.unwrap().deserialize().unwrap() + match s.unwrap().deserialize() { + Ok(v) => Ok(v), + Err(e) => Err(MeliError::new(e.to_string())), + } } } impl Settings { pub fn new() -> Settings { - let fs = FileSettings::new(); + let fs = FileSettings::new().unwrap_or_else(|e| panic!(format!("{}", e))); let mut s: HashMap = HashMap::new(); for (id, x) in fs.accounts { - let format = x.format.to_lowercase(); - let sent_folder = x.sent_folder.clone(); - let root_folder = x.root_folder.clone(); + let mut ac = AccountConf::from(x); + ac.account.set_name(id.clone()); - let acc = AccountSettings { - name: id.clone(), - root_folder, - format, - sent_folder, - }; - - s.insert( - id, - AccountConf { - account: acc, - conf: x, - }, - ); + s.insert(id, ac); } Settings {