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.async
parent
b50e770b5a
commit
b6efb14824
|
@ -21,12 +21,13 @@
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::backends::FolderHash;
|
use crate::backends::FolderHash;
|
||||||
|
use core::ops::{Index, IndexMut};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
use fnv::FnvHashMap;
|
use fnv::{FnvHashMap, FnvHashSet};
|
||||||
|
|
||||||
pub struct EnvelopeRef<'g> {
|
pub struct EnvelopeRef<'g> {
|
||||||
guard: RwLockReadGuard<'g, FnvHashMap<EnvelopeHash, Envelope>>,
|
guard: RwLockReadGuard<'g, FnvHashMap<EnvelopeHash, Envelope>>,
|
||||||
|
@ -68,6 +69,7 @@ pub struct Collection {
|
||||||
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
|
subject_index: Option<BTreeMap<String, EnvelopeHash>>,
|
||||||
pub threads: FnvHashMap<FolderHash, Threads>,
|
pub threads: FnvHashMap<FolderHash, Threads>,
|
||||||
sent_folder: Option<FolderHash>,
|
sent_folder: Option<FolderHash>,
|
||||||
|
pub mailboxes: FnvHashMap<FolderHash, FnvHashSet<EnvelopeHash>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for Collection {
|
impl Drop for Collection {
|
||||||
|
@ -101,6 +103,7 @@ impl Collection {
|
||||||
* /cur, or it was deleted).
|
* /cur, or it was deleted).
|
||||||
*/
|
*/
|
||||||
let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
||||||
|
let mailboxes = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
||||||
|
|
||||||
Collection {
|
Collection {
|
||||||
envelopes: Arc::new(RwLock::new(envelopes)),
|
envelopes: Arc::new(RwLock::new(envelopes)),
|
||||||
|
@ -108,6 +111,7 @@ impl Collection {
|
||||||
message_ids,
|
message_ids,
|
||||||
subject_index,
|
subject_index,
|
||||||
threads,
|
threads,
|
||||||
|
mailboxes,
|
||||||
sent_folder: None,
|
sent_folder: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -123,6 +127,9 @@ impl Collection {
|
||||||
pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
|
pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
|
||||||
debug!("DEBUG: Removing {}", envelope_hash);
|
debug!("DEBUG: Removing {}", envelope_hash);
|
||||||
self.envelopes.write().unwrap().remove(&envelope_hash);
|
self.envelopes.write().unwrap().remove(&envelope_hash);
|
||||||
|
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||||
|
m.remove(&envelope_hash);
|
||||||
|
});
|
||||||
self.threads
|
self.threads
|
||||||
.entry(folder_hash)
|
.entry(folder_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
|
@ -144,11 +151,15 @@ impl Collection {
|
||||||
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
let mut envelope = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||||
env.set_hash(new_hash);
|
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||||
|
m.remove(&old_hash);
|
||||||
|
m.insert(new_hash);
|
||||||
|
});
|
||||||
|
envelope.set_hash(new_hash);
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(env.message_id().raw().to_vec(), new_hash);
|
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
||||||
self.envelopes.write().unwrap().insert(new_hash, env);
|
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
||||||
{
|
{
|
||||||
if self
|
if self
|
||||||
.threads
|
.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
|
/// Returns a list of already existing folders whose threads were updated
|
||||||
pub fn merge(
|
pub fn merge(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -191,16 +202,21 @@ impl Collection {
|
||||||
let &mut Collection {
|
let &mut Collection {
|
||||||
ref mut threads,
|
ref mut threads,
|
||||||
ref mut envelopes,
|
ref mut envelopes,
|
||||||
|
ref mut mailboxes,
|
||||||
ref sent_folder,
|
ref sent_folder,
|
||||||
..
|
..
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if !threads.contains_key(&folder_hash) {
|
if !threads.contains_key(&folder_hash) {
|
||||||
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
|
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
|
||||||
|
mailboxes.insert(folder_hash, new_envelopes.keys().cloned().collect());
|
||||||
for (h, e) in new_envelopes {
|
for (h, e) in new_envelopes {
|
||||||
envelopes.write().unwrap().insert(h, e);
|
envelopes.write().unwrap().insert(h, e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
mailboxes.entry(folder_hash).and_modify(|m| {
|
||||||
|
m.extend(new_envelopes.keys().cloned());
|
||||||
|
});
|
||||||
threads.entry(folder_hash).and_modify(|t| {
|
threads.entry(folder_hash).and_modify(|t| {
|
||||||
let mut ordered_hash_set =
|
let mut ordered_hash_set =
|
||||||
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
|
new_envelopes.keys().cloned().collect::<Vec<EnvelopeHash>>();
|
||||||
|
@ -291,6 +307,10 @@ impl Collection {
|
||||||
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
let old_env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||||
envelope.set_thread(old_env.thread());
|
envelope.set_thread(old_env.thread());
|
||||||
let new_hash = envelope.hash();
|
let new_hash = envelope.hash();
|
||||||
|
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||||
|
m.remove(&old_hash);
|
||||||
|
m.insert(new_hash);
|
||||||
|
});
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
.insert(envelope.message_id().raw().to_vec(), new_hash);
|
||||||
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
||||||
|
@ -328,6 +348,9 @@ impl Collection {
|
||||||
|
|
||||||
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
|
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
|
||||||
let hash = envelope.hash();
|
let hash = envelope.hash();
|
||||||
|
self.mailboxes.entry(folder_hash).and_modify(|m| {
|
||||||
|
m.insert(hash);
|
||||||
|
});
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelope.message_id().raw().to_vec(), hash);
|
.insert(envelope.message_id().raw().to_vec(), hash);
|
||||||
self.envelopes.write().unwrap().insert(hash, envelope);
|
self.envelopes.write().unwrap().insert(hash, envelope);
|
||||||
|
@ -365,3 +388,16 @@ impl Collection {
|
||||||
self.envelopes.read().unwrap().contains_key(env_hash)
|
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 conf;
|
||||||
pub mod email;
|
pub mod email;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod mailbox;
|
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub use crate::email::*;
|
pub use crate::email::*;
|
||||||
pub use crate::thread::*;
|
pub use crate::thread::*;
|
||||||
|
@ -138,11 +137,10 @@ extern crate bitflags;
|
||||||
extern crate fnv;
|
extern crate fnv;
|
||||||
extern crate uuid;
|
extern crate uuid;
|
||||||
|
|
||||||
pub use crate::conf::*;
|
|
||||||
pub use crate::mailbox::*;
|
|
||||||
|
|
||||||
pub use crate::backends::{Backends, RefreshEvent, RefreshEventConsumer, SpecialUsageMailbox};
|
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::error::{MeliError, Result};
|
||||||
|
|
||||||
pub use crate::addressbook::*;
|
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 account = &mut context.accounts[self.coordinates().0];
|
||||||
let mut envs_to_set: SmallVec<[EnvelopeHash; 8]> = SmallVec::new();
|
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) {
|
for (_, h) in account.collection.threads[&folder_hash].thread_group_iter(thread_hash) {
|
||||||
envs_to_set.push(
|
envs_to_set.push(
|
||||||
account.collection.threads[&folder_hash].thread_nodes()[&h]
|
account.collection.threads[&folder_hash].thread_nodes()[&h]
|
||||||
|
@ -449,7 +449,8 @@ impl Component for Listing {
|
||||||
.list_folders()
|
.list_folders()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|folder_node| {
|
.filter(|folder_node| {
|
||||||
context.accounts[*account_index].ref_folders()[&folder_node.hash]
|
context.accounts[*account_index][&folder_node.hash]
|
||||||
|
.ref_folder
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.hash))
|
.map(|f| (f.depth, f.hash))
|
||||||
|
@ -464,7 +465,8 @@ impl Component for Listing {
|
||||||
.list_folders()
|
.list_folders()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|folder_node| {
|
.filter(|folder_node| {
|
||||||
context.accounts[*account_index].ref_folders()[&folder_node.hash]
|
context.accounts[*account_index][&folder_node.hash]
|
||||||
|
.ref_folder
|
||||||
.is_subscribed()
|
.is_subscribed()
|
||||||
})
|
})
|
||||||
.map(|f| (f.depth, f.hash))
|
.map(|f| (f.depth, f.hash))
|
||||||
|
@ -550,10 +552,10 @@ impl Component for Listing {
|
||||||
{
|
{
|
||||||
/* Account might have no folders yet if it's offline */
|
/* Account might have no folders yet if it's offline */
|
||||||
/* Check if per-folder configuration overrides general configuration */
|
/* Check if per-folder configuration overrides general configuration */
|
||||||
if let Some(index_style) =
|
if let Some(index_style) = context
|
||||||
context.accounts.get(self.cursor_pos.0).and_then(|account| {
|
.accounts
|
||||||
account.folder_confs(*folder_hash).conf_override.index_style
|
.get(self.cursor_pos.0)
|
||||||
})
|
.and_then(|account| account[folder_hash].conf.conf_override.index_style)
|
||||||
{
|
{
|
||||||
self.component.set_style(index_style);
|
self.component.set_style(index_style);
|
||||||
} else if let Some(index_style) = context
|
} else if let Some(index_style) = context
|
||||||
|
@ -870,15 +872,20 @@ impl Component for Listing {
|
||||||
};
|
};
|
||||||
|
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
if let Ok(m) = account[folder_hash].as_result() {
|
use crate::conf::accounts::MailboxStatus;
|
||||||
format!(
|
match account[&folder_hash].status {
|
||||||
|
MailboxStatus::Available | MailboxStatus::Parsing(_, _) => format!(
|
||||||
"Mailbox: {}, Messages: {}, New: {}",
|
"Mailbox: {}, Messages: {}, New: {}",
|
||||||
m.folder.name(),
|
account[&folder_hash].ref_folder.name(),
|
||||||
m.envelopes.len(),
|
account.collection[&folder_hash].len(),
|
||||||
m.folder.count().ok().map(|(v, _)| v).unwrap_or(0),
|
account[&folder_hash]
|
||||||
)
|
.ref_folder
|
||||||
} else {
|
.count()
|
||||||
account[folder_hash].to_string()
|
.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
|
let entries: SmallVec<[(usize, FolderHash); 16]> = a
|
||||||
.list_folders()
|
.list_folders()
|
||||||
.into_iter()
|
.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))
|
.map(|f| (f.depth, f.hash))
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
|
|
||||||
|
@ -974,8 +981,11 @@ impl Listing {
|
||||||
debug!("BUG: invalid area in print_account");
|
debug!("BUG: invalid area in print_account");
|
||||||
}
|
}
|
||||||
// Each entry and its index in the account
|
// Each entry and its index in the account
|
||||||
let folders: FnvHashMap<FolderHash, Folder> =
|
let folders: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
|
||||||
context.accounts[a.index].ref_folders().clone();
|
.folder_entries
|
||||||
|
.iter()
|
||||||
|
.map(|(&hash, entry)| (hash, entry.ref_folder.clone()))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
@ -1160,7 +1170,9 @@ impl Listing {
|
||||||
.list_folders()
|
.list_folders()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|folder_node| {
|
.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))
|
.map(|f| (f.depth, f.hash))
|
||||||
.collect::<_>();
|
.collect::<_>();
|
||||||
|
@ -1175,7 +1187,7 @@ impl Listing {
|
||||||
if let Some(index_style) = context
|
if let Some(index_style) = context
|
||||||
.accounts
|
.accounts
|
||||||
.get(self.cursor_pos.0)
|
.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);
|
self.component.set_style(index_style);
|
||||||
} else if let Some(index_style) = context
|
} else if let Some(index_style) = context
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl MailListingTrait for CompactListing {
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
let message: String =
|
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] =
|
self.data_columns.columns[0] =
|
||||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
|
@ -650,7 +650,7 @@ impl CompactListing {
|
||||||
hash: ThreadHash,
|
hash: ThreadHash,
|
||||||
) -> EntryStrings {
|
) -> EntryStrings {
|
||||||
let thread = threads.thread_ref(hash);
|
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 tags = String::new();
|
||||||
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
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>>) {
|
fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
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.order.clear();
|
||||||
self.selection.clear();
|
self.selection.clear();
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
|
@ -752,7 +751,7 @@ impl CompactListing {
|
||||||
debug!("key = {}", root_env_hash);
|
debug!("key = {}", root_env_hash);
|
||||||
debug!(
|
debug!(
|
||||||
"name = {} {}",
|
"name = {} {}",
|
||||||
mailbox.name(),
|
account[&self.cursor_pos.1].name(),
|
||||||
context.accounts[self.cursor_pos.0].name()
|
context.accounts[self.cursor_pos.0].name()
|
||||||
);
|
);
|
||||||
debug!("{:#?}", context.accounts);
|
debug!("{:#?}", context.accounts);
|
||||||
|
@ -837,7 +836,7 @@ impl CompactListing {
|
||||||
//debug!("key = {}", root_env_hash);
|
//debug!("key = {}", root_env_hash);
|
||||||
//debug!(
|
//debug!(
|
||||||
// "name = {} {}",
|
// "name = {} {}",
|
||||||
// mailbox.name(),
|
// account[&self.cursor_pos.1].name(),
|
||||||
// context.accounts[self.cursor_pos.0].name()
|
// context.accounts[self.cursor_pos.0].name()
|
||||||
//);
|
//);
|
||||||
//debug!("{:#?}", context.accounts);
|
//debug!("{:#?}", context.accounts);
|
||||||
|
@ -968,8 +967,7 @@ impl CompactListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.length == 0 && self.filter_term.is_empty() {
|
if self.length == 0 && self.filter_term.is_empty() {
|
||||||
let mailbox = &account[self.cursor_pos.1];
|
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
|
||||||
let message = mailbox.to_string();
|
|
||||||
self.data_columns.columns[0] =
|
self.data_columns.columns[0] =
|
||||||
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
|
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
|
|
|
@ -128,7 +128,7 @@ impl MailListingTrait for ConversationsListing {
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
let message: String =
|
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 =
|
self.content =
|
||||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
|
@ -592,7 +592,7 @@ impl ConversationsListing {
|
||||||
hash: ThreadHash,
|
hash: ThreadHash,
|
||||||
) -> EntryStrings {
|
) -> EntryStrings {
|
||||||
let thread = threads.thread_ref(hash);
|
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 tags = String::new();
|
||||||
let mut colors = SmallVec::new();
|
let mut colors = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
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>>) {
|
fn redraw_list(&mut self, context: &Context, items: Box<dyn Iterator<Item = ThreadHash>>) {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
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.order.clear();
|
||||||
self.selection.clear();
|
self.selection.clear();
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
|
@ -684,7 +683,7 @@ impl ConversationsListing {
|
||||||
debug!("key = {}", root_env_hash);
|
debug!("key = {}", root_env_hash);
|
||||||
debug!(
|
debug!(
|
||||||
"name = {} {}",
|
"name = {} {}",
|
||||||
mailbox.name(),
|
account[&self.cursor_pos.1].name(),
|
||||||
context.accounts[self.cursor_pos.0].name()
|
context.accounts[self.cursor_pos.0].name()
|
||||||
);
|
);
|
||||||
debug!("{:#?}", context.accounts);
|
debug!("{:#?}", context.accounts);
|
||||||
|
@ -852,8 +851,7 @@ impl ConversationsListing {
|
||||||
.set_attrs(self.color_cache.theme_default.attrs);
|
.set_attrs(self.color_cache.theme_default.attrs);
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
let mailbox = &account[self.cursor_pos.1];
|
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
|
||||||
let message = mailbox.to_string();
|
|
||||||
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&message,
|
&message,
|
||||||
|
|
|
@ -142,7 +142,7 @@ impl MailListingTrait for PlainListing {
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
let message: String =
|
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] =
|
self.data_columns.columns[0] =
|
||||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
|
@ -158,9 +158,7 @@ impl MailListingTrait for PlainListing {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.local_collection = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
self.local_collection = context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1]
|
||||||
.unwrap()
|
|
||||||
.envelopes
|
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -169,9 +167,8 @@ impl MailListingTrait for PlainListing {
|
||||||
.envelopes
|
.envelopes
|
||||||
.read()
|
.read()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
|
self.thread_node_hashes = context.accounts[self.cursor_pos.0].collection
|
||||||
.unwrap()
|
[&self.cursor_pos.1]
|
||||||
.envelopes
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| (*h, env_lck[h].thread()))
|
.map(|h| (*h, env_lck[h].thread()))
|
||||||
.collect();
|
.collect();
|
||||||
|
@ -616,7 +613,7 @@ impl PlainListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
|
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 tags = String::new();
|
||||||
let mut colors = SmallVec::new();
|
let mut colors = SmallVec::new();
|
||||||
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
|
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) {
|
fn redraw_list(&mut self, context: &Context) {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
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.order.clear();
|
||||||
self.selection.clear();
|
self.selection.clear();
|
||||||
|
@ -890,8 +887,7 @@ impl PlainListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if self.length == 0 && self.filter_term.is_empty() {
|
if self.length == 0 && self.filter_term.is_empty() {
|
||||||
let mailbox = &account[self.cursor_pos.1];
|
let message = format!("{} is empty", account[&self.cursor_pos.1].name());
|
||||||
let message = mailbox.to_string();
|
|
||||||
self.data_columns.columns[0] =
|
self.data_columns.columns[0] =
|
||||||
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
|
CellBuffer::new_with_context(message.len(), self.length + 1, default_cell, context);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
|
@ -1139,10 +1135,7 @@ impl Component for PlainListing {
|
||||||
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
if !account.collection.contains_key(new_hash)
|
if !account.collection.contains_key(new_hash)
|
||||||
|| !account[self.cursor_pos.1]
|
|| !account.collection[&self.cursor_pos.1].contains(new_hash)
|
||||||
.unwrap()
|
|
||||||
.envelopes
|
|
||||||
.contains(new_hash)
|
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
let message: String =
|
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 =
|
self.content =
|
||||||
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
self.length = 0;
|
self.length = 0;
|
||||||
|
@ -116,9 +116,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let mailbox = account[self.cursor_pos.1].unwrap();
|
let threads = &account.collection.threads[&self.cursor_pos.1];
|
||||||
|
|
||||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
|
||||||
self.length = threads.len();
|
self.length = threads.len();
|
||||||
self.locations.clear();
|
self.locations.clear();
|
||||||
let default_cell = {
|
let default_cell = {
|
||||||
|
@ -129,7 +127,7 @@ impl MailListingTrait for ThreadListing {
|
||||||
ret
|
ret
|
||||||
};
|
};
|
||||||
if self.length == 0 {
|
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);
|
self.content = CellBuffer::new_with_context(message.len(), 1, default_cell, context);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&message,
|
&message,
|
||||||
|
@ -348,12 +346,7 @@ impl ListingTrait for ThreadListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
|
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() {
|
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
|
||||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].unwrap()
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if mailbox.is_empty() || mailbox.len() <= idx {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -414,12 +407,7 @@ impl ThreadListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn highlight_line_self(&mut self, idx: usize, context: &Context) {
|
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() {
|
if context.accounts[self.cursor_pos.0].collection[&self.cursor_pos.1].is_empty() {
|
||||||
context.accounts[self.cursor_pos.0][self.cursor_pos.1].unwrap()
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
if mailbox.is_empty() {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.locations[idx] != 0 {
|
if self.locations[idx] != 0 {
|
||||||
|
|
|
@ -307,10 +307,14 @@ impl StatusPanel {
|
||||||
self.content[(2, h + y + 1)].set_ch(' ');
|
self.content[(2, h + y + 1)].set_ch(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let count = a.ref_folders.values().fold((0, 0), |acc, f| {
|
let count = a
|
||||||
let count = f.count().unwrap_or((0, 0));
|
.folder_entries
|
||||||
(acc.0 + count.0, acc.1 + count.1)
|
.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(
|
let (mut column_width, _) = write_string_to_grid(
|
||||||
&format!("Messages total {}, unseen {}", count.1, count.0),
|
&format!("Messages total {}, unseen {}", count.1, count.0),
|
||||||
&mut self.content,
|
&mut self.content,
|
||||||
|
@ -357,8 +361,9 @@ impl StatusPanel {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
for (i, f) in a
|
for (i, f) in a
|
||||||
.ref_folders
|
.folder_entries
|
||||||
.values()
|
.values()
|
||||||
|
.map(|entry| &entry.ref_folder)
|
||||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
|
@ -474,8 +479,9 @@ impl Component for AccountStatus {
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
for f in a
|
for f in a
|
||||||
.ref_folders
|
.folder_entries
|
||||||
.values()
|
.values()
|
||||||
|
.map(|entry| &entry.ref_folder)
|
||||||
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
.filter(|f| f.special_usage() != SpecialUsageMailbox::Normal)
|
||||||
{
|
{
|
||||||
line += 1;
|
line += 1;
|
||||||
|
@ -501,7 +507,7 @@ impl Component for AccountStatus {
|
||||||
);
|
);
|
||||||
line += 2;
|
line += 2;
|
||||||
for folder_node in a.list_folders() {
|
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() {
|
if f.is_subscribed() {
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
f.path(),
|
f.path(),
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub struct ThreadView {
|
||||||
impl ThreadView {
|
impl ThreadView {
|
||||||
const DESCRIPTION: &'static str = "thread view";
|
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_hash: optional position of expanded entry when we render the threadview. Default
|
||||||
* expanded message is the last one.
|
* expanded message is the last one.
|
||||||
* context: current context
|
* context: current context
|
||||||
|
@ -163,8 +163,7 @@ impl ThreadView {
|
||||||
|
|
||||||
fn initiate(&mut self, expanded_hash: Option<ThreadNodeHash>, context: &Context) {
|
fn initiate(&mut self, expanded_hash: Option<ThreadNodeHash>, context: &Context) {
|
||||||
let account = &context.accounts[self.coordinates.0];
|
let account = &context.accounts[self.coordinates.0];
|
||||||
let mailbox = &account[self.coordinates.1].unwrap();
|
let threads = &account.collection.threads[&self.coordinates.1];
|
||||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
|
||||||
|
|
||||||
if !threads.groups.contains_key(&self.thread_group) {
|
if !threads.groups.contains_key(&self.thread_group) {
|
||||||
return;
|
return;
|
||||||
|
@ -649,8 +648,7 @@ impl ThreadView {
|
||||||
/* First draw the thread subject on the first row */
|
/* First draw the thread subject on the first row */
|
||||||
let y = if self.dirty {
|
let y = if self.dirty {
|
||||||
let account = &context.accounts[self.coordinates.0];
|
let account = &context.accounts[self.coordinates.0];
|
||||||
let mailbox = &account[self.coordinates.1].unwrap();
|
let threads = &account.collection.threads[&self.coordinates.1];
|
||||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
|
||||||
let thread_root = threads
|
let thread_root = threads
|
||||||
.thread_group_iter(self.thread_group)
|
.thread_group_iter(self.thread_group)
|
||||||
.next()
|
.next()
|
||||||
|
@ -752,8 +750,7 @@ impl ThreadView {
|
||||||
/* First draw the thread subject on the first row */
|
/* First draw the thread subject on the first row */
|
||||||
let y = {
|
let y = {
|
||||||
let account = &context.accounts[self.coordinates.0];
|
let account = &context.accounts[self.coordinates.0];
|
||||||
let mailbox = &account[self.coordinates.1].unwrap();
|
let threads = &account.collection.threads[&self.coordinates.1];
|
||||||
let threads = &account.collection.threads[&mailbox.folder.hash()];
|
|
||||||
let thread_root = threads
|
let thread_root = threads
|
||||||
.thread_group_iter(self.thread_group)
|
.thread_group_iter(self.thread_group)
|
||||||
.next()
|
.next()
|
||||||
|
|
|
@ -30,11 +30,12 @@ use melib::backends::{
|
||||||
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
|
BackendOp, Backends, Folder, FolderHash, MailBackend, NotifyFn, ReadOnlyOp, RefreshEvent,
|
||||||
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
|
RefreshEventConsumer, RefreshEventKind, SpecialUsageMailbox,
|
||||||
};
|
};
|
||||||
|
use melib::email::*;
|
||||||
use melib::error::{MeliError, Result};
|
use melib::error::{MeliError, Result};
|
||||||
use melib::mailbox::*;
|
|
||||||
use melib::text_processing::GlobMatch;
|
use melib::text_processing::GlobMatch;
|
||||||
use melib::thread::{SortField, SortOrder, ThreadNode, ThreadNodeHash, Threads};
|
use melib::thread::{SortField, SortOrder, ThreadNode, ThreadNodeHash, Threads};
|
||||||
use melib::AddressBook;
|
use melib::AddressBook;
|
||||||
|
use melib::Collection;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
|
||||||
use crate::types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
|
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>>>>;
|
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)]
|
#[derive(Serialize, Debug)]
|
||||||
pub enum MailboxEntry {
|
pub enum MailboxStatus {
|
||||||
Available(Mailbox),
|
Available,
|
||||||
Failed(MeliError),
|
Failed(MeliError),
|
||||||
/// first argument is done work, and second is total work
|
/// first argument is done work, and second is total work
|
||||||
Parsing(Mailbox, usize, usize),
|
Parsing(usize, usize),
|
||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for MailboxEntry {
|
impl Default for MailboxStatus {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
MailboxEntry::Parsing(Mailbox::default(), 0, 0)
|
MailboxStatus::Parsing(0, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl std::fmt::Display for MailboxEntry {
|
impl MailboxStatus {
|
||||||
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 {
|
|
||||||
pub fn is_available(&self) -> bool {
|
pub fn is_available(&self) -> bool {
|
||||||
if let MailboxEntry::Available(_) = self {
|
if let MailboxStatus::Available = self {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn is_parsing(&self) -> bool {
|
pub fn is_parsing(&self) -> bool {
|
||||||
if let MailboxEntry::Parsing(_, _, _) = self {
|
if let MailboxStatus::Parsing(_, _) = self {
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn as_result(&self) -> Result<&Mailbox> {
|
#[derive(Debug)]
|
||||||
match self {
|
pub struct FolderEntry {
|
||||||
MailboxEntry::Available(ref m) => Ok(m),
|
pub status: MailboxStatus,
|
||||||
MailboxEntry::Parsing(ref m, _, _) => Ok(m),
|
pub name: String,
|
||||||
MailboxEntry::Failed(ref e) => Err(MeliError::new(format!(
|
pub ref_folder: Folder,
|
||||||
"Mailbox is not available: {}",
|
pub conf: FileFolderConf,
|
||||||
e.to_string()
|
pub worker: Worker,
|
||||||
))),
|
}
|
||||||
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
|
|
||||||
|
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> {
|
pub fn name(&self) -> &str {
|
||||||
match self {
|
&self.name
|
||||||
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)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,20 +114,13 @@ pub struct Account {
|
||||||
pub index: usize,
|
pub index: usize,
|
||||||
name: String,
|
name: String,
|
||||||
pub is_online: bool,
|
pub is_online: bool,
|
||||||
pub(crate) folders: FnvHashMap<FolderHash, MailboxEntry>,
|
pub(crate) folder_entries: FnvHashMap<FolderHash, FolderEntry>,
|
||||||
pub(crate) ref_folders: FnvHashMap<FolderHash, Folder>,
|
|
||||||
pub(crate) folder_confs: FnvHashMap<FolderHash, FileFolderConf>,
|
|
||||||
pub(crate) folders_order: Vec<FolderHash>,
|
pub(crate) folders_order: Vec<FolderHash>,
|
||||||
pub(crate) folder_names: FnvHashMap<FolderHash, String>,
|
|
||||||
tree: Vec<FolderNode>,
|
tree: Vec<FolderNode>,
|
||||||
sent_folder: Option<FolderHash>,
|
sent_folder: Option<FolderHash>,
|
||||||
pub(crate) collection: Collection,
|
pub(crate) collection: Collection,
|
||||||
|
|
||||||
pub(crate) address_book: AddressBook,
|
pub(crate) address_book: AddressBook,
|
||||||
|
|
||||||
pub(crate) workers: FnvHashMap<FolderHash, Worker>,
|
|
||||||
pub(crate) work_context: WorkContext,
|
pub(crate) work_context: WorkContext,
|
||||||
|
|
||||||
pub(crate) settings: AccountConf,
|
pub(crate) settings: AccountConf,
|
||||||
pub(crate) runtime_settings: AccountConf,
|
pub(crate) runtime_settings: AccountConf,
|
||||||
pub(crate) backend: Arc<RwLock<Box<dyn MailBackend>>>,
|
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.
|
permissions.set_mode(0o600); // Read/write for owner only.
|
||||||
f.set_permissions(permissions).unwrap();
|
f.set_permissions(permissions).unwrap();
|
||||||
let writer = io::BufWriter::new(f);
|
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);
|
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)]
|
#[derive(Serialize, Debug, Clone, Default)]
|
||||||
pub struct FolderNode {
|
pub struct FolderNode {
|
||||||
pub hash: FolderHash,
|
pub hash: FolderHash,
|
||||||
|
@ -295,16 +233,12 @@ impl Account {
|
||||||
index,
|
index,
|
||||||
name,
|
name,
|
||||||
is_online: false,
|
is_online: false,
|
||||||
folders: Default::default(),
|
folder_entries: Default::default(),
|
||||||
ref_folders: Default::default(),
|
|
||||||
folder_confs: Default::default(),
|
|
||||||
folders_order: Default::default(),
|
folders_order: Default::default(),
|
||||||
folder_names: Default::default(),
|
|
||||||
tree: Default::default(),
|
tree: Default::default(),
|
||||||
address_book,
|
address_book,
|
||||||
sent_folder: Default::default(),
|
sent_folder: Default::default(),
|
||||||
collection: Default::default(),
|
collection: Default::default(),
|
||||||
workers: Default::default(),
|
|
||||||
work_context,
|
work_context,
|
||||||
runtime_settings: settings.clone(),
|
runtime_settings: settings.clone(),
|
||||||
settings,
|
settings,
|
||||||
|
@ -325,12 +259,9 @@ impl Account {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mut folders: FnvHashMap<FolderHash, MailboxEntry> =
|
let mut folder_entries: FnvHashMap<FolderHash, FolderEntry> =
|
||||||
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
|
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
|
||||||
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
|
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;
|
let mut sent_folder = None;
|
||||||
for f in ref_folders.values_mut() {
|
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 {
|
} else {
|
||||||
let mut new = FileFolderConf::default();
|
let mut new = FileFolderConf::default();
|
||||||
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
new.folder_conf.usage = if f.special_usage() != SpecialUsageMailbox::Normal {
|
||||||
|
@ -368,9 +308,17 @@ impl Account {
|
||||||
tmp
|
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();
|
let mut tree: Vec<FolderNode> = Vec::new();
|
||||||
|
@ -378,36 +326,27 @@ impl Account {
|
||||||
for (h, f) in ref_folders.iter() {
|
for (h, f) in ref_folders.iter() {
|
||||||
if !f.is_subscribed() {
|
if !f.is_subscribed() {
|
||||||
/* Skip unsubscribed folder */
|
/* Skip unsubscribed folder */
|
||||||
folders.insert(*h, MailboxEntry::None);
|
|
||||||
workers.insert(*h, None);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
folders.insert(
|
folder_entries.entry(*h).and_modify(|entry| {
|
||||||
*h,
|
entry.status = MailboxStatus::Parsing(0, 0);
|
||||||
MailboxEntry::Parsing(Mailbox::new(f.clone(), &FnvHashMap::default()), 0, 0),
|
entry.worker = Account::new_worker(
|
||||||
);
|
|
||||||
workers.insert(
|
|
||||||
*h,
|
|
||||||
Account::new_worker(
|
|
||||||
f.clone(),
|
f.clone(),
|
||||||
&mut self.backend,
|
&mut self.backend,
|
||||||
&self.work_context,
|
&self.work_context,
|
||||||
self.notify_fn.clone(),
|
self.notify_fn.clone(),
|
||||||
),
|
);
|
||||||
);
|
});
|
||||||
|
collection.mailboxes.insert(*h, Default::default());
|
||||||
collection.threads.insert(*h, Threads::default());
|
collection.threads.insert(*h, Threads::default());
|
||||||
}
|
}
|
||||||
|
|
||||||
build_folders_order(&mut tree, &ref_folders, &mut folders_order);
|
build_folders_order(&mut tree, &folder_entries, &mut folders_order);
|
||||||
self.folders = folders;
|
|
||||||
self.ref_folders = ref_folders;
|
|
||||||
self.folder_confs = folder_confs;
|
|
||||||
self.folders_order = folders_order;
|
self.folders_order = folders_order;
|
||||||
self.folder_names = folder_names;
|
self.folder_entries = folder_entries;
|
||||||
self.tree = tree;
|
self.tree = tree;
|
||||||
self.sent_folder = sent_folder;
|
self.sent_folder = sent_folder;
|
||||||
self.collection = collection;
|
self.collection = collection;
|
||||||
self.workers = workers;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_worker(
|
fn new_worker(
|
||||||
|
@ -483,7 +422,7 @@ impl Account {
|
||||||
Some(w)
|
Some(w)
|
||||||
}
|
}
|
||||||
pub fn reload(&mut self, event: RefreshEvent, folder_hash: FolderHash) -> Option<UIEvent> {
|
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));
|
self.event_queue.push_back((folder_hash, event));
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -493,7 +432,6 @@ impl Account {
|
||||||
//let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
|
//let mailbox: &mut Mailbox = self.folders[idx].as_mut().unwrap().as_mut().unwrap();
|
||||||
match kind {
|
match kind {
|
||||||
RefreshEventKind::Update(old_hash, envelope) => {
|
RefreshEventKind::Update(old_hash, envelope) => {
|
||||||
mailbox!(&folder_hash, self.folders).rename(old_hash, envelope.hash());
|
|
||||||
#[cfg(feature = "sqlite3")]
|
#[cfg(feature = "sqlite3")]
|
||||||
{
|
{
|
||||||
if let Err(err) = crate::sqlite3::remove(old_hash).and_then(|_| {
|
if let Err(err) = crate::sqlite3::remove(old_hash).and_then(|_| {
|
||||||
|
@ -514,7 +452,6 @@ impl Account {
|
||||||
}
|
}
|
||||||
RefreshEventKind::Rename(old_hash, new_hash) => {
|
RefreshEventKind::Rename(old_hash, new_hash) => {
|
||||||
debug!("rename {} to {}", 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);
|
self.collection.rename(old_hash, new_hash, folder_hash);
|
||||||
#[cfg(feature = "sqlite3")]
|
#[cfg(feature = "sqlite3")]
|
||||||
{
|
{
|
||||||
|
@ -538,13 +475,10 @@ impl Account {
|
||||||
RefreshEventKind::Create(envelope) => {
|
RefreshEventKind::Create(envelope) => {
|
||||||
let env_hash = envelope.hash();
|
let env_hash = envelope.hash();
|
||||||
if self.collection.contains_key(&env_hash)
|
if self.collection.contains_key(&env_hash)
|
||||||
&& mailbox!(&folder_hash, self.folders)
|
&& self.collection[&folder_hash].contains(&env_hash)
|
||||||
.envelopes
|
|
||||||
.contains(&env_hash)
|
|
||||||
{
|
{
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
mailbox!(&folder_hash, self.folders).insert(env_hash);
|
|
||||||
let (is_seen, is_draft) =
|
let (is_seen, is_draft) =
|
||||||
{ (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
|
{ (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
|
||||||
let (subject, from) = {
|
let (subject, from) = {
|
||||||
|
@ -578,10 +512,12 @@ impl Account {
|
||||||
self.collection.insert_reply(env_hash);
|
self.collection.insert_reply(env_hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
let ref_folders: FnvHashMap<FolderHash, Folder> =
|
if self.folder_entries[&folder_hash]
|
||||||
self.backend.read().unwrap().folders().unwrap();
|
.conf
|
||||||
let folder_conf = &self.folder_confs[&folder_hash];
|
.folder_conf
|
||||||
if folder_conf.folder_conf().ignore.is_true() {
|
.ignore
|
||||||
|
.is_true()
|
||||||
|
{
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -606,13 +542,12 @@ impl Account {
|
||||||
"{}\n{} {}",
|
"{}\n{} {}",
|
||||||
subject,
|
subject,
|
||||||
self.name,
|
self.name,
|
||||||
ref_folders[&folder_hash].name(),
|
self.folder_entries[&folder_hash].name()
|
||||||
),
|
),
|
||||||
Some(crate::types::NotificationType::NewMail),
|
Some(crate::types::NotificationType::NewMail),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
RefreshEventKind::Remove(envelope_hash) => {
|
RefreshEventKind::Remove(envelope_hash) => {
|
||||||
mailbox!(&folder_hash, self.folders).remove(envelope_hash);
|
|
||||||
#[cfg(feature = "sqlite3")]
|
#[cfg(feature = "sqlite3")]
|
||||||
{
|
{
|
||||||
let envelopes = self.collection.envelopes.read();
|
let envelopes = self.collection.envelopes.read();
|
||||||
|
@ -633,15 +568,15 @@ impl Account {
|
||||||
return Some(EnvelopeRemove(envelope_hash));
|
return Some(EnvelopeRemove(envelope_hash));
|
||||||
}
|
}
|
||||||
RefreshEventKind::Rescan => {
|
RefreshEventKind::Rescan => {
|
||||||
let ref_folders: FnvHashMap<FolderHash, Folder> =
|
|
||||||
self.backend.read().unwrap().folders().unwrap();
|
|
||||||
let handle = Account::new_worker(
|
let handle = Account::new_worker(
|
||||||
ref_folders[&folder_hash].clone(),
|
self.folder_entries[&folder_hash].ref_folder.clone(),
|
||||||
&mut self.backend,
|
&mut self.backend,
|
||||||
&self.work_context,
|
&self.work_context,
|
||||||
self.notify_fn.clone(),
|
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) => {
|
RefreshEventKind::Failure(e) => {
|
||||||
debug!("RefreshEvent Failure: {}", e.to_string());
|
debug!("RefreshEvent Failure: {}", e.to_string());
|
||||||
|
@ -717,18 +652,14 @@ impl Account {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.folders.len()
|
self.tree.len()
|
||||||
}
|
}
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.folders.is_empty()
|
self.tree.is_empty()
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ref_folders(&self) -> &FnvHashMap<FolderHash, Folder> {
|
|
||||||
&self.ref_folders
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_folders(&self) -> Vec<FolderNode> {
|
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>) {
|
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
|
||||||
ret.push(node.clone());
|
ret.push(node.clone());
|
||||||
for c in node.children.iter() {
|
for c in node.children.iter() {
|
||||||
|
@ -747,14 +678,12 @@ impl Account {
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&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>>) {
|
fn load_mailbox(&mut self, folder_hash: FolderHash, payload: Result<Vec<Envelope>>) {
|
||||||
if payload.is_err() {
|
if payload.is_err() {
|
||||||
self.folders
|
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||||
.insert(folder_hash, MailboxEntry::Failed(payload.unwrap_err()));
|
entry.status = MailboxStatus::Failed(payload.unwrap_err());
|
||||||
|
});
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(folder_hash)))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -765,20 +694,14 @@ impl Account {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| (e.hash(), e))
|
.map(|e| (e.hash(), e))
|
||||||
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
|
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
|
||||||
match self.folders.entry(folder_hash).or_default() {
|
if let Some(updated_folders) =
|
||||||
MailboxEntry::Failed(_) | MailboxEntry::None => {}
|
self.collection
|
||||||
MailboxEntry::Parsing(ref mut m, _, _) | MailboxEntry::Available(ref mut m) => {
|
.merge(envelopes, folder_hash, self.sent_folder)
|
||||||
m.merge(&envelopes);
|
{
|
||||||
if let Some(updated_folders) =
|
for f in updated_folders {
|
||||||
self.collection
|
self.sender
|
||||||
.merge(envelopes, folder_hash, self.sent_folder)
|
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
||||||
{
|
.unwrap();
|
||||||
for f in updated_folders {
|
|
||||||
self.sender
|
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.sender
|
self.sender
|
||||||
|
@ -791,11 +714,17 @@ impl Account {
|
||||||
return Err(0);
|
return Err(0);
|
||||||
}
|
}
|
||||||
loop {
|
loop {
|
||||||
match self.workers.get_mut(&folder_hash).unwrap() {
|
match self
|
||||||
|
.folder_entries
|
||||||
|
.get_mut(&folder_hash)
|
||||||
|
.unwrap()
|
||||||
|
.worker
|
||||||
|
.as_mut()
|
||||||
|
{
|
||||||
None => {
|
None => {
|
||||||
return if self.folders[&folder_hash].is_available()
|
return if self.folder_entries[&folder_hash].status.is_available()
|
||||||
|| (self.folders[&folder_hash].is_parsing()
|
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
||||||
&& self.collection.threads.contains_key(&folder_hash))
|
&& self.collection.mailboxes.contains_key(&folder_hash))
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -812,13 +741,9 @@ impl Account {
|
||||||
}
|
}
|
||||||
Ok(AsyncStatus::Finished) => {
|
Ok(AsyncStatus::Finished) => {
|
||||||
debug!("got finished in status for {}", folder_hash);
|
debug!("got finished in status for {}", folder_hash);
|
||||||
self.folders.entry(folder_hash).and_modify(|f| {
|
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||||
let m = if let MailboxEntry::Parsing(m, _, _) = f {
|
entry.status = MailboxStatus::Available;
|
||||||
std::mem::replace(m, Mailbox::default())
|
entry.worker = None;
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
*f = MailboxEntry::Available(m);
|
|
||||||
});
|
});
|
||||||
self.sender
|
self.sender
|
||||||
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
.send(ThreadEvent::UIEvent(UIEvent::MailboxUpdate((
|
||||||
|
@ -826,13 +751,14 @@ impl Account {
|
||||||
folder_hash,
|
folder_hash,
|
||||||
))))
|
))))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
self.workers.insert(folder_hash, None);
|
|
||||||
}
|
}
|
||||||
Ok(AsyncStatus::ProgressReport(n)) => {
|
Ok(AsyncStatus::ProgressReport(n)) => {
|
||||||
self.folders.entry(folder_hash).and_modify(|f| {
|
self.folder_entries.entry(folder_hash).and_modify(|entry| {
|
||||||
if let MailboxEntry::Parsing(_, ref mut d, _) = f {
|
match entry.status {
|
||||||
*d += n;
|
MailboxStatus::Parsing(ref mut d, _) => {
|
||||||
|
*d += n;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//return Err(n);
|
//return Err(n);
|
||||||
|
@ -843,9 +769,9 @@ impl Account {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if self.folders[&folder_hash].is_available()
|
if self.folder_entries[&folder_hash].status.is_available()
|
||||||
|| (self.folders[&folder_hash].is_parsing()
|
|| (self.folder_entries[&folder_hash].status.is_parsing()
|
||||||
&& self.collection.threads.contains_key(&folder_hash))
|
&& self.collection.mailboxes.contains_key(&folder_hash))
|
||||||
{
|
{
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
|
@ -909,13 +835,6 @@ impl Account {
|
||||||
}
|
}
|
||||||
self.backend.write().unwrap().save(bytes, folder, flags)
|
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 {
|
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
|
||||||
self.collection.contains_key(&h)
|
self.collection.contains_key(&h)
|
||||||
|
@ -966,39 +885,40 @@ impl Account {
|
||||||
tmp
|
tmp
|
||||||
};
|
};
|
||||||
|
|
||||||
self.folder_confs.insert(folder.hash(), new);
|
let folder_hash = folder.hash();
|
||||||
self.folder_names
|
self.folder_entries.insert(
|
||||||
.insert(folder.hash(), folder.path().to_string());
|
folder_hash,
|
||||||
self.folders.insert(
|
FolderEntry {
|
||||||
folder.hash(),
|
name: folder.path().to_string(),
|
||||||
MailboxEntry::Parsing(
|
status: MailboxStatus::Parsing(0, 0),
|
||||||
Mailbox::new(folder.clone(), &FnvHashMap::default()),
|
conf: new,
|
||||||
0,
|
worker: Account::new_worker(
|
||||||
0,
|
folder.clone(),
|
||||||
),
|
&mut self.backend,
|
||||||
);
|
&self.work_context,
|
||||||
self.workers.insert(
|
self.notify_fn.clone(),
|
||||||
folder.hash(),
|
),
|
||||||
Account::new_worker(
|
ref_folder: folder,
|
||||||
folder.clone(),
|
},
|
||||||
&mut self.backend,
|
|
||||||
&self.work_context,
|
|
||||||
self.notify_fn.clone(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
self.collection
|
self.collection
|
||||||
.threads
|
.threads
|
||||||
.insert(folder.hash(), Threads::default());
|
.insert(folder_hash, Threads::default());
|
||||||
self.ref_folders = self.backend.read().unwrap().folders()?;
|
build_folders_order(
|
||||||
build_folders_order(&mut self.tree, &self.ref_folders, &mut self.folders_order);
|
&mut self.tree,
|
||||||
|
&self.folder_entries,
|
||||||
|
&mut self.folders_order,
|
||||||
|
);
|
||||||
Ok(format!("`{}` successfully created.", &path))
|
Ok(format!("`{}` successfully created.", &path))
|
||||||
}
|
}
|
||||||
FolderOperation::Delete(path) => {
|
FolderOperation::Delete(path) => {
|
||||||
if self.ref_folders.len() == 1 {
|
if self.folder_entries.len() == 1 {
|
||||||
return Err(MeliError::new("Cannot delete only mailbox."));
|
return Err(MeliError::new("Cannot delete only mailbox."));
|
||||||
}
|
}
|
||||||
let folder_hash = if let Some((folder_hash, _)) =
|
let folder_hash = if let Some((folder_hash, _)) = self
|
||||||
self.ref_folders.iter().find(|(_, f)| f.path() == path)
|
.folder_entries
|
||||||
|
.iter()
|
||||||
|
.find(|(_, f)| f.ref_folder.path() == path)
|
||||||
{
|
{
|
||||||
*folder_hash
|
*folder_hash
|
||||||
} else {
|
} else {
|
||||||
|
@ -1011,13 +931,9 @@ impl Account {
|
||||||
folder_hash,
|
folder_hash,
|
||||||
))))
|
))))
|
||||||
.unwrap();
|
.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) {
|
if let Some(pos) = self.folders_order.iter().position(|&h| h == folder_hash) {
|
||||||
self.folders_order.remove(pos);
|
self.folders_order.remove(pos);
|
||||||
}
|
}
|
||||||
self.folder_names.remove(&folder_hash);
|
|
||||||
if let Some(pos) = self.tree.iter().position(|n| n.hash == folder_hash) {
|
if let Some(pos) = self.tree.iter().position(|n| n.hash == folder_hash) {
|
||||||
self.tree.remove(pos);
|
self.tree.remove(pos);
|
||||||
}
|
}
|
||||||
|
@ -1025,7 +941,9 @@ impl Account {
|
||||||
self.sent_folder = None;
|
self.sent_folder = None;
|
||||||
}
|
}
|
||||||
self.collection.threads.remove(&folder_hash);
|
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
|
// 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> {
|
pub fn special_use_folder(&self, special_use: SpecialUsageMailbox) -> Option<&str> {
|
||||||
let ret = self
|
let ret = self
|
||||||
.folder_confs
|
.folder_entries
|
||||||
.iter()
|
.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() {
|
if let Some(ret) = ret.as_ref() {
|
||||||
Some(&self.folder_names[&ret.0])
|
Some(ret.1.name())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -1117,7 +1019,7 @@ impl Account {
|
||||||
let mut ret = SmallVec::new();
|
let mut ret = SmallVec::new();
|
||||||
let envelopes = self.collection.envelopes.read().unwrap();
|
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];
|
let envelope = &envelopes[&env_hash];
|
||||||
if envelope.subject().contains(&search_term) {
|
if envelope.subject().contains(&search_term) {
|
||||||
ret.push(env_hash);
|
ret.push(env_hash);
|
||||||
|
@ -1140,32 +1042,32 @@ impl Account {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<FolderHash> for Account {
|
impl Index<&FolderHash> for Account {
|
||||||
type Output = MailboxEntry;
|
type Output = FolderEntry;
|
||||||
fn index(&self, index: FolderHash) -> &MailboxEntry {
|
fn index(&self, index: &FolderHash) -> &FolderEntry {
|
||||||
&self.folders[&index]
|
&self.folder_entries[index]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl IndexMut<FolderHash> for Account {
|
impl IndexMut<&FolderHash> for Account {
|
||||||
fn index_mut(&mut self, index: FolderHash) -> &mut MailboxEntry {
|
fn index_mut(&mut self, index: &FolderHash) -> &mut FolderEntry {
|
||||||
self.folders.get_mut(&index).unwrap()
|
self.folder_entries.get_mut(index).unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_folders_order(
|
fn build_folders_order(
|
||||||
tree: &mut Vec<FolderNode>,
|
tree: &mut Vec<FolderNode>,
|
||||||
ref_folders: &FnvHashMap<FolderHash, Folder>,
|
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
||||||
folders_order: &mut Vec<FolderHash>,
|
folders_order: &mut Vec<FolderHash>,
|
||||||
) {
|
) {
|
||||||
let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new();
|
let mut stack: SmallVec<[FolderHash; 8]> = SmallVec::new();
|
||||||
tree.clear();
|
tree.clear();
|
||||||
folders_order.clear();
|
folders_order.clear();
|
||||||
for (h, f) in ref_folders.iter() {
|
for (h, f) in folder_entries.iter() {
|
||||||
if f.parent().is_none() {
|
if f.ref_folder.parent().is_none() {
|
||||||
fn rec(
|
fn rec(
|
||||||
h: FolderHash,
|
h: FolderHash,
|
||||||
ref_folders: &FnvHashMap<FolderHash, Folder>,
|
folder_entries: &FnvHashMap<FolderHash, FolderEntry>,
|
||||||
depth: usize,
|
depth: usize,
|
||||||
) -> FolderNode {
|
) -> FolderNode {
|
||||||
let mut node = FolderNode {
|
let mut node = FolderNode {
|
||||||
|
@ -1173,18 +1075,18 @@ fn build_folders_order(
|
||||||
children: Vec::new(),
|
children: Vec::new(),
|
||||||
depth,
|
depth,
|
||||||
};
|
};
|
||||||
for &c in ref_folders[&h].children() {
|
for &c in folder_entries[&h].ref_folder.children() {
|
||||||
node.children.push(rec(c, ref_folders, depth + 1));
|
node.children.push(rec(c, folder_entries, depth + 1));
|
||||||
}
|
}
|
||||||
node
|
node
|
||||||
};
|
};
|
||||||
|
|
||||||
tree.push(rec(*h, &ref_folders, 0));
|
tree.push(rec(*h, &folder_entries, 0));
|
||||||
for &c in f.children() {
|
for &c in f.ref_folder.children() {
|
||||||
stack.push(c);
|
stack.push(c);
|
||||||
}
|
}
|
||||||
while let Some(next) = stack.pop() {
|
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);
|
stack.push(*c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1192,14 +1094,23 @@ fn build_folders_order(
|
||||||
}
|
}
|
||||||
|
|
||||||
tree.sort_unstable_by(|a, b| {
|
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
|
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
|
std::cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
ref_folders[&a.hash]
|
folder_entries[&a.hash]
|
||||||
|
.ref_folder
|
||||||
.path()
|
.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() {
|
for n in tree.iter_mut() {
|
||||||
folders_order.push(n.hash);
|
folders_order.push(n.hash);
|
||||||
n.children.sort_unstable_by(|a, b| {
|
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
|
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
|
std::cmp::Ordering::Less
|
||||||
} else {
|
} else {
|
||||||
ref_folders[&a.hash]
|
folder_entries[&a.hash]
|
||||||
|
.ref_folder
|
||||||
.path()
|
.path()
|
||||||
.cmp(&ref_folders[&b.hash].path())
|
.cmp(&folder_entries[&b.hash].ref_folder.path())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
stack.extend(n.children.iter().rev().map(Some));
|
stack.extend(n.children.iter().rev().map(Some));
|
||||||
|
|
|
@ -153,7 +153,7 @@ impl Context {
|
||||||
debug!(
|
debug!(
|
||||||
"hash & folder: {:?} {}",
|
"hash & folder: {:?} {}",
|
||||||
folder_node.hash,
|
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);
|
mailbox_hashes.insert(folder_node.hash, account_pos);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue