diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index bfece643..5c62210d 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -96,6 +96,40 @@ impl ListingTrait for ListingComponent { } } +impl ListingComponent { + fn set_style(&mut self, new_style: IndexStyle) { + match new_style { + IndexStyle::Plain => { + if let Plain(_) = self { + return; + } + let mut new_l = PlainListing::default(); + let coors = self.coordinates(); + new_l.set_coordinates((coors.0, coors.1, None)); + *self = Plain(new_l); + } + IndexStyle::Threaded => { + if let Threaded(_) = self { + return; + } + let mut new_l = ThreadListing::default(); + let coors = self.coordinates(); + new_l.set_coordinates((coors.0, coors.1, None)); + *self = Threaded(new_l); + } + IndexStyle::Compact => { + if let Compact(_) = self { + return; + } + let mut new_l = CompactListing::default(); + let coors = self.coordinates(); + new_l.set_coordinates((coors.0, coors.1, None)); + *self = Compact(new_l); + } + } + } +} + #[derive(Debug)] pub struct Listing { component: ListingComponent, @@ -226,6 +260,14 @@ impl Component for Listing { } let folder_hash = context.accounts[self.cursor_pos.0].folders_order[self.cursor_pos.1]; + /* Check if per-folder configuration overrides general configuration */ + if let Some(index_style) = context + .accounts + .get(self.cursor_pos.0) + .and_then(|account| account.folder_confs(folder_hash).conf_override.index) + { + self.component.set_style(index_style); + }; // Inform State that we changed the current folder view. context .replies @@ -258,6 +300,14 @@ impl Component for Listing { } let folder_hash = context.accounts[self.cursor_pos.0].folders_order[self.cursor_pos.1]; + /* Check if per-folder configuration overrides general configuration */ + if let Some(index_style) = context + .accounts + .get(self.cursor_pos.0) + .and_then(|account| account.folder_confs(folder_hash).conf_override.index) + { + self.component.set_style(index_style); + }; // Inform State that we changed the current folder view. context .replies @@ -266,30 +316,15 @@ impl Component for Listing { } UIEvent::Action(ref action) => match action { Action::Listing(ListingAction::SetPlain) => { - if let Plain(_) = self.component { - return true; - } - let mut new_l = PlainListing::default(); - new_l.set_coordinates((self.cursor_pos.0, self.cursor_pos.1, None)); - self.component = Plain(new_l); + self.component.set_style(IndexStyle::Plain); return true; } Action::Listing(ListingAction::SetThreaded) => { - if let Threaded(_) = self.component { - return true; - } - let mut new_l = ThreadListing::default(); - new_l.set_coordinates((self.cursor_pos.0, self.cursor_pos.1, None)); - self.component = Threaded(new_l); + self.component.set_style(IndexStyle::Threaded); return true; } Action::Listing(ListingAction::SetCompact) => { - if let Compact(_) = self.component { - return true; - } - let mut new_l = CompactListing::default(); - new_l.set_coordinates((self.cursor_pos.0, self.cursor_pos.1, None)); - self.component = Compact(new_l); + self.component.set_style(IndexStyle::Compact); return true; } _ => {} @@ -437,7 +472,7 @@ impl From for ListingComponent { impl Listing { const DESCRIPTION: &'static str = "listing"; pub fn new(accounts: &[Account]) -> Self { - let accounts = accounts + let account_entries = accounts .iter() .enumerate() .map(|(i, a)| AccountMenuEntry { @@ -445,9 +480,20 @@ impl Listing { index: i, }) .collect(); + /* Check if per-folder configuration overrides general configuration */ + let component = if let Some(index_style) = accounts.get(0).and_then(|account| { + account + .folders_order + .get(0) + .and_then(|folder_hash| account.folder_confs(*folder_hash).conf_override.index) + }) { + ListingComponent::from(index_style) + } else { + Compact(Default::default()) + }; Listing { - component: Compact(Default::default()), - accounts, + component, + accounts: account_entries, visible: true, dirty: true, cursor_pos: (0, 0), diff --git a/ui/src/conf.rs b/ui/src/conf.rs index a55208e8..289781d4 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -89,17 +89,29 @@ impl ToggleFlag { } } +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +pub struct MailUIConf { + pub pager: Option, + pub notifications: Option, + pub shortcuts: Option, + pub mailer: Option, + pub identity: Option, + pub index: Option, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FolderConf { - rename: Option, + pub rename: Option, #[serde(default = "true_val")] - autoload: bool, + pub autoload: bool, #[serde(deserialize_with = "toggleflag_de", default)] - subscribe: ToggleFlag, + pub subscribe: ToggleFlag, #[serde(deserialize_with = "toggleflag_de", default)] - ignore: ToggleFlag, + pub ignore: ToggleFlag, #[serde(default = "none")] - usage: Option, + pub usage: Option, + #[serde(flatten)] + pub conf_override: MailUIConf, } impl Default for FolderConf { @@ -108,8 +120,9 @@ impl Default for FolderConf { rename: None, autoload: true, subscribe: ToggleFlag::Unset, - ignore: ToggleFlag::False, + ignore: ToggleFlag::Unset, usage: None, + conf_override: MailUIConf::default(), } } } @@ -132,7 +145,6 @@ pub struct FileAccount { #[serde(default = "none")] display_name: Option, - #[serde(deserialize_with = "index_from_str")] index: IndexStyle, /// A command to pipe html output before displaying it in a pager @@ -197,7 +209,10 @@ impl From for AccountConf { } if folder_confs[s].usage.is_none() { - let name = s.split('/').last().unwrap_or(""); + let name = s + .split(if s.contains('/') { '/' } else { '.' }) + .last() + .unwrap_or(""); folder_confs.get_mut(s).unwrap().usage = if name.eq_ignore_ascii_case("inbox") { Some(SpecialUseMailbox::Inbox) } else if name.eq_ignore_ascii_case("archive") { @@ -265,9 +280,9 @@ struct FileSettings { #[derive(Debug, Clone, Default)] pub struct AccountConf { - account: AccountSettings, - conf: FileAccount, - folder_confs: HashMap, + pub(crate) account: AccountSettings, + pub(crate) conf: FileAccount, + pub(crate) folder_confs: HashMap, } impl AccountConf { @@ -380,7 +395,7 @@ impl Settings { } } -#[derive(Copy, Debug, Clone, Serialize, Deserialize)] +#[derive(Copy, Debug, Clone, Hash, PartialEq)] pub enum IndexStyle { Plain, Threaded, @@ -393,19 +408,6 @@ impl Default for IndexStyle { } } -fn index_from_str<'de, D>(deserializer: D) -> std::result::Result -where - D: Deserializer<'de>, -{ - let s = ::deserialize(deserializer)?; - match s.as_str() { - "Plain" | "plain" => Ok(IndexStyle::Plain), - "Threaded" | "threaded" => Ok(IndexStyle::Threaded), - "Compact" | "compact" => Ok(IndexStyle::Compact), - _ => Err(de::Error::custom("invalid `index` value")), - } -} - fn non_empty_string<'de, D>(deserializer: D) -> std::result::Result, D::Error> where D: Deserializer<'de>, @@ -468,3 +470,31 @@ mod default_vals { None } } + +impl<'de> Deserialize<'de> for IndexStyle { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + let s = ::deserialize(deserializer)?; + match s.as_str() { + "Plain" | "plain" => Ok(IndexStyle::Plain), + "Threaded" | "threaded" => Ok(IndexStyle::Threaded), + "Compact" | "compact" => Ok(IndexStyle::Compact), + _ => Err(de::Error::custom("invalid `index` value")), + } + } +} + +impl Serialize for IndexStyle { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + IndexStyle::Plain => serializer.serialize_str("plain"), + IndexStyle::Threaded => serializer.serialize_str("threaded"), + IndexStyle::Compact => serializer.serialize_str("compact"), + } + } +} diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs index 5f9bcee6..53d3423e 100644 --- a/ui/src/conf/accounts.rs +++ b/ui/src/conf/accounts.rs @@ -23,7 +23,7 @@ * Account management from user configuration. */ -use super::AccountConf; +use super::{AccountConf, FolderConf}; use fnv::FnvHashMap; use melib::async_workers::{Async, AsyncBuilder, AsyncStatus}; use melib::backends::{ @@ -116,8 +116,9 @@ impl MailboxEntry { pub struct Account { name: String, pub(crate) folders: FnvHashMap, + pub(crate) folder_confs: FnvHashMap, pub(crate) folders_order: Vec, - folder_names: FnvHashMap, + pub(crate) folder_names: FnvHashMap, tree: Vec, sent_folder: Option, pub(crate) collection: Collection, @@ -205,6 +206,7 @@ impl Account { let mut workers: FnvHashMap = FnvHashMap::default(); let notify_fn = Arc::new(notify_fn); let mut folder_names = FnvHashMap::default(); + let mut folder_confs = FnvHashMap::default(); let mut sent_folder = None; for f in ref_folders.values_mut() { @@ -221,6 +223,7 @@ impl Account { } _ => {} } + folder_confs.insert(f.hash(), settings.folder_confs[f.path()].clone()); folder_names.insert(f.hash(), f.path().to_string()); } @@ -300,6 +303,7 @@ impl Account { Account { name, folders, + folder_confs, folders_order, folder_names, tree, @@ -669,6 +673,10 @@ impl Account { pub fn folder_operation(&mut self, path: &str, op: FolderOperation) -> Result<()> { self.backend.folder_operation(path, op) } + + pub fn folder_confs(&self, folder_hash: FolderHash) -> &FolderConf { + &self.folder_confs[&folder_hash] + } } impl Index for Account {