browse: mark mails as read or unread

This commit is contained in:
FliegendeWurst 2021-04-08 20:00:26 +02:00 committed by Arne Keller
parent 95b6abc572
commit 480987bf6c
4 changed files with 76 additions and 31 deletions

View File

@ -1,6 +1,6 @@
#![feature(internal_output_capture)] #![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::{Cursive, CursiveExt, Vec2};
use cursive::align::HAlign; 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 cursive_tree_view::{Placement, TreeEntry, TreeView};
use inboxid::*; use inboxid::*;
use io::Write; use io::Write;
use imap::types::Flag;
use itertools::Itertools; use itertools::Itertools;
use log::error; use log::error;
use mailparse::{MailHeaderMap, ParsedMail}; use mailparse::{MailHeaderMap, ParsedMail};
@ -41,7 +42,8 @@ fn main() -> Result<()> {
} }
fn show_listing(mailbox: &str) -> 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(); let mut mails = Vec::new();
for x in maildir.list_cur() { for x in maildir.list_cur() {
@ -51,24 +53,6 @@ fn show_listing(mailbox: &str) -> Result<()> {
let mut mails = maildir.get_mails2(mails)?; let mut mails = maildir.get_mails2(mails)?;
mails.sort_by_key(|x| x.date); 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 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 mails_by_id = HashMap::new();
let mut threads: HashMap<_, Vec<_>> = HashMap::new(); let mut threads: HashMap<_, Vec<_>> = HashMap::new();
@ -179,7 +163,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
(false, 0) (false, 0)
}; };
let tree_on_select = |siv: &mut Cursive, row| { 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() *tree.borrow_item(row).unwrap()
}).unwrap(); }).unwrap();
if item.is_pseudo() { if item.is_pseudo() {
@ -226,13 +210,13 @@ fn show_listing(mailbox: &str) -> Result<()> {
} }
part_to_display part_to_display
}).unwrap() { }).unwrap() {
siv.call_on_name("mail_info", |view: &mut MailInfoView| {
view.set(item);
});
siv.call_on_name("mail", |view: &mut TextView| { siv.call_on_name("mail", |view: &mut TextView| {
view.set_content(mail.get_body().unwrap()); 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_select(tree_on_select);
tree.set_on_submit(|siv, _row| { tree.set_on_submit(|siv, _row| {
@ -240,6 +224,27 @@ fn show_listing(mailbox: &str) -> Result<()> {
}); });
let mut tree = tree.with_name("tree").scrollable(); let mut tree = tree.with_name("tree").scrollable();
tree.set_scroll_strategy(ScrollStrategy::StickToBottom); 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 tree_resized = ResizedView::new(SizeConstraint::AtMost(120), SizeConstraint::Free, tree);
let mail_info = MailInfoView::new().with_name("mail_info"); let mail_info = MailInfoView::new().with_name("mail_info");
let mail_content = TextView::new("").with_name("mail").scrollable(); let mail_content = TextView::new("").with_name("mail").scrollable();
@ -321,6 +326,8 @@ fn show_listing(mailbox: &str) -> Result<()> {
Ok(()) Ok(())
} }
type MailTreeView<'a> = TreeView<&'a EasyMail<'a>>;
#[derive(Debug)] #[derive(Debug)]
struct MailPart { struct MailPart {
part: &'static ParsedMail<'static> part: &'static ParsedMail<'static>

View File

@ -27,7 +27,7 @@ fn show_listing(mailbox: &str) -> Result<()> {
let mut rows = Vec::new(); let mut rows = Vec::new();
for (i, mail) in mails.iter().enumerate() { for (i, mail) in mails.iter().enumerate() {
let flags = &mail.flags; let flags = &mail.get_flags();
let mut flags_display = String::new(); let mut flags_display = String::new();
if flags.contains('F') { if flags.contains('F') {
flags_display.push('+'); flags_display.push('+');

View File

@ -29,7 +29,7 @@ fn main() -> Result<()> {
if message_id.is_empty() { if message_id.is_empty() {
message_id = format!("<{}_{}_{}@no-message-id>", mailbox, mail.id.uid_validity, mail.id.uid); 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()])?;
} }
} }
} }

View File

@ -105,7 +105,7 @@ impl MaildirID {
pub struct EasyMail<'a> { pub struct EasyMail<'a> {
mail: Option<ParsedMail<'a>>, mail: Option<ParsedMail<'a>>,
pub id: MaildirID, pub id: MaildirID,
pub flags: String, flags: RwLock<String>,
from: Option<SingleInfo>, from: Option<SingleInfo>,
from_raw: String, from_raw: String,
pub subject: String, pub subject: String,
@ -118,7 +118,7 @@ impl EasyMail<'_> {
Self { Self {
mail: None, mail: None,
id: MaildirID::new(0, 0), id: MaildirID::new(0, 0),
flags: "S".to_owned(), flags: "S".to_owned().into(),
from: None, from: None,
from_raw: String::new(), from_raw: String::new(),
subject, 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 { pub fn get_header(&self, header: &str) -> String {
self.get_headers().get_all_values(header).join(" ") self.get_headers().get_all_values(header).join(" ")
} }
@ -217,7 +247,7 @@ impl TreeEntry for &EasyMail<'_> {
line.push(' '); line.push(' ');
line += &self.date_iso; 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![ let spans = vec![
IndexedSpan { IndexedSpan {
content: IndexedCow::Borrowed { content: IndexedCow::Borrowed {
@ -341,7 +371,7 @@ impl MaildirExtension for Maildir {
)?; )?;
mails.push(EasyMail { mails.push(EasyMail {
mail: Some(mail), mail: Some(mail),
flags, flags: flags.into(),
id, id,
from, from,
from_raw, from_raw,
@ -370,7 +400,7 @@ impl MaildirExtension for Maildir {
)?; )?;
mails.push(EasyMail { mails.push(EasyMail {
mail: Some(mail), mail: Some(mail),
flags, flags: flags.into(),
id, id,
from, from,
from_raw, from_raw,
@ -563,3 +593,11 @@ pub fn imap_flags_to_maildir(mut f: String, flags: &[Flag]) -> String {
} }
f f
} }
pub fn imap_flag_to_maildir(flag: &Flag) -> Option<char> {
match flag {
Flag::Seen => Some('S'),
Flag::Answered => Some('R'),
_ => None
}
}