ui + wallet: account creation, update translations, set current account from config on opening, separate wallet refreshing method, refresh wallet after transaction receiving, optimize wallet deletion
This commit is contained in:
+3
-2
@@ -9,6 +9,7 @@ change: Change
|
||||
show: Show
|
||||
delete: Delete
|
||||
clear: Clear
|
||||
create: Create
|
||||
wallets:
|
||||
await_conf_amount: Awaiting confirmation
|
||||
await_fin_amount: Awaiting finalization
|
||||
@@ -23,7 +24,6 @@ wallets:
|
||||
current_pass: 'Current password:'
|
||||
new_pass: 'New password:'
|
||||
min_tx_conf_count: 'Minimum amount of confirmations for transactions:'
|
||||
create: Create
|
||||
recover: Restore
|
||||
recovery_phrase: Recovery phrase
|
||||
words_count: 'Words count:'
|
||||
@@ -53,6 +53,8 @@ wallets:
|
||||
wallet_checking: Checking wallet
|
||||
tx_loading: Loading transactions
|
||||
default_account: Default account
|
||||
create_account: Create account
|
||||
label: 'Label:'
|
||||
tx_sent: Sent
|
||||
tx_received: Received
|
||||
tx_sending: Sending
|
||||
@@ -123,7 +125,6 @@ network_node:
|
||||
height: Height
|
||||
difficulty: Difficulty
|
||||
time: Time
|
||||
transactions: Transactions
|
||||
main_pool: Main pool
|
||||
stem_pool: Stem pool
|
||||
data: Data
|
||||
|
||||
+3
-2
@@ -9,6 +9,7 @@ change: Изменить
|
||||
show: Показать
|
||||
delete: Удалить
|
||||
clear: Очистить
|
||||
create: Создать
|
||||
wallets:
|
||||
await_conf_amount: Ожидает подтверждения
|
||||
await_fin_amount: Ожидает завершения
|
||||
@@ -23,7 +24,6 @@ wallets:
|
||||
current_pass: 'Текущий пароль:'
|
||||
new_pass: 'Новый пароль:'
|
||||
min_tx_conf_count: 'Минимальное количество подтверждений для транзакций:'
|
||||
create: Создать
|
||||
recover: Восстановить
|
||||
recovery_phrase: Фраза восстановления
|
||||
words_count: 'Количество слов:'
|
||||
@@ -53,6 +53,8 @@ wallets:
|
||||
wallet_checking: Проверка кошелька
|
||||
tx_loading: Загрузка транзакций
|
||||
default_account: Стандартный аккаунт
|
||||
create_account: Создать аккаунт
|
||||
label: 'Метка:'
|
||||
tx_sent: Отправлено
|
||||
tx_received: Получено
|
||||
tx_sending: Отправка
|
||||
@@ -123,7 +125,6 @@ network_node:
|
||||
height: Высота
|
||||
difficulty: Сложность
|
||||
time: Время
|
||||
transactions: Транзакции
|
||||
main_pool: Основной пул
|
||||
stem_pool: Stem пул
|
||||
data: Данные
|
||||
|
||||
@@ -119,7 +119,7 @@ impl MnemonicSetup {
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered(|ui| {
|
||||
let create_mode = PhraseMode::Generate;
|
||||
let create_text = t!("wallets.create");
|
||||
let create_text = t!("create");
|
||||
View::radio_value(ui, &mut mode, create_mode, create_text);
|
||||
});
|
||||
columns[1].vertical_centered(|ui| {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use egui::{Align, Layout, Margin, RichText, Rounding};
|
||||
use egui::{Align, Id, Layout, Margin, RichText, Rounding, TextStyle, Widget};
|
||||
use grin_chain::SyncStatus;
|
||||
use grin_core::core::amount_to_hr_string;
|
||||
|
||||
@@ -22,31 +22,47 @@ use crate::AppConfig;
|
||||
use crate::gui::Colors;
|
||||
use crate::gui::icons::{DOWNLOAD, FILE_ARCHIVE, GEAR_FINE, LIST, PACKAGE, PLUS, POWER, REPEAT, UPLOAD, WALLET};
|
||||
use crate::gui::platform::PlatformCallbacks;
|
||||
use crate::gui::views::{Root, View};
|
||||
use crate::gui::views::{Modal, Root, View};
|
||||
use crate::gui::views::types::ModalPosition;
|
||||
use crate::gui::views::wallets::{WalletInfo, WalletReceive, WalletSend, WalletSettings};
|
||||
use crate::gui::views::wallets::types::{WalletTab, WalletTabType};
|
||||
use crate::node::Node;
|
||||
use crate::wallet::{Wallet, WalletConfig};
|
||||
use crate::wallet::types::WalletData;
|
||||
use crate::wallet::Wallet;
|
||||
|
||||
/// Selected and opened wallet content.
|
||||
pub struct WalletContent {
|
||||
/// Account label [`Modal`] value.
|
||||
pub account_label_edit: String,
|
||||
/// Flag to check if error occurred during account creation at [`Modal`].
|
||||
pub account_creation_error: bool,
|
||||
|
||||
/// Current tab content to show.
|
||||
pub current_tab: Box<dyn WalletTab>,
|
||||
}
|
||||
|
||||
impl Default for WalletContent {
|
||||
fn default() -> Self {
|
||||
Self { current_tab: Box::new(WalletInfo::default()) }
|
||||
Self {
|
||||
account_label_edit: "".to_string(),
|
||||
account_creation_error: false,
|
||||
current_tab: Box::new(WalletInfo::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier for account creation [`Modal`].
|
||||
const CREATE_ACCOUNT_MODAL: &'static str = "create_account_modal";
|
||||
|
||||
impl WalletContent {
|
||||
pub fn ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
frame: &mut eframe::Frame,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
// Show modal content for this ui container.
|
||||
self.modal_content_ui(ui, wallet, cb);
|
||||
|
||||
let data = wallet.get_data();
|
||||
let data_empty = data.is_none();
|
||||
|
||||
@@ -74,7 +90,7 @@ impl WalletContent {
|
||||
ui.vertical_centered(|ui| {
|
||||
// Draw wallet tabs.
|
||||
View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.35, |ui| {
|
||||
Self::account_ui(ui, data.as_ref().unwrap(), &wallet.config.account);
|
||||
Self::account_ui(ui, data.as_ref().unwrap(), &wallet.config.account, cb);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -120,8 +136,31 @@ impl WalletContent {
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw [`Modal`] content for this ui container.
|
||||
fn modal_content_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
match Modal::opened() {
|
||||
None => {}
|
||||
Some(id) => {
|
||||
match id {
|
||||
CREATE_ACCOUNT_MODAL => {
|
||||
Modal::ui(ui.ctx(), |ui, modal| {
|
||||
self.create_account_modal_ui(ui, wallet, modal, cb);
|
||||
});
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Draw wallet account content.
|
||||
fn account_ui(ui: &mut egui::Ui, data: &WalletData, account: &Option<String>) {
|
||||
fn account_ui(ui: &mut egui::Ui,
|
||||
data: &WalletData,
|
||||
account: &String,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
let mut rect = ui.available_rect_before_wrap();
|
||||
rect.set_height(75.0);
|
||||
// Draw round background.
|
||||
@@ -134,7 +173,12 @@ impl WalletContent {
|
||||
|
||||
// Draw button to add new account.
|
||||
View::item_button(ui, View::item_rounding(0, 2, true), PLUS, None, || {
|
||||
//TODO add account modal.
|
||||
// Show account creation modal.
|
||||
Modal::new(CREATE_ACCOUNT_MODAL)
|
||||
.position(ModalPosition::CenterTop)
|
||||
.title(t!("wallets.create_account"))
|
||||
.show();
|
||||
cb.show_keyboard();
|
||||
});
|
||||
|
||||
// Draw button to show list of accounts.
|
||||
@@ -152,14 +196,16 @@ impl WalletContent {
|
||||
let amount_text = format!("{} ツ", amount);
|
||||
ui.label(RichText::new(amount_text).size(18.0).color(Colors::BLACK));
|
||||
|
||||
// Show account name.
|
||||
let account_name = match account {
|
||||
None => t!("wallets.default_account"),
|
||||
Some(name) => name.to_owned()
|
||||
// Show account label.
|
||||
let default_acc_label = &WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string();
|
||||
let acc_label = if account == default_acc_label {
|
||||
t!("wallets.default_account")
|
||||
} else {
|
||||
account.to_owned()
|
||||
};
|
||||
let account_text = format!("{} {}", FILE_ARCHIVE, account_name);
|
||||
let acc_text = format!("{} {}", FILE_ARCHIVE, acc_label);
|
||||
ui.add_space(-2.0);
|
||||
View::ellipsize_text(ui, account_text, 15.0, Colors::TEXT);
|
||||
View::ellipsize_text(ui, acc_text, 15.0, Colors::TEXT);
|
||||
|
||||
// Show confirmed height.
|
||||
let height_text = format!("{} {}", PACKAGE, data.info.last_confirmed_height);
|
||||
@@ -169,6 +215,84 @@ impl WalletContent {
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw account creation [`Modal`] content.
|
||||
fn create_account_modal_ui(&mut self,
|
||||
ui: &mut egui::Ui,
|
||||
wallet: &mut Wallet,
|
||||
modal: &Modal,
|
||||
cb: &dyn PlatformCallbacks) {
|
||||
ui.add_space(6.0);
|
||||
ui.vertical_centered(|ui| {
|
||||
ui.label(RichText::new(t!("wallets.name"))
|
||||
.size(17.0)
|
||||
.color(Colors::GRAY));
|
||||
ui.add_space(8.0);
|
||||
|
||||
// Draw account name edit.
|
||||
let text_edit_resp = egui::TextEdit::singleline(&mut self.account_label_edit)
|
||||
.id(Id::from(modal.id).with(wallet.config.id))
|
||||
.font(TextStyle::Heading)
|
||||
.desired_width(ui.available_width())
|
||||
.cursor_at_end(true)
|
||||
.ui(ui);
|
||||
text_edit_resp.request_focus();
|
||||
if text_edit_resp.clicked() {
|
||||
cb.show_keyboard();
|
||||
}
|
||||
ui.add_space(8.0);
|
||||
});
|
||||
|
||||
// Show error occurred during account creation..
|
||||
if self.account_creation_error {
|
||||
ui.add_space(2.0);
|
||||
ui.label(RichText::new(t!("error"))
|
||||
.size(17.0)
|
||||
.color(Colors::RED));
|
||||
}
|
||||
ui.add_space(12.0);
|
||||
|
||||
// Show modal buttons.
|
||||
ui.scope(|ui| {
|
||||
// Setup spacing between buttons.
|
||||
ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0);
|
||||
|
||||
ui.columns(2, |columns| {
|
||||
columns[0].vertical_centered_justified(|ui| {
|
||||
View::button(ui, t!("modal.cancel"), Colors::WHITE, || {
|
||||
// Close modal.
|
||||
cb.hide_keyboard();
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
columns[1].vertical_centered_justified(|ui| {
|
||||
// Create button callback.
|
||||
let mut on_create = || {
|
||||
if !self.account_label_edit.is_empty() {
|
||||
let label = &self.account_label_edit;
|
||||
match wallet.create_account(label) {
|
||||
Ok(_) => match wallet.set_active_account(label) {
|
||||
Ok(_) => {
|
||||
cb.hide_keyboard();
|
||||
modal.close();
|
||||
}
|
||||
Err(_) => self.account_creation_error = true
|
||||
},
|
||||
Err(_) => self.account_creation_error = true
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
View::on_enter_key(ui, || {
|
||||
(on_create)();
|
||||
});
|
||||
|
||||
View::button(ui, t!("create"), Colors::WHITE, on_create);
|
||||
});
|
||||
});
|
||||
ui.add_space(6.0);
|
||||
});
|
||||
}
|
||||
|
||||
/// Draw tab buttons in the bottom of the screen.
|
||||
fn tabs_ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.scope(|ui| {
|
||||
|
||||
+14
-4
@@ -14,6 +14,7 @@
|
||||
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::string::ToString;
|
||||
|
||||
use grin_core::global::ChainTypes;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@@ -24,8 +25,8 @@ use crate::wallet::types::ConnectionMethod;
|
||||
/// Wallet configuration.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct WalletConfig {
|
||||
/// Last chosen wallet account label.
|
||||
pub account: Option<String>,
|
||||
/// Current wallet account label.
|
||||
pub account: String,
|
||||
/// Chain type for current wallet.
|
||||
pub chain_type: ChainTypes,
|
||||
/// Identifier for a wallet.
|
||||
@@ -39,7 +40,7 @@ pub struct WalletConfig {
|
||||
}
|
||||
|
||||
/// Base wallets directory name.
|
||||
pub const BASE_DIR_NAME: &'static str = "wallets";
|
||||
const BASE_DIR_NAME: &'static str = "wallets";
|
||||
/// Wallet configuration file name.
|
||||
const CONFIG_FILE_NAME: &'static str = "grim-wallet.toml";
|
||||
/// Slatepacks directory name.
|
||||
@@ -49,6 +50,9 @@ const SLATEPACKS_DIR_NAME: &'static str = "slatepacks";
|
||||
const MIN_CONFIRMATIONS_DEFAULT: u64 = 10;
|
||||
|
||||
impl WalletConfig {
|
||||
/// Default account name value.
|
||||
pub const DEFAULT_ACCOUNT_LABEL: &'static str = "default";
|
||||
|
||||
/// Create new wallet config.
|
||||
pub fn create(name: String, conn_method: &ConnectionMethod) -> WalletConfig {
|
||||
// Setup configuration path.
|
||||
@@ -57,7 +61,7 @@ impl WalletConfig {
|
||||
let config_path = Self::get_config_file_path(chain_type, id);
|
||||
// Write configuration to the file.
|
||||
let config = WalletConfig {
|
||||
account: None,
|
||||
account: Self::DEFAULT_ACCOUNT_LABEL.to_string(),
|
||||
chain_type,
|
||||
id,
|
||||
name,
|
||||
@@ -128,4 +132,10 @@ impl WalletConfig {
|
||||
let config_path = Self::get_config_file_path(self.chain_type, self.id);
|
||||
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
-45
@@ -227,10 +227,14 @@ impl Wallet {
|
||||
self.set_sync_error(false);
|
||||
self.reset_sync_attempts();
|
||||
|
||||
// Set current account.
|
||||
let wallet_inst = lc.wallet_inst()?;
|
||||
let label = self.config.account.to_owned();
|
||||
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
||||
|
||||
// Start new synchronization thread or wake up existing one.
|
||||
let mut thread_w = self.sync_thread.write().unwrap();
|
||||
if thread_w.is_none() {
|
||||
// Start wallet synchronization.
|
||||
let thread = start_sync(self.clone());
|
||||
*thread_w = Some(thread);
|
||||
} else {
|
||||
@@ -288,11 +292,9 @@ impl Wallet {
|
||||
wallet_close.foreign_api_server.read().unwrap().is_some()
|
||||
};
|
||||
if api_server_exists {
|
||||
let _ = thread::spawn(move || {
|
||||
let mut api_server_w = wallet_close.foreign_api_server.write().unwrap();
|
||||
api_server_w.as_mut().unwrap().stop();
|
||||
*api_server_w = None;
|
||||
}).join();
|
||||
let mut api_server_w = wallet_close.foreign_api_server.write().unwrap();
|
||||
api_server_w.as_mut().unwrap().stop();
|
||||
*api_server_w = None;
|
||||
}
|
||||
|
||||
// Close the wallet.
|
||||
@@ -302,11 +304,8 @@ impl Wallet {
|
||||
wallet_close.closing.store(false, Ordering::Relaxed);
|
||||
wallet_close.is_open.store(false, Ordering::Relaxed);
|
||||
|
||||
// Wake up wallet thread.
|
||||
let thread_r = wallet_close.sync_thread.read().unwrap();
|
||||
if let Some(thread) = thread_r.as_ref() {
|
||||
thread.unpark();
|
||||
}
|
||||
// Wake up thread to exit.
|
||||
wallet_close.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -318,15 +317,34 @@ impl Wallet {
|
||||
}
|
||||
|
||||
/// Create account into wallet.
|
||||
pub fn create_account(&self, label: String) -> Result<(), Error> {
|
||||
pub fn create_account(&self, label: &String) -> Result<(), Error> {
|
||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
controller::owner_single_use(None, None, Some(&mut api), |api, m| {
|
||||
api.create_account_path(m, &label)?;
|
||||
println!("Account: '{}' Created!", label);
|
||||
api.create_account_path(m, label)?;
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
/// Set active account from provided label.
|
||||
pub fn set_active_account(&mut self, label: &String) -> Result<(), Error> {
|
||||
let instance = self.instance.clone().unwrap();
|
||||
let mut wallet_lock = instance.lock();
|
||||
let lc = wallet_lock.lc_provider()?;
|
||||
let wallet_inst = lc.wallet_inst()?;
|
||||
wallet_inst.set_parent_key_id_by_name(label.as_str())?;
|
||||
|
||||
// Save account label into config.
|
||||
self.config.save_account(label);
|
||||
|
||||
// Clear wallet info.
|
||||
let mut w_data = self.data.write().unwrap();
|
||||
*w_data = None;
|
||||
|
||||
// Refresh wallet data.
|
||||
self.refresh();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Get list of accounts for the wallet.
|
||||
pub fn accounts(&self) -> Vec<AcctPathMapping> {
|
||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
@@ -396,17 +414,21 @@ impl Wallet {
|
||||
r_data.clone()
|
||||
}
|
||||
|
||||
/// Wake up wallet thread to refresh wallet info and update statuses.
|
||||
fn refresh(&self) {
|
||||
let thread_r = self.sync_thread.read().unwrap();
|
||||
if let Some(thread) = thread_r.as_ref() {
|
||||
thread.unpark();
|
||||
}
|
||||
}
|
||||
|
||||
/// Receive transaction via Slatepack Message.
|
||||
pub fn receive(&self, message: String) -> Result<String, Error> {
|
||||
let mut api = Owner::new(self.instance.clone().unwrap(), None);
|
||||
match parse_slatepack(&mut api, None, None, Some(message.clone())) {
|
||||
Ok((mut slate, _)) => {
|
||||
controller::foreign_single_use(api.wallet_inst.clone(), None, |api| {
|
||||
let account = if let Some(acc) = self.config.clone().account {
|
||||
acc
|
||||
} else {
|
||||
"default".to_string()
|
||||
};
|
||||
let account = self.config.clone().account;
|
||||
slate = api.receive_tx(&slate, Some(account.as_str()), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
@@ -426,6 +448,9 @@ impl Wallet {
|
||||
output.write_all(response.as_bytes())?;
|
||||
output.sync_all()?;
|
||||
|
||||
// Refresh wallet info.
|
||||
self.refresh();
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
Err(_) => {
|
||||
@@ -447,15 +472,12 @@ impl Wallet {
|
||||
}
|
||||
|
||||
// Launch tx cancelling at separate thread.
|
||||
let mut wallet_cancel = self.clone();
|
||||
let wallet_cancel = self.clone();
|
||||
let instance = wallet_cancel.instance.clone().unwrap();
|
||||
thread::spawn(move || {
|
||||
let _ = cancel_tx(instance, None, &None, Some(id), None);
|
||||
// Wake up wallet thread to update statuses.
|
||||
let thread_r = wallet_cancel.sync_thread.read().unwrap();
|
||||
if let Some(thread) = thread_r.as_ref() {
|
||||
thread.unpark();
|
||||
}
|
||||
// Refresh wallet info to update statuses.
|
||||
wallet_cancel.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -480,11 +502,7 @@ impl Wallet {
|
||||
/// Initiate wallet repair by scanning its outputs.
|
||||
pub fn repair(&self) {
|
||||
self.repair_needed.store(true, Ordering::Relaxed);
|
||||
// Wake up wallet thread.
|
||||
let thread_r = self.sync_thread.read().unwrap();
|
||||
if let Some(thread) = thread_r.as_ref() {
|
||||
thread.unpark();
|
||||
}
|
||||
self.refresh();
|
||||
}
|
||||
|
||||
/// Check if wallet is repairing.
|
||||
@@ -514,27 +532,23 @@ impl Wallet {
|
||||
|
||||
// Delete wallet at separate thread.
|
||||
let wallet_delete = self.clone();
|
||||
let instance = wallet_delete.instance.clone().unwrap();
|
||||
thread::spawn(move || {
|
||||
thread::sleep(Duration::from_millis(1000));
|
||||
if let Some(instance) = wallet_delete.instance {
|
||||
// Close the wallet.
|
||||
Self::close_wallet(&instance);
|
||||
// Remove wallet files.
|
||||
let mut wallet_lock = instance.lock();
|
||||
let _ = wallet_lock.lc_provider().unwrap();
|
||||
let _ = fs::remove_dir_all(wallet_delete.config.get_data_path());
|
||||
}
|
||||
// Close the wallet.
|
||||
Self::close_wallet(&instance);
|
||||
|
||||
// Remove wallet files.
|
||||
let mut wallet_lock = instance.lock();
|
||||
let _ = wallet_lock.lc_provider().unwrap();
|
||||
let _ = fs::remove_dir_all(wallet_delete.config.get_data_path());
|
||||
|
||||
// Mark wallet as not opened and deleted.
|
||||
wallet_delete.closing.store(false, Ordering::Relaxed);
|
||||
wallet_delete.is_open.store(false, Ordering::Relaxed);
|
||||
wallet_delete.deleted.store(true, Ordering::Relaxed);
|
||||
|
||||
// Wake up wallet thread.
|
||||
let thread_r = wallet_delete.sync_thread.read().unwrap();
|
||||
if let Some(thread) = thread_r.as_ref() {
|
||||
thread.unpark();
|
||||
}
|
||||
// Wake up thread to exit.
|
||||
wallet_delete.refresh();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -863,4 +877,4 @@ fn scan_wallet(wallet: &Wallet) {
|
||||
|
||||
// Reset repair progress.
|
||||
wallet.repair_progress.store(0, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user