listing/conversations.rs: add `thread_subject_pack` command to pack different inner thread subjects in entry title

pull/150/head
Manos Pitsidianakis 2022-09-09 02:03:13 +03:00
parent 9dc4d4055c
commit 8c7b001aa5
6 changed files with 100 additions and 19 deletions

View File

@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added ### 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`) - 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. - `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 - `cbe593cf` add configurable header preample suffix and prefix for editing

View File

@ -1059,6 +1059,11 @@ The URL will be given as the first argument of the command.
.El .El
.Sh LISTING .Sh LISTING
.Bl -tag -width 36n .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 .It Ic datetime_fmt Ar String
.Pq Em optional .Pq Em optional
Datetime formatting passed verbatim to strftime(3). 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. This is the width of the right container to the entire screen width.
.\" default value .\" default value
.Pq Em 90 .Pq Em 90
.It Ic show_menu_scrollbar Ar boolean .It Ic unseen_flag Ar Option<String>
.Pq Em optional Flag to show if thread entry contains unseen mail.
Show auto-hiding scrollbar in accounts sidebar menu.
.\" default value .\" default value
.Pq Em true .Pq Em "●"
.It Ic thread_snoozed_flag Ar Option<String>
Flag to show if thread has been snoozed.
.\" default value
.Pq Em "💤"
.It Ic selected_flag Ar Option<String>
Flag to show if thread entry has been selected.
.\" default value
.Pq Em "☑️"
.It Ic attachment_flag Ar Option<String>
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 .El
.Ss Examples of sidebar mailbox tree customization .Ss Examples of sidebar mailbox tree customization
The default values The default values

View File

@ -834,6 +834,7 @@ impl Threads {
} }
} }
} }
for i in 0..self.thread_nodes[&id].children.len() { for i in 0..self.thread_nodes[&id].children.len() {
let child_hash = self.thread_nodes[&id].children[i]; let child_hash = self.thread_nodes[&id].children[i];
if let Some(child_env_hash) = self.thread_nodes[&child_hash].message() { if let Some(child_env_hash) = self.thread_nodes[&child_hash].message() {

View File

@ -22,6 +22,7 @@
use super::*; use super::*;
use crate::components::PageMovement; use crate::components::PageMovement;
use crate::jobs::JoinHandle; use crate::jobs::JoinHandle;
use indexmap::IndexSet;
use std::iter::FromIterator; use std::iter::FromIterator;
macro_rules! row_attr { macro_rules! row_attr {
@ -240,6 +241,7 @@ impl MailListingTrait for ConversationsListing {
} }
let mut max_entry_columns = 0; let mut max_entry_columns = 0;
let mut other_subjects = IndexSet::new();
let mut from_address_list = Vec::new(); let mut from_address_list = Vec::new();
let mut from_address_set: std::collections::HashSet<Vec<u8>> = let mut from_address_set: std::collections::HashSet<Vec<u8>> =
std::collections::HashSet::new(); std::collections::HashSet::new();
@ -273,17 +275,30 @@ impl MailListingTrait for ConversationsListing {
panic!(); panic!();
} }
other_subjects.clear();
from_address_list.clear(); from_address_list.clear();
from_address_set.clear(); from_address_set.clear();
for envelope in threads for (envelope, show_subject) in threads
.thread_group_iter(thread) .thread_group_iter(thread)
.filter_map(|(_, h)| threads.thread_nodes()[&h].message()) .filter_map(|(_, h)| {
.map(|env_hash| { Some((
context.accounts[&self.cursor_pos.0] threads.thread_nodes()[&h].message()?,
.collection threads.thread_nodes()[&h].show_subject(),
.get_env(env_hash) ))
})
.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() { for addr in envelope.from().iter() {
if from_address_set.contains(addr.address_spec_raw()) { if from_address_set.contains(addr.address_spec_raw()) {
continue; continue;
@ -313,6 +328,7 @@ impl MailListingTrait for ConversationsListing {
context, context,
&from_address_list, &from_address_list,
&threads, &threads,
&other_subjects,
thread, thread,
); );
max_entry_columns = std::cmp::max( max_entry_columns = std::cmp::max(
@ -627,8 +643,9 @@ impl ConversationsListing {
&self, &self,
e: &Envelope, e: &Envelope,
context: &Context, context: &Context,
from: &Vec<Address>, from: &[Address],
threads: &Threads, threads: &Threads,
other_subjects: &IndexSet<String>,
hash: ThreadHash, hash: ThreadHash,
) -> EntryStrings { ) -> EntryStrings {
let thread = threads.thread_ref(hash); let thread = threads.thread_ref(hash);
@ -669,8 +686,24 @@ impl ConversationsListing {
tags.pop(); tags.pop();
} }
} }
let mut subject = e.subject().to_string(); let mut subject = if *mailbox_settings!(
subject.truncate_at_boundary(150); 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 { if thread.len() > 1 {
EntryStrings { EntryStrings {
date: DateString(ConversationsListing::format_date(context, thread.date())), date: DateString(ConversationsListing::format_date(context, thread.date())),
@ -755,18 +788,29 @@ impl ConversationsListing {
let idx: usize = self.order[&thread_hash]; let idx: usize = self.order[&thread_hash];
let env_hash = threads.thread_nodes()[&thread_node_hash].message().unwrap(); 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_list = Vec::new();
let mut from_address_set: std::collections::HashSet<Vec<u8>> = let mut from_address_set: std::collections::HashSet<Vec<u8>> =
std::collections::HashSet::new(); std::collections::HashSet::new();
for envelope in threads for (envelope, show_subject) in threads
.thread_group_iter(thread_hash) .thread_group_iter(thread_hash)
.filter_map(|(_, h)| threads.thread_nodes()[&h].message()) .filter_map(|(_, h)| {
.map(|env_hash| { threads.thread_nodes()[&h]
context.accounts[&self.cursor_pos.0] .message()
.collection .map(|env_hash| (env_hash, threads.thread_nodes()[&h].show_subject()))
.get_env(env_hash) })
.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() { for addr in envelope.from().iter() {
if from_address_set.contains(addr.address_spec_raw()) { if from_address_set.contains(addr.address_spec_raw()) {
continue; continue;
@ -781,6 +825,7 @@ impl ConversationsListing {
context, context,
&from_address_list, &from_address_list,
&threads, &threads,
&other_subjects,
thread_hash, thread_hash,
); );
drop(envelope); drop(envelope);

View File

@ -129,6 +129,12 @@ pub struct ListingSettings {
/// Default: "📎" /// Default: "📎"
#[serde(default)] #[serde(default)]
pub attachment_flag: Option<String>, pub attachment_flag: Option<String>,
/// 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 { const fn default_divider() -> char {
@ -158,6 +164,7 @@ impl Default for ListingSettings {
thread_snoozed_flag: None, thread_snoozed_flag: None,
selected_flag: None, selected_flag: None,
attachment_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), "thread_snoozed_flag" => self.thread_snoozed_flag.lookup(field, tail),
"selected_flag" => self.selected_flag.lookup(field, tail), "selected_flag" => self.selected_flag.lookup(field, tail),
"attachment_flag" => self.attachment_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!( other => Err(MeliError::new(format!(
"{} has no field named {}", "{} has no field named {}",
parent_field, other parent_field, other

View File

@ -171,6 +171,11 @@ pub struct ListingSettingsOverride {
#[doc = " Default: \"📎\""] #[doc = " Default: \"📎\""]
#[serde(default)] #[serde(default)]
pub attachment_flag: Option<Option<String>>, pub attachment_flag: Option<Option<String>>,
#[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<bool>,
} }
impl Default for ListingSettingsOverride { impl Default for ListingSettingsOverride {
fn default() -> Self { fn default() -> Self {
@ -191,6 +196,7 @@ impl Default for ListingSettingsOverride {
thread_snoozed_flag: None, thread_snoozed_flag: None,
selected_flag: None, selected_flag: None,
attachment_flag: None, attachment_flag: None,
thread_subject_pack: None,
} }
} }
} }