diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index 26a14753..4ee5536d 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -46,6 +46,77 @@ struct AccountMenuEntry { index: usize, } +pub trait MailListingTrait: ListingTrait { + fn perform_action( + &mut self, + context: &mut Context, + thread_hash: ThreadHash, + a: &ListingAction, + ) { + let account = &mut context.accounts[self.coordinates().0]; + let mut envs_to_set: StackVec = StackVec::new(); + let folder_hash = account[self.coordinates().1].unwrap().folder.hash(); + { + let mut stack = StackVec::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 env_hash in envs_to_set { + let op = account.operation(env_hash); + let mut envelope: EnvelopeRefMut = account.collection.get_env_mut(env_hash); + match a { + ListingAction::SetSeen => { + if let Err(e) = envelope.set_seen(op) { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(e.to_string()), + )); + } + self.row_updates().push(thread_hash); + } + ListingAction::SetUnseen => { + if let Err(e) = envelope.set_unseen(op) { + context.replies.push_back(UIEvent::StatusEvent( + StatusEvent::DisplayMessage(e.to_string()), + )); + } + } + ListingAction::Delete => { + /* do nothing */ + continue; + } + _ => unreachable!(), + } + self.row_updates().push(thread_hash); + drop(envelope); + } + } + + fn row_updates(&mut self) -> &mut StackVec; + + fn update_line(&mut self, context: &Context, thread_hash: ThreadHash); +} + pub trait ListingTrait: Component { fn coordinates(&self) -> (usize, usize); fn set_coordinates(&mut self, _: (usize, usize)); diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index 1bfa6855..4106c181 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -74,6 +74,76 @@ pub struct CompactListing { id: ComponentId, } +impl MailListingTrait for CompactListing { + fn row_updates(&mut self) -> &mut StackVec { + &mut self.row_updates + } + + fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) { + 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 !account.contains_key(env_hash) { + /* The envelope has been renamed or removed, so wait for the appropriate event to + * arrive */ + return; + } + let envelope: EnvelopeRef = account.collection.get_env(env_hash); + let has_attachments = envelope.has_attachments(); + drop(envelope); + let fg_color = if threads[&thread_hash].has_unseen() { + 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() { + Color::Byte(251) + } else if idx % 2 == 0 { + Color::Byte(252) + } else { + Color::Default + } + } else { + if threads[&thread_hash].has_unseen() { + Color::Byte(253) + } else if idx % 2 == 0 { + Color::Byte(236) + } else { + Color::Default + } + }; + for i in 0..self.data_columns.columns.len() { + let column_width = self.data_columns.columns[i].size().0; + if column_width == 0 { + continue; + } + change_colors( + &mut self.data_columns.columns[i], + ((0, idx), (column_width - 1, idx)), + fg_color, + bg_color, + ); + } + match (threads.is_snoozed(thread_hash), has_attachments) { + (true, true) => { + self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103)); + self.data_columns.columns[3][(2, idx)].set_fg(Color::Red); + } + (true, false) => { + self.data_columns.columns[3][(0, idx)].set_fg(Color::Red); + } + (false, true) => { + self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103)); + } + (false, false) => {} + } + } + } +} + impl ListingTrait for CompactListing { fn coordinates(&self) -> (usize, usize) { (self.new_cursor_pos.0, self.new_cursor_pos.1) @@ -888,136 +958,6 @@ impl CompactListing { self.filtered_selection[cursor] } } - - fn perform_action( - &mut self, - context: &mut Context, - thread_hash: ThreadHash, - a: &ListingAction, - ) { - let account = &mut context.accounts[self.cursor_pos.0]; - let mut envs_to_set: StackVec = StackVec::new(); - let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); - { - let mut stack = StackVec::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 env_hash in envs_to_set { - let op = account.operation(env_hash); - let mut envelope: EnvelopeRefMut = account.collection.get_env_mut(env_hash); - match a { - ListingAction::SetSeen => { - if let Err(e) = envelope.set_seen(op) { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(e.to_string()), - )); - } - self.row_updates.push(thread_hash); - } - ListingAction::SetUnseen => { - if let Err(e) = envelope.set_unseen(op) { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(e.to_string()), - )); - } - } - ListingAction::Delete => { - /* do nothing */ - continue; - } - _ => unreachable!(), - } - self.row_updates.push(thread_hash); - let has_attachments = envelope.has_attachments(); - drop(envelope); - let threads = &account.collection.threads[&folder_hash]; - let fg_color = if threads[&thread_hash].has_unseen() { - 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() { - Color::Byte(251) - } else if idx % 2 == 0 { - Color::Byte(252) - } else { - Color::Default - } - } else { - if threads[&thread_hash].has_unseen() { - Color::Byte(253) - } else if idx % 2 == 0 { - Color::Byte(236) - } else { - Color::Default - } - }; - let min_width = ( - self.data_columns.columns[0].size().0, - self.data_columns.columns[1].size().0, - self.data_columns.columns[2].size().0, - self.data_columns.columns[3].size().0, - self.data_columns.columns[4].size().0, - ); - for x in 0..min_width.0 { - self.data_columns.columns[0][(x, idx)].set_fg(fg_color); - self.data_columns.columns[0][(x, idx)].set_bg(bg_color); - } - for x in 0..min_width.1 { - self.data_columns.columns[1][(x, idx)].set_fg(fg_color); - self.data_columns.columns[1][(x, idx)].set_bg(bg_color); - } - for x in 0..min_width.2 { - self.data_columns.columns[2][(x, idx)].set_fg(fg_color); - self.data_columns.columns[2][(x, idx)].set_bg(bg_color); - } - for x in 0..min_width.3 { - self.data_columns.columns[3][(x, idx)].set_fg(fg_color); - self.data_columns.columns[3][(x, idx)].set_bg(bg_color); - } - for x in 0..min_width.4 { - self.data_columns.columns[4][(x, idx)].set_fg(fg_color); - self.data_columns.columns[4][(x, idx)].set_bg(bg_color); - } - match (threads.is_snoozed(thread_hash), has_attachments) { - (true, true) => { - self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103)); - self.data_columns.columns[3][(2, idx)].set_fg(Color::Red); - } - (true, false) => { - self.data_columns.columns[3][(0, idx)].set_fg(Color::Red); - } - (false, true) => { - self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103)); - } - (false, false) => {} - } - } - } } impl Component for CompactListing { @@ -1053,44 +993,8 @@ impl Component for CompactListing { if !self.row_updates.is_empty() { let (upper_left, bottom_right) = area; while let Some(row) = self.row_updates.pop() { + self.update_line(context, row); let row: usize = self.order[&row]; - let i = self.get_thread_under_cursor(row, 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() { - Color::Byte(0) - } else { - Color::Default - }; - let bg_color = if thread_node.has_unseen() { - Color::Byte(251) - } else if self.selection[&i] { - Color::Byte(210) - } else if row % 2 == 0 { - if context.settings.terminal.theme == "light" { - Color::Byte(252) - } else { - Color::Byte(236) - } - } else { - Color::Default - }; - for i in 0..self.data_columns.columns.len() { - let column_width = self.data_columns.columns[i].size().0; - if column_width == 0 { - continue; - } - change_colors( - &mut self.data_columns.columns[i], - ((0, row), (column_width - 1, row)), - fg_color, - bg_color, - ); - } let rows = get_y(bottom_right) - get_y(upper_left) + 1; let page_no = (self.new_cursor_pos.2).wrapping_div(rows); diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs index d2ab594a..7de6631e 100644 --- a/ui/src/components/mail/listing/conversations.rs +++ b/ui/src/components/mail/listing/conversations.rs @@ -106,6 +106,48 @@ pub struct ConversationsListing { id: ComponentId, } +impl MailListingTrait for ConversationsListing { + fn row_updates(&mut self) -> &mut StackVec { + &mut self.row_updates + } + + fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) { + 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[&thread_hash]; + let row: usize = self.order[&thread_hash]; + let width = self.content.size().0; + + let fg_color = if thread_node.has_unseen() { + Color::Byte(0) + } else { + Color::Default + }; + let bg_color = if thread_node.has_unseen() { + Color::Byte(251) + } else { + Color::Default + }; + change_colors( + &mut self.content, + ((0, 3 * row), (width - 1, 3 * row + 1)), + fg_color, + bg_color, + ); + let padding_fg = if context.settings.terminal.theme == "light" { + Color::Byte(254) + } else { + Color::Byte(235) + }; + change_colors( + &mut self.content, + ((0, 3 * row + 2), (width - 1, 3 * row + 2)), + padding_fg, + bg_color, + ); + } +} impl ListingTrait for ConversationsListing { fn coordinates(&self) -> (usize, usize) { (self.new_cursor_pos.0, self.new_cursor_pos.1) @@ -898,70 +940,6 @@ impl ConversationsListing { self.filtered_selection[cursor] } } - - fn perform_action( - &mut self, - context: &mut Context, - thread_hash: ThreadHash, - a: &ListingAction, - ) { - let account = &mut context.accounts[self.cursor_pos.0]; - let mut envs_to_set: StackVec = StackVec::new(); - { - let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash(); - let mut stack = StackVec::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 env_hash in envs_to_set { - let hash = account.collection.get_env(env_hash).hash(); - let op = account.operation(hash); - let mut envelope: EnvelopeRefMut = account.collection.get_env_mut(env_hash); - match a { - ListingAction::SetSeen => { - if let Err(e) = envelope.set_seen(op) { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(e.to_string()), - )); - } - } - ListingAction::SetUnseen => { - if let Err(e) = envelope.set_unseen(op) { - context.replies.push_back(UIEvent::StatusEvent( - StatusEvent::DisplayMessage(e.to_string()), - )); - } - } - ListingAction::Delete => { - /* do nothing */ - continue; - } - _ => unreachable!(), - } - self.row_updates.push(thread_hash); - } - } } impl Component for ConversationsListing { @@ -1016,43 +994,10 @@ impl Component for ConversationsListing { /* certain rows need to be updated (eg an unseen message was just set seen) * */ let (upper_left, bottom_right) = area; - let width = self.content.size().0; while let Some(row) = self.row_updates.pop() { + self.update_line(context, row); let row: usize = self.order[&row]; - let i = self.get_thread_under_cursor(row, 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() { - Color::Byte(0) - } else { - Color::Default - }; - let bg_color = if thread_node.has_unseen() { - Color::Byte(251) - } else { - Color::Default - }; - change_colors( - &mut self.content, - ((0, 3 * row), (width - 1, 3 * row + 1)), - fg_color, - bg_color, - ); - let padding_fg = if context.settings.terminal.theme == "light" { - Color::Byte(254) - } else { - Color::Byte(235) - }; - change_colors( - &mut self.content, - ((0, 3 * row + 2), (width - 1, 3 * row + 2)), - padding_fg, - bg_color, - ); let rows = (get_y(bottom_right) - get_y(upper_left) + 1) / 3; let page_no = (self.cursor_pos.2).wrapping_div(rows); diff --git a/ui/src/terminal/cells.rs b/ui/src/terminal/cells.rs index 042a2fcc..6995d0c2 100644 --- a/ui/src/terminal/cells.rs +++ b/ui/src/terminal/cells.rs @@ -1005,8 +1005,6 @@ pub fn clear_area(grid: &mut CellBuffer, area: Area) { if !is_valid_area!(area) { return; } - let upper_left = upper_left!(area); - let bottom_right = bottom_right!(area); for row in grid.bounds_iter(area) { for c in row { grid[c] = Cell::default();