mirror of
https://github.com/FliegendeWurst/inboxid.git
synced 2024-11-22 00:45:01 +00:00
browse: mark mails as read or unread
This commit is contained in:
parent
95b6abc572
commit
480987bf6c
@ -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() {
|
||||||
@ -52,24 +54,6 @@ fn show_listing(mailbox: &str) -> Result<()> {
|
|||||||
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();
|
||||||
for i in 0..mails.len() {
|
for i in 0..mails.len() {
|
||||||
@ -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>
|
||||||
|
@ -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('+');
|
||||||
|
@ -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()])?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
48
src/lib.rs
48
src/lib.rs
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user