From 480987bf6cd424db7dc63104646ca1c97a7f5e96 Mon Sep 17 00:00:00 2001 From: FliegendeWurst <2012gdwu+github@posteo.de> Date: Thu, 8 Apr 2021 20:00:26 +0200 Subject: [PATCH] browse: mark mails as read or unread --- src/bin/browse.rs | 55 ++++++++++++++++++++++++------------------- src/bin/list.rs | 2 +- src/bin/rebuild-db.rs | 2 +- src/lib.rs | 48 +++++++++++++++++++++++++++++++++---- 4 files changed, 76 insertions(+), 31 deletions(-) diff --git a/src/bin/browse.rs b/src/bin/browse.rs index c0e8356..fd4ddde 100644 --- a/src/bin/browse.rs +++ b/src/bin/browse.rs @@ -1,6 +1,6 @@ #![feature(internal_output_capture)] -use std::{array::IntoIter, cell::RefCell, cmp, collections::{HashMap, HashSet}, env, fmt::Display, io, sync::{Arc, Mutex}}; +use std::{cell::RefCell, cmp, collections::{HashMap, HashSet}, env, fmt::Display, io, sync::{Arc, Mutex}}; use cursive::{Cursive, CursiveExt, Vec2}; use cursive::align::HAlign; @@ -11,6 +11,7 @@ use cursive::views::{Checkbox, LinearLayout, OnEventView, Panel, ResizedView, Se use cursive_tree_view::{Placement, TreeEntry, TreeView}; use inboxid::*; use io::Write; +use imap::types::Flag; use itertools::Itertools; use log::error; use mailparse::{MailHeaderMap, ParsedMail}; @@ -41,7 +42,8 @@ fn main() -> Result<()> { } fn show_listing(mailbox: &str) -> Result<()> { - let maildir = get_maildir(mailbox)?; + let maildir = Box::leak(Box::new(get_maildir(mailbox)?)); + let maildir = &*maildir; let mut mails = Vec::new(); for x in maildir.list_cur() { @@ -51,24 +53,6 @@ fn show_listing(mailbox: &str) -> Result<()> { let mut mails = maildir.get_mails2(mails)?; mails.sort_by_key(|x| x.date); let mails = Box::leak(Box::new(mails.into_iter().map(Box::new).map(Box::leak).collect_vec())); - - let mut rows = Vec::new(); - for (i, mail) in mails.iter().enumerate() { - let flags = &mail.flags; - let mut flags_display = String::new(); - if flags.contains('F') { - flags_display.push('+'); - } - if flags.contains('R') { - flags_display.push('R'); - } - if flags.contains('S') { - flags_display.push(' '); - } else { - flags_display.push('*'); - } - rows.push(IntoIter::new([(mails.len() - i).to_string(), flags_display, mail.from(), mail.subject.clone(), mail.date_iso.clone()])); - } let mut mails_by_id = HashMap::new(); let mut threads: HashMap<_, Vec<_>> = HashMap::new(); @@ -179,7 +163,7 @@ fn show_listing(mailbox: &str) -> Result<()> { (false, 0) }; let tree_on_select = |siv: &mut Cursive, row| { - let item = siv.call_on_name("tree", |tree: &mut TreeView<&EasyMail>| { + let item = siv.call_on_name("tree", |tree: &mut MailTreeView| { *tree.borrow_item(row).unwrap() }).unwrap(); if item.is_pseudo() { @@ -226,13 +210,13 @@ fn show_listing(mailbox: &str) -> Result<()> { } part_to_display }).unwrap() { + siv.call_on_name("mail_info", |view: &mut MailInfoView| { + view.set(item); + }); siv.call_on_name("mail", |view: &mut TextView| { view.set_content(mail.get_body().unwrap()); }); } - siv.call_on_name("mail_info", |view: &mut MailInfoView| { - view.set(item); - }); }; tree.set_on_select(tree_on_select); tree.set_on_submit(|siv, _row| { @@ -240,6 +224,27 @@ fn show_listing(mailbox: &str) -> Result<()> { }); let mut tree = tree.with_name("tree").scrollable(); tree.set_scroll_strategy(ScrollStrategy::StickToBottom); + let tree = OnEventView::new(tree) + .on_event('r', move |siv| { + siv.call_on_name("tree", |tree: &mut MailTreeView| { + if let Some(r) = tree.row() { + let mail = tree.borrow_item_mut(r).unwrap(); + mail.add_flag(Flag::Seen); + mail.remove_flag2('U'); + let _ = mail.save_flags(&maildir); + } + }); + }) + .on_event('u', move |siv| { + siv.call_on_name("tree", |tree: &mut MailTreeView| { + if let Some(r) = tree.row() { + let mail = tree.borrow_item_mut(r).unwrap(); + mail.remove_flag(Flag::Seen); + mail.add_flag2('U'); + let _ = mail.save_flags(&maildir); + } + }); + }); let tree_resized = ResizedView::new(SizeConstraint::AtMost(120), SizeConstraint::Free, tree); let mail_info = MailInfoView::new().with_name("mail_info"); let mail_content = TextView::new("").with_name("mail").scrollable(); @@ -321,6 +326,8 @@ fn show_listing(mailbox: &str) -> Result<()> { Ok(()) } +type MailTreeView<'a> = TreeView<&'a EasyMail<'a>>; + #[derive(Debug)] struct MailPart { part: &'static ParsedMail<'static> diff --git a/src/bin/list.rs b/src/bin/list.rs index dc1167a..b12fbd9 100644 --- a/src/bin/list.rs +++ b/src/bin/list.rs @@ -27,7 +27,7 @@ fn show_listing(mailbox: &str) -> Result<()> { let mut rows = Vec::new(); for (i, mail) in mails.iter().enumerate() { - let flags = &mail.flags; + let flags = &mail.get_flags(); let mut flags_display = String::new(); if flags.contains('F') { flags_display.push('+'); diff --git a/src/bin/rebuild-db.rs b/src/bin/rebuild-db.rs index 74cbcef..7ce0ad7 100644 --- a/src/bin/rebuild-db.rs +++ b/src/bin/rebuild-db.rs @@ -29,7 +29,7 @@ fn main() -> Result<()> { if message_id.is_empty() { message_id = format!("<{}_{}_{}@no-message-id>", mailbox, mail.id.uid_validity, mail.id.uid); } - save_mail.execute(params![&mailbox, mail.id.to_i64(), message_id, mail.flags])?; + save_mail.execute(params![&mailbox, mail.id.to_i64(), message_id, mail.get_flags()])?; } } } diff --git a/src/lib.rs b/src/lib.rs index 31c62e5..9691914 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -105,7 +105,7 @@ impl MaildirID { pub struct EasyMail<'a> { mail: Option>, pub id: MaildirID, - pub flags: String, + flags: RwLock, from: Option, from_raw: String, pub subject: String, @@ -118,7 +118,7 @@ impl EasyMail<'_> { Self { mail: None, id: MaildirID::new(0, 0), - flags: "S".to_owned(), + flags: "S".to_owned().into(), from: None, from_raw: String::new(), subject, @@ -145,6 +145,36 @@ impl EasyMail<'_> { } } + pub fn has_flag(&self, flag: &Flag) -> bool { + self.flags.read().contains(imap_flag_to_maildir(flag).unwrap()) + } + + pub fn add_flag(&self, flag: Flag) { + self.flags.write().push(imap_flag_to_maildir(&flag).unwrap()); + } + + pub fn add_flag2(&self, flag: char) { + self.flags.write().push(flag); + } + + pub fn remove_flag(&self, flag: Flag) { + self.remove_flag2(imap_flag_to_maildir(&flag).unwrap()); + } + + pub fn remove_flag2(&self, flag: char) { + let mut f = self.flags.write(); + *f = f.replace(flag, ""); + } + + pub fn save_flags(&self, maildir: &Maildir) -> Result<()> { + maildir.set_flags(&self.id.to_string(), &self.flags.read())?; + Ok(()) + } + + pub fn get_flags(&self) -> String { + self.flags.read().clone() + } + pub fn get_header(&self, header: &str) -> String { self.get_headers().get_all_values(header).join(" ") } @@ -217,7 +247,7 @@ impl TreeEntry for &EasyMail<'_> { line.push(' '); line += &self.date_iso; - let style = if self.flags.contains('S') { Style::default() } else { CONFIG.get().unwrap().read().browse.unread_style }; + let style = if self.has_flag(&Flag::Seen) { Style::default() } else { CONFIG.get().unwrap().read().browse.unread_style }; let spans = vec![ IndexedSpan { content: IndexedCow::Borrowed { @@ -341,7 +371,7 @@ impl MaildirExtension for Maildir { )?; mails.push(EasyMail { mail: Some(mail), - flags, + flags: flags.into(), id, from, from_raw, @@ -370,7 +400,7 @@ impl MaildirExtension for Maildir { )?; mails.push(EasyMail { mail: Some(mail), - flags, + flags: flags.into(), id, from, from_raw, @@ -563,3 +593,11 @@ pub fn imap_flags_to_maildir(mut f: String, flags: &[Flag]) -> String { } f } + +pub fn imap_flag_to_maildir(flag: &Flag) -> Option { + match flag { + Flag::Seen => Some('S'), + Flag::Answered => Some('R'), + _ => None + } +}