From 3c14f72c24ab3cc5546f9e61e470361da3108ebb Mon Sep 17 00:00:00 2001 From: ardocrat Date: Wed, 9 Aug 2023 04:19:15 +0300 Subject: [PATCH] wallet + ui: optimize loading, update translations --- locales/en.yml | 2 +- locales/ru.yml | 2 +- src/gui/views/network/connections/mod.rs | 16 --- src/gui/views/wallets/wallet/content.rs | 23 +-- src/wallet/wallets.rs | 170 +++++++++++++---------- 5 files changed, 112 insertions(+), 101 deletions(-) delete mode 100644 src/gui/views/network/connections/mod.rs diff --git a/locales/en.yml b/locales/en.yml index c47b4bc..9e5dd6d 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -31,7 +31,7 @@ wallets: unlocked: Unlocked 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_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_loading_err: 'An error occurred during loading the wallet, you can change connection settings by selecting %{settings} at the bottom of the screen.' wallet: Wallet send: Send receive: Receive diff --git a/locales/ru.yml b/locales/ru.yml index 807b9a4..5d804fa 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -31,7 +31,7 @@ wallets: unlocked: Разблокирован enable_node: 'Чтобы использовать кошелёк, включите встроенный узел или измените настройки подключения, выбрав %{settings} внизу экрана.' wallet_loading: 'Кошелёк загружается' - wallet_loading_err: 'Во время загрузки кошелька произошла ошибка, вы можете повторить загрузку или изменить настройки подключения, выбрав %{settings} внизу экрана.' + wallet_loading_err: 'Во время загрузки кошелька произошла ошибка, вы можете изменить настройки подключения, выбрав %{settings} внизу экрана.' wallet: Кошелёк send: Отправить receive: Получить diff --git a/src/gui/views/network/connections/mod.rs b/src/gui/views/network/connections/mod.rs deleted file mode 100644 index b3e1aa2..0000000 --- a/src/gui/views/network/connections/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -mod content; -pub use content::ConnectionsContent; \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 22dd691..6032c1b 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -12,10 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. +use std::time::Duration; + use egui::{Margin, RichText}; use crate::gui::Colors; -use crate::gui::icons::{DOWNLOAD, POWER, UPLOAD, WALLET, WRENCH}; +use crate::gui::icons::{DOWNLOAD, GEAR_FINE, POWER, UPLOAD, WALLET}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Root, View}; use crate::gui::views::wallets::{WalletInfo, WalletReceive, WalletSend, WalletSettings}; @@ -74,9 +76,9 @@ impl WalletContent { self.current_tab.ui(ui, frame, wallet, cb); }); - // Refresh content after delay for loaded wallet. + // Refresh content after 1 second for loaded wallet. if wallet.get_info().is_some() { - ui.ctx().request_repaint_after(Wallet::INFO_UPDATE_DELAY); + ui.ctx().request_repaint_after(Duration::from_millis(1000)); } else { ui.ctx().request_repaint(); } @@ -109,7 +111,7 @@ impl WalletContent { }); }); columns[3].vertical_centered_justified(|ui| { - View::tab_button(ui, WRENCH, current_type == WalletTabType::Settings, || { + View::tab_button(ui, GEAR_FINE, current_type == WalletTabType::Settings, || { self.current_tab = Box::new(WalletSettings::default()); }); }); @@ -122,7 +124,7 @@ impl WalletContent { if wallet.config.ext_conn_id.is_none() && !Node::is_running() { let dual_panel_root = Root::is_dual_panel_mode(frame); View::center_content(ui, if !dual_panel_root { 162.0 } else { 96.0 }, |ui| { - let text = t!("wallets.enable_node", "settings" => WRENCH); + let text = t!("wallets.enable_node", "settings" => GEAR_FINE); 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 { @@ -136,11 +138,11 @@ impl WalletContent { return true; } else { if wallet.get_info().is_none() || wallet.loading_progress() < 100 { - if let Some(error) = &wallet.loading_error { - println!("e: {}", error); - let text = t!("wallets.wallet_loading_err"); - ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT)); - //TODO: button to retry. + if wallet.loading_error() { + View::center_content(ui, 96.0, |ui| { + let text = t!("wallets.wallet_loading_err", "settings" => GEAR_FINE); + ui.label(RichText::new(text).size(16.0).color(Colors::INACTIVE_TEXT)); + }); } else { View::center_content(ui, 162.0, |ui| { View::big_loading_spinner(ui); @@ -157,7 +159,6 @@ impl WalletContent { } return true; } else { - println!("12345 info!!!"); } } false diff --git a/src/wallet/wallets.rs b/src/wallet/wallets.rs index 12e1393..1459e61 100644 --- a/src/wallet/wallets.rs +++ b/src/wallet/wallets.rs @@ -12,15 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::thread; use std::path::PathBuf; use std::sync::{Arc, mpsc, RwLock}; use std::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use std::thread; use std::time::Duration; use grin_core::global; use grin_core::global::ChainTypes; use grin_keychain::{ExtKeychain, Keychain}; +use grin_util::secp::SecretKey; use grin_util::types::ZeroingString; use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient}; use grin_wallet_libwallet::{Error, NodeClient, StatusMessage, WalletBackend, WalletInfo, WalletInst, WalletLCProvider}; @@ -69,6 +70,9 @@ impl Wallets { /// Reinitialize wallets for provided [`ChainTypes`]. pub fn reinit(&mut self, chain_type: ChainTypes) { + for w in self.list.iter_mut() { + let _ = w.close(); + } self.list = Self::init(chain_type); } @@ -122,7 +126,7 @@ pub struct Wallet { is_open: Arc, /// Error on wallet loading. - pub loading_error: Option, + loading_error: Arc, /// Loading progress in percents pub loading_progress: Arc, @@ -131,16 +135,13 @@ pub struct Wallet { } impl Wallet { - /// Delay in seconds to update wallet info. - pub const INFO_UPDATE_DELAY: Duration = Duration::from_millis(20 * 1000); - /// Create wallet from provided instance and config. fn new(instance: WalletInstance, config: WalletConfig) -> Self { Self { instance, config, is_open: Arc::from(AtomicBool::new(false)), - loading_error: None, + loading_error: Arc::from(AtomicBool::new(false)), loading_progress: Arc::new(AtomicU8::new(0)), info: Arc::new(RwLock::new(None)), } @@ -239,85 +240,34 @@ impl Wallet { Ok(Arc::new(Mutex::new(wallet))) } - /// Open wallet and start info updating at separate thread. + /// Open the wallet and start commands handling and updating info at separate thread. pub fn open(&self, password: String) -> Result<(), Error> { let mut wallet_lock = self.instance.lock(); let lc = wallet_lock.lc_provider()?; lc.close_wallet(None)?; - let mut wallet = self.clone(); - match lc.open_wallet(None, ZeroingString::from(password), false, false) { - Ok(result) => { - self.is_open.store(true, Ordering::Relaxed); - let keychain_mask = result.clone(); - - // Launch loop at separate thread to update wallet info. - thread::spawn(move || loop { - // Stop updating if wallet was closed. - if !wallet.is_open() { - break; - } - let (tx, rx) = mpsc::channel::(); - // Update progress at separate thread. - let wallet_scan = wallet.clone(); - thread::spawn(move || { - while let Ok(m) = rx.recv() { - println!("m: {}", serde_json::to_string::(&m.clone()).unwrap()); - match m { - StatusMessage::UpdatingOutputs(_) => {} - StatusMessage::UpdatingTransactions(_) => {} - StatusMessage::FullScanWarn(_) => {} - StatusMessage::Scanning(_, progress) => { - wallet_scan - .loading_progress - .store(progress, Ordering::Relaxed); - } - StatusMessage::ScanningComplete(_) => { - wallet_scan - .loading_progress - .store(100, Ordering::Relaxed); - - } - StatusMessage::UpdateWarning(_) => {} - } - } - }); - // Retrieve wallet info. - match retrieve_summary_info( - wallet.instance.clone(), - keychain_mask.as_ref(), - &Some(tx), - true, - wallet.config.min_confirmations - ) { - Ok(info) => { - let mut w_info = wallet.info.write().unwrap(); - *w_info = Some(info.1); - } - Err(e) => { - println!("Error!: {}", e); - wallet.loading_error = Some(e); - } - } - - // Repeat after default delay or after 1 second if update was not complete. - let delay = if wallet.loading_progress() == 100 { - Self::INFO_UPDATE_DELAY - } else { - Duration::from_millis(1000) - }; - thread::sleep(delay); - }); - } - Err(e) => return Err(e) + // Open the wallet. + match lc.open_wallet(None, ZeroingString::from(password), false, false) { + Ok(keychain) => { + self.is_open.store(true, Ordering::Relaxed); + // Start commands handling and updating info. + start_wallet(self.clone(), keychain); } + Err(e) => return Err(e) + } Ok(()) } + /// Get wallet loading progress. pub fn loading_progress(&self) -> u8 { self.loading_progress.load(Ordering::Relaxed) } + /// Check if wallet had an error on loading. + pub fn loading_error(&self) -> bool { + self.loading_error.load(Ordering::Relaxed) + } + /// Check if wallet is open. pub fn is_open(&self) -> bool { self.is_open.load(Ordering::Relaxed) @@ -339,4 +289,80 @@ impl Wallet { let r_info = self.info.read().unwrap(); r_info.clone() } +} + +/// Delay in seconds to update wallet info. +const INFO_UPDATE_DELAY: Duration = Duration::from_millis(20 * 1000); + +/// Maximum number of attempts to load the wallet on start before stopping the loop. +const MAX_LOADING_ATTEMPTS: i8 = 10; + +/// Start wallet by launching separate thread to handle commands and updating info after delay. +fn start_wallet(wallet: Wallet, keychain_mask: Option) { + // Launch loop at separate thread to update wallet info. + let mut loading_attempts = 1; + thread::spawn(move || loop { + // Stop updating if wallet was closed. + if !wallet.is_open() { + break; + } + + // Update progress at separate thread. + let wallet_scan = wallet.clone(); + let (tx, rx) = mpsc::channel::(); + thread::spawn(move || { + while let Ok(m) = rx.recv() { + // Stop updating if wallet was closed or maximum. + if !wallet_scan.is_open() || loading_attempts == MAX_LOADING_ATTEMPTS { + return; + } + println!("m: {}", serde_json::to_string::(&m.clone()).unwrap()); + match m { + StatusMessage::UpdatingOutputs(_) => {} + StatusMessage::UpdatingTransactions(_) => {} + StatusMessage::FullScanWarn(_) => {} + StatusMessage::Scanning(_, progress) => { + wallet_scan.loading_progress.store(progress, Ordering::Relaxed); + } + StatusMessage::ScanningComplete(_) => { + wallet_scan.loading_progress.store(100, Ordering::Relaxed); + } + StatusMessage::UpdateWarning(_) => {} + } + } + }); + + // Retrieve wallet info. + match retrieve_summary_info( + wallet.instance.clone(), + keychain_mask.as_ref(), + &Some(tx), + true, + wallet.config.min_confirmations + ) { + Ok(info) => { + wallet.loading_error.store(false, Ordering::Relaxed); + let mut w_info = wallet.info.write().unwrap(); + *w_info = Some(info.1); + } + Err(e) => { + println!("Error!: {}", e); + loading_attempts += 1; + 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; + } + } + } + + // Repeat after default delay or after 1 second if update was not complete. + let delay = if wallet.loading_progress() == 100 { + INFO_UPDATE_DELAY + } else { + Duration::from_millis(1000) + }; + thread::sleep(delay); + }); } \ No newline at end of file