diff options
author | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-01-18 01:48:29 +0200 |
---|---|---|
committer | Manos Pitsidianakis <el13635@mail.ntua.gr> | 2020-01-20 16:03:06 +0200 |
commit | 47a69f8eb9c62cfc6b3e58002a2a978a6a16acdc (patch) | |
tree | 2929282b09cf69e423623d7b473d810d3294f930 /ui/src/components | |
parent | 20f86f2741cb1d78c983b723cfc5f927846e7354 (diff) |
melib: add ThreadGroup
Instead of using Union/Find to gather mail that belongs in the same
e-mail thread together, add a new entity ThreadGroup that ThreadNodes
point to. ThreadGroup represents an actual Thread: A thread root
ThreadGroup::Group or a reply ThreadGroup::Node.
To make semantics more accurate:
- ThreadNode hash should be renamed to ThreadNodeHash
- ThreadGroupHash should be renamed to ThreadHash
- ThreadGroup::Group should be a struct named Thread instead
- move ThreadGroup::Node logic to ThreadNode akin to Union/Find
- rename ThreaddGroup::Group to Thread
Diffstat (limited to 'ui/src/components')
-rw-r--r-- | ui/src/components/mail/compose.rs | 51 | ||||
-rw-r--r-- | ui/src/components/mail/listing.rs | 39 | ||||
-rw-r--r-- | ui/src/components/mail/listing/compact.rs | 249 | ||||
-rw-r--r-- | ui/src/components/mail/listing/conversations.rs | 291 | ||||
-rw-r--r-- | ui/src/components/mail/listing/plain.rs | 9 | ||||
-rw-r--r-- | ui/src/components/mail/listing/thread.rs | 6 | ||||
-rw-r--r-- | ui/src/components/mail/view.rs | 12 | ||||
-rw-r--r-- | ui/src/components/mail/view/thread.rs | 19 |
8 files changed, 253 insertions, 423 deletions
diff --git a/ui/src/components/mail/compose.rs b/ui/src/components/mail/compose.rs index 0c3a42ad..19790f4c 100644 --- a/ui/src/components/mail/compose.rs +++ b/ui/src/components/mail/compose.rs @@ -63,7 +63,7 @@ impl std::ops::DerefMut for EmbedStatus { #[derive(Debug)] pub struct Composer { - reply_context: Option<(usize, usize)>, // (folder_index, thread_node_index) + reply_context: Option<(FolderHash, EnvelopeHash)>, account_cursor: usize, cursor: Cursor, @@ -148,11 +148,7 @@ impl Composer { ..Default::default() } } - /* - * coordinates: (account index, mailbox index, root set thread_node index) - * msg: index of message we reply to in thread_nodes - * context: current context - */ + pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Result<Self> { let mut ret = Composer::default(); let op = context.accounts[account_pos].operation(h); @@ -163,18 +159,15 @@ impl Composer { ret.account_cursor = account_pos; Ok(ret) } + pub fn with_context( - coordinates: (usize, usize, usize), - msg: ThreadHash, + coordinates: (usize, FolderHash), + msg: EnvelopeHash, context: &Context, ) -> Self { let account = &context.accounts[coordinates.0]; - let mailbox = &account[coordinates.1].unwrap(); - let threads = &account.collection.threads[&mailbox.folder.hash()]; - let thread_nodes = &threads.thread_nodes(); let mut ret = Composer::default(); - let p = &thread_nodes[&msg]; - let parent_message = account.collection.get_env(p.message().unwrap()); + let parent_message = account.collection.get_env(msg); /* If message is from a mailing list and we detect a List-Post header, ask user if they * want to reply to the mailing list or the submitter of the message */ if let Some(actions) = list_management::ListActions::detect(&parent_message) { @@ -202,32 +195,22 @@ impl Composer { } } - let mut op = account.operation(parent_message.hash()); + let mut op = account.operation(msg); let parent_bytes = op.as_bytes(); ret.draft = Draft::new_reply(&parent_message, parent_bytes.unwrap()); + let subject = parent_message.subject(); ret.draft.headers_mut().insert( "Subject".into(), - if p.show_subject() { - format!( - "Re: {}", - account - .collection - .get_env(p.message().unwrap()) - .subject() - .clone() - ) + if !subject.starts_with("Re: ") { + format!("Re: {}", subject) } else { - account - .collection - .get_env(p.message().unwrap()) - .subject() - .into() + subject.into() }, ); ret.account_cursor = coordinates.0; - ret.reply_context = Some((coordinates.1, coordinates.2)); + ret.reply_context = Some((coordinates.1, msg)); ret } @@ -553,13 +536,13 @@ impl Component for Composer { fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool { let shortcuts = self.get_shortcuts(context); - match (&mut self.mode, &mut self.reply_context, &event) { - (ViewMode::Edit, _, _) => { + match (&mut self.mode, &event) { + (ViewMode::Edit, _) => { if self.pager.process_event(event, context) { return true; } } - (ViewMode::Send(ref mut selector), _, _) => { + (ViewMode::Send(ref mut selector), _) => { if selector.process_event(event, context) { if selector.is_done() { let s = match std::mem::replace(&mut self.mode, ViewMode::Edit) { @@ -594,7 +577,7 @@ impl Component for Composer { return true; } } - (ViewMode::SelectRecipients(ref mut selector), _, _) => { + (ViewMode::SelectRecipients(ref mut selector), _) => { if selector.process_event(event, context) { if selector.is_done() { let s = match std::mem::replace(&mut self.mode, ViewMode::Edit) { @@ -615,7 +598,7 @@ impl Component for Composer { return true; } } - (ViewMode::Discard(_, ref mut selector), _, _) => { + (ViewMode::Discard(_, ref mut selector), _) => { if selector.process_event(event, context) { if selector.is_done() { let (u, s) = match std::mem::replace(&mut self.mode, ViewMode::Edit) { diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 67c38233..e8663621 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -53,37 +53,18 @@ pub trait MailListingTrait: ListingTrait { fn perform_action( &mut self, context: &mut Context, - thread_hash: ThreadHash, + thread_hash: ThreadGroupHash, a: &ListingAction, ) { let account = &mut context.accounts[self.coordinates().0]; let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new(); let folder_hash = account[self.coordinates().1].unwrap().folder.hash(); - { - let mut stack: SmallVec<[ThreadHash; 8]> = SmallVec::new(); - stack.push(thread_hash); - while let Some(thread_iter) = stack.pop() { - { - let threads = account.collection.threads.get_mut(&folder_hash).unwrap(); - threads - .thread_nodes - .entry(thread_iter) - .and_modify(|t| t.set_has_unseen(false)); - } - let threads = &account.collection.threads[&folder_hash]; - if let Some(env_hash) = threads[&thread_iter].message() { - if !account.contains_key(env_hash) { - /* The envelope has been renamed or removed, so wait for the appropriate event to - * arrive */ - continue; - } - envs_to_set.push(env_hash); - } - for c in 0..threads[&thread_iter].children().len() { - let c = threads[&thread_iter].children()[c]; - stack.push(c); - } - } + for (_, h) in account.collection.threads[&folder_hash].thread_group_iter(thread_hash) { + envs_to_set.push( + account.collection.threads[&folder_hash].thread_nodes()[&h] + .message() + .unwrap(), + ); } for env_hash in envs_to_set { let op = account.operation(env_hash); @@ -139,9 +120,9 @@ pub trait MailListingTrait: ListingTrait { } } - fn row_updates(&mut self) -> &mut SmallVec<[ThreadHash; 8]>; - fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]>; - fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) { + fn row_updates(&mut self) -> &mut SmallVec<[ThreadGroupHash; 8]>; + fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadGroupHash; 8]>; + fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadGroupHash>>) { unimplemented!() } } diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 72d7713b..90a607e8 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -54,33 +54,33 @@ pub struct CompactListing { length: usize, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), - all_threads: fnv::FnvHashSet<ThreadHash>, - order: FnvHashMap<ThreadHash, usize>, + all_threads: fnv::FnvHashSet<ThreadGroupHash>, + order: FnvHashMap<ThreadGroupHash, usize>, /// Cache current view. data_columns: DataColumns, filter_term: String, - filtered_selection: Vec<ThreadHash>, - filtered_order: FnvHashMap<ThreadHash, usize>, - selection: FnvHashMap<ThreadHash, bool>, + filtered_selection: Vec<ThreadGroupHash>, + filtered_order: FnvHashMap<ThreadGroupHash, usize>, + selection: FnvHashMap<ThreadGroupHash, bool>, /// If we must redraw on next redraw event dirty: bool, force_draw: bool, /// If `self.view` exists or not. unfocused: bool, view: ThreadView, - row_updates: SmallVec<[ThreadHash; 8]>, + row_updates: SmallVec<[ThreadGroupHash; 8]>, movement: Option<PageMovement>, id: ComponentId, } impl MailListingTrait for CompactListing { - fn row_updates(&mut self) -> &mut SmallVec<[ThreadHash; 8]> { + fn row_updates(&mut self) -> &mut SmallVec<[ThreadGroupHash; 8]> { &mut self.row_updates } - fn get_focused_items(&self, context: &Context) -> SmallVec<[ThreadHash; 8]> { + fn get_focused_items(&self, context: &Context) -> SmallVec<[ThreadGroupHash; 8]> { let is_selection_empty = self.selection.values().cloned().any(std::convert::identity); let i = [self.get_thread_under_cursor(self.cursor_pos.2, context)]; let cursor_iter; @@ -118,14 +118,13 @@ impl ListingTrait for CompactListing { if self.length == 0 { return; } - let i = self.get_thread_under_cursor(idx, context); + let thread = self.get_thread_under_cursor(idx, context); let account = &context.accounts[self.cursor_pos.0]; let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); let threads = &account.collection.threads[&folder_hash]; - let thread_node = &threads.thread_nodes[&i]; - let fg_color = if thread_node.has_unseen() { + let fg_color = if threads.groups[&thread].unseen() > 0 { Color::Byte(0) } else { Color::Default @@ -133,9 +132,9 @@ impl ListingTrait for CompactListing { let bg_color = if context.settings.terminal.theme == "light" { if self.cursor_pos.2 == idx { Color::Byte(244) - } else if self.selection[&i] { + } else if self.selection[&thread] { Color::Byte(210) - } else if thread_node.has_unseen() { + } else if threads.groups[&thread].unseen() > 0 { Color::Byte(251) } else if idx % 2 == 0 { Color::Byte(252) @@ -145,9 +144,9 @@ impl ListingTrait for CompactListing { } else { if self.cursor_pos.2 == idx { Color::Byte(246) - } else if self.selection[&i] { + } else if self.selection[&thread] { Color::Byte(210) - } else if thread_node.has_unseen() { + } else if threads.groups[&thread].unseen() > 0 { Color::Byte(251) } else if idx % 2 == 0 { Color::Byte(236) @@ -444,21 +443,19 @@ impl ListingTrait for CompactListing { if !threads.thread_nodes.contains_key(&env_hash_thread_hash) { continue; } - let thread_group = melib::find_root_hash( - &threads.thread_nodes, - threads.thread_nodes[&env_hash_thread_hash].thread_group(), - ); - if self.filtered_order.contains_key(&thread_group) { + let thread = + threads.find_group(threads.thread_nodes[&env_hash_thread_hash].group); + if self.filtered_order.contains_key(&thread) { continue; } - if self.all_threads.contains(&thread_group) { - self.filtered_selection.push(thread_group); + if self.all_threads.contains(&thread) { + self.filtered_selection.push(thread); self.filtered_order - .insert(thread_group, self.filtered_selection.len() - 1); + .insert(thread, self.filtered_selection.len() - 1); } } if !self.filtered_selection.is_empty() { - threads.vec_inner_sort_by( + threads.group_inner_sort_by( &mut self.filtered_selection, self.sort, &context.accounts[self.cursor_pos.0].collection.envelopes, @@ -472,7 +469,7 @@ impl ListingTrait for CompactListing { self.redraw_list( context, Box::new(self.filtered_selection.clone().into_iter()) - as Box<dyn Iterator<Item = ThreadHash>>, + as Box<dyn Iterator<Item = ThreadGroupHash>>, ); } Err(e) => { @@ -550,12 +547,9 @@ impl CompactListing { e: &Envelope, context: &Context, threads: &Threads, - hash: ThreadHash, + hash: ThreadGroupHash, ) -> EntryStrings { - let is_snoozed: bool = threads.is_snoozed(hash); - let date = - threads.thread_dates[&melib::thread::find_thread_group(threads.thread_nodes(), hash)]; - let thread_node = &threads[&hash]; + let thread = &threads.groups[&hash]; let folder_hash = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] .unwrap() .folder @@ -597,26 +591,26 @@ impl CompactListing { } let mut subject = e.subject().to_string(); subject.truncate_at_boundary(150); - if thread_node.len() > 0 { + if thread.len() > 1 { EntryStrings { - date: DateString(ConversationsListing::format_date(context, date)), - subject: SubjectString(format!("{} ({})", subject, thread_node.len(),)), + date: DateString(ConversationsListing::format_date(context, thread.date())), + subject: SubjectString(format!("{} ({})", subject, thread.len(),)), flag: FlagString(format!( "{}{}", if e.has_attachments() { "📎" } else { "" }, - if is_snoozed { "💤" } else { "" } + if thread.snoozed() { "💤" } else { "" } )), from: FromString(address_list!((e.from()) as comma_sep_list)), tags: TagString(tags, colors), } } else { EntryStrings { - date: DateString(ConversationsListing::format_date(context, date)), + date: DateString(ConversationsListing::format_date(context, thread.date())), subject: SubjectString(subject), flag: FlagString(format!( "{}{}", if e.has_attachments() { "📎" } else { "" }, - if is_snoozed { "💤" } else { "" } + if thread.snoozed() { "💤" } else { "" } )), from: FromString(address_list!((e.from()) as comma_sep_list)), tags: TagString(tags, colors), @@ -669,28 +663,31 @@ impl CompactListing { return; } } - if old_cursor_pos == self.new_cursor_pos { - self.view.update(context); - } else if self.unfocused { - self.view = ThreadView::new(self.new_cursor_pos, None, context); - } let threads = &context.accounts[self.cursor_pos.0].collection.threads[&folder_hash]; - threads.sort_by( + self.all_threads.clear(); + let mut roots = threads.roots(); + threads.group_inner_sort_by( + &mut roots, self.sort, - self.subsort, &context.accounts[self.cursor_pos.0].collection.envelopes, ); - self.all_threads.clear(); self.redraw_list( context, - Box::new(threads.root_iter().collect::<Vec<ThreadHash>>().into_iter()) - as Box<dyn Iterator<Item = ThreadHash>>, + Box::new(roots.into_iter()) as Box<dyn Iterator<Item = ThreadGroupHash>>, ); + + if old_cursor_pos == self.new_cursor_pos { + self.view.update(context); + } else if self.unfocused { + let thread = self.get_thread_under_cursor(self.cursor_pos.2, context); + + self.view = ThreadView::new(self.new_cursor_pos, thread, None, context); + } } - fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) { + fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadGroupHash>>) { let account = &context.accounts[self.cursor_pos.0]; let mailbox = &account[self.cursor_pos.1].unwrap(); @@ -714,18 +711,19 @@ impl CompactListing { SmallVec::new(), ); - for (idx, root_idx) in items.enumerate() { + for (idx, thread) in items.enumerate() { + debug!(thread); self.length += 1; - let thread_node = &threads.thread_nodes()[&root_idx]; - let i = thread_node.message().unwrap_or_else(|| { + let thread_node = &threads.thread_nodes()[&threads.groups[&thread].root().unwrap()]; + let root_env_hash = thread_node.message().unwrap_or_else(|| { let mut iter_ptr = thread_node.children()[0]; while threads.thread_nodes()[&iter_ptr].message().is_none() { iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0]; } threads.thread_nodes()[&iter_ptr].message().unwrap() }); - if !context.accounts[self.cursor_pos.0].contains_key(i) { - debug!("key = {}", i); + if !context.accounts[self.cursor_pos.0].contains_key(root_env_hash) { + debug!("key = {}", root_env_hash); debug!( "name = {} {}", mailbox.name(), @@ -735,10 +733,11 @@ impl CompactListing { panic!(); } - let root_envelope: EnvelopeRef = - context.accounts[self.cursor_pos.0].collection.get_env(i); + let root_envelope: EnvelopeRef = context.accounts[self.cursor_pos.0] + .collection + .get_env(root_env_hash); - let entry_strings = self.make_entry_string(&root_envelope, context, threads, root_idx); + let entry_strings = self.make_entry_string(&root_envelope, context, threads, thread); row_widths.1.push( entry_strings .date @@ -772,11 +771,11 @@ impl CompactListing { min_width.4, entry_strings.subject.grapheme_width() + 1 + entry_strings.tags.grapheme_width(), ); /* subject */ - rows.push(((idx, root_idx), entry_strings)); - self.all_threads.insert(root_idx); + rows.push(((idx, (thread, root_env_hash)), entry_strings)); + self.all_threads.insert(thread); - self.order.insert(root_idx, idx); - self.selection.insert(root_idx, false); + self.order.insert(thread, idx); + self.selection.insert(thread, false); } min_width.0 = self.length.saturating_sub(1).to_string().len(); @@ -800,17 +799,9 @@ impl CompactListing { CellBuffer::new_with_context(min_width.4, rows.len(), Cell::with_char(' '), context); self.data_columns.segment_tree[4] = row_widths.4.into(); - for ((idx, root_idx), strings) in rows { - let thread_node = &threads.thread_nodes()[&root_idx]; - let i = thread_node.message().unwrap_or_else(|| { - let mut iter_ptr = thread_node.children()[0]; - while threads.thread_nodes()[&iter_ptr].message().is_none() { - iter_ptr = threads.thread_nodes()[&iter_ptr].children()[0]; - } - threads.thread_nodes()[&iter_ptr].message().unwrap() - }); - if !context.accounts[self.cursor_pos.0].contains_key(i) { - //debug!("key = {}", i); + for ((idx, (thread, root_env_hash)), strings) in rows { + if !context.accounts[self.cursor_pos.0].contains_key(root_env_hash) { + //debug!("key = {}", root_env_hash); //debug!( // "name = {} {}", // mailbox.name(), @@ -820,13 +811,13 @@ impl CompactListing { panic!(); } - let fg_color = if thread_node.has_unseen() { + let fg_color = if threads.groups[&thread].unseen() > 0 { Color::Byte(0) } else { Color::Default }; let bg_color = if context.settings.terminal.theme == "light" { - if thread_node.has_unseen() { + if threads.groups[&thread].unseen() > 0 { Color::Byte(251) } else if idx % 2 == 0 { Color::Byte(252) @@ -834,7 +825,7 @@ impl CompactListing { Color::Default } } else { - if thread_node.has_unseen() { + if threads.groups[&thread].unseen() > 0 { Color::Byte(251) } else if idx % 2 == 0 { Color::Byte(236) @@ -930,10 +921,10 @@ impl CompactListing { self.data_columns.columns[4][(x, idx)].set_bg(bg_color); } match ( - threads.is_snoozed(root_idx), + threads.groups[&thread].snoozed(), context.accounts[self.cursor_pos.0] .collection - .get_env(i) + .get_env(root_env_hash) .has_attachments(), ) { (true, true) => { @@ -970,7 +961,7 @@ impl CompactListing { } } - fn get_thread_under_cursor(&self, cursor: usize, context: &Context) -> ThreadHash { + fn get_thread_under_cursor(&self, cursor: usize, context: &Context) -> ThreadGroupHash { //let account = &context.accounts[self.cursor_pos.0]; //let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); if self.filter_term.is_empty() { @@ -983,17 +974,18 @@ impl CompactListing { panic!(); }) .0 - //threads.root_set(cursor) } else { self.filtered_selection[cursor] } } - fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) { + fn update_line(&mut self, context: &Context, thread_hash: ThreadGroupHash) { let account = &context.accounts[self.cursor_pos.0]; let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); let threads = &account.collection.threads[&folder_hash]; - if let Some(env_hash) = threads[&thread_hash].message() { + if let Some(env_hash) = + threads.thread_nodes()[&threads.groups[&thread_hash].root().unwrap()].message() + { if !account.contains_key(env_hash) { /* The envelope has been renamed or removed, so wait for the appropriate event to * arrive */ @@ -1001,14 +993,14 @@ impl CompactListing { } let envelope: EnvelopeRef = account.collection.get_env(env_hash); let has_attachments = envelope.has_attachments(); - let fg_color = if threads[&thread_hash].has_unseen() { + let fg_color = if threads.groups[&thread_hash].unseen() > 0 { Color::Byte(0) } else { Color::Default }; let idx = self.order[&thread_hash]; let bg_color = if context.settings.terminal.theme == "light" { - if threads[&thread_hash].has_unseen() { + if threads.groups[&thread_hash].unseen() > 0 { Color::Byte(251) } else if idx % 2 == 0 { Color::Byte(252) @@ -1016,7 +1008,7 @@ impl CompactListing { Color::Default } } else { - if threads[&thread_hash].has_unseen() { + if threads.groups[&thread_hash].unseen() > 0 { Color::Byte(253) } else if idx % 2 == 0 { Color::Byte(236) @@ -1125,7 +1117,7 @@ impl CompactListing { columns[4][c].set_ch(' '); columns[4][c].set_bg(bg_color); } - match (threads.is_snoozed(thread_hash), has_attachments) { + match (threads.groups[&thread_hash].snoozed(), has_attachments) { (true, true) => { columns[3][(0, idx)].set_fg(Color::Byte(103)); columns[3][(2, idx)].set_fg(Color::Red); @@ -1225,22 +1217,8 @@ impl Component for CompactListing { k == shortcuts[CompactListing::DESCRIPTION]["open_thread"] ) => { - if self.filtered_selection.is_empty() { - self.view = ThreadView::new(self.cursor_pos, None, context); - } else { - let mut temp = self.cursor_pos; - let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2, context); - let account = &context.accounts[self.cursor_pos.0]; - let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); - let threads = &account.collection.threads[&folder_hash]; - let root_thread_index = threads.root_iter().position(|t| t == thread_hash); - if let Some(pos) = root_thread_index { - temp.2 = pos; - self.view = ThreadView::new(temp, Some(thread_hash), context); - } else { - return true; - } - } + let thread = self.get_thread_under_cursor(self.cursor_pos.2, context); + self.view = ThreadView::new(self.cursor_pos, thread, None, context); self.unfocused = true; self.dirty = true; return true; @@ -1269,48 +1247,34 @@ impl Component for CompactListing { self.selection.entry(thread_hash).and_modify(|e| *e = !*e); } UIEvent::Action(ref action) => match action { - Action::SubSort(field, order) if !self.unfocused => { - debug!("SubSort {:?} , {:?}", field, order); - self.subsort = (*field, *order); - //if !self.filtered_selection.is_empty() { - // let threads = &account.collection.threads[&folder_hash]; - // threads.vec_inner_sort_by(&mut self.filtered_selection, self.sort, &account.collection); - //} else { - // self.refresh_mailbox(context); - //} - return true; - } Action::Sort(field, order) if !self.unfocused => { debug!("Sort {:?} , {:?}", field, order); self.sort = (*field, *order); if !self.filtered_selection.is_empty() { - let folder_hash = context.accounts[self.cursor_pos.0] - [self.cursor_pos.1] - .unwrap() - .folder - .hash(); - let threads = &context.accounts[self.cursor_pos.0].collection.threads - [&folder_hash]; - threads.vec_inner_sort_by( - &mut self.filtered_selection, - self.sort, - &context.accounts[self.cursor_pos.0].collection.envelopes, - ); + // FIXME: perform sort self.dirty = true; } else { self.refresh_mailbox(context); } return true; } + Action::SubSort(field, order) if !self.unfocused => { + debug!("SubSort {:?} , {:?}", field, order); + self.subsort = (*field, *order); + // FIXME: perform subsort. + return true; + } Action::ToggleThreadSnooze if !self.unfocused => { - let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2, context); + let thread = self.get_thread_under_cursor(self.cursor_pos.2, context); let account = &mut context.accounts[self.cursor_pos.0]; let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); let threads = account.collection.threads.entry(folder_hash).or_default(); - let root_node = threads.thread_nodes.entry(thread_hash).or_default(); - let is_snoozed = root_node.snoozed(); - root_node.set_snoozed(!is_snoozed); - self.row_updates.push(thread_hash); + let is_snoozed = threads.groups[&thread].snoozed(); + threads + .groups + .entry(thread) + .and_modify(|entry| entry.set_snoozed(!is_snoozed)); + self.row_updates.push(thread); self.refresh_mailbox(context); return true; } @@ -1352,33 +1316,10 @@ impl Component for CompactListing { if !threads.thread_nodes.contains_key(&new_env_thread_hash) { return false; } - let thread_group = melib::find_root_hash( - &threads.thread_nodes, - threads.thread_nodes[&new_env_thread_hash].thread_group(), - ); - let (&thread_hash, &row): (&ThreadHash, &usize) = self - .order - .iter() - .find(|(n, _)| { - melib::find_root_hash( - &threads.thread_nodes, - threads.thread_nodes[&n].thread_group(), - ) == thread_group - }) - .unwrap(); - - let new_thread_hash = threads.root_set(row); - self.row_updates.push(new_thread_hash); - if let Some(row) = self.order.remove(&thread_hash) { - self.order.insert(new_thread_hash, row); - let selection_status = self.selection.remove(&thread_hash).unwrap(); - self.selection.insert(new_thread_hash, selection_status); - for h in self.filtered_selection.iter_mut() { - if *h == thread_hash { - *h = new_thread_hash; - break; - } - } + let thread: ThreadGroupHash = + threads.find_group(threads.thread_nodes()[&new_env_thread_hash].group); + if self.order.contains_key(&thread) { + self.row_updates.push(thread); } self.dirty = true; diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs index 23e590a6..b9096762 100644 --- a/ui/src/components/mail/listing/conversations.rs +++ b/ui/src/components/mail/listing/conversations.rs @@ -85,33 +85,33 @@ pub struct ConversationsListing { length: usize, sort: (SortField, SortOrder), subsort: (SortField, SortOrder), - all_threads: fnv::FnvHashSet<ThreadHash>, - order: FnvHashMap<ThreadHash, usize>, + all_threads: fnv::FnvHashSet<ThreadGroupHash>, + order: FnvHashMap<ThreadGroupHash, usize>, /// Cache current view. content: CellBuffer, filter_term: String, - filtered_selection: Vec<ThreadHash>, - filtered_order: FnvHashMap<ThreadHash, usize>, - selection: FnvHashMap<ThreadHash, bool>, + filtered_selection: Vec<ThreadGroupHash>, + filtered_order: FnvHashMap<ThreadGroupHash, usize>, + selection: FnvHashMap<ThreadGroupHash, bool>, /// If we must redraw on next redraw event dirty: bool, force_draw: bool, /// If `self.view` exists or not. unfocused: bool, view: ThreadView, - row_updates: SmallVec<[ThreadHash; 8]>, + row_updates: SmallVec<[ThreadGroupHash; 8]>, movement: Option<PageMovement>, id: ComponentId, } impl MailListingTrait for ConversationsListing { - fn row_updates(&mut self) -> &mut SmallVec<[ThreadHash; 8]> { + fn row_updates(&mut self) -> &mut SmallVec<[ThreadGroupHash; 8]> { &mut self.row_updates } - fn get_focused_items(&self, context: &Context) -> SmallVec<[ThreadHash; 8]> { + fn get_focused_items(&self, context: &Context) -> SmallVec<[ThreadGroupHash; 8]> { let is_selection_empty = self.selection.values().cloned().any(std::convert::identity); let i = [self.get_thread_under_cursor(self.cursor_pos.2, context)]; let cursor_iter; @@ -149,23 +149,22 @@ impl ListingTrait for ConversationsListing { if self.length == 0 { return; } - let i = self.get_thread_under_cursor(idx, context); + let thread = self.get_thread_under_cursor(idx, context); let account = &context.accounts[self.cursor_pos.0]; let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); let threads = &account.collection.threads[&folder_hash]; - let thread_node = &threads.thread_nodes[&i]; - let fg_color = if thread_node.has_unseen() { + let fg_color = if threads.groups[&thread].unseen() > 0 { Color::Byte(0) } else { Color::Default }; let bg_color = if self.cursor_pos.2 == idx { Color::Byte(246) - } else if self.selection[&i] { + } else if self.selection[&thread] { Color::Byte(210) - } else if thread_node.has_unseen() { + } else if threads.groups[&thread].unseen() > 0 { Color::Byte(251) } else { Color::Default @@ -187,7 +186,7 @@ impl ListingTrait for ConversationsListing { let (upper_left, bottom_right) = area; let width = self.content.size().0; let (x, y) = upper_left; - if self.cursor_pos.2 == idx || self.selection[&i] { + if self.cursor_pos.2 == idx || self.selection[&thread] { |