Add account online status

Add a boolean field to accounts that states if the account can be
accessed. Local backends (Maildir/mbox) return true every time, but
remote backends (IMAP) may not. Accounts start as offline and then get
initialised when their status goes to online. Right now if an IMAP
account startup but later get offline, there are crashes. With this
change the account can be switched back to offline when that happens.
embed
Manos Pitsidianakis 2019-10-24 20:30:17 +03:00
parent 9ef9293a45
commit e5b6faf6bd
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
9 changed files with 249 additions and 150 deletions

View File

@ -39,7 +39,7 @@ use crossbeam::{
use std::fmt;
use std::sync::Arc;
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct WorkContext {
pub new_work: Sender<Work>,
pub set_name: Sender<(std::thread::ThreadId, String)>,

View File

@ -44,7 +44,7 @@ use fnv::FnvHashMap;
use std;
pub type BackendCreator =
Box<dyn Fn(&AccountSettings, Box<dyn Fn(&str) -> bool>) -> Box<dyn MailBackend>>;
Box<dyn Fn(&AccountSettings, Box<dyn Fn(&str) -> bool + Send + Sync>) -> Box<dyn MailBackend>>;
/// A hashmap containing all available mail backends.
/// An abstraction over any available backends.
@ -108,7 +108,7 @@ pub enum RefreshEventKind {
/// Rename(old_hash, new_hash)
Rename(EnvelopeHash, EnvelopeHash),
Create(Box<Envelope>),
Remove(FolderHash),
Remove(EnvelopeHash),
Rescan,
Failure(MeliError),
}
@ -177,6 +177,7 @@ pub enum FolderOperation {
type NewFolderName = String;
pub trait MailBackend: ::std::fmt::Debug {
fn is_online(&self) -> bool;
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>>;
fn watch(
&self,

View File

@ -65,14 +65,30 @@ pub struct ImapServerConf {
pub danger_accept_invalid_certs: bool,
}
struct IsSubscribedFn(Box<dyn Fn(&str) -> bool + Send + Sync>);
impl std::fmt::Debug for IsSubscribedFn {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "IsSubscribedFn Box")
}
}
impl std::ops::Deref for IsSubscribedFn {
type Target = Box<dyn Fn(&str) -> bool + Send + Sync>;
fn deref(&self) -> &Box<dyn Fn(&str) -> bool + Send + Sync> {
&self.0
}
}
type Capabilities = FnvHashSet<Vec<u8>>;
#[derive(Debug)]
pub struct ImapType {
account_name: String,
online: Arc<Mutex<bool>>,
is_subscribed: Arc<IsSubscribedFn>,
connection: Arc<Mutex<ImapConnection>>,
server_conf: ImapServerConf,
folders: FnvHashMap<FolderHash, ImapFolder>,
folders: Arc<Mutex<FnvHashMap<FolderHash, ImapFolder>>>,
hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
uid_index: Arc<Mutex<FnvHashMap<usize, EnvelopeHash>>>,
@ -80,6 +96,9 @@ pub struct ImapType {
}
impl MailBackend for ImapType {
fn is_online(&self) -> bool {
*self.online.lock().unwrap()
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
macro_rules! exit_on_error {
($tx:expr,$($result:expr)+) => {
@ -97,7 +116,7 @@ impl MailBackend for ImapType {
let uid_index = self.uid_index.clone();
let folder_path = folder.path().to_string();
let folder_hash = folder.hash();
let folder_exists = self.folders[&folder_hash].exists.clone();
let folder_exists = self.folders.lock().unwrap()[&folder_hash].exists.clone();
let connection = self.connection.clone();
let closure = move |_work_context| {
let connection = connection.clone();
@ -189,6 +208,7 @@ impl MailBackend for ImapType {
let folders = self.folders.clone();
let conn = ImapConnection::new_connection(&self.server_conf);
let main_conn = self.connection.clone();
let is_online = self.online.clone();
let hash_index = self.hash_index.clone();
let uid_index = self.uid_index.clone();
let handle = std::thread::Builder::new()
@ -201,6 +221,7 @@ impl MailBackend for ImapType {
.unwrap();
let kit = ImapWatchKit {
conn,
is_online,
main_conn,
hash_index,
uid_index,
@ -218,55 +239,20 @@ impl MailBackend for ImapType {
}
fn folders(&self) -> FnvHashMap<FolderHash, Folder> {
if !self.folders.is_empty() {
return self
.folders
let mut folders = self.folders.lock().unwrap();
if !folders.is_empty() {
return folders
.iter()
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
.collect();
}
let mut folders: FnvHashMap<FolderHash, ImapFolder> = Default::default();
let mut res = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock().unwrap();
conn.send_command(b"LIST \"\" \"*\"").unwrap();
conn.read_response(&mut res).unwrap();
debug!("out: {}", &res);
for l in res.lines().map(|l| l.trim()) {
if let Ok(mut folder) =
protocol_parser::list_folder_result(l.as_bytes()).to_full_result()
{
if let Some(parent) = folder.parent {
if folders.contains_key(&parent) {
folders
.entry(parent)
.and_modify(|e| e.children.push(folder.hash));
} else {
/* Insert dummy parent entry, populating only the children field. Later
* when we encounter the parent entry we will swap its children with
* dummy's */
folders.insert(
parent,
ImapFolder {
children: vec![folder.hash],
..ImapFolder::default()
},
);
}
}
if folders.contains_key(&folder.hash) {
let entry = folders.entry(folder.hash).or_default();
std::mem::swap(&mut entry.children, &mut folder.children);
std::mem::swap(entry, &mut folder);
} else {
folders.insert(folder.hash, folder);
}
} else {
debug!("parse error for {:?}", l);
}
*folders = ImapType::imap_folders(&self.connection);
folders.retain(|_, f| (self.is_subscribed)(f.path()));
let keys = folders.keys().cloned().collect::<FnvHashSet<FolderHash>>();
for f in folders.values_mut() {
f.children.retain(|c| keys.contains(c));
}
debug!(&folders);
*self.online.lock().unwrap() = true;
folders
.iter()
.map(|(h, f)| (*h, Box::new(Clone::clone(f)) as Folder))
@ -277,25 +263,31 @@ impl MailBackend for ImapType {
let (uid, folder_hash) = self.hash_index.lock().unwrap()[&hash];
Box::new(ImapOp::new(
uid,
self.folders[&folder_hash].path().to_string(),
self.folders.lock().unwrap()[&folder_hash]
.path()
.to_string(),
self.connection.clone(),
self.byte_cache.clone(),
))
}
fn save(&self, bytes: &[u8], folder: &str, flags: Option<Flag>) -> Result<()> {
let path = self
.folders
.values()
.find(|v| v.name == folder)
.ok_or(MeliError::new(""))?;
let path = {
let folders = self.folders.lock().unwrap();
folders
.values()
.find(|v| v.name == folder)
.map(|v| v.path().to_string())
.ok_or(MeliError::new(""))?
};
let mut response = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock().unwrap();
let flags = flags.unwrap_or(Flag::empty());
conn.send_command(
format!(
"APPEND \"{}\" ({}) {{{}}}",
path.path(),
&path,
flags_to_imap_list!(flags),
bytes.len()
)
@ -311,7 +303,14 @@ impl MailBackend for ImapType {
fn folder_operation(&mut self, path: &str, op: FolderOperation) -> Result<()> {
use FolderOperation::*;
match (&op, self.folders.values().any(|f| f.path == path)) {
match (
&op,
self.folders
.lock()
.unwrap()
.values()
.any(|f| f.path == path),
) {
(Create, true) => {
return Err(MeliError::new(format!(
"Folder named `{}` in account `{}` already exists.",
@ -391,7 +390,10 @@ macro_rules! get_conf_val {
}
impl ImapType {
pub fn new(s: &AccountSettings, is_subscribed: Box<dyn Fn(&str) -> bool>) -> Self {
pub fn new(
s: &AccountSettings,
is_subscribed: Box<dyn Fn(&str) -> bool + Send + Sync>,
) -> Self {
debug!(s);
let server_hostname = get_conf_val!(s["server_hostname"]);
let server_username = get_conf_val!(s["server_username"]);
@ -416,28 +418,18 @@ impl ImapType {
};
let connection = ImapConnection::new_connection(&server_conf);
let mut m = ImapType {
ImapType {
account_name: s.name().to_string(),
online: Arc::new(Mutex::new(false)),
server_conf,
is_subscribed: Arc::new(IsSubscribedFn(is_subscribed)),
folders: Default::default(),
folders: Arc::new(Mutex::new(Default::default())),
connection: Arc::new(Mutex::new(connection)),
hash_index: Default::default(),
uid_index: Default::default(),
byte_cache: Default::default(),
};
m.folders = m.imap_folders();
m.folders.retain(|_, f| is_subscribed(f.path()));
let keys = m
.folders
.keys()
.cloned()
.collect::<FnvHashSet<FolderHash>>();
for f in m.folders.values_mut() {
f.children.retain(|c| keys.contains(c));
}
m
}
pub fn shell(&mut self) {
@ -472,10 +464,12 @@ impl ImapType {
}
}
pub fn imap_folders(&self) -> FnvHashMap<FolderHash, ImapFolder> {
pub fn imap_folders(
connection: &Arc<Mutex<ImapConnection>>,
) -> FnvHashMap<FolderHash, ImapFolder> {
let mut folders: FnvHashMap<FolderHash, ImapFolder> = Default::default();
let mut res = String::with_capacity(8 * 1024);
let mut conn = self.connection.lock().unwrap();
let mut conn = connection.lock().unwrap();
conn.send_command(b"LIST \"\" \"*\"").unwrap();
conn.read_response(&mut res).unwrap();
debug!("out: {}", &res);

View File

@ -24,10 +24,11 @@ use std::sync::{Arc, Mutex};
/// Arguments for IMAP watching functions
pub struct ImapWatchKit {
pub conn: ImapConnection,
pub is_online: Arc<Mutex<bool>>,
pub main_conn: Arc<Mutex<ImapConnection>>,
pub hash_index: Arc<Mutex<FnvHashMap<EnvelopeHash, (UID, FolderHash)>>>,
pub uid_index: Arc<Mutex<FnvHashMap<usize, EnvelopeHash>>>,
pub folders: FnvHashMap<FolderHash, ImapFolder>,
pub folders: Arc<Mutex<FnvHashMap<FolderHash, ImapFolder>>>,
pub sender: RefreshEventConsumer,
pub work_context: WorkContext,
}
@ -49,6 +50,7 @@ macro_rules! exit_on_error {
pub fn poll_with_examine(kit: ImapWatchKit) {
debug!("poll with examine");
let ImapWatchKit {
is_online,
mut conn,
main_conn,
hash_index,
@ -57,6 +59,12 @@ pub fn poll_with_examine(kit: ImapWatchKit) {
sender,
work_context,
} = kit;
loop {
if *is_online.lock().unwrap() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
let mut response = String::with_capacity(8 * 1024);
let thread_id: std::thread::ThreadId = std::thread::current().id();
loop {
@ -65,6 +73,7 @@ pub fn poll_with_examine(kit: ImapWatchKit) {
.send((thread_id, "sleeping...".to_string()))
.unwrap();
std::thread::sleep(std::time::Duration::from_millis(5 * 60 * 1000));
let folders = folders.lock().unwrap();
for folder in folders.values() {
work_context
.set_status
@ -94,6 +103,7 @@ pub fn idle(kit: ImapWatchKit) {
* minutes wake up and poll the others */
let ImapWatchKit {
mut conn,
is_online,
main_conn,
hash_index,
uid_index,
@ -101,10 +111,19 @@ pub fn idle(kit: ImapWatchKit) {
sender,
work_context,
} = kit;
loop {
if *is_online.lock().unwrap() {
break;
}
std::thread::sleep(std::time::Duration::from_millis(100));
}
let thread_id: std::thread::ThreadId = std::thread::current().id();
let folder: &ImapFolder = folders
let folder: ImapFolder = folders
.lock()
.unwrap()
.values()
.find(|f| f.parent.is_none() && f.path().eq_ignore_ascii_case("INBOX"))
.map(std::clone::Clone::clone)
.unwrap();
let folder_hash = folder.hash();
let mut response = String::with_capacity(8 * 1024);
@ -185,7 +204,7 @@ pub fn idle(kit: ImapWatchKit) {
iter.conn.send_raw(b"DONE")
iter.conn.read_response(&mut response)
);
for (hash, folder) in &folders {
for (hash, folder) in folders.lock().unwrap().iter() {
if *hash == folder_hash {
/* Skip INBOX */
continue;

View File

@ -183,6 +183,9 @@ fn move_to_cur(p: PathBuf) -> Result<PathBuf> {
}
impl MailBackend for MaildirType {
fn is_online(&self) -> bool {
true
}
fn folders(&self) -> FnvHashMap<FolderHash, Folder> {
self.folders
.iter()

View File

@ -367,6 +367,9 @@ pub struct MboxType {
}
impl MailBackend for MboxType {
fn is_online(&self) -> bool {
true
}
fn get(&mut self, folder: &Folder) -> Async<Result<Vec<Envelope>>> {
let mut w = AsyncBuilder::new();
let handle = {

View File

@ -184,6 +184,10 @@ impl fmt::Display for Listing {
impl Component for Listing {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
for a in context.accounts.iter_mut() {
a.is_online();
}
if !self.is_dirty() {
return;
}
@ -218,7 +222,22 @@ impl Component for Listing {
.push_back(((mid, get_y(upper_left)), (mid, get_y(bottom_right))));
}
self.dirty = false;
if right_component_width == total_cols {
if !context.accounts[self.cursor_pos.0].is_online() {
clear_area(grid, area);
write_string_to_grid(
"offline",
grid,
Color::Byte(243),
Color::Default,
Attr::Default,
area,
false,
);
context.dirty_areas.push_back(area);
return;
}
match self.component {
Compact(ref mut l) => l.draw(grid, area, context),
Plain(ref mut l) => l.draw(grid, area, context),
@ -229,6 +248,20 @@ impl Component for Listing {
self.draw_menu(grid, area, context);
} else {
self.draw_menu(grid, (upper_left, (mid, get_y(bottom_right))), context);
if !context.accounts[self.cursor_pos.0].is_online() {
clear_area(grid, (set_x(upper_left, mid + 1), bottom_right));
write_string_to_grid(
"offline",
grid,
Color::Byte(243),
Color::Default,
Attr::Default,
(set_x(upper_left, mid + 1), bottom_right),
false,
);
context.dirty_areas.push_back(area);
return;
}
match self.component {
Compact(ref mut l) => {
l.draw(grid, (set_x(upper_left, mid + 1), bottom_right), context)
@ -337,21 +370,26 @@ impl Component for Listing {
}
_ => return false,
}
let folder_hash =
context.accounts[self.cursor_pos.0].folders_order[self.cursor_pos.1];
/* Check if per-folder configuration overrides general configuration */
if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| account.folder_confs(folder_hash).conf_override.index_style)
/* Account might have no folders yet if it's offline */
if let Some(&folder_hash) = context.accounts[self.cursor_pos.0]
.folders_order
.get(self.cursor_pos.1)
{
self.component.set_style(index_style);
} else if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| Some(account.settings.conf.index_style()))
{
self.component.set_style(index_style);
/* Check if per-folder configuration overrides general configuration */
if let Some(index_style) =
context.accounts.get(self.cursor_pos.0).and_then(|account| {
account.folder_confs(folder_hash).conf_override.index_style
})
{
self.component.set_style(index_style);
} else if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| Some(account.settings.conf.index_style()))
{
self.component.set_style(index_style);
}
}
context
.replies
@ -740,6 +778,15 @@ impl Listing {
);
if lines.is_empty() {
write_string_to_grid(
"offline",
grid,
Color::Byte(243),
Color::Default,
Attr::Default,
(pos_inc(upper_left, (0, 1)), bottom_right),
false,
);
return 0;
}

View File

@ -25,7 +25,7 @@
use super::{AccountConf, FolderConf};
use fnv::FnvHashMap;
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus};
use melib::async_workers::{Async, AsyncBuilder, AsyncStatus, WorkContext};
use melib::backends::{
BackendOp, Backends, Folder, FolderHash, FolderOperation, MailBackend, NotifyFn, ReadOnlyOp,
RefreshEvent, RefreshEventConsumer, RefreshEventKind, SpecialUseMailbox,
@ -118,6 +118,7 @@ impl MailboxEntry {
pub struct Account {
pub index: usize,
name: String,
is_online: bool,
pub(crate) folders: FnvHashMap<FolderHash, MailboxEntry>,
pub(crate) folder_confs: FnvHashMap<FolderHash, FolderConf>,
pub(crate) folders_order: Vec<FolderHash>,
@ -129,6 +130,7 @@ pub struct Account {
pub(crate) address_book: AddressBook,
pub(crate) workers: FnvHashMap<FolderHash, Worker>,
pub(crate) work_context: WorkContext,
pub(crate) settings: AccountConf,
pub(crate) runtime_settings: AccountConf,
@ -199,40 +201,84 @@ impl Account {
name: String,
settings: AccountConf,
map: &Backends,
work_context: WorkContext,
notify_fn: NotifyFn,
) -> Self {
let s = settings.clone();
let mut backend = map.get(settings.account().format())(
let backend = map.get(settings.account().format())(
settings.account(),
Box::new(move |path: &str| {
s.folder_confs.contains_key(path) && s.folder_confs[path].subscribe.is_true()
}),
);
let mut ref_folders: FnvHashMap<FolderHash, Folder> = backend.folders();
let notify_fn = Arc::new(notify_fn);
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
if data.exists() {
let reader = io::BufReader::new(fs::File::open(data).unwrap());
let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
if let Ok(data_t) = result {
data_t
} else {
AddressBook::new(name.clone())
}
} else {
AddressBook::new(name.clone())
}
} else {
AddressBook::new(name.clone())
};
let mut ret = Account {
index,
name,
is_online: false,
folders: Default::default(),
folder_confs: Default::default(),
folders_order: Default::default(),
folder_names: Default::default(),
tree: Default::default(),
address_book,
sent_folder: Default::default(),
collection: Default::default(),
workers: Default::default(),
work_context,
runtime_settings: settings.clone(),
settings,
backend,
notify_fn,
event_queue: VecDeque::with_capacity(8),
};
ret.is_online();
ret
}
fn init(&mut self) {
let mut ref_folders: FnvHashMap<FolderHash, Folder> = self.backend.folders();
let mut folders: FnvHashMap<FolderHash, MailboxEntry> =
FnvHashMap::with_capacity_and_hasher(ref_folders.len(), Default::default());
let mut folders_order: Vec<FolderHash> = Vec::with_capacity(ref_folders.len());
let mut workers: FnvHashMap<FolderHash, Worker> = FnvHashMap::default();
let notify_fn = Arc::new(notify_fn);
let mut folder_names = FnvHashMap::default();
let mut folder_confs = FnvHashMap::default();
let mut sent_folder = None;
for f in ref_folders.values_mut() {
if !settings.folder_confs.contains_key(f.path())
|| settings.folder_confs[f.path()].subscribe.is_false()
if !self.settings.folder_confs.contains_key(f.path())
|| self.settings.folder_confs[f.path()].subscribe.is_false()
{
/* Skip unsubscribed folder */
continue;
}
match settings.folder_confs[f.path()].usage {
match self.settings.folder_confs[f.path()].usage {
Some(SpecialUseMailbox::Sent) => {
sent_folder = Some(f.hash());
}
_ => {}
}
folder_confs.insert(f.hash(), settings.folder_confs[f.path()].clone());
folder_confs.insert(f.hash(), self.settings.folder_confs[f.path()].clone());
folder_names.insert(f.hash(), f.path().to_string());
}
@ -240,8 +286,8 @@ impl Account {
let mut tree: Vec<FolderNode> = Vec::new();
let mut collection: Collection = Collection::new(Default::default());
for (h, f) in ref_folders.iter() {
if !settings.folder_confs.contains_key(f.path())
|| settings.folder_confs[f.path()].subscribe.is_false()
if !self.settings.folder_confs.contains_key(f.path())
|| self.settings.folder_confs[f.path()].subscribe.is_false()
{
/* Skip unsubscribed folder */
continue;
@ -274,7 +320,13 @@ impl Account {
);
workers.insert(
*h,
Account::new_worker(&settings, f.clone(), &mut backend, notify_fn.clone()),
Account::new_worker(
&self.settings,
f.clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
),
);
collection.threads.insert(*h, Threads::default());
}
@ -292,47 +344,21 @@ impl Account {
}
}
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
if data.exists() {
let reader = io::BufReader::new(fs::File::open(data).unwrap());
let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
if let Ok(data_t) = result {
data_t
} else {
AddressBook::new(name.clone())
}
} else {
AddressBook::new(name.clone())
}
} else {
AddressBook::new(name.clone())
};
Account {
index,
name,
folders,
folder_confs,
folders_order,
folder_names,
tree,
address_book,
sent_folder,
collection,
workers,
settings: settings.clone(),
runtime_settings: settings,
backend,
notify_fn,
event_queue: VecDeque::with_capacity(8),
}
self.folders = folders;
self.folder_confs = folder_confs;
self.folders_order = folders_order;
self.folder_names = folder_names;
self.tree = tree;
self.sent_folder = sent_folder;
self.collection = collection;
self.workers = workers;
}
fn new_worker(
settings: &AccountConf,
folder: Folder,
backend: &mut Box<dyn MailBackend>,
work_context: &WorkContext,
notify_fn: Arc<NotifyFn>,
) -> Worker {
let mailbox_handle = backend.get(&folder);
@ -361,7 +387,7 @@ impl Account {
* threads' closures this could be avoided, but it requires green threads.
*/
builder.set_priority(priority).set_is_static(true);
let w = builder.build(Box::new(move |work_context| {
let mut w = builder.build(Box::new(move |work_context| {
let name = format!("Parsing {}", folder.path());
let mut mailbox_handle = mailbox_handle.clone();
let work = mailbox_handle.work().unwrap();
@ -397,6 +423,9 @@ impl Account {
}
}
}));
if let Some(w) = w.work() {
work_context.new_work.send(w).unwrap();
}
Some(w)
}
pub fn reload(
@ -485,6 +514,7 @@ impl Account {
&self.settings,
ref_folders[&folder_hash].clone(),
&mut self.backend,
&self.work_context,
self.notify_fn.clone(),
);
self.workers.insert(folder_hash, handle);
@ -758,6 +788,15 @@ impl Account {
.find(|(_, f)| f.usage == Some(special_use));
ret.as_ref().map(|r| r.0.as_str())
}
pub fn is_online(&mut self) -> bool {
let ret = self.backend.is_online();
if ret != self.is_online && ret {
self.init();
}
self.is_online = ret;
ret
}
}
impl Index<FolderHash> for Account {

View File

@ -171,6 +171,8 @@ impl State {
let termrows = termsize.map(|(_, h)| h);
let cols = termcols.unwrap_or(0) as usize;
let rows = termrows.unwrap_or(0) as usize;
let work_controller = WorkController::new(sender.clone());
let mut accounts: Vec<Account> = settings
.accounts
.iter()
@ -182,6 +184,7 @@ impl State {
n.to_string(),
a_s.clone(),
&backends,
work_controller.get_context(),
NotifyFn::new(Box::new(move |f: FolderHash| {
sender
.send(ThreadEvent::UIEvent(UIEvent::StartupCheck(f)))
@ -210,7 +213,7 @@ impl State {
dirty_areas: VecDeque::with_capacity(5),
replies: VecDeque::with_capacity(5),
temp_files: Vec::new(),
work_controller: WorkController::new(sender.clone()),
work_controller,
sender,
receiver,
@ -225,16 +228,6 @@ impl State {
s.grid.set_ascii_drawing(true);
}
for a in s.context.accounts.iter_mut() {
for worker in a.workers.values_mut() {
if let Some(worker) = worker.as_mut() {
if let Some(w) = worker.work() {
s.context.work_controller.queue.add_work(w);
}
}
}
}
s.switch_to_alternate_screen();
debug!("inserting mailbox hashes:");
{