ui: Use FolderHash instead of usize for folder cursor

Use FolderHash directly as a cursor type for folders within an account
isntead of having a usize (being the order of the folder within the
account) and figuring out the folder_hash everytime it's needed.

Add OfflineListing for offline accounts and AccountStatusChange event.
master
Manos Pitsidianakis 2020-02-08 23:42:31 +02:00
parent 42747ef590
commit 647cb10b33
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
13 changed files with 392 additions and 396 deletions

View File

@ -305,7 +305,7 @@ fn run_app() -> Result<()> {
let mut state = State::new(sender, receiver.clone())?;
let window = Box::new(Tabbed::new(vec![
Box::new(listing::Listing::new(&state.context.accounts)),
Box::new(listing::Listing::new(&mut state.context)),
Box::new(ContactList::new(&state.context)),
Box::new(StatusPanel::new()),
]));

View File

@ -36,6 +36,9 @@ pub use self::thread::*;
mod plain;
pub use self::plain::*;
mod offline;
pub use self::offline::*;
#[derive(Debug, Default, Clone)]
pub struct DataColumns {
pub columns: [CellBuffer; 12],
@ -122,6 +125,7 @@ struct AccountMenuEntry {
name: String,
// Index in the config account vector.
index: usize,
entries: SmallVec<[(usize, FolderHash); 16]>,
}
pub trait MailListingTrait: ListingTrait {
@ -206,8 +210,8 @@ pub trait MailListingTrait: ListingTrait {
}
pub trait ListingTrait: Component {
fn coordinates(&self) -> (usize, usize);
fn set_coordinates(&mut self, _: (usize, usize));
fn coordinates(&self) -> (usize, FolderHash);
fn set_coordinates(&mut self, _: (usize, FolderHash));
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context);
fn highlight_line(&mut self, grid: &mut CellBuffer, area: Area, idx: usize, context: &Context);
fn filter(&mut self, _filter_term: &str, _context: &Context) {}
@ -220,6 +224,7 @@ pub enum ListingComponent {
Threaded(ThreadListing),
Compact(CompactListing),
Conversations(ConversationsListing),
Offline(OfflineListing),
}
use crate::ListingComponent::*;
@ -232,6 +237,7 @@ impl core::ops::Deref for ListingComponent {
Plain(ref l) => l,
Threaded(ref l) => l,
Conversations(ref l) => l,
Offline(ref l) => l,
}
}
}
@ -243,6 +249,7 @@ impl core::ops::DerefMut for ListingComponent {
Plain(l) => l,
Threaded(l) => l,
Conversations(l) => l,
Offline(l) => l,
}
}
}
@ -254,37 +261,25 @@ impl ListingComponent {
if let Plain(_) = self {
return;
}
let mut new_l = PlainListing::default();
let coors = self.coordinates();
new_l.set_coordinates((coors.0, coors.1));
*self = Plain(new_l);
*self = Plain(PlainListing::new(self.coordinates()));
}
IndexStyle::Threaded => {
if let Threaded(_) = self {
return;
}
let mut new_l = ThreadListing::default();
let coors = self.coordinates();
new_l.set_coordinates((coors.0, coors.1));
*self = Threaded(new_l);
*self = Threaded(ThreadListing::new(self.coordinates()));
}
IndexStyle::Compact => {
if let Compact(_) = self {
return;
}
let mut new_l = CompactListing::default();
let coors = self.coordinates();
new_l.set_coordinates((coors.0, coors.1));
*self = Compact(new_l);
*self = Compact(CompactListing::new(self.coordinates()));
}
IndexStyle::Conversations => {
if let Conversations(_) = self {
return;
}
let mut new_l = ConversationsListing::default();
let coors = self.coordinates();
new_l.set_coordinates((coors.0, coors.1));
*self = Conversations(new_l);
*self = Conversations(ConversationsListing::new(self.coordinates()));
}
}
}
@ -315,6 +310,7 @@ impl fmt::Display for Listing {
Plain(ref l) => write!(f, "{}", l),
Threaded(ref l) => write!(f, "{}", l),
Conversations(ref l) => write!(f, "{}", l),
Offline(ref l) => write!(f, "{}", l),
}
}
}
@ -331,7 +327,6 @@ impl Component for Listing {
if !is_valid_area!(area) {
return;
}
self.theme_default = crate::conf::value(context, "theme_default");
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let total_cols = get_x(bottom_right) - get_x(upper_left);
@ -431,12 +426,7 @@ impl Component for Listing {
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match event {
UIEvent::StartupCheck(ref f) => {
if context.accounts[self.component.coordinates().0]
.folders_order
.get(self.component.coordinates().1)
.map(|&folder_hash| *f == folder_hash)
.unwrap_or(false)
{
if self.component.coordinates().1 == *f {
if !self.startup_checks_rate.tick() {
return false;
}
@ -445,27 +435,52 @@ impl Component for Listing {
UIEvent::Timer(n) if *n == self.startup_checks_rate.id() => {
if self.startup_checks_rate.active {
self.startup_checks_rate.reset();
if let Some(folder_hash) = context.accounts[self.component.coordinates().0]
.folders_order
.get(self.component.coordinates().1)
{
return self
.process_event(&mut UIEvent::StartupCheck(*folder_hash), context);
}
return self.process_event(
&mut UIEvent::StartupCheck(self.component.coordinates().1),
context,
);
}
}
UIEvent::AccountStatusChange(account_index) => {
if self.cursor_pos.0 == *account_index {
self.change_account(context);
} else {
self.accounts[*account_index].entries = context.accounts[*account_index]
.list_folders()
.into_iter()
.filter(|folder_node| {
context.accounts[*account_index].ref_folders()[&folder_node.hash]
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
.collect::<_>();
self.set_dirty(true);
}
return true;
}
UIEvent::MailboxDelete((account_index, _folder_hash))
| UIEvent::MailboxCreate((account_index, _folder_hash)) => {
self.accounts[*account_index].entries = context.accounts[*account_index]
.list_folders()
.into_iter()
.filter(|folder_node| {
context.accounts[*account_index].ref_folders()[&folder_node.hash]
.is_subscribed()
})
.map(|f| (f.depth, f.hash))
.collect::<_>();
if self.cursor_pos.0 == *account_index {
self.cursor_pos.1 = std::cmp::min(
context.accounts[*account_index].len() - 1,
self.accounts[self.cursor_pos.0].entries.len() - 1,
self.cursor_pos.1,
);
self.component
.set_coordinates((self.cursor_pos.0, self.cursor_pos.1));
self.component.set_coordinates((
self.cursor_pos.0,
self.accounts[self.cursor_pos.0].entries[self.cursor_pos.1].1,
));
self.component.refresh_mailbox(context, true);
self.set_dirty(true);
}
self.set_dirty(true);
return true;
}
_ => {}
@ -496,15 +511,15 @@ impl Component for Listing {
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
return true;
};
let folder_length = context.accounts[self.cursor_pos.0].len();
match k {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"])
&& folder_length > 0 =>
{
if self.cursor_pos.1 + amount < folder_length {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_folder"]) => {
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1 + amount)
{
self.cursor_pos.1 += amount;
self.component
.set_coordinates((self.cursor_pos.0, self.cursor_pos.1));
.set_coordinates((self.cursor_pos.0, *folder_hash));
self.set_dirty(true);
} else {
return true;
@ -512,25 +527,32 @@ impl Component for Listing {
}
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_folder"]) => {
if self.cursor_pos.1 >= amount {
self.cursor_pos.1 -= amount;
self.component
.set_coordinates((self.cursor_pos.0, self.cursor_pos.1));
self.set_dirty(true);
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1 - amount)
{
self.cursor_pos.1 -= amount;
self.component
.set_coordinates((self.cursor_pos.0, *folder_hash));
self.set_dirty(true);
} else {
return true;
}
} else {
return true;
}
}
_ => return false,
_ => {}
}
/* Account might have no folders yet if it's offline */
if let Some(&folder_hash) = context.accounts[self.cursor_pos.0]
.folders_order
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1)
{
/* Account might have no folders yet if it's offline */
/* Check if per-folder configuration overrides general configuration */
if let Some(index_style) =
context.accounts.get(self.cursor_pos.0).and_then(|account| {
account.folder_confs(folder_hash).conf_override.index_style
account.folder_confs(*folder_hash).conf_override.index_style
})
{
self.component.set_style(index_style);
@ -572,8 +594,6 @@ impl Component for Listing {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["next_account"]) => {
if self.cursor_pos.0 + amount < self.accounts.len() {
self.cursor_pos = (self.cursor_pos.0 + amount, 0);
self.component.set_coordinates((self.cursor_pos.0, 0));
self.set_dirty(true);
} else {
return true;
}
@ -581,40 +601,14 @@ impl Component for Listing {
k if shortcut!(k == shortcuts[Listing::DESCRIPTION]["prev_account"]) => {
if self.cursor_pos.0 >= amount {
self.cursor_pos = (self.cursor_pos.0 - amount, 0);
self.component.set_coordinates((self.cursor_pos.0, 0));
self.set_dirty(true);
} else {
return true;
}
}
_ => return false,
}
self.change_account(context);
/* Account might have no folders yet if it's offline */
if let Some(&folder_hash) = context.accounts[self.cursor_pos.0]
.folders_order
.get(self.cursor_pos.1)
{
/* Check if per-folder configuration overrides general configuration */
if let Some(index_style) =
context.accounts.get(self.cursor_pos.0).and_then(|account| {
account.folder_confs(folder_hash).conf_override.index_style
})
{
self.component.set_style(index_style);
} else if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| Some(account.settings.conf.index_style()))
{
self.component.set_style(index_style);
}
}
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
self.get_status(context),
)));
return true;
}
UIEvent::Action(ref action) => match action {
@ -876,57 +870,55 @@ impl Component for Listing {
}
}
impl From<IndexStyle> for ListingComponent {
fn from(index_style: IndexStyle) -> Self {
impl From<(IndexStyle, (usize, FolderHash))> for ListingComponent {
fn from((index_style, coordinates): (IndexStyle, (usize, FolderHash))) -> Self {
match index_style {
IndexStyle::Plain => Plain(Default::default()),
IndexStyle::Threaded => Threaded(Default::default()),
IndexStyle::Compact => Compact(Default::default()),
IndexStyle::Conversations => Conversations(Default::default()),
IndexStyle::Plain => Plain(PlainListing::new(coordinates)),
IndexStyle::Threaded => Threaded(ThreadListing::new(coordinates)),
IndexStyle::Compact => Compact(CompactListing::new(coordinates)),
IndexStyle::Conversations => Conversations(ConversationsListing::new(coordinates)),
}
}
}
impl Listing {
const DESCRIPTION: &'static str = "listing";
pub fn new(accounts: &[Account]) -> Self {
let account_entries = accounts
pub fn new(context: &mut Context) -> Self {
let account_entries: Vec<AccountMenuEntry> = context
.accounts
.iter()
.enumerate()
.map(|(i, a)| AccountMenuEntry {
name: a.name().to_string(),
index: i,
.map(|(i, a)| {
let entries: SmallVec<[(usize, FolderHash); 16]> = a
.list_folders()
.into_iter()
.filter(|folder_node| a.ref_folders()[&folder_node.hash].is_subscribed())
.map(|f| (f.depth, f.hash))
.collect::<_>();
AccountMenuEntry {
name: a.name().to_string(),
index: i,
entries,
}
})
.collect();
/* Check if per-folder configuration overrides general configuration */
let component = if let Some(index_style) = accounts.get(0).and_then(|account| {
account.folders_order.get(0).and_then(|folder_hash| {
account.folder_confs(*folder_hash).conf_override.index_style
})
}) {
ListingComponent::from(index_style)
} else if let Some(index_style) = accounts
.get(0)
.and_then(|account| Some(account.settings.conf.index_style()))
{
ListingComponent::from(index_style)
} else {
Conversations(Default::default())
};
Listing {
component,
let mut ret = Listing {
component: Offline(OfflineListing::new((0, 0))),
accounts: account_entries,
visible: true,
dirty: true,
cursor_pos: (0, 0),
startup_checks_rate: RateLimit::new(2, 1000),
theme_default: ThemeAttribute::default(),
theme_default: conf::value(context, "theme_default"),
id: ComponentId::new_v4(),
show_divider: false,
menu_visibility: true,
ratio: 90,
cmd_buf: String::with_capacity(4),
}
};
ret.change_account(context);
ret
}
fn draw_menu(&mut self, grid: &mut CellBuffer, mut area: Area, context: &mut Context) {
@ -969,86 +961,31 @@ impl Listing {
debug!("BUG: invalid area in print_account");
}
// Each entry and its index in the account
let entries: FnvHashMap<FolderHash, Folder> = context.accounts[a.index]
.list_folders()
.into_iter()
.map(|f| (f.hash(), f))
.collect();
let folders_order: FnvHashMap<FolderHash, usize> = context.accounts[a.index]
.folders_order()
.iter()
.enumerate()
.map(|(i, &fh)| (fh, i))
.collect();
let folders: FnvHashMap<FolderHash, Folder> =
context.accounts[a.index].ref_folders().clone();
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
let must_highlight_account: bool = self.cursor_pos.0 == a.index;
let mut inc = 0;
let mut depth = 0;
let mut lines: Vec<(usize, usize, FolderHash, Option<usize>)> = Vec::new();
/* Gather the folder tree structure in `lines` recursively */
fn print(
folder_idx: FolderHash,
depth: &mut usize,
inc: &mut usize,
entries: &FnvHashMap<FolderHash, Folder>,
folders_order: &FnvHashMap<FolderHash, usize>,
lines: &mut Vec<(usize, usize, FolderHash, Option<usize>)>,
index: usize, //account index
context: &mut Context,
) {
match context.accounts[index].status(entries[&folder_idx].hash()) {
Ok(_) => {
lines.push((
*depth,
*inc,
folder_idx,
entries[&folder_idx].count().ok().map(|(v, _)| v),
));
for (i, &(depth, folder_hash)) in a.entries.iter().enumerate() {
if folders[&folder_hash].is_subscribed() {
match context.accounts[a.index].status(folder_hash) {
Ok(_) => {
lines.push((
depth,
i,
folder_hash,
folders[&folder_hash].count().ok().map(|(v, _)| v),
));
}
Err(_) => {
lines.push((depth, i, folder_hash, None));
}
}
Err(_) => {
lines.push((*depth, *inc, folder_idx, None));
}
}
*inc += 1;
let mut children: Vec<FolderHash> = entries[&folder_idx].children().to_vec();
children
.sort_unstable_by(|a, b| folders_order[a].partial_cmp(&folders_order[b]).unwrap());
*depth += 1;
for child in children {
print(
child,
depth,
inc,
entries,
folders_order,
lines,
index,
context,
);
}
*depth -= 1;
}
let mut keys = entries.keys().cloned().collect::<Vec<FolderHash>>();
keys.sort_unstable_by(|a, b| folders_order[a].partial_cmp(&folders_order[b]).unwrap());
/* Start with roots */
for f in keys {
if entries[&f].parent().is_none() {
print(
f,
&mut depth,
&mut inc,
&entries,
&folders_order,
&mut lines,
a.index,
context,
);
}
}
@ -1155,7 +1092,7 @@ impl Listing {
None,
);
let (x, _) = write_string_to_grid(
entries[&folder_idx].name(),
folders[&folder_idx].name(),
grid,
att.fg,
att.bg,
@ -1207,4 +1144,46 @@ impl Listing {
idx - 1
}
}
fn change_account(&mut self, context: &mut Context) {
self.accounts[self.cursor_pos.0].entries = context.accounts[self.cursor_pos.0]
.list_folders()
.into_iter()
.filter(|folder_node| {
context.accounts[self.cursor_pos.0].ref_folders()[&folder_node.hash].is_subscribed()
})
.map(|f| (f.depth, f.hash))
.collect::<_>();
/* Account might have no folders yet if it's offline */
if let Some((_, folder_hash)) = self.accounts[self.cursor_pos.0]
.entries
.get(self.cursor_pos.1)
{
self.component
.set_coordinates((self.cursor_pos.0, *folder_hash));
/* Check if per-folder configuration overrides general configuration */
if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| account.folder_confs(*folder_hash).conf_override.index_style)
{
self.component.set_style(index_style);
} else if let Some(index_style) = context
.accounts
.get(self.cursor_pos.0)
.and_then(|account| Some(account.settings.conf.index_style()))
{
self.component.set_style(index_style);
}
} else {
/* Set to dummy */
self.component = Offline(OfflineListing::new((self.cursor_pos.0, 0)));
}
self.set_dirty(true);
context
.replies
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(debug!(
self.get_status(context)
))));
}
}

View File

@ -49,9 +49,8 @@ macro_rules! address_list {
#[derive(Debug)]
pub struct CompactListing {
/// (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),
folder_hash: FolderHash,
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -114,16 +113,6 @@ impl MailListingTrait for CompactListing {
}
self.cursor_pos.1 = self.new_cursor_pos.1;
self.cursor_pos.0 = self.new_cursor_pos.0;
self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0]
.folders_order
.get(self.cursor_pos.1)
{
*h
} else {
self.cursor_pos.1 = old_cursor_pos.1;
self.dirty = false;
return;
};
self.color_cache = ColorCache {
unseen: crate::conf::value(context, "mail.listing.compact.unseen"),
@ -145,7 +134,7 @@ impl MailListingTrait for CompactListing {
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(self.folder_hash) {
match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
Ok(()) => {}
Err(_) => {
let default_cell = {
@ -156,7 +145,7 @@ impl MailListingTrait for CompactListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.folder_hash].to_string();
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -173,7 +162,7 @@ impl MailListingTrait for CompactListing {
}
}
let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.folder_hash];
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(
@ -198,11 +187,11 @@ impl MailListingTrait for CompactListing {
}
impl ListingTrait for CompactListing {
fn coordinates(&self) -> (usize, usize) {
fn coordinates(&self) -> (usize, FolderHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, usize)) {
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = ThreadView::default();
@ -219,7 +208,7 @@ impl ListingTrait for CompactListing {
let thread_hash = self.get_thread_under_cursor(idx);
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
let thread = threads.thread_ref(thread_hash);
let fg_color = if thread.unseen() > 0 {
@ -541,9 +530,9 @@ impl ListingTrait for CompactListing {
}
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.folder_hash) {
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
@ -633,19 +622,12 @@ impl fmt::Display for CompactListing {
}
}
impl Default for CompactListing {
fn default() -> Self {
CompactListing::new()
}
}
impl CompactListing {
const DESCRIPTION: &'static str = "compact listing";
fn new() -> Self {
pub fn new(coordinates: (usize, FolderHash)) -> Self {
CompactListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (0, 0, 0),
folder_hash: 0,
new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0,
sort: (Default::default(), Default::default()),
subsort: (SortField::Date, SortOrder::Desc),
@ -674,7 +656,7 @@ impl CompactListing {
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.folder_hash];
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
let mut tags = String::new();
let mut colors: SmallVec<[_; 8]> = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
@ -1026,7 +1008,7 @@ impl CompactListing {
fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) {
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
let thread = threads.thread_ref(thread_hash);
let thread_node_hash = threads.thread_group_iter(thread_hash).next().unwrap().1;
if let Some(env_hash) = threads.thread_nodes()[&thread_node_hash].message() {
@ -1310,7 +1292,7 @@ impl Component for CompactListing {
account
.collection
.threads
.entry(self.folder_hash)
.entry(self.cursor_pos.1)
.and_modify(|threads| {
let is_snoozed = threads.thread_ref(thread).snoozed();
threads.thread_ref_mut(thread).set_snoozed(!is_snoozed);
@ -1328,18 +1310,18 @@ impl Component for CompactListing {
}
match *event {
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) =>
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{
self.refresh_mailbox(context, false);
self.set_dirty(true);
}
UIEvent::StartupCheck(ref f) if *f == self.folder_hash => {
UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => {
self.refresh_mailbox(context, false);
self.set_dirty(true);
}
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
if !account.collection.contains_key(&new_hash) {
return false;
}

View File

@ -28,9 +28,8 @@ use std::iter::FromIterator;
#[derive(Debug)]
pub struct ConversationsListing {
/// (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),
folder_hash: FolderHash,
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -84,7 +83,7 @@ impl MailListingTrait for ConversationsListing {
/// chosen.
fn refresh_mailbox(&mut self, context: &mut Context, force: bool) {
self.dirty = true;
let old_folder_hash = self.folder_hash;
let old_folder_hash = self.cursor_pos.1;
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)
@ -94,16 +93,6 @@ impl MailListingTrait for ConversationsListing {
}
self.cursor_pos.1 = self.new_cursor_pos.1;
self.cursor_pos.0 = self.new_cursor_pos.0;
self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0]
.folders_order
.get(self.cursor_pos.1)
{
*h
} else {
self.cursor_pos.1 = old_cursor_pos.1;
self.dirty = false;
return;
};
self.color_cache = ColorCache {
theme_default: crate::conf::value(context, "mail.listing.conversations"),
@ -130,7 +119,7 @@ impl MailListingTrait for ConversationsListing {
}
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(self.folder_hash) {
match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
Ok(()) => {}
Err(_) => {
let default_cell = {
@ -141,7 +130,7 @@ impl MailListingTrait for ConversationsListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.folder_hash].to_string();
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
self.content =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -158,7 +147,7 @@ impl MailListingTrait for ConversationsListing {
}
}
let threads = &context.accounts[self.cursor_pos.0].collection.threads[&self.folder_hash];
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(
@ -172,7 +161,7 @@ impl MailListingTrait for ConversationsListing {
Box::new(roots.into_iter()) as Box<dyn Iterator<Item = ThreadHash>>,
);
if !force && old_cursor_pos == self.new_cursor_pos && old_folder_hash == self.folder_hash {
if !force && old_cursor_pos == self.new_cursor_pos && old_folder_hash == self.cursor_pos.1 {
self.view.update(context);
} else if self.unfocused {
let thread_group = self.get_thread_under_cursor(self.cursor_pos.2);
@ -183,11 +172,11 @@ impl MailListingTrait for ConversationsListing {
}
impl ListingTrait for ConversationsListing {
fn coordinates(&self) -> (usize, usize) {
fn coordinates(&self) -> (usize, FolderHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, usize)) {
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
@ -205,7 +194,7 @@ impl ListingTrait for ConversationsListing {
let thread_hash = self.get_thread_under_cursor(idx);
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
let thread = threads.thread_ref(thread_hash);
let fg_color = if thread.unseen() > 0 {
@ -475,9 +464,9 @@ impl ListingTrait for ConversationsListing {
}
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.folder_hash) {
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
continue;
@ -566,19 +555,12 @@ impl fmt::Display for ConversationsListing {
}
}
impl Default for ConversationsListing {
fn default() -> Self {
ConversationsListing::new()
}
}
impl ConversationsListing {
const DESCRIPTION: &'static str = "compact listing";
fn new() -> Self {
pub fn new(coordinates: (usize, FolderHash)) -> Self {
ConversationsListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (0, 0, 0),
folder_hash: 0,
new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0,
sort: (Default::default(), Default::default()),
subsort: (SortField::Date, SortOrder::Desc),
@ -608,7 +590,7 @@ impl ConversationsListing {
hash: ThreadHash,
) -> EntryStrings {
let thread = threads.thread_ref(hash);
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.folder_hash];
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
let mut tags = String::new();
let mut colors = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
@ -935,7 +917,7 @@ impl ConversationsListing {
fn update_line(&mut self, context: &Context, thread_hash: ThreadHash) {
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
let thread = threads.thread_ref(thread_hash);
let thread_node_hash = threads.thread_group_iter(thread_hash).next().unwrap().1;
let idx: usize = self.order[&thread_hash];
@ -1218,7 +1200,7 @@ impl Component for ConversationsListing {
}
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[self.cursor_pos.0];
let threads = &account.collection.threads[&self.folder_hash];
let threads = &account.collection.threads[&self.cursor_pos.1];
if !account.collection.contains_key(&new_hash) {
return false;
}
@ -1243,7 +1225,7 @@ impl Component for ConversationsListing {
self.subsort = (*field, *order);
// FIXME subsort
//if !self.filtered_selection.is_empty() {
// let threads = &account.collection.threads[&self.folder_hash];
// let threads = &account.collection.threads[&self.cursor_pos.1];
// threads.vec_inner_sort_by(&mut self.filtered_selection, self.sort, &account.collection);
//} else {
// self.refresh_mailbox(context, false);
@ -1257,7 +1239,7 @@ impl Component for ConversationsListing {
self.sort = (*field, *order);
if !self.filtered_selection.is_empty() {
let threads = &context.accounts[self.cursor_pos.0].collection.threads
[&self.folder_hash];
[&self.cursor_pos.1];
threads.vec_inner_sort_by(
&mut self.filtered_selection,
self.sort,
@ -1276,7 +1258,7 @@ impl Component for ConversationsListing {
account
.collection
.threads
.entry(self.folder_hash)
.entry(self.cursor_pos.1)
.and_modify(|threads| {
let is_snoozed = threads.thread_ref(thread).snoozed();
threads.thread_ref_mut(thread).set_snoozed(!is_snoozed);
@ -1292,12 +1274,12 @@ impl Component for ConversationsListing {
}
match *event {
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) =>
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{
self.refresh_mailbox(context, false);
self.set_dirty(true);
}
UIEvent::StartupCheck(ref f) if *f == self.folder_hash => {
UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => {
self.refresh_mailbox(context, false);
self.set_dirty(true);
}

View File

@ -0,0 +1,103 @@
/*
* meli
*
* Copyright 2020 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::*;
use crate::components::utilities::PageMovement;
#[derive(Debug)]
pub struct OfflineListing {
cursor_pos: (usize, FolderHash),
_row_updates: SmallVec<[ThreadHash; 8]>,
id: ComponentId,
}
impl MailListingTrait for OfflineListing {
fn row_updates(&mut self) -> &mut SmallVec<[ThreadHash; 8]> {
&mut self._row_updates
}
fn get_focused_items(&self, _context: &Context) -> SmallVec<[ThreadHash; 8]> {
return SmallVec::new();
}
/// chosen.
fn refresh_mailbox(&mut self, _context: &mut Context, _force: bool) {}
}
impl ListingTrait for OfflineListing {
fn coordinates(&self) -> (usize, FolderHash) {
self.cursor_pos
}
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
self.cursor_pos = coordinates;
}
fn highlight_line(
&mut self,
_grid: &mut CellBuffer,
_area: Area,
_idx: usize,
_context: &Context,
) {
}
fn draw_list(&mut self, _: &mut CellBuffer, _: Area, _: &mut Context) {}
fn filter(&mut self, _: &str, _: &Context) {}
fn set_movement(&mut self, _: PageMovement) {}
}
impl fmt::Display for OfflineListing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "mail")
}
}
impl OfflineListing {
pub fn new(cursor_pos: (usize, FolderHash)) -> Self {
OfflineListing {
cursor_pos,
_row_updates: SmallVec::new(),
id: ComponentId::new_v4(),
}
}
}
impl Component for OfflineListing {
fn draw(&mut self, _grid: &mut CellBuffer, _area: Area, _context: &mut Context) {}
fn process_event(&mut self, _event: &mut UIEvent, _context: &mut Context) -> bool {
false
}
fn is_dirty(&self) -> bool {
false
}
fn set_dirty(&mut self, _value: bool) {}
fn id(&self) -> ComponentId {
self.id
}
fn set_id(&mut self, id: ComponentId) {
self.id = id;
}
}

View File

@ -48,9 +48,8 @@ macro_rules! address_list {
#[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),
folder_hash: FolderHash,
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -115,16 +114,6 @@ impl MailListingTrait for PlainListing {
}
self.cursor_pos.1 = self.new_cursor_pos.1;
self.cursor_pos.0 = self.new_cursor_pos.0;
self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0]
.folders_order
.get(self.cursor_pos.1)
{
*h
} else {
self.cursor_pos.1 = old_cursor_pos.1;
self.dirty = false;
return;
};
self.color_cache = ColorCache {
unseen: crate::conf::value(context, "mail.listing.plain.unseen"),
@ -145,7 +134,7 @@ impl MailListingTrait for PlainListing {
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(self.folder_hash) {
match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
Ok(()) => {}
Err(_) => {
let default_cell = {
@ -156,7 +145,7 @@ impl MailListingTrait for PlainListing {
ret
};
let message: String =
context.accounts[self.cursor_pos.0][self.folder_hash].to_string();
context.accounts[self.cursor_pos.0][self.cursor_pos.1].to_string();
self.data_columns.columns[0] =
CellBuffer::new_with_context(message.len(), 1, default_cell, context);
self.length = 0;
@ -172,7 +161,7 @@ impl MailListingTrait for PlainListing {
return;
}
}
self.local_collection = context.accounts[self.cursor_pos.0][self.folder_hash]
self.local_collection = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.unwrap()
.envelopes
.iter()
@ -183,7 +172,7 @@ impl MailListingTrait for PlainListing {
.envelopes
.read()
.unwrap();
self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.folder_hash]
self.thread_node_hashes = context.accounts[self.cursor_pos.0][self.cursor_pos.1]
.unwrap()
.envelopes
.iter()
@ -205,11 +194,11 @@ impl MailListingTrait for PlainListing {
}
impl ListingTrait for PlainListing {
fn coordinates(&self) -> (usize, usize) {
fn coordinates(&self) -> (usize, FolderHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, usize)) {
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = MailView::default();
@ -523,7 +512,7 @@ impl ListingTrait for PlainListing {
}
let account = &context.accounts[self.cursor_pos.0];
match account.search(&self.filter_term, self.sort, self.folder_hash) {
match account.search(&self.filter_term, self.sort, self.cursor_pos.1) {
Ok(results) => {
for env_hash in results {
if !account.collection.contains_key(&env_hash) {
@ -599,19 +588,12 @@ impl fmt::Display for PlainListing {
}
}
impl Default for PlainListing {
fn default() -> Self {
PlainListing::new()
}
}
impl PlainListing {
const DESCRIPTION: &'static str = "plain listing";
fn new() -> Self {
pub fn new(coordinates: (usize, FolderHash)) -> Self {
PlainListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (0, 0, 0),
folder_hash: 0,
new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0,
sort: (Default::default(), Default::default()),
subsort: (SortField::Date, SortOrder::Desc),
@ -637,7 +619,7 @@ impl PlainListing {
}
}
fn make_entry_string(&self, e: EnvelopeRef, context: &Context) -> EntryStrings {
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.folder_hash];
let folder = &context.accounts[self.cursor_pos.0].folder_confs[&self.cursor_pos.1];
let mut tags = String::new();
let mut colors = SmallVec::new();
let backend_lck = context.accounts[self.cursor_pos.0].backend.read().unwrap();
@ -1097,7 +1079,7 @@ impl Component for PlainListing {
debug!("SubSort {:?} , {:?}", field, order);
self.subsort = (*field, *order);
//if !self.filtered_selection.is_empty() {
// let threads = &account.collection.threads[&self.folder_hash];
// let threads = &account.collection.threads[&self.cursor_pos.1];
// threads.vec_inner_sort_by(&mut self.filtered_selection, self.sort, &account.collection);
//} else {
// self.refresh_mailbox(contex, falset);
@ -1148,19 +1130,19 @@ impl Component for PlainListing {
}
match *event {
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) =>
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{
self.refresh_mailbox(context, false);
self.set_dirty(true);
}
UIEvent::StartupCheck(ref f) if *f == self.folder_hash => {
UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => {
self.refresh_mailbox(context, false);
self.set_dirty(true);
}
UIEvent::EnvelopeRename(ref old_hash, ref new_hash) => {
let account = &context.accounts[self.cursor_pos.0];
if !account.collection.contains_key(new_hash)
|| !account[self.folder_hash]
|| !account[self.cursor_pos.1]
.unwrap()
.envelopes
.contains(new_hash)

View File

@ -29,9 +29,8 @@ const MAX_COLS: usize = 500;
#[derive(Debug)]
pub struct ThreadListing {
/// (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),
folder_hash: FolderHash,
cursor_pos: (usize, FolderHash, usize),
new_cursor_pos: (usize, FolderHash, usize),
length: usize,
sort: (SortField, SortOrder),
subsort: (SortField, SortOrder),
@ -72,14 +71,6 @@ impl MailListingTrait for ThreadListing {
}
self.cursor_pos.1 = self.new_cursor_pos.1;
self.cursor_pos.0 = self.new_cursor_pos.0;
self.folder_hash = if let Some(h) = context.accounts[self.cursor_pos.0]
.folders_order
.get(self.cursor_pos.1)
{
*h
} else {
return;
};
self.color_cache = ColorCache {
unseen: crate::conf::value(context, "mail.listing.plain.unseen"),
@ -100,7 +91,7 @@ impl MailListingTrait for ThreadListing {
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(self.folder_hash) {
match context.accounts[self.cursor_pos.0].status(self.cursor_pos.1) {
Ok(_) => {}
Err(_) => {
let default_cell = {
@ -234,10 +225,10 @@ impl MailListingTrait for ThreadListing {
}
impl ListingTrait for ThreadListing {
fn coordinates(&self) -> (usize, usize) {
fn coordinates(&self) -> (usize, FolderHash) {
(self.new_cursor_pos.0, self.new_cursor_pos.1)
}
fn set_coordinates(&mut self, coordinates: (usize, usize)) {
fn set_coordinates(&mut self, coordinates: (usize, FolderHash)) {
self.new_cursor_pos = (coordinates.0, coordinates.1, 0);
self.unfocused = false;
self.view = None;
@ -398,12 +389,6 @@ impl ListingTrait for ThreadListing {
}
}
impl Default for ThreadListing {
fn default() -> Self {
Self::new()
}
}
impl fmt::Display for ThreadListing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "mail")
@ -411,12 +396,10 @@ impl fmt::Display for ThreadListing {
}
impl ThreadListing {
pub fn new() -> Self {
let content = CellBuffer::new(0, 0, Cell::with_char(' '));
pub fn new(coordinates: (usize, FolderHash)) -> Self {
ThreadListing {
cursor_pos: (0, 1, 0),
new_cursor_pos: (0, 0, 0),
folder_hash: 0,
new_cursor_pos: (coordinates.0, coordinates.1, 0),
length: 0,
sort: (Default::default(), Default::default()),
subsort: (Default::default(), Default::default()),
@ -662,12 +645,12 @@ impl Component for ThreadListing {
return true;
}
UIEvent::MailboxUpdate((ref idxa, ref idxf))
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.folder_hash) =>
if (*idxa, *idxf) == (self.new_cursor_pos.0, self.cursor_pos.1) =>
{
self.refresh_mailbox(context, false);
self.set_dirty(true);
}
UIEvent::StartupCheck(ref f) if *f == self.folder_hash => {
UIEvent::StartupCheck(ref f) if *f == self.cursor_pos.1 => {
self.refresh_mailbox(context, false);
self.set_dirty(true);
}

View File

@ -484,7 +484,8 @@ impl Component for AccountStatus {
None,
);
line += 2;
for f in a.list_folders() {
for folder_node in a.list_folders() {
let f: &Folder = &a.ref_folders()[&folder_node.hash];
if f.is_subscribed() {
write_string_to_grid(
f.path(),

View File

@ -80,7 +80,7 @@ impl ViewMode {
/// menus
#[derive(Debug, Default)]
pub struct MailView {
coordinates: (usize, usize, EnvelopeHash),
coordinates: (usize, FolderHash, EnvelopeHash),
pager: Pager,
subview: Option<Box<dyn Component>>,
dirty: bool,
@ -117,7 +117,7 @@ impl fmt::Display for MailView {
impl MailView {
const DESCRIPTION: &'static str = "view mail";
pub fn new(
coordinates: (usize, usize, EnvelopeHash),
coordinates: (usize, FolderHash, EnvelopeHash),
pager: Option<Pager>,
subview: Option<Box<dyn Component>>,
context: &Context,
@ -331,7 +331,7 @@ impl MailView {
}
}
pub fn update(&mut self, new_coordinates: (usize, usize, EnvelopeHash)) {
pub fn update(&mut self, new_coordinates: (usize, FolderHash, EnvelopeHash)) {
self.coordinates = new_coordinates;
self.mode = ViewMode::Normal;
self.set_dirty(true);
@ -791,10 +791,8 @@ impl Component for MailView {
UIEvent::Input(ref key)
if shortcut!(key == shortcuts[MailView::DESCRIPTION]["reply"]) =>
{
let account = &context.accounts[self.coordinates.0];
let folder_hash = account[self.coordinates.1].unwrap().folder.hash();
context.replies.push_back(UIEvent::Action(Tab(Reply(
(self.coordinates.0, folder_hash),
(self.coordinates.0, self.coordinates.1),
self.coordinates.2,
))));
return true;

View File

@ -51,7 +51,7 @@ pub struct ThreadView {
expanded_pos: usize,
new_expanded_pos: usize,
reversed: bool,
coordinates: (usize, usize, usize),
coordinates: (usize, FolderHash, usize),
thread_group: ThreadHash,
mailview: MailView,
show_mailview: bool,
@ -75,7 +75,7 @@ impl ThreadView {
* context: current context
*/
pub fn new(
coordinates: (usize, usize, usize),
coordinates: (usize, FolderHash, usize),
thread_group: ThreadHash,
expanded_hash: Option<ThreadNodeHash>,
context: &Context,

View File

@ -62,6 +62,7 @@ pub enum MailboxEntry {
Failed(MeliError),
/// first argument is done work, and second is total work
Parsing(Mailbox, usize, usize),
None,
}
impl Default for MailboxEntry {
@ -78,6 +79,7 @@ impl std::fmt::Display for MailboxEntry {
match self {
MailboxEntry::Available(ref m) => m.name().to_string(),
MailboxEntry::Failed(ref e) => e.to_string(),
MailboxEntry::None => "Not subscribed, is this a bug?".to_string(),
MailboxEntry::Parsing(_, done, total) => {
format!("Parsing messages. [{}/{}]", done, total)
}
@ -109,6 +111,7 @@ impl MailboxEntry {
"Mailbox is not available: {}",
e.to_string()
))),
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
}
}
@ -120,6 +123,7 @@ impl MailboxEntry {
"Mailbox is not available: {}",
e.to_string()
))),
MailboxEntry::None => Err(MeliError::new("Mailbox is not subscribed.")),
}
}
@ -234,10 +238,11 @@ impl<'a> Iterator for MailboxIterator<'a> {
}
}
#[derive(Serialize, Debug, Default)]
struct FolderNode {
hash: FolderHash,
kids: Vec<FolderNode>,
#[derive(Serialize, Debug, Clone, Default)]
pub struct FolderNode {
pub hash: FolderHash,
pub depth: usize,
pub children: Vec<FolderNode>,
}
impl Account {
@ -393,7 +398,7 @@ impl Account {
collection.threads.insert(*h, Threads::default());
}
build_folders_order(&folder_confs, &mut tree, &ref_folders, &mut folders_order);
build_folders_order(&mut tree, &ref_folders, &mut folders_order);
self.folders = folders;
self.ref_folders = ref_folders;
self.folder_confs = folder_confs;
@ -717,50 +722,25 @@ impl Account {
pub fn is_empty(&self) -> bool {
self.folders.is_empty()
}
pub fn list_folders(&self) -> Vec<Folder> {
let mut folders = self.ref_folders.clone();
let folder_confs = &self.folder_confs;
//debug!("folder renames: {:?}", folder_renames);
for f in folders.values_mut() {
if let Some(r) = folder_confs.get(&f.hash()) {
if let Some(rename) = r.folder_conf().alias() {
f.change_name(rename);
}
pub fn ref_folders(&self) -> &FnvHashMap<FolderHash, Folder> {
&self.ref_folders
}
pub fn list_folders(&self) -> Vec<FolderNode> {
let mut ret = Vec::with_capacity(self.folders.len());
fn rec(node: &FolderNode, ret: &mut Vec<FolderNode>) {
ret.push(node.clone());
for c in node.children.iter() {
rec(c, ret);
}
}
/*
if let Some(pos) = folders
.iter()
.position(|f| f.name().eq_ignore_ascii_case("INBOX"))
{
folders.swap(pos, 0);
for node in &self.tree {
rec(node, &mut ret);
}
*/
let order: FnvHashMap<FolderHash, usize> = self
.folders_order
.iter()
.enumerate()
.map(|(i, &fh)| (fh, i))
.collect();
let mut folders: Vec<Folder> = folders
.drain()
.map(|(_, f)| f)
.filter(|f| {
self.folders.contains_key(&f.hash())
|| self
.settings
.account
.subscribed_folders
.iter()
.any(|m| f.path().matches_glob(m))
})
.collect();
if order.is_empty() {
return Vec::new();
}
folders.sort_unstable_by(|a, b| order[&a.hash()].partial_cmp(&order[&b.hash()]).unwrap());
folders
ret
}
pub fn folders_order(&self) -> &Vec<FolderHash> {
&self.folders_order
}
@ -786,7 +766,7 @@ impl Account {
.map(|e| (e.hash(), e))
.collect::<FnvHashMap<EnvelopeHash, Envelope>>();
match self.folders.entry(folder_hash).or_default() {
MailboxEntry::Failed(_) => {}
MailboxEntry::Failed(_) | MailboxEntry::None => {}
MailboxEntry::Parsing(ref mut m, _, _) | MailboxEntry::Available(ref mut m) => {
m.merge(&envelopes);
if let Some(updated_folders) =
@ -807,6 +787,9 @@ impl Account {
}
pub fn status(&mut self, folder_hash: FolderHash) -> result::Result<(), usize> {
if folder_hash == 0 {
return Err(0);
}
loop {
match self.workers.get_mut(&folder_hash).unwrap() {
None => {
@ -1007,12 +990,7 @@ impl Account {
.threads
.insert(folder.hash(), Threads::default());
self.ref_folders = self.backend.read().unwrap().folders()?;
build_folders_order(
&self.folder_confs,
&mut self.tree,
&self.ref_folders,
&mut self.folders_order,
);
build_folders_order(&mut self.tree, &self.ref_folders, &mut self.folders_order);
Ok(format!("`{}` successfully created.", &path))
}
FolderOperation::Delete(path) => {
@ -1190,7 +1168,6 @@ impl IndexMut<usize> for Account {
}
fn build_folders_order(
folder_confs: &FnvHashMap<FolderHash, FileFolderConf>,
tree: &mut Vec<FolderNode>,
ref_folders: &FnvHashMap<FolderHash, Folder>,
folders_order: &mut Vec<FolderHash>,
@ -1199,23 +1176,24 @@ fn build_folders_order(
tree.clear();
folders_order.clear();
for (h, f) in ref_folders.iter() {
if !folder_confs.contains_key(&h) {
continue;
}
if f.parent().is_none() {
fn rec(h: FolderHash, ref_folders: &FnvHashMap<FolderHash, Folder>) -> FolderNode {
fn rec(
h: FolderHash,
ref_folders: &FnvHashMap<FolderHash, Folder>,
depth: usize,
) -> FolderNode {
let mut node = FolderNode {
hash: h,
kids: Vec::new(),
children: Vec::new(),
depth,
};
for &c in ref_folders[&h].children() {
node.kids.push(rec(c, ref_folders));
node.children.push(rec(c, ref_folders, depth + 1));
}
node
};
tree.push(rec(*h, &ref_folders));
tree.push(rec(*h, &ref_folders, 0));
for &c in f.children() {
stack.push(c);
}
@ -1239,10 +1217,10 @@ fn build_folders_order(
}
});
let mut stack: SmallVec<[Option<&FolderNode>; 8]> = SmallVec::new();
let mut stack: SmallVec<[Option<&FolderNode>; 16]> = SmallVec::new();
for n in tree.iter_mut() {
folders_order.push(n.hash);
n.kids.sort_unstable_by(|a, b| {
n.children.sort_unstable_by(|a, b| {
if ref_folders[&b.hash].path().eq_ignore_ascii_case("INBOX") {
std::cmp::Ordering::Greater
} else if ref_folders[&a.hash].path().eq_ignore_ascii_case("INBOX") {
@ -1253,10 +1231,10 @@ fn build_folders_order(
.cmp(&ref_folders[&b.hash].path())
}
});
stack.extend(n.kids.iter().rev().map(Some));
stack.extend(n.children.iter().rev().map(Some));
while let Some(Some(next)) = stack.pop() {
folders_order.push(next.hash);
stack.extend(next.kids.iter().rev().map(Some));
stack.extend(next.children.iter().rev().map(Some));
}
}
}

View File

@ -142,15 +142,20 @@ impl Context {
let Context {
ref mut accounts,
ref mut mailbox_hashes,
ref mut replies,
..
} = self;
let was_online = accounts[account_pos].is_online;
let ret = accounts[account_pos].is_online();
if ret.is_ok() {
if !was_online {
for folder in accounts[account_pos].list_folders() {
debug!("hash & folder: {:?} {}", folder.hash(), folder.name());
mailbox_hashes.insert(folder.hash(), account_pos);
for folder_node in accounts[account_pos].list_folders() {
debug!(
"hash & folder: {:?} {}",
folder_node.hash,
accounts[account_pos].ref_folders()[&folder_node.hash].name()
);
mailbox_hashes.insert(folder_node.hash, account_pos);
}
/* Account::watch() needs
* - work_controller to pass `work_context` to the watcher threads and then add them
@ -160,6 +165,8 @@ impl Context {
* - replies to report any failures to the user
*/
accounts[account_pos].watch();
replies.push_back(UIEvent::AccountStatusChange(account_pos));
}
}
ret

View File

@ -113,6 +113,7 @@ pub enum UIEvent {
MailboxUpdate((usize, FolderHash)), // (account_idx, mailbox_idx)
MailboxDelete((usize, FolderHash)),
MailboxCreate((usize, FolderHash)),
AccountStatusChange(usize),
ComponentKill(Uuid),
WorkerProgress(FolderHash),
StartupCheck(FolderHash),