wallet: accounts selection, fix config access from different threads
This commit is contained in:
parent
d628390e97
commit
4b48195b75
14 changed files with 337 additions and 122 deletions
|
@ -54,7 +54,7 @@ wallets:
|
||||||
tx_loading: Loading transactions
|
tx_loading: Loading transactions
|
||||||
default_account: Default account
|
default_account: Default account
|
||||||
create_account: Create account
|
create_account: Create account
|
||||||
label: 'Label:'
|
accounts: Accounts
|
||||||
tx_sent: Sent
|
tx_sent: Sent
|
||||||
tx_received: Received
|
tx_received: Received
|
||||||
tx_sending: Sending
|
tx_sending: Sending
|
||||||
|
|
|
@ -54,7 +54,7 @@ wallets:
|
||||||
tx_loading: Загрузка транзакций
|
tx_loading: Загрузка транзакций
|
||||||
default_account: Стандартный аккаунт
|
default_account: Стандартный аккаунт
|
||||||
create_account: Создать аккаунт
|
create_account: Создать аккаунт
|
||||||
label: 'Метка:'
|
accounts: Аккаунты
|
||||||
tx_sent: Отправлено
|
tx_sent: Отправлено
|
||||||
tx_received: Получено
|
tx_received: Получено
|
||||||
tx_sending: Отправка
|
tx_sending: Отправка
|
||||||
|
|
|
@ -145,7 +145,7 @@ impl WalletsContent {
|
||||||
let list = self.wallets.mut_list();
|
let list = self.wallets.mut_list();
|
||||||
for wallet in list {
|
for wallet in list {
|
||||||
// Show content for selected wallet.
|
// Show content for selected wallet.
|
||||||
if selected_id == Some(wallet.config.id) {
|
if selected_id == Some(wallet.get_config().id) {
|
||||||
// Setup wallet content width.
|
// Setup wallet content width.
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
let mut width = ui.available_width();
|
let mut width = ui.available_width();
|
||||||
|
@ -345,7 +345,7 @@ impl WalletsContent {
|
||||||
// Check if wallet reopen is needed.
|
// Check if wallet reopen is needed.
|
||||||
if !wallet.is_open() && wallet.reopen_needed() {
|
if !wallet.is_open() && wallet.reopen_needed() {
|
||||||
wallet.set_reopen(false);
|
wallet.set_reopen(false);
|
||||||
self.wallets.select(Some(wallet.config.id));
|
self.wallets.select(Some(wallet.get_config().id));
|
||||||
self.show_open_wallet_modal(cb);
|
self.show_open_wallet_modal(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -371,7 +371,8 @@ impl WalletsContent {
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
wallet: &Wallet,
|
wallet: &Wallet,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
let id = wallet.config.id;
|
let config = wallet.get_config();
|
||||||
|
let id = config.id;
|
||||||
let is_selected = self.wallets.selected_id == Some(id);
|
let is_selected = self.wallets.selected_id == Some(id);
|
||||||
let is_current = wallet.is_open() && is_selected;
|
let is_current = wallet.is_open() && is_selected;
|
||||||
|
|
||||||
|
@ -422,7 +423,7 @@ impl WalletsContent {
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
// Setup wallet name text.
|
// Setup wallet name text.
|
||||||
let name_color = if is_selected { Colors::BLACK } else { Colors::TITLE };
|
let name_color = if is_selected { Colors::BLACK } else { Colors::TITLE };
|
||||||
View::ellipsize_text(ui, wallet.config.name.to_owned(), 18.0, name_color);
|
View::ellipsize_text(ui, config.name, 18.0, name_color);
|
||||||
|
|
||||||
// Setup wallet connection text.
|
// Setup wallet connection text.
|
||||||
let conn_text = if let Some(id) = wallet.get_current_ext_conn_id() {
|
let conn_text = if let Some(id) = wallet.get_current_ext_conn_id() {
|
||||||
|
|
|
@ -75,17 +75,18 @@ impl CommonSetup {
|
||||||
self.modal_content_ui(ui, wallet, cb);
|
self.modal_content_ui(ui, wallet, cb);
|
||||||
|
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
|
let wallet_name = wallet.get_config().name;
|
||||||
// Show wallet name.
|
// Show wallet name.
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
ui.label(RichText::new(t!("wallets.name")).size(16.0).color(Colors::GRAY));
|
ui.label(RichText::new(t!("wallets.name")).size(16.0).color(Colors::GRAY));
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
ui.label(RichText::new(wallet.config.name.clone()).size(16.0).color(Colors::BLACK));
|
ui.label(RichText::new(wallet_name.clone()).size(16.0).color(Colors::BLACK));
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
|
||||||
// Show wallet name setup.
|
// Show wallet name setup.
|
||||||
let name_text = format!("{} {}", PENCIL, t!("change"));
|
let name_text = format!("{} {}", PENCIL, t!("change"));
|
||||||
View::button(ui, name_text, Colors::BUTTON, || {
|
View::button(ui, name_text, Colors::BUTTON, || {
|
||||||
self.name_edit = wallet.config.name.clone();
|
self.name_edit = wallet_name;
|
||||||
// Show wallet name modal.
|
// Show wallet name modal.
|
||||||
Modal::new(NAME_EDIT_MODAL)
|
Modal::new(NAME_EDIT_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
|
@ -125,9 +126,10 @@ impl CommonSetup {
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
|
|
||||||
// Show minimum amount of confirmations value setup.
|
// Show minimum amount of confirmations value setup.
|
||||||
let min_conf_text = format!("{} {}", CLOCK_COUNTDOWN, wallet.config.min_confirmations);
|
let min_confirmations = wallet.get_config().min_confirmations;
|
||||||
|
let min_conf_text = format!("{} {}", CLOCK_COUNTDOWN, min_confirmations);
|
||||||
View::button(ui, min_conf_text, Colors::BUTTON, || {
|
View::button(ui, min_conf_text, Colors::BUTTON, || {
|
||||||
self.min_confirmations_edit = wallet.config.min_confirmations.to_string();
|
self.min_confirmations_edit = min_confirmations.to_string();
|
||||||
// Show minimum amount of confirmations value modal.
|
// Show minimum amount of confirmations value modal.
|
||||||
Modal::new(MIN_CONFIRMATIONS_EDIT_MODAL)
|
Modal::new(MIN_CONFIRMATIONS_EDIT_MODAL)
|
||||||
.position(ModalPosition::CenterTop)
|
.position(ModalPosition::CenterTop)
|
||||||
|
@ -187,7 +189,7 @@ impl CommonSetup {
|
||||||
|
|
||||||
// Draw wallet name edit.
|
// Draw wallet name edit.
|
||||||
let text_edit_resp = egui::TextEdit::singleline(&mut self.name_edit)
|
let text_edit_resp = egui::TextEdit::singleline(&mut self.name_edit)
|
||||||
.id(Id::from(modal.id).with(wallet.config.id))
|
.id(Id::from(modal.id).with(wallet.get_config().id))
|
||||||
.font(TextStyle::Heading)
|
.font(TextStyle::Heading)
|
||||||
.desired_width(ui.available_width())
|
.desired_width(ui.available_width())
|
||||||
.cursor_at_end(true)
|
.cursor_at_end(true)
|
||||||
|
@ -216,8 +218,7 @@ impl CommonSetup {
|
||||||
// Save button callback.
|
// Save button callback.
|
||||||
let mut on_save = || {
|
let mut on_save = || {
|
||||||
if !self.name_edit.is_empty() {
|
if !self.name_edit.is_empty() {
|
||||||
wallet.config.name = self.name_edit.clone();
|
wallet.change_name(self.name_edit.clone());
|
||||||
wallet.config.save();
|
|
||||||
cb.hide_keyboard();
|
cb.hide_keyboard();
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
}
|
||||||
|
@ -240,6 +241,8 @@ impl CommonSetup {
|
||||||
wallet: &mut Wallet,
|
wallet: &mut Wallet,
|
||||||
modal: &Modal,
|
modal: &Modal,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
|
let wallet_id = wallet.get_config().id;
|
||||||
|
|
||||||
ui.add_space(6.0);
|
ui.add_space(6.0);
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
ui.label(RichText::new(t!("wallets.current_pass"))
|
ui.label(RichText::new(t!("wallets.current_pass"))
|
||||||
|
@ -260,7 +263,7 @@ impl CommonSetup {
|
||||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||||
// Draw current wallet password text edit.
|
// Draw current wallet password text edit.
|
||||||
let old_pass_resp = egui::TextEdit::singleline(&mut self.current_pass_edit)
|
let old_pass_resp = egui::TextEdit::singleline(&mut self.current_pass_edit)
|
||||||
.id(Id::from(modal.id).with(wallet.config.id).with("old_pass"))
|
.id(Id::from(modal.id).with(wallet_id).with("old_pass"))
|
||||||
.font(TextStyle::Heading)
|
.font(TextStyle::Heading)
|
||||||
.desired_width(ui.available_width())
|
.desired_width(ui.available_width())
|
||||||
.cursor_at_end(true)
|
.cursor_at_end(true)
|
||||||
|
@ -297,7 +300,7 @@ impl CommonSetup {
|
||||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||||
// Draw new wallet password text edit.
|
// Draw new wallet password text edit.
|
||||||
let new_pass_resp = egui::TextEdit::singleline(&mut self.new_pass_edit)
|
let new_pass_resp = egui::TextEdit::singleline(&mut self.new_pass_edit)
|
||||||
.id(Id::from(modal.id).with(wallet.config.id).with("new_pass"))
|
.id(Id::from(modal.id).with(wallet_id).with("new_pass"))
|
||||||
.font(TextStyle::Heading)
|
.font(TextStyle::Heading)
|
||||||
.desired_width(ui.available_width())
|
.desired_width(ui.available_width())
|
||||||
.cursor_at_end(true)
|
.cursor_at_end(true)
|
||||||
|
@ -426,7 +429,7 @@ impl CommonSetup {
|
||||||
// Save button callback.
|
// Save button callback.
|
||||||
let mut on_save = || {
|
let mut on_save = || {
|
||||||
if let Ok(min_conf) = self.min_confirmations_edit.parse::<u64>() {
|
if let Ok(min_conf) = self.min_confirmations_edit.parse::<u64>() {
|
||||||
wallet.config.min_confirmations = min_conf;
|
wallet.update_min_confirmations(min_conf);
|
||||||
cb.hide_keyboard();
|
cb.hide_keyboard();
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl ConnectionSetup {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup connection value from provided wallet.
|
// Setup connection value from provided wallet.
|
||||||
match wallet.config.ext_conn_id {
|
match wallet.get_config().ext_conn_id {
|
||||||
None => self.method = ConnectionMethod::Integrated,
|
None => self.method = ConnectionMethod::Integrated,
|
||||||
Some(id) => self.method = ConnectionMethod::External(id)
|
Some(id) => self.method = ConnectionMethod::External(id)
|
||||||
}
|
}
|
||||||
|
@ -112,25 +112,25 @@ impl ConnectionSetup {
|
||||||
self.ui(ui, frame, cb);
|
self.ui(ui, frame, cb);
|
||||||
|
|
||||||
// Setup wallet connection value after change.
|
// Setup wallet connection value after change.
|
||||||
|
let config = wallet.get_config();
|
||||||
let changed = match self.method {
|
let changed = match self.method {
|
||||||
ConnectionMethod::Integrated => {
|
ConnectionMethod::Integrated => {
|
||||||
let changed = wallet.config.ext_conn_id.is_some();
|
let changed = config.ext_conn_id.is_some();
|
||||||
if changed {
|
if changed {
|
||||||
wallet.config.ext_conn_id = None;
|
wallet.update_ext_conn_id(None);
|
||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
ConnectionMethod::External(id) => {
|
ConnectionMethod::External(id) => {
|
||||||
let changed = wallet.config.ext_conn_id != Some(id);
|
let changed = config.ext_conn_id != Some(id);
|
||||||
if changed {
|
if changed {
|
||||||
wallet.config.ext_conn_id = Some(id);
|
wallet.update_ext_conn_id(Some(id));
|
||||||
}
|
}
|
||||||
changed
|
changed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
wallet.config.save();
|
|
||||||
// Show reopen confirmation modal.
|
// Show reopen confirmation modal.
|
||||||
Modal::new(REOPEN_WALLET_CONFIRMATION_MODAL)
|
Modal::new(REOPEN_WALLET_CONFIRMATION_MODAL)
|
||||||
.position(ModalPosition::Center)
|
.position(ModalPosition::Center)
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl RecoverySetup {
|
||||||
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||||
// Draw current wallet password text edit.
|
// Draw current wallet password text edit.
|
||||||
let pass_resp = egui::TextEdit::singleline(&mut self.pass_edit)
|
let pass_resp = egui::TextEdit::singleline(&mut self.pass_edit)
|
||||||
.id(Id::from(modal.id).with(wallet.config.id).with("recovery_phrase"))
|
.id(Id::from(modal.id).with(wallet.get_config().id).with("recovery_phrase"))
|
||||||
.font(TextStyle::Heading)
|
.font(TextStyle::Heading)
|
||||||
.desired_width(ui.available_width())
|
.desired_width(ui.available_width())
|
||||||
.cursor_at_end(true)
|
.cursor_at_end(true)
|
||||||
|
|
|
@ -14,13 +14,13 @@
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use egui::{Align, Id, Layout, Margin, RichText, Rounding, TextStyle, Widget};
|
use egui::{Align, Id, Layout, Margin, RichText, Rounding, ScrollArea, TextStyle, Widget};
|
||||||
use grin_chain::SyncStatus;
|
use grin_chain::SyncStatus;
|
||||||
use grin_core::core::amount_to_hr_string;
|
use grin_core::core::amount_to_hr_string;
|
||||||
|
|
||||||
use crate::AppConfig;
|
use crate::AppConfig;
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{DOWNLOAD, FILE_ARCHIVE, GEAR_FINE, LIST, PACKAGE, PLUS, POWER, REPEAT, UPLOAD, WALLET};
|
use crate::gui::icons::{CHECK, CHECK_FAT, CREDIT_CARD, DOWNLOAD, FILE_ARCHIVE, GEAR_FINE, LIST, PACKAGE, PLUS, POWER, REPEAT, UPLOAD, WALLET};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, Root, View};
|
use crate::gui::views::{Modal, Root, View};
|
||||||
use crate::gui::views::types::ModalPosition;
|
use crate::gui::views::types::ModalPosition;
|
||||||
|
@ -28,14 +28,17 @@ use crate::gui::views::wallets::{WalletInfo, WalletReceive, WalletSend, WalletSe
|
||||||
use crate::gui::views::wallets::types::{WalletTab, WalletTabType};
|
use crate::gui::views::wallets::types::{WalletTab, WalletTabType};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
use crate::wallet::{Wallet, WalletConfig};
|
use crate::wallet::{Wallet, WalletConfig};
|
||||||
use crate::wallet::types::WalletData;
|
use crate::wallet::types::{GRIN, WalletAccount, WalletData};
|
||||||
|
|
||||||
/// Selected and opened wallet content.
|
/// Selected and opened wallet content.
|
||||||
pub struct WalletContent {
|
pub struct WalletContent {
|
||||||
|
/// List of wallet accounts for [`Modal`].
|
||||||
|
accounts: Vec<WalletAccount>,
|
||||||
|
|
||||||
/// Account label [`Modal`] value.
|
/// Account label [`Modal`] value.
|
||||||
pub account_label_edit: String,
|
account_label_edit: String,
|
||||||
/// Flag to check if error occurred during account creation at [`Modal`].
|
/// Flag to check if error occurred during account creation at [`Modal`].
|
||||||
pub account_creation_error: bool,
|
account_creation_error: bool,
|
||||||
|
|
||||||
/// Current tab content to show.
|
/// Current tab content to show.
|
||||||
pub current_tab: Box<dyn WalletTab>,
|
pub current_tab: Box<dyn WalletTab>,
|
||||||
|
@ -44,6 +47,7 @@ pub struct WalletContent {
|
||||||
impl Default for WalletContent {
|
impl Default for WalletContent {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
accounts: vec![],
|
||||||
account_label_edit: "".to_string(),
|
account_label_edit: "".to_string(),
|
||||||
account_creation_error: false,
|
account_creation_error: false,
|
||||||
current_tab: Box::new(WalletInfo::default())
|
current_tab: Box::new(WalletInfo::default())
|
||||||
|
@ -53,6 +57,8 @@ impl Default for WalletContent {
|
||||||
|
|
||||||
/// Identifier for account creation [`Modal`].
|
/// Identifier for account creation [`Modal`].
|
||||||
const CREATE_ACCOUNT_MODAL: &'static str = "create_account_modal";
|
const CREATE_ACCOUNT_MODAL: &'static str = "create_account_modal";
|
||||||
|
/// Identifier for account list [`Modal`].
|
||||||
|
const ACCOUNT_LIST_MODAL: &'static str = "account_list_modal";
|
||||||
|
|
||||||
impl WalletContent {
|
impl WalletContent {
|
||||||
pub fn ui(&mut self,
|
pub fn ui(&mut self,
|
||||||
|
@ -90,7 +96,7 @@ impl WalletContent {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
// Draw wallet tabs.
|
// Draw wallet tabs.
|
||||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.35, |ui| {
|
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.35, |ui| {
|
||||||
Self::account_ui(ui, data.as_ref().unwrap(), &wallet.config.account, cb);
|
self.account_ui(ui, wallet, data.unwrap(), cb);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -149,6 +155,11 @@ impl WalletContent {
|
||||||
Modal::ui(ui.ctx(), |ui, modal| {
|
Modal::ui(ui.ctx(), |ui, modal| {
|
||||||
self.create_account_modal_ui(ui, wallet, modal, cb);
|
self.create_account_modal_ui(ui, wallet, modal, cb);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
ACCOUNT_LIST_MODAL => {
|
||||||
|
Modal::ui(ui.ctx(), |ui, modal| {
|
||||||
|
self.account_list_modal_ui(ui, wallet, modal);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -157,9 +168,10 @@ impl WalletContent {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw wallet account content.
|
/// Draw wallet account content.
|
||||||
fn account_ui(ui: &mut egui::Ui,
|
fn account_ui(&mut self,
|
||||||
data: &WalletData,
|
ui: &mut egui::Ui,
|
||||||
account: &String,
|
wallet: &Wallet,
|
||||||
|
data: WalletData,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
rect.set_height(75.0);
|
rect.set_height(75.0);
|
||||||
|
@ -182,8 +194,14 @@ impl WalletContent {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Draw button to show list of accounts.
|
// Draw button to show list of accounts.
|
||||||
View::item_button(ui, Rounding::none(), LIST, None, || {
|
View::item_button(ui, Rounding::ZERO, LIST, None, || {
|
||||||
//TODO: accounts list modal
|
// Load accounts.
|
||||||
|
self.accounts = wallet.accounts();
|
||||||
|
// Show account list modal.
|
||||||
|
Modal::new(ACCOUNT_LIST_MODAL)
|
||||||
|
.position(ModalPosition::Center)
|
||||||
|
.title(t!("wallets.accounts"))
|
||||||
|
.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
let layout_size = ui.available_size();
|
let layout_size = ui.available_size();
|
||||||
|
@ -193,18 +211,19 @@ impl WalletContent {
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
// Show spendable amount.
|
// Show spendable amount.
|
||||||
let amount = amount_to_hr_string(data.info.amount_currently_spendable, false);
|
let amount = amount_to_hr_string(data.info.amount_currently_spendable, false);
|
||||||
let amount_text = format!("{} ツ", amount);
|
let amount_text = format!("{} {}", amount, GRIN);
|
||||||
ui.label(RichText::new(amount_text).size(18.0).color(Colors::BLACK));
|
ui.label(RichText::new(amount_text).size(18.0).color(Colors::BLACK));
|
||||||
|
ui.add_space(-2.0);
|
||||||
|
|
||||||
// Show account label.
|
// Show account label.
|
||||||
let default_acc_label = &WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string();
|
let account = wallet.get_config().account;
|
||||||
|
let default_acc_label = WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string();
|
||||||
let acc_label = if account == default_acc_label {
|
let acc_label = if account == default_acc_label {
|
||||||
t!("wallets.default_account")
|
t!("wallets.default_account")
|
||||||
} else {
|
} else {
|
||||||
account.to_owned()
|
account.to_owned()
|
||||||
};
|
};
|
||||||
let acc_text = format!("{} {}", FILE_ARCHIVE, acc_label);
|
let acc_text = format!("{} {}", FILE_ARCHIVE, acc_label);
|
||||||
ui.add_space(-2.0);
|
|
||||||
View::ellipsize_text(ui, acc_text, 15.0, Colors::TEXT);
|
View::ellipsize_text(ui, acc_text, 15.0, Colors::TEXT);
|
||||||
|
|
||||||
// Show confirmed height.
|
// Show confirmed height.
|
||||||
|
@ -215,6 +234,43 @@ impl WalletContent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Draw account list [`Modal`] content.
|
||||||
|
fn account_list_modal_ui(&mut self, ui: &mut egui::Ui, wallet: &mut Wallet, modal: &Modal) {
|
||||||
|
ui.add_space(3.0);
|
||||||
|
|
||||||
|
// Show list of accounts.
|
||||||
|
let size = self.accounts.len();
|
||||||
|
ScrollArea::vertical()
|
||||||
|
.max_height(300.0)
|
||||||
|
.id_source("account_list_modal_scroll")
|
||||||
|
.auto_shrink([true; 2])
|
||||||
|
.show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| {
|
||||||
|
for index in row_range {
|
||||||
|
// Add space before the first item.
|
||||||
|
if index == 0 {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
}
|
||||||
|
let acc = self.accounts.get(index).unwrap();
|
||||||
|
account_item_ui(ui, modal, wallet, acc, index, size);
|
||||||
|
if index == size - 1 {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(2.0);
|
||||||
|
View::horizontal_line(ui, Colors::STROKE);
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
ui.vertical_centered_justified(|ui| {
|
||||||
|
View::button(ui, t!("close"), Colors::WHITE, || {
|
||||||
|
// Close modal.
|
||||||
|
modal.close();
|
||||||
|
});
|
||||||
|
ui.add_space(6.0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// Draw account creation [`Modal`] content.
|
/// Draw account creation [`Modal`] content.
|
||||||
fn create_account_modal_ui(&mut self,
|
fn create_account_modal_ui(&mut self,
|
||||||
ui: &mut egui::Ui,
|
ui: &mut egui::Ui,
|
||||||
|
@ -230,7 +286,7 @@ impl WalletContent {
|
||||||
|
|
||||||
// Draw account name edit.
|
// Draw account name edit.
|
||||||
let text_edit_resp = egui::TextEdit::singleline(&mut self.account_label_edit)
|
let text_edit_resp = egui::TextEdit::singleline(&mut self.account_label_edit)
|
||||||
.id(Id::from(modal.id).with(wallet.config.id))
|
.id(Id::from(modal.id).with(wallet.get_config().id))
|
||||||
.font(TextStyle::Heading)
|
.font(TextStyle::Heading)
|
||||||
.desired_width(ui.available_width())
|
.desired_width(ui.available_width())
|
||||||
.cursor_at_end(true)
|
.cursor_at_end(true)
|
||||||
|
@ -240,7 +296,6 @@ impl WalletContent {
|
||||||
cb.show_keyboard();
|
cb.show_keyboard();
|
||||||
}
|
}
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
});
|
|
||||||
|
|
||||||
// Show error occurred during account creation..
|
// Show error occurred during account creation..
|
||||||
if self.account_creation_error {
|
if self.account_creation_error {
|
||||||
|
@ -249,6 +304,8 @@ impl WalletContent {
|
||||||
.size(17.0)
|
.size(17.0)
|
||||||
.color(Colors::RED));
|
.color(Colors::RED));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
ui.add_space(12.0);
|
ui.add_space(12.0);
|
||||||
|
|
||||||
// Show modal buttons.
|
// Show modal buttons.
|
||||||
|
@ -270,12 +327,10 @@ impl WalletContent {
|
||||||
if !self.account_label_edit.is_empty() {
|
if !self.account_label_edit.is_empty() {
|
||||||
let label = &self.account_label_edit;
|
let label = &self.account_label_edit;
|
||||||
match wallet.create_account(label) {
|
match wallet.create_account(label) {
|
||||||
Ok(_) => match wallet.set_active_account(label) {
|
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
|
let _ = wallet.set_active_account(label);
|
||||||
cb.hide_keyboard();
|
cb.hide_keyboard();
|
||||||
modal.close();
|
modal.close();
|
||||||
}
|
|
||||||
Err(_) => self.account_creation_error = true
|
|
||||||
},
|
},
|
||||||
Err(_) => self.account_creation_error = true
|
Err(_) => self.account_creation_error = true
|
||||||
};
|
};
|
||||||
|
@ -299,7 +354,7 @@ impl WalletContent {
|
||||||
// Setup spacing between tabs.
|
// Setup spacing between tabs.
|
||||||
ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0);
|
ui.style_mut().spacing.item_spacing = egui::vec2(4.0, 0.0);
|
||||||
// Setup vertical padding inside tab button.
|
// Setup vertical padding inside tab button.
|
||||||
ui.style_mut().spacing.button_padding = egui::vec2(0.0, 8.0);
|
ui.style_mut().spacing.button_padding = egui::vec2(0.0, 4.0);
|
||||||
|
|
||||||
// Draw tab buttons.
|
// Draw tab buttons.
|
||||||
let current_type = self.current_tab.get_type();
|
let current_type = self.current_tab.get_type();
|
||||||
|
@ -407,6 +462,7 @@ impl WalletContent {
|
||||||
let integrated_node = wallet.get_current_ext_conn_id().is_none();
|
let integrated_node = wallet.get_current_ext_conn_id().is_none();
|
||||||
let integrated_node_ready = Node::get_sync_status() == Some(SyncStatus::NoSync);
|
let integrated_node_ready = Node::get_sync_status() == Some(SyncStatus::NoSync);
|
||||||
let info_progress = wallet.info_sync_progress();
|
let info_progress = wallet.info_sync_progress();
|
||||||
|
|
||||||
if wallet.is_closing() {
|
if wallet.is_closing() {
|
||||||
t!("wallets.wallet_closing")
|
t!("wallets.wallet_closing")
|
||||||
} else if integrated_node && !integrated_node_ready {
|
} else if integrated_node && !integrated_node_ready {
|
||||||
|
@ -437,3 +493,65 @@ impl WalletContent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ACCOUNT_ITEM_HEIGHT: f32 = 75.0;
|
||||||
|
|
||||||
|
/// Draw account item.
|
||||||
|
fn account_item_ui(ui: &mut egui::Ui,
|
||||||
|
modal: &Modal,
|
||||||
|
wallet: &mut Wallet,
|
||||||
|
acc: &WalletAccount,
|
||||||
|
index: usize,
|
||||||
|
size: usize) {
|
||||||
|
// Setup layout size.
|
||||||
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
|
rect.set_height(ACCOUNT_ITEM_HEIGHT);
|
||||||
|
|
||||||
|
// Draw round background.
|
||||||
|
let bg_rect = rect.clone();
|
||||||
|
let item_rounding = View::item_rounding(index, size, false);
|
||||||
|
ui.painter().rect(bg_rect, item_rounding, Colors::FILL, View::ITEM_STROKE);
|
||||||
|
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| {
|
||||||
|
// Draw button to select account.
|
||||||
|
let is_current_account = wallet.get_config().account == acc.label;
|
||||||
|
if !is_current_account {
|
||||||
|
let button_rounding = View::item_rounding(index, size, true);
|
||||||
|
View::item_button(ui, button_rounding, CHECK, None, || {
|
||||||
|
let _ = wallet.set_active_account(&acc.label);
|
||||||
|
modal.close();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ui.add_space(12.0);
|
||||||
|
ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::GREEN));
|
||||||
|
}
|
||||||
|
|
||||||
|
let layout_size = ui.available_size();
|
||||||
|
ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
ui.vertical(|ui| {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
// Show spendable amount.
|
||||||
|
let amount = amount_to_hr_string(acc.spendable_amount, false);
|
||||||
|
let amount_text = format!("{} {}", amount, GRIN);
|
||||||
|
ui.label(RichText::new(amount_text).size(18.0).color(Colors::BLACK));
|
||||||
|
ui.add_space(-2.0);
|
||||||
|
|
||||||
|
// Show account name.
|
||||||
|
let default_acc_label = WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string();
|
||||||
|
let acc_label = if acc.label == default_acc_label {
|
||||||
|
t!("wallets.default_account")
|
||||||
|
} else {
|
||||||
|
acc.label.to_owned()
|
||||||
|
};
|
||||||
|
View::ellipsize_text(ui, acc_label, 15.0, Colors::TEXT);
|
||||||
|
|
||||||
|
// Show account BIP32 derivation path.
|
||||||
|
ui.label(RichText::new(acc.path.to_owned()).size(15.0).color(Colors::GRAY));
|
||||||
|
ui.add_space(3.0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -133,7 +133,7 @@ impl WalletInfo {
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
|
.scroll_bar_visibility(ScrollBarVisibility::AlwaysVisible)
|
||||||
.id_source(Id::from("txs_content").with(wallet.config.id))
|
.id_source(Id::from("txs_content").with(wallet.get_config().id))
|
||||||
.auto_shrink([false; 2])
|
.auto_shrink([false; 2])
|
||||||
.show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| {
|
.show_rows(ui, TX_ITEM_HEIGHT, txs_size, |ui, row_range| {
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
|
@ -244,12 +244,13 @@ fn tx_item_ui(ui: &mut egui::Ui,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let tx_height = tx.kernel_lookup_min_height.unwrap_or(0);
|
let tx_height = tx.kernel_lookup_min_height.unwrap_or(0);
|
||||||
|
let min_confirmations = wallet.get_config().min_confirmations;
|
||||||
match tx.tx_type {
|
match tx.tx_type {
|
||||||
TxLogEntryType::ConfirmedCoinbase => {
|
TxLogEntryType::ConfirmedCoinbase => {
|
||||||
format!("{} {}", CHECK_CIRCLE, t!("wallets.tx_confirmed"))
|
format!("{} {}", CHECK_CIRCLE, t!("wallets.tx_confirmed"))
|
||||||
},
|
},
|
||||||
TxLogEntryType::TxReceived => {
|
TxLogEntryType::TxReceived => {
|
||||||
if last_height - tx_height > wallet.config.min_confirmations {
|
if last_height - tx_height > min_confirmations {
|
||||||
format!("{} {}", ARROW_CIRCLE_DOWN, t!("wallets.tx_received"))
|
format!("{} {}", ARROW_CIRCLE_DOWN, t!("wallets.tx_received"))
|
||||||
} else {
|
} else {
|
||||||
format!("{} {}",
|
format!("{} {}",
|
||||||
|
@ -258,7 +259,7 @@ fn tx_item_ui(ui: &mut egui::Ui,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TxLogEntryType::TxSent => {
|
TxLogEntryType::TxSent => {
|
||||||
if last_height - tx_height > wallet.config.min_confirmations {
|
if last_height - tx_height > min_confirmations {
|
||||||
format!("{} {}", ARROW_CIRCLE_DOWN, t!("wallets.tx_sent"))
|
format!("{} {}", ARROW_CIRCLE_DOWN, t!("wallets.tx_sent"))
|
||||||
} else {
|
} else {
|
||||||
format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_confirming"))
|
format!("{} {}", DOTS_THREE_CIRCLE, t!("wallets.tx_confirming"))
|
||||||
|
|
|
@ -117,7 +117,7 @@ impl WalletReceive {
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
.max_height(128.0)
|
.max_height(128.0)
|
||||||
.id_source(Id::from("receive_input").with(wallet.config.id))
|
.id_source(Id::from("receive_input").with(wallet.get_config().id))
|
||||||
.auto_shrink([false; 2])
|
.auto_shrink([false; 2])
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.add_space(7.0);
|
ui.add_space(7.0);
|
||||||
|
|
|
@ -75,7 +75,7 @@ impl WalletTab for WalletSettings {
|
||||||
})
|
})
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
.id_source(Id::from("wallet_settings_scroll").with(wallet.config.id))
|
.id_source(Id::from("wallet_settings_scroll").with(wallet.get_config().id))
|
||||||
.auto_shrink([false; 2])
|
.auto_shrink([false; 2])
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.vertical_centered(|ui| {
|
ui.vertical_centered(|ui| {
|
||||||
|
|
|
@ -132,10 +132,4 @@ impl WalletConfig {
|
||||||
let config_path = Self::get_config_file_path(self.chain_type, self.id);
|
let config_path = Self::get_config_file_path(self.chain_type, self.id);
|
||||||
Settings::write_to_file(self, config_path);
|
Settings::write_to_file(self, config_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save account label value.
|
|
||||||
pub fn save_account(&mut self, label: &String) {
|
|
||||||
self.account = label.to_owned();
|
|
||||||
self.save();
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -59,8 +59,8 @@ impl WalletList {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Sort wallets by id.
|
// Sort wallets by id.
|
||||||
main_wallets.sort_by_key(|w| -w.config.id);
|
main_wallets.sort_by_key(|w| -w.get_config().id);
|
||||||
test_wallets.sort_by_key(|w| -w.config.id);
|
test_wallets.sort_by_key(|w| -w.get_config().id);
|
||||||
(main_wallets, test_wallets)
|
(main_wallets, test_wallets)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ impl WalletList {
|
||||||
|
|
||||||
/// Add created [`Wallet`] to the list.
|
/// Add created [`Wallet`] to the list.
|
||||||
pub fn add(&mut self, wallet: Wallet) {
|
pub fn add(&mut self, wallet: Wallet) {
|
||||||
self.selected_id = Some(wallet.config.id);
|
self.selected_id = Some(wallet.get_config().id);
|
||||||
let list = self.mut_list();
|
let list = self.mut_list();
|
||||||
list.insert(0, wallet);
|
list.insert(0, wallet);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ impl WalletList {
|
||||||
pub fn remove(&mut self, id: i64) {
|
pub fn remove(&mut self, id: i64) {
|
||||||
let list = self.mut_list();
|
let list = self.mut_list();
|
||||||
for (index, wallet) in list.iter().enumerate() {
|
for (index, wallet) in list.iter().enumerate() {
|
||||||
if wallet.config.id == id {
|
if wallet.get_config().id == id {
|
||||||
list.remove(index);
|
list.remove(index);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -108,8 +108,9 @@ impl WalletList {
|
||||||
/// Get selected [`Wallet`] name.
|
/// Get selected [`Wallet`] name.
|
||||||
pub fn selected_name(&self) -> String {
|
pub fn selected_name(&self) -> String {
|
||||||
for w in self.list() {
|
for w in self.list() {
|
||||||
if Some(w.config.id) == self.selected_id {
|
let config = w.get_config();
|
||||||
return w.config.name.to_owned()
|
if Some(config.id) == self.selected_id {
|
||||||
|
return config.name.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
t!("wallets.unlocked")
|
t!("wallets.unlocked")
|
||||||
|
@ -118,7 +119,8 @@ impl WalletList {
|
||||||
/// Check if selected [`Wallet`] is open.
|
/// Check if selected [`Wallet`] is open.
|
||||||
pub fn is_selected_open(&self) -> bool {
|
pub fn is_selected_open(&self) -> bool {
|
||||||
for w in self.list() {
|
for w in self.list() {
|
||||||
if Some(w.config.id) == self.selected_id {
|
let config = w.get_config();
|
||||||
|
if Some(config.id) == self.selected_id {
|
||||||
return w.is_open()
|
return w.is_open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +136,7 @@ impl WalletList {
|
||||||
pub fn open_selected(&mut self, password: String) -> Result<(), Error> {
|
pub fn open_selected(&mut self, password: String) -> Result<(), Error> {
|
||||||
let selected_id = self.selected_id.clone();
|
let selected_id = self.selected_id.clone();
|
||||||
for w in self.mut_list() {
|
for w in self.mut_list() {
|
||||||
if Some(w.config.id) == selected_id {
|
if Some(w.get_config().id) == selected_id {
|
||||||
return w.open(password.clone());
|
return w.open(password.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,9 @@ use grin_util::Mutex;
|
||||||
use grin_wallet_impls::{DefaultLCProvider, HTTPNodeClient};
|
use grin_wallet_impls::{DefaultLCProvider, HTTPNodeClient};
|
||||||
use grin_wallet_libwallet::{TxLogEntry, WalletInfo, WalletInst};
|
use grin_wallet_libwallet::{TxLogEntry, WalletInfo, WalletInst};
|
||||||
|
|
||||||
|
/// GRIN coin symbol.
|
||||||
|
pub const GRIN: &str = "ツ";
|
||||||
|
|
||||||
/// Mnemonic phrase setup mode.
|
/// Mnemonic phrase setup mode.
|
||||||
#[derive(PartialEq, Clone)]
|
#[derive(PartialEq, Clone)]
|
||||||
pub enum PhraseMode {
|
pub enum PhraseMode {
|
||||||
|
@ -87,11 +90,22 @@ pub type WalletInstance = Arc<
|
||||||
>,
|
>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
/// Contains wallet data to show.
|
/// Wallet account data.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct WalletAccount {
|
||||||
|
/// Spendable balance amount.
|
||||||
|
pub spendable_amount: u64,
|
||||||
|
/// Account label.
|
||||||
|
pub label: String,
|
||||||
|
/// Account BIP32 derivation path.
|
||||||
|
pub path: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wallet balance and transactions data.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WalletData {
|
pub struct WalletData {
|
||||||
/// Wallet balance information.
|
/// Balance data for current account.
|
||||||
pub info: WalletInfo,
|
pub info: WalletInfo,
|
||||||
/// Wallet transactions.
|
/// Transactions data.
|
||||||
pub txs: Vec<TxLogEntry>
|
pub txs: Vec<TxLogEntry>
|
||||||
}
|
}
|
|
@ -40,13 +40,13 @@ use grin_wallet_libwallet::api_impl::owner::{cancel_tx, retrieve_summary_info, r
|
||||||
|
|
||||||
use crate::node::{Node, NodeConfig};
|
use crate::node::{Node, NodeConfig};
|
||||||
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
||||||
use crate::wallet::types::{ConnectionMethod, WalletData, WalletInstance};
|
use crate::wallet::types::{ConnectionMethod, WalletAccount, WalletData, WalletInstance};
|
||||||
|
|
||||||
/// Contains wallet instance, configuration and state, handles wallet commands.
|
/// Contains wallet instance, configuration and state, handles wallet commands.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
/// Wallet configuration.
|
/// Wallet configuration.
|
||||||
pub config: WalletConfig,
|
config: Arc<RwLock<WalletConfig>>,
|
||||||
/// Wallet instance, initializing on wallet opening and clearing on wallet closing.
|
/// Wallet instance, initializing on wallet opening and clearing on wallet closing.
|
||||||
instance: Option<WalletInstance>,
|
instance: Option<WalletInstance>,
|
||||||
/// [`WalletInstance`] external connection id applied after opening.
|
/// [`WalletInstance`] external connection id applied after opening.
|
||||||
|
@ -74,7 +74,10 @@ pub struct Wallet {
|
||||||
/// Transactions loading progress in percents.
|
/// Transactions loading progress in percents.
|
||||||
txs_sync_progress: Arc<AtomicU8>,
|
txs_sync_progress: Arc<AtomicU8>,
|
||||||
|
|
||||||
/// Wallet data.
|
/// Wallet accounts.
|
||||||
|
accounts: Arc<RwLock<Vec<WalletAccount>>>,
|
||||||
|
|
||||||
|
/// Wallet info to show at ui.
|
||||||
data: Arc<RwLock<Option<WalletData>>>,
|
data: Arc<RwLock<Option<WalletData>>>,
|
||||||
/// Attempts amount to update wallet data.
|
/// Attempts amount to update wallet data.
|
||||||
sync_attempts: Arc<AtomicU8>,
|
sync_attempts: Arc<AtomicU8>,
|
||||||
|
@ -97,7 +100,7 @@ impl Wallet {
|
||||||
/// Create new [`Wallet`] instance with provided [`WalletConfig`].
|
/// Create new [`Wallet`] instance with provided [`WalletConfig`].
|
||||||
fn new(config: WalletConfig) -> Self {
|
fn new(config: WalletConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
config,
|
config: Arc::new(RwLock::new(config)),
|
||||||
instance: None,
|
instance: None,
|
||||||
instance_ext_conn_id: Arc::new(AtomicI64::new(0)),
|
instance_ext_conn_id: Arc::new(AtomicI64::new(0)),
|
||||||
sync_thread: Arc::from(RwLock::new(None)),
|
sync_thread: Arc::from(RwLock::new(None)),
|
||||||
|
@ -109,7 +112,8 @@ impl Wallet {
|
||||||
sync_error: Arc::from(AtomicBool::new(false)),
|
sync_error: Arc::from(AtomicBool::new(false)),
|
||||||
info_sync_progress: Arc::from(AtomicU8::new(0)),
|
info_sync_progress: Arc::from(AtomicU8::new(0)),
|
||||||
txs_sync_progress: Arc::from(AtomicU8::new(0)),
|
txs_sync_progress: Arc::from(AtomicU8::new(0)),
|
||||||
data: Arc::from(RwLock::new(None)),
|
accounts: Arc::new(RwLock::new(vec![])),
|
||||||
|
data: Arc::new(RwLock::new(None)),
|
||||||
sync_attempts: Arc::new(AtomicU8::new(0)),
|
sync_attempts: Arc::new(AtomicU8::new(0)),
|
||||||
repair_needed: Arc::new(AtomicBool::new(false)),
|
repair_needed: Arc::new(AtomicBool::new(false)),
|
||||||
repair_progress: Arc::new(AtomicU8::new(0)),
|
repair_progress: Arc::new(AtomicU8::new(0)),
|
||||||
|
@ -201,6 +205,32 @@ impl Wallet {
|
||||||
Ok(Arc::new(Mutex::new(wallet)))
|
Ok(Arc::new(Mutex::new(wallet)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get wallet config.
|
||||||
|
pub fn get_config(&self) -> WalletConfig {
|
||||||
|
self.config.read().unwrap().clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change wallet name.
|
||||||
|
pub fn change_name(&self, name: String) {
|
||||||
|
let mut w_config = self.config.write().unwrap();
|
||||||
|
w_config.name = name;
|
||||||
|
w_config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update minimal amount of confirmations.
|
||||||
|
pub fn update_min_confirmations(&self, min_confirmations: u64) {
|
||||||
|
let mut w_config = self.config.write().unwrap();
|
||||||
|
w_config.min_confirmations = min_confirmations;
|
||||||
|
w_config.save();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update external connection identifier.
|
||||||
|
pub fn update_ext_conn_id(&self, id: Option<i64>) {
|
||||||
|
let mut w_config = self.config.write().unwrap();
|
||||||
|
w_config.ext_conn_id = id;
|
||||||
|
w_config.save();
|
||||||
|
}
|
||||||
|
|
||||||
/// Open the wallet and start [`WalletData`] sync at separate thread.
|
/// Open the wallet and start [`WalletData`] sync at separate thread.
|
||||||
pub fn open(&mut self, password: String) -> Result<(), Error> {
|
pub fn open(&mut self, password: String) -> Result<(), Error> {
|
||||||
if self.is_open() {
|
if self.is_open() {
|
||||||
|
@ -209,9 +239,10 @@ impl Wallet {
|
||||||
|
|
||||||
// Create new wallet instance if sync thread was stopped or instance was not created.
|
// Create new wallet instance if sync thread was stopped or instance was not created.
|
||||||
if self.sync_thread.write().unwrap().is_none() || self.instance.is_none() {
|
if self.sync_thread.write().unwrap().is_none() || self.instance.is_none() {
|
||||||
let new_instance = Self::create_wallet_instance(self.config.clone())?;
|
let config = self.get_config();
|
||||||
|
let new_instance = Self::create_wallet_instance(config.clone())?;
|
||||||
self.instance = Some(new_instance);
|
self.instance = Some(new_instance);
|
||||||
self.instance_ext_conn_id.store(match self.config.ext_conn_id {
|
self.instance_ext_conn_id.store(match config.ext_conn_id {
|
||||||
None => 0,
|
None => 0,
|
||||||
Some(conn_id) => conn_id
|
Some(conn_id) => conn_id
|
||||||
}, Ordering::Relaxed);
|
}, Ordering::Relaxed);
|
||||||
|
@ -229,7 +260,7 @@ impl Wallet {
|
||||||
|
|
||||||
// Set current account.
|
// Set current account.
|
||||||
let wallet_inst = lc.wallet_inst()?;
|
let wallet_inst = lc.wallet_inst()?;
|
||||||
let label = self.config.account.to_owned();
|
let label = self.get_config().account.to_owned();
|
||||||
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
||||||
|
|
||||||
// Start new synchronization thread or wake up existing one.
|
// Start new synchronization thread or wake up existing one.
|
||||||
|
@ -262,7 +293,7 @@ impl Wallet {
|
||||||
Some(ext_conn_id)
|
Some(ext_conn_id)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.config.ext_conn_id
|
self.get_config().ext_conn_id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,7 +336,7 @@ impl Wallet {
|
||||||
wallet_close.is_open.store(false, Ordering::Relaxed);
|
wallet_close.is_open.store(false, Ordering::Relaxed);
|
||||||
|
|
||||||
// Wake up thread to exit.
|
// Wake up thread to exit.
|
||||||
wallet_close.refresh();
|
wallet_close.sync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,33 +358,33 @@ impl Wallet {
|
||||||
|
|
||||||
/// Set active account from provided label.
|
/// Set active account from provided label.
|
||||||
pub fn set_active_account(&mut self, label: &String) -> Result<(), Error> {
|
pub fn set_active_account(&mut self, label: &String) -> Result<(), Error> {
|
||||||
let instance = self.instance.clone().unwrap();
|
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||||
let mut wallet_lock = instance.lock();
|
controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
||||||
let lc = wallet_lock.lc_provider()?;
|
api.set_active_account(m, label)?;
|
||||||
let wallet_inst = lc.wallet_inst()?;
|
Ok(())
|
||||||
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
})?;
|
||||||
|
|
||||||
// Save account label into config.
|
// Save account label into config.
|
||||||
self.config.save_account(label);
|
let mut w_config = self.config.write().unwrap();
|
||||||
|
w_config.account = label.to_owned();
|
||||||
|
w_config.save();
|
||||||
|
|
||||||
// Clear wallet info.
|
// Clear wallet info.
|
||||||
let mut w_data = self.data.write().unwrap();
|
let mut w_data = self.data.write().unwrap();
|
||||||
*w_data = None;
|
*w_data = None;
|
||||||
|
|
||||||
// Refresh wallet data.
|
// Reset progress values.
|
||||||
self.refresh();
|
self.info_sync_progress.store(0, Ordering::Relaxed);
|
||||||
|
self.txs_sync_progress.store(0, Ordering::Relaxed);
|
||||||
|
|
||||||
|
// Sync wallet data.
|
||||||
|
self.sync();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get list of accounts for the wallet.
|
/// Get list of accounts for the wallet.
|
||||||
pub fn accounts(&self) -> Vec<AcctPathMapping> {
|
pub fn accounts(&self) -> Vec<WalletAccount> {
|
||||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
self.accounts.read().unwrap().clone()
|
||||||
let mut accounts = vec![];
|
|
||||||
let _ = controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
|
||||||
accounts = api.accounts(m)?;
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
accounts
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set wallet reopen status.
|
/// Set wallet reopen status.
|
||||||
|
@ -414,8 +445,8 @@ impl Wallet {
|
||||||
r_data.clone()
|
r_data.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wake up wallet thread to refresh wallet info and update statuses.
|
/// Wake up wallet thread to sync wallet data and update statuses.
|
||||||
fn refresh(&self) {
|
fn sync(&self) {
|
||||||
let thread_r = self.sync_thread.read().unwrap();
|
let thread_r = self.sync_thread.read().unwrap();
|
||||||
if let Some(thread) = thread_r.as_ref() {
|
if let Some(thread) = thread_r.as_ref() {
|
||||||
thread.unpark();
|
thread.unpark();
|
||||||
|
@ -427,9 +458,9 @@ impl Wallet {
|
||||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||||
match parse_slatepack(&mut api, None, None, Some(message.clone())) {
|
match parse_slatepack(&mut api, None, None, Some(message.clone())) {
|
||||||
Ok((mut slate, _)) => {
|
Ok((mut slate, _)) => {
|
||||||
|
let config = self.get_config();
|
||||||
controller::foreign_single_use(api.wallet_inst.clone(), None, |api| {
|
controller::foreign_single_use(api.wallet_inst.clone(), None, |api| {
|
||||||
let account = self.config.clone().account;
|
slate = api.receive_tx(&slate, Some(config.account.as_str()), None)?;
|
||||||
slate = api.receive_tx(&slate, Some(account.as_str()), None)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
})?;
|
})?;
|
||||||
let mut response = "".to_string();
|
let mut response = "".to_string();
|
||||||
|
@ -439,7 +470,7 @@ impl Wallet {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Create a directory to which slatepack files will be output.
|
// Create a directory to which slatepack files will be output.
|
||||||
let mut slatepack_dir = self.config.get_slatepacks_path();
|
let mut slatepack_dir = config.get_slatepacks_path();
|
||||||
let slatepack_file_name = format!("{}.{}.slatepack", slate.id, slate.state);
|
let slatepack_file_name = format!("{}.{}.slatepack", slate.id, slate.state);
|
||||||
slatepack_dir.push(slatepack_file_name);
|
slatepack_dir.push(slatepack_file_name);
|
||||||
|
|
||||||
|
@ -448,8 +479,8 @@ impl Wallet {
|
||||||
output.write_all(response.as_bytes())?;
|
output.write_all(response.as_bytes())?;
|
||||||
output.sync_all()?;
|
output.sync_all()?;
|
||||||
|
|
||||||
// Refresh wallet info.
|
// Sync wallet info.
|
||||||
self.refresh();
|
self.sync();
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
@ -477,7 +508,7 @@ impl Wallet {
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
let _ = cancel_tx(instance, None, &None, Some(id), None);
|
let _ = cancel_tx(instance, None, &None, Some(id), None);
|
||||||
// Refresh wallet info to update statuses.
|
// Refresh wallet info to update statuses.
|
||||||
wallet_cancel.refresh();
|
wallet_cancel.sync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,7 +533,7 @@ impl Wallet {
|
||||||
/// Initiate wallet repair by scanning its outputs.
|
/// Initiate wallet repair by scanning its outputs.
|
||||||
pub fn repair(&self) {
|
pub fn repair(&self) {
|
||||||
self.repair_needed.store(true, Ordering::Relaxed);
|
self.repair_needed.store(true, Ordering::Relaxed);
|
||||||
self.refresh();
|
self.sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if wallet is repairing.
|
/// Check if wallet is repairing.
|
||||||
|
@ -538,9 +569,7 @@ impl Wallet {
|
||||||
Self::close_wallet(&instance);
|
Self::close_wallet(&instance);
|
||||||
|
|
||||||
// Remove wallet files.
|
// Remove wallet files.
|
||||||
let mut wallet_lock = instance.lock();
|
let _ = fs::remove_dir_all(wallet_delete.get_config().get_data_path());
|
||||||
let _ = wallet_lock.lc_provider().unwrap();
|
|
||||||
let _ = fs::remove_dir_all(wallet_delete.config.get_data_path());
|
|
||||||
|
|
||||||
// Mark wallet as not opened and deleted.
|
// Mark wallet as not opened and deleted.
|
||||||
wallet_delete.closing.store(false, Ordering::Relaxed);
|
wallet_delete.closing.store(false, Ordering::Relaxed);
|
||||||
|
@ -548,7 +577,7 @@ impl Wallet {
|
||||||
wallet_delete.deleted.store(true, Ordering::Relaxed);
|
wallet_delete.deleted.store(true, Ordering::Relaxed);
|
||||||
|
|
||||||
// Wake up thread to exit.
|
// Wake up thread to exit.
|
||||||
wallet_delete.refresh();
|
wallet_delete.sync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -616,7 +645,7 @@ fn start_sync(mut wallet: Wallet) -> Thread {
|
||||||
// Scan outputs if repair is needed or sync data if there is no error.
|
// Scan outputs if repair is needed or sync data if there is no error.
|
||||||
if !wallet.sync_error() {
|
if !wallet.sync_error() {
|
||||||
if wallet.is_repairing() {
|
if wallet.is_repairing() {
|
||||||
scan_wallet(&wallet)
|
repair_wallet(&wallet)
|
||||||
} else {
|
} else {
|
||||||
sync_wallet_data(&wallet);
|
sync_wallet_data(&wallet);
|
||||||
}
|
}
|
||||||
|
@ -721,18 +750,22 @@ fn sync_wallet_data(wallet: &Wallet) {
|
||||||
None,
|
None,
|
||||||
&Some(info_tx),
|
&Some(info_tx),
|
||||||
true,
|
true,
|
||||||
wallet.config.min_confirmations
|
wallet.get_config().min_confirmations
|
||||||
) {
|
) {
|
||||||
Ok(info) => {
|
Ok(info) => {
|
||||||
// Do not retrieve txs if wallet was closed.
|
// Do not retrieve txs if wallet was closed.
|
||||||
if !wallet.is_open() {
|
if !wallet.is_open() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Retrieve txs if retrieving info was success.
|
|
||||||
if wallet.info_sync_progress() == 100 {
|
if wallet.info_sync_progress() == 100 {
|
||||||
|
// Retrieve accounts data.
|
||||||
|
let last_height = info.1.last_confirmed_height;
|
||||||
|
update_accounts(wallet, last_height, info.1.amount_currently_spendable);
|
||||||
|
|
||||||
|
// Update txs sync progress at separate thread.
|
||||||
let wallet_txs = wallet.clone();
|
let wallet_txs = wallet.clone();
|
||||||
let (txs_tx, txs_rx) = mpsc::channel::<StatusMessage>();
|
let (txs_tx, txs_rx) = mpsc::channel::<StatusMessage>();
|
||||||
// Update txs sync progress at separate thread.
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
while let Ok(m) = txs_rx.recv() {
|
while let Ok(m) = txs_rx.recv() {
|
||||||
println!("SYNC TXS MESSAGE");
|
println!("SYNC TXS MESSAGE");
|
||||||
|
@ -828,15 +861,66 @@ fn sync_wallet_data(wallet: &Wallet) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Update wallet accounts data.
|
||||||
|
fn update_accounts(wallet: &Wallet, current_height: u64, current_spendable: u64) {
|
||||||
|
// Update only current account if list is not empty.
|
||||||
|
if !wallet.accounts.read().unwrap().is_empty() {
|
||||||
|
let mut accounts = wallet.accounts.read().unwrap().clone();
|
||||||
|
for mut a in accounts.iter_mut() {
|
||||||
|
if a.label == wallet.get_config().account {
|
||||||
|
a.spendable_amount = current_spendable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Save accounts data.
|
||||||
|
let mut w_data = wallet.accounts.write().unwrap();
|
||||||
|
*w_data = accounts;
|
||||||
|
} else {
|
||||||
|
let mut api = Owner::new(wallet.instance.clone().unwrap(), None);
|
||||||
|
let _ = controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
||||||
|
let mut accounts = vec![];
|
||||||
|
for a in api.accounts(m)? {
|
||||||
|
api.set_active_account(m, a.label.as_str())?;
|
||||||
|
// Calculate wallet account balance.
|
||||||
|
let outputs = api.retrieve_outputs(m, true, false, None)?;
|
||||||
|
let min_confirmations = wallet.get_config().min_confirmations;
|
||||||
|
let mut spendable_amount = 0;
|
||||||
|
for out_mapping in outputs.1 {
|
||||||
|
let out = out_mapping.output;
|
||||||
|
if out.status == grin_wallet_libwallet::OutputStatus::Unspent {
|
||||||
|
if !out.is_coinbase || out.lock_height <= current_height
|
||||||
|
|| out.num_confirmations(current_height) >= min_confirmations {
|
||||||
|
spendable_amount += out.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add account to list.
|
||||||
|
accounts.push(WalletAccount {
|
||||||
|
spendable_amount,
|
||||||
|
label: a.label,
|
||||||
|
path: a.path.to_bip_32_string(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save accounts data.
|
||||||
|
let mut w_data = wallet.accounts.write().unwrap();
|
||||||
|
*w_data = accounts;
|
||||||
|
|
||||||
|
// Set current active account from config.
|
||||||
|
api.set_active_account(m, wallet.get_config().account.as_str())?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Scan wallet's outputs, repairing and restoring missing outputs if required.
|
/// Scan wallet's outputs, repairing and restoring missing outputs if required.
|
||||||
fn scan_wallet(wallet: &Wallet) {
|
fn repair_wallet(wallet: &Wallet) {
|
||||||
println!("repair the wallet");
|
|
||||||
let (info_tx, info_rx) = mpsc::channel::<StatusMessage>();
|
let (info_tx, info_rx) = mpsc::channel::<StatusMessage>();
|
||||||
// Update scan progress at separate thread.
|
// Update scan progress at separate thread.
|
||||||
let wallet_scan = wallet.clone();
|
let wallet_scan = wallet.clone();
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
while let Ok(m) = info_rx.recv() {
|
while let Ok(m) = info_rx.recv() {
|
||||||
println!("REPAIR WALLET MESSAGE");
|
|
||||||
match m {
|
match m {
|
||||||
StatusMessage::UpdatingOutputs(_) => {}
|
StatusMessage::UpdatingOutputs(_) => {}
|
||||||
StatusMessage::UpdatingTransactions(_) => {}
|
StatusMessage::UpdatingTransactions(_) => {}
|
||||||
|
@ -856,7 +940,6 @@ fn scan_wallet(wallet: &Wallet) {
|
||||||
let api = Owner::new(wallet.instance.clone().unwrap(), Some(info_tx));
|
let api = Owner::new(wallet.instance.clone().unwrap(), Some(info_tx));
|
||||||
match api.scan(None, Some(1), false) {
|
match api.scan(None, Some(1), false) {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
println!("repair was complete");
|
|
||||||
// Set sync error if scanning was not complete and wallet is open.
|
// Set sync error if scanning was not complete and wallet is open.
|
||||||
if wallet.is_open() && wallet.repair_progress.load(Ordering::Relaxed) != 100 {
|
if wallet.is_open() && wallet.repair_progress.load(Ordering::Relaxed) != 100 {
|
||||||
wallet.set_sync_error(true);
|
wallet.set_sync_error(true);
|
||||||
|
@ -865,7 +948,6 @@ fn scan_wallet(wallet: &Wallet) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
println!("error on repair {}", e);
|
|
||||||
// Set sync error if wallet is open.
|
// Set sync error if wallet is open.
|
||||||
if wallet.is_open() {
|
if wallet.is_open() {
|
||||||
wallet.set_sync_error(true);
|
wallet.set_sync_error(true);
|
||||||
|
|
Loading…
Reference in a new issue