diff --git a/locales/en.yml b/locales/en.yml index 46ea51c..d2675a4 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -9,6 +9,10 @@ change: Change show: Show delete: Delete wallets: + await_conf_amount: Awaiting confirmation + await_fin_amount: Awaiting finalization + locked_amount: Locked + txs_empty: 'To receive/send coins use %{receive}/%{send} buttons at the bottom of the screen, to change wallet settings press %{settings}.' title: Wallets create_desc: Create or import existing wallet from saved recovery phrase. add: Add wallet @@ -47,6 +51,7 @@ wallets: wallet_closing: Closing wallet wallet_checking: Checking wallet tx_loading: Loading transactions + default_account: Default account recovery: Recovery repair_wallet: Repair wallet repair_desc: Check a wallet, repairing and restoring missing outputs if required. This operation will take time. @@ -132,7 +137,7 @@ network_mining: disconnected: Disconnected network_settings: change_value: Change value - stratum_ip: 'Stratum IP Address:' + stratum_ip: 'Stratum IP address:' stratum_port: 'Stratum port:' port_unavailable: Specified port is unavailable restart_node_required: Node restart is required to apply changes. @@ -140,8 +145,8 @@ network_settings: disable: Disable restart: Restart server: Server - api_ip: 'API IP Address:' - api_port: 'API Port:' + api_ip: 'API IP address:' + api_port: 'API port:' api_secret: 'Rest API and V2 Owner API token:' foreign_api_secret: 'Foreign API token:' disabled: Disabled diff --git a/locales/ru.yml b/locales/ru.yml index 2698836..1c6f66e 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -9,6 +9,10 @@ change: Изменить show: Показать delete: Удалить wallets: + await_conf_amount: Ожидает подтверждения + await_fin_amount: Ожидает завершения + locked_amount: Заблокировано + txs_empty: 'Для получения/отправки монет используйте кнопки %{receive}/%{send} внизу экрана, для изменения настроек кошелька нажмите %{settings}.' title: Кошельки create_desc: Создайте или импортируйте существующий кошелёк из сохранённой фразы восстановления. add: Добавить кошелёк @@ -47,6 +51,7 @@ wallets: wallet_closing: Закрытие кошелька wallet_checking: Проверка кошелька tx_loading: Загрузка транзакций + default_account: Стандартный аккаунт recovery: Восстановление repair_wallet: Починить кошелёк repair_desc: Проверить кошелёк, исправляя и восстанавливая недостающие выходы, если это необходимо. Эта операция займёт время. @@ -132,7 +137,7 @@ network_mining: disconnected: Отключен network_settings: change_value: Изменить значение - stratum_ip: 'Stratum IP Адрес:' + stratum_ip: 'Stratum IP адрес:' stratum_port: 'Порт Stratum:' port_unavailable: Указанный порт недоступен restart_node_required: Для применения изменений требуется перезапуск узла. @@ -140,8 +145,8 @@ network_settings: disable: Выключить restart: Перезапуск server: Сервер - api_ip: 'API IP Адрес:' - api_port: 'API Порт:' + api_ip: 'API IP адрес:' + api_port: 'API порт:' api_secret: 'Rest и V2 Owner API токен:' foreign_api_secret: 'Foreign API токен:' disabled: Отключен diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 2475b75..55aa766 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -14,17 +14,19 @@ use std::time::Duration; -use egui::{Margin, RichText}; +use egui::{Align, Layout, Margin, RichText, Rounding}; use grin_chain::SyncStatus; +use grin_core::core::amount_to_hr_string; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{DOWNLOAD, GEAR_FINE, POWER, REPEAT, UPLOAD, WALLET}; +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::wallets::{WalletInfo, WalletReceive, WalletSend, WalletSettings}; use crate::gui::views::wallets::types::{WalletTab, WalletTabType}; use crate::node::Node; +use crate::wallet::types::WalletData; use crate::wallet::Wallet; /// Selected and opened wallet content. @@ -45,9 +47,36 @@ impl WalletContent { frame: &mut eframe::Frame, wallet: &mut Wallet, cb: &dyn PlatformCallbacks) { + let data = wallet.get_data(); + let data_empty = data.is_none(); + + // Show wallet balance panel when data is not empty and current tab is not Settings. + let show_balance = self.current_tab.get_type() != WalletTabType::Settings && !data_empty; + egui::TopBottomPanel::top("wallet_balance") + .frame(egui::Frame { + fill: Colors::FILL, + stroke: View::DEFAULT_STROKE, + inner_margin: Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::get_right_inset() + 4.0, + top: 4.0, + bottom: View::get_bottom_inset() + 4.0, + }, + ..Default::default() + }) + .show_animated_inside(ui, show_balance, |ui| { + ui.vertical_centered(|ui| { + // Draw wallet tabs. + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + Self::account_balance_ui(ui, data.as_ref().unwrap(), &wallet.config.account); + }); + }); + }); + // Show wallet tabs panel. egui::TopBottomPanel::bottom("wallet_tabs") .frame(egui::Frame { + stroke: View::DEFAULT_STROKE, fill: Colors::FILL, inner_margin: Margin { left: View::far_left_inset_margin(ui) + 4.0, @@ -69,14 +98,8 @@ impl WalletContent { // Show tab content panel. egui::CentralPanel::default() .frame(egui::Frame { - stroke: View::DEFAULT_STROKE, fill: Colors::WHITE, - inner_margin: Margin { - left: View::far_left_inset_margin(ui) + 4.0, - right: View::get_right_inset() + 4.0, - top: 3.0, - bottom: 4.0, - }, + stroke: View::DEFAULT_STROKE, ..Default::default() }) .show_inside(ui, |ui| { @@ -84,13 +107,67 @@ impl WalletContent { }); // Refresh content after 1 second for synced wallet. - if wallet.get_data().is_some() { + if !data_empty { ui.ctx().request_repaint_after(Duration::from_millis(1000)); } else { ui.ctx().request_repaint(); } } + /// Draw wallet account balance. + fn account_balance_ui(ui: &mut egui::Ui, data: &WalletData, account: &Option) { + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(76.0); + // Draw round background. + let rounding = View::item_rounding(0, 1, false); + ui.painter().rect(rect, rounding, Colors::BUTTON, View::ITEM_STROKE); + + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Setup padding for item buttons. + ui.style_mut().spacing.button_padding = egui::vec2(14.0, 0.0); + // Setup rounding for item buttons. + ui.style_mut().visuals.widgets.inactive.rounding = Rounding::same(8.0); + ui.style_mut().visuals.widgets.hovered.rounding = Rounding::same(8.0); + ui.style_mut().visuals.widgets.active.rounding = Rounding::same(8.0); + + // Draw button to add new account. + View::item_button(ui, View::item_rounding(0, 1, true), PLUS, None, || { + //TODO add account modal. + }); + + // Draw button to show list of accounts. + View::item_button(ui, Rounding::none(), LIST, None, || { + //TODO: accounts list modal + }); + + 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(3.0); + // Show spendable amount. + let amount = amount_to_hr_string(data.info.amount_currently_spendable, false); + let amount_text = format!("1234567{} ツ", 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() + }; + let account_text = format!("{} {}", FILE_ARCHIVE, account_name); + ui.add_space(-1.0); + View::ellipsize_text(ui, account_text, 15.0, Colors::TEXT); + ui.add_space(1.0); + + // Show confirmed height. + let height_text = format!("{} {}", PACKAGE, data.info.last_confirmed_height); + ui.label(RichText::new(height_text).size(15.0).color(Colors::GRAY)); + }) + }); + }); + } + /// Draw tab buttons in the bottom of the screen. fn tabs_ui(&mut self, ui: &mut egui::Ui) { ui.scope(|ui| { diff --git a/src/gui/views/wallets/wallet/info.rs b/src/gui/views/wallets/wallet/info.rs index 7d1dc6c..19b08ab 100644 --- a/src/gui/views/wallets/wallet/info.rs +++ b/src/gui/views/wallets/wallet/info.rs @@ -12,10 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. +use egui::{Margin, RichText}; +use grin_core::core::amount_to_hr_string; + +use crate::gui::Colors; +use crate::gui::icons::{DOWNLOAD, GEAR_FINE, UPLOAD}; use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::{Root, View}; use crate::gui::views::wallets::types::WalletTab; use crate::gui::views::wallets::wallet::types::WalletTabType; use crate::gui::views::wallets::wallet::WalletContent; +use crate::wallet::types::WalletData; use crate::wallet::Wallet; /// Wallet info tab content. @@ -35,5 +42,65 @@ impl WalletTab for WalletInfo { if WalletContent::sync_ui(ui, frame, wallet) { return; } + + let data = wallet.get_data().unwrap(); + + // Show wallet transactions panel. + egui::CentralPanel::default() + .frame(egui::Frame { + stroke: View::ITEM_STROKE, + fill: Colors::BUTTON, + inner_margin: Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::get_right_inset() + 4.0, + top: 0.0, + bottom: 4.0, + }, + ..Default::default() + }) + .show_inside(ui, |ui| { + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + self.txs_ui(ui, &data); + }); + }); + if data.txs.is_empty() { + View::center_content(ui, 96.0, |ui| { + let empty_text = t!( + "wallets.txs_empty", + "receive" => DOWNLOAD, + "send" => UPLOAD, + "settings" => GEAR_FINE + ); + ui.label(RichText::new(empty_text).size(16.0).color(Colors::INACTIVE_TEXT)); + }); + } else { + + } + }); + } +} + +impl WalletInfo { + /// Draw transactions content. + fn txs_ui(&self, ui: &mut egui::Ui, data: &WalletData) { + // Show awaiting confirmation amount. + let awaiting_conf = amount_to_hr_string(data.info.amount_awaiting_confirmation, false); + View::rounded_box(ui, + format!("{} ツ", awaiting_conf), + t!("wallets.await_conf_amount"), + [false, false, false, false]); + // Show awaiting finalization amount. + let awaiting_conf = amount_to_hr_string(data.info.amount_awaiting_finalization, false); + View::rounded_box(ui, + format!("{} ツ", awaiting_conf), + t!("wallets.await_fin_amount"), + [false, false, false, false]); + // Show locked amount. + let awaiting_conf = amount_to_hr_string(data.info.amount_locked, false); + View::rounded_box(ui, + format!("{} ツ", awaiting_conf), + t!("wallets.locked_amount"), + [false, false, true, true]); } } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/receive.rs b/src/gui/views/wallets/wallet/receive.rs index e4f4b2b..80be65c 100644 --- a/src/gui/views/wallets/wallet/receive.rs +++ b/src/gui/views/wallets/wallet/receive.rs @@ -12,12 +12,16 @@ // See the License for the specific language governing permissions and // limitations under the License. +use egui::Margin; + +use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::View; use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; use crate::wallet::Wallet; -/// Receive funds tab content. +/// Receiving tab content. #[derive(Default)] pub struct WalletReceive; @@ -34,5 +38,29 @@ impl WalletTab for WalletReceive { if WalletContent::sync_ui(ui, frame, wallet) { return; } + + // Show receiving content panel. + egui::CentralPanel::default() + .frame(egui::Frame { + stroke: View::DEFAULT_STROKE, + fill: Colors::WHITE, + inner_margin: Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::get_right_inset() + 4.0, + top: 3.0, + bottom: 4.0, + }, + ..Default::default() + }) + .show_inside(ui, |ui| { + self.receive_ui(ui, wallet); + }); + } +} + +impl WalletReceive { + /// Draw receiving content. + pub fn receive_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { + } } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/send.rs b/src/gui/views/wallets/wallet/send.rs index ecd9586..824485e 100644 --- a/src/gui/views/wallets/wallet/send.rs +++ b/src/gui/views/wallets/wallet/send.rs @@ -12,12 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. +use egui::Margin; +use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::View; use crate::gui::views::wallets::wallet::types::{WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::WalletContent; use crate::wallet::Wallet; -/// Send funds tab content. +/// Sending tab content. #[derive(Default)] pub struct WalletSend; @@ -30,9 +33,33 @@ impl WalletTab for WalletSend { ui: &mut egui::Ui, frame: &mut eframe::Frame, wallet: &mut Wallet, - cb: &dyn PlatformCallbacks) { + _: &dyn PlatformCallbacks) { if WalletContent::sync_ui(ui, frame, wallet) { return; } + + // Show sending content panel. + egui::CentralPanel::default() + .frame(egui::Frame { + stroke: View::DEFAULT_STROKE, + fill: Colors::WHITE, + inner_margin: Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::get_right_inset() + 4.0, + top: 3.0, + bottom: 4.0, + }, + ..Default::default() + }) + .show_inside(ui, |ui| { + self.send_ui(ui, wallet); + }); + } +} + +impl WalletSend { + /// Draw sending content. + pub fn send_ui(&self, ui: &mut egui::Ui, wallet: &mut Wallet) { + } } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/settings.rs b/src/gui/views/wallets/wallet/settings.rs index 436c6c4..521a087 100644 --- a/src/gui/views/wallets/wallet/settings.rs +++ b/src/gui/views/wallets/wallet/settings.rs @@ -12,8 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Id, ScrollArea}; +use egui::{Id, Margin, ScrollArea}; +use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Root, View}; use crate::gui::views::wallets::setup::{CommonSetup, ConnectionSetup, RecoverySetup}; @@ -59,20 +60,35 @@ impl WalletTab for WalletSettings { return; } - ScrollArea::vertical() - .id_source(Id::from("wallet_settings_scroll").with(wallet.config.id)) - .auto_shrink([false; 2]) - .show(ui, |ui| { - ui.vertical_centered(|ui| { - View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { - // Show common wallet setup. - self.common_setup.ui(ui, frame, wallet, cb); - // Show wallet connections setup. - self.conn_setup.wallet_ui(ui, frame, wallet, cb); - // Show wallet recovery setup. - self.recovery_setup.ui(ui, frame, wallet, cb); + // Show settings content panel. + egui::CentralPanel::default() + .frame(egui::Frame { + stroke: View::DEFAULT_STROKE, + fill: Colors::WHITE, + inner_margin: Margin { + left: View::far_left_inset_margin(ui) + 4.0, + right: View::get_right_inset() + 4.0, + top: 3.0, + bottom: 4.0, + }, + ..Default::default() + }) + .show_inside(ui, |ui| { + ScrollArea::vertical() + .id_source(Id::from("wallet_settings_scroll").with(wallet.config.id)) + .auto_shrink([false; 2]) + .show(ui, |ui| { + ui.vertical_centered(|ui| { + View::max_width_ui(ui, Root::SIDE_PANEL_WIDTH * 1.3, |ui| { + // Show common wallet setup. + self.common_setup.ui(ui, frame, wallet, cb); + // Show wallet connections setup. + self.conn_setup.wallet_ui(ui, frame, wallet, cb); + // Show wallet recovery setup. + self.recovery_setup.ui(ui, frame, wallet, cb); + }); + }); }); - }); }); } } \ No newline at end of file diff --git a/src/wallet/config.rs b/src/wallet/config.rs index f3f516f..27ad52f 100644 --- a/src/wallet/config.rs +++ b/src/wallet/config.rs @@ -24,11 +24,13 @@ use crate::wallet::types::ConnectionMethod; /// Wallet configuration. #[derive(Serialize, Deserialize, Clone)] pub struct WalletConfig { + /// Last chosen wallet account label. + pub account: Option, /// Chain type for current wallet. pub chain_type: ChainTypes, /// Identifier for a wallet. pub id: i64, - /// Human-readable wallet name for ui. + /// Wallet name. pub name: String, /// External connection identifier. pub ext_conn_id: Option, @@ -41,11 +43,11 @@ pub const BASE_DIR_NAME: &'static str = "wallets"; /// Wallet configuration file name. const CONFIG_FILE_NAME: &'static str = "grim-wallet.toml"; -/// Minimal amount of confirmations default value. +/// Default value of minimal amount of confirmations. const MIN_CONFIRMATIONS_DEFAULT: u64 = 10; impl WalletConfig { - /// Create wallet config. + /// Create new wallet config. pub fn create(name: String, conn_method: &ConnectionMethod) -> WalletConfig { // Setup configuration path. let id = chrono::Utc::now().timestamp(); @@ -53,6 +55,7 @@ impl WalletConfig { let config_path = Self::get_config_file_path(chain_type, id); // Write configuration to the file. let config = WalletConfig { + account: None, chain_type, id, name, @@ -60,7 +63,7 @@ impl WalletConfig { ConnectionMethod::Integrated => None, ConnectionMethod::External(id) => Some(*id) }, - min_confirmations: MIN_CONFIRMATIONS_DEFAULT, + min_confirmations: MIN_CONFIRMATIONS_DEFAULT }; Settings::write_to_file(&config, config_path); config