CompactListing: add select command

Select envelopes based on query
memfd
Manos Pitsidianakis 2020-07-17 00:03:35 +03:00
parent 0d3fe288c5
commit 5e1fa2d8d7
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
3 changed files with 111 additions and 2 deletions

View File

@ -67,6 +67,11 @@ pub struct CompactListing {
oneshot::Receiver<Result<SmallVec<[EnvelopeHash; 512]>>>,
JobId,
)>,
select_job: Option<(
String,
oneshot::Receiver<Result<SmallVec<[EnvelopeHash; 512]>>>,
JobId,
)>,
filter_term: String,
filtered_selection: Vec<ThreadHash>,
filtered_order: HashMap<ThreadHash, usize>,
@ -112,6 +117,8 @@ impl MailListingTrait for CompactListing {
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
self.dirty = true;
self.all_threads.clear();
self.selection.clear();
let old_cursor_pos = self.cursor_pos;
if !(self.cursor_pos.0 == self.new_cursor_pos.0
&& self.cursor_pos.1 == self.new_cursor_pos.1)
@ -172,7 +179,6 @@ impl MailListingTrait for CompactListing {
}
let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.cursor_pos.1];
self.all_threads.clear();
let mut roots = threads.roots();
threads.group_inner_sort_by(
&mut roots,
@ -203,7 +209,6 @@ impl MailListingTrait for CompactListing {
let threads = &account.collection.threads[&self.cursor_pos.1];
self.order.clear();
self.selection.clear();
self.length = 0;
let mut rows = Vec::with_capacity(1024);
let mut min_width = (0, 0, 0, 0, 0);
@ -849,6 +854,7 @@ impl CompactListing {
all_threads: HashSet::default(),
order: HashMap::default(),
search_job: None,
select_job: None,
filter_term: String::new(),
filtered_selection: Vec::new(),
filtered_order: HashMap::default(),
@ -1280,6 +1286,54 @@ impl CompactListing {
}
}
}
fn select(
&mut self,
search_term: &str,
results: Result<SmallVec<[EnvelopeHash; 512]>>,
context: &mut Context,
) {
let account = &context.accounts[self.cursor_pos.0];
match results {
Ok(results) => {
let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
}
debug!(account.collection.get_env(env_hash).subject());
let env_thread_node_hash = account.collection.get_env(env_hash).thread();
if !threads.thread_nodes.contains_key(&env_thread_node_hash) {
continue;
}
let thread =
threads.find_group(threads.thread_nodes[&env_thread_node_hash].group);
if self.all_threads.contains(&thread) {
self.selection
.entry(thread)
.and_modify(|entry| *entry = true);
}
}
}
Err(err) => {
self.cursor_pos.2 = 0;
self.new_cursor_pos.2 = 0;
let message = format!(
"Encountered an error while searching for `{}`: {}.",
search_term, err
);
log(
format!("Failed to search for term {}: {}", search_term, err),
ERROR,
);
context.replies.push_back(UIEvent::Notification(
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::ERROR),
));
}
}
}
}
impl Component for CompactListing {
@ -1552,6 +1606,35 @@ impl Component for CompactListing {
};
self.set_dirty(true);
}
UIEvent::Action(Action::Listing(Select(ref search_term))) if !self.unfocused => {
match context.accounts[self.cursor_pos.0].search(
search_term,
self.sort,
self.cursor_pos.1,
) {
Ok(job) => {
let (mut chan, handle, job_id) = context.accounts[self.cursor_pos.0]
.job_executor
.spawn_specialized(job);
if let Ok(Some(search_result)) = try_recv_timeout!(&mut chan) {
self.select(search_term, search_result, context);
} else {
context.accounts[self.cursor_pos.0]
.active_jobs
.insert(job_id, crate::conf::accounts::JobRequest::Search(handle));
self.select_job = Some((search_term.to_string(), chan, job_id));
}
}
Err(err) => {
context.replies.push_back(UIEvent::Notification(
Some("Could not perform search".to_string()),
err.to_string(),
Some(crate::types::NotificationType::ERROR),
));
}
};
self.set_dirty(true);
}
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id))
if self
.search_job
@ -1564,6 +1647,18 @@ impl Component for CompactListing {
self.filter(filter_term, results, context);
self.set_dirty(true);
}
UIEvent::StatusEvent(StatusEvent::JobFinished(ref job_id))
if self
.select_job
.as_ref()
.map(|(_, _, j)| j == job_id)
.unwrap_or(false) =>
{
let (search_term, mut rcvr, _job_id) = self.select_job.take().unwrap();
let results = rcvr.try_recv().unwrap().unwrap();
self.select(&search_term, results, context);
self.set_dirty(true);
}
_ => {}
}
false

View File

@ -374,6 +374,18 @@ define_commands!([
}
)
},
{ tags: ["select"],
desc: "select <TERM>, selects envelopes matching with given term",
tokens: &[One(Literal("select")), One(RestOfStringValue)],
parser:(
fn select(input: &[u8]) -> IResult<&[u8], Action> {
let (input, _) = tag("select")(input.trim())?;
let (input, _) = is_a(" ")(input)?;
let (input, string) = map_res(not_line_ending, std::str::from_utf8)(input)?;
Ok((input, Listing(Select(String::from(string)))))
}
)
},
{ tags: ["list-archive", "list-post", "list-unsubscribe", "list-"],
desc: "list-[unsubscribe/post/archive]",
tokens: &[One(Alternatives(&[to_stream!(One(Literal("list-archive"))), to_stream!(One(Literal("list-post"))), to_stream!(One(Literal("list-unsubscribe")))]))],
@ -681,6 +693,7 @@ fn listing_action(input: &[u8]) -> IResult<&[u8], Action> {
delete_message,
copymove,
search,
select,
toggle_thread_snooze,
open_in_new_tab,
_tag,

View File

@ -44,6 +44,7 @@ pub enum ListingAction {
SetCompact,
SetConversations,
Search(String),
Select(String),
SetSeen,
SetUnseen,
CopyTo(MailboxPath),