ui: add contacts and account panel

embed
Manos Pitsidianakis 2019-02-15 09:06:42 +02:00
parent 07a51de0b6
commit 92bb3bf8d3
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
15 changed files with 765 additions and 13 deletions

View File

@ -7,7 +7,7 @@ workspace = ".."
[dependencies]
bitflags = "1.0"
chan = "0.1.21"
chrono = "0.4"
chrono = { version = "0.4", features = ["serde"] }
crossbeam = "^0.3.0"
data-encoding = "2.1.1"
encoding = "0.2.33"
@ -21,3 +21,4 @@ xdg = "2.1.0"
serde = "1.0.71"
serde_derive = "1.0.71"
bincode = "1.0.1"
uuid = { version = "0.6", features = ["serde", "v4"] }

View File

@ -0,0 +1,160 @@
/*
* meli - addressbook module
*
* 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 chrono::{DateTime, Local};
use uuid::Uuid;
use fnv::FnvHashMap;
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct AddressBook {
display_name: String,
created: DateTime<Local>,
last_edited: DateTime<Local>,
cards: FnvHashMap<Uuid, Card>
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
pub struct Card {
uuid: Uuid,
title: String,
firstname: String,
lastname: String,
additionalname: String,
name_prefix: String,
name_suffix: String,
//address
birthday: Option<DateTime<Local>>,
email: String,
url: String,
key: String,
last_edited: DateTime<Local>,
extra_properties: FnvHashMap<String, String>
}
impl AddressBook {
pub fn new(display_name: String) -> AddressBook {
AddressBook {
display_name,
created: Local::now(),
last_edited: Local::now(),
cards: FnvHashMap::default(),
}
}
pub fn add_card(&mut self, card: Card) {
self.cards.insert(card.uuid, card);
}
pub fn remove_card(&mut self, card_uuid: Uuid) {
self.cards.remove(&card_uuid);
}
pub fn card_exists(&self, card_uuid: Uuid) -> bool {
self.cards.contains_key(&card_uuid)
}
}
impl Card {
pub fn new() -> Card {
Card {
uuid: Uuid::new_v4(),
title: String::new(),
firstname: String::new(),
lastname: String::new(),
additionalname: String::new(),
name_prefix: String::new(),
name_suffix: String::new(),
//address
birthday: None,
email: String::new(),
url: String::new(),
key: String::new(),
last_edited: Local::now(),
extra_properties: FnvHashMap::default(),
}
}
pub fn title(&self) -> &str {
self.title.as_str()
}
pub fn firstname(&self) -> &str {
self.firstname.as_str()
}
pub fn lastname(&self) -> &str {
self.lastname.as_str()
}
pub fn additionalname(&self) -> &str {
self.additionalname.as_str()
}
pub fn name_prefix(&self) -> &str {
self.name_prefix.as_str()
}
pub fn name_suffix(&self) -> &str {
self.name_suffix.as_str()
}
pub fn email(&self) -> &str {
self.email.as_str()
}
pub fn url(&self) -> &str {
self.url.as_str()
}
pub fn key(&self) -> &str {
self.key.as_str()
}
pub fn set_title(&mut self, new: &str) {
self.title = new.to_string();()
}
pub fn set_firstname(&mut self, new: &str) {
self.firstname = new.to_string();
}
pub fn set_lastname(&mut self, new: &str) {
self.lastname = new.to_string();
}
pub fn set_additionalname(&mut self, new: &str) {
self.additionalname = new.to_string();
}
pub fn set_name_prefix(&mut self, new: &str) {
self.name_prefix = new.to_string();
}
pub fn set_name_suffix(&mut self, new: &str) {
self.name_suffix = new.to_string();
}
pub fn set_email(&mut self, new: &str) {
self.email = new.to_string();
}
pub fn set_url(&mut self, new: &str) {
self.url = new.to_string();
}
pub fn set_key(&mut self, new: &str) {
self.key = new.to_string();
}
pub fn set_extra_property(&mut self, key: &str, value: String) {
self.extra_properties.insert(key.to_string(), value);
}
pub fn extra_property(&self, key: &str) -> Option<&str> {
self.extra_properties.get(key).map(|v| v.as_str())
}
}

View File

@ -22,6 +22,7 @@ pub mod async;
pub mod conf;
pub mod error;
pub mod mailbox;
pub mod addressbook;
#[macro_use]
extern crate serde_derive;
@ -37,6 +38,8 @@ extern crate chan;
#[macro_use]
extern crate bitflags;
extern crate uuid;
extern crate fnv;
pub use conf::*;
pub use mailbox::*;
@ -44,3 +47,5 @@ pub use mailbox::*;
pub use error::{MeliError, Result};
pub use mailbox::backends::{Backends, RefreshEvent, RefreshEventConsumer};
pub use mailbox::email::{Envelope, Flag};
pub use addressbook::*;

View File

@ -67,6 +67,22 @@ pub enum Address {
Group(GroupAddress),
}
impl Address {
pub fn get_display_name(&self) -> String {
match self {
Address::Mailbox(m) => m.display_name.display(&m.raw),
Address::Group(g) => g.display_name.display(&g.raw),
}
}
pub fn get_email(&self) -> String {
match self {
Address::Mailbox(m) => m.address_spec.display(&m.raw),
Address::Group(_) => String::new(),
}
}
}
impl Eq for Address {}
impl PartialEq for Address {
fn eq(&self, other: &Address) -> bool {

View File

@ -66,7 +66,7 @@ fn main() {
let menu = Entity::from(Box::new(AccountMenu::new(&state.context.accounts)));
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 tabs = Box::new(Tabbed::new(vec![Box::new(VSplit::new(menu, b, 90, true)), Box::new(AccountsPanel::new(&state.context))]));
let window = Entity::from(tabs);
let status_bar = Entity::from(Box::new(StatusBar::new(window)));

View File

@ -19,4 +19,5 @@ nom = "3.2.0"
notify = "4.0.1"
notify-rust = "^3"
termion = "1.5.1"
bincode = "1.0.1"
uuid = { version = "0.6", features = ["serde", "v4"] }

View File

@ -0,0 +1,76 @@
/*
* meli - contacts module
*
* 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 melib::{AddressBook, Card};
#[derive(Debug)]
pub struct ContactManager {
content: CellBuffer,
dirty: bool,
initialized: bool,
}
impl Default for ContactManager {
fn default() -> Self {
ContactManager {
content: CellBuffer::default(),
dirty: true,
initialized: false,
}
}
}
impl fmt::Display for ContactManager {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "contacts")
}
}
impl ContactManager {
}
impl Component for ContactManager {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if !self.initialized {
clear_area(grid, area);
self.initialized = true;
}
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self) {
self.dirty = true;
self.initialized = false;
}
fn kill(&mut self, uuid: Uuid) {
}
}

View File

@ -0,0 +1,105 @@
/*
* meli - ui crate.
*
* 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::*;
#[derive(Debug)]
pub struct ContactsPanel {
content: CellBuffer,
dirty: bool,
}
impl fmt::Display for ContactsPanel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "contacts")
}
}
impl Component for ContactsPanel {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if self.dirty {
self.dirty = false;
}
clear_area(grid, area);
let (width, height) = self.content.size();
copy_area(grid, &self.content, area, ((0, 0), (width - 1, height - 1)));
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self) {
self.dirty = true;
}
}
impl ContactsPanel {
pub fn new(context: &Context) -> ContactsPanel {
let mut content = CellBuffer::new(120, 25 + context.accounts.len() * 20, Cell::default());
write_string_to_grid(
"Contacts",
&mut content,
Color::Default,
Color::Default,
((2, 3), (120 - 1, 3)),
true,
);
for (i, a) in context.accounts.iter().enumerate() {
create_box(&mut content, ((2,5+i*10 ), (120-1, 15+i*10)));
let (x, y) = write_string_to_grid(
a.name(),
&mut content,
Color::Default,
Color::Default,
((3, 5 + i*10), (120 - 2, 5 + i*10)),
true,
);
write_string_to_grid(
" ▒██▒ ",
&mut content,
Color::Byte(32),
Color::Default,
((x, y), (120 - 2, 5 + i*10)),
true,
);
write_string_to_grid(
&a.runtime_settings.account().identity,
&mut content,
Color::Default,
Color::Default,
((4, y + 2), (120 - 2, y + 2)),
true,
);
}
ContactsPanel {
content,
dirty: true,
}
}
}

View File

@ -0,0 +1,159 @@
/*
* meli - accounts module.
*
* 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/>.
*/
mod contacts;
pub use contacts::*;
use super::*;
use std::fmt;
#[derive(Debug)]
pub struct AccountsPanel {
cursor: usize,
content: CellBuffer,
dirty: bool,
}
impl fmt::Display for AccountsPanel {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "accounts")
}
}
impl Component for AccountsPanel {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
if self.dirty {
write_string_to_grid(
"Accounts",
&mut self.content,
Color::Default,
Color::Default,
((2, 3), (120 - 1, 3)),
true,
);
for (i, a) in context.accounts.iter().enumerate() {
create_box(&mut self.content, ((2,5+i*10 ), (120-1, 15+i*10)));
let (x, y) = write_string_to_grid(
a.name(),
&mut self.content,
Color::Default,
Color::Default,
((3, 5 + i*10), (120 - 2, 5 + i*10)),
true,
);
write_string_to_grid(
" ▒██▒ ",
&mut self.content,
Color::Byte(32),
Color::Default,
((x, y), (120 - 2, 5 + i*10)),
true,
);
write_string_to_grid(
&a.runtime_settings.account().identity,
&mut self.content,
Color::Default,
Color::Default,
((4, y + 2), (120 - 2, y + 2)),
true,
);
if i == self.cursor {
for h in 1..8 {
self.content[(2, h+y+1)].set_ch('*');
}
}
write_string_to_grid(
"- Settings",
&mut self.content,
Color::Default,
Color::Default,
((5, y + 3), (120 - 2, y + 3)),
true,
);
write_string_to_grid(
"- Contacts",
&mut self.content,
Color::Default,
Color::Default,
((5, y + 4), (120 - 2, y + 4)),
true,
);
write_string_to_grid(
"- Mailing Lists",
&mut self.content,
Color::Default,
Color::Default,
((5, y + 5), (120 - 2, y + 5)),
true,
);
}
self.dirty = false;
}
clear_area(grid, area);
let (width, height) = self.content.size();
copy_area(grid, &self.content, area, ((0, 0), (width - 1, height - 1)));
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
match event.event_type {
UIEventType::Input(Key::Up) => {
self.cursor = self.cursor.saturating_sub(1);
self.dirty = true;
return true;
},
UIEventType::Input(Key::Down) => {
if self.cursor + 1 < context.accounts.len() {
self.cursor += 1;
self.dirty = true;
}
return true;
},
_ => {},
}
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self) {
self.dirty = true;
}
}
impl AccountsPanel {
pub fn new(context: &Context) -> AccountsPanel {
let mut content = CellBuffer::new(120, 25 + context.accounts.len() * 20, Cell::default());
AccountsPanel {
cursor: 0,
content,
dirty: true,
}
}
}

View File

@ -31,6 +31,9 @@ pub use view::*;
mod compose;
pub use self::compose::*;
mod accounts;
pub use self::accounts::*;
#[derive(Debug)]
struct AccountMenuEntry {
name: String,

View File

@ -22,6 +22,7 @@
use super::*;
use linkify::{Link, LinkFinder};
use std::process::{Command, Stdio};
use std::any::Any;
mod html;
pub use self::html::*;
@ -40,6 +41,7 @@ enum ViewMode {
Attachment(usize),
Raw,
Subview,
ContactSelector(Selector),
}
impl Default for ViewMode {
@ -170,7 +172,8 @@ impl MailView {
let mut ret = "Viewing attachment. Press `r` to return \n".to_string();
ret.push_str(&attachments[aidx].text());
ret
}
},
ViewMode::ContactSelector(_) => { unimplemented!()},
}
}
pub fn plain_text_to_buf(s: &str, highlight_urls: bool) -> CellBuffer {
@ -339,11 +342,15 @@ impl Component for MailView {
};
self.dirty = false;
}
match self.mode {
ViewMode::Subview => {
if let Some(s) = self.subview.as_mut() {
s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context);
}
},
ViewMode::ContactSelector(ref mut s) => {
s.draw(grid, (set_y(upper_left, y + 1), bottom_right), context);
}
_ => {
if let Some(p) = self.pager.as_mut() {
@ -361,7 +368,12 @@ impl Component for MailView {
return true;
}
}
},
ViewMode::ContactSelector(ref mut s) => {
if s.process_event(event, context) {
return true;
}
},
_ => {
if let Some(p) = self.pager.as_mut() {
if p.process_event(event, context) {
@ -372,6 +384,36 @@ impl Component for MailView {
}
match event.event_type {
UIEventType::Input(Key::Char('c')) => {
/*
let mut new_card: Card = Card::new();
new_card.set_email(&envelope.from()[0].get_email());
new_card.set_firstname(&envelope.from()[0].get_display_name());
eprintln!("{:?}", new_card);
*/
match self.mode {
ViewMode::ContactSelector(_) => {
if let ViewMode::ContactSelector(s) = std::mem::replace(&mut self.mode, ViewMode::Normal) {
//eprintln!("{:?}", s.collect());
}
return true;
},
_ => {},
}
let accounts = &context.accounts;
let mailbox = &accounts[self.coordinates.0][self.coordinates.1]
.as_ref()
.unwrap();
let envelope: &Envelope = &mailbox.collection[&self.coordinates.2];
let mut entries = Vec::new();
entries.push((envelope.from()[0].get_email().into_bytes(), format!("{}", envelope.from()[0])));
entries.push((String::from("foo@bar.de").into_bytes(), String::from("Johann de Vir <foo@bar.de>")));
self.mode = ViewMode::ContactSelector(Selector::new(entries, true));
//context.accounts.context(self.coordinates.0).address_book.add_card(new_card);
},
UIEventType::Input(Key::Esc) | UIEventType::Input(Key::Alt('')) => {
self.cmd_buf.clear();
context.replies.push_back(UIEvent {
@ -556,7 +598,7 @@ impl Component for MailView {
true
}
fn is_dirty(&self) -> bool {
self.dirty
self.dirty || true
|| self.pager.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
|| self.subview.as_ref().map(|p| p.is_dirty()).unwrap_or(false)
}

View File

@ -38,6 +38,9 @@ pub use self::indexer::*;
pub mod utilities;
pub use self::utilities::*;
pub mod contacts;
pub use contacts::*;
use std::fmt;
use std::fmt::{Debug, Display};
use std::ops::{Deref, DerefMut};
@ -67,6 +70,11 @@ const LIGHT_DOWN_AND_HORIZONTAL: char = '┬';
const LIGHT_UP_AND_HORIZONTAL: char = '┴';
const DOUBLE_DOWN_AND_RIGHT: char = '╔';
const DOUBLE_DOWN_AND_LEFT: char = '╗';
const DOUBLE_UP_AND_LEFT: char = '╝';
const DOUBLE_UP_AND_RIGHT: char = '╚';
/// `Entity` is a container for Components.
#[derive(Debug)]
pub struct Entity {
@ -405,3 +413,22 @@ pub(crate) fn set_and_join_box(grid: &mut CellBuffer, idx: Pos, ch: char) {
grid[idx].set_ch(bin_to_ch(bin_set));
}
pub fn create_box(grid: &mut CellBuffer, area: Area) {
let upper_left = upper_left!(area);
let bottom_right = bottom_right!(area);
for x in get_x(upper_left)..get_x(bottom_right) {
grid[(x, get_y(upper_left))].set_ch(HORZ_BOUNDARY);
grid[(x, get_y(bottom_right))].set_ch(HORZ_BOUNDARY);
}
for y in get_y(upper_left)..get_y(bottom_right) {
grid[(get_x(upper_left), y)].set_ch(VERT_BOUNDARY);
grid[(get_x(bottom_right), y)].set_ch(VERT_BOUNDARY);
}
set_and_join_box(grid, upper_left, HORZ_BOUNDARY);
set_and_join_box(grid, set_x(upper_left, get_x(bottom_right)), HORZ_BOUNDARY);
set_and_join_box(grid, set_y(upper_left, get_y(bottom_right)), VERT_BOUNDARY);
set_and_join_box(grid, bottom_right, VERT_BOUNDARY);
}

View File

@ -836,3 +836,114 @@ impl Component for Tabbed {
self.children[self.cursor_pos].set_dirty();
}
}
type EntryIdentifier = Vec<u8>;
/// Shows selection to user
#[derive(Debug, PartialEq)]
pub struct Selector {
single_only: bool, /// allow only one selection
entries: Vec<(EntryIdentifier, bool)>,
selected_entry_count: u32,
content: CellBuffer,
cursor: usize,
dirty: bool,
}
impl fmt::Display for Selector {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Display::fmt("Selector", f)
}
}
impl Component for Selector {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
let (width, height) = self.content.size();
copy_area_with_break(
grid,
&self.content,
area,
((0, 0), (width, height)),
);
context.dirty_areas.push_back(area);
}
fn process_event(&mut self, event: &UIEvent, context: &mut Context) -> bool {
let (width, height) = self.content.size();
match event.event_type {
UIEventType::Input(Key::Char(' ')) => {
self.entries[self.cursor].1 = ! self.entries[self.cursor].1;
if self.entries[self.cursor].1 {
write_string_to_grid(
"x",
&mut self.content,
Color::Default,
Color::Default,
((1, self.cursor), (width, self.cursor)),
false,
);
} else {
write_string_to_grid(
" ",
&mut self.content,
Color::Default,
Color::Default,
((1, self.cursor), (width, self.cursor)),
false,
);
}
return true;
},
UIEventType::Input(Key::Up) if self.cursor > 0 => {
self.cursor -= 1;
return true;
},
UIEventType::Input(Key::Down) if self.cursor < height - 1=> {
self.cursor += 1;
return true;
},
_ => {}
}
false
}
fn is_dirty(&self) -> bool {
self.dirty
}
fn set_dirty(&mut self) {
self.dirty = true;
}
}
impl Selector {
pub fn new(mut entries: Vec<(EntryIdentifier, String)>, single_only: bool) -> Selector {
let width = entries.iter().max_by_key(|e| e.1.len()).map(|v| v.1.len()).unwrap_or(0) + 4;
let height = entries.len();
let mut content = CellBuffer::new(width, height, Cell::with_char(' '));
let identifiers = entries.iter_mut().map(|(id, _)| (std::mem::replace(&mut *id, Vec::new()), false)).collect();
for (i, e) in entries.into_iter().enumerate() {
write_string_to_grid(
&format!("[ ] {}", e.1),
&mut content,
Color::Default,
Color::Default,
((0, i), (width - 1, i)),
false,
);
}
Selector {
single_only,
entries: identifiers,
selected_entry_count: 0,
content,
cursor: 0,
dirty: true,
}
}
pub fn collect(self) -> Vec<EntryIdentifier> {
self.entries.into_iter().filter(|v| v.1).map(|(id, _)| id).collect()
}
}

View File

@ -30,6 +30,10 @@ use mailbox::backends::{
};
use mailbox::*;
use melib::error::Result;
use melib::AddressBook;
use std::fs;
use std::io;
use std::mem;
use std::ops::{Index, IndexMut};
use std::result;
@ -48,17 +52,38 @@ macro_rules! mailbox {
pub struct Account {
name: String,
folders: Vec<Option<Result<Mailbox>>>,
pub workers: Vec<Worker>,
sent_folder: Option<usize>,
pub settings: AccountConf,
pub runtime_settings: AccountConf,
pub backend: Box<MailBackend>,
pub(crate) address_book: AddressBook,
pub(crate) workers: Vec<Worker>,
pub(crate) settings: AccountConf,
pub(crate) runtime_settings: AccountConf,
pub(crate) backend: Box<MailBackend>,
notify_fn: Arc<NotifyFn>,
}
impl Drop for Account {
fn drop(&mut self) {
let data_dir =
xdg::BaseDirectories::with_profile("meli", &self.name)
.unwrap();
if let Ok(data) = data_dir.place_data_file("addressbook") {
/* place result in cache directory */
let f = match fs::File::create(data) {
Ok(f) => f,
Err(e) => {
panic!("{}", e);
}
};
let writer = io::BufWriter::new(f);
bincode::serialize_into(writer, &self.address_book).unwrap();
};
}
}
impl Account {
pub fn new(name: String, settings: AccountConf, map: &Backends, notify_fn: NotifyFn) -> Self {
let mut backend = map.get(settings.account().format())(settings.account());
@ -73,12 +98,31 @@ impl Account {
folders.push(None);
workers.push(Account::new_worker(f, &mut backend, notify_fn.clone()));
}
eprintln!("sent_folder for {} is {:?}", name, sent_folder);
let data_dir =
xdg::BaseDirectories::with_profile("meli", &name)
.unwrap();
let address_book = if let Ok(data) = data_dir.place_data_file("addressbook") {
if data.exists() {
let reader = io::BufReader::new(fs::File::open(data).unwrap());
let result: result::Result<AddressBook, _> = bincode::deserialize_from(reader);
if let Ok(mut data_t) = result {
data_t
} else {
AddressBook::new(name.clone())
}
} else {
AddressBook::new(name.clone())
}
} else {
AddressBook::new(name.clone())
};
Account {
name,
folders,
workers,
address_book,
sent_folder,
workers,
settings: settings.clone(),
runtime_settings: settings,
backend,

View File

@ -22,6 +22,8 @@
extern crate config;
extern crate serde;
extern crate xdg;
extern crate bincode;
pub mod pager;
pub mod accounts;