From 647cb10b33b5dabc3cb5f5ee2d74205600f64fcc Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 8 Feb 2020 23:42:31 +0200 Subject: [PATCH] ui: Use FolderHash instead of usize for folder cursor Use FolderHash directly as a cursor type for folders within an account isntead of having a usize (being the order of the folder within the account) and figuring out the folder_hash everytime it's needed. Add OfflineListing for offline accounts and AccountStatusChange event. --- src/bin.rs | 2 +- src/components/mail/listing.rs | 343 +++++++++---------- src/components/mail/listing/compact.rs | 54 +-- src/components/mail/listing/conversations.rs | 62 ++-- src/components/mail/listing/offline.rs | 103 ++++++ src/components/mail/listing/plain.rs | 50 +-- src/components/mail/listing/thread.rs | 35 +- src/components/mail/status.rs | 3 +- src/components/mail/view.rs | 10 +- src/components/mail/view/thread.rs | 4 +- src/conf/accounts.rs | 108 +++--- src/state.rs | 13 +- src/types.rs | 1 + 13 files changed, 392 insertions(+), 396 deletions(-) create mode 100644 src/components/mail/listing/offline.rs diff --git a/src/bin.rs b/src/bin.rs index 331f9120..7907d8fd 100644 --- a/src/bin.rs +++ b/src/bin.rs @@ -305,7 +305,7 @@ fn run_app() -> Result<()> { let mut state = State::new(sender, receiver.clone())?; let window = Box::new(Tabbed::new(vec![ - Box::new(listing::Listing::new(&state.context.accounts)), + Box::new(listing::Listing::new(&mut state.context)), Box::new(ContactList::new(&state.context)), Box::new(StatusPanel::new()), ])); diff --git a/src/components/mail/listing.rs b/src/components/mail/listing.rs index 6806f9eb..71b3132d 100644 --- a/src/components/mail/listing.rs +++ b/src/components/mail/listing.rs @@ -36,6 +36,9 @@ pub use self::thread::*; mod plain; pub use self::plain::*; +mod offline; +pub use self::offline::*; + #[derive(Debug, Default, Clone)] pub struct DataColumns { pub columns: [CellBuffer; 12], @@ -122,6 +125,7 @@ struct AccountMenuEntry { name: String, // Index in the config account vector. index: usize, + entries: SmallVec<[(usize, FolderHash); 16]>, } pub trait MailListingTrait: ListingTrait { @@ -206,8 +210,8 @@ pub trait MailListingTrait: ListingTrait { } pub trait ListingTrait: Component { - fn coordinates(&self) -> (usize, usize); - fn set_coordinates(&mut self, _: (usize, usize)); + fn coordinates(&self) -> (usize, FolderHash); + fn set_coordinates(&mut self, _: (usize, FolderHash)); fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context); fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context); fn filter(&mut self, _filter_term: &str, _context: &Context) {} @@ -220,6 +224,7 @@ pub enum ListingComponent { Threaded(ThreadListing), Compact(CompactListing), Conversations(ConversationsListing), + Offline(OfflineListing), } use crate::ListingComponent::*; @@ -232,6 +237,7 @@ impl core::ops::Deref for ListingComponent { Plain(ref l) => l, Threaded(ref l) => l, Conversations(ref l) => l, + Offline(ref l) => l, } } } @@ -243,6 +249,7 @@ impl core::ops::DerefMut for ListingComponent { Plain(l) => l, Threaded(l) => l, Conversations(l) => l, + Offline(l) => l, } } } @@ -254,37 +261,25 @@ impl ListingComponent { if let Plain(_) = self { return; } - let mut new_l = PlainListing::default(); - let coors = self.coordinates(); - new_l.set_coordinates((coors.0, coors.1)); - *self = Plain(new_l); + *self = Plain(PlainListing::new(self.coordinates())); } 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)); - *self = Threaded(new_l); + *self = Threaded(ThreadListing::new(self.coordinates())); } 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)); - *self = Compact(new_l); + *self = Compact(CompactListing::new(self.coordinates())); } IndexStyle::Conversations => { if let Conversations(_) = self { return; } - let mut new_l = ConversationsListing::default(); - let coors = self.coordinates(); - new_l.set_coordinates((coors.0, coors.1)); - *self = Conversations(new_l); + *self = Conversations(ConversationsListing::new(self.coordinates())); } } } @@ -315,6 +310,7 @@ impl fmt::Display for Listing { Plain(ref l) => write!(f, "{}", l), Threaded(ref l) => write!(f, "{}", l), Conversations(ref l) => write!(f, "{}", l), + Offline(ref l) => write!(f, "{}", l), } } } @@ -331,7 +327,6 @@ impl Component for Listing { if !is_valid_area!(area) { return; } - self.theme_default = crate::conf::value(context, "theme_default"); let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let total_cols = get_x(bottom_right) - get_x(upper_left); @@ -431,12 +426,7 @@ impl Component for Listing { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { match event { UIEvent::StartupCheck(ref f) => { - if context.accounts[self.component.coordinates().0] - .folders_order - .get(self.component.coordinates().1) - .map(|&folder_hash| *f == folder_hash) - .unwrap_or(false) - { + if self.component.coordinates().1 == *f { if !self.startup_checks_rate.tick() { return false; } @@ -445,27 +435,52 @@ impl Component for Listing { UIEvent::Timer(n) if *n == self.startup_checks_rate.id() => { if self.startup_checks_rate.active { self.startup_checks_rate.reset(); - if let Some(folder_hash) = context.accounts[self.component.coordinates().0] - .folders_order - .get(self.component.coordinates().1) - { - return self - .process_event(&mut UIEvent::StartupCheck(*folder_hash), context); - } + return self.process_event( + &mut UIEvent::StartupCheck(self.component.coordinates().1), + context, + ); } } + UIEvent::AccountStatusChange(account_index) => { + if self.cursor_pos.0 == *account_index { + self.change_account(context); + } else { + self.accounts[*account_index].entries = context.accounts[*account_index] + .list_folders() + .into_iter() + .filter(|folder_node| { + context.accounts[*account_index].ref_folders()[&folder_node.hash] + .is_subscribed() + }) + .map(|f| (f.depth, f.hash)) + .collect::<_>(); + self.set_dirty(true); + } + return true; + } UIEvent::MailboxDelete((account_index, _folder_hash)) | UIEvent::MailboxCreate((account_index, _folder_hash)) => { + self.accounts[*account_index].entries = context.accounts[*account_index] + .list_folders() + .into_iter() + .filter(|folder_node| { + context.accounts[*account_index].ref_folders()[&folder_node.hash] + .is_subscribed() + }) + .map(|f| (f.depth, f.hash)) + .collect::<_>(); if self.cursor_pos.0 == *account_index { self.cursor_pos.1 = std::cmp::min( - context.accounts[*account_index].len() - 1, + self.accounts[self.cursor_pos.0].entries.len() - 1, self.cursor_pos.1, ); - self.component - .set_coordinates((self.cursor_pos.0, self.cursor_pos.1)); + self.component.set_coordinates(( + self.cursor_pos.0, + self.accounts[self.cursor_pos.0].entries[self.cursor_pos.1].1, + )); self.component.refresh_mailbox(context, true); - self.set_dirty(true); } + self.set_dirty(true); return true; } _ => {} @@ -496,15 +511,15 @@ impl Component for Listing { .push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); return true; }; - let folder_length = context.accounts[self.cursor_pos.0].len(); match k { - k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"]) - && folder_length > 0 => - { - if self.cursor_pos.1 + amount < folder_length { + k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"]) => { + if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0] + .entries + .get(self.cursor_pos.1 + amount) + { self.cursor_pos.1 += amount; self.component - .set_coordinates((self.cursor_pos.0, self.cursor_pos.1)); + .set_coordinates((self.cursor_pos.0, *folder_hash)); self.set_dirty(true); } else { return true; @@ -512,25 +527,32 @@ impl Component for Listing { } k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) => { if self.cursor_pos.1 >= amount { - self.cursor_pos.1 -= amount; - self.component - .set_coordinates((self.cursor_pos.0, self.cursor_pos.1)); - self.set_dirty(true); + if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0] + .entries + .get(self.cursor_pos.1 - amount) + { + self.cursor_pos.1 -= amount; + self.component + .set_coordinates((self.cursor_pos.0, *folder_hash)); + self.set_dirty(true); + } else { + return true; + } } else { return true; } } - _ => return false, + _ => {} } - /* Account might have no folders yet if it's offline */ - if let Some(&folder_hash) = context.accounts[self.cursor_pos.0] - .folders_order + if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0] + .entries .get(self.cursor_pos.1) { + /* Account might have no folders yet if it's offline */ /* 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_style + account.folder_confs(*folder_hash).conf_override.index_style }) { self.component.set_style(index_style); @@ -572,8 +594,6 @@ impl Component for Listing { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_account"]) => { if self.cursor_pos.0 + amount < self.accounts.len() { self.cursor_pos = (self.cursor_pos.0 + amount, 0); - self.component.set_coordinates((self.cursor_pos.0, 0)); - self.set_dirty(true); } else { return true; } @@ -581,40 +601,14 @@ impl Component for Listing { k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_account"]) => { if self.cursor_pos.0 >= amount { self.cursor_pos = (self.cursor_pos.0 - amount, 0); - self.component.set_coordinates((self.cursor_pos.0, 0)); - self.set_dirty(true); } else { return true; } } _ => return false, } + self.change_account(context); - /* Account might have no folders yet if it's offline */ - if let Some(&folder_hash) = context.accounts[self.cursor_pos.0] - .folders_order - .get(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_style - }) - { - self.component.set_style(index_style); - } else if let Some(index_style) = context - .accounts - .get(self.cursor_pos.0) - .and_then(|account| Some(account.settings.conf.index_style())) - { - self.component.set_style(index_style); - } - } - context - .replies - .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus( - self.get_status(context), - ))); return true; } UIEvent::Action(ref action) => match action { @@ -876,57 +870,55 @@ impl Component for Listing { } } -impl From for ListingComponent { - fn from(index_style: IndexStyle) -> Self { +impl From<(IndexStyle, (usize, FolderHash))> for ListingComponent { + fn from((index_style, coordinates): (IndexStyle, (usize, FolderHash))) -> Self { match index_style { - IndexStyle::Plain => Plain(Default::default()), - IndexStyle::Threaded => Threaded(Default::default()), - IndexStyle::Compact => Compact(Default::default()), - IndexStyle::Conversations => Conversations(Default::default()), + IndexStyle::Plain => Plain(PlainListing::new(coordinates)), + IndexStyle::Threaded => Threaded(ThreadListing::new(coordinates)), + IndexStyle::Compact => Compact(CompactListing::new(coordinates)), + IndexStyle::Conversations => Conversations(ConversationsListing::new(coordinates)), } } } impl Listing { const DESCRIPTION: &'static str = "listing"; - pub fn new(accounts: &[Account]) -> Self { - let account_entries = accounts + pub fn new(context: &mut Context) -> Self { + let account_entries: Vec = context + .accounts .iter() .enumerate() - .map(|(i, a)| AccountMenuEntry { - name: a.name().to_string(), - index: i, + .map(|(i, a)| { + let entries: SmallVec<[(usize, FolderHash); 16]> = a + .list_folders() + .into_iter() + .filter(|folder_node| a.ref_folders()[&folder_node.hash].is_subscribed()) + .map(|f| (f.depth, f.hash)) + .collect::<_>(); + + AccountMenuEntry { + name: a.name().to_string(), + index: i, + entries, + } }) .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_style - }) - }) { - ListingComponent::from(index_style) - } else if let Some(index_style) = accounts - .get(0) - .and_then(|account| Some(account.settings.conf.index_style())) - { - ListingComponent::from(index_style) - } else { - Conversations(Default::default()) - }; - Listing { - component, + let mut ret = Listing { + component: Offline(OfflineListing::new((0, 0))), accounts: account_entries, visible: true, dirty: true, cursor_pos: (0, 0), startup_checks_rate: RateLimit::new(2, 1000), - theme_default: ThemeAttribute::default(), + theme_default: conf::value(context, "theme_default"), id: ComponentId::new_v4(), show_divider: false, menu_visibility: true, ratio: 90, cmd_buf: String::with_capacity(4), - } + }; + ret.change_account(context); + ret } fn draw_menu(&mut self, grid: &mut CellBuffer, mut area: Area, context: &mut Context) { @@ -969,86 +961,31 @@ impl Listing { debug!("BUG: invalid area in print_account"); } // Each entry and its index in the account - let entries: FnvHashMap = context.accounts[a.index] - .list_folders() - .into_iter() - .map(|f| (f.hash(), f)) - .collect(); - let folders_order: FnvHashMap = context.accounts[a.index] - .folders_order() - .iter() - .enumerate() - .map(|(i, &fh)| (fh, i)) - .collect(); + let folders: FnvHashMap = + context.accounts[a.index].ref_folders().clone(); let upper_left = upper_left!(area); let bottom_right = bottom_right!(area); let must_highlight_account: bool = self.cursor_pos.0 == a.index; - let mut inc = 0; - let mut depth = 0; let mut lines: Vec<(usize, usize, FolderHash, Option)> = Vec::new(); - /* Gather the folder tree structure in `lines` recursively */ - fn print( - folder_idx: FolderHash, - depth: &mut usize, - inc: &mut usize, - entries: &FnvHashMap, - folders_order: &FnvHashMap, - lines: &mut Vec<(usize, usize, FolderHash, Option)>, - index: usize, //account index - context: &mut Context, - ) { - match context.accounts[index].status(entries[&folder_idx].hash()) { - Ok(_) => { - lines.push(( - *depth, - *inc, - folder_idx, - entries[&folder_idx].count().ok().map(|(v, _)| v), - )); + for (i, &(depth, folder_hash)) in a.entries.iter().enumerate() { + if folders[&folder_hash].is_subscribed() { + match context.accounts[a.index].status(folder_hash) { + Ok(_) => { + lines.push(( + depth, + i, + folder_hash, + folders[&folder_hash].count().ok().map(|(v, _)| v), + )); + } + Err(_) => { + lines.push((depth, i, folder_hash, None)); + } } - Err(_) => { - lines.push((*depth, *inc, folder_idx, None)); - } - } - *inc += 1; - let mut children: Vec = entries[&folder_idx].children().to_vec(); - children - .sort_unstable_by(|a, b| folders_order[a].partial_cmp(&folders_order[b]).unwrap()); - *depth += 1; - for child in children { - print( - child, - depth, - inc, - entries, - folders_order, - lines, - index, - context, - ); - } - *depth -= 1; - } - let mut keys = entries.keys().cloned().collect::>(); - keys.sort_unstable_by(|a, b| folders_order[a].partial_cmp(&folders_order[b]).unwrap()); - - /* Start with roots */ - for f in keys { - if entries[&f].parent().is_none() { - print( - f, - &mut depth, - &mut inc, - &entries, - &folders_order, - &mut lines, - a.index, - context, - ); } } @@ -1155,7 +1092,7 @@ impl Listing { None, ); let (x, _) = write_string_to_grid( - entries[&folder_idx].name(), + folders[&folder_idx].name(), grid, att.fg, att.bg, @@ -1207,4 +1144,46 @@ impl Listing { idx - 1 } } + + fn change_account(&mut self, context: &mut Context) { + self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0] + .list_folders() + .into_iter() + .filter(|folder_node| { + context.accounts[self.cursor_pos.0].ref_folders()[&folder_node.hash].is_subscribed() + }) + .map(|f| (f.depth, f.hash)) + .collect::<_>(); + /* Account might have no folders yet if it's offline */ + if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0] + .entries + .get(self.cursor_pos.1) + { + self.component + .set_coordinates((self.cursor_pos.0, *folder_hash)); + /* 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_style) + { + self.component.set_style(index_style); + } else if let Some(index_style) = context + .accounts + .get(self.cursor_pos.0) + .and_then(|account| Some(account.settings.conf.index_style())) + { + self.component.set_style(index_style); + } + } else { + /* Set to dummy */ + self.component = Offline(OfflineListing::new((self.cursor_pos.0, 0))); + } + self.set_dirty(true); + context + .replies + .push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(debug!( + self.get_status(context) + )))); + } } diff --git a/src/components/mail/listing/compact.rs b/src/components/mail/listing/compact.rs index b16199bc..1c1c7ef0 100644 --- a/src/components/mail/listing/compact.rs +++ b/src/components/mail/listing/compact.rs @@ -49,9 +49,8 @@ macro_rules! address_list { #[derive(Debug)] pub struct CompactListing { /// (x, y, z): x is accounts, y is folders, z is index inside a folder. - cursor_pos: (usize, usize, usize), - new_cursor_pos: (usize, usize, usize), - folder_hash: FolderHash, + cursor_pos: (usize, FolderHash, usize), + new_cursor_pos: (usize, FolderHash, usize), length: usize, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), @@ -114,16 +113,6 @@ impl MailListingTrait for CompactListing { } self.cursor_pos.1 = self.new_cursor_pos.1; self.cursor_pos.0 = self.new_cursor_pos.0; - self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0] - .folders_order - .get(self.cursor_pos.1) - { - *h - } else { - self.cursor_pos.1 = old_cursor_pos.1; - self.dirty = false; - return; - }; self.color_cache = ColorCache { unseen: crate::conf::value(context, "mail.listing.compact.unseen"), @@ -145,7 +134,7 @@ impl MailListingTrait for CompactListing { // Get mailbox as a reference. // - match context.accounts[self.cursor_pos.0].status(self.folder_hash) { + match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { Ok(()) => {} Err(_) => { let default_cell = { @@ -156,7 +145,7 @@ impl MailListingTrait for CompactListing { ret }; let message: String = - context.accounts[self.cursor_pos.0][self.folder_hash].to_string(); + context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string(); self.data_columns.columns[0] = CellBuffer::new_with_context(message.len(), 1, default_cell, context); self.length = 0; @@ -173,7 +162,7 @@ impl MailListingTrait for CompactListing { } } - let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.folder_hash]; + let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.cursor_pos.1]; self.all_threads.clear(); let mut roots = threads.roots(); threads.group_inner_sort_by( @@ -198,11 +187,11 @@ impl MailListingTrait for CompactListing { } impl ListingTrait for CompactListing { - fn coordinates(&self) -> (usize, usize) { + fn coordinates(&self) -> (usize, FolderHash) { (self.new_cursor_pos.0, self.new_cursor_pos.1) } - fn set_coordinates(&mut self, coordinates: (usize, usize)) { + fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) { self.new_cursor_pos = (coordinates.0, coordinates.1, 0); self.unfocused = false; self.view = ThreadView::default(); @@ -219,7 +208,7 @@ impl ListingTrait for CompactListing { let thread_hash = self.get_thread_under_cursor(idx); let account = &context.accounts[self.cursor_pos.0]; - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; let thread = threads.thread_ref(thread_hash); let fg_color = if thread.unseen() > 0 { @@ -541,9 +530,9 @@ impl ListingTrait for CompactListing { } let account = &context.accounts[self.cursor_pos.0]; - match account.search(&self.filter_term, self.sort, self.folder_hash) { + match account.search(&self.filter_term, self.sort, self.cursor_pos.1) { Ok(results) => { - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; for env_hash in results { if !account.collection.contains_key(&env_hash) { continue; @@ -633,19 +622,12 @@ impl fmt::Display for CompactListing { } } -impl Default for CompactListing { - fn default() -> Self { - CompactListing::new() - } -} - impl CompactListing { const DESCRIPTION: &'static str = "compact listing"; - fn new() -> Self { + pub fn new(coordinates: (usize, FolderHash)) -> Self { CompactListing { cursor_pos: (0, 1, 0), - new_cursor_pos: (0, 0, 0), - folder_hash: 0, + new_cursor_pos: (coordinates.0, coordinates.1, 0), length: 0, sort: (Default::default(), Default::default()), subsort: (SortField::Date, SortOrder::Desc), @@ -674,7 +656,7 @@ impl CompactListing { hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); - let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.folder_hash]; + let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1]; let mut tags = String::new(); let mut colors: SmallVec<[_; 8]> = SmallVec::new(); let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap(); @@ -1026,7 +1008,7 @@ impl CompactListing { fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) { let account = &context.accounts[self.cursor_pos.0]; - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; let thread = threads.thread_ref(thread_hash); let thread_node_hash = threads.thread_group_iter(thread_hash).next().unwrap().1; if let Some(env_hash) = threads.thread_nodes()[&thread_node_hash].message() { @@ -1310,7 +1292,7 @@ impl Component for CompactListing { account .collection .threads - .entry(self.folder_hash) + .entry(self.cursor_pos.1) .and_modify(|threads| { let is_snoozed = threads.thread_ref(thread).snoozed(); threads.thread_ref_mut(thread).set_snoozed(!is_snoozed); @@ -1328,18 +1310,18 @@ impl Component for CompactListing { } match *event { UIEvent::MailboxUpdate((ref idxa, ref idxf)) - if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) => + if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) => { self.refresh_mailbox(context, false); self.set_dirty(true); } - UIEvent::StartupCheck(ref f) if *f == self.folder_hash => { + UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => { self.refresh_mailbox(context, false); self.set_dirty(true); } UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => { let account = &context.accounts[self.cursor_pos.0]; - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; if !account.collection.contains_key(&new_hash) { return false; } diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index fe10465e..41448606 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -28,9 +28,8 @@ use std::iter::FromIterator; #[derive(Debug)] pub struct ConversationsListing { /// (x, y, z): x is accounts, y is folders, z is index inside a folder. - cursor_pos: (usize, usize, usize), - new_cursor_pos: (usize, usize, usize), - folder_hash: FolderHash, + cursor_pos: (usize, FolderHash, usize), + new_cursor_pos: (usize, FolderHash, usize), length: usize, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), @@ -84,7 +83,7 @@ impl MailListingTrait for ConversationsListing { /// chosen. fn refresh_mailbox(&mut self, context: &mut Context, force: bool) { self.dirty = true; - let old_folder_hash = self.folder_hash; + let old_folder_hash = self.cursor_pos.1; let old_cursor_pos = self.cursor_pos; if !(self.cursor_pos.0 == self.new_cursor_pos.0 && self.cursor_pos.1 == self.new_cursor_pos.1) @@ -94,16 +93,6 @@ impl MailListingTrait for ConversationsListing { } self.cursor_pos.1 = self.new_cursor_pos.1; self.cursor_pos.0 = self.new_cursor_pos.0; - self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0] - .folders_order - .get(self.cursor_pos.1) - { - *h - } else { - self.cursor_pos.1 = old_cursor_pos.1; - self.dirty = false; - return; - }; self.color_cache = ColorCache { theme_default: crate::conf::value(context, "mail.listing.conversations"), @@ -130,7 +119,7 @@ impl MailListingTrait for ConversationsListing { } // Get mailbox as a reference. // - match context.accounts[self.cursor_pos.0].status(self.folder_hash) { + match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { Ok(()) => {} Err(_) => { let default_cell = { @@ -141,7 +130,7 @@ impl MailListingTrait for ConversationsListing { ret }; let message: String = - context.accounts[self.cursor_pos.0][self.folder_hash].to_string(); + context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string(); self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context); self.length = 0; @@ -158,7 +147,7 @@ impl MailListingTrait for ConversationsListing { } } - let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.folder_hash]; + let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.cursor_pos.1]; self.all_threads.clear(); let mut roots = threads.roots(); threads.group_inner_sort_by( @@ -172,7 +161,7 @@ impl MailListingTrait for ConversationsListing { Box::new(roots.into_iter()) as Box>, ); - if !force && old_cursor_pos == self.new_cursor_pos && old_folder_hash == self.folder_hash { + if !force && old_cursor_pos == self.new_cursor_pos && old_folder_hash == self.cursor_pos.1 { self.view.update(context); } else if self.unfocused { let thread_group = self.get_thread_under_cursor(self.cursor_pos.2); @@ -183,11 +172,11 @@ impl MailListingTrait for ConversationsListing { } impl ListingTrait for ConversationsListing { - fn coordinates(&self) -> (usize, usize) { + fn coordinates(&self) -> (usize, FolderHash) { (self.new_cursor_pos.0, self.new_cursor_pos.1) } - fn set_coordinates(&mut self, coordinates: (usize, usize)) { + fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) { self.new_cursor_pos = (coordinates.0, coordinates.1, 0); self.new_cursor_pos = (coordinates.0, coordinates.1, 0); self.unfocused = false; @@ -205,7 +194,7 @@ impl ListingTrait for ConversationsListing { let thread_hash = self.get_thread_under_cursor(idx); let account = &context.accounts[self.cursor_pos.0]; - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; let thread = threads.thread_ref(thread_hash); let fg_color = if thread.unseen() > 0 { @@ -475,9 +464,9 @@ impl ListingTrait for ConversationsListing { } let account = &context.accounts[self.cursor_pos.0]; - match account.search(&self.filter_term, self.sort, self.folder_hash) { + match account.search(&self.filter_term, self.sort, self.cursor_pos.1) { Ok(results) => { - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; for env_hash in results { if !account.collection.contains_key(&env_hash) { continue; @@ -566,19 +555,12 @@ impl fmt::Display for ConversationsListing { } } -impl Default for ConversationsListing { - fn default() -> Self { - ConversationsListing::new() - } -} - impl ConversationsListing { const DESCRIPTION: &'static str = "compact listing"; - fn new() -> Self { + pub fn new(coordinates: (usize, FolderHash)) -> Self { ConversationsListing { cursor_pos: (0, 1, 0), - new_cursor_pos: (0, 0, 0), - folder_hash: 0, + new_cursor_pos: (coordinates.0, coordinates.1, 0), length: 0, sort: (Default::default(), Default::default()), subsort: (SortField::Date, SortOrder::Desc), @@ -608,7 +590,7 @@ impl ConversationsListing { hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); - let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.folder_hash]; + let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1]; let mut tags = String::new(); let mut colors = SmallVec::new(); let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap(); @@ -935,7 +917,7 @@ impl ConversationsListing { fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) { let account = &context.accounts[self.cursor_pos.0]; - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; let thread = threads.thread_ref(thread_hash); let thread_node_hash = threads.thread_group_iter(thread_hash).next().unwrap().1; let idx: usize = self.order[&thread_hash]; @@ -1218,7 +1200,7 @@ impl Component for ConversationsListing { } UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => { let account = &context.accounts[self.cursor_pos.0]; - let threads = &account.collection.threads[&self.folder_hash]; + let threads = &account.collection.threads[&self.cursor_pos.1]; if !account.collection.contains_key(&new_hash) { return false; } @@ -1243,7 +1225,7 @@ impl Component for ConversationsListing { self.subsort = (*field, *order); // FIXME subsort //if !self.filtered_selection.is_empty() { - // let threads = &account.collection.threads[&self.folder_hash]; + // let threads = &account.collection.threads[&self.cursor_pos.1]; // threads.vec_inner_sort_by(&mut self.filtered_selection, self.sort, &account.collection); //} else { // self.refresh_mailbox(context, false); @@ -1257,7 +1239,7 @@ impl Component for ConversationsListing { self.sort = (*field, *order); if !self.filtered_selection.is_empty() { let threads = &context.accounts[self.cursor_pos.0].collection.threads - [&self.folder_hash]; + [&self.cursor_pos.1]; threads.vec_inner_sort_by( &mut self.filtered_selection, self.sort, @@ -1276,7 +1258,7 @@ impl Component for ConversationsListing { account .collection .threads - .entry(self.folder_hash) + .entry(self.cursor_pos.1) .and_modify(|threads| { let is_snoozed = threads.thread_ref(thread).snoozed(); threads.thread_ref_mut(thread).set_snoozed(!is_snoozed); @@ -1292,12 +1274,12 @@ impl Component for ConversationsListing { } match *event { UIEvent::MailboxUpdate((ref idxa, ref idxf)) - if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) => + if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) => { self.refresh_mailbox(context, false); self.set_dirty(true); } - UIEvent::StartupCheck(ref f) if *f == self.folder_hash => { + UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => { self.refresh_mailbox(context, false); self.set_dirty(true); } diff --git a/src/components/mail/listing/offline.rs b/src/components/mail/listing/offline.rs new file mode 100644 index 00000000..26b3ed91 --- /dev/null +++ b/src/components/mail/listing/offline.rs @@ -0,0 +1,103 @@ +/* + * meli + * + * Copyright 2020 Manos Pitsidianakis + * + * This file is part of meli. + * + * meli is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * meli is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with meli. If not, see . + */ + +use super::*; +use crate::components::utilities::PageMovement; + +#[derive(Debug)] +pub struct OfflineListing { + cursor_pos: (usize, FolderHash), + _row_updates: SmallVec<[ThreadHash; 8]>, + + id: ComponentId, +} + +impl MailListingTrait for OfflineListing { + fn row_updates(&mut self) -> &mut SmallVec<[ThreadHash; 8]> { + &mut self._row_updates + } + + fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> { + return SmallVec::new(); + } + + /// chosen. + fn refresh_mailbox(&mut self, _context: &mut Context, _force: bool) {} +} + +impl ListingTrait for OfflineListing { + fn coordinates(&self) -> (usize, FolderHash) { + self.cursor_pos + } + + fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) { + self.cursor_pos = coordinates; + } + + fn highlight_line( + &mut self, + _grid: &mut CellBuffer, + _area: Area, + _idx: usize, + _context: &Context, + ) { + } + + fn draw_list(&mut self, _: &mut CellBuffer, _: Area, _: &mut Context) {} + + fn filter(&mut self, _: &str, _: &Context) {} + + fn set_movement(&mut self, _: PageMovement) {} +} + +impl fmt::Display for OfflineListing { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "mail") + } +} + +impl OfflineListing { + pub fn new(cursor_pos: (usize, FolderHash)) -> Self { + OfflineListing { + cursor_pos, + _row_updates: SmallVec::new(), + id: ComponentId::new_v4(), + } + } +} + +impl Component for OfflineListing { + fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {} + fn process_event(&mut self, _event: &mut UIEvent, _context: &mut Context) -> bool { + false + } + fn is_dirty(&self) -> bool { + false + } + fn set_dirty(&mut self, _value: bool) {} + + fn id(&self) -> ComponentId { + self.id + } + fn set_id(&mut self, id: ComponentId) { + self.id = id; + } +} diff --git a/src/components/mail/listing/plain.rs b/src/components/mail/listing/plain.rs index 9cb3383f..62b95a2e 100644 --- a/src/components/mail/listing/plain.rs +++ b/src/components/mail/listing/plain.rs @@ -48,9 +48,8 @@ macro_rules! address_list { #[derive(Debug)] pub struct PlainListing { /// (x, y, z): x is accounts, y is folders, z is index inside a folder. - cursor_pos: (usize, usize, usize), - new_cursor_pos: (usize, usize, usize), - folder_hash: FolderHash, + cursor_pos: (usize, FolderHash, usize), + new_cursor_pos: (usize, FolderHash, usize), length: usize, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), @@ -115,16 +114,6 @@ impl MailListingTrait for PlainListing { } self.cursor_pos.1 = self.new_cursor_pos.1; self.cursor_pos.0 = self.new_cursor_pos.0; - self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0] - .folders_order - .get(self.cursor_pos.1) - { - *h - } else { - self.cursor_pos.1 = old_cursor_pos.1; - self.dirty = false; - return; - }; self.color_cache = ColorCache { unseen: crate::conf::value(context, "mail.listing.plain.unseen"), @@ -145,7 +134,7 @@ impl MailListingTrait for PlainListing { // Get mailbox as a reference. // - match context.accounts[self.cursor_pos.0].status(self.folder_hash) { + match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { Ok(()) => {} Err(_) => { let default_cell = { @@ -156,7 +145,7 @@ impl MailListingTrait for PlainListing { ret }; let message: String = - context.accounts[self.cursor_pos.0][self.folder_hash].to_string(); + context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string(); self.data_columns.columns[0] = CellBuffer::new_with_context(message.len(), 1, default_cell, context); self.length = 0; @@ -172,7 +161,7 @@ impl MailListingTrait for PlainListing { return; } } - self.local_collection = context.accounts[self.cursor_pos.0][self.folder_hash] + self.local_collection = context.accounts[self.cursor_pos.0][self.cursor_pos.1] .unwrap() .envelopes .iter() @@ -183,7 +172,7 @@ impl MailListingTrait for PlainListing { .envelopes .read() .unwrap(); - self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.folder_hash] + self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.cursor_pos.1] .unwrap() .envelopes .iter() @@ -205,11 +194,11 @@ impl MailListingTrait for PlainListing { } impl ListingTrait for PlainListing { - fn coordinates(&self) -> (usize, usize) { + fn coordinates(&self) -> (usize, FolderHash) { (self.new_cursor_pos.0, self.new_cursor_pos.1) } - fn set_coordinates(&mut self, coordinates: (usize, usize)) { + fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) { self.new_cursor_pos = (coordinates.0, coordinates.1, 0); self.unfocused = false; self.view = MailView::default(); @@ -523,7 +512,7 @@ impl ListingTrait for PlainListing { } let account = &context.accounts[self.cursor_pos.0]; - match account.search(&self.filter_term, self.sort, self.folder_hash) { + match account.search(&self.filter_term, self.sort, self.cursor_pos.1) { Ok(results) => { for env_hash in results { if !account.collection.contains_key(&env_hash) { @@ -599,19 +588,12 @@ impl fmt::Display for PlainListing { } } -impl Default for PlainListing { - fn default() -> Self { - PlainListing::new() - } -} - impl PlainListing { const DESCRIPTION: &'static str = "plain listing"; - fn new() -> Self { + pub fn new(coordinates: (usize, FolderHash)) -> Self { PlainListing { cursor_pos: (0, 1, 0), - new_cursor_pos: (0, 0, 0), - folder_hash: 0, + new_cursor_pos: (coordinates.0, coordinates.1, 0), length: 0, sort: (Default::default(), Default::default()), subsort: (SortField::Date, SortOrder::Desc), @@ -637,7 +619,7 @@ impl PlainListing { } } fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings { - let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.folder_hash]; + let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1]; let mut tags = String::new(); let mut colors = SmallVec::new(); let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap(); @@ -1097,7 +1079,7 @@ impl Component for PlainListing { debug!("SubSort {:?} , {:?}", field, order); self.subsort = (*field, *order); //if !self.filtered_selection.is_empty() { - // let threads = &account.collection.threads[&self.folder_hash]; + // let threads = &account.collection.threads[&self.cursor_pos.1]; // threads.vec_inner_sort_by(&mut self.filtered_selection, self.sort, &account.collection); //} else { // self.refresh_mailbox(contex, falset); @@ -1148,19 +1130,19 @@ impl Component for PlainListing { } match *event { UIEvent::MailboxUpdate((ref idxa, ref idxf)) - if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) => + if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) => { self.refresh_mailbox(context, false); self.set_dirty(true); } - UIEvent::StartupCheck(ref f) if *f == self.folder_hash => { + UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => { self.refresh_mailbox(context, false); self.set_dirty(true); } UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => { let account = &context.accounts[self.cursor_pos.0]; if !account.collection.contains_key(new_hash) - || !account[self.folder_hash] + || !account[self.cursor_pos.1] .unwrap() .envelopes .contains(new_hash) diff --git a/src/components/mail/listing/thread.rs b/src/components/mail/listing/thread.rs index c58ded68..97fb18bc 100644 --- a/src/components/mail/listing/thread.rs +++ b/src/components/mail/listing/thread.rs @@ -29,9 +29,8 @@ const MAX_COLS: usize = 500; #[derive(Debug)] pub struct ThreadListing { /// (x, y, z): x is accounts, y is folders, z is index inside a folder. - cursor_pos: (usize, usize, usize), - new_cursor_pos: (usize, usize, usize), - folder_hash: FolderHash, + cursor_pos: (usize, FolderHash, usize), + new_cursor_pos: (usize, FolderHash, usize), length: usize, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), @@ -72,14 +71,6 @@ impl MailListingTrait for ThreadListing { } self.cursor_pos.1 = self.new_cursor_pos.1; self.cursor_pos.0 = self.new_cursor_pos.0; - self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0] - .folders_order - .get(self.cursor_pos.1) - { - *h - } else { - return; - }; self.color_cache = ColorCache { unseen: crate::conf::value(context, "mail.listing.plain.unseen"), @@ -100,7 +91,7 @@ impl MailListingTrait for ThreadListing { // Get mailbox as a reference. // - match context.accounts[self.cursor_pos.0].status(self.folder_hash) { + match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) { Ok(_) => {} Err(_) => { let default_cell = { @@ -234,10 +225,10 @@ impl MailListingTrait for ThreadListing { } impl ListingTrait for ThreadListing { - fn coordinates(&self) -> (usize, usize) { + fn coordinates(&self) -> (usize, FolderHash) { (self.new_cursor_pos.0, self.new_cursor_pos.1) } - fn set_coordinates(&mut self, coordinates: (usize, usize)) { + fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) { self.new_cursor_pos = (coordinates.0, coordinates.1, 0); self.unfocused = false; self.view = None; @@ -398,12 +389,6 @@ impl ListingTrait for ThreadListing { } } -impl Default for ThreadListing { - fn default() -> Self { - Self::new() - } -} - impl fmt::Display for ThreadListing { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "mail") @@ -411,12 +396,10 @@ impl fmt::Display for ThreadListing { } impl ThreadListing { - pub fn new() -> Self { - let content = CellBuffer::new(0, 0, Cell::with_char(' ')); + pub fn new(coordinates: (usize, FolderHash)) -> Self { ThreadListing { cursor_pos: (0, 1, 0), - new_cursor_pos: (0, 0, 0), - folder_hash: 0, + new_cursor_pos: (coordinates.0, coordinates.1, 0), length: 0, sort: (Default::default(), Default::default()), subsort: (Default::default(), Default::default()), @@ -662,12 +645,12 @@ impl Component for ThreadListing { return true; } UIEvent::MailboxUpdate((ref idxa, ref idxf)) - if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) => + if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) => { self.refresh_mailbox(context, false); self.set_dirty(true); } - UIEvent::StartupCheck(ref f) if *f == self.folder_hash => { + UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => { self.refresh_mailbox(context, false); self.set_dirty(true); } diff --git a/src/components/mail/status.rs b/src/components/mail/status.rs index de89c0b9..f9cc3227 100644 --- a/src/components/mail/status.rs +++ b/src/components/mail/status.rs @@ -484,7 +484,8 @@ impl Component for AccountStatus { None, ); line += 2; - for f in a.list_folders() { + for folder_node in a.list_folders() { + let f: &Folder = &a.ref_folders()[&folder_node.hash]; if f.is_subscribed() { write_string_to_grid( f.path(), diff --git a/src/components/mail/view.rs b/src/components/mail/view.rs index f302d1b4..82207c34 100644 --- a/src/components/mail/view.rs +++ b/src/components/mail/view.rs @@ -80,7 +80,7 @@ impl ViewMode { /// menus #[derive(Debug, Default)] pub struct MailView { - coordinates: (usize, usize, EnvelopeHash), + coordinates: (usize, FolderHash, EnvelopeHash), pager: Pager, subview: Option>, dirty: bool, @@ -117,7 +117,7 @@ impl fmt::Display for MailView { impl MailView { const DESCRIPTION: &'static str = "view mail"; pub fn new( - coordinates: (usize, usize, EnvelopeHash), + coordinates: (usize, FolderHash, EnvelopeHash), pager: Option, subview: Option>, context: &Context, @@ -331,7 +331,7 @@ impl MailView { } } - pub fn update(&mut self, new_coordinates: (usize, usize, EnvelopeHash)) { + pub fn update(&mut self, new_coordinates: (usize, FolderHash, EnvelopeHash)) { self.coordinates = new_coordinates; self.mode = ViewMode::Normal; self.set_dirty(true); @@ -791,10 +791,8 @@ impl Component for MailView { UIEvent::Input(ref key) if shortcut!(key == shortcuts[MailView::DESCRIPTION]["reply"]) => { - let account = &context.accounts[self.coordinates.0]; - let folder_hash = account[self.coordinates.1].unwrap().folder.hash(); context.replies.push_back(UIEvent::Action(Tab(Reply( - (self.coordinates.0, folder_hash), + (self.coordinates.0, self.coordinates.1), self.coordinates.2, )))); return true; diff --git a/src/components/mail/view/thread.rs b/src/components/mail/view/thread.rs index 1f73c2b0..b0bf8fb6 100644 --- a/src/components/mail/view/thread.rs +++ b/src/components/mail/view/thread.rs @@ -51,7 +51,7 @@ pub struct ThreadView { expanded_pos: usize, new_expanded_pos: usize, reversed: bool, - coordinates: (usize, usize, usize), + coordinates: (usize, FolderHash, usize), thread_group: ThreadHash, mailview: MailView, show_mailview: bool, @@ -75,7 +75,7 @@ impl ThreadView { * context: current context */ pub fn new( - coordinates: (usize, usize, usize), + coordinates: (usize, FolderHash, usize), thread_group: ThreadHash, expanded_hash: Option, context: &Context, diff --git a/src/conf/accounts.rs b/src/conf/accounts.rs index 8af057c7..f2298b63 100644 --- a/src/conf/accounts.rs +++ b/src/conf/accounts.rs @@ -62,6 +62,7 @@ pub enum MailboxEntry { Failed(MeliError), /// first argument is done work, and second is total work Parsing(Mailbox, usize, usize), + None, } impl Default for MailboxEntry { @@ -78,6 +79,7 @@ impl std::fmt::Display for MailboxEntry { match self { MailboxEntry::Available(ref m) => m.name().to_string(), MailboxEntry::Failed(ref e) => e.to_string(), + MailboxEntry::None => "Not subscribed, is this a bug?".to_string(), MailboxEntry::Parsing(_, done, total) => { format!("Parsing messages. [{}/{}]", done, total) } @@ -109,6 +111,7 @@ impl MailboxEntry { "Mailbox is not available: {}", e.to_string() ))), + MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")), } } @@ -120,6 +123,7 @@ impl MailboxEntry { "Mailbox is not available: {}", e.to_string() ))), + MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")), } } @@ -234,10 +238,11 @@ impl<'a> Iterator for MailboxIterator<'a> { } } -#[derive(Serialize, Debug, Default)] -struct FolderNode { - hash: FolderHash, - kids: Vec, +#[derive(Serialize, Debug, Clone, Default)] +pub struct FolderNode { + pub hash: FolderHash, + pub depth: usize, + pub children: Vec, } impl Account { @@ -393,7 +398,7 @@ impl Account { collection.threads.insert(*h, Threads::default()); } - build_folders_order(&folder_confs, &mut tree, &ref_folders, &mut folders_order); + build_folders_order(&mut tree, &ref_folders, &mut folders_order); self.folders = folders; self.ref_folders = ref_folders; self.folder_confs = folder_confs; @@ -717,50 +722,25 @@ impl Account { pub fn is_empty(&self) -> bool { self.folders.is_empty() } - pub fn list_folders(&self) -> Vec { - let mut folders = self.ref_folders.clone(); - let folder_confs = &self.folder_confs; - //debug!("folder renames: {:?}", folder_renames); - for f in folders.values_mut() { - if let Some(r) = folder_confs.get(&f.hash()) { - if let Some(rename) = r.folder_conf().alias() { - f.change_name(rename); - } + + pub fn ref_folders(&self) -> &FnvHashMap { + &self.ref_folders + } + + pub fn list_folders(&self) -> Vec { + let mut ret = Vec::with_capacity(self.folders.len()); + fn rec(node: &FolderNode, ret: &mut Vec) { + ret.push(node.clone()); + for c in node.children.iter() { + rec(c, ret); } } - /* - if let Some(pos) = folders - .iter() - .position(|f| f.name().eq_ignore_ascii_case("INBOX")) - { - folders.swap(pos, 0); + for node in &self.tree { + rec(node, &mut ret); } - */ - let order: FnvHashMap = self - .folders_order - .iter() - .enumerate() - .map(|(i, &fh)| (fh, i)) - .collect(); - let mut folders: Vec = folders - .drain() - .map(|(_, f)| f) - .filter(|f| { - self.folders.contains_key(&f.hash()) - || self - .settings - .account - .subscribed_folders - .iter() - .any(|m| f.path().matches_glob(m)) - }) - .collect(); - if order.is_empty() { - return Vec::new(); - } - folders.sort_unstable_by(|a, b| order[&a.hash()].partial_cmp(&order[&b.hash()]).unwrap()); - folders + ret } + pub fn folders_order(&self) -> &Vec { &self.folders_order } @@ -786,7 +766,7 @@ impl Account { .map(|e| (e.hash(), e)) .collect::>(); match self.folders.entry(folder_hash).or_default() { - MailboxEntry::Failed(_) => {} + MailboxEntry::Failed(_) | MailboxEntry::None => {} MailboxEntry::Parsing(ref mut m, _, _) | MailboxEntry::Available(ref mut m) => { m.merge(&envelopes); if let Some(updated_folders) = @@ -807,6 +787,9 @@ impl Account { } pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> { + if folder_hash == 0 { + return Err(0); + } loop { match self.workers.get_mut(&folder_hash).unwrap() { None => { @@ -1007,12 +990,7 @@ impl Account { .threads .insert(folder.hash(), Threads::default()); self.ref_folders = self.backend.read().unwrap().folders()?; - build_folders_order( - &self.folder_confs, - &mut self.tree, - &self.ref_folders, - &mut self.folders_order, - ); + build_folders_order(&mut self.tree, &self.ref_folders, &mut self.folders_order); Ok(format!("`{}` successfully created.", &path)) } FolderOperation::Delete(path) => { @@ -1190,7 +1168,6 @@ impl IndexMut for Account { } fn build_folders_order( - folder_confs: &FnvHashMap, tree: &mut Vec, ref_folders: &FnvHashMap, folders_order: &mut Vec, @@ -1199,23 +1176,24 @@ fn build_folders_order( tree.clear(); folders_order.clear(); for (h, f) in ref_folders.iter() { - if !folder_confs.contains_key(&h) { - continue; - } - if f.parent().is_none() { - fn rec(h: FolderHash, ref_folders: &FnvHashMap) -> FolderNode { + fn rec( + h: FolderHash, + ref_folders: &FnvHashMap, + depth: usize, + ) -> FolderNode { let mut node = FolderNode { hash: h, - kids: Vec::new(), + children: Vec::new(), + depth, }; for &c in ref_folders[&h].children() { - node.kids.push(rec(c, ref_folders)); + node.children.push(rec(c, ref_folders, depth + 1)); } node }; - tree.push(rec(*h, &ref_folders)); + tree.push(rec(*h, &ref_folders, 0)); for &c in f.children() { stack.push(c); } @@ -1239,10 +1217,10 @@ fn build_folders_order( } }); - let mut stack: SmallVec<[Option<&FolderNode>; 8]> = SmallVec::new(); + let mut stack: SmallVec<[Option<&FolderNode>; 16]> = SmallVec::new(); for n in tree.iter_mut() { folders_order.push(n.hash); - n.kids.sort_unstable_by(|a, b| { + n.children.sort_unstable_by(|a, b| { if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") { std::cmp::Ordering::Greater } else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") { @@ -1253,10 +1231,10 @@ fn build_folders_order( .cmp(&ref_folders[&b.hash].path()) } }); - stack.extend(n.kids.iter().rev().map(Some)); + stack.extend(n.children.iter().rev().map(Some)); while let Some(Some(next)) = stack.pop() { folders_order.push(next.hash); - stack.extend(next.kids.iter().rev().map(Some)); + stack.extend(next.children.iter().rev().map(Some)); } } } diff --git a/src/state.rs b/src/state.rs index 1d1c47c6..61779cc8 100644 --- a/src/state.rs +++ b/src/state.rs @@ -142,15 +142,20 @@ impl Context { let Context { ref mut accounts, ref mut mailbox_hashes, + ref mut replies, .. } = self; let was_online = accounts[account_pos].is_online; let ret = accounts[account_pos].is_online(); if ret.is_ok() { if !was_online { - for folder in accounts[account_pos].list_folders() { - debug!("hash & folder: {:?} {}", folder.hash(), folder.name()); - mailbox_hashes.insert(folder.hash(), account_pos); + for folder_node in accounts[account_pos].list_folders() { + debug!( + "hash & folder: {:?} {}", + folder_node.hash, + accounts[account_pos].ref_folders()[&folder_node.hash].name() + ); + mailbox_hashes.insert(folder_node.hash, account_pos); } /* Account::watch() needs * - work_controller to pass `work_context` to the watcher threads and then add them @@ -160,6 +165,8 @@ impl Context { * - replies to report any failures to the user */ accounts[account_pos].watch(); + + replies.push_back(UIEvent::AccountStatusChange(account_pos)); } } ret diff --git a/src/types.rs b/src/types.rs index bbae7d07..0d9bd372 100644 --- a/src/types.rs +++ b/src/types.rs @@ -113,6 +113,7 @@ pub enum UIEvent { MailboxUpdate((usize, FolderHash)), // (account_idx, mailbox_idx) MailboxDelete((usize, FolderHash)), MailboxCreate((usize, FolderHash)), + AccountStatusChange(usize), ComponentKill(Uuid), WorkerProgress(FolderHash), StartupCheck(FolderHash),