melib: Hide Envelope behind RwLock
Envelope can now only be accessed from within a RwLock. Two new structs are introduced: EnvelopeRef and EnvelopeRefMut. These hold a reference to an Envelope and the mutex guard that keeps them alive. This change allows sharing of the envelopes hash map amongst threads.sql
parent
e9d17f6897
commit
78eecbb104
|
@ -19,7 +19,7 @@ notify-rust = { version = "^3", optional = true }
|
||||||
termion = "1.5.1"
|
termion = "1.5.1"
|
||||||
xdg = "2.1.0"
|
xdg = "2.1.0"
|
||||||
native-tls = { version ="0.2", optional=true }
|
native-tls = { version ="0.2", optional=true }
|
||||||
serde = "1.0.71"
|
serde = { version = "1.0.71", features = ["rc", ] }
|
||||||
serde_derive = "1.0.71"
|
serde_derive = "1.0.71"
|
||||||
bincode = "1.2.0"
|
bincode = "1.2.0"
|
||||||
uuid = { version = "0.7.4", features = ["serde", "v4"] }
|
uuid = { version = "0.7.4", features = ["serde", "v4"] }
|
||||||
|
|
|
@ -4,12 +4,45 @@ use std::collections::BTreeMap;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::ops::{Deref, DerefMut};
|
use std::ops::{Deref, DerefMut};
|
||||||
|
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};
|
||||||
|
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
|
||||||
|
pub struct EnvelopeRef<'g> {
|
||||||
|
guard: RwLockReadGuard<'g, FnvHashMap<EnvelopeHash, Envelope>>,
|
||||||
|
env_hash: EnvelopeHash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for EnvelopeRef<'_> {
|
||||||
|
type Target = Envelope;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Envelope {
|
||||||
|
self.guard.get(&self.env_hash).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EnvelopeRefMut<'g> {
|
||||||
|
guard: RwLockWriteGuard<'g, FnvHashMap<EnvelopeHash, Envelope>>,
|
||||||
|
env_hash: EnvelopeHash,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for EnvelopeRefMut<'_> {
|
||||||
|
type Target = Envelope;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Envelope {
|
||||||
|
self.guard.get(&self.env_hash).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for EnvelopeRefMut<'_> {
|
||||||
|
fn deref_mut(&mut self) -> &mut Envelope {
|
||||||
|
self.guard.get_mut(&self.env_hash).unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Default, Serialize)]
|
#[derive(Debug, Clone, Deserialize, Default, Serialize)]
|
||||||
pub struct Collection {
|
pub struct Collection {
|
||||||
pub envelopes: FnvHashMap<EnvelopeHash, Envelope>,
|
pub envelopes: Arc<RwLock<FnvHashMap<EnvelopeHash, Envelope>>>,
|
||||||
message_ids: FnvHashMap<Vec<u8>, EnvelopeHash>,
|
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>>,
|
||||||
|
@ -48,7 +81,7 @@ impl Collection {
|
||||||
let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
let threads = FnvHashMap::with_capacity_and_hasher(16, Default::default());
|
||||||
|
|
||||||
Collection {
|
Collection {
|
||||||
envelopes,
|
envelopes: Arc::new(RwLock::new(envelopes)),
|
||||||
date_index,
|
date_index,
|
||||||
message_ids,
|
message_ids,
|
||||||
subject_index,
|
subject_index,
|
||||||
|
@ -58,16 +91,16 @@ impl Collection {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.envelopes.len()
|
self.envelopes.read().unwrap().len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.envelopes.is_empty()
|
self.envelopes.read().unwrap().is_empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
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.remove(&envelope_hash);
|
self.envelopes.write().unwrap().remove(&envelope_hash);
|
||||||
self.threads
|
self.threads
|
||||||
.entry(folder_hash)
|
.entry(folder_hash)
|
||||||
.or_default()
|
.or_default()
|
||||||
|
@ -86,14 +119,14 @@ impl Collection {
|
||||||
new_hash: EnvelopeHash,
|
new_hash: EnvelopeHash,
|
||||||
folder_hash: FolderHash,
|
folder_hash: FolderHash,
|
||||||
) {
|
) {
|
||||||
if !self.envelopes.contains_key(&old_hash) {
|
if !self.envelopes.write().unwrap().contains_key(&old_hash) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let mut env = self.envelopes.remove(&old_hash).unwrap();
|
let mut env = self.envelopes.write().unwrap().remove(&old_hash).unwrap();
|
||||||
env.set_hash(new_hash);
|
env.set_hash(new_hash);
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(env.message_id().raw().to_vec(), new_hash);
|
.insert(env.message_id().raw().to_vec(), new_hash);
|
||||||
self.envelopes.insert(new_hash, env);
|
self.envelopes.write().unwrap().insert(new_hash, env);
|
||||||
{
|
{
|
||||||
if self
|
if self
|
||||||
.threads
|
.threads
|
||||||
|
@ -150,9 +183,9 @@ impl Collection {
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
if !threads.contains_key(&folder_hash) {
|
if !threads.contains_key(&folder_hash) {
|
||||||
threads.insert(folder_hash, Threads::new(&mut new_envelopes));
|
threads.insert(folder_hash, Threads::new(new_envelopes.len()));
|
||||||
for (h, e) in new_envelopes {
|
for (h, e) in new_envelopes {
|
||||||
envelopes.insert(h, e);
|
envelopes.write().unwrap().insert(h, e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
threads.entry(folder_hash).and_modify(|t| {
|
threads.entry(folder_hash).and_modify(|t| {
|
||||||
|
@ -165,7 +198,10 @@ impl Collection {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
for h in ordered_hash_set {
|
for h in ordered_hash_set {
|
||||||
envelopes.insert(h, new_envelopes.remove(&h).unwrap());
|
envelopes
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.insert(h, new_envelopes.remove(&h).unwrap());
|
||||||
t.insert(envelopes, h);
|
t.insert(envelopes, h);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -178,17 +214,19 @@ impl Collection {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
if sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
||||||
|
let envelopes_lck = envelopes.read().unwrap();
|
||||||
let mut ordered_hash_set = threads[&folder_hash]
|
let mut ordered_hash_set = threads[&folder_hash]
|
||||||
.hash_set
|
.hash_set
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<EnvelopeHash>>();
|
.collect::<Vec<EnvelopeHash>>();
|
||||||
ordered_hash_set.sort_by(|a, b| {
|
ordered_hash_set.sort_by(|a, b| {
|
||||||
envelopes[a]
|
envelopes_lck[a]
|
||||||
.date()
|
.date()
|
||||||
.partial_cmp(&envelopes[b].date())
|
.partial_cmp(&envelopes_lck[b].date())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
drop(envelopes_lck);
|
||||||
let mut updated = false;
|
let mut updated = false;
|
||||||
for h in ordered_hash_set {
|
for h in ordered_hash_set {
|
||||||
updated |= threads.entry(t_fh).or_default().insert_reply(envelopes, h);
|
updated |= threads.entry(t_fh).or_default().insert_reply(envelopes, h);
|
||||||
|
@ -199,17 +237,19 @@ impl Collection {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if sent_folder.map(|f| f == t_fh).unwrap_or(false) {
|
if sent_folder.map(|f| f == t_fh).unwrap_or(false) {
|
||||||
|
let envelopes_lck = envelopes.read().unwrap();
|
||||||
let mut ordered_hash_set = threads[&t_fh]
|
let mut ordered_hash_set = threads[&t_fh]
|
||||||
.hash_set
|
.hash_set
|
||||||
.iter()
|
.iter()
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<EnvelopeHash>>();
|
.collect::<Vec<EnvelopeHash>>();
|
||||||
ordered_hash_set.sort_by(|a, b| {
|
ordered_hash_set.sort_by(|a, b| {
|
||||||
envelopes[a]
|
envelopes_lck[a]
|
||||||
.date()
|
.date()
|
||||||
.partial_cmp(&envelopes[b].date())
|
.partial_cmp(&envelopes_lck[b].date())
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
drop(envelopes_lck);
|
||||||
let mut updated = false;
|
let mut updated = false;
|
||||||
for h in ordered_hash_set {
|
for h in ordered_hash_set {
|
||||||
updated |= threads
|
updated |= threads
|
||||||
|
@ -235,12 +275,12 @@ impl Collection {
|
||||||
mut envelope: Envelope,
|
mut envelope: Envelope,
|
||||||
folder_hash: FolderHash,
|
folder_hash: FolderHash,
|
||||||
) {
|
) {
|
||||||
let old_env = self.envelopes.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.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.insert(new_hash, envelope);
|
self.envelopes.write().unwrap().insert(new_hash, envelope);
|
||||||
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
if self.sent_folder.map(|f| f == folder_hash).unwrap_or(false) {
|
||||||
for (_, t) in self.threads.iter_mut() {
|
for (_, t) in self.threads.iter_mut() {
|
||||||
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
t.update_envelope(&self.envelopes, old_hash, new_hash)
|
||||||
|
@ -273,11 +313,11 @@ impl Collection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) -> &Envelope {
|
pub fn insert(&mut self, envelope: Envelope, folder_hash: FolderHash) {
|
||||||
let hash = envelope.hash();
|
let hash = envelope.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.insert(hash, envelope);
|
self.envelopes.write().unwrap().insert(hash, envelope);
|
||||||
if !self
|
if !self
|
||||||
.threads
|
.threads
|
||||||
.entry(folder_hash)
|
.entry(folder_hash)
|
||||||
|
@ -289,26 +329,26 @@ impl Collection {
|
||||||
.or_default()
|
.or_default()
|
||||||
.insert(&mut self.envelopes, hash);
|
.insert(&mut self.envelopes, hash);
|
||||||
}
|
}
|
||||||
&self.envelopes[&hash]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_reply(&mut self, env_hash: EnvelopeHash) {
|
pub fn insert_reply(&mut self, env_hash: EnvelopeHash) {
|
||||||
debug_assert!(self.envelopes.contains_key(&env_hash));
|
debug_assert!(self.envelopes.read().unwrap().contains_key(&env_hash));
|
||||||
for (_, t) in self.threads.iter_mut() {
|
for (_, t) in self.threads.iter_mut() {
|
||||||
t.insert_reply(&mut self.envelopes, env_hash);
|
t.insert_reply(&mut self.envelopes, env_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for Collection {
|
pub fn get_env<'g>(&'g self, env_hash: EnvelopeHash) -> EnvelopeRef<'g> {
|
||||||
type Target = FnvHashMap<EnvelopeHash, Envelope>;
|
let guard: RwLockReadGuard<'g, _> = self.envelopes.read().unwrap();
|
||||||
|
EnvelopeRef { guard, env_hash }
|
||||||
|
}
|
||||||
|
|
||||||
fn deref(&self) -> &FnvHashMap<EnvelopeHash, Envelope> {
|
pub fn get_env_mut<'g>(&'g mut self, env_hash: EnvelopeHash) -> EnvelopeRefMut<'g> {
|
||||||
&self.envelopes
|
let guard = self.envelopes.write().unwrap();
|
||||||
}
|
EnvelopeRefMut { guard, env_hash }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DerefMut for Collection {
|
pub fn contains_key(&self, env_hash: &EnvelopeHash) -> bool {
|
||||||
fn deref_mut(&mut self) -> &mut FnvHashMap<EnvelopeHash, Envelope> {
|
self.envelopes.read().unwrap().contains_key(env_hash)
|
||||||
&mut self.envelopes
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,8 +50,9 @@ use std::ops::Index;
|
||||||
use std::result::Result as StdResult;
|
use std::result::Result as StdResult;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::string::ToString;
|
use std::string::ToString;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
type Envelopes = FnvHashMap<EnvelopeHash, Envelope>;
|
type Envelopes = Arc<RwLock<FnvHashMap<EnvelopeHash, Envelope>>>;
|
||||||
|
|
||||||
#[derive(PartialEq, Hash, Eq, Copy, Clone, Serialize, Deserialize, Default)]
|
#[derive(PartialEq, Hash, Eq, Copy, Clone, Serialize, Deserialize, Default)]
|
||||||
pub struct ThreadHash(Uuid);
|
pub struct ThreadHash(Uuid);
|
||||||
|
@ -485,6 +486,7 @@ impl ThreadNode {
|
||||||
buf: &FnvHashMap<ThreadHash, ThreadNode>,
|
buf: &FnvHashMap<ThreadHash, ThreadNode>,
|
||||||
envelopes: &Envelopes,
|
envelopes: &Envelopes,
|
||||||
) -> usize {
|
) -> usize {
|
||||||
|
let envelopes = envelopes.read().unwrap();
|
||||||
match sort {
|
match sort {
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
match vec.binary_search_by(|probe| buf[&probe].date.cmp(&buf[&child].date)) {
|
match vec.binary_search_by(|probe| buf[&probe].date.cmp(&buf[&child].date)) {
|
||||||
|
@ -682,25 +684,25 @@ impl Threads {
|
||||||
x_root
|
x_root
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(envelopes: &mut Envelopes) -> Threads {
|
pub fn new(length: usize) -> Threads {
|
||||||
/* To reconstruct thread information from the mails we need: */
|
/* To reconstruct thread information from the mails we need: */
|
||||||
|
|
||||||
/* a vector to hold thread members */
|
/* a vector to hold thread members */
|
||||||
let thread_nodes: FnvHashMap<ThreadHash, ThreadNode> = FnvHashMap::with_capacity_and_hasher(
|
let thread_nodes: FnvHashMap<ThreadHash, ThreadNode> = FnvHashMap::with_capacity_and_hasher(
|
||||||
(envelopes.len() as f64 * 1.2) as usize,
|
(length as f64 * 1.2) as usize,
|
||||||
Default::default(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
/* A hash table of Message IDs */
|
/* A hash table of Message IDs */
|
||||||
let message_ids: FnvHashMap<Vec<u8>, ThreadHash> =
|
let message_ids: FnvHashMap<Vec<u8>, ThreadHash> =
|
||||||
FnvHashMap::with_capacity_and_hasher(envelopes.len(), Default::default());
|
FnvHashMap::with_capacity_and_hasher(length, Default::default());
|
||||||
/* A hash set of Message IDs we haven't encountered yet as an Envelope */
|
/* A hash set of Message IDs we haven't encountered yet as an Envelope */
|
||||||
let missing_message_ids: FnvHashSet<Vec<u8>> =
|
let missing_message_ids: FnvHashSet<Vec<u8>> =
|
||||||
FnvHashSet::with_capacity_and_hasher(envelopes.len(), Default::default());
|
FnvHashSet::with_capacity_and_hasher(length, Default::default());
|
||||||
/* A hash set of Message IDs we have encountered as a MessageID */
|
/* A hash set of Message IDs we have encountered as a MessageID */
|
||||||
let message_ids_set: FnvHashSet<Vec<u8>> =
|
let message_ids_set: FnvHashSet<Vec<u8>> =
|
||||||
FnvHashSet::with_capacity_and_hasher(envelopes.len(), Default::default());
|
FnvHashSet::with_capacity_and_hasher(length, Default::default());
|
||||||
let hash_set: FnvHashSet<EnvelopeHash> =
|
let hash_set: FnvHashSet<EnvelopeHash> =
|
||||||
FnvHashSet::with_capacity_and_hasher(envelopes.len(), Default::default());
|
FnvHashSet::with_capacity_and_hasher(length, Default::default());
|
||||||
|
|
||||||
Threads {
|
Threads {
|
||||||
thread_nodes,
|
thread_nodes,
|
||||||
|
@ -754,8 +756,8 @@ impl Threads {
|
||||||
};
|
};
|
||||||
|
|
||||||
self.thread_nodes.get_mut(&thread_hash).unwrap().message = Some(new_hash);
|
self.thread_nodes.get_mut(&thread_hash).unwrap().message = Some(new_hash);
|
||||||
self.thread_nodes.get_mut(&thread_hash).unwrap().has_unseen = !envelopes[&new_hash]
|
self.thread_nodes.get_mut(&thread_hash).unwrap().has_unseen =
|
||||||
.is_seen()
|
!envelopes.read().unwrap()[&new_hash].is_seen()
|
||||||
|| self.thread_nodes[&thread_hash]
|
|| self.thread_nodes[&thread_hash]
|
||||||
.children
|
.children
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -802,18 +804,20 @@ impl Threads {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn amend(&mut self, envelopes: &mut Envelopes) {
|
pub fn amend(&mut self, envelopes: &mut Envelopes) {
|
||||||
let new_hash_set = FnvHashSet::from_iter(envelopes.keys().cloned());
|
let envelopes_lck = envelopes.read().unwrap();
|
||||||
|
let new_hash_set = FnvHashSet::from_iter(envelopes_lck.keys().cloned());
|
||||||
|
|
||||||
let difference: Vec<EnvelopeHash> =
|
let difference: Vec<EnvelopeHash> =
|
||||||
self.hash_set.difference(&new_hash_set).cloned().collect();
|
self.hash_set.difference(&new_hash_set).cloned().collect();
|
||||||
for h in difference {
|
for h in difference {
|
||||||
self.remove(h);
|
self.remove(h);
|
||||||
}
|
}
|
||||||
|
drop(envelopes_lck);
|
||||||
|
|
||||||
let difference: Vec<EnvelopeHash> =
|
let difference: Vec<EnvelopeHash> =
|
||||||
new_hash_set.difference(&self.hash_set).cloned().collect();
|
new_hash_set.difference(&self.hash_set).cloned().collect();
|
||||||
for h in difference {
|
for h in difference {
|
||||||
debug!("inserting {}", envelopes[&h].subject());
|
//debug!("inserting {}", envelopes_lck[&h].subject());
|
||||||
self.insert(envelopes, h);
|
self.insert(envelopes, h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -825,6 +829,7 @@ impl Threads {
|
||||||
env_hash: EnvelopeHash,
|
env_hash: EnvelopeHash,
|
||||||
envelopes: &Envelopes,
|
envelopes: &Envelopes,
|
||||||
) {
|
) {
|
||||||
|
let envelopes = envelopes.read().unwrap();
|
||||||
let mut subject = envelopes[&env_hash].subject();
|
let mut subject = envelopes[&env_hash].subject();
|
||||||
let mut subject = subject.to_mut().as_bytes();
|
let mut subject = subject.to_mut().as_bytes();
|
||||||
let stripped_subject = subject.strip_prefixes();
|
let stripped_subject = subject.strip_prefixes();
|
||||||
|
@ -861,17 +866,18 @@ impl Threads {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) {
|
pub fn insert(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) {
|
||||||
|
let envelopes_lck = envelopes.read().unwrap();
|
||||||
if self
|
if self
|
||||||
.message_ids
|
.message_ids
|
||||||
.contains_key(envelopes[&env_hash].message_id().raw())
|
.contains_key(envelopes_lck[&env_hash].message_id().raw())
|
||||||
&& !self
|
&& !self
|
||||||
.missing_message_ids
|
.missing_message_ids
|
||||||
.contains(envelopes[&env_hash].message_id().raw())
|
.contains(envelopes_lck[&env_hash].message_id().raw())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let reply_to_id: Option<ThreadHash> = envelopes[&env_hash]
|
let reply_to_id: Option<ThreadHash> = envelopes_lck[&env_hash]
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
.map(crate::email::StrBuild::raw)
|
.map(crate::email::StrBuild::raw)
|
||||||
.and_then(|r| self.message_ids.get(r).cloned());
|
.and_then(|r| self.message_ids.get(r).cloned());
|
||||||
|
@ -881,24 +887,28 @@ impl Threads {
|
||||||
ThreadNode {
|
ThreadNode {
|
||||||
message: Some(env_hash),
|
message: Some(env_hash),
|
||||||
parent: reply_to_id,
|
parent: reply_to_id,
|
||||||
date: envelopes[&env_hash].date(),
|
date: envelopes_lck[&env_hash].date(),
|
||||||
has_unseen: !envelopes[&env_hash].is_seen(),
|
has_unseen: !envelopes_lck[&env_hash].is_seen(),
|
||||||
..ThreadNode::new(new_id)
|
..ThreadNode::new(new_id)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelopes[&env_hash].message_id().raw().to_vec(), new_id);
|
.insert(envelopes_lck[&env_hash].message_id().raw().to_vec(), new_id);
|
||||||
self.message_ids_set
|
self.message_ids_set.insert(
|
||||||
.insert(envelopes[&env_hash].message_id().raw().to_vec().to_vec());
|
envelopes_lck[&env_hash]
|
||||||
|
.message_id()
|
||||||
|
.raw()
|
||||||
|
.to_vec()
|
||||||
|
.to_vec(),
|
||||||
|
);
|
||||||
self.missing_message_ids
|
self.missing_message_ids
|
||||||
.remove(envelopes[&env_hash].message_id().raw());
|
.remove(envelopes_lck[&env_hash].message_id().raw());
|
||||||
envelopes.get_mut(&env_hash).unwrap().set_thread(new_id);
|
|
||||||
self.hash_set.insert(env_hash);
|
self.hash_set.insert(env_hash);
|
||||||
if let Some(reply_to_id) = reply_to_id {
|
if let Some(reply_to_id) = reply_to_id {
|
||||||
self.union(reply_to_id, new_id);
|
self.union(reply_to_id, new_id);
|
||||||
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
||||||
} else {
|
} else {
|
||||||
if let Some(r) = envelopes[&env_hash]
|
if let Some(r) = envelopes_lck[&env_hash]
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
.map(crate::email::StrBuild::raw)
|
.map(crate::email::StrBuild::raw)
|
||||||
{
|
{
|
||||||
|
@ -906,7 +916,7 @@ impl Threads {
|
||||||
self.thread_nodes.insert(
|
self.thread_nodes.insert(
|
||||||
reply_to_id,
|
reply_to_id,
|
||||||
ThreadNode {
|
ThreadNode {
|
||||||
date: envelopes[&env_hash].date(),
|
date: envelopes_lck[&env_hash].date(),
|
||||||
thread_group: reply_to_id,
|
thread_group: reply_to_id,
|
||||||
..ThreadNode::new(reply_to_id)
|
..ThreadNode::new(reply_to_id)
|
||||||
},
|
},
|
||||||
|
@ -919,23 +929,31 @@ impl Threads {
|
||||||
}
|
}
|
||||||
self.tree_insert_root(new_id, envelopes);
|
self.tree_insert_root(new_id, envelopes);
|
||||||
}
|
}
|
||||||
|
drop(envelopes_lck);
|
||||||
self.update_show_subject(new_id, env_hash, envelopes);
|
self.update_show_subject(new_id, env_hash, envelopes);
|
||||||
|
envelopes
|
||||||
|
.write()
|
||||||
|
.unwrap()
|
||||||
|
.get_mut(&env_hash)
|
||||||
|
.unwrap()
|
||||||
|
.set_thread(new_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Insert or update */
|
/* Insert or update */
|
||||||
pub fn insert_reply(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) -> bool {
|
pub fn insert_reply(&mut self, envelopes: &mut Envelopes, env_hash: EnvelopeHash) -> bool {
|
||||||
let reply_to_id: Option<ThreadHash> = envelopes[&env_hash]
|
let mut envelopes_lck = envelopes.write().unwrap();
|
||||||
|
let reply_to_id: Option<ThreadHash> = envelopes_lck[&env_hash]
|
||||||
.in_reply_to()
|
.in_reply_to()
|
||||||
.map(crate::email::StrBuild::raw)
|
.map(crate::email::StrBuild::raw)
|
||||||
.and_then(|r| self.message_ids.get(r).cloned());
|
.and_then(|r| self.message_ids.get(r).cloned());
|
||||||
if let Some(id) = self
|
if let Some(id) = self
|
||||||
.message_ids
|
.message_ids
|
||||||
.get(envelopes[&env_hash].message_id().raw())
|
.get(envelopes_lck[&env_hash].message_id().raw())
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
self.thread_nodes.entry(id).and_modify(|n| {
|
self.thread_nodes.entry(id).and_modify(|n| {
|
||||||
n.message = Some(env_hash);
|
n.message = Some(env_hash);
|
||||||
n.date = envelopes[&env_hash].date();
|
n.date = envelopes_lck[&env_hash].date();
|
||||||
n.pruned = false;
|
n.pruned = false;
|
||||||
if n.parent.is_none() {
|
if n.parent.is_none() {
|
||||||
if let Some(reply_to_id) = reply_to_id {
|
if let Some(reply_to_id) = reply_to_id {
|
||||||
|
@ -951,13 +969,19 @@ impl Threads {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelopes[&env_hash].message_id().raw().to_vec(), id);
|
.insert(envelopes_lck[&env_hash].message_id().raw().to_vec(), id);
|
||||||
self.message_ids_set
|
self.message_ids_set.insert(
|
||||||
.insert(envelopes[&env_hash].message_id().raw().to_vec().to_vec());
|
envelopes_lck[&env_hash]
|
||||||
|
.message_id()
|
||||||
|
.raw()
|
||||||
|
.to_vec()
|
||||||
|
.to_vec(),
|
||||||
|
);
|
||||||
self.missing_message_ids
|
self.missing_message_ids
|
||||||
.remove(envelopes[&env_hash].message_id().raw());
|
.remove(envelopes_lck[&env_hash].message_id().raw());
|
||||||
envelopes.get_mut(&env_hash).unwrap().set_thread(id);
|
envelopes_lck.get_mut(&env_hash).unwrap().set_thread(id);
|
||||||
self.hash_set.insert(env_hash);
|
self.hash_set.insert(env_hash);
|
||||||
|
drop(envelopes_lck);
|
||||||
if self.thread_nodes[&id].parent.is_none() {
|
if self.thread_nodes[&id].parent.is_none() {
|
||||||
self.tree_insert_root(id, envelopes);
|
self.tree_insert_root(id, envelopes);
|
||||||
}
|
}
|
||||||
|
@ -978,21 +1002,27 @@ impl Threads {
|
||||||
ThreadNode {
|
ThreadNode {
|
||||||
message: Some(env_hash),
|
message: Some(env_hash),
|
||||||
parent: Some(reply_to_id),
|
parent: Some(reply_to_id),
|
||||||
date: envelopes[&env_hash].date(),
|
date: envelopes_lck[&env_hash].date(),
|
||||||
has_unseen: !envelopes[&env_hash].is_seen(),
|
has_unseen: !envelopes_lck[&env_hash].is_seen(),
|
||||||
..ThreadNode::new(new_id)
|
..ThreadNode::new(new_id)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
self.message_ids
|
self.message_ids
|
||||||
.insert(envelopes[&env_hash].message_id().raw().to_vec(), new_id);
|
.insert(envelopes_lck[&env_hash].message_id().raw().to_vec(), new_id);
|
||||||
self.message_ids_set
|
self.message_ids_set.insert(
|
||||||
.insert(envelopes[&env_hash].message_id().raw().to_vec().to_vec());
|
envelopes_lck[&env_hash]
|
||||||
|
.message_id()
|
||||||
|
.raw()
|
||||||
|
.to_vec()
|
||||||
|
.to_vec(),
|
||||||
|
);
|
||||||
self.missing_message_ids
|
self.missing_message_ids
|
||||||
.remove(envelopes[&env_hash].message_id().raw());
|
.remove(envelopes_lck[&env_hash].message_id().raw());
|
||||||
envelopes.get_mut(&env_hash).unwrap().set_thread(new_id);
|
envelopes_lck.get_mut(&env_hash).unwrap().set_thread(new_id);
|
||||||
self.hash_set.insert(env_hash);
|
self.hash_set.insert(env_hash);
|
||||||
self.union(reply_to_id, new_id);
|
self.union(reply_to_id, new_id);
|
||||||
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
make!((reply_to_id) parent of (new_id), &mut self.thread_nodes);
|
||||||
|
drop(envelopes_lck);
|
||||||
self.update_show_subject(new_id, env_hash, envelopes);
|
self.update_show_subject(new_id, env_hash, envelopes);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
|
@ -1073,6 +1103,7 @@ impl Threads {
|
||||||
sort: (SortField, SortOrder),
|
sort: (SortField, SortOrder),
|
||||||
envelopes: &Envelopes,
|
envelopes: &Envelopes,
|
||||||
) {
|
) {
|
||||||
|
let envelopes = envelopes.read().unwrap();
|
||||||
vec.sort_by(|b, a| match sort {
|
vec.sort_by(|b, a| match sort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[&a];
|
let a = &self.thread_nodes[&a];
|
||||||
|
@ -1148,6 +1179,7 @@ impl Threads {
|
||||||
}
|
}
|
||||||
fn inner_sort_by(&self, sort: (SortField, SortOrder), envelopes: &Envelopes) {
|
fn inner_sort_by(&self, sort: (SortField, SortOrder), envelopes: &Envelopes) {
|
||||||
let tree = &mut self.tree_index.borrow_mut();
|
let tree = &mut self.tree_index.borrow_mut();
|
||||||
|
let envelopes = envelopes.read().unwrap();
|
||||||
tree.sort_by(|b, a| match sort {
|
tree.sort_by(|b, a| match sort {
|
||||||
(SortField::Date, SortOrder::Desc) => {
|
(SortField::Date, SortOrder::Desc) => {
|
||||||
let a = &self.thread_nodes[&a];
|
let a = &self.thread_nodes[&a];
|
||||||
|
|
|
@ -50,6 +50,7 @@ enum CacheType {
|
||||||
Sqlite3,
|
Sqlite3,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
kind: CacheType,
|
kind: CacheType,
|
||||||
|
@ -108,3 +109,4 @@ impl Cache {
|
||||||
&self.collection.threads[&f].thread_nodes()[&h]
|
&self.collection.threads[&f].thread_nodes()[&h]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
|
@ -169,9 +169,9 @@ impl Composer {
|
||||||
pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Result<Self> {
|
pub fn edit(account_pos: usize, h: EnvelopeHash, context: &Context) -> Result<Self> {
|
||||||
let mut ret = Composer::default();
|
let mut ret = Composer::default();
|
||||||
let op = context.accounts[account_pos].operation(h);
|
let op = context.accounts[account_pos].operation(h);
|
||||||
let envelope: &Envelope = context.accounts[account_pos].get_env(&h);
|
let envelope: EnvelopeRef = context.accounts[account_pos].collection.get_env(h);
|
||||||
|
|
||||||
ret.draft = Draft::edit(envelope, op)?;
|
ret.draft = Draft::edit(&envelope, op)?;
|
||||||
|
|
||||||
ret.account_cursor = account_pos;
|
ret.account_cursor = account_pos;
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
|
@ -187,10 +187,10 @@ impl Composer {
|
||||||
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 = &account.collection[&p.message().unwrap()];
|
let parent_message = account.collection.get_env(p.message().unwrap());
|
||||||
/* If message is from a mailing list and we detect a List-Post header, ask user if they
|
/* If message is from a mailing list and we detect a List-Post header, ask user if they
|
||||||
* want to reply to the mailing list or the submitter of the message */
|
* want to reply to the mailing list or the submitter of the message */
|
||||||
if let Some(actions) = list_management::detect(parent_message) {
|
if let Some(actions) = list_management::detect(&parent_message) {
|
||||||
if let Some(post) = actions.post {
|
if let Some(post) = actions.post {
|
||||||
/* Try to parse header value in this order
|
/* Try to parse header value in this order
|
||||||
* - <mailto:*****@*****>
|
* - <mailto:*****@*****>
|
||||||
|
@ -224,16 +224,24 @@ impl Composer {
|
||||||
let mut op = account.operation(parent_message.hash());
|
let mut op = account.operation(parent_message.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());
|
||||||
ret.draft.headers_mut().insert(
|
ret.draft.headers_mut().insert(
|
||||||
"Subject".into(),
|
"Subject".into(),
|
||||||
if p.show_subject() {
|
if p.show_subject() {
|
||||||
format!(
|
format!(
|
||||||
"Re: {}",
|
"Re: {}",
|
||||||
account.get_env(&p.message().unwrap()).subject().clone()
|
account
|
||||||
|
.collection
|
||||||
|
.get_env(p.message().unwrap())
|
||||||
|
.subject()
|
||||||
|
.clone()
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
account.get_env(&p.message().unwrap()).subject().into()
|
account
|
||||||
|
.collection
|
||||||
|
.get_env(p.message().unwrap())
|
||||||
|
.subject()
|
||||||
|
.into()
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -579,13 +579,15 @@ impl Component for Listing {
|
||||||
} else {
|
} else {
|
||||||
return Some(String::new());
|
return Some(String::new());
|
||||||
};
|
};
|
||||||
|
let envelopes = account.collection.envelopes.clone();
|
||||||
|
let envelopes = envelopes.read().unwrap();
|
||||||
format!(
|
format!(
|
||||||
"Mailbox: {}, Messages: {}, New: {}",
|
"Mailbox: {}, Messages: {}, New: {}",
|
||||||
m.folder.name(),
|
m.folder.name(),
|
||||||
m.envelopes.len(),
|
m.envelopes.len(),
|
||||||
m.envelopes
|
m.envelopes
|
||||||
.iter()
|
.iter()
|
||||||
.map(|h| &account.collection[&h])
|
.map(|h| &envelopes[&h])
|
||||||
.filter(|e| !e.is_seen())
|
.filter(|e| !e.is_seen())
|
||||||
.count()
|
.count()
|
||||||
)
|
)
|
||||||
|
@ -714,8 +716,8 @@ impl Listing {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.envelopes
|
.envelopes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|h| {
|
.filter_map(|&h| {
|
||||||
if account.collection[&h].is_seen() {
|
if account.collection.get_env(h).is_seen() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(())
|
Some(())
|
||||||
|
|
|
@ -364,10 +364,10 @@ impl ListingTrait for CompactListing {
|
||||||
Ok(results) => {
|
Ok(results) => {
|
||||||
let threads = &account.collection.threads[&folder_hash];
|
let threads = &account.collection.threads[&folder_hash];
|
||||||
for env_hash in results {
|
for env_hash in results {
|
||||||
if !account.collection.envelopes.contains_key(&env_hash) {
|
if !account.collection.contains_key(&env_hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let env_hash_thread_hash = account.get_env(&env_hash).thread();
|
let env_hash_thread_hash = account.collection.get_env(env_hash).thread();
|
||||||
if !threads.thread_nodes.contains_key(&env_hash_thread_hash) {
|
if !threads.thread_nodes.contains_key(&env_hash_thread_hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -388,7 +388,7 @@ impl ListingTrait for CompactListing {
|
||||||
threads.vec_inner_sort_by(
|
threads.vec_inner_sort_by(
|
||||||
&mut self.filtered_selection,
|
&mut self.filtered_selection,
|
||||||
self.sort,
|
self.sort,
|
||||||
&context.accounts[self.cursor_pos.0].collection,
|
&context.accounts[self.cursor_pos.0].collection.envelopes,
|
||||||
);
|
);
|
||||||
self.new_cursor_pos.2 =
|
self.new_cursor_pos.2 =
|
||||||
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
|
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
|
||||||
|
@ -469,7 +469,11 @@ impl CompactListing {
|
||||||
id: ComponentId::new_v4(),
|
id: ComponentId::new_v4(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn make_entry_string(e: &Envelope, thread_node: &ThreadNode, is_snoozed: bool) -> EntryStrings {
|
fn make_entry_string(
|
||||||
|
e: EnvelopeRef,
|
||||||
|
thread_node: &ThreadNode,
|
||||||
|
is_snoozed: bool,
|
||||||
|
) -> EntryStrings {
|
||||||
if thread_node.len() > 0 {
|
if thread_node.len() > 0 {
|
||||||
EntryStrings {
|
EntryStrings {
|
||||||
date: DateString(ConversationsListing::format_date(thread_node)),
|
date: DateString(ConversationsListing::format_date(thread_node)),
|
||||||
|
@ -560,7 +564,7 @@ impl CompactListing {
|
||||||
let mut rows = Vec::with_capacity(1024);
|
let mut rows = Vec::with_capacity(1024);
|
||||||
let mut min_width = (0, 0, 0, 0, 0);
|
let mut min_width = (0, 0, 0, 0, 0);
|
||||||
|
|
||||||
threads.sort_by(self.sort, self.subsort, &account.collection);
|
threads.sort_by(self.sort, self.subsort, &account.collection.envelopes);
|
||||||
|
|
||||||
let mut refresh_mailbox = false;
|
let mut refresh_mailbox = false;
|
||||||
let threads_iter = if self.filter_term.is_empty() {
|
let threads_iter = if self.filter_term.is_empty() {
|
||||||
|
@ -594,7 +598,8 @@ impl CompactListing {
|
||||||
|
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
let root_envelope: &Envelope = &context.accounts[self.cursor_pos.0].get_env(&i);
|
let root_envelope: EnvelopeRef =
|
||||||
|
context.accounts[self.cursor_pos.0].collection.get_env(i);
|
||||||
|
|
||||||
let entry_strings = CompactListing::make_entry_string(
|
let entry_strings = CompactListing::make_entry_string(
|
||||||
root_envelope,
|
root_envelope,
|
||||||
|
@ -750,8 +755,9 @@ impl CompactListing {
|
||||||
}
|
}
|
||||||
match (
|
match (
|
||||||
threads.is_snoozed(root_idx),
|
threads.is_snoozed(root_idx),
|
||||||
&context.accounts[self.cursor_pos.0]
|
context.accounts[self.cursor_pos.0]
|
||||||
.get_env(&i)
|
.collection
|
||||||
|
.get_env(i)
|
||||||
.has_attachments(),
|
.has_attachments(),
|
||||||
) {
|
) {
|
||||||
(true, true) => {
|
(true, true) => {
|
||||||
|
@ -835,11 +841,11 @@ impl CompactListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for env_hash in envs_to_set {
|
for env_hash in envs_to_set {
|
||||||
|
let hash = account.collection.get_env(env_hash).hash();
|
||||||
|
let op = account.operation(hash);
|
||||||
|
let mut envelope: EnvelopeRefMut = account.collection.get_env_mut(env_hash);
|
||||||
match a {
|
match a {
|
||||||
ListingAction::SetSeen => {
|
ListingAction::SetSeen => {
|
||||||
let hash = account.get_env(&env_hash).hash();
|
|
||||||
let op = account.operation(hash);
|
|
||||||
let envelope: &mut Envelope = &mut account.get_env_mut(&env_hash);
|
|
||||||
if let Err(e) = envelope.set_seen(op) {
|
if let Err(e) = envelope.set_seen(op) {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(e.to_string()),
|
StatusEvent::DisplayMessage(e.to_string()),
|
||||||
|
@ -848,19 +854,19 @@ impl CompactListing {
|
||||||
self.row_updates.push(thread_hash);
|
self.row_updates.push(thread_hash);
|
||||||
}
|
}
|
||||||
ListingAction::SetUnseen => {
|
ListingAction::SetUnseen => {
|
||||||
let hash = account.get_env(&env_hash).hash();
|
|
||||||
let op = account.operation(hash);
|
|
||||||
let envelope: &mut Envelope = &mut account.get_env_mut(&env_hash);
|
|
||||||
if let Err(e) = envelope.set_unseen(op) {
|
if let Err(e) = envelope.set_unseen(op) {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(e.to_string()),
|
StatusEvent::DisplayMessage(e.to_string()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.row_updates.push(thread_hash);
|
|
||||||
}
|
}
|
||||||
ListingAction::Delete => { /* do nothing */ }
|
ListingAction::Delete => {
|
||||||
|
/* do nothing */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
self.row_updates.push(thread_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1009,10 +1015,10 @@ impl Component for CompactListing {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
|
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
|
||||||
let threads = &account.collection.threads[&folder_hash];
|
let threads = &account.collection.threads[&folder_hash];
|
||||||
if !account.collection.envelopes.contains_key(&new_hash) {
|
if !account.collection.contains_key(&new_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let new_env_thread_hash = account.get_env(new_hash).thread();
|
let new_env_thread_hash = account.collection.get_env(*new_hash).thread();
|
||||||
if !threads.thread_nodes.contains_key(&new_env_thread_hash) {
|
if !threads.thread_nodes.contains_key(&new_env_thread_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1094,7 +1100,7 @@ impl Component for CompactListing {
|
||||||
threads.vec_inner_sort_by(
|
threads.vec_inner_sort_by(
|
||||||
&mut self.filtered_selection,
|
&mut self.filtered_selection,
|
||||||
self.sort,
|
self.sort,
|
||||||
&context.accounts[self.cursor_pos.0].collection,
|
&context.accounts[self.cursor_pos.0].collection.envelopes,
|
||||||
);
|
);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -355,10 +355,10 @@ impl ListingTrait for ConversationsListing {
|
||||||
Ok(results) => {
|
Ok(results) => {
|
||||||
let threads = &account.collection.threads[&folder_hash];
|
let threads = &account.collection.threads[&folder_hash];
|
||||||
for env_hash in results {
|
for env_hash in results {
|
||||||
if !account.collection.envelopes.contains_key(&env_hash) {
|
if !account.collection.contains_key(&env_hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let env_hash_thread_hash = account.get_env(&env_hash).thread();
|
let env_hash_thread_hash = account.collection.get_env(env_hash).thread();
|
||||||
if !threads.thread_nodes.contains_key(&env_hash_thread_hash) {
|
if !threads.thread_nodes.contains_key(&env_hash_thread_hash) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ impl ListingTrait for ConversationsListing {
|
||||||
threads.vec_inner_sort_by(
|
threads.vec_inner_sort_by(
|
||||||
&mut self.filtered_selection,
|
&mut self.filtered_selection,
|
||||||
self.sort,
|
self.sort,
|
||||||
&context.accounts[self.cursor_pos.0].collection,
|
&context.accounts[self.cursor_pos.0].collection.envelopes,
|
||||||
);
|
);
|
||||||
self.new_cursor_pos.2 =
|
self.new_cursor_pos.2 =
|
||||||
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
|
std::cmp::min(self.filtered_selection.len() - 1, self.cursor_pos.2);
|
||||||
|
@ -555,7 +555,7 @@ impl ConversationsListing {
|
||||||
let mut rows = Vec::with_capacity(1024);
|
let mut rows = Vec::with_capacity(1024);
|
||||||
let mut max_entry_columns = 0;
|
let mut max_entry_columns = 0;
|
||||||
|
|
||||||
threads.sort_by(self.sort, self.subsort, &account.collection);
|
threads.sort_by(self.sort, self.subsort, &account.collection.envelopes);
|
||||||
|
|
||||||
let mut refresh_mailbox = false;
|
let mut refresh_mailbox = false;
|
||||||
let threads_iter = if self.filter_term.is_empty() {
|
let threads_iter = if self.filter_term.is_empty() {
|
||||||
|
@ -589,7 +589,8 @@ impl ConversationsListing {
|
||||||
|
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
let root_envelope: &Envelope = &context.accounts[self.cursor_pos.0].get_env(&i);
|
let root_envelope: &EnvelopeRef =
|
||||||
|
&context.accounts[self.cursor_pos.0].collection.get_env(i);
|
||||||
|
|
||||||
let strings = ConversationsListing::make_entry_string(
|
let strings = ConversationsListing::make_entry_string(
|
||||||
root_envelope,
|
root_envelope,
|
||||||
|
@ -808,32 +809,31 @@ impl ConversationsListing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for env_hash in envs_to_set {
|
for env_hash in envs_to_set {
|
||||||
|
let hash = account.collection.get_env(env_hash).hash();
|
||||||
|
let op = account.operation(hash);
|
||||||
|
let mut envelope: EnvelopeRefMut = account.collection.get_env_mut(env_hash);
|
||||||
match a {
|
match a {
|
||||||
ListingAction::SetSeen => {
|
ListingAction::SetSeen => {
|
||||||
let hash = account.get_env(&env_hash).hash();
|
|
||||||
let op = account.operation(hash);
|
|
||||||
let envelope: &mut Envelope = &mut account.get_env_mut(&env_hash);
|
|
||||||
if let Err(e) = envelope.set_seen(op) {
|
if let Err(e) = envelope.set_seen(op) {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(e.to_string()),
|
StatusEvent::DisplayMessage(e.to_string()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.row_updates.push(thread_hash);
|
|
||||||
}
|
}
|
||||||
ListingAction::SetUnseen => {
|
ListingAction::SetUnseen => {
|
||||||
let hash = account.get_env(&env_hash).hash();
|
|
||||||
let op = account.operation(hash);
|
|
||||||
let envelope: &mut Envelope = &mut account.get_env_mut(&env_hash);
|
|
||||||
if let Err(e) = envelope.set_unseen(op) {
|
if let Err(e) = envelope.set_unseen(op) {
|
||||||
context.replies.push_back(UIEvent::StatusEvent(
|
context.replies.push_back(UIEvent::StatusEvent(
|
||||||
StatusEvent::DisplayMessage(e.to_string()),
|
StatusEvent::DisplayMessage(e.to_string()),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
self.row_updates.push(thread_hash);
|
|
||||||
}
|
}
|
||||||
ListingAction::Delete => { /* do nothing */ }
|
ListingAction::Delete => {
|
||||||
|
/* do nothing */
|
||||||
|
continue;
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
self.row_updates.push(thread_hash);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1049,10 +1049,10 @@ impl Component for ConversationsListing {
|
||||||
let account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
|
let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
|
||||||
let threads = &account.collection.threads[&folder_hash];
|
let threads = &account.collection.threads[&folder_hash];
|
||||||
if !account.collection.envelopes.contains_key(&new_hash) {
|
if !account.collection.contains_key(&new_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
let new_env_thread_hash = account.get_env(new_hash).thread();
|
let new_env_thread_hash = account.collection.get_env(*new_hash).thread();
|
||||||
if !threads.thread_nodes.contains_key(&new_env_thread_hash) {
|
if !threads.thread_nodes.contains_key(&new_env_thread_hash) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1133,7 +1133,7 @@ impl Component for ConversationsListing {
|
||||||
threads.vec_inner_sort_by(
|
threads.vec_inner_sort_by(
|
||||||
&mut self.filtered_selection,
|
&mut self.filtered_selection,
|
||||||
self.sort,
|
self.sort,
|
||||||
&context.accounts[self.cursor_pos.0].collection,
|
&context.accounts[self.cursor_pos.0].collection.envelopes,
|
||||||
);
|
);
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -97,7 +97,7 @@ impl ListingTrait for PlainListing {
|
||||||
}
|
}
|
||||||
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 account = &context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
|
let envelope: EnvelopeRef = account.collection.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)
|
||||||
|
@ -323,27 +323,28 @@ impl PlainListing {
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
self.local_collection = account.collection.keys().cloned().collect();
|
let envelopes = account.collection.envelopes.read().unwrap();
|
||||||
|
self.local_collection = envelopes.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 = &account.get_env(a);
|
let ma = &envelopes[a];
|
||||||
let mb = &account.get_env(b);
|
let mb = &envelopes[b];
|
||||||
mb.date().cmp(&ma.date())
|
mb.date().cmp(&ma.date())
|
||||||
}
|
}
|
||||||
(SortField::Date, SortOrder::Asc) => {
|
(SortField::Date, SortOrder::Asc) => {
|
||||||
let ma = &account.get_env(a);
|
let ma = &envelopes[a];
|
||||||
let mb = &account.get_env(b);
|
let mb = &envelopes[b];
|
||||||
ma.date().cmp(&mb.date())
|
ma.date().cmp(&mb.date())
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Desc) => {
|
(SortField::Subject, SortOrder::Desc) => {
|
||||||
let ma = &account.get_env(a);
|
let ma = &envelopes[a];
|
||||||
let mb = &account.get_env(b);
|
let mb = &envelopes[b];
|
||||||
ma.subject().cmp(&mb.subject())
|
ma.subject().cmp(&mb.subject())
|
||||||
}
|
}
|
||||||
(SortField::Subject, SortOrder::Asc) => {
|
(SortField::Subject, SortOrder::Asc) => {
|
||||||
let ma = &account.get_env(a);
|
let ma = &envelopes[a];
|
||||||
let mb = &account.get_env(b);
|
let mb = &envelopes[b];
|
||||||
mb.subject().cmp(&ma.subject())
|
mb.subject().cmp(&ma.subject())
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -353,9 +354,9 @@ impl PlainListing {
|
||||||
let widths: (usize, usize, usize);
|
let widths: (usize, usize, usize);
|
||||||
|
|
||||||
for idx in 0..self.local_collection.len() {
|
for idx in 0..self.local_collection.len() {
|
||||||
let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
|
let envelope: EnvelopeRef = account.collection.get_env(self.local_collection[idx]);
|
||||||
|
|
||||||
let strings = PlainListing::make_entry_string(envelope, idx);
|
let strings = PlainListing::make_entry_string(&envelope, idx);
|
||||||
min_width.0 = cmp::max(min_width.0, strings.0.len()); /* index */
|
min_width.0 = cmp::max(min_width.0, strings.0.len()); /* index */
|
||||||
min_width.1 = cmp::max(min_width.1, strings.2.split_graphemes().len()); /* date */
|
min_width.1 = cmp::max(min_width.1, strings.2.split_graphemes().len()); /* date */
|
||||||
min_width.2 = cmp::max(min_width.2, strings.3.split_graphemes().len()); /* subject */
|
min_width.2 = cmp::max(min_width.2, strings.3.split_graphemes().len()); /* subject */
|
||||||
|
@ -381,7 +382,7 @@ impl PlainListing {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Write an entire line for each envelope entry. */
|
/* Write an entire line for each envelope entry. */
|
||||||
let envelope: &Envelope = &account.get_env(&self.local_collection[idx]);
|
let envelope: EnvelopeRef = account.collection.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)
|
||||||
|
@ -533,9 +534,9 @@ impl Component for PlainListing {
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let account = &mut context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let envelope: &mut Envelope =
|
let envelope: EnvelopeRef =
|
||||||
&mut account.get_env_mut(&self.local_collection[idx]);
|
account.collection.get_env(self.local_collection[idx]);
|
||||||
!envelope.is_seen()
|
!envelope.is_seen()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -177,8 +177,9 @@ impl ListingTrait for ThreadListing {
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.locations[idx] != 0 {
|
if self.locations[idx] != 0 {
|
||||||
let envelope: &Envelope =
|
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0]
|
||||||
&context.accounts[self.cursor_pos.0].get_env(&self.locations[idx]);
|
.collection
|
||||||
|
.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)
|
||||||
|
@ -297,7 +298,7 @@ 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. */
|
||||||
threads.sort_by(self.sort, self.subsort, &account.collection);
|
threads.sort_by(self.sort, self.subsort, &account.collection.envelopes);
|
||||||
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() */
|
||||||
|
@ -309,7 +310,8 @@ impl ThreadListing {
|
||||||
thread_idx += 1;
|
thread_idx += 1;
|
||||||
}
|
}
|
||||||
if thread_node.has_message() {
|
if thread_node.has_message() {
|
||||||
let envelope: &Envelope = &account.get_env(&thread_node.message().unwrap());
|
let envelope: EnvelopeRef =
|
||||||
|
account.collection.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)
|
||||||
|
@ -325,7 +327,7 @@ impl ThreadListing {
|
||||||
};
|
};
|
||||||
let (x, _) = write_string_to_grid(
|
let (x, _) = write_string_to_grid(
|
||||||
&ThreadListing::make_thread_entry(
|
&ThreadListing::make_thread_entry(
|
||||||
envelope,
|
&envelope,
|
||||||
idx,
|
idx,
|
||||||
indentation,
|
indentation,
|
||||||
thread_hash,
|
thread_hash,
|
||||||
|
@ -378,8 +380,9 @@ impl ThreadListing {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if self.locations[idx] != 0 {
|
if self.locations[idx] != 0 {
|
||||||
let envelope: &Envelope =
|
let envelope: EnvelopeRef = context.accounts[self.cursor_pos.0]
|
||||||
&context.accounts[self.cursor_pos.0].get_env(&self.locations[idx]);
|
.collection
|
||||||
|
.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)
|
||||||
|
@ -509,8 +512,10 @@ impl Component for ThreadListing {
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
let account = &mut context.accounts[self.cursor_pos.0];
|
let account = &context.accounts[self.cursor_pos.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.locations[self.cursor_pos.2]);
|
let envelope: EnvelopeRef = account
|
||||||
|
.collection
|
||||||
|
.get_env(self.locations[self.cursor_pos.2]);
|
||||||
envelope.is_seen()
|
envelope.is_seen()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -317,12 +317,13 @@ impl Component for MailView {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let (hash, is_seen) = {
|
let (hash, is_seen) = {
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
(envelope.hash(), envelope.is_seen())
|
(envelope.hash(), envelope.is_seen())
|
||||||
};
|
};
|
||||||
if !is_seen {
|
if !is_seen {
|
||||||
let op = account.operation(hash);
|
let op = account.operation(hash);
|
||||||
let envelope: &mut Envelope = &mut account.get_env_mut(&self.coordinates.2);
|
let mut envelope: EnvelopeRefMut =
|
||||||
|
account.collection.get_env_mut(self.coordinates.2);
|
||||||
if let Err(e) = envelope.set_seen(op) {
|
if let Err(e) = envelope.set_seen(op) {
|
||||||
context
|
context
|
||||||
.replies
|
.replies
|
||||||
|
@ -332,7 +333,7 @@ impl Component for MailView {
|
||||||
))));
|
))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
|
|
||||||
let header_fg = if context.settings.terminal.theme == "light" {
|
let header_fg = if context.settings.terminal.theme == "light" {
|
||||||
Color::Black
|
Color::Black
|
||||||
|
@ -459,7 +460,7 @@ impl Component for MailView {
|
||||||
ref archive,
|
ref archive,
|
||||||
ref post,
|
ref post,
|
||||||
ref unsubscribe,
|
ref unsubscribe,
|
||||||
}) = list_management::detect(envelope)
|
}) = list_management::detect(&envelope)
|
||||||
{
|
{
|
||||||
let mut x = get_x(upper_left);
|
let mut x = get_x(upper_left);
|
||||||
y += 1;
|
y += 1;
|
||||||
|
@ -577,7 +578,7 @@ impl Component for MailView {
|
||||||
if self.dirty {
|
if self.dirty {
|
||||||
let body = {
|
let body = {
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
let op = account.operation(envelope.hash());
|
let op = account.operation(envelope.hash());
|
||||||
match envelope.body(op) {
|
match envelope.body(op) {
|
||||||
Ok(body) => body,
|
Ok(body) => body,
|
||||||
|
@ -615,7 +616,7 @@ impl Component for MailView {
|
||||||
ViewMode::Raw => {
|
ViewMode::Raw => {
|
||||||
let text = {
|
let text = {
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
let mut op = account.operation(envelope.hash());
|
let mut op = account.operation(envelope.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())
|
||||||
|
@ -714,7 +715,7 @@ impl Component for MailView {
|
||||||
match *event {
|
match *event {
|
||||||
UIEvent::Input(Key::Char('c')) if !self.mode.is_contact_selector() => {
|
UIEvent::Input(Key::Char('c')) if !self.mode.is_contact_selector() => {
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
|
|
||||||
let mut entries = Vec::new();
|
let mut entries = Vec::new();
|
||||||
for addr in envelope.from().iter().chain(envelope.to().iter()) {
|
for addr in envelope.from().iter().chain(envelope.to().iter()) {
|
||||||
|
@ -723,6 +724,7 @@ impl Component for MailView {
|
||||||
new_card.set_name(addr.get_display_name());
|
new_card.set_name(addr.get_display_name());
|
||||||
entries.push((new_card, format!("{}", addr)));
|
entries.push((new_card, format!("{}", addr)));
|
||||||
}
|
}
|
||||||
|
drop(envelope);
|
||||||
self.mode = ViewMode::ContactSelector(Selector::new(
|
self.mode = ViewMode::ContactSelector(Selector::new(
|
||||||
"select contacts to add",
|
"select contacts to add",
|
||||||
entries,
|
entries,
|
||||||
|
@ -777,7 +779,7 @@ impl Component for MailView {
|
||||||
|
|
||||||
{
|
{
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
let op = account.operation(envelope.hash());
|
let op = account.operation(envelope.hash());
|
||||||
|
|
||||||
let attachments = match envelope.body(op) {
|
let attachments = match envelope.body(op) {
|
||||||
|
@ -912,7 +914,7 @@ impl Component for MailView {
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
let url = {
|
let url = {
|
||||||
let account = &mut context.accounts[self.coordinates.0];
|
let account = &mut context.accounts[self.coordinates.0];
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
let finder = LinkFinder::new();
|
let finder = LinkFinder::new();
|
||||||
let op = account.operation(envelope.hash());
|
let op = account.operation(envelope.hash());
|
||||||
let t = match envelope.body(op) {
|
let t = match envelope.body(op) {
|
||||||
|
@ -973,8 +975,8 @@ impl Component for MailView {
|
||||||
* arrive */
|
* arrive */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let envelope: &Envelope = &account.get_env(&self.coordinates.2);
|
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
|
||||||
if let Some(actions) = list_management::detect(envelope) {
|
if let Some(actions) = list_management::detect(&envelope) {
|
||||||
match e {
|
match e {
|
||||||
MailingListAction::ListPost if actions.post.is_some() => {
|
MailingListAction::ListPost if actions.post.is_some() => {
|
||||||
/* open composer */
|
/* open composer */
|
||||||
|
|
|
@ -165,7 +165,7 @@ impl ThreadView {
|
||||||
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 = account.get_env(&msg_hash).is_seen();
|
let seen: bool = account.collection.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;
|
||||||
|
@ -190,7 +190,9 @@ 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 = &context.accounts[self.coordinates.0].get_env(&e.msg_hash);
|
let envelope: EnvelopeRef = context.accounts[self.coordinates.0]
|
||||||
|
.collection
|
||||||
|
.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();
|
||||||
|
@ -265,8 +267,9 @@ impl ThreadView {
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
let envelope: &Envelope =
|
let envelope: EnvelopeRef = context.accounts[self.coordinates.0]
|
||||||
&context.accounts[self.coordinates.0].get_env(&e.msg_hash);
|
.collection
|
||||||
|
.get_env(e.msg_hash);
|
||||||
if envelope.has_attachments() {
|
if envelope.has_attachments() {
|
||||||
content[(e.index.0 * 4 + e.heading.grapheme_width(), 2 * y)]
|
content[(e.index.0 * 4 + e.heading.grapheme_width(), 2 * y)]
|
||||||
.set_fg(Color::Byte(103));
|
.set_fg(Color::Byte(103));
|
||||||
|
@ -342,8 +345,9 @@ impl ThreadView {
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
{
|
{
|
||||||
let envelope: &Envelope =
|
let envelope: EnvelopeRef = context.accounts[self.coordinates.0]
|
||||||
&context.accounts[self.coordinates.0].get_env(&e.msg_hash);
|
.collection
|
||||||
|
.get_env(e.msg_hash);
|
||||||
if envelope.has_attachments() {
|
if envelope.has_attachments() {
|
||||||
content[(e.index.0 * 4 + e.heading.grapheme_width(), 2 * y)]
|
content[(e.index.0 * 4 + e.heading.grapheme_width(), 2 * y)]
|
||||||
.set_fg(Color::Byte(103));
|
.set_fg(Color::Byte(103));
|
||||||
|
@ -624,7 +628,7 @@ impl ThreadView {
|
||||||
}
|
}
|
||||||
threads.thread_nodes()[&iter_ptr].message().unwrap()
|
threads.thread_nodes()[&iter_ptr].message().unwrap()
|
||||||
};
|
};
|
||||||
let envelope: &Envelope = account.get_env(&i);
|
let envelope: EnvelopeRef = account.collection.get_env(i);
|
||||||
|
|
||||||
let (x, y) = write_string_to_grid(
|
let (x, y) = write_string_to_grid(
|
||||||
&envelope.subject(),
|
&envelope.subject(),
|
||||||
|
@ -710,7 +714,7 @@ impl ThreadView {
|
||||||
}
|
}
|
||||||
threads.thread_nodes()[&iter_ptr].message().unwrap()
|
threads.thread_nodes()[&iter_ptr].message().unwrap()
|
||||||
};
|
};
|
||||||
let envelope: &Envelope = account.get_env(&i);
|
let envelope: EnvelopeRef = account.collection.get_env(i);
|
||||||
|
|
||||||
let (x, y) = write_string_to_grid(
|
let (x, y) = write_string_to_grid(
|
||||||
&envelope.subject(),
|
&envelope.subject(),
|
||||||
|
@ -954,7 +958,7 @@ impl Component for ThreadView {
|
||||||
.message()
|
.message()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
};
|
};
|
||||||
let envelope: &Envelope = &account.get_env(&i);
|
let envelope: EnvelopeRef = account.collection.get_env(i);
|
||||||
let op = account.operation(envelope.hash());
|
let op = account.operation(envelope.hash());
|
||||||
debug!(
|
debug!(
|
||||||
"sending action edit for {}, {}",
|
"sending action edit for {}, {}",
|
||||||
|
@ -1061,7 +1065,7 @@ impl Component for ThreadView {
|
||||||
for e in self.entries.iter_mut() {
|
for e in self.entries.iter_mut() {
|
||||||
if e.msg_hash == *old_hash {
|
if e.msg_hash == *old_hash {
|
||||||
e.msg_hash = *new_hash;
|
e.msg_hash = *new_hash;
|
||||||
let seen: bool = account.get_env(&new_hash).is_seen();
|
let seen: bool = account.collection.get_env(*new_hash).is_seen();
|
||||||
if seen != e.seen {
|
if seen != e.seen {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1533,7 +1533,8 @@ impl Component for Tabbed {
|
||||||
format!(
|
format!(
|
||||||
"Failed to open envelope {}: {}",
|
"Failed to open envelope {}: {}",
|
||||||
context.accounts[account_pos]
|
context.accounts[account_pos]
|
||||||
.get_env(&msg)
|
.collection
|
||||||
|
.get_env(msg)
|
||||||
.message_id_display(),
|
.message_id_display(),
|
||||||
e.to_string()
|
e.to_string()
|
||||||
),
|
),
|
||||||
|
|
|
@ -484,7 +484,7 @@ impl Account {
|
||||||
}
|
}
|
||||||
RefreshEventKind::Create(envelope) => {
|
RefreshEventKind::Create(envelope) => {
|
||||||
let env_hash = envelope.hash();
|
let env_hash = envelope.hash();
|
||||||
if self.collection.envelopes.contains_key(&env_hash)
|
if self.collection.contains_key(&env_hash)
|
||||||
&& mailbox!(&folder_hash, self.folders)
|
&& mailbox!(&folder_hash, self.folders)
|
||||||
.envelopes
|
.envelopes
|
||||||
.contains(&env_hash)
|
.contains(&env_hash)
|
||||||
|
@ -492,6 +492,14 @@ impl Account {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
mailbox!(&folder_hash, self.folders).insert(env_hash);
|
mailbox!(&folder_hash, self.folders).insert(env_hash);
|
||||||
|
let (is_seen, is_draft) =
|
||||||
|
{ (envelope.is_seen(), envelope.flags().contains(Flag::DRAFT)) };
|
||||||
|
let (subject, from) = {
|
||||||
|
(
|
||||||
|
envelope.subject().into_owned(),
|
||||||
|
envelope.field_from_to_string(),
|
||||||
|
)
|
||||||
|
};
|
||||||
self.collection.insert(*envelope, folder_hash);
|
self.collection.insert(*envelope, folder_hash);
|
||||||
if self
|
if self
|
||||||
.sent_folder
|
.sent_folder
|
||||||
|
@ -507,20 +515,23 @@ impl Account {
|
||||||
if folder_conf.ignore.is_true() {
|
if folder_conf.ignore.is_true() {
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||||
}
|
}
|
||||||
let (_, thread_node) = self.mail_and_thread(env_hash, folder_hash);
|
|
||||||
|
let thread_node = {
|
||||||
|
let thread_hash = &mut self.collection.get_env(env_hash).thread();
|
||||||
|
&self.collection.threads[&folder_hash][&thread_hash]
|
||||||
|
};
|
||||||
if thread_node.snoozed() {
|
if thread_node.snoozed() {
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||||
}
|
}
|
||||||
let env = self.get_env(&env_hash);
|
if is_seen || is_draft {
|
||||||
if env.is_seen() || env.flags().contains(Flag::DRAFT) {
|
|
||||||
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
return Some(UIEvent::MailboxUpdate((self.index, folder_hash)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return Some(Notification(
|
return Some(Notification(
|
||||||
Some(format!("new e-mail from: {}", env.field_from_to_string())),
|
Some(format!("new e-mail from: {}", from)),
|
||||||
format!(
|
format!(
|
||||||
"{}\n{} {}",
|
"{}\n{} {}",
|
||||||
env.subject(),
|
subject,
|
||||||
self.name,
|
self.name,
|
||||||
ref_folders[&folder_hash].name(),
|
ref_folders[&folder_hash].name(),
|
||||||
),
|
),
|
||||||
|
@ -725,12 +736,6 @@ impl Account {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
pub fn contains_key(&self, h: EnvelopeHash) -> bool {
|
||||||
self.collection.contains_key(&h)
|
self.collection.contains_key(&h)
|
||||||
}
|
}
|
||||||
|
@ -752,33 +757,10 @@ impl Account {
|
||||||
}
|
}
|
||||||
debug!("didn't find {}", h);
|
debug!("didn't find {}", h);
|
||||||
debug!(&self.folders);
|
debug!(&self.folders);
|
||||||
debug!(&self.collection.envelopes);
|
//debug!(&self.collection.envelopes);
|
||||||
unreachable!()
|
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 {
|
pub fn thread(&self, h: ThreadHash, f: FolderHash) -> &ThreadNode {
|
||||||
&self.collection.threads[&f].thread_nodes()[&h]
|
&self.collection.threads[&f].thread_nodes()[&h]
|
||||||
}
|
}
|
||||||
|
@ -836,9 +818,11 @@ impl Account {
|
||||||
#[cfg(not(feature = "sqlite3"))]
|
#[cfg(not(feature = "sqlite3"))]
|
||||||
{
|
{
|
||||||
let mut ret = StackVec::new();
|
let mut ret = StackVec::new();
|
||||||
|
let envelopes = self.collection.envelopes.clone().read();
|
||||||
|
let envelopes = envelopes.unwrap();
|
||||||
|
|
||||||
for env_hash in self.folders[folder_hash].as_result()?.envelopes {
|
for env_hash in self.folders[folder_hash].as_result()?.envelopes {
|
||||||
let envelope = &account.collection[&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);
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -169,6 +169,7 @@ pub fn insert(context: &crate::state::Context) -> Result<()> {
|
||||||
.map_err(|e| MeliError::new(e.to_string()))?,
|
.map_err(|e| MeliError::new(e.to_string()))?,
|
||||||
)
|
)
|
||||||
.map_err(|e| MeliError::new(e.to_string()))?;
|
.map_err(|e| MeliError::new(e.to_string()))?;
|
||||||
|
/*
|
||||||
for acc in context.accounts.iter() {
|
for acc in context.accounts.iter() {
|
||||||
debug!("inserting {} envelopes", acc.collection.envelopes.len());
|
debug!("inserting {} envelopes", acc.collection.envelopes.len());
|
||||||
for e in acc.collection.envelopes.values() {
|
for e in acc.collection.envelopes.values() {
|
||||||
|
@ -180,6 +181,7 @@ pub fn insert(context: &crate::state::Context) -> Result<()> {
|
||||||
.map_err(|e| MeliError::new(e.to_string()))?;
|
.map_err(|e| MeliError::new(e.to_string()))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue