melib: remove Mailbox
Refactor Collection from melib to hold what folders have what envelopes. Frontend accounts will now have a FolderEntry for each logical folder and will unify many Account fields into one and eliminate a lot of duplicate/dead code.master
parent
b50e770b5a
commit
b6efb14824
|
@ -21,12 +21,13 @@
|
|||
|
||||
use super::*;
|
||||
use crate::backends::FolderHash;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use smallvec::SmallVec;
|
||||
use std::collections::BTreeMap;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||
|
||||
use fnv::FnvHashMap;
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
|
||||
pub struct EnvelopeRef<'g> {
|
||||
guard: RwLockReadGuard<'g, FnvHashMap<EnvelopeHash, Envelope>>,
|
||||
|
@ -68,6 +69,7 @@ pub struct Collection {
|
|||
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
|
||||
pub threads: FnvHashMap<FolderHash, Threads>,
|
||||
sent_folder: Option<FolderHash>,
|
||||
pub mailboxes: FnvHashMap<FolderHash, FnvHashSet<EnvelopeHash>>,
|
||||
}
|
||||
|
||||
impl Drop for Collection {
|
||||
|
@ -101,6 +103,7 @@ impl Collection {
|
|||
* /cur, or it was deleted).
|
||||
*/
|
||||
let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
||||
let mailboxes = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
||||
|
||||
Collection {
|
||||
envelopes: Arc::new(RwLock::new(envelopes)),
|
||||
|
@ -108,6 +111,7 @@ impl Collection {
|
|||
message_ids,
|
||||
subject_index,
|
||||
threads,
|
||||
mailboxes,
|
||||
sent_folder: None,
|
||||
}
|
||||
}
|
||||
|
@ -123,6 +127,9 @@ impl Collection {
|
|||
pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
|
||||
debug!("DEBUG: Removing {}", envelope_hash);
|
||||
self.envelopes.write().unwrap().remove(&envelope_hash);
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
m.remove(&envelope_hash);
|
||||
});
|
||||
self.threads
|
||||
.entry(folder_hash)
|
||||
.or_default()
|
||||
|
@ -144,11 +151,15 @@ impl Collection {
|
|||
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
||||
return;
|
||||
}
|
||||
let mut env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||
env.set_hash(new_hash);
|
||||
let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
m.remove(&old_hash);
|
||||
m.insert(new_hash);
|
||||
});
|
||||
envelope.set_hash(new_hash);
|
||||
self.message_ids
|
||||
.insert(env.message_id().raw().to_vec(), new_hash);
|
||||
self.envelopes.write().unwrap().insert(new_hash, env);
|
||||
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
||||
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
||||
{
|
||||
if self
|
||||
.threads
|
||||
|
@ -175,7 +186,7 @@ impl Collection {
|
|||
}
|
||||
}
|
||||
|
||||
/// Merge new Mailbox to collection and update threads.
|
||||
/// Merge new mailbox to collection and update threads.
|
||||
/// Returns a list of already existing folders whose threads were updated
|
||||
pub fn merge(
|
||||
&mut self,
|
||||
|
@ -191,16 +202,21 @@ impl Collection {
|
|||
let &mut Collection {
|
||||
ref mut threads,
|
||||
ref mut envelopes,
|
||||
ref mut mailboxes,
|
||||
ref sent_folder,
|
||||
..
|
||||
} = self;
|
||||
|
||||
if !threads.contains_key(&folder_hash) {
|
||||
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
|
||||
mailboxes.insert(folder_hash, new_envelopes.keys().cloned().collect());
|
||||
for (h, e) in new_envelopes {
|
||||
envelopes.write().unwrap().insert(h, e);
|
||||
}
|
||||
} else {
|
||||
mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
m.extend(new_envelopes.keys().cloned());
|
||||
});
|
||||
threads.entry(folder_hash).and_modify(|t| {
|
||||
let mut ordered_hash_set =
|
||||
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
|
||||
|
@ -291,6 +307,10 @@ impl Collection {
|
|||
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||
envelope.set_thread(old_env.thread());
|
||||
let new_hash = envelope.hash();
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
m.remove(&old_hash);
|
||||
m.insert(new_hash);
|
||||
});
|
||||
self.message_ids
|
||||
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
||||
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
||||
|
@ -328,6 +348,9 @@ impl Collection {
|
|||
|
||||
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
|
||||
let hash = envelope.hash();
|
||||
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||
m.insert(hash);
|
||||
});
|
||||
self.message_ids
|
||||
.insert(envelope.message_id().raw().to_vec(), hash);
|
||||
self.envelopes.write().unwrap().insert(hash, envelope);
|
||||
|
@ -365,3 +388,16 @@ impl Collection {
|
|||
self.envelopes.read().unwrap().contains_key(env_hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<&FolderHash> for Collection {
|
||||
type Output = FnvHashSet<EnvelopeHash>;
|
||||
fn index(&self, index: &FolderHash) -> &FnvHashSet<EnvelopeHash> {
|
||||
&self.mailboxes[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<&FolderHash> for Collection {
|
||||
fn index_mut(&mut self, index: &FolderHash) -> &mut FnvHashSet<EnvelopeHash> {
|
||||
self.mailboxes.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,6 @@ mod collection;
|
|||
pub mod conf;
|
||||
pub mod email;
|
||||
pub mod error;
|
||||
pub mod mailbox;
|
||||
pub mod thread;
|
||||
pub use crate::email::*;
|
||||
pub use crate::thread::*;
|
||||
|
@ -138,11 +137,10 @@ extern crate bitflags;
|
|||
extern crate fnv;
|
||||
extern crate uuid;
|
||||
|
||||
pub use crate::conf::*;
|
||||
pub use crate::mailbox::*;
|
||||
|
||||
pub use crate::backends::{Backends, RefreshEvent, RefreshEventConsumer, SpecialUsageMailbox};
|
||||
pub use crate::email::{Envelope, Flag};
|
||||
pub use crate::collection::*;
|
||||
pub use crate::conf::*;
|
||||
pub use crate::email::{Envelope, EnvelopeHash, Flag};
|
||||
pub use crate::error::{MeliError, Result};
|
||||
|
||||
pub use crate::addressbook::*;
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
/*
|
||||
* meli - mailbox module.
|
||||
*
|
||||
* Copyright 2017 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/*!
|
||||
* Mail related code.
|
||||
*
|
||||
* This module handles reading emails from various backends, handling account data etc
|
||||
*/
|
||||
|
||||
use crate::backends::Folder;
|
||||
pub use crate::collection::*;
|
||||
pub use crate::email::*;
|
||||
use fnv::{FnvHashMap, FnvHashSet};
|
||||
|
||||
/// `Mailbox` represents a folder of mail.
|
||||
#[derive(Debug, Deserialize, Serialize, Clone, Default)]
|
||||
pub struct Mailbox {
|
||||
#[serde(skip_serializing, skip_deserializing)]
|
||||
pub folder: Folder,
|
||||
name: String,
|
||||
pub envelopes: FnvHashSet<EnvelopeHash>,
|
||||
has_sent: bool,
|
||||
}
|
||||
|
||||
impl Mailbox {
|
||||
pub fn new(folder: Folder, envelopes: &FnvHashMap<EnvelopeHash, Envelope>) -> Mailbox {
|
||||
let name = folder.name().into();
|
||||
let envelopes = envelopes.keys().cloned().collect();
|
||||
Mailbox {
|
||||
folder,
|
||||
name,
|
||||
envelopes,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, envelopes: &FnvHashMap<EnvelopeHash, Envelope>) {
|
||||
self.envelopes.extend(envelopes.keys().cloned());
|
||||
}
|
||||
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.envelopes.is_empty()
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
self.envelopes.len()
|
||||
}
|
||||
pub fn insert(&mut self, h: EnvelopeHash) {
|
||||
self.envelopes.insert(h);
|
||||
}
|
||||
pub fn rename(&mut self, old_hash: EnvelopeHash, new_hash: EnvelopeHash) {
|
||||
self.envelopes.remove(&old_hash);
|
||||
|
||||
self.envelopes.insert(new_hash);
|
||||
}
|
||||
pub fn remove(&mut self, h: EnvelopeHash) {
|
||||
self.envelopes.remove(&h);
|
||||
}
|
||||
}
|
|
@ -137,7 +137,7 @@ pub trait MailListingTrait: ListingTrait {
|
|||
) {
|
||||
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 folder_hash = self.coordinates().1;
|
||||
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]
|
||||
|
@ -449,7 +449,8 @@ impl Component for Listing {
|
|||
.list_folders()
|
||||
.into_iter()
|
||||
.filter(|folder_node| {
|
||||
context.accounts[*account_index].ref_folders()[&folder_node.hash]
|
||||
context.accounts[*account_index][&folder_node.hash]
|
||||
.ref_folder
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| (f.depth, f.hash))
|
||||
|
@ -464,7 +465,8 @@ impl Component for Listing {
|
|||
.list_folders()
|
||||
.into_iter()
|
||||
.filter(|folder_node| {
|
||||
context.accounts[*account_index].ref_folders()[&folder_node.hash]
|
||||
context.accounts[*account_index][&folder_node.hash]
|
||||
.ref_folder
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| (f.depth, f.hash))
|
||||
|
@ -550,10 +552,10 @@ impl Component for Listing {
|
|||
{
|
||||
/* Account might have no folders yet if it's offline */
|
||||
/* Check if per-folder configuration overrides general configuration */
|
||||
if let Some(index_style) =
|
||||
context.accounts.get(self.cursor_pos.0).and_then(|account| {
|
||||
account.folder_confs(*folder_hash).conf_override.index_style
|
||||
})
|
||||
if let Some(index_style) = context
|
||||
.accounts
|
||||
.get(self.cursor_pos.0)
|
||||
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
|
||||
{
|
||||
self.component.set_style(index_style);
|
||||
} else if let Some(index_style) = context
|
||||
|
@ -870,15 +872,20 @@ impl Component for Listing {
|
|||
};
|
||||
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
if let Ok(m) = account[folder_hash].as_result() {
|
||||
format!(
|
||||
use crate::conf::accounts::MailboxStatus;
|
||||
match account[&folder_hash].status {
|
||||
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
|
||||
"Mailbox: {}, Messages: {}, New: {}",
|
||||
m.folder.name(),
|
||||
m.envelopes.len(),
|
||||
m.folder.count().ok().map(|(v, _)| v).unwrap_or(0),
|
||||
)
|
||||
} else {
|
||||
account[folder_hash].to_string()
|
||||
account[&folder_hash].ref_folder.name(),
|
||||
account.collection[&folder_hash].len(),
|
||||
account[&folder_hash]
|
||||
.ref_folder
|
||||
.count()
|
||||
.ok()
|
||||
.map(|(v, _)| v)
|
||||
.unwrap_or(0),
|
||||
),
|
||||
MailboxStatus::Failed(_) | MailboxStatus::None => account[&folder_hash].status(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -905,7 +912,7 @@ impl Listing {
|
|||
let entries: SmallVec<[(usize, FolderHash); 16]> = a
|
||||
.list_folders()
|
||||
.into_iter()
|
||||
.filter(|folder_node| a.ref_folders()[&folder_node.hash].is_subscribed())
|
||||
.filter(|folder_node| a[&folder_node.hash].ref_folder.is_subscribed())
|
||||
.map(|f| (f.depth, f.hash))
|
||||
.collect::<_>();
|
||||
|
||||
|
@ -974,8 +981,11 @@ impl Listing {
|
|||
debug!("BUG: invalid area in print_account");
|
||||
}
|
||||
// Each entry and its index in the account
|
||||
let folders: FnvHashMap<FolderHash, Folder> =
|
||||
context.accounts[a.index].ref_folders().clone();
|
||||
let folders: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
|
||||
.folder_entries
|
||||
.iter()
|
||||
.map(|(&hash, entry)| (hash, entry.ref_folder.clone()))
|
||||
.collect();
|
||||
|
||||
let upper_left = upper_left!(area);
|
||||
let bottom_right = bottom_right!(area);
|
||||
|
@ -1160,7 +1170,9 @@ impl Listing {
|
|||
.list_folders()
|
||||
.into_iter()
|
||||
.filter(|folder_node| {
|
||||
context.accounts[self.cursor_pos.0].ref_folders()[&folder_node.hash].is_subscribed()
|
||||
context.accounts[self.cursor_pos.0][&folder_node.hash]
|
||||
.ref_folder
|
||||
.is_subscribed()
|
||||
})
|
||||
.map(|f| (f.depth, f.hash))
|
||||
.collect::<_>();
|
||||
|
@ -1175,7 +1187,7 @@ impl Listing {
|
|||
if let Some(index_style) = context
|
||||
.accounts
|
||||
.get(self.cursor_pos.0)
|
||||
.and_then(|account| account.folder_confs(*folder_hash).conf_override.index_style)
|
||||
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
|
||||
{
|
||||
self.component.set_style(index_style);
|
||||
} else if let Some(index_style) = context
|
||||
|
|
|
@ -142,7 +142,7 @@ impl MailListingTrait for CompactListing {
|
|||
ret
|
||||
};
|
||||
let message: String =
|
||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
|
||||
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
self.length = 0;
|
||||
|
@ -650,7 +650,7 @@ impl CompactListing {
|
|||
hash: ThreadHash,
|
||||
) -> EntryStrings {
|
||||
let thread = threads.thread_ref(hash);
|
||||
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
|
||||
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mut tags = String::new();
|
||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||
|
@ -716,9 +716,8 @@ impl CompactListing {
|
|||
|
||||
fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) {
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
let mailbox = &account[self.cursor_pos.1].unwrap();
|
||||
|
||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
||||
let threads = &account.collection.threads[&self.cursor_pos.1];
|
||||
self.order.clear();
|
||||
self.selection.clear();
|
||||
self.length = 0;
|
||||
|
@ -752,7 +751,7 @@ impl CompactListing {
|
|||
debug!("key = {}", root_env_hash);
|
||||
debug!(
|
||||
"name = {} {}",
|
||||
mailbox.name(),
|
||||
account[&self.cursor_pos.1].name(),
|
||||
context.accounts[self.cursor_pos.0].name()
|
||||
);
|
||||
debug!("{:#?}", context.accounts);
|
||||
|
@ -837,7 +836,7 @@ impl CompactListing {
|
|||
//debug!("key = {}", root_env_hash);
|
||||
//debug!(
|
||||
// "name = {} {}",
|
||||
// mailbox.name(),
|
||||
// account[&self.cursor_pos.1].name(),
|
||||
// context.accounts[self.cursor_pos.0].name()
|
||||
//);
|
||||
//debug!("{:#?}", context.accounts);
|
||||
|
@ -968,8 +967,7 @@ impl CompactListing {
|
|||
}
|
||||
}
|
||||
if self.length == 0 && self.filter_term.is_empty() {
|
||||
let mailbox = &account[self.cursor_pos.1];
|
||||
let message = mailbox.to_string();
|
||||
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
|
||||
write_string_to_grid(
|
||||
|
|
|
@ -128,7 +128,7 @@ impl MailListingTrait for ConversationsListing {
|
|||
ret
|
||||
};
|
||||
let message: String =
|
||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
|
||||
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
|
||||
self.content =
|
||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
self.length = 0;
|
||||
|
@ -592,7 +592,7 @@ impl ConversationsListing {
|
|||
hash: ThreadHash,
|
||||
) -> EntryStrings {
|
||||
let thread = threads.thread_ref(hash);
|
||||
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
|
||||
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mut tags = String::new();
|
||||
let mut colors = SmallVec::new();
|
||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||
|
@ -658,9 +658,8 @@ impl ConversationsListing {
|
|||
|
||||
fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) {
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
let mailbox = &account[self.cursor_pos.1].unwrap();
|
||||
|
||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
||||
let threads = &account.collection.threads[&self.cursor_pos.1];
|
||||
self.order.clear();
|
||||
self.selection.clear();
|
||||
self.length = 0;
|
||||
|
@ -684,7 +683,7 @@ impl ConversationsListing {
|
|||
debug!("key = {}", root_env_hash);
|
||||
debug!(
|
||||
"name = {} {}",
|
||||
mailbox.name(),
|
||||
account[&self.cursor_pos.1].name(),
|
||||
context.accounts[self.cursor_pos.0].name()
|
||||
);
|
||||
debug!("{:#?}", context.accounts);
|
||||
|
@ -852,8 +851,7 @@ impl ConversationsListing {
|
|||
.set_attrs(self.color_cache.theme_default.attrs);
|
||||
ret
|
||||
};
|
||||
let mailbox = &account[self.cursor_pos.1];
|
||||
let message = mailbox.to_string();
|
||||
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
|
||||
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
write_string_to_grid(
|
||||
&message,
|
||||
|
|
|
@ -142,7 +142,7 @@ impl MailListingTrait for PlainListing {
|
|||
ret
|
||||
};
|
||||
let message: String =
|
||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
|
||||
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
self.length = 0;
|
||||
|
@ -158,9 +158,7 @@ impl MailListingTrait for PlainListing {
|
|||
return;
|
||||
}
|
||||
}
|
||||
self.local_collection = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.unwrap()
|
||||
.envelopes
|
||||
self.local_collection = context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1]
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
@ -169,9 +167,8 @@ impl MailListingTrait for PlainListing {
|
|||
.envelopes
|
||||
.read()
|
||||
.unwrap();
|
||||
self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
||||
.unwrap()
|
||||
.envelopes
|
||||
self.thread_node_hashes = context.accounts[self.cursor_pos.0].collection
|
||||
[&self.cursor_pos.1]
|
||||
.iter()
|
||||
.map(|h| (*h, env_lck[h].thread()))
|
||||
.collect();
|
||||
|
@ -616,7 +613,7 @@ impl PlainListing {
|
|||
}
|
||||
}
|
||||
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
||||
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
|
||||
let folder = &context.accounts[self.cursor_pos.0][&self.cursor_pos.1].conf;
|
||||
let mut tags = String::new();
|
||||
let mut colors = SmallVec::new();
|
||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
||||
|
@ -664,7 +661,7 @@ impl PlainListing {
|
|||
|
||||
fn redraw_list(&mut self, context: &Context) {
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
let mailbox = &account[self.cursor_pos.1].unwrap();
|
||||
let mailbox = &account[&self.cursor_pos.1];
|
||||
|
||||
self.order.clear();
|
||||
self.selection.clear();
|
||||
|
@ -890,8 +887,7 @@ impl PlainListing {
|
|||
}
|
||||
}
|
||||
if self.length == 0 && self.filter_term.is_empty() {
|
||||
let mailbox = &account[self.cursor_pos.1];
|
||||
let message = mailbox.to_string();
|
||||
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
|
||||
self.data_columns.columns[0] =
|
||||
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
|
||||
write_string_to_grid(
|
||||
|
@ -1139,10 +1135,7 @@ impl Component for PlainListing {
|
|||
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
if !account.collection.contains_key(new_hash)
|
||||
|| !account[self.cursor_pos.1]
|
||||
.unwrap()
|
||||
.envelopes
|
||||
.contains(new_hash)
|
||||
|| !account.collection[&self.cursor_pos.1].contains(new_hash)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -99,7 +99,7 @@ impl MailListingTrait for ThreadListing {
|
|||
ret
|
||||
};
|
||||
let message: String =
|
||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
|
||||
context.accounts[self.cursor_pos.0][&self.cursor_pos.1].status();
|
||||
self.content =
|
||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
self.length = 0;
|
||||
|
@ -116,9 +116,7 @@ impl MailListingTrait for ThreadListing {
|
|||
}
|
||||
}
|
||||
let account = &context.accounts[self.cursor_pos.0];
|
||||
let mailbox = account[self.cursor_pos.1].unwrap();
|
||||
|
||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
||||
let threads = &account.collection.threads[&self.cursor_pos.1];
|
||||
self.length = threads.len();
|
||||
self.locations.clear();
|
||||
let default_cell = {
|
||||
|
@ -129,7 +127,7 @@ impl MailListingTrait for ThreadListing {
|
|||
ret
|
||||
};
|
||||
if self.length == 0 {
|
||||
let message = format!("Folder `{}` is empty.", mailbox.folder.name());
|
||||
let message = format!("Folder `{}` is empty.", account[&self.cursor_pos.1].name());
|
||||
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||
write_string_to_grid(
|
||||
&message,
|
||||
|
@ -348,12 +346,7 @@ impl ListingTrait for ThreadListing {
|
|||
}
|
||||
|
||||
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
||||
let mailbox = if context.accounts[self.cursor_pos.0][self.cursor_pos.1].is_available() {
|
||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].unwrap()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if mailbox.is_empty() || mailbox.len() <= idx {
|
||||
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -414,12 +407,7 @@ impl ThreadListing {
|
|||
}
|
||||
|
||||
fn highlight_line_self(&mut self, idx: usize, context: &Context) {
|
||||
let mailbox = if context.accounts[self.cursor_pos.0][self.cursor_pos.1].is_available() {
|
||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].unwrap()
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
if mailbox.is_empty() {
|
||||
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
|
||||
return;
|
||||
}
|
||||
if self.locations[idx] != 0 {
|
||||
|
|
|
@ -307,10 +307,14 @@ impl StatusPanel {
|
|||
self.content[(2, h + y + 1)].set_ch(' ');
|
||||
}
|
||||
}
|
||||
let count = a.ref_folders.values().fold((0, 0), |acc, f| {
|
||||
let count = f.count().unwrap_or((0, 0));
|
||||
(acc.0 + count.0, acc.1 + count.1)
|
||||
});
|
||||
let count = a
|
||||
.folder_entries
|
||||
.values()
|
||||
.map(|entry| &entry.ref_folder)
|
||||
.fold((0, 0), |acc, f| {
|
||||
let count = f.count().unwrap_or((0, 0));
|
||||
(acc.0 + count.0, acc.1 + count.1)
|
||||
});
|
||||
let (mut column_width, _) = write_string_to_grid(
|
||||
&format!("Messages total {}, unseen {}", count.1, count.0),
|
||||
&mut self.content,
|
||||
|
@ -357,8 +361,9 @@ impl StatusPanel {
|
|||
None,
|
||||
);
|
||||
for (i, f) in a
|
||||
.ref_folders
|
||||
.folder_entries
|
||||
.values()
|
||||
.map(|entry| &entry.ref_folder)
|
||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||
.enumerate()
|
||||
{
|
||||
|
@ -474,8 +479,9 @@ impl Component for AccountStatus {
|
|||
None,
|
||||
);
|
||||
for f in a
|
||||
.ref_folders
|
||||
.folder_entries
|
||||
.values()
|
||||
.map(|entry| &entry.ref_folder)
|
||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||
{
|
||||
line += 1;
|
||||
|
@ -501,7 +507,7 @@ impl Component for AccountStatus {
|
|||
);
|
||||
line += 2;
|
||||
for folder_node in a.list_folders() {
|
||||
let f: &Folder = &a.ref_folders()[&folder_node.hash];
|
||||
let f: &Folder = &a[&folder_node.hash].ref_folder;
|
||||
if f.is_subscribed() {
|
||||
write_string_to_grid(
|
||||
f.path(),
|
||||
|
|
|
@ -69,7 +69,7 @@ pub struct ThreadView {
|
|||
impl ThreadView {
|
||||
const DESCRIPTION: &'static str = "thread view";
|
||||
/*
|
||||
* coordinates: (account index, mailbox index, root set thread_node index)
|
||||
* coordinates: (account index, folder_hash, root set thread_node index)
|
||||
* expanded_hash: optional position of expanded entry when we render the threadview. Default
|
||||
* expanded message is the last one.
|
||||
* context: current context
|
||||
|
@ -163,8 +163,7 @@ impl ThreadView {
|
|||
|
||||
fn initiate(&mut self, expanded_hash: Option<ThreadNodeHash>, context: &Context) {
|
||||
let account = &context.accounts[self.coordinates.0];
|
||||
let mailbox = &account[self.coordinates.1].unwrap();
|
||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
||||
let threads = &account.collection.threads[&self.coordinates.1];
|
||||
|
||||
if !threads.groups.contains_key(&self.thread_group) {
|
||||
return;
|
||||
|
@ -649,8 +648,7 @@ impl ThreadView {
|
|||
/* First draw the thread subject on the first row */
|
||||
let y = if self.dirty {
|
||||
let account = &context.accounts[self.coordinates.0];
|
||||
let mailbox = &account[self.coordinates.1].unwrap();
|
||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
||||
let threads = &account.collection.threads[&self.coordinates.1];
|
||||
let thread_root = threads
|
||||
.thread_group_iter(self.thread_group)
|
||||
.next()
|
||||
|
@ -752,8 +750,7 @@ impl ThreadView {
|
|||
/* First draw the thread subject on the first row */
|
||||
let y = {
|
||||
let account = &context.accounts[self.coordinates.0];
|
||||
let mailbox = &account[self.coordinates.1].unwrap();
|
||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
||||
let threads = &account.collection.threads[&self.coordinates.1];
|
||||
let thread_root = threads
|
||||
.thread_group_iter(self.thread_group)
|
||||
.next()
|
||||
|
|
|
@ -30,11 +30,12 @@ use melib::backends::{
|
|||
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
|
||||
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
|
||||
};
|
||||
use melib::email::*;
|
||||
use melib::error::{MeliError, Result};
|
||||
use melib::mailbox::*;
|
||||
use melib::text_processing::GlobMatch;
|
||||
use melib::thread::{SortField, SortOrder, ThreadNode, ThreadNodeHash, Threads};
|
||||
use melib::AddressBook;
|
||||
use melib::Collection;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
|
||||
|
@ -50,97 +51,61 @@ use std::sync::{Arc, RwLock};
|
|||
|
||||
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
|
||||
|
||||
macro_rules! mailbox {
|
||||
($idx:expr, $folders:expr) => {
|
||||
$folders.get_mut(&$idx).unwrap().unwrap_mut()
|
||||
};
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub enum MailboxEntry {
|
||||
Available(Mailbox),
|
||||
pub enum MailboxStatus {
|
||||
Available,
|
||||
Failed(MeliError),
|
||||
/// first argument is done work, and second is total work
|
||||
Parsing(Mailbox, usize, usize),
|
||||
Parsing(usize, usize),
|
||||
None,
|
||||
}
|
||||
|
||||
impl Default for MailboxEntry {
|
||||
impl Default for MailboxStatus {
|
||||
fn default() -> Self {
|
||||
MailboxEntry::Parsing(Mailbox::default(), 0, 0)
|
||||
MailboxStatus::Parsing(0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for MailboxEntry {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
MailboxEntry::Available(ref m) => m.name().to_string(),
|
||||
MailboxEntry::Failed(ref e) => e.to_string(),
|
||||
MailboxEntry::None => "Not subscribed, is this a bug?".to_string(),
|
||||
MailboxEntry::Parsing(_, done, total) => {
|
||||
format!("Parsing messages. [{}/{}]", done, total)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
impl MailboxEntry {
|
||||
impl MailboxStatus {
|
||||
pub fn is_available(&self) -> bool {
|
||||
if let MailboxEntry::Available(_) = self {
|
||||
if let MailboxStatus::Available = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_parsing(&self) -> bool {
|
||||
if let MailboxEntry::Parsing(_, _, _) = self {
|
||||
if let MailboxStatus::Parsing(_, _) = self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_result(&self) -> Result<&Mailbox> {
|
||||
match self {
|
||||
MailboxEntry::Available(ref m) => Ok(m),
|
||||
MailboxEntry::Parsing(ref m, _, _) => Ok(m),
|
||||
MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
|
||||
"Mailbox is not available: {}",
|
||||
e.to_string()
|
||||
))),
|
||||
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
|
||||
#[derive(Debug)]
|
||||
pub struct FolderEntry {
|
||||
pub status: MailboxStatus,
|
||||
pub name: String,
|
||||
pub ref_folder: Folder,
|
||||
pub conf: FileFolderConf,
|
||||
pub worker: Worker,
|
||||
}
|
||||
|
||||
impl FolderEntry {
|
||||
pub fn status(&self) -> String {
|
||||
match self.status {
|
||||
MailboxStatus::Available => self.name().to_string(),
|
||||
MailboxStatus::Failed(ref e) => e.to_string(),
|
||||
MailboxStatus::None => "Not subscribed, is this a bug?".to_string(),
|
||||
MailboxStatus::Parsing(done, total) => {
|
||||
format!("Parsing messages. [{}/{}]", done, total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_mut_result(&mut self) -> Result<&mut Mailbox> {
|
||||
match self {
|
||||
MailboxEntry::Available(ref mut m) => Ok(m),
|
||||
MailboxEntry::Parsing(ref mut m, _, _) => Ok(m),
|
||||
MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
|
||||
"Mailbox is not available: {}",
|
||||
e.to_string()
|
||||
))),
|
||||
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap_mut(&mut self) -> &mut Mailbox {
|
||||
match self {
|
||||
MailboxEntry::Available(ref mut m) => m,
|
||||
MailboxEntry::Parsing(ref mut m, _, _) => m,
|
||||
e => panic!(format!("mailbox is not available! {:#}", e)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unwrap(&self) -> &Mailbox {
|
||||
match self {
|
||||
MailboxEntry::Available(ref m) => m,
|
||||
MailboxEntry::Parsing(ref m, _, _) => m,
|
||||
e => panic!(format!("mailbox is not available! {:#}", e)),
|
||||
}
|
||||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,20 +114,13 @@ pub struct Account {
|
|||
pub index: usize,
|
||||
name: String,
|
||||
pub is_online: bool,
|
||||
pub(crate) folders: FnvHashMap<FolderHash, MailboxEntry>,
|
||||
pub(crate) ref_folders: FnvHashMap<FolderHash, Folder>,
|
||||
pub(crate) folder_confs: FnvHashMap<FolderHash, FileFolderConf>,
|
||||
pub(crate) folder_entries: FnvHashMap<FolderHash, FolderEntry>,
|
||||
pub(crate) folders_order: Vec<FolderHash>,
|
||||
pub(crate) folder_names: FnvHashMap<FolderHash, String>,
|
||||
tree: Vec<FolderNode>,
|
||||
sent_folder: Option<FolderHash>,
|
||||
pub(crate) collection: Collection,
|
||||
|
||||
pub(crate) address_book: AddressBook,
|
||||
|
||||
pub(crate) workers: FnvHashMap<FolderHash, Worker>,
|
||||
pub(crate) work_context: WorkContext,
|
||||
|
||||
pub(crate) settings: AccountConf,
|
||||
pub(crate) runtime_settings: AccountConf,
|
||||
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
||||
|
@ -210,7 +168,7 @@ impl Drop for Account {
|
|||
permissions.set_mode(0o600); // Read/write for owner only.
|
||||
f.set_permissions(permissions).unwrap();
|
||||
let writer = io::BufWriter::new(f);
|
||||
if let Err(err) = bincode::serialize_into(writer, &self.folders) {
|
||||
if let Err(err) = bincode::serialize_into(writer, &self.collection) {
|
||||
eprintln!("{}", err);
|
||||
};
|
||||
};
|
||||
|
@ -218,26 +176,6 @@ impl Drop for Account {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MailboxIterator<'a> {
|
||||
folders_order: &'a [FolderHash],
|
||||
folders: &'a FnvHashMap<FolderHash, MailboxEntry>,
|
||||
pos: usize,
|
||||
}
|
||||
|
||||
impl<'a> Iterator for MailboxIterator<'a> {
|
||||
type Item = &'a MailboxEntry;
|
||||
|
||||
fn next(&mut self) -> Option<&'a MailboxEntry> {
|
||||
if self.pos == self.folders.len() {
|
||||
return None;
|
||||
}
|
||||
let fh = &self.folders_order[self.pos];
|
||||
|
||||
self.pos += 1;
|
||||
Some(&self.folders[&fh])
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug, Clone, Default)]
|
||||
pub struct FolderNode {
|
||||
pub hash: FolderHash,
|
||||
|
@ -295,16 +233,12 @@ impl Account {
|
|||
index,
|
||||
name,
|
||||
is_online: false,
|
||||
folders: Default::default(),
|
||||
ref_folders: Default::default(),
|
||||
folder_confs: Default::default(),
|
||||
folder_entries: Default::default(),
|
||||
folders_order: Default::default(),
|
||||
folder_names: Default::default(),
|
||||
tree: Default::default(),
|
||||
address_book,
|
||||
sent_folder: Default::default(),
|
||||
collection: Default::default(),
|
||||
workers: Default::default(),
|
||||
work_context,
|
||||
runtime_settings: settings.clone(),
|
||||
settings,
|
||||
|
@ -325,12 +259,9 @@ impl Account {
|
|||
return;
|
||||
}
|
||||
};
|
||||
let mut folders: FnvHashMap<FolderHash, MailboxEntry> =
|
||||
let mut folder_entries: FnvHashMap<FolderHash, FolderEntry> =
|
||||
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
|
||||
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
|
||||
let mut workers: FnvHashMap<FolderHash, Worker> = FnvHashMap::default();
|
||||
let mut folder_names = FnvHashMap::default();
|
||||
let mut folder_confs = FnvHashMap::default();
|
||||
|
||||
let mut sent_folder = None;
|
||||
for f in ref_folders.values_mut() {
|
||||
|
@ -355,7 +286,16 @@ impl Account {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
folder_confs.insert(f.hash(), conf.clone());
|
||||
folder_entries.insert(
|
||||
f.hash(),
|
||||
FolderEntry {
|
||||
ref_folder: f.clone(),
|
||||
name: f.path().to_string(),
|
||||
status: MailboxStatus::None,
|
||||
conf: conf.clone(),
|
||||
worker: None,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let mut new = FileFolderConf::default();
|
||||
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||
|
@ -368,9 +308,17 @@ impl Account {
|
|||
tmp
|
||||
};
|
||||
|
||||
folder_confs.insert(f.hash(), new);
|
||||
folder_entries.insert(
|
||||
f.hash(),
|
||||
FolderEntry {
|
||||
ref_folder: f.clone(),
|
||||
name: f.path().to_string(),
|
||||
status: MailboxStatus::None,
|
||||
conf: new,
|
||||
worker: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
folder_names.insert(f.hash(), f.path().to_string());
|
||||
}
|
||||
|
||||
let mut tree: Vec<FolderNode> = Vec::new();
|
||||
|
@ -378,36 +326,27 @@ impl Account {
|
|||
for (h, f) in ref_folders.iter() {
|
||||
if !f.is_subscribed() {
|
||||
/* Skip unsubscribed folder */
|
||||
folders.insert(*h, MailboxEntry::None);
|
||||
workers.insert(*h, None);
|
||||
continue;
|
||||
}
|
||||
folders.insert(
|
||||
*h,
|
||||
MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0),
|
||||
);
|
||||
workers.insert(
|
||||
*h,
|
||||
Account::new_worker(
|
||||
folder_entries.entry(*h).and_modify(|entry| {
|
||||
entry.status = MailboxStatus::Parsing(0, 0);
|
||||
entry.worker = Account::new_worker(
|
||||
f.clone(),
|
||||
&mut self.backend,
|
||||
&self.work_context,
|
||||
self.notify_fn.clone(),
|
||||
),
|
||||
);
|
||||
);
|
||||
});
|
||||
collection.mailboxes.insert(*h, Default::default());
|
||||
collection.threads.insert(*h, Threads::default());
|
||||
}
|
||||
|
||||
build_folders_order(&mut tree, &ref_folders, &mut folders_order);
|
||||
self.folders = folders;
|
||||
self.ref_folders = ref_folders;
|
||||
self.folder_confs = folder_confs;
|
||||
build_folders_order(&mut tree, &folder_entries, &mut folders_order);
|
||||
self.folders_order = folders_order;
|
||||
self.folder_names = folder_names;
|
||||
self.folder_entries = folder_entries;
|
||||
self.tree = tree;
|
||||
self.sent_folder = sent_folder;
|
||||
self.collection = collection;
|
||||
self.workers = workers;
|
||||
}
|
||||
|
||||
fn new_worker(
|
||||
|
@ -483,7 +422,7 @@ impl Account {
|
|||
Some(w)
|
||||
}
|
||||
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
|
||||
if !self.folders[&folder_hash].is_available() {
|
||||
if !self.folder_entries[&folder_hash].status.is_available() {
|
||||
self.event_queue.push_back((folder_hash, event));
|
||||
return None;
|
||||
}
|
||||
|
@ -493,7 +432,6 @@ impl Account {
|
|||
//let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
|
||||
match kind {
|
||||
RefreshEventKind::Update(old_hash, envelope) => {
|
||||
mailbox!(&folder_hash, self.folders).rename(old_hash, envelope.hash());
|
||||
#[cfg(feature = "sqlite3")]
|
||||
{
|
||||
if let Err(err) = crate::sqlite3::remove(old_hash).and_then(|_| {
|
||||
|
@ -514,7 +452,6 @@ impl Account {
|
|||
}
|
||||
RefreshEventKind::Rename(old_hash, new_hash) => {
|
||||
debug!("rename {} to {}", old_hash, new_hash);
|
||||
mailbox!(&folder_hash, self.folders).rename(old_hash, new_hash);
|
||||
self.collection.rename(old_hash, new_hash, folder_hash);
|
||||
#[cfg(feature = "sqlite3")]
|
||||
{
|
||||
|
@ -538,13 +475,10 @@ impl Account {
|
|||
RefreshEventKind::Create(envelope) => {
|
||||
let env_hash = envelope.hash();
|
||||
if self.collection.contains_key(&env_hash)
|
||||
&& mailbox!(&folder_hash, self.folders)
|
||||
.envelopes
|
||||
.contains(&env_hash)
|
||||
&& self.collection[&folder_hash].contains(&env_hash)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
mailbox!(&folder_hash, self.folders).insert(env_hash);
|
||||
let (is_seen, is_draft) =
|
||||
{ (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
|
||||
let (subject, from) = {
|
||||
|
@ -578,10 +512,12 @@ impl Account {
|
|||
self.collection.insert_reply(env_hash);
|
||||
}
|
||||
|
||||
let ref_folders: FnvHashMap<FolderHash, Folder> =
|
||||
self.backend.read().unwrap().folders().unwrap();
|
||||
let folder_conf = &self.folder_confs[&folder_hash];
|
||||
if folder_conf.folder_conf().ignore.is_true() {
|
||||
if self.folder_entries[&folder_hash]
|
||||
.conf
|
||||
.folder_conf
|
||||
.ignore
|
||||
.is_true()
|
||||
{
|
||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||
}
|
||||
|
||||
|
@ -606,13 +542,12 @@ impl Account {
|
|||
"{}\n{} {}",
|
||||
subject,
|
||||
self.name,
|
||||
ref_folders[&folder_hash].name(),
|
||||
self.folder_entries[&folder_hash].name()
|
||||
),
|
||||
Some(crate::types::NotificationType::NewMail),
|
||||
));
|
||||
}
|
||||
RefreshEventKind::Remove(envelope_hash) => {
|
||||
mailbox!(&folder_hash, self.folders).remove(envelope_hash);
|
||||
#[cfg(feature = "sqlite3")]
|
||||
{
|
||||
let envelopes = self.collection.envelopes.read();
|
||||
|
@ -633,15 +568,15 @@ impl Account {
|
|||
return Some(EnvelopeRemove(envelope_hash));
|
||||
}
|
||||
RefreshEventKind::Rescan => {
|
||||
let ref_folders: FnvHashMap<FolderHash, Folder> =
|
||||
self.backend.read().unwrap().folders().unwrap();
|
||||
let handle = Account::new_worker(
|
||||
ref_folders[&folder_hash].clone(),
|
||||
self.folder_entries[&folder_hash].ref_folder.clone(),
|
||||
&mut self.backend,
|
||||
&self.work_context,
|
||||
self.notify_fn.clone(),
|
||||
);
|
||||
self.workers.insert(folder_hash, handle);
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
entry.worker = handle;
|
||||
});
|
||||
}
|
||||
RefreshEventKind::Failure(e) => {
|
||||
debug!("RefreshEvent Failure: {}", e.to_string());
|
||||
|
@ -717,18 +652,14 @@ impl Account {
|
|||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.folders.len()
|
||||
self.tree.len()
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.folders.is_empty()
|
||||
}
|
||||
|
||||
pub fn ref_folders(&self) -> &FnvHashMap<FolderHash, Folder> {
|
||||
&self.ref_folders
|
||||
self.tree.is_empty()
|
||||
}
|
||||
|
||||
pub fn list_folders(&self) -> Vec<FolderNode> {
|
||||
let mut ret = Vec::with_capacity(self.folders.len());
|
||||
let mut ret = Vec::with_capacity(self.folder_entries.len());
|
||||
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
|
||||
ret.push(node.clone());
|
||||
for c in node.children.iter() {
|
||||
|
@ -747,14 +678,12 @@ impl Account {
|
|||
pub fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
pub fn workers(&mut self) -> &mut FnvHashMap<FolderHash, Worker> {
|
||||
&mut self.workers
|
||||
}
|
||||
|
||||
fn load_mailbox(&mut self, folder_hash: FolderHash, payload: Result<Vec<Envelope>>) {
|
||||
if payload.is_err() {
|
||||
self.folders
|
||||
.insert(folder_hash, MailboxEntry::Failed(payload.unwrap_err()));
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
entry.status = MailboxStatus::Failed(payload.unwrap_err());
|
||||
});
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
||||
.unwrap();
|
||||
|
@ -765,20 +694,14 @@ impl Account {
|
|||
.into_iter()
|
||||
.map(|e| (e.hash(), e))
|
||||
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
|
||||
match self.folders.entry(folder_hash).or_default() {
|
||||
MailboxEntry::Failed(_) | MailboxEntry::None => {}
|
||||
MailboxEntry::Parsing(ref mut m, _, _) | MailboxEntry::Available(ref mut m) => {
|
||||
m.merge(&envelopes);
|
||||
if let Some(updated_folders) =
|
||||
self.collection
|
||||
.merge(envelopes, folder_hash, self.sent_folder)
|
||||
{
|
||||
for f in updated_folders {
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
if let Some(updated_folders) =
|
||||
self.collection
|
||||
.merge(envelopes, folder_hash, self.sent_folder)
|
||||
{
|
||||
for f in updated_folders {
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
self.sender
|
||||
|
@ -791,11 +714,17 @@ impl Account {
|
|||
return Err(0);
|
||||
}
|
||||
loop {
|
||||
match self.workers.get_mut(&folder_hash).unwrap() {
|
||||
match self
|
||||
.folder_entries
|
||||
.get_mut(&folder_hash)
|
||||
.unwrap()
|
||||
.worker
|
||||
.as_mut()
|
||||
{
|
||||
None => {
|
||||
return if self.folders[&folder_hash].is_available()
|
||||
|| (self.folders[&folder_hash].is_parsing()
|
||||
&& self.collection.threads.contains_key(&folder_hash))
|
||||
return if self.folder_entries[&folder_hash].status.is_available()
|
||||
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
||||
&& self.collection.mailboxes.contains_key(&folder_hash))
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -812,13 +741,9 @@ impl Account {
|
|||
}
|
||||
Ok(AsyncStatus::Finished) => {
|
||||
debug!("got finished in status for {}", folder_hash);
|
||||
self.folders.entry(folder_hash).and_modify(|f| {
|
||||
let m = if let MailboxEntry::Parsing(m, _, _) = f {
|
||||
std::mem::replace(m, Mailbox::default())
|
||||
} else {
|
||||
return;
|
||||
};
|
||||
*f = MailboxEntry::Available(m);
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
entry.status = MailboxStatus::Available;
|
||||
entry.worker = None;
|
||||
});
|
||||
self.sender
|
||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
||||
|
@ -826,13 +751,14 @@ impl Account {
|
|||
folder_hash,
|
||||
))))
|
||||
.unwrap();
|
||||
|
||||
self.workers.insert(folder_hash, None);
|
||||
}
|
||||
Ok(AsyncStatus::ProgressReport(n)) => {
|
||||
self.folders.entry(folder_hash).and_modify(|f| {
|
||||
if let MailboxEntry::Parsing(_, ref mut d, _) = f {
|
||||
*d += n;
|
||||
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||
match entry.status {
|
||||
MailboxStatus::Parsing(ref mut d, _) => {
|
||||
*d += n;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
//return Err(n);
|
||||
|
@ -843,9 +769,9 @@ impl Account {
|
|||
},
|
||||
};
|
||||
}
|
||||
if self.folders[&folder_hash].is_available()
|
||||
|| (self.folders[&folder_hash].is_parsing()
|
||||
&& self.collection.threads.contains_key(&folder_hash))
|
||||
if self.folder_entries[&folder_hash].status.is_available()
|
||||
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
||||
&& self.collection.mailboxes.contains_key(&folder_hash))
|
||||
{
|
||||
Ok(())
|
||||
} else {
|
||||
|
@ -909,13 +835,6 @@ impl Account {
|
|||
}
|
||||
self.backend.write().unwrap().save(bytes, folder, flags)
|
||||
}
|
||||
pub fn iter_mailboxes(&self) -> MailboxIterator {
|
||||
MailboxIterator {
|
||||
folders_order: &self.folders_order,
|
||||
folders: &self.folders,
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
|
||||
self.collection.contains_key(&h)
|
||||
|
@ -966,39 +885,40 @@ impl Account {
|
|||
tmp
|
||||
};
|
||||
|
||||
self.folder_confs.insert(folder.hash(), new);
|
||||
self.folder_names
|
||||
.insert(folder.hash(), folder.path().to_string());
|
||||
self.folders.insert(
|
||||
folder.hash(),
|
||||
MailboxEntry::Parsing(
|
||||
Mailbox::new(folder.clone(), &FnvHashMap::default()),
|
||||
0,
|
||||
0,
|
||||
),
|
||||
);
|
||||
self.workers.insert(
|
||||
folder.hash(),
|
||||
Account::new_worker(
|
||||
folder.clone(),
|
||||
&mut self.backend,
|
||||
&self.work_context,
|
||||
self.notify_fn.clone(),
|
||||
),
|
||||
let folder_hash = folder.hash();
|
||||
self.folder_entries.insert(
|
||||
folder_hash,
|
||||
FolderEntry {
|
||||
name: folder.path().to_string(),
|
||||
status: MailboxStatus::Parsing(0, 0),
|
||||
conf: new,
|
||||
worker: Account::new_worker(
|
||||
folder.clone(),
|
||||
&mut self.backend,
|
||||
&self.work_context,
|
||||
self.notify_fn.clone(),
|
||||
),
|
||||
ref_folder: folder,
|
||||
},
|
||||
);
|
||||
self.collection
|
||||
.threads
|
||||
.insert(folder.hash(), Threads::default());
|
||||
self.ref_folders = self.backend.read().unwrap().folders()?;
|
||||
build_folders_order(&mut self.tree, &self.ref_folders, &mut self.folders_order);
|
||||
.insert(folder_hash, Threads::default());
|
||||
build_folders_order(
|
||||
&mut self.tree,
|
||||
&self.folder_entries,
|
||||
&mut self.folders_order,
|
||||
);
|
||||
Ok(format!("`{}` successfully created.", &path))
|
||||
}
|
||||
FolderOperation::Delete(path) => {
|
||||
if self.ref_folders.len() == 1 {
|
||||
if self.folder_entries.len() == 1 {
|
||||
return Err(MeliError::new("Cannot delete only mailbox."));
|
||||
}
|
||||
let folder_hash = if let Some((folder_hash, _)) =
|
||||
self.ref_folders.iter().find(|(_, f)| f.path() == path)
|
||||
let folder_hash = if let Some((folder_hash, _)) = self
|
||||
.folder_entries
|
||||
.iter()
|
||||
.find(|(_, f)| f.ref_folder.path() == path)
|
||||
{
|
||||
*folder_hash
|
||||
} else {
|
||||
|
@ -1011,13 +931,9 @@ impl Account {
|
|||
folder_hash,
|
||||
))))
|
||||
.unwrap();
|
||||
self.folders.remove(&folder_hash);
|
||||
self.ref_folders = self.backend.read().unwrap().folders()?;
|
||||
self.folder_confs.remove(&folder_hash);
|
||||
if let Some(pos) = self.folders_order.iter().position(|&h| h == folder_hash) {
|
||||
self.folders_order.remove(pos);
|
||||
}
|
||||
self.folder_names.remove(&folder_hash);
|
||||
if let Some(pos) = self.tree.iter().position(|n| n.hash == folder_hash) {
|
||||
self.tree.remove(pos);
|
||||
}
|
||||
|
@ -1025,7 +941,9 @@ impl Account {
|
|||
self.sent_folder = None;
|
||||
}
|
||||
self.collection.threads.remove(&folder_hash);
|
||||
self.workers.remove(&folder_hash); // FIXME Kill worker as well
|
||||
self.collection.mailboxes.remove(&folder_hash);
|
||||
self.folder_entries.remove(&folder_hash);
|
||||
// FIXME Kill worker as well
|
||||
|
||||
// FIXME remove from settings as well
|
||||
|
||||
|
@ -1038,29 +956,13 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn folder_confs(&self, folder_hash: FolderHash) -> &FileFolderConf {
|
||||
&self.folder_confs[&folder_hash]
|
||||
}
|
||||
|
||||
pub fn sent_folder(&self) -> &str {
|
||||
let sent_folder = self
|
||||
.folder_confs
|
||||
.iter()
|
||||
.find(|(_, f)| f.folder_conf().usage == Some(SpecialUsageMailbox::Sent));
|
||||
if let Some(sent_folder) = sent_folder.as_ref() {
|
||||
&self.folder_names[&sent_folder.0]
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
pub fn special_use_folder(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
|
||||
let ret = self
|
||||
.folder_confs
|
||||
.folder_entries
|
||||
.iter()
|
||||
.find(|(_, f)| f.folder_conf().usage == Some(special_use));
|
||||
.find(|(_, f)| f.conf.folder_conf().usage == Some(special_use));
|
||||
if let Some(ret) = ret.as_ref() {
|
||||
Some(&self.folder_names[&ret.0])
|
||||
Some(ret.1.name())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -1117,7 +1019,7 @@ impl Account {
|
|||
let mut ret = SmallVec::new();
|
||||
let envelopes = self.collection.envelopes.read().unwrap();
|
||||
|
||||
for &env_hash in &self.folders[&folder_hash].as_result()?.envelopes {
|
||||
for &env_hash in &self.collection[&folder_hash].iter() {
|
||||
let envelope = &envelopes[&env_hash];
|
||||
if envelope.subject().contains(&search_term) {
|
||||
ret.push(env_hash);
|
||||
|
@ -1140,32 +1042,32 @@ impl Account {
|
|||
}
|
||||
}
|
||||
|
||||
impl Index<FolderHash> for Account {
|
||||
type Output = MailboxEntry;
|
||||
fn index(&self, index: FolderHash) -> &MailboxEntry {
|
||||
&self.folders[&index]
|
||||
impl Index<&FolderHash> for Account {
|
||||
type Output = FolderEntry;
|
||||
fn index(&self, index: &FolderHash) -> &FolderEntry {
|
||||
&self.folder_entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<FolderHash> for Account {
|
||||
fn index_mut(&mut self, index: FolderHash) -> &mut MailboxEntry {
|
||||
self.folders.get_mut(&index).unwrap()
|
||||
impl IndexMut<&FolderHash> for Account {
|
||||
fn index_mut(&mut self, index: &FolderHash) -> &mut FolderEntry {
|
||||
self.folder_entries.get_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn build_folders_order(
|
||||
tree: &mut Vec<FolderNode>,
|
||||
ref_folders: &FnvHashMap<FolderHash, Folder>,
|
||||
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
||||
folders_order: &mut Vec<FolderHash>,
|
||||
) {
|
||||
let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new();
|
||||
tree.clear();
|
||||
folders_order.clear();
|
||||
for (h, f) in ref_folders.iter() {
|
||||
if f.parent().is_none() {
|
||||
for (h, f) in folder_entries.iter() {
|
||||
if f.ref_folder.parent().is_none() {
|
||||
fn rec(
|
||||
h: FolderHash,
|
||||
ref_folders: &FnvHashMap<FolderHash, Folder>,
|
||||
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
||||
depth: usize,
|
||||
) -> FolderNode {
|
||||
let mut node = FolderNode {
|
||||
|
@ -1173,18 +1075,18 @@ fn build_folders_order(
|
|||
children: Vec::new(),
|
||||
depth,
|
||||
};
|
||||
for &c in ref_folders[&h].children() {
|
||||
node.children.push(rec(c, ref_folders, depth + 1));
|
||||
for &c in folder_entries[&h].ref_folder.children() {
|
||||
node.children.push(rec(c, folder_entries, depth + 1));
|
||||
}
|
||||
node
|
||||
};
|
||||
|
||||
tree.push(rec(*h, &ref_folders, 0));
|
||||
for &c in f.children() {
|
||||
tree.push(rec(*h, &folder_entries, 0));
|
||||
for &c in f.ref_folder.children() {
|
||||
stack.push(c);
|
||||
}
|
||||
while let Some(next) = stack.pop() {
|
||||
for c in ref_folders[&next].children() {
|
||||
for c in folder_entries[&next].ref_folder.children() {
|
||||
stack.push(*c);
|
||||
}
|
||||
}
|
||||
|
@ -1192,14 +1094,23 @@ fn build_folders_order(
|
|||
}
|
||||
|
||||
tree.sort_unstable_by(|a, b| {
|
||||
if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
|
||||
if folder_entries[&b.hash]
|
||||
.ref_folder
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Greater
|
||||
} else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
|
||||
} else if folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
ref_folders[&a.hash]
|
||||
folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
.path()
|
||||
.cmp(&ref_folders[&b.hash].path())
|
||||
.cmp(&folder_entries[&b.hash].ref_folder.path())
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -1207,14 +1118,23 @@ fn build_folders_order(
|
|||
for n in tree.iter_mut() {
|
||||
folders_order.push(n.hash);
|
||||
n.children.sort_unstable_by(|a, b| {
|
||||
if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
|
||||
if folder_entries[&b.hash]
|
||||
.ref_folder
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Greater
|
||||
} else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
|
||||
} else if folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
.path()
|
||||
.eq_ignore_ascii_case("INBOX")
|
||||
{
|
||||
std::cmp::Ordering::Less
|
||||
} else {
|
||||
ref_folders[&a.hash]
|
||||
folder_entries[&a.hash]
|
||||
.ref_folder
|
||||
.path()
|
||||
.cmp(&ref_folders[&b.hash].path())
|
||||
.cmp(&folder_entries[&b.hash].ref_folder.path())
|
||||
}
|
||||
});
|
||||
stack.extend(n.children.iter().rev().map(Some));
|
||||
|
|
|
@ -153,7 +153,7 @@ impl Context {
|
|||
debug!(
|
||||
"hash & folder: {:?} {}",
|
||||
folder_node.hash,
|
||||
accounts[account_pos].ref_folders()[&folder_node.hash].name()
|
||||
accounts[account_pos][&folder_node.hash].name()
|
||||
);
|
||||
mailbox_hashes.insert(folder_node.hash, account_pos);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue