wallet + ui: optimize loading, update translations
This commit is contained in:
parent
417e8a28cb
commit
3c14f72c24
5 changed files with 112 additions and 101 deletions
|
@ -31,7 +31,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 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
|
wallet: Wallet
|
||||||
send: Send
|
send: Send
|
||||||
receive: Receive
|
receive: Receive
|
||||||
|
|
|
@ -31,7 +31,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: Получить
|
||||||
|
|
|
@ -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;
|
|
|
@ -12,10 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use egui::{Margin, RichText};
|
use egui::{Margin, RichText};
|
||||||
|
|
||||||
use crate::gui::Colors;
|
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::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};
|
||||||
|
@ -74,9 +76,9 @@ impl WalletContent {
|
||||||
self.current_tab.ui(ui, frame, wallet, cb);
|
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() {
|
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 {
|
} else {
|
||||||
ui.ctx().request_repaint();
|
ui.ctx().request_repaint();
|
||||||
}
|
}
|
||||||
|
@ -109,7 +111,7 @@ impl WalletContent {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
columns[3].vertical_centered_justified(|ui| {
|
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());
|
self.current_tab = Box::new(WalletSettings::default());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -122,7 +124,7 @@ impl WalletContent {
|
||||||
if wallet.config.ext_conn_id.is_none() && !Node::is_running() {
|
if wallet.config.ext_conn_id.is_none() && !Node::is_running() {
|
||||||
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" => WRENCH);
|
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.
|
// Show button to enable integrated node at non-dual root panel mode.
|
||||||
if !dual_panel_root {
|
if !dual_panel_root {
|
||||||
|
@ -136,11 +138,11 @@ impl WalletContent {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
if wallet.get_info().is_none() || wallet.loading_progress() < 100 {
|
if wallet.get_info().is_none() || wallet.loading_progress() < 100 {
|
||||||
if let Some(error) = &wallet.loading_error {
|
if wallet.loading_error() {
|
||||||
println!("e: {}", error);
|
View::center_content(ui, 96.0, |ui| {
|
||||||
let text = t!("wallets.wallet_loading_err");
|
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));
|
||||||
//TODO: button to retry.
|
});
|
||||||
} else {
|
} else {
|
||||||
View::center_content(ui, 162.0, |ui| {
|
View::center_content(ui, 162.0, |ui| {
|
||||||
View::big_loading_spinner(ui);
|
View::big_loading_spinner(ui);
|
||||||
|
@ -157,7 +159,6 @@ impl WalletContent {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
println!("12345 info!!!");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
|
@ -12,15 +12,16 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::thread;
|
|
||||||
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, AtomicU8, Ordering};
|
||||||
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
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};
|
||||||
|
use grin_util::secp::SecretKey;
|
||||||
use grin_util::types::ZeroingString;
|
use grin_util::types::ZeroingString;
|
||||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
|
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl, HTTPNodeClient};
|
||||||
use grin_wallet_libwallet::{Error, NodeClient, StatusMessage, WalletBackend, WalletInfo, WalletInst, WalletLCProvider};
|
use grin_wallet_libwallet::{Error, NodeClient, StatusMessage, WalletBackend, WalletInfo, WalletInst, WalletLCProvider};
|
||||||
|
@ -69,6 +70,9 @@ impl Wallets {
|
||||||
|
|
||||||
/// Reinitialize wallets for provided [`ChainTypes`].
|
/// Reinitialize wallets 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() {
|
||||||
|
let _ = w.close();
|
||||||
|
}
|
||||||
self.list = Self::init(chain_type);
|
self.list = Self::init(chain_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,7 +126,7 @@ pub struct Wallet {
|
||||||
is_open: Arc<AtomicBool>,
|
is_open: Arc<AtomicBool>,
|
||||||
|
|
||||||
/// Error on wallet loading.
|
/// Error on wallet loading.
|
||||||
pub loading_error: Option<Error>,
|
loading_error: Arc<AtomicBool>,
|
||||||
/// Loading progress in percents
|
/// Loading progress in percents
|
||||||
pub loading_progress: Arc<AtomicU8>,
|
pub loading_progress: Arc<AtomicU8>,
|
||||||
|
|
||||||
|
@ -131,16 +135,13 @@ pub struct Wallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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.
|
/// Create wallet from provided instance and config.
|
||||||
fn new(instance: WalletInstance, config: WalletConfig) -> Self {
|
fn new(instance: WalletInstance, config: WalletConfig) -> Self {
|
||||||
Self {
|
Self {
|
||||||
instance,
|
instance,
|
||||||
config,
|
config,
|
||||||
is_open: Arc::from(AtomicBool::new(false)),
|
is_open: Arc::from(AtomicBool::new(false)),
|
||||||
loading_error: None,
|
loading_error: Arc::from(AtomicBool::new(false)),
|
||||||
loading_progress: Arc::new(AtomicU8::new(0)),
|
loading_progress: Arc::new(AtomicU8::new(0)),
|
||||||
info: Arc::new(RwLock::new(None)),
|
info: Arc::new(RwLock::new(None)),
|
||||||
}
|
}
|
||||||
|
@ -239,85 +240,34 @@ impl Wallet {
|
||||||
Ok(Arc::new(Mutex::new(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> {
|
pub fn open(&self, password: String) -> Result<(), Error> {
|
||||||
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)?;
|
||||||
|
|
||||||
let mut wallet = self.clone();
|
// Open the wallet.
|
||||||
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
match lc.open_wallet(None, ZeroingString::from(password), false, false) {
|
||||||
Ok(result) => {
|
Ok(keychain) => {
|
||||||
self.is_open.store(true, Ordering::Relaxed);
|
self.is_open.store(true, Ordering::Relaxed);
|
||||||
let keychain_mask = result.clone();
|
// Start commands handling and updating info.
|
||||||
|
start_wallet(self.clone(), keychain);
|
||||||
// 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::<StatusMessage>();
|
|
||||||
// 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::<StatusMessage>(&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)
|
|
||||||
}
|
}
|
||||||
|
Err(e) => return Err(e)
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get wallet loading progress.
|
||||||
pub fn loading_progress(&self) -> u8 {
|
pub fn loading_progress(&self) -> u8 {
|
||||||
self.loading_progress.load(Ordering::Relaxed)
|
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.
|
/// Check if wallet is 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)
|
||||||
|
@ -339,4 +289,80 @@ impl Wallet {
|
||||||
let r_info = self.info.read().unwrap();
|
let r_info = self.info.read().unwrap();
|
||||||
r_info.clone()
|
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<SecretKey>) {
|
||||||
|
// 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::<StatusMessage>();
|
||||||
|
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::<StatusMessage>(&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);
|
||||||
|
});
|
||||||
}
|
}
|
Loading…
Reference in a new issue