ui: move Collection to Account

Each account had one mailbox per folder, which had one associated
collection. Now each Account has one Collection for all folders and each
Mailbox object holds only the hashes of each message.

Collection also gets Threads for each folder in order to mix messages
(ie from/to Sent folder).

Insert Sent emails in chronological order

if inserted unsorted, mails a, b with a happened-before b, might never
  get added.

Fix multiple insertions in ThreadTree upon insert_reply

insert_reply was creating multiple copies in threading
embed
Manos Pitsidianakis 2019-05-26 02:34:03 +03:00
parent eff1c1641c
commit 42654410e3
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
14 changed files with 915 additions and 626 deletions

View File

@ -41,26 +41,30 @@ pub use self::collection::*;
use std::option::Option; use std::option::Option;
use fnv::{FnvHashMap, FnvHashSet};
/// `Mailbox` represents a folder of mail. /// `Mailbox` represents a folder of mail.
#[derive(Debug, Deserialize, Serialize, Clone, Default)] #[derive(Debug, Deserialize, Serialize, Clone, Default)]
pub struct Mailbox { pub struct Mailbox {
#[serde(skip_serializing, skip_deserializing)] #[serde(skip_serializing, skip_deserializing)]
pub folder: Folder, pub folder: Folder,
name: String, name: String,
pub collection: Collection, pub envelopes: FnvHashSet<EnvelopeHash>,
pub thread_root_set: FnvHashSet<ThreadHash>,
has_sent: bool, has_sent: bool,
} }
impl Mailbox { impl Mailbox {
pub fn new(folder: Folder, envelopes: Result<Vec<Envelope>>) -> Result<Mailbox> { pub fn new(
let mut envelopes: Vec<Envelope> = envelopes?; folder: Folder,
envelopes.sort_by(|a, b| a.date().cmp(&b.date())); envelopes: Result<&FnvHashMap<EnvelopeHash, Envelope>>,
let collection = Collection::new(envelopes, &folder); ) -> Result<Mailbox> {
let envelopes = envelopes?;
let name = folder.name().into(); let name = folder.name().into();
let envelopes = envelopes.keys().cloned().collect();
Ok(Mailbox { Ok(Mailbox {
folder, folder,
collection,
name, name,
envelopes,
..Default::default() ..Default::default()
}) })
} }
@ -70,65 +74,20 @@ impl Mailbox {
} }
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.collection.is_empty() self.envelopes.is_empty()
} }
pub fn len(&self) -> usize { pub fn len(&self) -> usize {
self.collection.len() self.envelopes.len()
} }
pub fn thread_to_mail_mut(&mut self, h: ThreadHash) -> &mut Envelope { pub fn insert(&mut self, h: EnvelopeHash) {
self.collection self.envelopes.insert(h);
.envelopes
.entry(self.collection.threads.thread_to_mail(h))
.or_default()
} }
pub fn thread_to_mail(&self, h: ThreadHash) -> &Envelope {
&self.collection.envelopes[&self.collection.threads.thread_to_mail(h)]
}
pub fn threaded_mail(&self, h: ThreadHash) -> EnvelopeHash {
self.collection.threads.thread_to_mail(h)
}
pub fn mail_and_thread(&mut self, i: EnvelopeHash) -> (&mut Envelope, &ThreadNode) {
let thread;
{
let x = &mut self.collection.envelopes.entry(i).or_default();
thread = &self.collection.threads[&x.thread()];
}
(self.collection.envelopes.entry(i).or_default(), thread)
}
pub fn thread(&self, h: ThreadHash) -> &ThreadNode {
&self.collection.threads.thread_nodes()[&h]
}
pub fn insert_sent_folder(&mut self, _sent: &Mailbox) {
/*if !self.has_sent {
for envelope in sent.collection.envelopes.values() {
self.insert_reply(envelope);
}
self.has_sent = true;
}*/
}
pub fn rename(&mut self, old_hash: EnvelopeHash, new_hash: EnvelopeHash) { pub fn rename(&mut self, old_hash: EnvelopeHash, new_hash: EnvelopeHash) {
self.collection.rename(old_hash, new_hash); self.envelopes.remove(&old_hash);
}
pub fn update(&mut self, old_hash: EnvelopeHash, envelope: Envelope) { self.envelopes.insert(new_hash);
self.collection.update_envelope(old_hash, envelope);
} }
pub fn remove(&mut self, h: EnvelopeHash) {
pub fn insert(&mut self, envelope: Envelope) -> &Envelope { self.envelopes.remove(&h);
let hash = envelope.hash();
self.collection.insert(envelope);
&self.collection[&hash]
}
pub fn insert_reply(&mut self, envelope: &Envelope) {
debug!("mailbox insert reply {}", self.name);
self.collection.insert_reply(envelope);
}
pub fn remove(&mut self, envelope_hash: EnvelopeHash) {
self.collection.remove(envelope_hash);
// debug!("envelope_hash: {}\ncollection:\n{:?}", envelope_hash, self.collection);
} }
} }

View File

@ -1,4 +1,5 @@
use super::*; use super::*;
use crate::mailbox::backends::FolderHash;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::fs; use std::fs;
use std::io; use std::io;
@ -6,22 +7,20 @@ use std::ops::{Deref, DerefMut};
use fnv::FnvHashMap; use fnv::FnvHashMap;
/// `Mailbox` represents a folder of mail.
#[derive(Debug, Clone, Deserialize, Default, Serialize)] #[derive(Debug, Clone, Deserialize, Default, Serialize)]
pub struct Collection { pub struct Collection {
#[serde(skip_serializing, skip_deserializing)]
folder: Folder,
pub envelopes: FnvHashMap<EnvelopeHash, Envelope>, pub envelopes: FnvHashMap<EnvelopeHash, Envelope>,
message_ids: FnvHashMap<Vec<u8>, EnvelopeHash>,
date_index: BTreeMap<UnixTimestamp, EnvelopeHash>, date_index: BTreeMap<UnixTimestamp, EnvelopeHash>,
subject_index: Option<BTreeMap<String, EnvelopeHash>>, subject_index: Option<BTreeMap<String, EnvelopeHash>>,
pub threads: Threads, pub threads: FnvHashMap<FolderHash, Threads>,
sent_folder: Option<FolderHash>,
} }
impl Drop for Collection { impl Drop for Collection {
fn drop(&mut self) { fn drop(&mut self) {
let cache_dir = let cache_dir: xdg::BaseDirectories =
xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", self.folder.hash())) xdg::BaseDirectories::with_profile("meli", "threads".to_string()).unwrap();
.unwrap();
if let Ok(cached) = cache_dir.place_cache_file("threads") { if let Ok(cached) = cache_dir.place_cache_file("threads") {
/* place result in cache directory */ /* place result in cache directory */
let f = match fs::File::create(cached) { let f = match fs::File::create(cached) {
@ -37,47 +36,24 @@ impl Drop for Collection {
} }
impl Collection { impl Collection {
pub fn new(vec: Vec<Envelope>, folder: &Folder) -> Collection { pub fn new(envelopes: FnvHashMap<EnvelopeHash, Envelope>) -> Collection {
let mut envelopes: FnvHashMap<EnvelopeHash, Envelope> =
FnvHashMap::with_capacity_and_hasher(vec.len(), Default::default());
for e in vec {
envelopes.insert(e.hash(), e);
}
let date_index = BTreeMap::new(); let date_index = BTreeMap::new();
let subject_index = None; let subject_index = None;
let message_ids = FnvHashMap::with_capacity_and_hasher(2048, Default::default());
/* Scrap caching for now. When a cached threads file is loaded, we must remove/rehash the /* Scrap caching for now. When a cached threads file is loaded, we must remove/rehash the
* thread nodes that shouldn't exist anymore (e.g. because their file moved from /new to * thread nodes that shouldn't exist anymore (e.g. because their file moved from /new to
* /cur, or it was deleted). * /cur, or it was deleted).
*/ */
let threads = Threads::new(&mut envelopes); let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
/*let cache_dir =
xdg::BaseDirectories::with_profile("meli", format!("{}_Thread", folder.hash()))
.unwrap();
if let Some(cached) = cache_dir.find_cache_file("threads") {
let reader = io::BufReader::new(fs::File::open(cached).unwrap());
let result: result::Result<Threads, _> = bincode::deserialize_from(reader);
let ret = if let Ok(mut cached_t) = result {
use std::iter::FromIterator;
debug!("loaded cache, our hash set is {:?}\n and the cached one is {:?}", FnvHashSet::from_iter(envelopes.keys().cloned()), cached_t.hash_set);
cached_t.amend(&mut envelopes);
cached_t
} else {
Threads::new(&mut envelopes)
};
ret
} else {
Threads::new(&mut envelopes)
};
*/
Collection { Collection {
folder: folder.clone(),
envelopes, envelopes,
date_index, date_index,
message_ids,
subject_index, subject_index,
threads, threads,
sent_folder: None,
} }
} }
@ -89,22 +65,34 @@ impl Collection {
self.envelopes.is_empty() self.envelopes.is_empty()
} }
pub fn remove(&mut self, envelope_hash: EnvelopeHash) { pub fn remove(&mut self, envelope_hash: EnvelopeHash, folder_hash: FolderHash) {
debug!("DEBUG: Removing {}", envelope_hash); debug!("DEBUG: Removing {}", envelope_hash);
self.envelopes.remove(&envelope_hash); self.envelopes.remove(&envelope_hash);
self.threads.remove(envelope_hash, &mut self.envelopes); self.threads
.entry(folder_hash)
.or_default()
.remove(envelope_hash, &mut self.envelopes);
} }
pub fn rename(&mut self, old_hash: EnvelopeHash, new_hash: EnvelopeHash) { pub fn rename(
&mut self,
old_hash: EnvelopeHash,
new_hash: EnvelopeHash,
folder_hash: FolderHash,
) {
if !self.envelopes.contains_key(&old_hash) { if !self.envelopes.contains_key(&old_hash) {
return; return;
} }
let mut env = self.envelopes.remove(&old_hash).unwrap(); let mut env = self.envelopes.remove(&old_hash).unwrap();
env.set_hash(new_hash); env.set_hash(new_hash);
self.message_ids
.insert(env.message_id().raw().to_vec(), new_hash);
self.envelopes.insert(new_hash, env); self.envelopes.insert(new_hash, env);
{ {
if self if self
.threads .threads
.entry(folder_hash)
.or_default()
.update_envelope(old_hash, new_hash, &self.envelopes) .update_envelope(old_hash, new_hash, &self.envelopes)
.is_ok() .is_ok()
{ {
@ -114,17 +102,102 @@ impl Collection {
/* envelope is not in threads, so insert it */ /* envelope is not in threads, so insert it */
let env = self.envelopes.entry(new_hash).or_default() as *mut Envelope; let env = self.envelopes.entry(new_hash).or_default() as *mut Envelope;
unsafe { unsafe {
self.threads.insert(&mut (*env), &self.envelopes); self.threads
.entry(folder_hash)
.or_default()
.insert(&mut (*env), &self.envelopes);
} }
} }
pub fn update_envelope(&mut self, old_hash: EnvelopeHash, envelope: Envelope) { pub fn merge(
&mut self,
mut envelopes: FnvHashMap<EnvelopeHash, Envelope>,
folder_hash: FolderHash,
mailbox: &mut Result<Mailbox>,
sent_folder: Option<FolderHash>,
) {
self.sent_folder = sent_folder;
envelopes.retain(|&h, e| {
if self.message_ids.contains_key(e.message_id().raw()) {
/* skip duplicates until a better way to handle them is found. */
//FIXME
if let Ok(mailbox) = mailbox.as_mut() {
mailbox.remove(h);
}
false
} else {
self.message_ids.insert(e.message_id().raw().to_vec(), h);
true
}
});
let mut threads = Threads::new(&mut envelopes);
for (h, e) in envelopes {
self.envelopes.insert(h, e);
}
for (t_fh, t) in self.threads.iter_mut() {
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
let mut ordered_hash_set = threads
.hash_set
.iter()
.cloned()
.collect::<Vec<EnvelopeHash>>();
unsafe {
/* FIXME NLL
* Sorting ordered_hash_set triggers a borrow which should not happen with NLL
* probably */
let envelopes = &self.envelopes as *const FnvHashMap<EnvelopeHash, Envelope>;
ordered_hash_set.sort_by(|a, b| {
(*envelopes)[a]
.date()
.partial_cmp(&(*(envelopes))[b].date())
.unwrap()
});
}
for h in ordered_hash_set {
t.insert_reply(&mut self.envelopes, h);
}
continue;
}
if self.sent_folder.map(|f| f == *t_fh).unwrap_or(false) {
let mut ordered_hash_set =
t.hash_set.iter().cloned().collect::<Vec<EnvelopeHash>>();
unsafe {
/* FIXME NLL
* Sorting ordered_hash_set triggers a borrow which should not happen with NLL
* probably */
let envelopes = &self.envelopes as *const FnvHashMap<EnvelopeHash, Envelope>;
ordered_hash_set.sort_by(|a, b| {
(*envelopes)[a]
.date()
.partial_cmp(&(*(envelopes))[b].date())
.unwrap()
});
}
for h in ordered_hash_set {
threads.insert_reply(&mut self.envelopes, h);
}
}
}
self.threads.insert(folder_hash, threads);
}
pub fn update(&mut self, old_hash: EnvelopeHash, envelope: Envelope, folder_hash: FolderHash) {
self.envelopes.remove(&old_hash); self.envelopes.remove(&old_hash);
let new_hash = envelope.hash(); let new_hash = envelope.hash();
self.message_ids
.insert(envelope.message_id().raw().to_vec(), new_hash);
self.envelopes.insert(new_hash, envelope); self.envelopes.insert(new_hash, envelope);
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
for (_, t) in self.threads.iter_mut() {
t.update_envelope(old_hash, new_hash, &self.envelopes);
}
}
{ {
if self if self
.threads .threads
.entry(folder_hash)
.or_default()
.update_envelope(old_hash, new_hash, &self.envelopes) .update_envelope(old_hash, new_hash, &self.envelopes)
.is_ok() .is_ok()
{ {
@ -134,26 +207,28 @@ impl Collection {
/* envelope is not in threads, so insert it */ /* envelope is not in threads, so insert it */
let env = self.envelopes.entry(new_hash).or_default() as *mut Envelope; let env = self.envelopes.entry(new_hash).or_default() as *mut Envelope;
unsafe { unsafe {
self.threads.insert(&mut (*env), &self.envelopes); self.threads
.entry(folder_hash)
.or_default()
.insert(&mut (*env), &self.envelopes);
} }
} }
pub fn insert(&mut self, envelope: Envelope) { pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) -> &Envelope {
let hash = envelope.hash(); let hash = envelope.hash();
debug!("DEBUG: Inserting hash {} in {}", hash, self.folder.name()); self.message_ids
.insert(envelope.message_id().raw().to_vec(), hash);
self.envelopes.insert(hash, envelope); self.envelopes.insert(hash, envelope);
let env = self.envelopes.entry(hash).or_default() as *mut Envelope; self.threads
unsafe { .entry(folder_hash)
self.threads.insert(&mut (*env), &self.envelopes); .or_default()
} .insert_reply(&mut self.envelopes, hash);
&self.envelopes[&hash]
} }
pub(crate) fn insert_reply(&mut self, _envelope: &Envelope) { pub fn insert_reply(&mut self, env_hash: EnvelopeHash) {
return; for (_, t) in self.threads.iter_mut() {
/* t.insert_reply(&mut self.envelopes, env_hash);
//self.insert(envelope); }
debug!("insert_reply in collections");
self.threads.insert_reply(envelope, &mut self.envelopes);
*/
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -163,12 +163,11 @@ impl AccountsPanel {
write_string_to_grid( write_string_to_grid(
&format!( &format!(
"total {}", "total {}",
a.iter_mailboxes().fold(0, |mut acc, m| { a.collection
acc += m .envelopes
.map(|m| m.collection.values().filter(|e| !e.is_seen()).count()) .values()
.unwrap_or(0); .filter(|e| !e.is_seen())
acc .count()
})
), ),
&mut self.content, &mut self.content,
Color::Default, Color::Default,

View File

@ -127,21 +127,14 @@ impl Composer {
* msg: index of message we reply to in thread_nodes * msg: index of message we reply to in thread_nodes
* context: current context * context: current context
*/ */
pub fn edit(coordinates: (usize, usize, usize), msg: ThreadHash, context: &Context) -> Self { pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Self {
let mailbox = &context.accounts[coordinates.0][coordinates.1]
.as_ref()
.unwrap();
let threads = &mailbox.collection.threads;
let thread_nodes = &threads.thread_nodes();
let mut ret = Composer::default(); let mut ret = Composer::default();
let message = &mailbox.collection[&thread_nodes[&msg].message().unwrap()]; let op = context.accounts[account_pos].operation(&h);
let op = context.accounts[coordinates.0] let envelope: &Envelope = context.accounts[account_pos].get_env(&h);
.backend
.operation(message.hash(), mailbox.folder.hash());
ret.draft = Draft::edit(message, op); ret.draft = Draft::edit(envelope, op);
ret.account_cursor = coordinates.0; ret.account_cursor = account_pos;
ret ret
} }
pub fn with_context( pub fn with_context(
@ -149,17 +142,14 @@ impl Composer {
msg: ThreadHash, msg: ThreadHash,
context: &Context, context: &Context,
) -> Self { ) -> Self {
let mailbox = &context.accounts[coordinates.0][coordinates.1] let account = &context.accounts[coordinates.0];
.as_ref() let mailbox = &account[coordinates.1].as_ref().unwrap();
.unwrap(); let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &mailbox.collection.threads;
let thread_nodes = &threads.thread_nodes(); let thread_nodes = &threads.thread_nodes();
let mut ret = Composer::default(); let mut ret = Composer::default();
let p = &thread_nodes[&msg]; let p = &thread_nodes[&msg];
let parent_message = &mailbox.collection[&p.message().unwrap()]; let parent_message = &account.collection[&p.message().unwrap()];
let mut op = context.accounts[coordinates.0] let mut op = account.operation(&parent_message.hash());
.backend
.operation(parent_message.hash(), mailbox.folder.hash());
let parent_bytes = op.as_bytes(); let parent_bytes = op.as_bytes();
ret.draft = Draft::new_reply(parent_message, parent_bytes.unwrap()); ret.draft = Draft::new_reply(parent_message, parent_bytes.unwrap());
@ -168,10 +158,10 @@ impl Composer {
if p.show_subject() { if p.show_subject() {
format!( format!(
"Re: {}", "Re: {}",
mailbox.collection[&p.message().unwrap()].subject().clone() account.get_env(&p.message().unwrap()).subject().clone()
) )
} else { } else {
mailbox.collection[&p.message().unwrap()].subject().into() account.get_env(&p.message().unwrap()).subject().into()
}, },
); );

View File

@ -500,11 +500,13 @@ impl Listing {
) { ) {
match context.accounts[index].status(entries[&folder_idx].hash()) { match context.accounts[index].status(entries[&folder_idx].hash()) {
Ok(_) => { Ok(_) => {
let count = context.accounts[index][entries[&folder_idx].hash()] let account = &context.accounts[index];
let count = account[entries[&folder_idx].hash()]
.as_ref() .as_ref()
.unwrap() .unwrap()
.collection .envelopes
.values() .iter()
.map(|h| &account.collection[&h])
.filter(|e| !e.is_seen()) .filter(|e| !e.is_seen())
.count(); .count();
let len = s.len(); let len = s.len();

View File

@ -189,15 +189,15 @@ impl MailboxView {
} }
if old_cursor_pos == self.new_cursor_pos { if old_cursor_pos == self.new_cursor_pos {
self.view.update(context); self.view.update(context);
} else { } else if self.unfocused {
self.view = ThreadView::new(self.new_cursor_pos, None, context); self.view = ThreadView::new(self.new_cursor_pos, None, context);
} }
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] let account = &context.accounts[self.cursor_pos.0];
.as_ref() let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
.unwrap();
self.length = mailbox.collection.threads.root_len(); let threads = &account.collection.threads[&mailbox.folder.hash()];
self.length = threads.root_len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' ')); self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
self.order.clear(); self.order.clear();
if self.length == 0 { if self.length == 0 {
@ -211,11 +211,10 @@ impl MailboxView {
); );
return; return;
} }
let threads = &mailbox.collection.threads;
let mut rows = Vec::with_capacity(1024); let mut rows = Vec::with_capacity(1024);
let mut min_width = (0, 0, 0); let mut min_width = (0, 0, 0);
threads.sort_by(self.sort, self.subsort, &mailbox.collection); threads.sort_by(self.sort, self.subsort, &account.collection);
for (idx, root_idx) in threads.root_iter().enumerate() { for (idx, root_idx) in threads.root_iter().enumerate() {
let thread_node = &threads.thread_nodes()[&root_idx]; let thread_node = &threads.thread_nodes()[&root_idx];
let i = if let Some(i) = thread_node.message() { let i = if let Some(i) = thread_node.message() {
@ -227,7 +226,7 @@ impl MailboxView {
} }
threads.thread_nodes()[&iter_ptr].message().unwrap() threads.thread_nodes()[&iter_ptr].message().unwrap()
}; };
if !mailbox.collection.contains_key(&i) { if !context.accounts[self.cursor_pos.0].contains_key(&i) {
debug!("key = {}", i); debug!("key = {}", i);
debug!( debug!(
"name = {} {}", "name = {} {}",
@ -238,7 +237,7 @@ impl MailboxView {
panic!(); panic!();
} }
let root_envelope: &Envelope = &mailbox.collection[&i]; let root_envelope: &Envelope = &context.accounts[self.cursor_pos.0].get_env(&i);
let strings = MailboxView::make_entry_string( let strings = MailboxView::make_entry_string(
root_envelope, root_envelope,
@ -277,14 +276,14 @@ impl MailboxView {
} }
threads.thread_nodes()[&iter_ptr].message().unwrap() threads.thread_nodes()[&iter_ptr].message().unwrap()
}; };
if !mailbox.collection.contains_key(&i) { if !context.accounts[self.cursor_pos.0].contains_key(&i) {
debug!("key = {}", i); //debug!("key = {}", i);
debug!( //debug!(
"name = {} {}", // "name = {} {}",
mailbox.name(), // mailbox.name(),
context.accounts[self.cursor_pos.0].name() // context.accounts[self.cursor_pos.0].name()
); //);
debug!("{:#?}", context.accounts); //debug!("{:#?}", context.accounts);
panic!(); panic!();
} }
@ -365,13 +364,12 @@ impl MailboxView {
context: &Context, context: &Context,
) { ) {
if idx == self.cursor_pos.2 || grid.is_none() { if idx == self.cursor_pos.2 || grid.is_none() {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] if self.length == 0 {
.as_ref()
.unwrap();
if mailbox.is_empty() {
return; return;
} }
let threads = &mailbox.collection.threads; let account = &context.accounts[self.cursor_pos.0];
let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
let threads = &account.collection.threads[&mailbox.folder.hash()];
let thread_node = threads.root_set(idx); let thread_node = threads.root_set(idx);
let thread_node = &threads.thread_nodes()[&thread_node]; let thread_node = &threads.thread_nodes()[&thread_node];
let i = if let Some(i) = thread_node.message() { let i = if let Some(i) = thread_node.message() {
@ -384,7 +382,7 @@ impl MailboxView {
threads.thread_nodes()[&iter_ptr].message().unwrap() threads.thread_nodes()[&iter_ptr].message().unwrap()
}; };
let root_envelope: &Envelope = &mailbox.collection[&i]; let root_envelope: &Envelope = &account.get_env(&i);
let fg_color = if !root_envelope.is_seen() { let fg_color = if !root_envelope.is_seen() {
Color::Byte(0) Color::Byte(0)
} else { } else {
@ -671,11 +669,12 @@ impl Component for MailboxView {
Action::ToggleThreadSnooze => { Action::ToggleThreadSnooze => {
{ {
//FIXME NLL //FIXME NLL
let account = &mut context.accounts[self.cursor_pos.0];
let mailbox = &mut context.accounts[self.cursor_pos.0][self.cursor_pos.1] let folder_hash = account[self.cursor_pos.1]
.as_mut() .as_ref()
.map(|m| m.folder.hash())
.unwrap(); .unwrap();
let threads = &mut mailbox.collection.threads; let threads = account.collection.threads.entry(folder_hash).or_default();
let thread_group = threads.thread_nodes() let thread_group = threads.thread_nodes()
[&threads.root_set(self.cursor_pos.2)] [&threads.root_set(self.cursor_pos.2)]
.thread_group(); .thread_group();

View File

@ -131,9 +131,8 @@ impl PlainListing {
return; return;
} }
} }
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] let account = &context.accounts[self.cursor_pos.0];
.as_ref() let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
.unwrap();
self.length = mailbox.len(); self.length = mailbox.len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' ')); self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
@ -158,31 +157,31 @@ impl PlainListing {
break; break;
} }
/* Write an entire line for each envelope entry. */ /* Write an entire line for each envelope entry. */
self.local_collection = mailbox.collection.keys().cloned().collect(); self.local_collection = account.collection.keys().cloned().collect();
let sort = self.sort; let sort = self.sort;
self.local_collection.sort_by(|a, b| match sort { self.local_collection.sort_by(|a, b| match sort {
(SortField::Date, SortOrder::Desc) => { (SortField::Date, SortOrder::Desc) => {
let ma = &mailbox.collection[a]; let ma = &account.get_env(a);
let mb = &mailbox.collection[b]; let mb = &account.get_env(b);
mb.date().cmp(&ma.date()) mb.date().cmp(&ma.date())
} }
(SortField::Date, SortOrder::Asc) => { (SortField::Date, SortOrder::Asc) => {
let ma = &mailbox.collection[a]; let ma = &account.get_env(a);
let mb = &mailbox.collection[b]; let mb = &account.get_env(b);
ma.date().cmp(&mb.date()) ma.date().cmp(&mb.date())
} }
(SortField::Subject, SortOrder::Desc) => { (SortField::Subject, SortOrder::Desc) => {
let ma = &mailbox.collection[a]; let ma = &account.get_env(a);
let mb = &mailbox.collection[b]; let mb = &account.get_env(b);
ma.subject().cmp(&mb.subject()) ma.subject().cmp(&mb.subject())
} }
(SortField::Subject, SortOrder::Asc) => { (SortField::Subject, SortOrder::Asc) => {
let ma = &mailbox.collection[a]; let ma = &account.get_env(a);
let mb = &mailbox.collection[b]; let mb = &account.get_env(b);
mb.subject().cmp(&ma.subject()) mb.subject().cmp(&ma.subject())
} }
}); });
let envelope: &Envelope = &mailbox.collection[&self.local_collection[idx]]; let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
let fg_color = if !envelope.is_seen() { let fg_color = if !envelope.is_seen() {
Color::Byte(0) Color::Byte(0)
@ -230,10 +229,8 @@ impl PlainListing {
} }
fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) { fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] let account = &context.accounts[self.cursor_pos.0];
.as_ref() let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
.unwrap();
let envelope: &Envelope = &mailbox.collection[&self.local_collection[idx]];
let fg_color = if !envelope.is_seen() { let fg_color = if !envelope.is_seen() {
Color::Byte(0) Color::Byte(0)
@ -367,11 +364,8 @@ impl Component for PlainListing {
false false
} else { } else {
let account = &mut context.accounts[self.cursor_pos.0]; let account = &mut context.accounts[self.cursor_pos.0];
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); let envelope: &mut Envelope =
let envelope: &mut Envelope = &mut mailbox &mut account.get_env_mut(&self.local_collection[idx]);
.collection
.entry(self.local_collection[idx])
.or_default();
!envelope.is_seen() !envelope.is_seen()
} }
}; };

View File

@ -20,7 +20,6 @@
*/ */
use super::*; use super::*;
use std::dbg;
const MAX_COLS: usize = 500; const MAX_COLS: usize = 500;
@ -129,11 +128,10 @@ impl ThreadListing {
return; return;
} }
} }
let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1] let account = &context.accounts[self.cursor_pos.0];
.as_ref() let mailbox = account[self.cursor_pos.1].as_ref().unwrap();
.unwrap();
self.length = mailbox.collection.threads.len(); self.length = account.collection.threads.len();
self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' ')); self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
self.locations.clear(); self.locations.clear();
if self.length == 0 { if self.length == 0 {
@ -151,8 +149,8 @@ impl ThreadListing {
let mut indentations: Vec<bool> = Vec::with_capacity(6); let mut indentations: Vec<bool> = Vec::with_capacity(6);
let mut thread_idx = 0; // needed for alternate thread colors let mut thread_idx = 0; // needed for alternate thread colors
/* Draw threaded view. */ /* Draw threaded view. */
let threads = &mailbox.collection.threads; let threads = &account.collection.threads[&mailbox.folder.hash()];
threads.sort_by(self.sort, self.subsort, &mailbox.collection); threads.sort_by(self.sort, self.subsort, &account.collection);
let thread_nodes: &FnvHashMap<ThreadHash, ThreadNode> = &threads.thread_nodes(); let thread_nodes: &FnvHashMap<ThreadHash, ThreadNode> = &threads.thread_nodes();
let mut iter = threads.threads_iter().peekable(); let mut iter = threads.threads_iter().peekable();
/* This is just a desugared for loop so that we can use .peek() */ /* This is just a desugared for loop so that we can use .peek() */
@ -164,7 +162,7 @@ impl ThreadListing {
thread_idx += 1; thread_idx += 1;
} }
if thread_node.has_message() { if thread_node.has_message() {
let envelope: &Envelope = &mailbox.collection[&thread_node.message().unwrap()]; let envelope: &Envelope = &account.get_env(&thread_node.message().unwrap());
self.locations.push(envelope.hash()); self.locations.push(envelope.hash());
let fg_color = if !envelope.is_seen() { let fg_color = if !envelope.is_seen() {
Color::Byte(0) Color::Byte(0)
@ -230,7 +228,8 @@ impl ThreadListing {
return; return;
} }
if self.locations[idx] != 0 { if self.locations[idx] != 0 {
let envelope: &Envelope = &mailbox.collection[&self.locations[idx]]; let envelope: &Envelope =
&context.accounts[self.cursor_pos.0].get_env(&self.locations[idx]);
let fg_color = if !envelope.is_seen() { let fg_color = if !envelope.is_seen() {
Color::Byte(0) Color::Byte(0)
@ -262,7 +261,8 @@ impl ThreadListing {
} }
if self.locations[idx] != 0 { if self.locations[idx] != 0 {
let envelope: &Envelope = &mailbox.collection[&self.locations[idx]]; let envelope: &Envelope =
&context.accounts[self.cursor_pos.0].get_env(&self.locations[idx]);
let fg_color = if !envelope.is_seen() { let fg_color = if !envelope.is_seen() {
Color::Byte(0) Color::Byte(0)
@ -469,26 +469,14 @@ impl Component for ThreadListing {
} else { } else {
let account = &mut context.accounts[self.cursor_pos.0]; let account = &mut context.accounts[self.cursor_pos.0];
let (hash, is_seen) = { let (hash, is_seen) = {
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
debug!("key is {}", self.locations[dbg!(self.cursor_pos).2]);
let envelope: &Envelope = let envelope: &Envelope =
&mailbox.collection[&self.locations[self.cursor_pos.2]]; &account.get_env(&self.locations[self.cursor_pos.2]);
(envelope.hash(), envelope.is_seen()) (envelope.hash(), envelope.is_seen())
}; };
if !is_seen { if !is_seen {
let folder_hash = { let op = account.operation(&hash);
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap(); let envelope: &mut Envelope =
mailbox.folder.hash() account.get_env_mut(&self.locations[self.cursor_pos.2]);
};
let op = {
let backend = &account.backend;
backend.operation(hash, folder_hash)
};
let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
let envelope: &mut Envelope = mailbox
.collection
.get_mut(&self.locations[self.cursor_pos.2])
.unwrap();
envelope.set_seen(op).unwrap(); envelope.set_seen(op).unwrap();
true true
} else { } else {

View File

@ -89,8 +89,7 @@ impl MailView {
) -> Self { ) -> Self {
let account = &mut context.accounts[coordinates.0]; let account = &mut context.accounts[coordinates.0];
let (hash, is_seen) = { let (hash, is_seen) = {
let mailbox = &mut account[coordinates.1].as_mut().unwrap(); let envelope: &Envelope = &account.get_env(&coordinates.2);
let envelope: &mut Envelope = &mut mailbox.collection.entry(coordinates.2).or_default();
(envelope.hash(), envelope.is_seen()) (envelope.hash(), envelope.is_seen())
}; };
if !is_seen { if !is_seen {
@ -102,8 +101,7 @@ impl MailView {
let backend = &account.backend; let backend = &account.backend;
backend.operation(hash, folder_hash) backend.operation(hash, folder_hash)
}; };
let mailbox = &mut account[coordinates.1].as_mut().unwrap(); let envelope: &mut Envelope = &mut account.get_env_mut(&coordinates.2);
let envelope: &mut Envelope = &mut mailbox.collection.entry(coordinates.2).or_default();
envelope.set_seen(op).unwrap(); envelope.set_seen(op).unwrap();
} }
MailView { MailView {
@ -292,16 +290,13 @@ impl Component for MailView {
let bottom_right = bottom_right!(area); let bottom_right = bottom_right!(area);
let y: usize = { let y: usize = {
let accounts = &mut context.accounts; let account = &mut context.accounts[self.coordinates.0];
let mailbox = &mut accounts[self.coordinates.0][self.coordinates.1] if !account.contains_key(&self.coordinates.2) {
.as_ref()
.unwrap();
if !mailbox.collection.contains_key(&self.coordinates.2) {
/* The envelope has been renamed or removed, so wait for the appropriate event to /* The envelope has been renamed or removed, so wait for the appropriate event to
* arrive */ * arrive */
return; return;
} }
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2]; let envelope: &Envelope = &account.get_env(&self.coordinates.2);
if self.mode == ViewMode::Raw { if self.mode == ViewMode::Raw {
clear_area(grid, area); clear_area(grid, area);
@ -383,14 +378,9 @@ impl Component for MailView {
if self.dirty { if self.dirty {
let body = { let body = {
let mailbox_idx = self.coordinates; // coordinates are mailbox idxs let account = &mut context.accounts[self.coordinates.0];
let mailbox = &context.accounts[mailbox_idx.0][mailbox_idx.1] let envelope: &Envelope = &account.get_env(&self.coordinates.2);
.as_ref() let op = account.operation(&envelope.hash());
.unwrap();
let envelope: &Envelope = &mailbox.collection[&mailbox_idx.2];
let op = context.accounts[mailbox_idx.0]
.backend
.operation(envelope.hash(), mailbox.folder.hash());
envelope.body(op) envelope.body(op)
}; };
match self.mode { match self.mode {
@ -413,14 +403,9 @@ impl Component for MailView {
ViewMode::Subview | ViewMode::ContactSelector(_) => {} ViewMode::Subview | ViewMode::ContactSelector(_) => {}
ViewMode::Raw => { ViewMode::Raw => {
let text = { let text = {
let mailbox_idx = self.coordinates; // coordinates are mailbox idxs let account = &mut context.accounts[self.coordinates.0];
let mailbox = &context.accounts[mailbox_idx.0][mailbox_idx.1] let envelope: &Envelope = &account.get_env(&self.coordinates.2);
.as_ref() let mut op = account.operation(&envelope.hash());
.unwrap();
let envelope: &Envelope = &mailbox.collection[&mailbox_idx.2];
let mut op = context.accounts[mailbox_idx.0]
.backend
.operation(envelope.hash(), mailbox.folder.hash());
op.as_bytes() op.as_bytes()
.map(|v| String::from_utf8_lossy(v).into_owned()) .map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_else(|e| e.to_string()) .unwrap_or_else(|e| e.to_string())
@ -507,8 +492,7 @@ impl Component for MailView {
let account = &mut context.accounts[self.coordinates.0]; let account = &mut context.accounts[self.coordinates.0];
let mut results = Vec::new(); let mut results = Vec::new();
{ {
let mailbox = &account[self.coordinates.1].as_ref().unwrap(); let envelope: &Envelope = &account.get_env(&self.coordinates.2);
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
for c in s.collect() { for c in s.collect() {
let c = usize::from_ne_bytes({ let c = usize::from_ne_bytes({
[c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]] [c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7]]
@ -536,11 +520,8 @@ impl Component for MailView {
} }
return true; return true;
} }
let accounts = &context.accounts; let account = &mut context.accounts[self.coordinates.0];
let mailbox = &accounts[self.coordinates.0][self.coordinates.1] let envelope: &Envelope = &account.get_env(&self.coordinates.2);
.as_ref()
.unwrap();
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
let mut entries = Vec::new(); let mut entries = Vec::new();
for (idx, env) in envelope for (idx, env) in envelope
@ -594,15 +575,9 @@ impl Component for MailView {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
{ {
let accounts = &context.accounts; let account = &mut context.accounts[self.coordinates.0];
let mailbox = &accounts[self.coordinates.0][self.coordinates.1] let envelope: &Envelope = &account.get_env(&self.coordinates.2);
.as_ref() let op = account.operation(&envelope.hash());
.unwrap();
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
let op = context.accounts[self.coordinates.0]
.backend
.operation(envelope.hash(), mailbox.folder.hash());
if let Some(u) = envelope.body(op).attachments().get(lidx) { if let Some(u) = envelope.body(op).attachments().get(lidx) {
match u.content_type() { match u.content_type() {
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => {
@ -690,16 +665,10 @@ impl Component for MailView {
.replies .replies
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear)); .push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
let url = { let url = {
let accounts = &context.accounts; let account = &mut context.accounts[self.coordinates.0];
let mailbox = &accounts[self.coordinates.0][self.coordinates.1] let envelope: &Envelope = &account.get_env(&self.coordinates.2);
.as_ref()
.unwrap();
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
let finder = LinkFinder::new(); let finder = LinkFinder::new();
let op = context.accounts[self.coordinates.0] let op = account.operation(&envelope.hash());
.backend
.operation(envelope.hash(), mailbox.folder.hash());
let mut t = envelope.body(op).text().to_string(); let mut t = envelope.body(op).text().to_string();
let links: Vec<Link> = finder.links(&t).collect(); let links: Vec<Link> = finder.links(&t).collect();
if let Some(u) = links.get(lidx) { if let Some(u) = links.get(lidx) {

View File

@ -146,16 +146,15 @@ impl ThreadView {
} }
fn initiate(&mut self, expanded_hash: Option<ThreadHash>, context: &Context) { fn initiate(&mut self, expanded_hash: Option<ThreadHash>, context: &Context) {
/* stack to push thread messages in order in order to pop and print them later */ /* stack to push thread messages in order in order to pop and print them later */
let mailbox = &context.accounts[self.coordinates.0][self.coordinates.1] let account = &context.accounts[self.coordinates.0];
.as_ref() let mailbox = &account[self.coordinates.1].as_ref().unwrap();
.unwrap(); let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &mailbox.collection.threads;
let thread_iter = threads.thread_iter(self.coordinates.2); let thread_iter = threads.thread_iter(self.coordinates.2);
self.entries.clear(); self.entries.clear();
for (line, (ind, thread_hash)) in thread_iter.enumerate() { for (line, (ind, thread_hash)) in thread_iter.enumerate() {
let entry = if let Some(msg_hash) = threads.thread_nodes()[&thread_hash].message() { let entry = if let Some(msg_hash) = threads.thread_nodes()[&thread_hash].message() {
let seen: bool = mailbox.collection[&msg_hash].is_seen(); let seen: bool = account.get_env(&msg_hash).is_seen();
self.make_entry((ind, thread_hash, line), msg_hash, seen) self.make_entry((ind, thread_hash, line), msg_hash, seen)
} else { } else {
continue; continue;
@ -180,7 +179,7 @@ impl ThreadView {
let mut highlight_reply_subjects: Vec<Option<usize>> = let mut highlight_reply_subjects: Vec<Option<usize>> =
Vec::with_capacity(self.entries.len()); Vec::with_capacity(self.entries.len());
for e in &mut self.entries { for e in &mut self.entries {
let envelope: &Envelope = &mailbox.collection[&e.msg_hash]; let envelope: &Envelope = &context.accounts[self.coordinates.0].get_env(&e.msg_hash);
let thread_node = &threads.thread_nodes()[&e.index.1]; let thread_node = &threads.thread_nodes()[&e.index.1];
let string = if thread_node.show_subject() { let string = if thread_node.show_subject() {
let subject = envelope.subject(); let subject = envelope.subject();
@ -533,10 +532,9 @@ 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 mailbox = &mut context.accounts[self.coordinates.0][self.coordinates.1] let account = &context.accounts[self.coordinates.0];
.as_ref() let mailbox = &account[self.coordinates.1].as_ref().unwrap();
.unwrap(); let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &mailbox.collection.threads;
let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)]; let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)];
let i = if let Some(i) = thread_node.message() { let i = if let Some(i) = thread_node.message() {
i i
@ -545,7 +543,7 @@ impl ThreadView {
.message() .message()
.unwrap() .unwrap()
}; };
let envelope: &Envelope = &mailbox.collection[&i]; let envelope: &Envelope = account.get_env(&i);
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&envelope.subject(), &envelope.subject(),
@ -613,10 +611,9 @@ 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 mailbox = &context.accounts[self.coordinates.0][self.coordinates.1] let account = &context.accounts[self.coordinates.0];
.as_ref() let mailbox = &account[self.coordinates.1].as_ref().unwrap();
.unwrap(); let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &mailbox.collection.threads;
let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)]; let thread_node = &threads.thread_nodes()[&threads.root_set(self.coordinates.2)];
let i = if let Some(i) = thread_node.message() { let i = if let Some(i) = thread_node.message() {
i i
@ -627,7 +624,7 @@ impl ThreadView {
} }
threads.thread_nodes()[&iter_ptr].message().unwrap() threads.thread_nodes()[&iter_ptr].message().unwrap()
}; };
let envelope: &Envelope = &mailbox.collection[&i]; let envelope: &Envelope = account.get_env(&i);
let (x, y) = write_string_to_grid( let (x, y) = write_string_to_grid(
&envelope.subject(), &envelope.subject(),
@ -833,10 +830,9 @@ impl Component for ThreadView {
} }
UIEvent::Input(Key::Char('e')) => { UIEvent::Input(Key::Char('e')) => {
{ {
let mailbox = &context.accounts[self.coordinates.0][self.coordinates.1] let account = &context.accounts[self.coordinates.0];
.as_ref() let mailbox = &account[self.coordinates.1].as_ref().unwrap();
.unwrap(); let threads = &account.collection.threads[&mailbox.folder.hash()];
let threads = &mailbox.collection.threads;
let thread_node = let thread_node =
&threads.thread_nodes()[&threads.root_set(self.coordinates.2)]; &threads.thread_nodes()[&threads.root_set(self.coordinates.2)];
let i = if let Some(i) = thread_node.message() { let i = if let Some(i) = thread_node.message() {
@ -846,20 +842,18 @@ impl Component for ThreadView {
.message() .message()
.unwrap() .unwrap()
}; };
let envelope: &Envelope = &mailbox.collection[&i]; let envelope: &Envelope = &account.get_env(&i);
let op = context.accounts[self.coordinates.0] let op = account.operation(&envelope.hash());
.backend
.operation(envelope.hash(), mailbox.folder.hash());
debug!( debug!(
"sending action edit for {}, {}", "sending action edit for {}, {}",
envelope.message_id(), envelope.message_id(),
op.description() op.description()
); );
context.replies.push_back(UIEvent::Action(Tab(Edit(
self.coordinates.0,
envelope.hash(),
))));
} }
context.replies.push_back(UIEvent::Action(Tab(Edit(
self.coordinates,
self.entries[self.expanded_pos].index.1,
))));
return true; return true;
} }
UIEvent::Input(Key::Up) => { UIEvent::Input(Key::Up) => {

View File

@ -868,13 +868,18 @@ impl Component for StatusBar {
return false; return false;
} }
} }
let m = &context.accounts[*idx_a][*idx_f].as_ref().unwrap(); let account = &context.accounts[*idx_a];
let m = &account[*idx_f].as_ref().unwrap();
self.status = format!( self.status = format!(
"{} | Mailbox: {}, Messages: {}, New: {}", "{} | Mailbox: {}, Messages: {}, New: {}",
self.mode, self.mode,
m.folder.name(), m.folder.name(),
m.collection.len(), m.envelopes.len(),
m.collection.values().filter(|e| !e.is_seen()).count() m.envelopes
.iter()
.map(|h| &account.collection[&h])
.filter(|e| !e.is_seen())
.count()
); );
self.dirty = true; self.dirty = true;
} }
@ -1254,8 +1259,8 @@ impl Component for Tabbed {
self.children[self.cursor_pos].set_dirty(); self.children[self.cursor_pos].set_dirty();
return true; return true;
} }
UIEvent::Action(Tab(Edit(coordinates, msg))) => { UIEvent::Action(Tab(Edit(account_pos, msg))) => {
self.add_component(Box::new(Composer::edit(coordinates, msg, context))); self.add_component(Box::new(Composer::edit(account_pos, msg, context)));
self.cursor_pos = self.children.len() - 1; self.cursor_pos = self.children.len() - 1;
self.children[self.cursor_pos].set_dirty(); self.children[self.cursor_pos].set_dirty();
return true; return true;

View File

@ -31,9 +31,11 @@ use melib::async_workers::{Async, AsyncBuilder, AsyncStatus};
use melib::backends::FolderHash; use melib::backends::FolderHash;
use melib::error::Result; use melib::error::Result;
use melib::mailbox::backends::{ use melib::mailbox::backends::{
Backends, Folder, MailBackend, NotifyFn, RefreshEvent, RefreshEventConsumer, RefreshEventKind, BackendOp, Backends, Folder, MailBackend, NotifyFn, RefreshEvent, RefreshEventConsumer,
RefreshEventKind,
}; };
use melib::mailbox::*; use melib::mailbox::*;
use melib::thread::ThreadHash;
use melib::AddressBook; use melib::AddressBook;
use std::collections::VecDeque; use std::collections::VecDeque;
@ -45,7 +47,7 @@ use std::result;
use std::sync::Arc; use std::sync::Arc;
use types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification}; use types::UIEvent::{self, EnvelopeRemove, EnvelopeRename, EnvelopeUpdate, Notification};
pub type Worker = Option<Async<Result<Mailbox>>>; pub type Worker = Option<Async<(Result<FnvHashMap<EnvelopeHash, Envelope>>, Result<Mailbox>)>>;
macro_rules! mailbox { macro_rules! mailbox {
($idx:expr, $folders:expr) => { ($idx:expr, $folders:expr) => {
@ -66,7 +68,8 @@ pub struct Account {
pub(crate) folders_order: Vec<FolderHash>, pub(crate) folders_order: Vec<FolderHash>,
folder_names: FnvHashMap<FolderHash, String>, folder_names: FnvHashMap<FolderHash, String>,
tree: Vec<FolderNode>, tree: Vec<FolderNode>,
sent_folder: Option<usize>, sent_folder: Option<FolderHash>,
pub(crate) collection: Collection,
pub(crate) address_book: AddressBook, pub(crate) address_book: AddressBook,
@ -161,11 +164,15 @@ impl Account {
let notify_fn = Arc::new(notify_fn); let notify_fn = Arc::new(notify_fn);
let mut folder_names = FnvHashMap::default(); let mut folder_names = FnvHashMap::default();
let mut sent_folder = None;
for f in ref_folders.values_mut() { for f in ref_folders.values_mut() {
let entry = settings let entry = settings
.folder_confs .folder_confs
.entry(f.name().to_string()) .entry(f.name().to_string())
.or_default(); .or_default();
if f.name().eq_ignore_ascii_case("sent") {
sent_folder = Some(f.hash());
}
if (f.name().eq_ignore_ascii_case("junk") if (f.name().eq_ignore_ascii_case("junk")
|| f.name().eq_ignore_ascii_case("spam") || f.name().eq_ignore_ascii_case("spam")
|| f.name().eq_ignore_ascii_case("sent") || f.name().eq_ignore_ascii_case("sent")
@ -247,7 +254,8 @@ impl Account {
folder_names, folder_names,
tree, tree,
address_book, address_book,
sent_folder: None, sent_folder,
collection: Collection::new(Default::default()),
workers, workers,
settings: settings.clone(), settings: settings.clone(),
runtime_settings: settings, runtime_settings: settings,
@ -271,10 +279,17 @@ impl Account {
let work = handle.work().unwrap(); let work = handle.work().unwrap();
work.compute(); work.compute();
handle.join(); handle.join();
let envelopes = handle.extract(); let envelopes: Result<FnvHashMap<EnvelopeHash, Envelope>> = handle.extract().map(|v| {
v.into_iter()
.map(|e| (e.hash(), e))
.collect::<FnvHashMap<EnvelopeHash, Envelope>>()
});
let hash = folder.hash(); let hash = folder.hash();
let ret = Mailbox::new(folder, envelopes); let m = {
tx.send(AsyncStatus::Payload(ret)); //FIXME NLL
Mailbox::new(folder, envelopes.as_ref().map_err(|e| e.clone()))
};
tx.send(AsyncStatus::Payload((envelopes, m)));
notify_fn.notify(hash); notify_fn.notify(hash);
}))) })))
} }
@ -291,31 +306,51 @@ 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).update(old_hash, *envelope); mailbox!(&folder_hash, self.folders).rename(old_hash, envelope.hash());
self.collection.update(old_hash, *envelope, folder_hash);
return Some(EnvelopeUpdate(old_hash)); return Some(EnvelopeUpdate(old_hash));
} }
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);
let mailbox = mailbox!(&folder_hash, self.folders); let mailbox = mailbox!(&folder_hash, self.folders);
mailbox.rename(old_hash, new_hash); mailbox.rename(old_hash, new_hash);
self.collection.rename(old_hash, new_hash, folder_hash);
return Some(EnvelopeRename(mailbox.folder.hash(), old_hash, new_hash)); return Some(EnvelopeRename(mailbox.folder.hash(), old_hash, new_hash));
} }
RefreshEventKind::Create(envelope) => { RefreshEventKind::Create(envelope) => {
debug!("create {}", envelope.hash()); let env_hash = envelope.hash();
let env_hash: EnvelopeHash = { {
//FIXME NLL
let mailbox = mailbox!(&folder_hash, self.folders); let mailbox = mailbox!(&folder_hash, self.folders);
mailbox.insert(*envelope).hash() mailbox.insert(env_hash);
}; self.collection.insert(*envelope, folder_hash);
if self
.sent_folder
.as_ref()
.map(|h| *h == folder_hash)
.unwrap_or(false)
{
self.collection.insert_reply(env_hash);
}
}
let ref_folders: FnvHashMap<FolderHash, Folder> = self.backend.folders(); let ref_folders: FnvHashMap<FolderHash, Folder> = self.backend.folders();
let folder_conf = &self.settings.folder_confs[&self.folder_names[&folder_hash]]; {
if folder_conf.ignore.is_true() { //FIXME NLL
return None; let folder_conf =
&self.settings.folder_confs[&self.folder_names[&folder_hash]];
if folder_conf.ignore.is_true() {
return None;
}
} }
let (env, thread_node) = {
mailbox!(&folder_hash, self.folders).mail_and_thread(env_hash); //FIXME NLL
if thread_node.snoozed() { let (_, thread_node) = self.mail_and_thread(env_hash, folder_hash);
return None; if thread_node.snoozed() {
return None;
}
} }
let env = self.get_env(&env_hash);
return Some(Notification( return Some(Notification(
Some("new mail".into()), Some("new mail".into()),
format!( format!(
@ -329,6 +364,7 @@ impl Account {
} }
RefreshEventKind::Remove(envelope_hash) => { RefreshEventKind::Remove(envelope_hash) => {
mailbox!(&folder_hash, self.folders).remove(envelope_hash); mailbox!(&folder_hash, self.folders).remove(envelope_hash);
self.collection.remove(envelope_hash, folder_hash);
return Some(EnvelopeRemove(envelope_hash)); return Some(EnvelopeRemove(envelope_hash));
} }
RefreshEventKind::Rescan => { RefreshEventKind::Rescan => {
@ -394,7 +430,19 @@ impl Account {
&mut self.workers &mut self.workers
} }
fn load_mailbox(&mut self, folder_hash: FolderHash, mailbox: Result<Mailbox>) { fn load_mailbox(
&mut self,
folder_hash: FolderHash,
mailbox: (Result<FnvHashMap<EnvelopeHash, Envelope>>, Result<Mailbox>),
) {
let (envs, mut mailbox) = mailbox;
if envs.is_err() {
self.folders.insert(folder_hash, None);
return;
}
let envs = envs.unwrap();
self.collection
.merge(envs, folder_hash, &mut mailbox, self.sent_folder);
self.folders.insert(folder_hash, Some(mailbox)); self.folders.insert(folder_hash, Some(mailbox));
} }
@ -440,7 +488,58 @@ impl Account {
pos: 0, pos: 0,
} }
} }
pub fn get_env(&self, h: &EnvelopeHash) -> &Envelope {
&self.collection[h]
}
pub fn get_env_mut(&mut self, h: &EnvelopeHash) -> &mut Envelope {
self.collection.entry(*h).or_default()
}
pub fn contains_key(&self, h: &EnvelopeHash) -> bool {
self.collection.contains_key(h)
}
pub fn operation(&self, h: &EnvelopeHash) -> Box<BackendOp> {
for mailbox in self.folders.values() {
if let Some(Ok(m)) = mailbox {
if m.envelopes.contains(h) {
return self.backend.operation(*h, m.folder.hash());
}
}
}
debug!("didn't find {}", *h);
std::dbg!(&self.folders);
std::dbg!(&self.collection.envelopes);
unreachable!()
}
pub fn thread_to_mail_mut(&mut self, h: ThreadHash, f: FolderHash) -> &mut Envelope {
self.collection
.envelopes
.entry(self.collection.threads[&f].thread_to_mail(h))
.or_default()
}
pub fn thread_to_mail(&self, h: ThreadHash, f: FolderHash) -> &Envelope {
&self.collection.envelopes[&self.collection.threads[&f].thread_to_mail(h)]
}
pub fn threaded_mail(&self, h: ThreadHash, f: FolderHash) -> EnvelopeHash {
self.collection.threads[&f].thread_to_mail(h)
}
pub fn mail_and_thread(
&mut self,
i: EnvelopeHash,
f: FolderHash,
) -> (&mut Envelope, &ThreadNode) {
let thread;
{
let x = &mut self.collection.envelopes.entry(i).or_default();
thread = &self.collection.threads[&f][&x.thread()];
}
(self.collection.envelopes.entry(i).or_default(), thread)
}
pub fn thread(&self, h: ThreadHash, f: FolderHash) -> &ThreadNode {
&self.collection.threads[&f].thread_nodes()[&h]
}
} }
impl Index<FolderHash> for Account { impl Index<FolderHash> for Account {
type Output = Result<Mailbox>; type Output = Result<Mailbox>;
fn index(&self, index: FolderHash) -> &Result<Mailbox> { fn index(&self, index: FolderHash) -> &Result<Mailbox> {

View File

@ -26,6 +26,7 @@
use components::Component; use components::Component;
pub use melib::mailbox::{SortField, SortOrder}; pub use melib::mailbox::{SortField, SortOrder};
use melib::thread::ThreadHash; use melib::thread::ThreadHash;
use melib::EnvelopeHash;
extern crate uuid; extern crate uuid;
use uuid::Uuid; use uuid::Uuid;
@ -43,7 +44,7 @@ pub enum TabAction {
NewDraft(usize), NewDraft(usize),
Reply((usize, usize, usize), ThreadHash), // thread coordinates (account, mailbox, root_set idx) and thread hash Reply((usize, usize, usize), ThreadHash), // thread coordinates (account, mailbox, root_set idx) and thread hash
Close, Close,
Edit((usize, usize, usize), ThreadHash), // thread coordinates (account, mailbox, root_set idx) and thread hash Edit(usize, EnvelopeHash), // account_position, envelope hash
Kill(Uuid), Kill(Uuid),
} }