summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--melib/src/mailbox/email/mod.rs6
-rw-r--r--melib/src/mailbox/mod.rs9
-rw-r--r--melib/src/mailbox/thread.rs14
-rw-r--r--src/bin.rs2
-rw-r--r--ui/src/components/mail/listing/mod.rs655
-rw-r--r--ui/src/components/mail/listing/plain.rs559
-rw-r--r--ui/src/components/mail/listing/thread.rs630
-rw-r--r--ui/src/execute/actions.rs4
-rw-r--r--ui/src/execute/mod.rs15
-rw-r--r--ui/src/state.rs12
10 files changed, 1286 insertions, 620 deletions
diff --git a/melib/src/mailbox/email/mod.rs b/melib/src/mailbox/email/mod.rs
index 66f54bea..e45fa08d 100644
--- a/melib/src/mailbox/email/mod.rs
+++ b/melib/src/mailbox/email/mod.rs
@@ -630,8 +630,7 @@ impl Envelope {
s.refs.push(new_ref);
}
None => {
- let mut v = Vec::new();
- v.push(new_ref);
+ let v = vec![new_ref];
self.references = Some(References {
raw: "".into(),
refs: v,
@@ -645,10 +644,9 @@ impl Envelope {
s.raw = new_val.into();
}
None => {
- let v = Vec::new();
self.references = Some(References {
raw: new_val.into(),
- refs: v,
+ refs: Vec::new(),
});
}
}
diff --git a/melib/src/mailbox/mod.rs b/melib/src/mailbox/mod.rs
index 010e4aa9..fcc42192 100644
--- a/melib/src/mailbox/mod.rs
+++ b/melib/src/mailbox/mod.rs
@@ -83,6 +83,15 @@ impl Mailbox {
pub fn len(&self) -> usize {
self.collection.len()
}
+ pub fn thread_to_mail_mut(&mut self, i: usize) -> &mut Envelope {
+ self.collection
+ .envelopes
+ .entry(self.collection.threads.thread_to_mail(i))
+ .or_default()
+ }
+ pub fn thread_to_mail(&self, i: usize) -> &Envelope {
+ &self.collection.envelopes[&self.collection.threads.thread_to_mail(i)]
+ }
pub fn threaded_mail(&self, i: usize) -> EnvelopeHash {
self.collection.threads.thread_to_mail(i)
}
diff --git a/melib/src/mailbox/thread.rs b/melib/src/mailbox/thread.rs
index b1e02680..3972b660 100644
--- a/melib/src/mailbox/thread.rs
+++ b/melib/src/mailbox/thread.rs
@@ -151,6 +151,10 @@ impl ThreadNode {
pub fn children(&self) -> &[usize] {
&self.children
}
+
+ pub fn indentation(&self) -> usize {
+ self.indentation
+ }
}
#[derive(Clone, Debug, Default)]
@@ -194,7 +198,7 @@ impl Threads {
if !thread_nodes[*v].has_message() && thread_nodes[*v].children.len() == 1 {
/* Do not promote the children if doing so would promote them to the root set
* -- unless there is only one child, in which case, do. */
- root_set.push(*v);
+ root_set.push(thread_nodes[*v].children[0]);
continue 'root_set;
}
@@ -334,7 +338,13 @@ fn link_threads(
/* The index of the reference we are currently examining, start from current message */
let mut ref_ptr = t_idx;
+ let mut iasf = 0;
for &refn in v.references().iter().rev() {
+ if iasf == 1 {
+ /*FIXME: Skips anything other than direct parents */
+ continue;
+ }
+ iasf += 1;
let r_id = String::from_utf8_lossy(refn.raw()).into();
let parent_id = if message_ids.contains_key(&r_id) {
let parent_id = message_ids[&r_id];
@@ -368,7 +378,6 @@ fn link_threads(
if thread_nodes[ref_ptr].parent.is_none() {
thread_nodes[ref_ptr].parent = Some(thread_nodes.len() - 1);
}
- /* Can't avoid copy here since we have different lifetimes */
message_ids.insert(r_id, thread_nodes.len() - 1);
thread_nodes.len() - 1
};
@@ -401,6 +410,7 @@ impl Index<usize> for Threads {
.expect("thread index out of bounds")
}
}
+
fn node_build(
idx: usize,
thread_nodes: &mut Vec<ThreadNode>,
diff --git a/src/bin.rs b/src/bin.rs
index 1c963c13..d97af8dd 100644
--- a/src/bin.rs
+++ b/src/bin.rs
@@ -62,7 +62,7 @@ fn main() {
/* Register some reasonably useful interfaces */
let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts)));
- let listing = PlainListing::new();
+ let listing = listing::Listing::default();
let b = Entity::from(Box::new(listing));
let tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true))]));
let window = Entity::from(tabs);
diff --git a/ui/src/components/mail/listing/mod.rs b/ui/src/components/mail/listing/mod.rs
index be1a2ef8..d600d33a 100644
--- a/ui/src/components/mail/listing/mod.rs
+++ b/ui/src/components/mail/listing/mod.rs
@@ -24,621 +24,75 @@ use super::*;
mod compact;
pub use self::compact::*;
-const MAX_COLS: usize = 500;
+mod thread;
+pub use self::thread::*;
-/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
-/// `MailView`.
-#[derive(Debug)]
-pub struct PlainListing {
- /// (x, y, z): x is accounts, y is folders, z is index inside a folder.
- cursor_pos: (usize, usize, usize),
- new_cursor_pos: (usize, usize, usize),
- length: usize,
- local_collection: Vec<EnvelopeHash>,
- sort: (SortField, SortOrder),
- subsort: (SortField, SortOrder),
- /// Cache current view.
- content: CellBuffer,
- /// If we must redraw on next redraw event
- dirty: bool,
- /// If `self.view` exists or not.
- unfocused: bool,
- view: Option<MailView>,
-}
+mod plain;
+pub use self::plain::*;
-impl Default for PlainListing {
- fn default() -> Self {
- Self::new()
- }
+#[derive(Debug)]
+pub enum Listing {
+ Plain(PlainListing),
+ Threaded(ThreadListing),
+ Compact(CompactListing),
}
-impl fmt::Display for PlainListing {
+impl fmt::Display for Listing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "mail")
- }
-}
-
-impl PlainListing {
- /// Helper function to format entry strings for PlainListing */
- /* TODO: Make this configurable */
- fn make_entry_string(e: &Envelope, idx: usize) -> String {
- format!(
- "{} {} {}",
- idx,
- &e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
- e.subject()
- )
- }
-
- pub fn new() -> Self {
- let content = CellBuffer::new(0, 0, Cell::with_char(' '));
- PlainListing {
- cursor_pos: (0, 1, 0),
- new_cursor_pos: (0, 0, 0),
- length: 0,
- local_collection: Vec::new(),
- sort: (Default::default(), Default::default()),
- subsort: (Default::default(), Default::default()),
- content,
- dirty: true,
- unfocused: false,
- view: None,
- }
- }
- /// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has
- /// chosen.
- fn refresh_mailbox(&mut self, context: &mut Context) {
- self.dirty = true;
- self.cursor_pos.2 = 0;
- self.new_cursor_pos.2 = 0;
- self.cursor_pos.1 = self.new_cursor_pos.1;
- self.cursor_pos.0 = self.new_cursor_pos.0;
-
- let threaded = context.accounts[self.cursor_pos.0]
- .runtime_settings
- .conf()
- .threaded();
- // Inform State that we changed the current folder view.
- context.replies.push_back(UIEvent {
- id: 0,
- event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)),
- });
- // Get mailbox as a reference.
- //
- loop {
- // TODO: Show progress visually
- if context.accounts[self.cursor_pos.0]
- .status(self.cursor_pos.1)
- .is_ok()
- {
- break;
- }
- }
- let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
- .as_ref()
- .unwrap();
-
- self.length = mailbox.len();
- self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
- if self.length == 0 {
- write_string_to_grid(
- &format!("Folder `{}` is empty.", mailbox.folder.name()),
- &mut self.content,
- Color::Default,
- Color::Default,
- ((0, 0), (MAX_COLS - 1, 0)),
- true,
- );
- return;
- }
-
- // Populate `CellBuffer` with every entry.
- let mut idx = 0;
- for y in 0..=self.length {
- if idx >= self.length {
- /* No more entries left, so fill the rest of the area with empty space */
- clear_area(&mut self.content, ((0, y), (MAX_COLS - 1, self.length)));
- break;
- }
- /* Write an entire line for each envelope entry. */
- self.local_collection = mailbox.collection.keys().map(|v| *v).collect();
- let sort = self.sort;
- self.local_collection.sort_by(|a, b| match sort {
- (SortField::Date, SortOrder::Desc) => {
- let ma = &mailbox.collection[a];
- let mb = &mailbox.collection[b];
- mb.date().cmp(&ma.date())
- }
- (SortField::Date, SortOrder::Asc) => {
- let ma = &mailbox.collection[a];
- let mb = &mailbox.collection[b];
- ma.date().cmp(&mb.date())
- }
- (SortField::Subject, SortOrder::Desc) => {
- let ma = &mailbox.collection[a];
- let mb = &mailbox.collection[b];
- ma.subject().cmp(&mb.subject())
- }
- (SortField::Subject, SortOrder::Asc) => {
- let ma = &mailbox.collection[a];
- let mb = &mailbox.collection[b];
- mb.subject().cmp(&ma.subject())
- }
- });
- let envelope: &Envelope = &mailbox.collection[&self.local_collection[idx]];
-
- let fg_color = if !envelope.is_seen() {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if !envelope.is_seen() {
- Color::Byte(251)
- } else if idx % 2 == 0 {
- Color::Byte(236)
- } else {
- Color::Default
- };
- let (x, y) = write_string_to_grid(
- &PlainListing::make_entry_string(envelope, idx),
- &mut self.content,
- fg_color,
- bg_color,
- ((0, y), (MAX_COLS - 1, y)),
- false,
- );
-
- for x in x..MAX_COLS {
- self.content[(x, y)].set_ch(' ');
- self.content[(x, y)].set_bg(bg_color);
- }
-
- idx += 1;
+ match self {
+ Listing::Compact(l) => write!(f, "{}", l),
+ Listing::Plain(l) => write!(f, "{}", l),
+ Listing::Threaded(l) => write!(f, "{}", l),
}
}
+}
- fn highlight_line_self(&mut self, idx: usize, context: &Context) {
- let threaded = context.accounts[self.cursor_pos.0]
- .runtime_settings
- .conf()
- .threaded();
- let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
- .as_ref()
- .unwrap();
- let envelope: &Envelope = &mailbox.collection[&self.local_collection[idx]];
-
- let fg_color = if !envelope.is_seen() {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if !envelope.is_seen() {
- Color::Byte(251)
- } else if idx % 2 == 0 {
- Color::Byte(236)
- } else {
- Color::Default
- };
- change_colors(
- &mut self.content,
- ((0, idx), (MAX_COLS - 1, idx)),
- fg_color,
- bg_color,
- );
- }
-
- fn highlight_line(&self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context) {
- let threaded = context.accounts[self.cursor_pos.0]
- .runtime_settings
- .conf()
- .threaded();
- let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
- .as_ref()
- .unwrap();
- let envelope: &Envelope = &mailbox.collection[&self.local_collection[idx]];
-
- let fg_color = if !envelope.is_seen() {
- Color::Byte(0)
- } else {
- Color::Default
- };
- let bg_color = if self.cursor_pos.2 == idx {
- Color::Byte(246)
- } else if !envelope.is_seen() {
- Color::Byte(251)
- } else if idx % 2 == 0 {
- Color::Byte(236)
- } else {
- Color::Default
- };
- change_colors(grid, area, fg_color, bg_color);
- }
-
- /// Draw the list of `Envelope`s.
- fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
- if self.cursor_pos.1 != self.new_cursor_pos.1 {
- self.refresh_mailbox(context);
- }
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
- if self.length == 0 {
- clear_area(grid, area);
- copy_area(grid, &self.content, area, ((0, 0), (MAX_COLS - 1, 0)));
- context.dirty_areas.push_back(area);
- return;
- }
- let rows = get_y(bottom_right) - get_y(upper_left) + 1;
- let prev_page_no = (self.cursor_pos.2).wrapping_div(rows);
- let page_no = (self.new_cursor_pos.2).wrapping_div(rows);
-
- let top_idx = page_no * rows;
-
- /* If cursor position has changed, remove the highlight from the previous position and
- * apply it in the new one. */
- if self.cursor_pos.2 != self.new_cursor_pos.2 && prev_page_no == page_no {
- let old_cursor_pos = self.cursor_pos;
- self.cursor_pos = self.new_cursor_pos;
- for idx in &[old_cursor_pos.2, self.new_cursor_pos.2] {
- if *idx >= self.length {
- continue; //bounds check
- }
- let new_area = (
- set_y(upper_left, get_y(upper_left) + (*idx % rows)),
- set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
- );
- self.highlight_line(grid, new_area, *idx, context);
- context.dirty_areas.push_back(new_area);
- }
- return;
- } else if self.cursor_pos != self.new_cursor_pos {
- self.cursor_pos = self.new_cursor_pos;
- }
-
- /* Page_no has changed, so draw new page */
- copy_area(
- grid,
- &self.content,
- area,
- ((0, top_idx), (MAX_COLS - 1, self.length)),
- );
- self.highlight_line(
- grid,
- (
- set_y(upper_left, get_y(upper_left) + (self.cursor_pos.2 % rows)),
- set_y(bottom_right, get_y(upper_left) + (self.cursor_pos.2 % rows)),
- ),
- self.cursor_pos.2,
- context,
- );
- context.dirty_areas.push_back(area);
- }
-
- fn make_thread_entry(
- envelope: &Envelope,
- idx: usize,
- indent: usize,
- node_idx: usize,
- nodes: &Threads,
- indentations: &[bool],
- idx_width: usize,
- //op: Box<BackendOp>,
- ) -> String {
- let has_sibling = nodes.has_sibling(node_idx);
- let has_parent = nodes[node_idx].has_parent();
- let show_subject = nodes[node_idx].show_subject();
-
- let mut s = format!(
- "{}{}{} ",
- idx,
- " ".repeat(idx_width + 2 - (idx.to_string().chars().count())),
- PlainListing::format_date(&envelope)
- );
- for i in 0..indent {
- if indentations.len() > i && indentations[i] {
- s.push('│');
- } else {
- s.push(' ');
- }
- if i > 0 {
- s.push(' ');
- }
- }
- if indent > 0 {
- if has_sibling && has_parent {
- s.push('├');
- } else if has_sibling {
- s.push('┬');
- } else {
- s.push('└');
- }
- s.push('─');
- s.push('>');
- }
-
- if show_subject {
- s.push_str(&format!("{:.85}", envelope.subject()));
- }
- /* TODO Very slow since we have to build all attachments
- let attach_count = envelope.body(op).count_attachments();
- if attach_count > 1 {
- s.push_str(&format!(" {}∞ ", attach_count - 1));
- }
- */
- s
- }
- fn format_date(envelope: &Envelope) -> String {
- let d = std::time::UNIX_EPOCH + std::time::Duration::from_secs(envelope.date());
- let now: std::time::Duration = std::time::SystemTime::now().duration_since(d).unwrap();
- match now.as_secs() {
- n if n < 10 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(8)),
- n if n < 24 * 60 * 60 => format!("{} hours ago{}", n / (60 * 60), " ".repeat(7)),
- n if n < 4 * 24 * 60 * 60 => {
- format!("{} days ago{}", n / (24 * 60 * 60), " ".repeat(9))
- }
- _ => envelope.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
- }
+impl Default for Listing {
+ fn default() -> Self {
+ Listing::Compact(Default::default())
}
}
-impl Component for PlainListing {
+impl Component for Listing {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
- if !self.unfocused {
- if !self.is_dirty() {
- return;
- }
- self.dirty = false;
- /* Draw the entire list */
- self.draw_list(grid, area, context);
- } else {
- let upper_left = upper_left!(area);
- let bottom_right = bottom_right!(area);
- if self.length == 0 && self.dirty {
- clear_area(grid, area);
- context.dirty_areas.push_back(area);
- }
-
- /* Render the mail body in a pager, basically copy what HSplit does */
- let total_rows = get_y(bottom_right) - get_y(upper_left);
- let pager_ratio = context.runtime_settings.pager.pager_ratio;
- let bottom_entity_rows = (pager_ratio * total_rows) / 100;
-
- if bottom_entity_rows > total_rows {
- clear_area(grid, area);
- context.dirty_areas.push_back(area);
- return;
- }
- /* Mark message as read */
- let idx = self.cursor_pos.2;
- let must_highlight = {
- if self.length == 0 {
- false
- } else {
- let threaded = context.accounts[self.cursor_pos.0]
- .runtime_settings
- .conf()
- .threaded();
- let account = &mut context.accounts[self.cursor_pos.0];
- let (hash, is_seen) = {
- let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
- let envelope: &mut Envelope = &mut mailbox
- .collection
- .entry(self.local_collection[idx])
- .or_default();
- (envelope.hash(), envelope.is_seen())
- };
- if !is_seen {
- let op = {
- let backend = &account.backend;
- backend.operation(hash)
- };
- let mailbox = &mut account[self.cursor_pos.1].as_mut().unwrap();
- let envelope: &mut Envelope = &mut mailbox
- .collection
- .entry(self.local_collection[idx])
- .or_default();
- envelope.set_seen(op).unwrap();
- true
- } else {
- false
- }
- }
- };
- if must_highlight {
- self.highlight_line_self(idx, context);
- }
- let mid = get_y(upper_left) + total_rows - bottom_entity_rows;
- self.draw_list(
- grid,
- (
- upper_left,
- (get_x(bottom_right), get_y(upper_left) + mid - 1),
- ),
- context,
- );
- if self.length == 0 {
- self.dirty = false;
- return;
- }
- {
- /* TODO: Move the box drawing business in separate functions */
- if get_x(upper_left) > 0 && grid[(get_x(upper_left) - 1, mid)].ch() == VERT_BOUNDARY
- {
- grid[(get_x(upper_left) - 1, mid)].set_ch(LIGHT_VERTICAL_AND_RIGHT);
- }
-
- for i in get_x(upper_left)..=get_x(bottom_right) {
- grid[(i, mid)].set_ch('─');
- }
- context
- .dirty_areas
- .push_back((set_y(upper_left, mid), set_y(bottom_right, mid)));
- }
- // TODO: Make headers view configurable
-
- if !self.dirty {
- if let Some(v) = self.view.as_mut() {
- v.draw(grid, (set_y(upper_left, mid + 1), bottom_right), context);
- }
- return;
- }
- {
- let threaded = context.accounts[self.cursor_pos.0]
- .runtime_settings
- .conf()
- .threaded();
- let account = &context.accounts[self.cursor_pos.0];
- let mailbox = &account[self.cursor_pos.1].as_ref().unwrap();
- let mut coordinates = self.cursor_pos;
- let coordinates = (
- coordinates.0,
- coordinates.1,
- self.local_collection[self.cursor_pos.2],
- );
- self.view = Some(MailView::new(coordinates, None, None));
- }
- self.view.as_mut().unwrap().draw(
- grid,
- (set_y(upper_left, mid + 1), bottom_right),
- context,
- );
- self.dirty = false;
+ match self {
+ Listing::Compact(l) => l.draw(grid, area, context),
+ Listing::Plain(l) => l.draw(grid, area, context),
+ Listing::Threaded(l) => l.draw(grid, area, context),
}
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
- if let Some(ref mut v) = self.view {
- if v.process_event(event, context) {
- return true;
- }
+ if match self {
+ Listing::Plain(l) => l.process_event(event, context),
+ Listing::Compact(l) => l.process_event(event, context),
+ Listing::Threaded(l) => l.process_event(event, context),
+ } {
+ return true;
}
+
match event.event_type {
- UIEventType::Input(Key::Up) => {
- if self.cursor_pos.2 > 0 {
- self.new_cursor_pos.2 -= 1;
- self.dirty = true;
- }
- return true;
- }
- UIEventType::Input(Key::Down) => {
- if self.length > 0 && self.new_cursor_pos.2 < self.length - 1 {
- self.new_cursor_pos.2 += 1;
- self.dirty = true;
- }
- return true;
- }
- UIEventType::Input(Key::Char('\n')) if !self.unfocused => {
- self.unfocused = true;
- self.dirty = true;
- return true;
- }
- UIEventType::Input(Key::Char('m')) if !self.unfocused => {
- context.replies.push_back(UIEvent {
- id: 0,
- event_type: UIEventType::Action(Tab(NewDraft)),
- });
- return true;
- }
- UIEventType::Input(Key::Char('i')) if self.unfocused => {
- self.unfocused = false;
- self.dirty = true;
- self.view = None;
- return true;
- }
- UIEventType::Input(Key::Char(k @ 'J')) | UIEventType::Input(Key::Char(k @ 'K')) => {
- let folder_length = context.accounts[self.cursor_pos.0].len();
- let accounts_length = context.accounts.len();
- match k {
- 'J' if folder_length > 0 => {
- if self.new_cursor_pos.1 < folder_length - 1 {
- self.new_cursor_pos.1 = self.cursor_pos.1 + 1;
- self.dirty = true;
- self.refresh_mailbox(context);
- } else if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1
- {
- self.new_cursor_pos.0 = self.cursor_pos.0 + 1;
- self.new_cursor_pos.1 = 0;
- self.dirty = true;
- self.refresh_mailbox(context);
- }
- }
- 'K' => {
- if self.cursor_pos.1 > 0 {
- self.new_cursor_pos.1 = self.cursor_pos.1 - 1;
- self.dirty = true;
- self.refresh_mailbox(context);
- } else if self.cursor_pos.0 > 0 {
- self.new_cursor_pos.0 = self.cursor_pos.0 - 1;
- self.new_cursor_pos.1 = 0;
- self.dirty = true;
- self.refresh_mailbox(context);
- }
- }
- _ => {}
- }
- return true;
- }
- UIEventType::Input(Key::Char(k @ 'h')) | UIEventType::Input(Key::Char(k @ 'l')) => {
- let accounts_length = context.accounts.len();
- match k {
- 'h' if accounts_length > 0 && self.new_cursor_pos.0 < accounts_length - 1 => {
- self.new_cursor_pos.0 = self.cursor_pos.0 + 1;
- self.new_cursor_pos.1 = 0;
- self.dirty = true;
- self.refresh_mailbox(context);
- }
- 'l' if self.cursor_pos.0 > 0 => {
- self.new_cursor_pos.0 = self.cursor_pos.0 - 1;
- self.new_cursor_pos.1 = 0;
- self.dirty = true;
- self.refresh_mailbox(context);
- }
- _ => {}
- }
- return true;
- }
- UIEventType::RefreshMailbox(_) => {
- self.dirty = true;
- self.view = None;
- }
- UIEventType::MailboxUpdate((ref idxa, ref idxf)) => {
- if *idxa == self.new_cursor_pos.0 && *idxf == self.new_cursor_pos.1 {
- self.dirty = true;
- self.refresh_mailbox(context);
- }
- }
- UIEventType::ChangeMode(UIMode::Normal) => {
- self.dirty = true;
- }
- UIEventType::Resize => {
- self.dirty = true;
- }
+ UIEventType::Resize => self.set_dirty(),
UIEventType::Action(ref action) => match action {
- Action::Listing(ListingAction::ToggleThreaded) => {
- context.accounts[self.cursor_pos.0]
- .runtime_settings
- .conf_mut()
- .toggle_threaded();
- self.refresh_mailbox(context);
- self.dirty = true;
- return true;
- }
- Action::ViewMailbox(idx) => {
- self.new_cursor_pos.1 = *idx;
- self.dirty = true;
- self.refresh_mailbox(context);
+ Action::Listing(ListingAction::SetPlain) => {
+ if let Listing::Plain(_) = self {
+ return true;
+ }
+ *self = Listing::Plain(PlainListing::default());
return true;
}
- Action::SubSort(field, order) => {
- eprintln!("SubSort {:?} , {:?}", field, order);
- self.subsort = (*field, *order);
- self.dirty = true;
- self.refresh_mailbox(context);
+ Action::Listing(ListingAction::SetThreaded) => {
+ if let Listing::Threaded(_) = self {
+ return true;
+ }
+ self.set_dirty();
+ *self = Listing::Threaded(ThreadListing::default());
return true;
}
- Action::Sort(field, order) => {
- eprintln!("Sort {:?} , {:?}", field, order);
- self.sort = (*field, *order);
- self.dirty = true;
- self.refresh_mailbox(context);
+ Action::Listing(ListingAction::SetCompact) => {
+ if let Listing::Compact(_) = self {
+ return true;
+ }
+ *self = Listing::Compact(CompactListing::default());
return true;
}
_ => {}
@@ -648,12 +102,17 @@ impl Component for PlainListing {
false
}
fn is_dirty(&self) -> bool {
- self.dirty || self.view.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
+ match self {
+ Listing::Compact(l) => l.is_dirty(),
+ Listing::Plain(l) => l.is_dirty(),
+ Listing::Threaded(l) => l.is_dirty(),
+ }
}
fn set_dirty(&mut self) {
- if let Some(p) = self.view.as_mut() {
- p.set_dirty();
- };
- self.dirty = true;
+ match self {
+ Listing::Compact(l) => l.set_dirty(),
+ Listing::Plain(l) => l.set_dirty(),
+ Listing::Threaded(l) => l.set_dirty(),
+ }
}
}
diff --git a/ui/src/components/mail/listing/plain.rs b/ui/src/components/mail/listing/plain.rs
new file mode 100644
index 00000000..42369fc6
--- /dev/null
+++ b/ui/src/components/mail/listing/plain.rs
@@ -0,0 +1,559 @@
+/*
+ * meli - ui crate.
+ *
+ * Copyright 2017-2018 Manos Pitsidianakis
+ *
+ * This file is part of meli.
+ *
+ * meli is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * meli is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with meli. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+use super::*;
+
+const MAX_COLS: usize = 500;
+
+/// A list of all mail (`Envelope`s) in a `Mailbox`. On `\n` it opens the `Envelope` content in a
+/// `MailView`.
+#[derive(Debug)]
+pub struct PlainListing {
+ /// (x, y, z): x is accounts, y is folders, z is index inside a folder.
+ cursor_pos: (usize, usize, usize),
+ new_cursor_pos: (usize, usize, usize),
+ length: usize,
+ local_collection: Vec<EnvelopeHash>,
+ sort: (SortField, SortOrder),
+ subsort: (SortField, SortOrder),
+ /// Cache current view.
+ content: CellBuffer,
+ /// If we must redraw on next redraw event
+ dirty: bool,
+ /// If `self.view` exists or not.
+ unfocused: bool,
+ view: Option<MailView>,
+}
+
+impl Default for PlainListing {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl fmt::Display for PlainListing {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "mail")
+ }
+}
+
+impl PlainListing {
+ /// Helper function to format entry strings for PlainListing */
+ /* TODO: Make this configurable */
+ fn make_entry_string(e: &Envelope, idx: usize) -> String {
+ format!(
+ "{} {} {}",
+ idx,
+ &e.datetime().format("%Y-%m-%d %H:%M:%S").to_string(),
+ e.subject()
+ )
+ }
+
+ pub fn new() -> Self {
+ let content = CellBuffer::new(0, 0, Cell::with_char(' '));
+ PlainListing {
+ cursor_pos: (0, 1, 0),
+ new_cursor_pos: (0, 0, 0),
+ length: 0,
+ local_collection: Vec::new(),
+ sort: (Default::default(), Default::default()),
+ subsort: (Default::default(), Default::default()),
+ content,
+ dirty: true,
+ unfocused: false,
+ view: None,
+ }
+ }
+ /// Fill the `self.content` `CellBuffer` with the contents of the account folder the user has
+ /// chosen.
+ fn refresh_mailbox(&mut self, context: &mut Context) {
+ self.dirty = true;
+ self.cursor_pos.2 = 0;
+ self.new_cursor_pos.2 = 0;
+ self.cursor_pos.1 = self.new_cursor_pos.1;
+ self.cursor_pos.0 = self.new_cursor_pos.0;
+
+ // Inform State that we changed the current folder view.
+ context.replies.push_back(UIEvent {
+ id: 0,
+ event_type: UIEventType::RefreshMailbox((self.cursor_pos.0, self.cursor_pos.1)),
+ });
+ // Get mailbox as a reference.
+ //
+ loop {
+ // TODO: Show progress visually
+ if context.accounts[self.cursor_pos.0]
+ .status(self.cursor_pos.1)
+ .is_ok()
+ {
+ break;
+ }
+ }
+ let mailbox = &context.accounts[self.cursor_pos.0][self.cursor_pos.1]
+ .as_ref()
+ .unwrap();
+
+ self.length = mailbox.len();
+ self.content = CellBuffer::new(MAX_COLS, self.length + 1, Cell::with_char(' '));
+ if self.length == 0 {
+ write_string_to_grid(
+ &format!("Folder `{}` is empty.", mailbox.folder.name()),