parent
1f49dfae3b
commit
4f715af248
|
@ -24,7 +24,7 @@ extern crate fnv;
|
|||
extern crate notify;
|
||||
extern crate xdg;
|
||||
|
||||
use super::{MaildirFolder, MaildirOp};
|
||||
use super::{MaildirFolder, MaildirOp, NotifyFn};
|
||||
use async::*;
|
||||
use conf::AccountSettings;
|
||||
use error::Result;
|
||||
|
@ -125,8 +125,8 @@ impl MailBackend for MaildirType {
|
|||
fn folders(&self) -> Vec<Folder> {
|
||||
self.folders.iter().map(|f| f.clone()).collect()
|
||||
}
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
self.multicore(4, folder)
|
||||
fn get(&mut self, folder: &Folder, notify_fn: Arc<NotifyFn>) -> Async<Result<Vec<Envelope>>> {
|
||||
self.multicore(4, folder, notify_fn)
|
||||
}
|
||||
fn watch(&self, sender: RefreshEventConsumer) -> Result<()> {
|
||||
let (tx, rx) = channel();
|
||||
|
@ -345,7 +345,12 @@ impl MaildirType {
|
|||
.0
|
||||
}
|
||||
|
||||
pub fn multicore(&mut self, cores: usize, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
|
||||
pub fn multicore(
|
||||
&mut self,
|
||||
cores: usize,
|
||||
folder: &Folder,
|
||||
notify_fn: Arc<NotifyFn>,
|
||||
) -> Async<Result<Vec<Envelope>>> {
|
||||
let mut w = AsyncBuilder::new();
|
||||
let root_path = self.path.to_path_buf();
|
||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap();
|
||||
|
@ -483,6 +488,7 @@ impl MaildirType {
|
|||
(*map).insert(e.hash(), y);
|
||||
}
|
||||
tx.send(AsyncStatus::Finished);
|
||||
notify_fn.notify();
|
||||
Ok(r)
|
||||
})
|
||||
.unwrap()
|
||||
|
|
|
@ -31,6 +31,7 @@ use mailbox::backends::maildir::MaildirType;
|
|||
use mailbox::email::{Envelope, EnvelopeHash, Flag};
|
||||
use std::fmt;
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
|
||||
extern crate fnv;
|
||||
use self::fnv::FnvHashMap;
|
||||
|
@ -117,8 +118,33 @@ impl RefreshEventConsumer {
|
|||
self.0(r);
|
||||
}
|
||||
}
|
||||
|
||||
pub struct NotifyFn(Box<Fn() -> ()>);
|
||||
unsafe impl Send for NotifyFn {}
|
||||
unsafe impl Sync for NotifyFn {}
|
||||
|
||||
impl fmt::Debug for NotifyFn {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "NotifyFn Box")
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<Fn() -> ()>> for NotifyFn {
|
||||
fn from(kind: Box<Fn() -> ()>) -> Self {
|
||||
NotifyFn(kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl NotifyFn {
|
||||
pub fn new(b: Box<Fn() -> ()>) -> Self {
|
||||
NotifyFn(b)
|
||||
}
|
||||
pub fn notify(&self) -> () {
|
||||
self.0();
|
||||
}
|
||||
}
|
||||
pub trait MailBackend: ::std::fmt::Debug {
|
||||
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
|
||||
fn get(&mut self, folder: &Folder, notify_fn: Arc<NotifyFn>) -> Async<Result<Vec<Envelope>>>;
|
||||
fn watch(&self, sender: RefreshEventConsumer) -> Result<()>;
|
||||
fn folders(&self) -> Vec<Folder>;
|
||||
fn operation(&self, hash: EnvelopeHash) -> Box<BackendOp>;
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
extern crate bincode;
|
||||
extern crate xdg;
|
||||
|
||||
use super::*;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs;
|
||||
use std::io;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::result;
|
||||
|
||||
extern crate fnv;
|
||||
use self::fnv::FnvHashMap;
|
||||
|
@ -15,7 +21,7 @@ pub struct Collection {
|
|||
}
|
||||
|
||||
impl Collection {
|
||||
pub fn new(vec: Vec<Envelope>) -> Collection {
|
||||
pub fn new(vec: Vec<Envelope>, name: &str) -> Collection {
|
||||
let mut envelopes: FnvHashMap<EnvelopeHash, Envelope> =
|
||||
FnvHashMap::with_capacity_and_hasher(vec.len(), Default::default());
|
||||
for e in vec {
|
||||
|
@ -24,7 +30,19 @@ impl Collection {
|
|||
let date_index = BTreeMap::new();
|
||||
let subject_index = None;
|
||||
|
||||
let threads = Threads::new(&mut envelopes); // sent_folder);
|
||||
let cache_dir = xdg::BaseDirectories::with_profile("meli", name).unwrap();
|
||||
let threads = 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);
|
||||
if let Ok(mut cached_t) = result {
|
||||
cached_t.update(&mut envelopes);
|
||||
cached_t
|
||||
} else {
|
||||
Threads::new(&mut envelopes) // sent_folder);
|
||||
}
|
||||
} else {
|
||||
Threads::new(&mut envelopes) // sent_folder);
|
||||
};
|
||||
Collection {
|
||||
envelopes,
|
||||
date_index,
|
||||
|
|
|
@ -71,7 +71,7 @@ impl Mailbox {
|
|||
) -> Result<Mailbox> {
|
||||
let mut envelopes: Vec<Envelope> = envelopes?;
|
||||
envelopes.sort_by(|a, b| a.date().cmp(&b.date()));
|
||||
let collection = Collection::new(envelopes);
|
||||
let collection = Collection::new(envelopes, folder.name());
|
||||
Ok(Mailbox {
|
||||
folder: (*folder).clone(),
|
||||
collection,
|
||||
|
|
|
@ -28,17 +28,18 @@ use mailbox::email::*;
|
|||
extern crate fnv;
|
||||
use self::fnv::{FnvHashMap, FnvHashSet};
|
||||
use std::cell::RefCell;
|
||||
use std::iter::FromIterator;
|
||||
use std::ops::Index;
|
||||
use std::result::Result as StdResult;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize)]
|
||||
pub enum SortOrder {
|
||||
Asc,
|
||||
Desc,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Copy)]
|
||||
#[derive(Debug, Clone, PartialEq, Copy, Deserialize)]
|
||||
pub enum SortField {
|
||||
Subject,
|
||||
Date,
|
||||
|
@ -78,7 +79,7 @@ impl FromStr for SortOrder {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Deserialize)]
|
||||
pub struct ThreadNode {
|
||||
message: Option<EnvelopeHash>,
|
||||
parent: Option<usize>,
|
||||
|
@ -157,7 +158,7 @@ impl ThreadNode {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[derive(Clone, Debug, Default, Deserialize)]
|
||||
pub struct Threads {
|
||||
thread_nodes: Vec<ThreadNode>,
|
||||
root_set: Vec<usize>,
|
||||
|
@ -226,6 +227,16 @@ impl Threads {
|
|||
t
|
||||
}
|
||||
|
||||
pub fn update(&mut self, collection: &mut FnvHashMap<EnvelopeHash, Envelope>) {
|
||||
let new_hash_set = FnvHashSet::from_iter(collection.keys().cloned());
|
||||
|
||||
let difference: Vec<EnvelopeHash> =
|
||||
new_hash_set.difference(&self.hash_set).cloned().collect();
|
||||
for h in difference {
|
||||
self.insert(collection.entry(h).or_default());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, envelope: &mut Envelope) {
|
||||
link_envelope(
|
||||
&mut self.thread_nodes,
|
||||
|
|
|
@ -147,7 +147,6 @@ fn main() {
|
|||
break 'inner; // `goto` 'reap loop, and wait on child.
|
||||
}
|
||||
ThreadEvent::UIEvent(UIEventType::StartupCheck) => {
|
||||
let mut flag = false;
|
||||
let mut render_flag = false;
|
||||
for idx_a in 0..state.context.accounts.len() {
|
||||
let len = state.context.accounts[idx_a].len();
|
||||
|
@ -156,16 +155,10 @@ fn main() {
|
|||
Ok(true) => {
|
||||
render_flag = true;
|
||||
},
|
||||
Ok(false) => {},
|
||||
Err(_) => {
|
||||
flag |= true;
|
||||
Ok(false) | Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !flag {
|
||||
state.finish_startup();
|
||||
}
|
||||
if render_flag {
|
||||
state.render();
|
||||
}
|
||||
|
|
|
@ -29,14 +29,13 @@
|
|||
*/
|
||||
|
||||
use super::*;
|
||||
use melib::backends::FolderHash;
|
||||
use melib::backends::{FolderHash, NotifyFn};
|
||||
|
||||
use chan::{Receiver, Sender};
|
||||
use fnv::FnvHashMap;
|
||||
use std::io::Write;
|
||||
use std::result;
|
||||
use std::thread;
|
||||
use std::time;
|
||||
use termion::raw::IntoRawMode;
|
||||
use termion::screen::AlternateScreen;
|
||||
use termion::{clear, cursor, style};
|
||||
|
@ -122,9 +121,6 @@ pub struct State {
|
|||
pub mode: UIMode,
|
||||
entities: Vec<Entity>,
|
||||
pub context: Context,
|
||||
|
||||
startup_thread: Option<chan::Sender<bool>>,
|
||||
|
||||
threads: FnvHashMap<thread::ThreadId, (chan::Sender<bool>, thread::JoinHandle<()>)>,
|
||||
}
|
||||
|
||||
|
@ -174,34 +170,19 @@ impl State {
|
|||
let mut accounts: Vec<Account> = settings
|
||||
.accounts
|
||||
.iter()
|
||||
.map(|(n, a_s)| Account::new(n.to_string(), a_s.clone(), &backends))
|
||||
.map(|(n, a_s)| {
|
||||
let sender = sender.clone();
|
||||
Account::new(
|
||||
n.to_string(),
|
||||
a_s.clone(),
|
||||
&backends,
|
||||
NotifyFn::new(Box::new(move || {
|
||||
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck))
|
||||
})),
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
accounts.sort_by(|a, b| a.name().cmp(&b.name()));
|
||||
let (startup_tx, startup_rx) = chan::async();
|
||||
let startup_thread = {
|
||||
let sender = sender.clone();
|
||||
let startup_rx = startup_rx.clone();
|
||||
|
||||
thread::Builder::new()
|
||||
.name("startup-thread".to_string())
|
||||
.spawn(move || {
|
||||
let dur = time::Duration::from_millis(800);
|
||||
loop {
|
||||
chan_select! {
|
||||
default => {},
|
||||
startup_rx.recv() -> _ => {
|
||||
sender.send(ThreadEvent::ThreadJoin(thread::current().id()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck));
|
||||
thread::sleep(dur);
|
||||
}
|
||||
startup_rx.recv();
|
||||
return;
|
||||
})
|
||||
.unwrap()
|
||||
};
|
||||
let mut s = State {
|
||||
cols,
|
||||
rows,
|
||||
|
@ -228,13 +209,8 @@ impl State {
|
|||
tx: input_thread.0,
|
||||
},
|
||||
},
|
||||
startup_thread: Some(startup_tx.clone()),
|
||||
threads: FnvHashMap::with_capacity_and_hasher(1, Default::default()),
|
||||
};
|
||||
s.threads.insert(
|
||||
startup_thread.thread().id(),
|
||||
(startup_tx.clone(), startup_thread),
|
||||
);
|
||||
write!(
|
||||
s.stdout(),
|
||||
"{}{}{}",
|
||||
|
@ -271,34 +247,6 @@ impl State {
|
|||
event_type: notification,
|
||||
});
|
||||
}
|
||||
|
||||
let (startup_tx, startup_rx) = chan::async();
|
||||
let startup_thread = {
|
||||
let sender = self.context.sender.clone();
|
||||
let startup_rx = startup_rx.clone();
|
||||
|
||||
thread::Builder::new()
|
||||
.name("startup-thread".to_string())
|
||||
.spawn(move || {
|
||||
let dur = time::Duration::from_millis(100);
|
||||
loop {
|
||||
chan_select! {
|
||||
default => {},
|
||||
startup_rx.recv() -> _ => {
|
||||
sender.send(ThreadEvent::UIEvent(UIEventType::MailboxUpdate((idxa,idxm))));
|
||||
sender.send(ThreadEvent::ThreadJoin(thread::current().id()));
|
||||
return;
|
||||
}
|
||||
}
|
||||
sender.send(ThreadEvent::UIEvent(UIEventType::StartupCheck));
|
||||
thread::sleep(dur);
|
||||
}
|
||||
})
|
||||
.expect("Failed to spawn startup-thread in hash_to_folder()")
|
||||
};
|
||||
self.startup_thread = Some(startup_tx.clone());
|
||||
self.threads
|
||||
.insert(startup_thread.thread().id(), (startup_tx, startup_thread));
|
||||
} else {
|
||||
eprintln!(
|
||||
"BUG: mailbox with hash {} not found in mailbox_hashes.",
|
||||
|
@ -315,19 +263,6 @@ impl State {
|
|||
handle.join().unwrap();
|
||||
}
|
||||
|
||||
/// If startup has finished, inform startup thread that it doesn't need to tick us with startup
|
||||
/// check reminders and let it die.
|
||||
pub fn finish_startup(&mut self) {
|
||||
// TODO: Encode startup process with the type system if possible
|
||||
if self.startup_thread.is_none() {
|
||||
return;
|
||||
}
|
||||
{
|
||||
let tx = self.startup_thread.take().unwrap();
|
||||
tx.send(true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Switch back to the terminal's main screen (The command line the user sees before opening
|
||||
/// the application)
|
||||
pub fn switch_to_main_screen(&mut self) {
|
||||
|
|
|
@ -26,12 +26,13 @@
|
|||
use async::*;
|
||||
use conf::AccountConf;
|
||||
use mailbox::backends::{
|
||||
Backends, Folder, MailBackend, RefreshEvent, RefreshEventConsumer, RefreshEventKind,
|
||||
Backends, Folder, MailBackend, NotifyFn, RefreshEvent, RefreshEventConsumer, RefreshEventKind,
|
||||
};
|
||||
use mailbox::*;
|
||||
use melib::error::Result;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::result;
|
||||
use std::sync::Arc;
|
||||
use types::UIEventType::{self, Notification};
|
||||
|
||||
pub type Worker = Option<Async<Result<Vec<Envelope>>>>;
|
||||
|
@ -48,10 +49,11 @@ pub struct Account {
|
|||
pub settings: AccountConf,
|
||||
pub runtime_settings: AccountConf,
|
||||
pub backend: Box<MailBackend>,
|
||||
notify_fn: Arc<NotifyFn>,
|
||||
}
|
||||
|
||||
impl Account {
|
||||
pub fn new(name: String, settings: AccountConf, map: &Backends) -> Self {
|
||||
pub fn new(name: String, settings: AccountConf, map: &Backends, notify_fn: NotifyFn) -> Self {
|
||||
let mut backend = map.get(settings.account().format())(settings.account());
|
||||
let ref_folders: Vec<Folder> = backend.folders();
|
||||
let mut folders: Vec<Option<Result<Mailbox>>> = Vec::with_capacity(ref_folders.len());
|
||||
|
@ -59,9 +61,10 @@ impl Account {
|
|||
let sent_folder = ref_folders
|
||||
.iter()
|
||||
.position(|x: &Folder| x.name() == settings.account().sent_folder);
|
||||
let notify_fn = Arc::new(notify_fn);
|
||||
for f in ref_folders {
|
||||
folders.push(None);
|
||||
let handle = backend.get(&f);
|
||||
let handle = backend.get(&f, notify_fn.clone());
|
||||
workers.push(Some(handle));
|
||||
}
|
||||
Account {
|
||||
|
@ -72,6 +75,7 @@ impl Account {
|
|||
settings: settings.clone(),
|
||||
runtime_settings: settings,
|
||||
backend,
|
||||
notify_fn,
|
||||
}
|
||||
}
|
||||
pub fn reload(&mut self, event: RefreshEvent, idx: usize) -> Option<UIEventType> {
|
||||
|
@ -99,7 +103,7 @@ impl Account {
|
|||
}
|
||||
RefreshEventKind::Rescan => {
|
||||
let ref_folders: Vec<Folder> = self.backend.folders();
|
||||
let handle = self.backend.get(&ref_folders[idx]);
|
||||
let handle = self.backend.get(&ref_folders[idx], self.notify_fn.clone());
|
||||
self.workers[idx] = Some(handle);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue