ui: load vcards to addressbook with vcard_folder account setting
parent
689327651f
commit
afff63c781
|
@ -124,8 +124,10 @@ plain:shows one row per mail, regardless of threading
|
||||||
attempt to not make any changes to this account.
|
attempt to not make any changes to this account.
|
||||||
.Pq Em false
|
.Pq Em false
|
||||||
.It Ic cache_type Ar String
|
.It Ic cache_type Ar String
|
||||||
choose which cache backend to use. Available options are 'none' and 'sqlite3'
|
(optional) choose which cache backend to use. Available options are 'none' and 'sqlite3'
|
||||||
.Pq Em "sqlite3"
|
.Pq Em "sqlite3"
|
||||||
|
.It Ic vcard_folder Ar String
|
||||||
|
(optional) Folder that contains .vcf files. They are parsed and imported read-only.
|
||||||
.It Ic folders Ar folder_config
|
.It Ic folders Ar folder_config
|
||||||
(optional) configuration for each folder. Its format is described below in
|
(optional) configuration for each folder. Its format is described below in
|
||||||
.Sx FOLDERS Ns
|
.Sx FOLDERS Ns
|
||||||
|
|
|
@ -61,7 +61,7 @@ pub struct AddressBook {
|
||||||
display_name: String,
|
display_name: String,
|
||||||
created: DateTime<Local>,
|
created: DateTime<Local>,
|
||||||
last_edited: DateTime<Local>,
|
last_edited: DateTime<Local>,
|
||||||
cards: FnvHashMap<CardId, Card>,
|
pub cards: FnvHashMap<CardId, Card>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||||
|
@ -95,6 +95,24 @@ impl AddressBook {
|
||||||
cards: FnvHashMap::default(),
|
cards: FnvHashMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn with_account(s: &crate::conf::AccountSettings) -> AddressBook {
|
||||||
|
let mut ret = AddressBook::new(s.name.clone());
|
||||||
|
|
||||||
|
#[cfg(feature = "vcard")]
|
||||||
|
{
|
||||||
|
if let Some(vcard_path) = s.vcard_folder() {
|
||||||
|
if let Ok(cards) = vcard::load_cards(&std::path::Path::new(vcard_path)) {
|
||||||
|
for c in cards {
|
||||||
|
ret.add_card(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_card(&mut self, card: Card) {
|
pub fn add_card(&mut self, card: Card) {
|
||||||
self.cards.insert(card.id, card);
|
self.cards.insert(card.id, card);
|
||||||
}
|
}
|
||||||
|
@ -108,7 +126,7 @@ impl AddressBook {
|
||||||
self.cards
|
self.cards
|
||||||
.values()
|
.values()
|
||||||
.filter(|c| c.email.contains(term))
|
.filter(|c| c.email.contains(term))
|
||||||
.map(|c| c.email.clone())
|
.map(|c| format!("{} <{}>", &c.name, &c.email))
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,6 +63,11 @@ impl AccountSettings {
|
||||||
pub fn subscribed_folders(&self) -> &Vec<String> {
|
pub fn subscribed_folders(&self) -> &Vec<String> {
|
||||||
&self.subscribed_folders
|
&self.subscribed_folders
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "vcard")]
|
||||||
|
pub fn vcard_folder(&self) -> Option<&str> {
|
||||||
|
self.extra.get("vcard_folder").map(String::as_str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
|
@ -1,3 +1,23 @@
|
||||||
|
/*
|
||||||
|
* meli
|
||||||
|
*
|
||||||
|
* Copyright 2019 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 super::*;
|
||||||
|
|
||||||
use melib::CardId;
|
use melib::CardId;
|
||||||
|
@ -88,8 +108,8 @@ impl ContactList {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize(&mut self, context: &mut Context) {
|
fn initialize(&mut self, context: &mut Context) {
|
||||||
let account = &mut context.accounts[self.account_pos];
|
let account = &context.accounts[self.account_pos];
|
||||||
let book = &mut account.address_book;
|
let book = &account.address_book;
|
||||||
self.length = book.len();
|
self.length = book.len();
|
||||||
|
|
||||||
self.id_positions.clear();
|
self.id_positions.clear();
|
||||||
|
@ -97,69 +117,39 @@ impl ContactList {
|
||||||
self.id_positions.reserve(book.len());
|
self.id_positions.reserve(book.len());
|
||||||
}
|
}
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
let mut min_width = ("Name".len(), "E-mail".len(), 0, 0, 0);
|
let mut min_width = ("Name".len(), "E-mail".len(), 0, "external".len(), 0, 0);
|
||||||
|
|
||||||
for c in book.values() {
|
for c in book.values() {
|
||||||
self.id_positions.push(*c.id());
|
/* name */
|
||||||
min_width.0 = cmp::max(min_width.0, c.name().split_graphemes().len()); /* name */
|
min_width.0 = cmp::max(min_width.0, c.name().split_graphemes().len());
|
||||||
min_width.1 = cmp::max(min_width.1, c.email().split_graphemes().len()); /* email */
|
/* email */
|
||||||
min_width.2 = cmp::max(min_width.2, c.url().split_graphemes().len());
|
min_width.1 = cmp::max(min_width.1, c.email().split_graphemes().len());
|
||||||
/* url */
|
/* url */
|
||||||
|
min_width.2 = cmp::max(min_width.2, c.url().split_graphemes().len());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* name column */
|
/* name column */
|
||||||
self.data_columns.columns[0] = CellBuffer::new_with_context(
|
self.data_columns.columns[0] =
|
||||||
min_width.0,
|
CellBuffer::new_with_context(min_width.0, self.length, Cell::with_char(' '), context);
|
||||||
self.length + 1,
|
|
||||||
Cell::with_char(' '),
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
/* email column */
|
/* email column */
|
||||||
self.data_columns.columns[1] = CellBuffer::new_with_context(
|
self.data_columns.columns[1] =
|
||||||
min_width.1,
|
CellBuffer::new_with_context(min_width.1, self.length, Cell::with_char(' '), context);
|
||||||
self.length + 1,
|
|
||||||
Cell::with_char(' '),
|
|
||||||
context,
|
|
||||||
);
|
|
||||||
/* url column */
|
/* url column */
|
||||||
self.data_columns.columns[2] = CellBuffer::new_with_context(
|
self.data_columns.columns[2] =
|
||||||
min_width.2,
|
CellBuffer::new_with_context(min_width.2, self.length, Cell::with_char(' '), context);
|
||||||
self.length + 1,
|
/* source column */
|
||||||
|
self.data_columns.columns[3] = CellBuffer::new_with_context(
|
||||||
|
"external".len(),
|
||||||
|
self.length,
|
||||||
Cell::with_char(' '),
|
Cell::with_char(' '),
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
write_string_to_grid(
|
|
||||||
"NAME",
|
|
||||||
&mut self.data_columns.columns[0],
|
|
||||||
Color::Black,
|
|
||||||
Color::White,
|
|
||||||
Attr::Bold,
|
|
||||||
((0, 0), (MAX_COLS - 1, self.length)),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
write_string_to_grid(
|
|
||||||
"E-MAIL",
|
|
||||||
&mut self.data_columns.columns[1],
|
|
||||||
Color::Black,
|
|
||||||
Color::White,
|
|
||||||
Attr::Bold,
|
|
||||||
((0, 0), (MAX_COLS - 1, self.length)),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
write_string_to_grid(
|
let account = &context.accounts[self.account_pos];
|
||||||
"URL",
|
let book = &account.address_book;
|
||||||
&mut self.data_columns.columns[2],
|
let mut book_values = book.values().collect::<Vec<&Card>>();
|
||||||
Color::Black,
|
book_values.sort_unstable_by_key(|c| c.name());
|
||||||
Color::White,
|
for (idx, c) in book_values.iter().enumerate() {
|
||||||
Attr::Bold,
|
|
||||||
((0, 0), (MAX_COLS - 1, self.length)),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
|
|
||||||
let account = &mut context.accounts[self.account_pos];
|
|
||||||
let book = &mut account.address_book;
|
|
||||||
for (idx, c) in book.values().enumerate() {
|
|
||||||
self.id_positions.push(*c.id());
|
self.id_positions.push(*c.id());
|
||||||
|
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
|
@ -168,7 +158,7 @@ impl ContactList {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Attr::Default,
|
Attr::Default,
|
||||||
((0, idx + 1), (min_width.0, idx + 1)),
|
((0, idx), (min_width.0, idx)),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -178,7 +168,7 @@ impl ContactList {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Attr::Default,
|
Attr::Default,
|
||||||
((0, idx + 1), (min_width.1, idx + 1)),
|
((0, idx), (min_width.1, idx)),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -188,7 +178,21 @@ impl ContactList {
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Attr::Default,
|
Attr::Default,
|
||||||
((0, idx + 1), (min_width.2, idx + 1)),
|
((0, idx), (min_width.2, idx)),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
|
||||||
|
write_string_to_grid(
|
||||||
|
if c.external_resource() {
|
||||||
|
"external"
|
||||||
|
} else {
|
||||||
|
"local"
|
||||||
|
},
|
||||||
|
&mut self.data_columns.columns[3],
|
||||||
|
Color::Default,
|
||||||
|
Color::Default,
|
||||||
|
Attr::Default,
|
||||||
|
((0, idx), (min_width.3, idx)),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -197,7 +201,7 @@ impl ContactList {
|
||||||
let message = "Address book is empty.".to_string();
|
let message = "Address book is empty.".to_string();
|
||||||
self.data_columns.columns[0] = CellBuffer::new_with_context(
|
self.data_columns.columns[0] = CellBuffer::new_with_context(
|
||||||
message.len(),
|
message.len(),
|
||||||
self.length + 1,
|
self.length,
|
||||||
Cell::with_char(' '),
|
Cell::with_char(' '),
|
||||||
context,
|
context,
|
||||||
);
|
);
|
||||||
|
@ -273,7 +277,8 @@ impl ContactList {
|
||||||
|
|
||||||
if a.name.grapheme_len() + s.len() > width + 1 {
|
if a.name.grapheme_len() + s.len() > width + 1 {
|
||||||
/* Print account name */
|
/* Print account name */
|
||||||
write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, None);
|
let (x, y) =
|
||||||
|
write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, None);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&s,
|
&s,
|
||||||
grid,
|
grid,
|
||||||
|
@ -304,10 +309,16 @@ impl ContactList {
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for x in x..=get_x(bottom_right!(area)) {
|
||||||
|
grid[(x, y)].set_fg(fg_color);
|
||||||
|
grid[(x, y)].set_bg(bg_color);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Print account name */
|
/* Print account name */
|
||||||
|
|
||||||
write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, None);
|
let (x, y) =
|
||||||
|
write_string_to_grid(&a.name, grid, fg_color, bg_color, Attr::Bold, area, None);
|
||||||
write_string_to_grid(
|
write_string_to_grid(
|
||||||
&s,
|
&s,
|
||||||
grid,
|
grid,
|
||||||
|
@ -323,11 +334,16 @@ impl ContactList {
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
|
for x in x..=get_x(bottom_right!(area)) {
|
||||||
|
grid[(x, y)].set_fg(fg_color);
|
||||||
|
grid[(x, y)].set_bg(bg_color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw_list(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
let upper_left = upper_left!(area);
|
/* reserve top row for column headers */
|
||||||
|
let upper_left = pos_inc(upper_left!(area), (0, 1));
|
||||||
let bottom_right = bottom_right!(area);
|
let bottom_right = bottom_right!(area);
|
||||||
|
|
||||||
if self.length == 0 {
|
if self.length == 0 {
|
||||||
|
@ -341,7 +357,7 @@ impl ContactList {
|
||||||
context.dirty_areas.push_back(area);
|
context.dirty_areas.push_back(area);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let rows = get_y(bottom_right) - get_y(upper_left);
|
let rows = get_y(bottom_right) - get_y(upper_left) + 1;
|
||||||
|
|
||||||
if let Some(mvm) = self.movement.take() {
|
if let Some(mvm) = self.movement.take() {
|
||||||
match mvm {
|
match mvm {
|
||||||
|
@ -352,14 +368,14 @@ impl ContactList {
|
||||||
self.new_cursor_pos = self.new_cursor_pos.saturating_sub(rows * multiplier);
|
self.new_cursor_pos = self.new_cursor_pos.saturating_sub(rows * multiplier);
|
||||||
}
|
}
|
||||||
PageMovement::Down(amount) => {
|
PageMovement::Down(amount) => {
|
||||||
if self.new_cursor_pos + amount + 1 < self.length {
|
if self.new_cursor_pos + amount < self.length {
|
||||||
self.new_cursor_pos += amount;
|
self.new_cursor_pos += amount;
|
||||||
} else {
|
} else {
|
||||||
self.new_cursor_pos = self.length - 1;
|
self.new_cursor_pos = self.length - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PageMovement::PageDown(multiplier) => {
|
PageMovement::PageDown(multiplier) => {
|
||||||
if self.new_cursor_pos + rows * multiplier + 1 < self.length {
|
if self.new_cursor_pos + rows * multiplier < self.length {
|
||||||
self.new_cursor_pos += rows * multiplier;
|
self.new_cursor_pos += rows * multiplier;
|
||||||
} else if self.new_cursor_pos + rows * multiplier > self.length {
|
} else if self.new_cursor_pos + rows * multiplier > self.length {
|
||||||
self.new_cursor_pos = self.length - 1;
|
self.new_cursor_pos = self.length - 1;
|
||||||
|
@ -372,11 +388,7 @@ impl ContactList {
|
||||||
self.new_cursor_pos = 0;
|
self.new_cursor_pos = 0;
|
||||||
}
|
}
|
||||||
PageMovement::End => {
|
PageMovement::End => {
|
||||||
if self.new_cursor_pos + rows > self.length {
|
self.new_cursor_pos = self.length - 1;
|
||||||
self.new_cursor_pos = self.length - 1;
|
|
||||||
} else {
|
|
||||||
self.new_cursor_pos = (self.length / rows) * rows;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -396,8 +408,8 @@ impl ContactList {
|
||||||
continue; //bounds check
|
continue; //bounds check
|
||||||
}
|
}
|
||||||
let new_area = (
|
let new_area = (
|
||||||
set_y(upper_left, get_y(upper_left) + (*idx % rows) + 1),
|
set_y(upper_left, get_y(upper_left) + (*idx % rows)),
|
||||||
set_y(bottom_right, get_y(upper_left) + (*idx % rows) + 1),
|
set_y(bottom_right, get_y(upper_left) + (*idx % rows)),
|
||||||
);
|
);
|
||||||
self.highlight_line(grid, new_area, *idx);
|
self.highlight_line(grid, new_area, *idx);
|
||||||
context.dirty_areas.push_back(new_area);
|
context.dirty_areas.push_back(new_area);
|
||||||
|
@ -416,6 +428,7 @@ impl ContactList {
|
||||||
self.data_columns.widths[0] = self.data_columns.columns[0].size().0; /* name */
|
self.data_columns.widths[0] = self.data_columns.columns[0].size().0; /* name */
|
||||||
self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* email*/
|
self.data_columns.widths[1] = self.data_columns.columns[1].size().0; /* email*/
|
||||||
self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* url */
|
self.data_columns.widths[2] = self.data_columns.columns[2].size().0; /* url */
|
||||||
|
self.data_columns.widths[3] = self.data_columns.columns[3].size().0; /* source */
|
||||||
|
|
||||||
let min_col_width = std::cmp::min(
|
let min_col_width = std::cmp::min(
|
||||||
15,
|
15,
|
||||||
|
@ -431,10 +444,31 @@ impl ContactList {
|
||||||
/* Page_no has changed, so draw new page */
|
/* Page_no has changed, so draw new page */
|
||||||
let mut x = get_x(upper_left);
|
let mut x = get_x(upper_left);
|
||||||
for i in 0..self.data_columns.columns.len() {
|
for i in 0..self.data_columns.columns.len() {
|
||||||
let (column_width, column_height) = self.data_columns.columns[i].size();
|
|
||||||
if self.data_columns.widths[i] == 0 {
|
if self.data_columns.widths[i] == 0 {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
let (column_width, column_height) = self.data_columns.columns[i].size();
|
||||||
|
write_string_to_grid(
|
||||||
|
match i {
|
||||||
|
0 => "NAME",
|
||||||
|
1 => "E-MAIL",
|
||||||
|
2 => "URL",
|
||||||
|
3 => "SOURCE",
|
||||||
|
_ => "",
|
||||||
|
},
|
||||||
|
grid,
|
||||||
|
Color::Black,
|
||||||
|
Color::White,
|
||||||
|
Attr::Bold,
|
||||||
|
(
|
||||||
|
set_x(upper_left!(area), x),
|
||||||
|
(
|
||||||
|
std::cmp::min(get_x(bottom_right), x + (self.data_columns.widths[i])),
|
||||||
|
get_y(upper_left!(area)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
);
|
||||||
copy_area(
|
copy_area(
|
||||||
grid,
|
grid,
|
||||||
&self.data_columns.columns[i],
|
&self.data_columns.columns[i],
|
||||||
|
@ -461,12 +495,15 @@ impl ContactList {
|
||||||
|
|
||||||
change_colors(
|
change_colors(
|
||||||
grid,
|
grid,
|
||||||
(upper_left, set_y(bottom_right, get_y(upper_left))),
|
(
|
||||||
|
upper_left!(area),
|
||||||
|
set_y(bottom_right, get_y(upper_left!(area))),
|
||||||
|
),
|
||||||
Color::Black,
|
Color::Black,
|
||||||
Color::White,
|
Color::White,
|
||||||
);
|
);
|
||||||
|
|
||||||
if top_idx + rows + 1 > self.length {
|
if top_idx + rows > self.length {
|
||||||
clear_area(
|
clear_area(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
|
@ -478,11 +515,8 @@ impl ContactList {
|
||||||
self.highlight_line(
|
self.highlight_line(
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
set_y(upper_left, get_y(upper_left) + (self.cursor_pos % rows) + 1),
|
set_y(upper_left, get_y(upper_left) + (self.cursor_pos % rows)),
|
||||||
set_y(
|
set_y(bottom_right, get_y(upper_left) + (self.cursor_pos % rows)),
|
||||||
bottom_right,
|
|
||||||
get_y(upper_left) + (self.cursor_pos % rows) + 1,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
self.cursor_pos,
|
self.cursor_pos,
|
||||||
);
|
);
|
||||||
|
@ -550,176 +584,244 @@ impl Component for ContactList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let shortcuts = &self.get_shortcuts(context)[Self::DESCRIPTION];
|
let shortcuts = &self.get_shortcuts(context)[Self::DESCRIPTION];
|
||||||
match *event {
|
if self.view.is_none() {
|
||||||
UIEvent::Input(ref key)
|
match *event {
|
||||||
if key == shortcuts["create_contact"] && self.view.is_none() =>
|
UIEvent::Input(ref key) if key == shortcuts["create_contact"] => {
|
||||||
{
|
let mut manager = ContactManager::default();
|
||||||
let mut manager = ContactManager::default();
|
manager.set_parent_id(self.id);
|
||||||
manager.set_parent_id(self.id);
|
manager.account_pos = self.account_pos;
|
||||||
manager.account_pos = self.account_pos;
|
|
||||||
|
|
||||||
self.mode = ViewMode::View(manager.id());
|
self.mode = ViewMode::View(manager.id());
|
||||||
self.view = Some(manager);
|
self.view = Some(manager);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UIEvent::Input(ref key)
|
|
||||||
if key == shortcuts["edit_contact"] && self.length > 0 && self.view.is_none() =>
|
|
||||||
{
|
|
||||||
let account = &mut context.accounts[self.account_pos];
|
|
||||||
let book = &mut account.address_book;
|
|
||||||
let card = book[&self.id_positions[self.cursor_pos]].clone();
|
|
||||||
let mut manager = ContactManager::default();
|
|
||||||
manager.set_parent_id(self.id);
|
|
||||||
manager.card = card;
|
|
||||||
manager.account_pos = self.account_pos;
|
|
||||||
|
|
||||||
self.mode = ViewMode::View(manager.id());
|
|
||||||
self.view = Some(manager);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
UIEvent::Input(ref key) if key == shortcuts["next_account"] && self.view.is_none() => {
|
|
||||||
let amount = if self.cmd_buf.is_empty() {
|
|
||||||
1
|
|
||||||
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
|
||||||
self.cmd_buf.clear();
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
amount
|
|
||||||
} else {
|
|
||||||
self.cmd_buf.clear();
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if self.accounts.is_empty() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if self.account_pos + amount < self.accounts.len() {
|
|
||||||
self.account_pos += amount;
|
UIEvent::Input(ref key) if key == shortcuts["edit_contact"] && self.length > 0 => {
|
||||||
|
let account = &mut context.accounts[self.account_pos];
|
||||||
|
let book = &mut account.address_book;
|
||||||
|
let card = book[&self.id_positions[self.cursor_pos]].clone();
|
||||||
|
let mut manager = ContactManager::default();
|
||||||
|
manager.set_parent_id(self.id);
|
||||||
|
manager.card = card;
|
||||||
|
manager.account_pos = self.account_pos;
|
||||||
|
|
||||||
|
self.mode = ViewMode::View(manager.id());
|
||||||
|
self.view = Some(manager);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(ref key) if key == shortcuts["mail_contact"] && self.length > 0 => {
|
||||||
|
let account = &context.accounts[self.account_pos];
|
||||||
|
let book = &account.address_book;
|
||||||
|
let card = &book[&self.id_positions[self.cursor_pos]];
|
||||||
|
let mut draft: Draft = Draft::default();
|
||||||
|
*draft.headers_mut().get_mut("To").unwrap() =
|
||||||
|
format!("{} <{}>", &card.name(), &card.email());
|
||||||
|
context.replies.push_back(UIEvent::Action(Tab(NewDraft(
|
||||||
|
self.account_pos,
|
||||||
|
Some(draft),
|
||||||
|
))));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(ref key) if key == shortcuts["next_account"] => {
|
||||||
|
let amount = if self.cmd_buf.is_empty() {
|
||||||
|
1
|
||||||
|
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
amount
|
||||||
|
} else {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if self.accounts.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if self.account_pos + amount < self.accounts.len() {
|
||||||
|
self.account_pos += amount;
|
||||||
|
self.set_dirty();
|
||||||
|
self.initialized = false;
|
||||||
|
self.cursor_pos = 0;
|
||||||
|
self.new_cursor_pos = 0;
|
||||||
|
self.length = 0;
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||||
|
self.get_status(context).unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(ref key) if key == shortcuts["prev_account"] => {
|
||||||
|
let amount = if self.cmd_buf.is_empty() {
|
||||||
|
1
|
||||||
|
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
amount
|
||||||
|
} else {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if self.accounts.is_empty() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if self.account_pos >= amount {
|
||||||
|
self.account_pos -= amount;
|
||||||
|
self.set_dirty();
|
||||||
|
self.cursor_pos = 0;
|
||||||
|
self.new_cursor_pos = 0;
|
||||||
|
self.length = 0;
|
||||||
|
self.initialized = false;
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
||||||
|
self.get_status(context).unwrap(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(ref k) if k == shortcuts["toggle_menu_visibility"] => {
|
||||||
|
self.menu_visibility = !self.menu_visibility;
|
||||||
self.set_dirty();
|
self.set_dirty();
|
||||||
self.initialized = false;
|
}
|
||||||
|
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) => {
|
||||||
|
self.cmd_buf.clear();
|
||||||
context
|
context
|
||||||
.replies
|
.replies
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
self.get_status(context).unwrap(),
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => {
|
||||||
|
self.cmd_buf.push(c);
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufSet(
|
||||||
|
self.cmd_buf.clone(),
|
||||||
)));
|
)));
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
UIEvent::Input(ref key) if key == shortcuts["prev_account"] && self.view.is_none() => {
|
|
||||||
let amount = if self.cmd_buf.is_empty() {
|
|
||||||
1
|
|
||||||
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
|
||||||
self.cmd_buf.clear();
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
amount
|
|
||||||
} else {
|
|
||||||
self.cmd_buf.clear();
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if self.accounts.is_empty() {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if self.account_pos >= amount {
|
UIEvent::Input(Key::Up) => {
|
||||||
self.account_pos -= amount;
|
let amount = if self.cmd_buf.is_empty() {
|
||||||
|
1
|
||||||
|
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
amount
|
||||||
|
} else {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
self.movement = Some(PageMovement::Up(amount));
|
||||||
self.set_dirty();
|
self.set_dirty();
|
||||||
self.initialized = false;
|
return true;
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::UpdateStatus(
|
|
||||||
self.get_status(context).unwrap(),
|
|
||||||
)));
|
|
||||||
}
|
}
|
||||||
return true;
|
UIEvent::Input(Key::Down) if self.cursor_pos < self.length.saturating_sub(1) => {
|
||||||
}
|
let amount = if self.cmd_buf.is_empty() {
|
||||||
UIEvent::Input(ref k)
|
1
|
||||||
if k == shortcuts["toggle_menu_visibility"] && self.view.is_none() =>
|
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
||||||
{
|
self.cmd_buf.clear();
|
||||||
self.menu_visibility = !self.menu_visibility;
|
context
|
||||||
self.set_dirty();
|
.replies
|
||||||
}
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
UIEvent::Input(Key::Esc) | UIEvent::Input(Key::Alt('')) => {
|
amount
|
||||||
self.cmd_buf.clear();
|
} else {
|
||||||
context
|
self.cmd_buf.clear();
|
||||||
.replies
|
context
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.replies
|
||||||
return true;
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
}
|
return true;
|
||||||
UIEvent::Input(Key::Char(c)) if c >= '0' && c <= '9' => {
|
};
|
||||||
self.cmd_buf.push(c);
|
self.set_dirty();
|
||||||
context
|
self.movement = Some(PageMovement::Down(amount));
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufSet(
|
|
||||||
self.cmd_buf.clone(),
|
|
||||||
)));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
UIEvent::Input(Key::Up) if self.view.is_none() => {
|
|
||||||
let amount = if self.cmd_buf.is_empty() {
|
|
||||||
1
|
|
||||||
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
|
||||||
self.cmd_buf.clear();
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
amount
|
|
||||||
} else {
|
|
||||||
self.cmd_buf.clear();
|
|
||||||
context
|
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
self.movement = Some(PageMovement::Up(amount));
|
UIEvent::Input(Key::PageUp) => {
|
||||||
self.set_dirty();
|
let mult = if self.cmd_buf.is_empty() {
|
||||||
return true;
|
1
|
||||||
}
|
} else if let Ok(mult) = self.cmd_buf.parse::<usize>() {
|
||||||
UIEvent::Input(Key::Down)
|
self.cmd_buf.clear();
|
||||||
if self.cursor_pos < self.length.saturating_sub(1) && self.view.is_none() =>
|
context
|
||||||
{
|
.replies
|
||||||
let amount = if self.cmd_buf.is_empty() {
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
1
|
mult
|
||||||
} else if let Ok(amount) = self.cmd_buf.parse::<usize>() {
|
} else {
|
||||||
self.cmd_buf.clear();
|
self.cmd_buf.clear();
|
||||||
context
|
context
|
||||||
.replies
|
.replies
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
amount
|
return true;
|
||||||
} else {
|
};
|
||||||
self.cmd_buf.clear();
|
self.set_dirty();
|
||||||
context
|
self.movement = Some(PageMovement::PageUp(mult));
|
||||||
.replies
|
|
||||||
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
|
||||||
return true;
|
return true;
|
||||||
};
|
}
|
||||||
self.set_dirty();
|
UIEvent::Input(Key::PageDown) => {
|
||||||
self.movement = Some(PageMovement::Down(amount));
|
let mult = if self.cmd_buf.is_empty() {
|
||||||
return true;
|
1
|
||||||
|
} else if let Ok(mult) = self.cmd_buf.parse::<usize>() {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
mult
|
||||||
|
} else {
|
||||||
|
self.cmd_buf.clear();
|
||||||
|
context
|
||||||
|
.replies
|
||||||
|
.push_back(UIEvent::StatusEvent(StatusEvent::BufClear));
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
self.set_dirty();
|
||||||
|
self.movement = Some(PageMovement::PageDown(mult));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(ref key) if *key == Key::Home => {
|
||||||
|
self.set_dirty();
|
||||||
|
self.movement = Some(PageMovement::Home);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::Input(ref key) if *key == Key::End => {
|
||||||
|
self.set_dirty();
|
||||||
|
self.movement = Some(PageMovement::End);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
UIEvent::ComponentKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => {
|
} else {
|
||||||
self.mode = ViewMode::List;
|
match event {
|
||||||
self.view.take();
|
UIEvent::ComponentKill(ref kill_id) if self.mode == ViewMode::View(*kill_id) => {
|
||||||
self.set_dirty();
|
self.mode = ViewMode::List;
|
||||||
return true;
|
self.view.take();
|
||||||
|
self.set_dirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
UIEvent::ChangeMode(UIMode::Normal) => {
|
||||||
|
self.set_dirty();
|
||||||
|
}
|
||||||
|
UIEvent::Resize => {
|
||||||
|
self.set_dirty();
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
UIEvent::ChangeMode(UIMode::Normal) => {
|
|
||||||
self.set_dirty();
|
|
||||||
}
|
|
||||||
UIEvent::Resize => {
|
|
||||||
self.set_dirty();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,12 +45,6 @@ struct AccountMenuEntry {
|
||||||
// Index in the config account vector.
|
// Index in the config account vector.
|
||||||
index: usize,
|
index: usize,
|
||||||
}
|
}
|
||||||
#[derive(Debug, Default, Clone)]
|
|
||||||
pub(in crate::listing) struct CachedSearchStrings {
|
|
||||||
subject: String,
|
|
||||||
from: String,
|
|
||||||
body: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ListingTrait {
|
pub trait ListingTrait {
|
||||||
fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>);
|
fn coordinates(&self) -> (usize, usize, Option<EnvelopeHash>);
|
||||||
|
|
|
@ -262,26 +262,22 @@ impl Account {
|
||||||
let notify_fn = Arc::new(notify_fn);
|
let notify_fn = Arc::new(notify_fn);
|
||||||
|
|
||||||
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
let data_dir = xdg::BaseDirectories::with_profile("meli", &name).unwrap();
|
||||||
let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
|
let mut address_book = AddressBook::with_account(&settings.account());
|
||||||
|
|
||||||
|
if let Ok(data) = data_dir.place_data_file("addressbook") {
|
||||||
if data.exists() {
|
if data.exists() {
|
||||||
let reader = io::BufReader::new(fs::File::open(data).unwrap());
|
let reader = io::BufReader::new(fs::File::open(data).unwrap());
|
||||||
let metadata = reader.get_ref().metadata().unwrap();
|
|
||||||
let mut permissions = metadata.permissions();
|
|
||||||
|
|
||||||
permissions.set_mode(0o600); // Read/write for owner only.
|
|
||||||
reader.get_ref().set_permissions(permissions).unwrap();
|
|
||||||
let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
|
let result: result::Result<AddressBook, _> = serde_json::from_reader(reader);
|
||||||
if let Ok(data_t) = result {
|
if let Ok(data_t) = result {
|
||||||
data_t
|
for (id, c) in data_t.cards {
|
||||||
} else {
|
if !address_book.card_exists(id) && !c.external_resource() {
|
||||||
AddressBook::new(name.clone())
|
address_book.add_card(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
AddressBook::new(name.clone())
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
AddressBook::new(name.clone())
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if settings.account().format() == "imap" {
|
if settings.account().format() == "imap" {
|
||||||
settings.conf.cache_type = crate::conf::CacheType::None;
|
settings.conf.cache_type = crate::conf::CacheType::None;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue