accounts: add default_mailbox setting
Run cargo lints / Lint on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 8m28s Details
Run Tests / Test on ${{ matrix.build }} (linux-amd64, ubuntu-latest, stable, x86_64-unknown-linux-gnu) (pull_request) Successful in 13m30s Details

Add a default mailbox setting:

> The mailbox that is the default to open / view for this account. Must be
> a valid mailbox name.
>
> If not specified, the default is [`Self::root_mailbox`].

Closes: #350
Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
pull/370/head
Manos Pitsidianakis 2024-03-16 15:01:28 +02:00
parent 742f038f74
commit 4e941a9e8b
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
4 changed files with 155 additions and 20 deletions

View File

@ -217,6 +217,11 @@ Default values are shown in parentheses.
The backend-specific path of the root_mailbox, usually
.Sy INBOX Ns
\&.
.It Ic default_mailbox Ar String
.Pq Em optional
The mailbox that is the default to open or view for this account.
Must be a valid mailbox path.
If not specified, the default will be the root mailbox.
.It Ic format Ar String Op maildir mbox imap notmuch jmap
The format of the mail backend.
.It Ic subscribed_mailboxes Ar [String,]

View File

@ -350,9 +350,19 @@ impl Account {
.keys()
.cloned()
.collect::<HashSet<String>>();
let mut default_mailbox = self
.settings
.conf
.default_mailbox
.clone()
.into_iter()
.collect::<HashSet<String>>();
for f in ref_mailboxes.values_mut() {
if let Some(conf) = self.settings.mailbox_confs.get_mut(f.path()) {
mailbox_conf_hash_set.remove(f.path());
if default_mailbox.remove(f.path()) {
self.settings.default_mailbox = Some(f.hash());
}
conf.mailbox_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
Some(f.special_usage())
} else {
@ -447,6 +457,23 @@ impl Account {
)));
}
match self.settings.conf.default_mailbox {
Some(ref v) if !default_mailbox.is_empty() => {
let err = Error::new(format!(
"Account `{}` has default mailbox set as `{}` but it doesn't exist.",
&self.name, v
))
.set_kind(ErrorKind::Configuration);
self.is_online.set_err(err.clone());
self.main_loop_handler
.send(ThreadEvent::UIEvent(UIEvent::AccountStatusChange(
self.hash, None,
)));
return Err(err);
}
_ => {}
}
let mut tree: Vec<MailboxNode> = Vec::new();
for (h, f) in ref_mailboxes.iter() {
if !f.is_subscribed() {
@ -1394,6 +1421,12 @@ impl Account {
}
}
pub fn default_mailbox(&self) -> Option<MailboxHash> {
self.settings
.default_mailbox
.or_else(|| Some(*self.mailboxes_order.first()?))
}
pub fn mailbox_by_path(&self, path: &str) -> Result<MailboxHash> {
if let Some((mailbox_hash, _)) = self
.mailbox_entries

View File

@ -165,6 +165,12 @@ use crate::conf::deserializers::extra_settings;
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
pub struct FileAccount {
pub root_mailbox: String,
/// The mailbox that is the default to open / view for this account. Must be
/// a valid mailbox path.
///
/// If not specified, the default is [`Self::root_mailbox`].
#[serde(default = "none", skip_serializing_if = "Option::is_none")]
pub default_mailbox: Option<String>,
pub format: String,
pub identity: String,
#[serde(default)]
@ -234,6 +240,7 @@ pub struct FileSettings {
#[derive(Clone, Debug, Default, Serialize)]
pub struct AccountConf {
pub account: AccountSettings,
pub default_mailbox: Option<MailboxHash>,
pub sent_mailbox: Option<MailboxHash>,
pub conf: FileAccount,
pub conf_override: MailUIConf,
@ -286,6 +293,7 @@ impl From<FileAccount> for AccountConf {
let mailbox_confs = x.mailboxes.clone();
Self {
account: acc,
default_mailbox: None,
sent_mailbox: None,
conf_override: x.conf_override.clone(),
conf: x,
@ -538,6 +546,7 @@ This is required so that you don't accidentally start meli and find out later th
mailboxes,
extra,
manual_refresh,
default_mailbox: _,
refresh_command: _,
search_backend: _,
conf_override: _,

View File

@ -475,6 +475,18 @@ struct AccountMenuEntry {
entries: SmallVec<[MailboxMenuEntry; 16]>,
}
impl AccountMenuEntry {
fn entry_by_hash(&self, needle: MailboxHash) -> Option<usize> {
self.entries.iter().enumerate().find_map(|(i, e)| {
if e.mailbox_hash == needle {
Some(i)
} else {
None
}
})
}
}
pub trait MailListingTrait: ListingTrait {
fn as_component(&self) -> &dyn Component
where
@ -1568,7 +1580,15 @@ impl Component for Listing {
k if shortcut!(k == shortcuts[Shortcuts::LISTING]["next_account"]) => {
if self.cursor_pos.account + amount < self.accounts.len() {
self.cursor_pos.account += amount;
self.cursor_pos.menu = MenuEntryCursor::Mailbox(0);
let _new_val = self.cursor_pos.account;
self.cursor_pos.menu = if let Some(idx) = context.accounts[_new_val]
.default_mailbox()
.and_then(|h| self.accounts[_new_val].entry_by_hash(h))
{
MenuEntryCursor::Mailbox(idx)
} else {
MenuEntryCursor::Status
};
} else {
return true;
}
@ -1576,7 +1596,15 @@ impl Component for Listing {
k if shortcut!(k == shortcuts[Shortcuts::LISTING]["prev_account"]) => {
if self.cursor_pos.account >= amount {
self.cursor_pos.account -= amount;
self.cursor_pos.menu = MenuEntryCursor::Mailbox(0);
let _new_val = self.cursor_pos.account;
self.cursor_pos.menu = if let Some(idx) = context.accounts[_new_val]
.default_mailbox()
.and_then(|h| self.accounts[_new_val].entry_by_hash(h))
{
MenuEntryCursor::Mailbox(idx)
} else {
MenuEntryCursor::Status
};
} else {
return true;
}
@ -2076,9 +2104,17 @@ impl Component for Listing {
} => {
if *account > 0 {
*account -= 1;
self.menu_cursor_pos.menu = MenuEntryCursor::Mailbox(
self.accounts[*account].entries.len().saturating_sub(1),
);
self.menu_cursor_pos.menu =
if self.accounts[*account].entries.is_empty() {
MenuEntryCursor::Status
} else {
MenuEntryCursor::Mailbox(
self.accounts[*account]
.entries
.len()
.saturating_sub(1),
)
};
} else {
return true;
}
@ -2111,7 +2147,12 @@ impl Component for Listing {
} if !self.accounts[*account].entries.is_empty()
&& *menu == MenuEntryCursor::Status =>
{
*menu = MenuEntryCursor::Mailbox(0);
if let Some(idx) = context.accounts[*account]
.default_mailbox()
.and_then(|h| self.accounts[*account].entry_by_hash(h))
{
*menu = MenuEntryCursor::Mailbox(idx);
}
}
// If current account has no mailboxes, go to next account
CursorPos {
@ -2250,15 +2291,32 @@ impl Component for Listing {
{
if self.menu_cursor_pos.account + amount >= self.accounts.len() {
// Go to last mailbox.
self.menu_cursor_pos.menu = MenuEntryCursor::Mailbox(
self.accounts[self.menu_cursor_pos.account]
.entries
.len()
.saturating_sub(1),
);
self.menu_cursor_pos.menu = if self.accounts
[self.menu_cursor_pos.account]
.entries
.is_empty()
{
MenuEntryCursor::Status
} else {
MenuEntryCursor::Mailbox(
self.accounts[self.menu_cursor_pos.account]
.entries
.len()
.saturating_sub(1),
)
};
} else if self.menu_cursor_pos.account + amount < self.accounts.len() {
self.menu_cursor_pos.account += amount;
self.menu_cursor_pos.menu = MenuEntryCursor::Mailbox(0);
let _new_val = self.menu_cursor_pos.account;
self.menu_cursor_pos.menu = if let Some(idx) = context.accounts
[_new_val]
.default_mailbox()
.and_then(|h| self.accounts[_new_val].entry_by_hash(h))
{
MenuEntryCursor::Mailbox(idx)
} else {
MenuEntryCursor::Status
};
} else {
return true;
}
@ -2268,7 +2326,16 @@ impl Component for Listing {
{
if self.menu_cursor_pos.account >= amount {
self.menu_cursor_pos.account -= amount;
self.menu_cursor_pos.menu = MenuEntryCursor::Mailbox(0);
let _new_val = self.menu_cursor_pos.account;
self.menu_cursor_pos.menu = if let Some(idx) = context.accounts
[_new_val]
.default_mailbox()
.and_then(|h| self.accounts[_new_val].entry_by_hash(h))
{
MenuEntryCursor::Mailbox(idx)
} else {
MenuEntryCursor::Status
};
} else {
return true;
}
@ -2294,12 +2361,24 @@ impl Component for Listing {
menu: MenuEntryCursor::Mailbox(0)
}
) {
// Can't go anywhere upwards, we're on top already.
return true;
}
if self.menu_cursor_pos.menu == MenuEntryCursor::Mailbox(0) {
self.menu_cursor_pos.account = 0;
} else {
self.menu_cursor_pos.menu = MenuEntryCursor::Mailbox(0);
match (
self.menu_cursor_pos.menu,
context.accounts[self.menu_cursor_pos.account]
.default_mailbox()
.and_then(|h| {
self.accounts[self.menu_cursor_pos.account].entry_by_hash(h)
}),
) {
(MenuEntryCursor::Mailbox(0), _) => {
self.menu_cursor_pos.account = 0;
}
(MenuEntryCursor::Mailbox(_), Some(v)) => {
self.menu_cursor_pos.menu = MenuEntryCursor::Mailbox(v);
}
_ => return true,
}
if self.show_menu_scrollbar != ShowMenuScrollbar::Never {
self.menu_scrollbar_show_timer.rearm();
@ -2337,11 +2416,20 @@ impl Component for Listing {
self.accounts[*account].entries.len().saturating_sub(1)
) {
*account = self.accounts.len().saturating_sub(1);
*menu = MenuEntryCursor::Mailbox(0);
} else {
*menu = if let Some(idx) = context.accounts[*account]
.default_mailbox()
.and_then(|h| self.accounts[*account].entry_by_hash(h))
{
MenuEntryCursor::Mailbox(idx)
} else {
MenuEntryCursor::Status
};
} else if !self.accounts[*account].entries.is_empty() {
*menu = MenuEntryCursor::Mailbox(
self.accounts[*account].entries.len().saturating_sub(1),
);
} else {
*menu = MenuEntryCursor::Status;
}
if self.show_menu_scrollbar != ShowMenuScrollbar::Never {
self.menu_scrollbar_show_timer.rearm();