From 65efb23f1498a6d0b11c933d6f9c4109b7e2c899 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 14 Dec 2019 18:55:46 +0200 Subject: [PATCH] melib/MailBackend: add refresh() method Initiate refresh manually. --- melib/src/backends.rs | 7 ++ melib/src/backends/maildir/backend.rs | 130 ++++++++++++++++++++++++++ melib/src/conf.rs | 2 + ui/src/components/mail/listing.rs | 9 ++ ui/src/conf.rs | 12 ++- ui/src/conf/accounts.rs | 12 +++ ui/src/conf/shortcuts.rs | 1 + 7 files changed, 169 insertions(+), 4 deletions(-) diff --git a/melib/src/backends.rs b/melib/src/backends.rs index fa70d43e..42e09b2c 100644 --- a/melib/src/backends.rs +++ b/melib/src/backends.rs @@ -248,6 +248,13 @@ pub trait MailBackend: ::std::fmt::Debug + Send + Sync { fn is_online(&self) -> Result<()>; fn connect(&mut self) {} fn get(&mut self, folder: &Folder) -> Async>>; + fn refresh( + &mut self, + folder_hash: FolderHash, + sender: RefreshEventConsumer, + ) -> Async>> { + unimplemented!() + } fn watch( &self, sender: RefreshEventConsumer, diff --git a/melib/src/backends/maildir/backend.rs b/melib/src/backends/maildir/backend.rs index 288c7ce7..33b19a90 100644 --- a/melib/src/backends/maildir/backend.rs +++ b/melib/src/backends/maildir/backend.rs @@ -199,6 +199,136 @@ impl MailBackend for MaildirType { fn get(&mut self, folder: &Folder) -> Async>> { self.multicore(4, folder) } + fn refresh( + &mut self, + folder_hash: FolderHash, + sender: RefreshEventConsumer, + ) -> Async>> { + let w = AsyncBuilder::new(); + let cache_dir = xdg::BaseDirectories::with_profile("meli", &self.name).unwrap(); + + let handle = { + let folder: &MaildirFolder = &self.folders[&folder_hash]; + let path: PathBuf = folder.fs_path().into(); + let name = format!("refresh {:?}", folder.name()); + let root_path = self.path.to_path_buf(); + let map = self.hash_indexes.clone(); + let folder_index = self.folder_index.clone(); + let cache_dir = cache_dir.clone(); + let sender = Arc::new(sender); + + Box::new(move |work_context: crate::async_workers::WorkContext| { + let cache_dir = cache_dir.clone(); + let folder_index = folder_index.clone(); + let root_path = root_path.clone(); + let path = path.clone(); + let name = name.clone(); + let map = map.clone(); + let sender = sender.clone(); + work_context + .set_name + .send((std::thread::current().id(), name.clone())) + .unwrap(); + let thunk = move |sender: &RefreshEventConsumer| { + debug!("refreshing"); + let cache_dir = cache_dir.clone(); + let map = map.clone(); + let folder_index = folder_index.clone(); + let folder_hash = folder_hash.clone(); + let root_path = root_path.clone(); + let mut path = path.clone(); + let cache_dir = cache_dir.clone(); + path.push("new"); + for d in path.read_dir()? { + if let Ok(p) = d { + move_to_cur(p.path()).ok().take(); + } + } + path.pop(); + + path.push("cur"); + let iter = path.read_dir()?; + let count = path.read_dir()?.count(); + let mut files: Vec = Vec::with_capacity(count); + for e in iter { + let e = e.and_then(|x| { + let path = x.path(); + Ok(path) + })?; + files.push(e); + } + let mut current_hashes = { + let mut map = map.lock().unwrap(); + let map = map.entry(folder_hash).or_default(); + map.keys().cloned().collect::>() + }; + for file in files { + let hash = get_file_hash(&file); + { + let mut map = map.lock().unwrap(); + let map = map.entry(folder_hash).or_default(); + if map.contains_key(&hash) { + map.remove(&hash); + current_hashes.remove(&hash); + continue; + } + (*map).insert(hash, PathBuf::from(&file).into()); + } + let op = Box::new(MaildirOp::new(hash, map.clone(), folder_hash)); + if let Some(e) = Envelope::from_token(op, hash) { + folder_index.lock().unwrap().insert(e.hash(), folder_hash); + let file_name = PathBuf::from(file) + .strip_prefix(&root_path) + .unwrap() + .to_path_buf(); + if let Ok(cached) = cache_dir.place_cache_file(file_name) { + /* place result in cache directory */ + let f = match fs::File::create(cached) { + Ok(f) => f, + Err(e) => { + panic!("{}", e); + } + }; + let metadata = f.metadata().unwrap(); + let mut permissions = metadata.permissions(); + + permissions.set_mode(0o600); // Read/write for owner only. + f.set_permissions(permissions).unwrap(); + + let writer = io::BufWriter::new(f); + bincode::serialize_into(writer, &e).unwrap(); + } + sender.send(RefreshEvent { + hash: folder_hash, + kind: Create(Box::new(e)), + }); + } else { + debug!( + "DEBUG: hash {}, path: {} couldn't be parsed", + hash, + file.as_path().display() + ); + continue; + } + } + for ev in current_hashes.into_iter().map(|h| RefreshEvent { + hash: folder_hash, + kind: Remove(h), + }) { + sender.send(ev); + } + Ok(()) + }; + if let Err(err) = thunk(&sender) { + sender.send(RefreshEvent { + hash: folder_hash, + kind: Failure(err), + }); + } + }) + }; + w.build(handle) + } fn watch( &self, sender: RefreshEventConsumer, diff --git a/melib/src/conf.rs b/melib/src/conf.rs index 6b5f8298..e6dfcd4a 100644 --- a/melib/src/conf.rs +++ b/melib/src/conf.rs @@ -33,6 +33,8 @@ pub struct AccountSettings { pub subscribed_folders: Vec, #[serde(default)] pub folders: HashMap, + #[serde(default)] + pub manual_refresh: bool, #[serde(flatten)] pub extra: HashMap, } diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index c6825a99..24ed7c94 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -673,6 +673,15 @@ impl Component for Listing { return true; } } + UIEvent::Input(ref key) + if shortcut!(key == shortcuts[Listing::DESCRIPTION]["refresh"]) => + { + let folder_hash = + context.accounts[self.cursor_pos.0].folders_order[self.cursor_pos.1]; + let account = &mut context.accounts[self.cursor_pos.0]; + account.refresh(folder_hash); + return true; + } UIEvent::StartupCheck(_) => { self.dirty = true; context diff --git a/ui/src/conf.rs b/ui/src/conf.rs index aa255da3..9e3fb798 100644 --- a/ui/src/conf.rs +++ b/ui/src/conf.rs @@ -100,10 +100,6 @@ pub struct FileAccount { root_folder: String, format: String, identity: String, - #[serde(flatten)] - #[serde(deserialize_with = "extra_settings")] - pub extra: HashMap, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */ - #[serde(default = "none")] display_name: Option, index_style: IndexStyle, @@ -115,6 +111,11 @@ pub struct FileAccount { folders: HashMap, #[serde(default)] cache_type: CacheType, + #[serde(default)] + pub manual_refresh: bool, + #[serde(flatten)] + #[serde(deserialize_with = "extra_settings")] + pub extra: HashMap, /* use custom deserializer to convert any given value (eg bool, number, etc) to string */ } impl From for AccountConf { @@ -138,6 +139,7 @@ impl From for AccountConf { display_name, subscribed_folders: x.subscribed_folders.clone(), folders, + manual_refresh: x.manual_refresh, extra: x.extra.clone(), }; @@ -344,6 +346,7 @@ impl FileSettings { subscribed_folders, folders, extra, + manual_refresh, index_style: _, cache_type: _, } = acc; @@ -357,6 +360,7 @@ impl FileSettings { read_only, display_name, subscribed_folders, + manual_refresh, folders: folders .into_iter() .map(|(k, v)| (k, v.folder_conf)) diff --git a/ui/src/conf/accounts.rs b/ui/src/conf/accounts.rs index c9c1f97a..b19dd6e6 100644 --- a/ui/src/conf/accounts.rs +++ b/ui/src/conf/accounts.rs @@ -699,7 +699,19 @@ impl Account { } None } + pub fn refresh(&mut self, folder_hash: FolderHash) { + let sender_ = self.sender.clone(); + let r = RefreshEventConsumer::new(Box::new(move |r| { + sender_.send(ThreadEvent::from(r)).unwrap(); + })); + let mut h = self.backend.write().unwrap().refresh(folder_hash, r); + self.work_context.new_work.send(h.work().unwrap()).unwrap(); + } pub fn watch(&self) { + if self.settings.account().manual_refresh { + return; + } + let sender_ = self.sender.clone(); let r = RefreshEventConsumer::new(Box::new(move |r| { sender_.send(ThreadEvent::from(r)).unwrap(); diff --git a/ui/src/conf/shortcuts.rs b/ui/src/conf/shortcuts.rs index 5323a519..9e4b68e6 100644 --- a/ui/src/conf/shortcuts.rs +++ b/ui/src/conf/shortcuts.rs @@ -115,6 +115,7 @@ shortcut_key_values! { "listing", prev_folder |> "Go to previous folder." |> Key::Char('K'), prev_page |> "Go to previous page." |> Key::PageUp, search |> "Search within list of e-mails." |> Key::Char('/'), + refresh |> "Manually request a folder refresh." |> Key::F(5), set_seen |> "Set thread as seen." |> Key::Char('n'), toggle_menu_visibility |> "Toggle visibility of side menu in mail list." |> Key::Char('`') }