From 1431e307ee5d0489d34e4d83ac7277d11bfafcb6 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Sat, 14 Sep 2024 15:21:08 +0300 Subject: [PATCH] ui: separate wallet accounts modal --- src/gui/views/wallets/wallet/accounts.rs | 240 +++++++++++++++++++++++ src/gui/views/wallets/wallet/content.rs | 213 ++------------------ src/gui/views/wallets/wallet/mod.rs | 4 +- 3 files changed, 256 insertions(+), 201 deletions(-) create mode 100644 src/gui/views/wallets/wallet/accounts.rs diff --git a/src/gui/views/wallets/wallet/accounts.rs b/src/gui/views/wallets/wallet/accounts.rs new file mode 100644 index 0000000..6b50168 --- /dev/null +++ b/src/gui/views/wallets/wallet/accounts.rs @@ -0,0 +1,240 @@ +// Copyright 2024 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. + +use egui::{Align, Id, Layout, RichText, ScrollArea}; +use egui::scroll_area::ScrollBarVisibility; +use grin_core::core::amount_to_hr_string; + +use crate::gui::Colors; +use crate::gui::icons::{CHECK, CHECK_FAT, FOLDER_USER, PATH}; +use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::{Modal, View}; +use crate::gui::views::types::TextEditOptions; +use crate::gui::views::wallets::wallet::types::GRIN; +use crate::wallet::types::WalletAccount; +use crate::wallet::{Wallet, WalletConfig}; + +/// Wallet accounts content. +pub struct WalletAccounts { + /// List of wallet accounts. + accounts: Vec, + /// Flag to check if account is creating. + account_creating: bool, + /// Account label value. + account_label_edit: String, + /// Flag to check if error occurred during account creation. + account_creation_error: bool, +} + +impl Default for WalletAccounts { + fn default() -> Self { + Self { + accounts: vec![], + account_creating: false, + account_label_edit: "".to_string(), + account_creation_error: false, + } + } +} + +impl WalletAccounts { + /// Create new instance from wallet accounts. + pub fn new(accounts: Vec) -> Self { + Self { + accounts, + account_creating: false, + account_label_edit: "".to_string(), + account_creation_error: false, + } + } + + /// Draw [`Modal`] content. + pub fn ui(&mut self, + ui: &mut egui::Ui, + wallet: &mut Wallet, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + if self.account_creating { + ui.add_space(6.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("wallets.new_account_desc")) + .size(17.0) + .color(Colors::gray())); + ui.add_space(8.0); + + // Draw account name edit. + let text_edit_id = Id::from(modal.id).with(wallet.get_config().id); + let mut text_edit_opts = TextEditOptions::new(text_edit_id); + View::text_edit(ui, cb, &mut self.account_label_edit, &mut text_edit_opts); + + // Show error occurred during account creation.. + if self.account_creation_error { + ui.add_space(12.0); + ui.label(RichText::new(t!("error")) + .size(17.0) + .color(Colors::red())); + } + ui.add_space(12.0); + }); + + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); + + // Show modal buttons. + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { + // 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(_) => { + let _ = wallet.set_active_account(label); + cb.hide_keyboard(); + modal.close(); + }, + Err(_) => self.account_creation_error = true + }; + } + }; + + View::on_enter_key(ui, || { + (on_create)(); + }); + + View::button(ui, t!("create"), Colors::white_or_black(false), on_create); + }); + }); + ui.add_space(6.0); + } else { + ui.add_space(3.0); + + // Show list of accounts. + let size = self.accounts.len(); + ScrollArea::vertical() + .id_source("account_list_modal_scroll") + .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) + .max_height(266.0) + .auto_shrink([true; 2]) + .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { + for index in row_range { + // Add space before the first item. + if index == 0 { + ui.add_space(4.0); + } + let acc = self.accounts.get(index).unwrap(); + account_item_ui(ui, modal, wallet, acc, index, size); + if index == size - 1 { + ui.add_space(4.0); + } + } + }); + + ui.add_space(2.0); + View::horizontal_line(ui, Colors::stroke()); + ui.add_space(6.0); + + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); + + // Show modal buttons. + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("create"), Colors::white_or_black(false), || { + self.account_creating = true; + cb.show_keyboard(); + }); + }); + }); + ui.add_space(6.0); + } + } + +} + +const ACCOUNT_ITEM_HEIGHT: f32 = 75.0; + +/// Draw account item. +fn account_item_ui(ui: &mut egui::Ui, + modal: &Modal, + wallet: &mut Wallet, + acc: &WalletAccount, + index: usize, + size: usize) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(ACCOUNT_ITEM_HEIGHT); + + // Draw round background. + let bg_rect = rect.clone(); + let item_rounding = View::item_rounding(index, size, false); + ui.painter().rect(bg_rect, item_rounding, Colors::fill(), View::item_stroke()); + + ui.vertical(|ui| { + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw button to select account. + let is_current_account = wallet.get_config().account == acc.label; + if !is_current_account { + let button_rounding = View::item_rounding(index, size, true); + View::item_button(ui, button_rounding, CHECK, None, || { + let _ = wallet.set_active_account(&acc.label); + modal.close(); + }); + } else { + ui.add_space(12.0); + ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green())); + } + + 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(4.0); + // Show spendable amount. + let amount = amount_to_hr_string(acc.spendable_amount, true); + let amount_text = format!("{} {}", amount, GRIN); + ui.label(RichText::new(amount_text).size(18.0).color(Colors::white_or_black(true))); + ui.add_space(-2.0); + + // Show account name. + let default_acc_label = WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string(); + let acc_label = if acc.label == default_acc_label { + t!("wallets.default_account") + } else { + acc.label.to_owned() + }; + let acc_name = format!("{} {}", FOLDER_USER, acc_label); + View::ellipsize_text(ui, acc_name, 15.0, Colors::text(false)); + + // Show account BIP32 derivation path. + let acc_path = format!("{} {}", PATH, acc.path); + ui.label(RichText::new(acc_path).size(15.0).color(Colors::gray())); + ui.add_space(3.0); + }); + }); + }); + }); +} \ 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 8888931..b7aad65 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -20,27 +20,22 @@ use grin_core::core::amount_to_hr_string; use crate::AppConfig; use crate::gui::Colors; -use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CHAT_CIRCLE_TEXT, CHECK, CHECK_FAT, COPY, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, PATH, POWER, SCAN, SPINNER, USERS_THREE}; +use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CHAT_CIRCLE_TEXT, COPY, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{CameraContent, Modal, Content, View}; -use crate::gui::views::types::{ModalPosition, QrScanResult, TextEditOptions}; +use crate::gui::views::types::{ModalPosition, QrScanResult}; use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport}; use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; +use crate::gui::views::wallets::wallet::accounts::WalletAccounts; use crate::gui::views::wallets::wallet::WalletSettings; use crate::node::Node; use crate::wallet::{Wallet, WalletConfig}; -use crate::wallet::types::{WalletAccount, WalletData}; +use crate::wallet::types::WalletData; /// Selected and opened wallet content. pub struct WalletContent { - /// List of wallet accounts for [`Modal`]. - accounts: Vec, - /// Flag to check if account is creating. - account_creating: bool, - /// Account label [`Modal`] value. - account_label_edit: String, - /// Flag to check if error occurred during account creation at [`Modal`]. - account_creation_error: bool, + /// Wallet accounts [`Modal`] content. + accounts_modal_content: Option, /// Camera content for QR scan [`Modal`]. camera_content: CameraContent, @@ -61,10 +56,7 @@ impl WalletContent { /// Create new instance with optional data. pub fn new(data: Option) -> Self { let mut content = Self { - accounts: vec![], - account_creating: false, - account_label_edit: "".to_string(), - account_creation_error: false, + accounts_modal_content: None, camera_content: CameraContent::default(), qr_scan_result: None, current_tab: Box::new(WalletTransactions::default()), @@ -195,9 +187,11 @@ impl WalletContent { Some(id) => { match id { ACCOUNT_LIST_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { - self.account_list_modal_ui(ui, wallet, modal, cb); - }); + if let Some(content) = self.accounts_modal_content.as_mut() { + Modal::ui(ui.ctx(), |ui, modal| { + content.ui(ui, wallet, modal, cb); + }); + } } QR_CODE_SCAN_MODAL => { Modal::ui(ui.ctx(), |ui, modal| { @@ -238,10 +232,7 @@ impl WalletContent { // Draw button to show list of accounts. View::item_button(ui, View::item_rounding(1, 3, true), USERS_THREE, None, || { - // Load accounts. - self.account_label_edit = "".to_string(); - self.accounts = wallet.accounts(); - self.account_creating = false; + self.accounts_modal_content = Some(WalletAccounts::new(wallet.accounts())); // Show account list modal. Modal::new(ACCOUNT_LIST_MODAL) .position(ModalPosition::CenterTop) @@ -308,120 +299,6 @@ impl WalletContent { }); } - /// Draw account list [`Modal`] content. - fn account_list_modal_ui(&mut self, - ui: &mut egui::Ui, - wallet: &mut Wallet, - modal: &Modal, - cb: &dyn PlatformCallbacks) { - if self.account_creating { - ui.add_space(6.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("wallets.new_account_desc")) - .size(17.0) - .color(Colors::gray())); - ui.add_space(8.0); - - // Draw account name edit. - let text_edit_id = Id::from(modal.id).with(wallet.get_config().id); - let mut text_edit_opts = TextEditOptions::new(text_edit_id); - View::text_edit(ui, cb, &mut self.account_label_edit, &mut text_edit_opts); - - // Show error occurred during account creation.. - if self.account_creation_error { - ui.add_space(12.0); - ui.label(RichText::new(t!("error")) - .size(17.0) - .color(Colors::red())); - } - ui.add_space(12.0); - }); - - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - // Show modal buttons. - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { - // 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(_) => { - let _ = wallet.set_active_account(label); - cb.hide_keyboard(); - modal.close(); - }, - Err(_) => self.account_creation_error = true - }; - } - }; - - View::on_enter_key(ui, || { - (on_create)(); - }); - - View::button(ui, t!("create"), Colors::white_or_black(false), on_create); - }); - }); - ui.add_space(6.0); - } else { - ui.add_space(3.0); - - // Show list of accounts. - let size = self.accounts.len(); - ScrollArea::vertical() - .id_source("account_list_modal_scroll") - .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) - .max_height(266.0) - .auto_shrink([true; 2]) - .show_rows(ui, ACCOUNT_ITEM_HEIGHT, size, |ui, row_range| { - for index in row_range { - // Add space before the first item. - if index == 0 { - ui.add_space(4.0); - } - let acc = self.accounts.get(index).unwrap(); - account_item_ui(ui, modal, wallet, acc, index, size); - if index == size - 1 { - ui.add_space(4.0); - } - } - }); - - ui.add_space(2.0); - View::horizontal_line(ui, Colors::stroke()); - ui.add_space(6.0); - - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); - - // Show modal buttons. - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::white_or_black(false), || { - modal.close(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("create"), Colors::white_or_black(false), || { - self.account_creating = true; - cb.show_keyboard(); - }); - }); - }); - ui.add_space(6.0); - } - } - /// Draw QR code scan [`Modal`] content. fn scan_qr_modal_ui(&mut self, ui: &mut egui::Ui, @@ -676,68 +553,4 @@ impl WalletContent { }); }); } -} - -const ACCOUNT_ITEM_HEIGHT: f32 = 75.0; - -/// Draw account item. -fn account_item_ui(ui: &mut egui::Ui, - modal: &Modal, - wallet: &mut Wallet, - acc: &WalletAccount, - index: usize, - size: usize) { - // Setup layout size. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(ACCOUNT_ITEM_HEIGHT); - - // Draw round background. - let bg_rect = rect.clone(); - let item_rounding = View::item_rounding(index, size, false); - ui.painter().rect(bg_rect, item_rounding, Colors::fill(), View::item_stroke()); - - ui.vertical(|ui| { - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw button to select account. - let is_current_account = wallet.get_config().account == acc.label; - if !is_current_account { - let button_rounding = View::item_rounding(index, size, true); - View::item_button(ui, button_rounding, CHECK, None, || { - let _ = wallet.set_active_account(&acc.label); - modal.close(); - }); - } else { - ui.add_space(12.0); - ui.label(RichText::new(CHECK_FAT).size(20.0).color(Colors::green())); - } - - 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(4.0); - // Show spendable amount. - let amount = amount_to_hr_string(acc.spendable_amount, true); - let amount_text = format!("{} {}", amount, GRIN); - ui.label(RichText::new(amount_text).size(18.0).color(Colors::white_or_black(true))); - ui.add_space(-2.0); - - // Show account name. - let default_acc_label = WalletConfig::DEFAULT_ACCOUNT_LABEL.to_string(); - let acc_label = if acc.label == default_acc_label { - t!("wallets.default_account") - } else { - acc.label.to_owned() - }; - let acc_name = format!("{} {}", FOLDER_USER, acc_label); - View::ellipsize_text(ui, acc_name, 15.0, Colors::text(false)); - - // Show account BIP32 derivation path. - let acc_path = format!("{} {}", PATH, acc.path); - ui.label(RichText::new(acc_path).size(15.0).color(Colors::gray())); - ui.add_space(3.0); - }); - }); - }); - }); } \ No newline at end of file diff --git a/src/gui/views/wallets/wallet/mod.rs b/src/gui/views/wallets/wallet/mod.rs index dc40505..a8f4233 100644 --- a/src/gui/views/wallets/wallet/mod.rs +++ b/src/gui/views/wallets/wallet/mod.rs @@ -27,4 +27,6 @@ mod transport; pub use transport::WalletTransport; mod content; -pub use content::WalletContent; \ No newline at end of file +pub use content::WalletContent; + +mod accounts; \ No newline at end of file