ui: add set_seen shortcut in {Compact,Conversation}
Shortcut sets an entire thread as seen.embed
parent
fada0ffce1
commit
fb7b038ee1
|
@ -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 {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue