summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorManos Pitsidianakis <el13635@mail.ntua.gr>2019-09-20 09:21:12 +0300
committerManos Pitsidianakis <el13635@mail.ntua.gr>2019-09-20 10:26:08 +0300
commitfb7b038ee1ab9b8b762890190a88eb324ecc92f8 (patch)
tree23c66a576e722ccf080cab91559bfd501b190680
parentfada0ffce132fad149bd85f05df94a35dd080631 (diff)
ui: add set_seen shortcut in {Compact,Conversation}
Shortcut sets an entire thread as seen.
-rw-r--r--melib/src/thread.rs21
-rw-r--r--ui/src/components/mail/listing/conversations.rs172
-rw-r--r--ui/src/conf/shortcuts.rs4
3 files changed, 131 insertions, 66 deletions
diff --git a/melib/src/thread.rs b/melib/src/thread.rs
index ed2c8adb..0daa2ddf 100644
--- a/melib/src/thread.rs
+++ b/melib/src/thread.rs
@@ -417,10 +417,25 @@ impl ThreadNode {
self.has_unseen
}
+ pub fn set_has_unseen(&mut self, new_val: bool) {
+ self.has_unseen = new_val;
+ }
+
pub fn len(&self) -> usize {
self.len
}
+ pub fn date(&self) -> UnixTimestamp {
+ self.date
+ }
+
+ pub fn datetime(&self) -> chrono::DateTime<chrono::Utc> {
+ use chrono::{TimeZone, Utc};
+ use std::convert::TryInto;
+
+ Utc.timestamp(self.date.try_into().unwrap_or(0), 0)
+ }
+
pub fn is_empty(&self) -> bool {
self.parent.is_none() && self.message.is_none() && self.children.is_empty()
}
@@ -575,12 +590,12 @@ impl<'a> Iterator for RootIterator<'a> {
}
}
-fn find_ref(buf: &FnvHashMap<ThreadHash, ThreadNode>, h: ThreadHash) -> ThreadHash {
+pub fn find_thread_group(buf: &FnvHashMap<ThreadHash, ThreadNode>, h: ThreadHash) -> ThreadHash {
if buf[&h].thread_group == h {
return h;
}
let p = buf[&h].thread_group;
- find_ref(buf, p)
+ find_thread_group(buf, p)
}
fn find(buf: &mut FnvHashMap<ThreadHash, ThreadNode>, h: ThreadHash) -> ThreadHash {
if buf[&h].thread_group == h {
@@ -617,7 +632,7 @@ fn union(buf: &mut FnvHashMap<ThreadHash, ThreadNode>, x: ThreadHash, y: ThreadH
impl Threads {
pub fn is_snoozed(&self, h: ThreadHash) -> bool {
- let root = find_ref(&self.thread_nodes, h);
+ let root = find_thread_group(&self.thread_nodes, h);
self.thread_nodes[&root].snoozed()
}
pub fn find(&mut self, i: ThreadHash) -> ThreadHash {
diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs
index 5c8c311c..716f0882 100644
--- a/ui/src/components/mail/listing/conversations.rs
+++ b/ui/src/components/mail/listing/conversations.rs
@@ -21,6 +21,7 @@
use super::*;
use crate::components::utilities::PageMovement;
+use std::iter::FromIterator;
use std::ops::{Deref, DerefMut};
const MAX_COLS: usize = 500;
@@ -892,6 +893,71 @@ impl ConversationsListing {
self.filtered_selection[cursor]
}
}
+
+ fn perform_action(
+ &mut self,
+ context: &mut Context,
+ thread_hash: ThreadHash,
+ a: &ListingAction,
+ ) {
+ let account = &mut context.accounts[self.cursor_pos.0];
+ let mut envs_to_set: StackVec<EnvelopeHash> = StackVec::new();
+ {
+ let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
+ let mut stack = StackVec::new();
+ stack.push(thread_hash);
+ while let Some(thread_iter) = stack.pop() {
+ {
+ let threads = account.collection.threads.get_mut(&folder_hash).unwrap();
+ threads
+ .thread_nodes
+ .entry(thread_iter)
+ .and_modify(|t| t.set_has_unseen(false));
+ }
+ let threads = &account.collection.threads[&folder_hash];
+ if let Some(env_hash) = threads[&thread_iter].message() {
+ if !account.contains_key(env_hash) {
+ /* The envelope has been renamed or removed, so wait for the appropriate event to
+ * arrive */
+ continue;
+ }
+ envs_to_set.push(env_hash);
+ }
+ for c in 0..threads[&thread_iter].children().len() {
+ let c = threads[&thread_iter].children()[c];
+ stack.push(c);
+ }
+ }
+ }
+ for env_hash in envs_to_set {
+ match a {
+ 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) {
+ context.replies.push_back(UIEvent::StatusEvent(
+ StatusEvent::DisplayMessage(e.to_string()),
+ ));
+ }
+ self.row_updates.push(thread_hash);
+ }
+ 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) {
+ context.replies.push_back(UIEvent::StatusEvent(
+ StatusEvent::DisplayMessage(e.to_string()),
+ ));
+ }
+ self.row_updates.push(thread_hash);
+ }
+ ListingAction::Delete => { /* do nothing */ }
+ _ => unreachable!(),
+ }
+ }
+ }
}
impl Component for ConversationsListing {
@@ -1074,6 +1140,12 @@ impl Component for ConversationsListing {
self.movement = Some(PageMovement::End);
self.set_dirty();
}
+ UIEvent::Input(ref key) if *key == shortcuts["set_seen"] => {
+ let thread_hash = self.get_thread_under_cursor(self.cursor_pos.2, context);
+ self.perform_action(context, thread_hash, &ListingAction::SetSeen);
+ self.row_updates.push(thread_hash);
+ self.set_dirty();
+ }
UIEvent::Input(ref k) if self.unfocused && *k == shortcuts["exit_thread"] => {
self.unfocused = false;
self.dirty = true;
@@ -1116,14 +1188,35 @@ impl Component for ConversationsListing {
if !threads.thread_nodes.contains_key(&new_env_thread_hash) {
return false;
}
- let thread_group = threads.thread_nodes[&new_env_thread_hash].thread_group();
- let (&thread_hash, _): (&ThreadHash, &usize) = self
+ let thread_group = melib::find_thread_group(
+ &threads.thread_nodes,
+ threads.thread_nodes[&new_env_thread_hash].thread_group(),
+ );
+ let (&thread_hash, &row): (&ThreadHash, &usize) = self
.order
.iter()
- .find(|(n, _)| threads.thread_nodes[&n].thread_group() == thread_group)
+ .find(|(n, _)| {
+ melib::find_thread_group(
+ &threads.thread_nodes,
+ threads.thread_nodes[&n].thread_group(),
+ ) == thread_group
+ })
.unwrap();
- self.row_updates.push(thread_hash);
+ let new_thread_hash = threads.root_set(row);
+ self.row_updates.push(new_thread_hash);
+ if let Some(row) = self.order.remove(&thread_hash) {
+ self.order.insert(new_thread_hash, row);
+ let selection_status = self.selection.remove(&thread_hash).unwrap();
+ self.selection.insert(new_thread_hash, selection_status);
+ for h in self.filtered_selection.iter_mut() {
+ if *h == thread_hash {
+ *h = new_thread_hash;
+ break;
+ }
+ }
+ }
+
self.dirty = true;
self.view
@@ -1182,10 +1275,6 @@ impl Component for ConversationsListing {
| Action::Listing(a @ ListingAction::Delete)
if !self.unfocused =>
{
- /* Iterate over selection if exists, else only over the envelope under the
- * cursor. Using two iterators allows chaining them which results into a Chain
- * type. We can't conditonally select either a slice iterator or a Map iterator
- * because of the type system */
let is_selection_empty =
self.selection.values().cloned().any(std::convert::identity);
let i = [self.get_thread_under_cursor(self.cursor_pos.2, context)];
@@ -1200,60 +1289,11 @@ impl Component for ConversationsListing {
let iter = sel_iter
.into_iter()
.flatten()
- .chain(cursor_iter.into_iter().flatten());
- for &i in iter {
- let account = &mut context.accounts[self.cursor_pos.0];
- let mut envs_to_set: StackVec<EnvelopeHash> = StackVec::new();
- {
- let folder_hash = account[self.cursor_pos.1].unwrap().folder.hash();
- let threads = &account.collection.threads[&folder_hash];
- let mut stack = StackVec::new();
- stack.push(i);
- while let Some(thread_iter) = stack.pop() {
- if let Some(env_hash) = threads[&thread_iter].message() {
- if !account.contains_key(env_hash) {
- /* The envelope has been renamed or removed, so wait for the appropriate event to
- * arrive */
- continue;
- }
- envs_to_set.push(env_hash);
- }
- for c in 0..threads[&thread_iter].children().len() {
- let c = threads[&thread_iter].children()[c];
- stack.push(c);
- }
- }
- }
- for env_hash in envs_to_set {
- match a {
- 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) {
- context.replies.push_back(UIEvent::StatusEvent(
- StatusEvent::DisplayMessage(e.to_string()),
- ));
- }
- self.row_updates.push(i);
- }
- 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) {
- context.replies.push_back(UIEvent::StatusEvent(
- StatusEvent::DisplayMessage(e.to_string()),
- ));
- }
- self.row_updates.push(i);
- }
- ListingAction::Delete => { /* do nothing */ }
- _ => unreachable!(),
- }
- }
+ .chain(cursor_iter.into_iter().flatten())
+ .cloned();
+ let stack = StackVec::from_iter(iter.into_iter());
+ for i in stack {
+ self.perform_action(context, i, a);
}
self.dirty = true;
for v in self.selection.values_mut() {
@@ -1344,6 +1384,14 @@ impl Component for ConversationsListing {
Key::Char('v')
},
),
+ (
+ "set_seen",
+ if let Some(key) = config_map.get("set_seen") {
+ (*key).clone()
+ } else {
+ Key::Char('n')
+ },
+ ),
]
.iter()
.cloned()
diff --git a/ui/src/conf/shortcuts.rs b/ui/src/conf/shortcuts.rs
index 6ff569cb..370cc79f 100644
--- a/ui/src/conf/shortcuts.rs
+++ b/ui/src/conf/shortcuts.rs
@@ -51,7 +51,8 @@ shortcut_key_values! { "compact_listing",
/// Shortcut listing for a mail listing in compact mode.
pub struct CompactListingShortcuts {
open_thread: Key |> "Open thread.",
- exit_thread: Key |> "Exit thread view."
+ exit_thread: Key |> "Exit thread view.",
+ set_seen: Key |> "Set thread as seen."
}
}
@@ -60,6 +61,7 @@ impl Default for CompactListingShortcuts {
CompactListingShortcuts {
open_thread: Key::Char('\n'),
exit_thread: Key::Char('i'),
+ set_seen: Key::Char('n'),
}
}
}