diff --git a/CHANGELOG.md b/CHANGELOG.md index 32bb9929..3e49f8b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- Added listing configuration setting `thread_subject_pack` (see meli.conf.5) - Added shortcuts for focusing to sidebar menu and back to the e-mail view (`focus_left` and `focus_right`) - `f76f4ea3` A new manual page, `meli.7` which contains a general tutorial for using meli. - `cbe593cf` add configurable header preample suffix and prefix for editing diff --git a/docs/meli.conf.5 b/docs/meli.conf.5 index 163e9722..8c33508d 100644 --- a/docs/meli.conf.5 +++ b/docs/meli.conf.5 @@ -1059,6 +1059,11 @@ The URL will be given as the first argument of the command. .El .Sh LISTING .Bl -tag -width 36n +.It Ic show_menu_scrollbar Ar boolean +.Pq Em optional + Show auto-hiding scrollbar in accounts sidebar menu. +.\" default value +.Pq Em true .It Ic datetime_fmt Ar String .Pq Em optional Datetime formatting passed verbatim to strftime(3). @@ -1106,11 +1111,26 @@ Sets the character to print as the divider between the accounts list and the mes This is the width of the right container to the entire screen width. .\" default value .Pq Em 90 -.It Ic show_menu_scrollbar Ar boolean -.Pq Em optional - Show auto-hiding scrollbar in accounts sidebar menu. +.It Ic unseen_flag Ar Option +Flag to show if thread entry contains unseen mail. .\" default value -.Pq Em true +.Pq Em "●" +.It Ic thread_snoozed_flag Ar Option +Flag to show if thread has been snoozed. +.\" default value +.Pq Em "💤" +.It Ic selected_flag Ar Option +Flag to show if thread entry has been selected. +.\" default value +.Pq Em "☑️" +.It Ic attachment_flag Ar Option +Flag to show if thread entry contains attachments. +.\" default value +.Pq Em "📎" +.It Ic thread_subject_pack Ar bool +Should threads with differentiating Subjects show a list of those subjects on the entry title? +.\" default value +.Pq Em "true" .El .Ss Examples of sidebar mailbox tree customization The default values diff --git a/melib/src/thread.rs b/melib/src/thread.rs index dd9c3ed7..a7ae3e02 100644 --- a/melib/src/thread.rs +++ b/melib/src/thread.rs @@ -834,6 +834,7 @@ impl Threads { } } } + for i in 0..self.thread_nodes[&id].children.len() { let child_hash = self.thread_nodes[&id].children[i]; if let Some(child_env_hash) = self.thread_nodes[&child_hash].message() { diff --git a/src/components/mail/listing/conversations.rs b/src/components/mail/listing/conversations.rs index 0252f709..a81be52c 100644 --- a/src/components/mail/listing/conversations.rs +++ b/src/components/mail/listing/conversations.rs @@ -22,6 +22,7 @@ use super::*; use crate::components::PageMovement; use crate::jobs::JoinHandle; +use indexmap::IndexSet; use std::iter::FromIterator; macro_rules! row_attr { @@ -240,6 +241,7 @@ impl MailListingTrait for ConversationsListing { } let mut max_entry_columns = 0; + let mut other_subjects = IndexSet::new(); let mut from_address_list = Vec::new(); let mut from_address_set: std::collections::HashSet> = std::collections::HashSet::new(); @@ -273,17 +275,30 @@ impl MailListingTrait for ConversationsListing { panic!(); } + other_subjects.clear(); from_address_list.clear(); from_address_set.clear(); - for envelope in threads + for (envelope, show_subject) in threads .thread_group_iter(thread) - .filter_map(|(_, h)| threads.thread_nodes()[&h].message()) - .map(|env_hash| { - context.accounts[&self.cursor_pos.0] - .collection - .get_env(env_hash) + .filter_map(|(_, h)| { + Some(( + threads.thread_nodes()[&h].message()?, + threads.thread_nodes()[&h].show_subject(), + )) + }) + .map(|(env_hash, show_subject)| { + ( + context.accounts[&self.cursor_pos.0] + .collection + .get_env(env_hash), + show_subject, + ) }) { + if show_subject { + other_subjects.insert(envelope.subject().to_string()); + } + for addr in envelope.from().iter() { if from_address_set.contains(addr.address_spec_raw()) { continue; @@ -313,6 +328,7 @@ impl MailListingTrait for ConversationsListing { context, &from_address_list, &threads, + &other_subjects, thread, ); max_entry_columns = std::cmp::max( @@ -627,8 +643,9 @@ impl ConversationsListing { &self, e: &Envelope, context: &Context, - from: &Vec
, + from: &[Address], threads: &Threads, + other_subjects: &IndexSet, hash: ThreadHash, ) -> EntryStrings { let thread = threads.thread_ref(hash); @@ -669,8 +686,24 @@ impl ConversationsListing { tags.pop(); } } - let mut subject = e.subject().to_string(); - subject.truncate_at_boundary(150); + let mut subject = if *mailbox_settings!( + context[self.cursor_pos.0][&self.cursor_pos.1] + .listing + .thread_subject_pack + ) { + other_subjects + .into_iter() + .fold(String::new(), |mut acc, s| { + if !acc.is_empty() { + acc.push_str(", "); + } + acc.push_str(&s); + acc + }) + } else { + e.subject().to_string() + }; + subject.truncate_at_boundary(100); if thread.len() > 1 { EntryStrings { date: DateString(ConversationsListing::format_date(context, thread.date())), @@ -755,18 +788,29 @@ impl ConversationsListing { let idx: usize = self.order[&thread_hash]; let env_hash = threads.thread_nodes()[&thread_node_hash].message().unwrap(); + let mut other_subjects = IndexSet::new(); let mut from_address_list = Vec::new(); let mut from_address_set: std::collections::HashSet> = std::collections::HashSet::new(); - for envelope in threads + for (envelope, show_subject) in threads .thread_group_iter(thread_hash) - .filter_map(|(_, h)| threads.thread_nodes()[&h].message()) - .map(|env_hash| { - context.accounts[&self.cursor_pos.0] - .collection - .get_env(env_hash) + .filter_map(|(_, h)| { + threads.thread_nodes()[&h] + .message() + .map(|env_hash| (env_hash, threads.thread_nodes()[&h].show_subject())) + }) + .map(|(env_hash, show_subject)| { + ( + context.accounts[&self.cursor_pos.0] + .collection + .get_env(env_hash), + show_subject, + ) }) { + if show_subject { + other_subjects.insert(envelope.subject().to_string()); + } for addr in envelope.from().iter() { if from_address_set.contains(addr.address_spec_raw()) { continue; @@ -781,6 +825,7 @@ impl ConversationsListing { context, &from_address_list, &threads, + &other_subjects, thread_hash, ); drop(envelope); diff --git a/src/conf/listing.rs b/src/conf/listing.rs index 1160646d..39951b20 100644 --- a/src/conf/listing.rs +++ b/src/conf/listing.rs @@ -129,6 +129,12 @@ pub struct ListingSettings { /// Default: "📎" #[serde(default)] pub attachment_flag: Option, + + /// Should threads with differentiating Subjects show a list of those subjects on the entry + /// title? + /// Default: "true" + #[serde(default = "true_val")] + pub thread_subject_pack: bool, } const fn default_divider() -> char { @@ -158,6 +164,7 @@ impl Default for ListingSettings { thread_snoozed_flag: None, selected_flag: None, attachment_flag: None, + thread_subject_pack: true, } } } @@ -192,6 +199,7 @@ impl DotAddressable for ListingSettings { "thread_snoozed_flag" => self.thread_snoozed_flag.lookup(field, tail), "selected_flag" => self.selected_flag.lookup(field, tail), "attachment_flag" => self.attachment_flag.lookup(field, tail), + "thread_subject_pack" => self.thread_subject_pack.lookup(field, tail), other => Err(MeliError::new(format!( "{} has no field named {}", parent_field, other diff --git a/src/conf/overrides.rs b/src/conf/overrides.rs index abc375b6..73e97d40 100644 --- a/src/conf/overrides.rs +++ b/src/conf/overrides.rs @@ -171,6 +171,11 @@ pub struct ListingSettingsOverride { #[doc = " Default: \"📎\""] #[serde(default)] pub attachment_flag: Option>, + #[doc = " Should threads with differentiating Subjects show a list of those subjects on the entry"] + #[doc = " title?"] + #[doc = " Default: \"true\""] + #[serde(default)] + pub thread_subject_pack: Option, } impl Default for ListingSettingsOverride { fn default() -> Self { @@ -191,6 +196,7 @@ impl Default for ListingSettingsOverride { thread_snoozed_flag: None, selected_flag: None, attachment_flag: None, + thread_subject_pack: None, } } }