wallet + ui: optimize loading, retry button for non-success loading, update translations
This commit is contained in:
parent
3c14f72c24
commit
ab83d7c95c
6 changed files with 191 additions and 119 deletions
|
@ -2,6 +2,9 @@ copy: Copy
|
||||||
paste: Paste
|
paste: Paste
|
||||||
continue: Continue
|
continue: Continue
|
||||||
complete: Complete
|
complete: Complete
|
||||||
|
loading: Loading
|
||||||
|
loading_error: Loading error
|
||||||
|
retry: Retry
|
||||||
wallets:
|
wallets:
|
||||||
title: Wallets
|
title: Wallets
|
||||||
create_desc: Create or import existing wallet from saved recovery phrase.
|
create_desc: Create or import existing wallet from saved recovery phrase.
|
||||||
|
@ -31,7 +34,7 @@ wallets:
|
||||||
unlocked: Unlocked
|
unlocked: Unlocked
|
||||||
enable_node: 'Enable integrated node to use the wallet or change connection settings by selecting %{settings} at the bottom of the screen.'
|
enable_node: 'Enable integrated node to use the wallet or change connection settings by selecting %{settings} at the bottom of the screen.'
|
||||||
wallet_loading: 'Wallet is loading'
|
wallet_loading: 'Wallet is loading'
|
||||||
wallet_loading_err: 'An error occurred during loading the wallet, you can change connection settings by selecting %{settings} at the bottom of the screen.'
|
wallet_loading_err: 'An error occurred during loading the wallet, you can retry or change connection settings by selecting %{settings} at the bottom of the screen.'
|
||||||
wallet: Wallet
|
wallet: Wallet
|
||||||
send: Send
|
send: Send
|
||||||
receive: Receive
|
receive: Receive
|
||||||
|
|
|
@ -2,6 +2,9 @@ copy: Копировать
|
||||||
paste: Вставить
|
paste: Вставить
|
||||||
continue: Продолжить
|
continue: Продолжить
|
||||||
complete: Завершить
|
complete: Завершить
|
||||||
|
loading: Загружается
|
||||||
|
loading_error: Ошибка загрузки
|
||||||
|
retry: Повторить
|
||||||
wallets:
|
wallets:
|
||||||
title: Кошельки
|
title: Кошельки
|
||||||
create_desc: Создайте или импортируйте существующий кошелёк из сохранённой фразы восстановления.
|
create_desc: Создайте или импортируйте существующий кошелёк из сохранённой фразы восстановления.
|
||||||
|
@ -31,7 +34,7 @@ wallets:
|
||||||
unlocked: Разблокирован
|
unlocked: Разблокирован
|
||||||
enable_node: 'Чтобы использовать кошелёк, включите встроенный узел или измените настройки подключения, выбрав %{settings} внизу экрана.'
|
enable_node: 'Чтобы использовать кошелёк, включите встроенный узел или измените настройки подключения, выбрав %{settings} внизу экрана.'
|
||||||
wallet_loading: 'Кошелёк загружается'
|
wallet_loading: 'Кошелёк загружается'
|
||||||
wallet_loading_err: 'Во время загрузки кошелька произошла ошибка, вы можете изменить настройки подключения, выбрав %{settings} внизу экрана.'
|
wallet_loading_err: 'Во время загрузки кошелька произошла ошибка, вы можете повторить попытку или изменить настройки подключения, выбрав %{settings} внизу экрана.'
|
||||||
wallet: Кошелёк
|
wallet: Кошелёк
|
||||||
send: Отправить
|
send: Отправить
|
||||||
receive: Получить
|
receive: Получить
|
||||||
|
|
|
@ -18,7 +18,7 @@ use grin_core::global::ChainTypes;
|
||||||
|
|
||||||
use crate::AppConfig;
|
use crate::AppConfig;
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, EYE, EYE_SLASH, FOLDER_LOCK, FOLDER_OPEN, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE};
|
use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, EYE, EYE_SLASH, FOLDER_LOCK, FOLDER_OPEN, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SPINNER, SUITCASE, WARNING_CIRCLE};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Modal, Root, TitlePanel, View};
|
use crate::gui::views::{Modal, Root, TitlePanel, View};
|
||||||
use crate::gui::views::types::{ModalContainer, ModalPosition, TitleType};
|
use crate::gui::views::types::{ModalContainer, ModalPosition, TitleType};
|
||||||
|
@ -408,7 +408,15 @@ impl WalletsContent {
|
||||||
|
|
||||||
// Setup wallet status text.
|
// Setup wallet status text.
|
||||||
let status_text = if wallet.is_open() {
|
let status_text = if wallet.is_open() {
|
||||||
|
if wallet.get_info().is_none() {
|
||||||
|
if wallet.loading_error() {
|
||||||
|
format!("{} {}", WARNING_CIRCLE, t!("loading_error"))
|
||||||
|
} else {
|
||||||
|
format!("{} {}", SPINNER, t!("loading"))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
format!("{} {}", FOLDER_OPEN, t!("wallets.unlocked"))
|
format!("{} {}", FOLDER_OPEN, t!("wallets.unlocked"))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
format!("{} {}", FOLDER_LOCK, t!("wallets.locked"))
|
format!("{} {}", FOLDER_LOCK, t!("wallets.locked"))
|
||||||
};
|
};
|
||||||
|
@ -532,7 +540,7 @@ impl WalletsContent {
|
||||||
if self.pass_edit.is_empty() {
|
if self.pass_edit.is_empty() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match self.wallets.launch_selected(self.pass_edit.clone()) {
|
match self.wallets.open_selected(self.pass_edit.clone()) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// Clear values.
|
// Clear values.
|
||||||
self.pass_edit = "".to_string();
|
self.pass_edit = "".to_string();
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
use egui::{Margin, RichText};
|
use egui::{Margin, RichText};
|
||||||
|
use grin_chain::SyncStatus;
|
||||||
|
|
||||||
|
use crate::AppConfig;
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{DOWNLOAD, GEAR_FINE, POWER, UPLOAD, WALLET};
|
use crate::gui::icons::{DOWNLOAD, GEAR_FINE, POWER, REPEAT, UPLOAD, WALLET};
|
||||||
use crate::gui::platform::PlatformCallbacks;
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Root, View};
|
use crate::gui::views::{Root, View};
|
||||||
use crate::gui::views::wallets::{WalletInfo, WalletReceive, WalletSend, WalletSettings};
|
use crate::gui::views::wallets::{WalletInfo, WalletReceive, WalletSend, WalletSettings};
|
||||||
|
@ -119,31 +121,52 @@ impl WalletContent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Content to draw when wallet is loading.
|
/// Content to draw when wallet is loading, returns `true` if wallet is not ready.
|
||||||
pub fn show_loading_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, wallet: &Wallet) -> bool {
|
pub fn loading_ui(ui: &mut egui::Ui, frame: &mut eframe::Frame, wallet: &Wallet) -> bool {
|
||||||
if wallet.config.ext_conn_id.is_none() && !Node::is_running() {
|
if wallet.config.ext_conn_id.is_none() {
|
||||||
|
if !Node::is_running() || Node::is_stopping() {
|
||||||
let dual_panel_root = Root::is_dual_panel_mode(frame);
|
let dual_panel_root = Root::is_dual_panel_mode(frame);
|
||||||
View::center_content(ui, if !dual_panel_root { 162.0 } else { 96.0 }, |ui| {
|
View::center_content(ui, if !dual_panel_root { 162.0 } else { 96.0 }, |ui| {
|
||||||
let text = t!("wallets.enable_node", "settings" => GEAR_FINE);
|
let text = t!("wallets.enable_node", "settings" => GEAR_FINE);
|
||||||
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
// Show button to enable integrated node at non-dual root panel mode.
|
|
||||||
if !dual_panel_root {
|
|
||||||
ui.add_space(8.0);
|
ui.add_space(8.0);
|
||||||
|
// Show button to enable integrated node at non-dual root panel mode
|
||||||
|
// or when network connections are not showing and node is not stopping
|
||||||
|
if (!dual_panel_root || AppConfig::show_connections_network_panel())
|
||||||
|
&& !Node::is_stopping() {
|
||||||
let enable_node_text = format!("{} {}", POWER, t!("network.enable_node"));
|
let enable_node_text = format!("{} {}", POWER, t!("network.enable_node"));
|
||||||
View::button(ui, enable_node_text, Colors::GOLD, || {
|
View::button(ui, enable_node_text, Colors::GOLD, || {
|
||||||
Node::start();
|
Node::start();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return true
|
||||||
|
} else if wallet.get_info().is_none() {
|
||||||
|
Self::progress_ui(ui, wallet);
|
||||||
return true;
|
return true;
|
||||||
} else {
|
}
|
||||||
if wallet.get_info().is_none() || wallet.loading_progress() < 100 {
|
} else if wallet.get_info().is_none() {
|
||||||
|
// Show error message with button to retry on wallet loading error or loading progress.
|
||||||
if wallet.loading_error() {
|
if wallet.loading_error() {
|
||||||
View::center_content(ui, 96.0, |ui| {
|
View::center_content(ui, 162.0, |ui| {
|
||||||
let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE);
|
let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE);
|
||||||
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
|
ui.add_space(8.0);
|
||||||
|
let retry_text = format!("{} {}", REPEAT, t!("retry"));
|
||||||
|
View::button(ui, retry_text, Colors::GOLD, || {
|
||||||
|
wallet.set_loading_error(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
Self::progress_ui(ui, wallet);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draw wallet loading progress.
|
||||||
|
fn progress_ui(ui: &mut egui::Ui, wallet: &Wallet) {
|
||||||
View::center_content(ui, 162.0, |ui| {
|
View::center_content(ui, 162.0, |ui| {
|
||||||
View::big_loading_spinner(ui);
|
View::big_loading_spinner(ui);
|
||||||
ui.add_space(18.0);
|
ui.add_space(18.0);
|
||||||
|
@ -157,10 +180,4 @@ impl WalletContent {
|
||||||
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -32,6 +32,6 @@ impl WalletTab for WalletInfo {
|
||||||
frame: &mut eframe::Frame,
|
frame: &mut eframe::Frame,
|
||||||
wallet: &Wallet,
|
wallet: &Wallet,
|
||||||
cb: &dyn PlatformCallbacks) {
|
cb: &dyn PlatformCallbacks) {
|
||||||
WalletContent::show_loading_ui(ui, frame, wallet);
|
WalletContent::loading_ui(ui, frame, wallet);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -14,10 +14,11 @@
|
||||||
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, mpsc, RwLock};
|
use std::sync::{Arc, mpsc, RwLock};
|
||||||
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU8, Ordering};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use grin_chain::SyncStatus;
|
||||||
use grin_core::global;
|
use grin_core::global;
|
||||||
use grin_core::global::ChainTypes;
|
use grin_core::global::ChainTypes;
|
||||||
use grin_keychain::{ExtKeychain, Keychain};
|
use grin_keychain::{ExtKeychain, Keychain};
|
||||||
|
@ -29,7 +30,7 @@ use grin_wallet_libwallet::api_impl::owner::retrieve_summary_info;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use crate::AppConfig;
|
use crate::AppConfig;
|
||||||
use crate::node::NodeConfig;
|
use crate::node::{Node, NodeConfig};
|
||||||
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
use crate::wallet::{ConnectionsConfig, ExternalConnection, WalletConfig};
|
||||||
use crate::wallet::types::{ConnectionMethod, WalletInstance};
|
use crate::wallet::types::{ConnectionMethod, WalletInstance};
|
||||||
|
|
||||||
|
@ -51,7 +52,7 @@ impl Default for Wallets {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallets {
|
impl Wallets {
|
||||||
/// Initialize wallets from base directory for provided [`ChainType`].
|
/// Initialize [`Wallet`] list from base directory for provided [`ChainType`].
|
||||||
fn init(chain_type: ChainTypes) -> Vec<Wallet> {
|
fn init(chain_type: ChainTypes) -> Vec<Wallet> {
|
||||||
let mut wallets = Vec::new();
|
let mut wallets = Vec::new();
|
||||||
let wallets_dir = WalletConfig::get_base_path(chain_type);
|
let wallets_dir = WalletConfig::get_base_path(chain_type);
|
||||||
|
@ -68,7 +69,7 @@ impl Wallets {
|
||||||
wallets
|
wallets
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinitialize wallets for provided [`ChainTypes`].
|
/// Reinitialize [`Wallet`] list for provided [`ChainTypes`].
|
||||||
pub fn reinit(&mut self, chain_type: ChainTypes) {
|
pub fn reinit(&mut self, chain_type: ChainTypes) {
|
||||||
for w in self.list.iter_mut() {
|
for w in self.list.iter_mut() {
|
||||||
let _ = w.close();
|
let _ = w.close();
|
||||||
|
@ -82,17 +83,17 @@ impl Wallets {
|
||||||
self.list.insert(0, wallet);
|
self.list.insert(0, wallet);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Select wallet with provided identifier.
|
/// Select [`Wallet`] with provided identifier.
|
||||||
pub fn select(&mut self, id: Option<i64>) {
|
pub fn select(&mut self, id: Option<i64>) {
|
||||||
self.selected_id = id;
|
self.selected_id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if wallet is selected for provided identifier.
|
/// Check if [`Wallet`] is selected for provided identifier.
|
||||||
pub fn is_selected(&self, id: i64) -> bool {
|
pub fn is_selected(&self, id: i64) -> bool {
|
||||||
return Some(id) == self.selected_id;
|
return Some(id) == self.selected_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 {
|
if Some(w.config.id) == self.selected_id {
|
||||||
|
@ -102,8 +103,8 @@ impl Wallets {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open and load selected wallet.
|
/// Open selected [`Wallet`].
|
||||||
pub fn launch_selected(&mut self, password: String) -> Result<(), Error> {
|
pub fn open_selected(&mut self, password: String) -> Result<(), Error> {
|
||||||
for w in self.list.iter_mut() {
|
for w in self.list.iter_mut() {
|
||||||
if Some(w.config.id) == self.selected_id {
|
if Some(w.config.id) == self.selected_id {
|
||||||
return w.open(password.clone());
|
return w.open(password.clone());
|
||||||
|
@ -113,33 +114,36 @@ impl Wallets {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Contains wallet instance and config.
|
/// Contains wallet instance and handles wallet commands.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Wallet {
|
pub struct Wallet {
|
||||||
/// Wallet instance.
|
/// Wallet instance.
|
||||||
instance: WalletInstance,
|
instance: WalletInstance,
|
||||||
|
|
||||||
/// Wallet configuration.
|
/// Wallet configuration.
|
||||||
pub config: WalletConfig,
|
pub config: WalletConfig,
|
||||||
|
|
||||||
|
/// Identifier for launched wallet thread.
|
||||||
|
thread_id: Arc<AtomicI64>,
|
||||||
|
|
||||||
/// Flag to check if wallet is open.
|
/// Flag to check if wallet is open.
|
||||||
is_open: Arc<AtomicBool>,
|
is_open: Arc<AtomicBool>,
|
||||||
|
|
||||||
/// Error on wallet loading.
|
/// Error on wallet loading.
|
||||||
loading_error: Arc<AtomicBool>,
|
loading_error: Arc<AtomicBool>,
|
||||||
/// Loading progress in percents
|
/// Loading progress in percents
|
||||||
pub loading_progress: Arc<AtomicU8>,
|
loading_progress: Arc<AtomicU8>,
|
||||||
|
|
||||||
/// Wallet balance information.
|
/// Wallet balance information.
|
||||||
info: Arc<RwLock<Option<WalletInfo>>>
|
info: Arc<RwLock<Option<WalletInfo>>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wallet {
|
impl Wallet {
|
||||||
/// Create wallet from provided instance and config.
|
/// Instantiate [`Wallet`] from provided [`WalletInstance`] and [`WalletConfig`].
|
||||||
fn new(instance: WalletInstance, config: WalletConfig) -> Self {
|
fn new(instance: WalletInstance, config: WalletConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
instance,
|
instance,
|
||||||
config,
|
config,
|
||||||
|
thread_id: Arc::new(AtomicI64::new(0)),
|
||||||
is_open: Arc::from(AtomicBool::new(false)),
|
is_open: Arc::from(AtomicBool::new(false)),
|
||||||
loading_error: Arc::from(AtomicBool::new(false)),
|
loading_error: Arc::from(AtomicBool::new(false)),
|
||||||
loading_progress: Arc::new(AtomicU8::new(0)),
|
loading_progress: Arc::new(AtomicU8::new(0)),
|
||||||
|
@ -170,7 +174,7 @@ impl Wallet {
|
||||||
Ok(w)
|
Ok(w)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialize wallet from provided data path.
|
/// Initialize [`Wallet`] from provided data path.
|
||||||
fn init(data_path: PathBuf) -> Option<Wallet> {
|
fn init(data_path: PathBuf) -> Option<Wallet> {
|
||||||
let wallet_config = WalletConfig::load(data_path.clone());
|
let wallet_config = WalletConfig::load(data_path.clone());
|
||||||
if let Some(config) = wallet_config {
|
if let Some(config) = wallet_config {
|
||||||
|
@ -181,14 +185,14 @@ impl Wallet {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reinitialize wallet instance to apply new config e.g. on change connection settings.
|
/// Reinitialize [`WalletInstance`] to apply new [`WalletConfig`].
|
||||||
pub fn reinit(&mut self) -> Result<(), Error> {
|
pub fn reinit(&mut self) -> Result<(), Error> {
|
||||||
self.close()?;
|
self.close()?;
|
||||||
self.instance = Self::create_wallet_instance(self.config.clone())?;
|
self.instance = Self::create_wallet_instance(self.config.clone())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create wallet instance from provided config.
|
/// Create [`WalletInstance`] from provided [`WalletConfig`].
|
||||||
fn create_wallet_instance(config: WalletConfig) -> Result<WalletInstance, Error> {
|
fn create_wallet_instance(config: WalletConfig) -> Result<WalletInstance, Error> {
|
||||||
// Assume global chain type has already been initialized.
|
// Assume global chain type has already been initialized.
|
||||||
let chain_type = config.chain_type;
|
let chain_type = config.chain_type;
|
||||||
|
@ -222,7 +226,7 @@ impl Wallet {
|
||||||
Ok(wallet)
|
Ok(wallet)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Instantiate wallet from provided node client and config.
|
/// Instantiate [`WalletInstance`] from provided node client and [`WalletConfig`].
|
||||||
fn inst_wallet<L, C, K>(
|
fn inst_wallet<L, C, K>(
|
||||||
config: WalletConfig,
|
config: WalletConfig,
|
||||||
node_client: C,
|
node_client: C,
|
||||||
|
@ -250,7 +254,7 @@ impl Wallet {
|
||||||
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
||||||
Ok(keychain) => {
|
Ok(keychain) => {
|
||||||
self.is_open.store(true, Ordering::Relaxed);
|
self.is_open.store(true, Ordering::Relaxed);
|
||||||
// Start commands handling and updating info.
|
// Start info updating and commands handling.
|
||||||
start_wallet(self.clone(), keychain);
|
start_wallet(self.clone(), keychain);
|
||||||
}
|
}
|
||||||
Err(e) => return Err(e)
|
Err(e) => return Err(e)
|
||||||
|
@ -258,7 +262,19 @@ impl Wallet {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get wallet loading progress.
|
/// Get wallet thread identifier.
|
||||||
|
fn get_thread_id(&self) -> i64 {
|
||||||
|
self.thread_id.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Refresh wallet thread identifier.
|
||||||
|
fn new_thread_id(&self) -> i64 {
|
||||||
|
let id = chrono::Utc::now().timestamp();
|
||||||
|
self.thread_id.store(id, Ordering::Relaxed);
|
||||||
|
id
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get wallet loading progress after opening.
|
||||||
pub fn loading_progress(&self) -> u8 {
|
pub fn loading_progress(&self) -> u8 {
|
||||||
self.loading_progress.load(Ordering::Relaxed)
|
self.loading_progress.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
@ -268,23 +284,32 @@ impl Wallet {
|
||||||
self.loading_error.load(Ordering::Relaxed)
|
self.loading_error.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if wallet is open.
|
/// Set an error for wallet on loading.
|
||||||
|
pub fn set_loading_error(&self, error: bool) {
|
||||||
|
self.loading_error.store(error, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check if wallet was open.
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.is_open.load(Ordering::Relaxed)
|
self.is_open.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Close wallet.
|
/// Close the wallet.
|
||||||
pub fn close(&mut self) -> Result<(), Error> {
|
pub fn close(&mut self) -> Result<(), Error> {
|
||||||
if self.is_open() {
|
if self.is_open() {
|
||||||
let mut wallet_lock = self.instance.lock();
|
let mut wallet_lock = self.instance.lock();
|
||||||
let lc = wallet_lock.lc_provider()?;
|
let lc = wallet_lock.lc_provider()?;
|
||||||
lc.close_wallet(None)?;
|
lc.close_wallet(None)?;
|
||||||
|
// Clear wallet info.
|
||||||
|
let mut w_info = self.info.write().unwrap();
|
||||||
|
*w_info = None;
|
||||||
|
// Mark wallet as not opened.
|
||||||
self.is_open.store(false, Ordering::Relaxed);
|
self.is_open.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get wallet info.
|
/// Get wallet balance info.
|
||||||
pub fn get_info(&self) -> Option<WalletInfo> {
|
pub fn get_info(&self) -> Option<WalletInfo> {
|
||||||
let r_info = self.info.read().unwrap();
|
let r_info = self.info.read().unwrap();
|
||||||
r_info.clone()
|
r_info.clone()
|
||||||
|
@ -294,29 +319,42 @@ impl Wallet {
|
||||||
/// Delay in seconds to update wallet info.
|
/// Delay in seconds to update wallet info.
|
||||||
const INFO_UPDATE_DELAY: Duration = Duration::from_millis(20 * 1000);
|
const INFO_UPDATE_DELAY: Duration = Duration::from_millis(20 * 1000);
|
||||||
|
|
||||||
/// Maximum number of attempts to load the wallet on start before stopping the loop.
|
/// Launch thread for commands handling and info updating after [`INFO_UPDATE_DELAY`].
|
||||||
const MAX_LOADING_ATTEMPTS: i8 = 10;
|
fn start_wallet(mut wallet: Wallet, keychain_mask: Option<SecretKey>) {
|
||||||
|
// Setup initial loading values.
|
||||||
|
wallet.loading_progress.store(0, Ordering::Relaxed);
|
||||||
|
wallet.set_loading_error(false);
|
||||||
|
|
||||||
/// Start wallet by launching separate thread to handle commands and updating info after delay.
|
|
||||||
fn start_wallet(wallet: Wallet, keychain_mask: Option<SecretKey>) {
|
|
||||||
// Launch loop at separate thread to update wallet info.
|
// Launch loop at separate thread to update wallet info.
|
||||||
let mut loading_attempts = 1;
|
let thread_id = wallet.new_thread_id();
|
||||||
thread::spawn(move || loop {
|
thread::spawn(move || loop {
|
||||||
// Stop updating if wallet was closed.
|
// Stop updating if wallet was closed or thread changed.
|
||||||
if !wallet.is_open() {
|
if !wallet.is_open() || thread_id != wallet.get_thread_id() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update progress at separate thread.
|
// Setup error when required integrated node is not enabled or skip when it's not ready.
|
||||||
|
if wallet.config.ext_conn_id.is_none() {
|
||||||
|
if !Node::is_running() {
|
||||||
|
wallet.set_loading_error(true);
|
||||||
|
} else if Node::get_sync_status() != Some(SyncStatus::NoSync) {
|
||||||
|
wallet.set_loading_error(false);
|
||||||
|
thread::sleep(Duration::from_millis(1000));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update wallet info if it was no loading error after several attempts.
|
||||||
|
if !wallet.loading_error() {
|
||||||
let wallet_scan = wallet.clone();
|
let wallet_scan = wallet.clone();
|
||||||
let (tx, rx) = mpsc::channel::<StatusMessage>();
|
let (tx, rx) = mpsc::channel::<StatusMessage>();
|
||||||
|
// Update loading progress at separate thread.
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
while let Ok(m) = rx.recv() {
|
while let Ok(m) = rx.recv() {
|
||||||
// Stop updating if wallet was closed or maximum.
|
// Stop updating if wallet was closed or maximum.
|
||||||
if !wallet_scan.is_open() || loading_attempts == MAX_LOADING_ATTEMPTS {
|
if !wallet_scan.is_open() || wallet_scan.get_thread_id() != thread_id {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
println!("m: {}", serde_json::to_string::<StatusMessage>(&m.clone()).unwrap());
|
|
||||||
match m {
|
match m {
|
||||||
StatusMessage::UpdatingOutputs(_) => {}
|
StatusMessage::UpdatingOutputs(_) => {}
|
||||||
StatusMessage::UpdatingTransactions(_) => {}
|
StatusMessage::UpdatingTransactions(_) => {}
|
||||||
|
@ -341,24 +379,27 @@ fn start_wallet(wallet: Wallet, keychain_mask: Option<SecretKey>) {
|
||||||
wallet.config.min_confirmations
|
wallet.config.min_confirmations
|
||||||
) {
|
) {
|
||||||
Ok(info) => {
|
Ok(info) => {
|
||||||
wallet.loading_error.store(false, Ordering::Relaxed);
|
if wallet.loading_progress() != 100 {
|
||||||
|
wallet.set_loading_error(true);
|
||||||
|
} else {
|
||||||
let mut w_info = wallet.info.write().unwrap();
|
let mut w_info = wallet.info.write().unwrap();
|
||||||
*w_info = Some(info.1);
|
*w_info = Some(info.1);
|
||||||
}
|
}
|
||||||
Err(e) => {
|
}
|
||||||
println!("Error!: {}", e);
|
Err(_) => {
|
||||||
loading_attempts += 1;
|
wallet.set_loading_error(true);
|
||||||
wallet.loading_progress.store(0, Ordering::Relaxed);
|
wallet.loading_progress.store(0, Ordering::Relaxed);
|
||||||
// Setup an error flag when maximum loading attempts were reached.
|
|
||||||
if loading_attempts == MAX_LOADING_ATTEMPTS {
|
|
||||||
wallet.loading_error.store(true, Ordering::Relaxed);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stop updating if wallet was closed.
|
||||||
|
if !wallet.is_open() || wallet.get_thread_id() != thread_id {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// Repeat after default delay or after 1 second if update was not complete.
|
// Repeat after default delay or after 1 second if update was not complete.
|
||||||
let delay = if wallet.loading_progress() == 100 {
|
let delay = if wallet.loading_progress() == 100 && !wallet.loading_error() {
|
||||||
INFO_UPDATE_DELAY
|
INFO_UPDATE_DELAY
|
||||||
} else {
|
} else {
|
||||||
Duration::from_millis(1000)
|
Duration::from_millis(1000)
|
||||||
|
|
Loading…
Reference in a new issue