diff --git a/src/gui/colors.rs b/src/gui/colors.rs index 7ccbeff..6498553 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -27,7 +27,7 @@ impl Colors { pub const RED: Color32 = Color32::from_rgb(0x8B, 0, 0); pub const FILL: Color32 = Color32::from_gray(244); pub const FILL_DARK: Color32 = Color32::from_gray(234); - pub const CHECKBOX: Color32 = Color32::from_gray(107); + pub const CHECKBOX: Color32 = Color32::from_gray(100); pub const TEXT: Color32 = Color32::from_gray(80); pub const TEXT_BUTTON: Color32 = Color32::from_gray(70); pub const TITLE: Color32 = Color32::from_gray(60); diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index 38fdf46..1df7be7 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -12,6 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub mod types; + mod views; pub use views::View; diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 08c8433..cf9d84c 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -21,35 +21,13 @@ use lazy_static::lazy_static; use crate::gui::Colors; use crate::gui::views::{Root, View}; +use crate::gui::views::types::{ModalPosition, ModalState}; lazy_static! { /// Showing [`Modal`] state to be accessible from different ui parts. static ref MODAL_STATE: RwLock = RwLock::new(ModalState::default()); } -#[derive(Default)] -struct ModalState { - modal: Option -} - -/// Contains ids to draw current [`Modal`] at this ui container if it's possible. -pub trait ModalContainer { - /// List of [`Modal`] ids to draw at current ui container. - fn modal_ids(&self) -> &Vec<&'static str>; - - /// Check if it's possible to draw [`Modal`] at current ui container. - fn can_draw_modal(&self) -> bool { - let modal_id = Modal::opened(); - modal_id.is_some() && self.modal_ids().contains(&modal_id.unwrap()) - } -} - -/// Position of [`Modal`] on the screen. -pub enum ModalPosition { - CenterTop, - Center -} - /// Stores data to draw modal [`egui::Window`] at ui. pub struct Modal { /// Identifier for modal. diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index dd8b4eb..fd91986 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -19,77 +19,27 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, POWER}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalContainer, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitlePanel, TitleType, View}; -use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; -use crate::gui::views::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::{NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings, Root, TitlePanel, View}; +use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::types::TitleType; use crate::node::Node; /// Network content. pub struct NetworkContent { /// Current tab view to show at ui. current_tab: Box, - /// [`Modal`] ids allowed at this ui container. - modal_ids: Vec<&'static str>, } impl Default for NetworkContent { fn default() -> Self { Self { - current_tab: Box::new(NetworkNode::default()), - modal_ids: vec![ - // Network settings modals. - NetworkSettings::NODE_RESTART_REQUIRED_MODAL, - NetworkSettings::RESET_SETTINGS_MODAL, - // Node setup modals. - NodeSetup::API_PORT_MODAL, - NodeSetup::API_SECRET_MODAL, - NodeSetup::FOREIGN_API_SECRET_MODAL, - NodeSetup::FTL_MODAL, - // P2P setup modals. - P2PSetup::PORT_MODAL, - P2PSetup::CUSTOM_SEED_MODAL, - P2PSetup::ALLOW_PEER_MODAL, - P2PSetup::DENY_PEER_MODAL, - P2PSetup::PREFER_PEER_MODAL, - P2PSetup::BAN_WINDOW_MODAL, - P2PSetup::MAX_INBOUND_MODAL, - P2PSetup::MAX_OUTBOUND_MODAL, - P2PSetup::MIN_OUTBOUND_MODAL, - // Stratum setup modals. - StratumSetup::STRATUM_PORT_MODAL, - StratumSetup::ATTEMPT_TIME_MODAL, - StratumSetup::MIN_SHARE_DIFF_MODAL, - // Pool setup modals. - PoolSetup::FEE_BASE_MODAL, - PoolSetup::REORG_PERIOD_MODAL, - PoolSetup::POOL_SIZE_MODAL, - PoolSetup::STEMPOOL_SIZE_MODAL, - PoolSetup::MAX_WEIGHT_MODAL, - // Dandelion setup modals. - DandelionSetup::EPOCH_MODAL, - DandelionSetup::EMBARGO_MODAL, - DandelionSetup::AGGREGATION_MODAL, - DandelionSetup::STEM_PROBABILITY_MODAL, - ] + current_tab: Box::new(NetworkNode::default()) } } } -impl ModalContainer for NetworkContent { - fn modal_ids(&self) -> &Vec<&'static str> { - self.modal_ids.as_ref() - } -} - impl NetworkContent { pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { - // Show modal content for current ui container. - if self.can_draw_modal() { - Modal::ui(ui.ctx(), |ui, modal| { - self.current_tab.as_mut().on_modal_ui(ui, modal, cb); - }); - } - // Show title panel. self.title_ui(ui, frame); @@ -113,7 +63,7 @@ impl NetworkContent { ..Default::default() }) .show_inside(ui, |ui| { - self.current_tab.ui(ui, cb); + self.current_tab.ui(ui, frame, cb); }); // Redraw content after delay if node is not syncing to update stats. @@ -186,7 +136,7 @@ impl NetworkContent { let title_content = TitleType::WithSubTitle(title_text, subtitle_text, !not_syncing); // Draw title panel. - TitlePanel::ui(title_content, |ui, frame| { + TitlePanel::ui(title_content, |ui, _| { View::title_button(ui, DOTS_THREE_OUTLINE_VERTICAL, || { //TODO: Show connections }); diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index 927d2b1..23550bb 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -19,8 +19,8 @@ use grin_servers::DiffBlock; use crate::gui::Colors; use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, NetworkContent, View}; -use crate::gui::views::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::{NetworkContent, View}; +use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::Node; /// Chain metrics tab content. @@ -36,7 +36,7 @@ impl NetworkTab for NetworkMetrics { NetworkTabType::Metrics } - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + fn ui(&mut self, ui: &mut egui::Ui, _: &mut eframe::Frame, _: &dyn PlatformCallbacks) { let server_stats = Node::get_stats(); // Show message to enable node when it's not running. if !Node::is_running() { @@ -145,8 +145,6 @@ impl NetworkTab for NetworkMetrics { }, ); } - - fn on_modal_ui(&mut self, _: &mut egui::Ui, _: &Modal, _: &dyn PlatformCallbacks) {} } const BLOCK_ITEM_HEIGHT: f32 = 77.0; diff --git a/src/gui/views/network/mining.rs b/src/gui/views/network/mining.rs index d719c82..4075be3 100644 --- a/src/gui/views/network/mining.rs +++ b/src/gui/views/network/mining.rs @@ -20,15 +20,23 @@ use grin_servers::WorkerStats; use crate::gui::Colors; use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_SIMPLE_MINUS, FOLDER_SIMPLE_PLUS, HARD_DRIVES, PLUGS, PLUGS_CONNECTED, POLYGON}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, NetworkContent, View}; +use crate::gui::views::{NetworkContent, View}; use crate::gui::views::network::setup::StratumSetup; -use crate::gui::views::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::{Node, NodeConfig}; /// Mining tab content. -#[derive(Default)] pub struct NetworkMining { - stratum_server_setup: StratumSetup + /// Stratum server setup content. + stratum_server_setup: StratumSetup, +} + +impl Default for NetworkMining { + fn default() -> Self { + Self { + stratum_server_setup: StratumSetup::default() + } + } } impl NetworkTab for NetworkMining { @@ -36,7 +44,7 @@ impl NetworkTab for NetworkMining { NetworkTabType::Mining } - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { let server_stats = Node::get_stats(); // Show message to enable node when it's not running. @@ -67,15 +75,14 @@ impl NetworkTab for NetworkMining { return; } - let stratum_stats = Node::get_stratum_stats(); - // Show stratum server setup when mining server is not running. + let stratum_stats = Node::get_stratum_stats(); if !stratum_stats.is_running { ScrollArea::vertical() .id_source("stratum_setup_scroll") .auto_shrink([false; 2]) .show(ui, |ui| { - self.stratum_server_setup.ui(ui, cb); + self.stratum_server_setup.ui(ui, frame, cb); }); return; } @@ -192,23 +199,9 @@ impl NetworkTab for NetworkMining { }); } } - - fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { - match modal.id { - StratumSetup::STRATUM_PORT_MODAL => { - self.stratum_server_setup.port_modal(ui, modal, cb); - }, - StratumSetup::ATTEMPT_TIME_MODAL => { - self.stratum_server_setup.attempt_modal(ui, modal, cb); - }, - StratumSetup::MIN_SHARE_DIFF_MODAL => { - self.stratum_server_setup.min_diff_modal(ui, modal, cb); - }, - _ => {} - } - } } +/// Height of Stratum server worker list item. const WORKER_ITEM_HEIGHT: f32 = 76.0; /// Draw worker statistics item. diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index 11c4a3c..273f6ec 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -18,8 +18,8 @@ use grin_servers::PeerStats; use crate::gui::Colors; use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, NetworkContent, View}; -use crate::gui::views::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::{NetworkContent, View}; +use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; use crate::node::Node; /// Integrated node tab content. @@ -31,7 +31,7 @@ impl NetworkTab for NetworkNode { NetworkTabType::Node } - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + fn ui(&mut self, ui: &mut egui::Ui, _: &mut eframe::Frame, _: &dyn PlatformCallbacks) { let server_stats = Node::get_stats(); // Show message to enable node when it's not running. if !Node::is_running() { @@ -175,8 +175,6 @@ impl NetworkTab for NetworkNode { } }); } - - fn on_modal_ui(&mut self, _: &mut egui::Ui, _: &Modal, _: &dyn PlatformCallbacks) {} } /// Draw connected peer info item. diff --git a/src/gui/views/network/settings.rs b/src/gui/views/network/settings.rs index 09fd713..846242b 100644 --- a/src/gui/views/network/settings.rs +++ b/src/gui/views/network/settings.rs @@ -17,19 +17,66 @@ use egui::{RichText, ScrollArea}; use crate::gui::Colors; use crate::gui::icons::ARROW_COUNTER_CLOCKWISE; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; -use crate::gui::views::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::network::types::{NetworkTab, NetworkTabType}; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::{Node, NodeConfig}; /// Integrated node settings tab content. -#[derive(Default)] pub struct NetworkSettings { + /// Integrated node general setup content. node: NodeSetup, + /// P2P server setup content. p2p: P2PSetup, + /// Stratum server setup content. stratum: StratumSetup, + /// Pool setup content. pool: PoolSetup, - dandelion: DandelionSetup + /// Dandelion server setup content. + dandelion: DandelionSetup, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str> +} + +/// Identifier for node restart confirmation [`Modal`]. +pub const NODE_RESTART_REQUIRED_MODAL: &'static str = "node_restart_required"; +/// Identifier for settings reset confirmation [`Modal`]. +pub const RESET_SETTINGS_MODAL: &'static str = "reset_settings"; + +impl Default for NetworkSettings { + fn default() -> Self { + Self { + node: NodeSetup::default(), + p2p: P2PSetup::default(), + stratum: StratumSetup::default(), + pool: PoolSetup::default(), + dandelion: DandelionSetup::default(), + modal_ids: vec![ + NODE_RESTART_REQUIRED_MODAL, + RESET_SETTINGS_MODAL + ] + } + } +} + +impl ModalContainer for NetworkSettings { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + _: &dyn PlatformCallbacks) { + match modal.id { + NODE_RESTART_REQUIRED_MODAL => node_restart_required_modal(ui, modal), + RESET_SETTINGS_MODAL => reset_settings_confirmation_modal(ui, modal), + _ => {} + } + } } impl NetworkTab for NetworkSettings { @@ -37,154 +84,56 @@ impl NetworkTab for NetworkSettings { NetworkTabType::Settings } - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); + ScrollArea::vertical() .id_source("network_settings") .auto_shrink([false; 2]) .show(ui, |ui| { - self.node.ui(ui, cb); + // Draw node setup section. + self.node.ui(ui, frame, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); - self.p2p.ui(ui, cb); + // Draw P2P server setup section. + self.p2p.ui(ui, frame, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); - self.stratum.ui(ui, cb); + // Draw Stratum server setup section. + self.stratum.ui(ui, frame, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); - self.pool.ui(ui, cb); + // Draw pool setup section. + self.pool.ui(ui, frame, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); - self.dandelion.ui(ui, cb); + // Draw Dandelion server setup section. + self.dandelion.ui(ui, frame, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(6.0); - self.reset_settings_ui(ui); + // Draw reset settings content. + reset_settings_ui(ui); }); } - - fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { - match modal.id { - // Settings modals. - Self::NODE_RESTART_REQUIRED_MODAL => self.node_restart_required_modal(ui, modal), - Self::RESET_SETTINGS_MODAL => self.reset_settings_confirmation_modal(ui, modal), - // Node server setup modals. - NodeSetup::API_PORT_MODAL => self.node.api_port_modal(ui, modal, cb), - NodeSetup::API_SECRET_MODAL => self.node.secret_modal(ui, modal, cb), - NodeSetup::FOREIGN_API_SECRET_MODAL => self.node.secret_modal(ui, modal, cb), - NodeSetup::FTL_MODAL => self.node.ftl_modal(ui, modal, cb), - // P2P setup modals. - P2PSetup::PORT_MODAL => self.p2p.port_modal(ui, modal, cb), - P2PSetup::CUSTOM_SEED_MODAL => self.p2p.peer_modal(ui, modal, cb), - P2PSetup::ALLOW_PEER_MODAL => self.p2p.peer_modal(ui, modal, cb), - P2PSetup::DENY_PEER_MODAL => self.p2p.peer_modal(ui, modal, cb), - P2PSetup::PREFER_PEER_MODAL => self.p2p.peer_modal(ui, modal, cb), - P2PSetup::BAN_WINDOW_MODAL => self.p2p.ban_window_modal(ui, modal, cb), - P2PSetup::MAX_INBOUND_MODAL => self.p2p.max_inbound_modal(ui, modal, cb), - P2PSetup::MAX_OUTBOUND_MODAL => self.p2p.max_outbound_modal(ui, modal, cb), - P2PSetup::MIN_OUTBOUND_MODAL => self.p2p.min_outbound_modal(ui, modal, cb), - // Stratum server setup modals. - StratumSetup::STRATUM_PORT_MODAL => self.stratum.port_modal(ui, modal, cb), - StratumSetup::ATTEMPT_TIME_MODAL => self.stratum.attempt_modal(ui, modal, cb), - StratumSetup::MIN_SHARE_DIFF_MODAL => self.stratum.min_diff_modal(ui, modal, cb), - // Pool setup modals. - PoolSetup::FEE_BASE_MODAL => self.pool.fee_base_modal(ui, modal, cb), - PoolSetup::REORG_PERIOD_MODAL => self.pool.reorg_period_modal(ui, modal, cb), - PoolSetup::POOL_SIZE_MODAL => self.pool.pool_size_modal(ui, modal, cb), - PoolSetup::STEMPOOL_SIZE_MODAL => self.pool.stempool_size_modal(ui, modal, cb), - PoolSetup::MAX_WEIGHT_MODAL => self.pool.max_weight_modal(ui, modal, cb), - // Dandelion setup modals. - DandelionSetup::EPOCH_MODAL => self.dandelion.epoch_modal(ui, modal, cb), - DandelionSetup::EMBARGO_MODAL => self.dandelion.embargo_modal(ui, modal, cb), - DandelionSetup::AGGREGATION_MODAL => self.dandelion.aggregation_modal(ui, modal, cb), - DandelionSetup::STEM_PROBABILITY_MODAL => self.dandelion.stem_prob_modal(ui, modal, cb), - _ => {} - } - } } impl NetworkSettings { - /// Identifier for node restart confirmation [`Modal`]. - pub const NODE_RESTART_REQUIRED_MODAL: &'static str = "node_restart_required"; - /// Identifier for settings reset confirmation [`Modal`]. - pub const RESET_SETTINGS_MODAL: &'static str = "reset_settings"; - - /// Draw button to reset integrated node settings to default values. - fn reset_settings_ui(&self, ui: &mut egui::Ui) { - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("network_settings.reset_settings_desc")) - .size(16.0) - .color(Colors::TEXT)); - ui.add_space(8.0); - let button_text = format!("{} {}", - ARROW_COUNTER_CLOCKWISE, - t!("network_settings.reset_settings")); - View::button(ui, button_text, Colors::GOLD, || { - // Show modal to confirm settings reset. - Modal::new(Self::RESET_SETTINGS_MODAL) - .position(ModalPosition::Center) - .title(t!("modal.confirmation")) - .show(); - }); - - // Show reminder to restart enabled node. - if Node::is_running() { - ui.add_space(12.0); - ui.label(RichText::new(t!("network_settings.restart_node_required")) - .size(16.0) - .color(Colors::GRAY) - ); - } - ui.add_space(12.0); - }); - - } - - /// Confirmation to reset settings to default values. - fn reset_settings_confirmation_modal(&self, ui: &mut egui::Ui, modal: &Modal) { - ui.add_space(6.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("network_settings.reset_settings_desc")) - .size(17.0) - .color(Colors::TEXT)); - ui.add_space(8.0); - }); - - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("network_settings.reset"), Colors::WHITE, || { - NodeConfig::reset_to_default(); - modal.close(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { - modal.close(); - }); - }); - }); - ui.add_space(6.0); - }); - } - /// Reminder to restart enabled node to show on edit setting at [`Modal`]. pub fn node_restart_required_ui(ui: &mut egui::Ui) { if Node::is_running() { @@ -200,45 +149,13 @@ impl NetworkSettings { pub fn show_node_restart_required_modal() { if Node::is_running() { // Show modal to apply changes by node restart. - Modal::new(Self::NODE_RESTART_REQUIRED_MODAL) + Modal::new(NODE_RESTART_REQUIRED_MODAL) .position(ModalPosition::Center) .title(t!("network.settings")) .show(); } } - /// Node restart reminder modal content. - fn node_restart_required_modal(&self, ui: &mut egui::Ui, modal: &Modal) { - ui.add_space(6.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("network_settings.restart_node_required")) - .size(17.0) - .color(Colors::GRAY)); - ui.add_space(8.0); - }); - - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); - - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("network_settings.restart"), Colors::WHITE, || { - Node::restart(); - modal.close(); - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { - modal.close(); - }); - }); - }); - ui.add_space(6.0); - }); - } - /// Draw IP addresses as radio buttons. pub fn ip_addrs_ui(ui: &mut egui::Ui, saved_ip: &String, @@ -288,4 +205,100 @@ impl NetworkSettings { ui.add_space(6.0); }); } +} + +/// Node restart reminder modal content. +fn node_restart_required_modal(ui: &mut egui::Ui, modal: &Modal) { + ui.add_space(6.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("network_settings.restart_node_required")) + .size(17.0) + .color(Colors::GRAY)); + ui.add_space(8.0); + }); + + // Show modal buttons. + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("network_settings.restart"), Colors::WHITE, || { + Node::restart(); + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + }); + ui.add_space(6.0); + }); +} + +/// Draw button to reset integrated node settings to default values. +fn reset_settings_ui(ui: &mut egui::Ui) { + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("network_settings.reset_settings_desc")) + .size(16.0) + .color(Colors::TEXT)); + ui.add_space(8.0); + let button_text = format!("{} {}", + ARROW_COUNTER_CLOCKWISE, + t!("network_settings.reset_settings")); + View::button(ui, button_text, Colors::GOLD, || { + // Show modal to confirm settings reset. + Modal::new(RESET_SETTINGS_MODAL) + .position(ModalPosition::Center) + .title(t!("modal.confirmation")) + .show(); + }); + + // Show reminder to restart enabled node. + if Node::is_running() { + ui.add_space(12.0); + ui.label(RichText::new(t!("network_settings.restart_node_required")) + .size(16.0) + .color(Colors::GRAY) + ); + } + ui.add_space(12.0); + }); + +} + +/// Confirmation to reset settings to default values. +fn reset_settings_confirmation_modal(ui: &mut egui::Ui, modal: &Modal) { + ui.add_space(6.0); + ui.vertical_centered(|ui| { + let reset_text = format!("{}?", t!("network_settings.reset_settings_desc")); + ui.label(RichText::new(reset_text) + .size(17.0) + .color(Colors::TEXT)); + ui.add_space(8.0); + }); + + // Show modal buttons. + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("network_settings.reset"), Colors::WHITE, || { + NodeConfig::reset_to_default(); + modal.close(); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); + }); + }); + }); + ui.add_space(6.0); + }); } \ No newline at end of file diff --git a/src/gui/views/network/setup/dandelion.rs b/src/gui/views/network/setup/dandelion.rs index cbf2e54..3da6257 100644 --- a/src/gui/views/network/setup/dandelion.rs +++ b/src/gui/views/network/setup/dandelion.rs @@ -12,16 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Id, RichText, TextStyle, Ui, Widget}; +use egui::{Id, RichText, TextStyle, Widget}; use crate::gui::Colors; use crate::gui::icons::{CLOCK_COUNTDOWN, GRAPH, TIMER, WATCH}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; use crate::gui::views::network::settings::NetworkSettings; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::NodeConfig; -/// Dandelion setup ui section. +/// Dandelion server setup section content. pub struct DandelionSetup { /// Epoch duration value in seconds. epoch_edit: String, @@ -34,30 +35,62 @@ pub struct DandelionSetup { /// Stem phase probability value (stem 90% of the time, fluff 10% of the time by default). stem_prob_edit: String, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str>, } +/// Identifier epoch duration value [`Modal`]. +pub const EPOCH_MODAL: &'static str = "epoch_secs"; +/// Identifier for embargo expiration time value [`Modal`]. +pub const EMBARGO_MODAL: &'static str = "embargo_secs"; +/// Identifier for aggregation period value [`Modal`]. +pub const AGGREGATION_MODAL: &'static str = "aggregation_secs"; +/// Identifier for Stem phase probability value [`Modal`]. +pub const STEM_PROBABILITY_MODAL: &'static str = "stem_probability"; + impl Default for DandelionSetup { fn default() -> Self { Self { epoch_edit: NodeConfig::get_dandelion_epoch(), embargo_edit: NodeConfig::get_reorg_cache_period(), aggregation_edit: NodeConfig::get_dandelion_aggregation(), - stem_prob_edit: NodeConfig::get_stem_probability() + stem_prob_edit: NodeConfig::get_stem_probability(), + modal_ids: vec![ + EPOCH_MODAL, + EMBARGO_MODAL, + AGGREGATION_MODAL, + STEM_PROBABILITY_MODAL + ] + } + } +} + +impl ModalContainer for DandelionSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + EPOCH_MODAL => self.epoch_modal(ui, modal, cb), + EMBARGO_MODAL => self.embargo_modal(ui, modal, cb), + AGGREGATION_MODAL => self.aggregation_modal(ui, modal, cb), + STEM_PROBABILITY_MODAL => self.stem_prob_modal(ui, modal, cb), + _ => {} } } } impl DandelionSetup { - /// Identifier epoch duration value [`Modal`]. - pub const EPOCH_MODAL: &'static str = "epoch_secs"; - /// Identifier for embargo expiration time value [`Modal`]. - pub const EMBARGO_MODAL: &'static str = "embargo_secs"; - /// Identifier for aggregation period value [`Modal`]. - pub const AGGREGATION_MODAL: &'static str = "aggregation_secs"; - /// Identifier for Stem phase probability value [`Modal`]. - pub const STEM_PROBABILITY_MODAL: &'static str = "stem_probability"; + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); - pub fn ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", GRAPH, "Dandelion")); View::horizontal_line(ui, Colors::STROKE); ui.add_space(6.0); @@ -101,7 +134,7 @@ impl DandelionSetup { } /// Draw epoch duration setup content. - fn epoch_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn epoch_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.epoch_duration")) .size(16.0) .color(Colors::GRAY) @@ -113,7 +146,7 @@ impl DandelionSetup { // Setup values for modal. self.epoch_edit = epoch; // Show epoch setup modal. - Modal::new(Self::EPOCH_MODAL) + Modal::new(EPOCH_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -123,7 +156,7 @@ impl DandelionSetup { } /// Draw epoch duration [`Modal`] content. - pub fn epoch_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn epoch_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.epoch_duration")) @@ -186,7 +219,7 @@ impl DandelionSetup { } /// Draw embargo expiration time setup content. - fn embargo_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn embargo_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.embargo_timer")) .size(16.0) .color(Colors::GRAY) @@ -198,16 +231,17 @@ impl DandelionSetup { // Setup values for modal. self.embargo_edit = embargo; // Show embargo setup modal. - Modal::new(Self::EMBARGO_MODAL) + Modal::new(EMBARGO_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); + cb.show_keyboard(); }); ui.add_space(6.0); } /// Draw epoch duration [`Modal`] content. - pub fn embargo_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn embargo_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.embargo_timer")) @@ -270,7 +304,7 @@ impl DandelionSetup { } /// Draw aggregation period setup content. - fn aggregation_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn aggregation_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.aggregation_period")) .size(16.0) .color(Colors::GRAY) @@ -282,7 +316,7 @@ impl DandelionSetup { // Setup values for modal. self.aggregation_edit = agg; // Show aggregation setup modal. - Modal::new(Self::AGGREGATION_MODAL) + Modal::new(AGGREGATION_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -292,7 +326,7 @@ impl DandelionSetup { } /// Draw aggregation period [`Modal`] content. - pub fn aggregation_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn aggregation_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.aggregation_period")) @@ -355,7 +389,7 @@ impl DandelionSetup { } /// Draw stem phase probability setup content. - fn stem_prob_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn stem_prob_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.stem_probability")) .size(16.0) .color(Colors::GRAY) @@ -367,7 +401,7 @@ impl DandelionSetup { // Setup values for modal. self.stem_prob_edit = stem_prob; // Show stem probability setup modal. - Modal::new(Self::STEM_PROBABILITY_MODAL) + Modal::new(STEM_PROBABILITY_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -377,7 +411,7 @@ impl DandelionSetup { } /// Draw stem phase probability [`Modal`] content. - pub fn stem_prob_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn stem_prob_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.stem_probability")) diff --git a/src/gui/views/network/setup/node.rs b/src/gui/views/network/setup/node.rs index 8b7211f..b36fe82 100644 --- a/src/gui/views/network/setup/node.rs +++ b/src/gui/views/network/setup/node.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Id, RichText, TextStyle, Ui, Widget}; +use egui::{Id, RichText, TextStyle, Widget}; use egui_extras::{Size, StripBuilder}; use grin_core::global::ChainTypes; @@ -20,11 +20,12 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{CLIPBOARD_TEXT, CLOCK_CLOCKWISE, COMPUTER_TOWER, COPY, PLUG, POWER, SHIELD, SHIELD_SLASH}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, NetworkContent, View}; +use crate::gui::views::{Modal, NetworkContent, View}; use crate::gui::views::network::settings::NetworkSettings; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::{Node, NodeConfig}; -/// Integrated node server setup ui section. +/// Integrated node general setup section content. pub struct NodeSetup { /// IP Addresses available at system. available_ips: Vec, @@ -42,8 +43,20 @@ pub struct NodeSetup { /// Future Time Limit value. ftl_edit: String, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str> } +/// Identifier for API port value [`Modal`]. +pub const API_PORT_MODAL: &'static str = "api_port"; +/// Identifier for API secret value [`Modal`]. +pub const API_SECRET_MODAL: &'static str = "api_secret"; +/// Identifier for Foreign API secret value [`Modal`]. +pub const FOREIGN_API_SECRET_MODAL: &'static str = "foreign_api_secret"; +/// Identifier for FTL value [`Modal`]. +pub const FTL_MODAL: &'static str = "ftl"; + impl Default for NodeSetup { fn default() -> Self { let (api_ip, api_port) = NodeConfig::get_api_ip_port(); @@ -55,17 +68,41 @@ impl Default for NodeSetup { is_api_port_available, secret_edit: "".to_string(), ftl_edit: NodeConfig::get_ftl(), + modal_ids: vec![ + API_PORT_MODAL, + API_SECRET_MODAL, + FOREIGN_API_SECRET_MODAL, + FTL_MODAL + ] + } + } +} + +impl ModalContainer for NodeSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + API_PORT_MODAL => self.api_port_modal(ui, modal, cb), + API_SECRET_MODAL => self.secret_modal(ui, modal, cb), + FOREIGN_API_SECRET_MODAL => self.secret_modal(ui, modal, cb), + FTL_MODAL => self.ftl_modal(ui, modal, cb), + _ => {} } } } impl NodeSetup { - pub const API_PORT_MODAL: &'static str = "api_port"; - pub const API_SECRET_MODAL: &'static str = "api_secret"; - pub const FOREIGN_API_SECRET_MODAL: &'static str = "foreign_api_secret"; - pub const FTL_MODAL: &'static str = "ftl"; + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); - pub fn ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server"))); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); @@ -150,10 +187,10 @@ impl NodeSetup { // Show API port setup. self.api_port_setup_ui(ui, cb); // Show API secret setup. - self.secret_ui(Self::API_SECRET_MODAL, ui, cb); + self.secret_ui(API_SECRET_MODAL, ui, cb); ui.add_space(12.0); // Show Foreign API secret setup. - self.secret_ui(Self::FOREIGN_API_SECRET_MODAL, ui, cb); + self.secret_ui(FOREIGN_API_SECRET_MODAL, ui, cb); ui.add_space(6.0); }); } @@ -183,7 +220,7 @@ impl NodeSetup { } /// Draw [`ChainTypes`] setup content. - fn chain_type_ui(&mut self, ui: &mut Ui) { + fn chain_type_ui(&mut self, ui: &mut egui::Ui) { let saved_chain_type = AppConfig::chain_type(); let mut selected_chain_type = saved_chain_type; @@ -207,7 +244,7 @@ impl NodeSetup { } /// Draw API port setup content. - fn api_port_setup_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn api_port_setup_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.api_port")) .size(16.0) .color(Colors::GRAY) @@ -221,7 +258,7 @@ impl NodeSetup { self.api_port_available_edit = self.is_api_port_available; // Show API port modal. - Modal::new(Self::API_PORT_MODAL) + Modal::new(API_PORT_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -240,7 +277,7 @@ impl NodeSetup { } /// Draw API port [`Modal`] content. - pub fn api_port_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn api_port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.api_port")) @@ -309,9 +346,9 @@ impl NodeSetup { } /// Draw API secret token setup content. - fn secret_ui(&mut self, modal_id: &'static str, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn secret_ui(&mut self, modal_id: &'static str, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let secret_title = match modal_id { - Self::API_SECRET_MODAL => t!("network_settings.api_secret"), + API_SECRET_MODAL => t!("network_settings.api_secret"), _ => t!("network_settings.foreign_api_secret") }; ui.label(RichText::new(secret_title) @@ -321,7 +358,7 @@ impl NodeSetup { ui.add_space(6.0); let secret_value = match modal_id { - Self::API_SECRET_MODAL => NodeConfig::get_api_secret(), + API_SECRET_MODAL => NodeConfig::get_api_secret(), _ => NodeConfig::get_foreign_api_secret() }; @@ -344,11 +381,11 @@ impl NodeSetup { } /// Draw API secret token [`Modal`] content. - pub fn secret_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn secret_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { let description = match modal.id { - Self::API_SECRET_MODAL => t!("network_settings.api_secret"), + API_SECRET_MODAL => t!("network_settings.api_secret"), _ => t!("network_settings.foreign_api_secret") }; ui.label(RichText::new(description).size(17.0).color(Colors::GRAY)); @@ -417,7 +454,7 @@ impl NodeSetup { let on_save = || { let secret = self.secret_edit.clone(); match modal.id { - Self::API_SECRET_MODAL => { + API_SECRET_MODAL => { NodeConfig::save_api_secret(&secret); } _ => { @@ -444,7 +481,7 @@ impl NodeSetup { } /// Draw FTL setup content. - fn ftl_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn ftl_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.ftl")) .size(16.0) .color(Colors::GRAY) @@ -456,7 +493,7 @@ impl NodeSetup { // Setup values for modal. self.ftl_edit = ftl; // Show ftl value setup modal. - Modal::new(Self::FTL_MODAL) + Modal::new(FTL_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -470,7 +507,7 @@ impl NodeSetup { } /// Draw FTL [`Modal`] content. - pub fn ftl_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn ftl_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.ftl")) @@ -533,7 +570,7 @@ impl NodeSetup { } /// Draw chain validation mode setup content. - pub fn validation_mode_ui(&mut self, ui: &mut Ui) { + pub fn validation_mode_ui(&mut self, ui: &mut egui::Ui) { let validate = NodeConfig::is_full_chain_validation(); View::checkbox(ui, validate, t!("network_settings.full_validation"), || { NodeConfig::toggle_full_chain_validation(); @@ -547,7 +584,7 @@ impl NodeSetup { } /// Draw archive mode setup content. - pub fn archive_mode_ui(&mut self, ui: &mut Ui) { + fn archive_mode_ui(&mut self, ui: &mut egui::Ui) { let archive_mode = NodeConfig::is_archive_mode(); View::checkbox(ui, archive_mode, t!("network_settings.archive_mode"), || { NodeConfig::toggle_archive_mode(); diff --git a/src/gui/views/network/setup/p2p.rs b/src/gui/views/network/setup/p2p.rs index e1d639a..ac119f5 100644 --- a/src/gui/views/network/setup/p2p.rs +++ b/src/gui/views/network/setup/p2p.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Align, Id, Layout, RichText, Rounding, TextStyle, Ui, Widget}; +use egui::{Align, Id, Layout, RichText, Rounding, TextStyle, Widget}; use egui_extras::{Size, StripBuilder}; use grin_core::global::ChainTypes; @@ -20,8 +20,9 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{ARROW_FAT_LINE_UP, ARROW_FAT_LINES_DOWN, ARROW_FAT_LINES_UP, CLIPBOARD_TEXT, GLOBE_SIMPLE, HANDSHAKE, PLUG, PLUS_CIRCLE, PROHIBIT_INSET, TRASH}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; use crate::gui::views::network::settings::NetworkSettings; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::{NodeConfig, PeersConfig}; /// Type of peer. @@ -34,7 +35,7 @@ enum PeerType { Preferred } -/// P2P server setup ui section. +/// P2P server setup section content. pub struct P2PSetup { /// P2P port value. port_edit: String, @@ -64,9 +65,31 @@ pub struct P2PSetup { max_outbound_count: String, /// Preferred minimum number of outbound peers. - min_outbound_count: String + min_outbound_count: String, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str> } +/// Identifier for port value [`Modal`]. +pub const PORT_MODAL: &'static str = "p2p_port"; +/// Identifier for custom seed [`Modal`]. +pub const CUSTOM_SEED_MODAL: &'static str = "p2p_custom_seed"; +/// Identifier for allowed peer [`Modal`]. +pub const ALLOW_PEER_MODAL: &'static str = "p2p_allow_peer"; +/// Identifier for denied peer [`Modal`]. +pub const DENY_PEER_MODAL: &'static str = "p2p_deny_peer"; +/// Identifier for preferred peer [`Modal`]. +pub const PREFER_PEER_MODAL: &'static str = "p2p_prefer_peer"; +/// Identifier for ban window [`Modal`]. +pub const BAN_WINDOW_MODAL: &'static str = "p2p_ban_window"; +/// Identifier for maximum number of inbound peers [`Modal`]. +pub const MAX_INBOUND_MODAL: &'static str = "p2p_max_inbound"; +/// Identifier for maximum number of outbound peers [`Modal`]. +pub const MAX_OUTBOUND_MODAL: &'static str = "p2p_max_outbound"; +/// Identifier for minimum number of outbound peers [`Modal`]. +pub const MIN_OUTBOUND_MODAL: &'static str = "p2p_min_outbound"; + impl Default for P2PSetup { fn default() -> Self { let port = NodeConfig::get_p2p_port(); @@ -91,34 +114,54 @@ impl Default for P2PSetup { max_inbound_count: NodeConfig::get_max_inbound_peers(), max_outbound_count: NodeConfig::get_max_outbound_peers(), min_outbound_count: NodeConfig::get_min_outbound_peers(), + modal_ids: vec![ + PORT_MODAL, + CUSTOM_SEED_MODAL, + ALLOW_PEER_MODAL, + DENY_PEER_MODAL, + PREFER_PEER_MODAL, + BAN_WINDOW_MODAL, + MAX_INBOUND_MODAL, + MAX_OUTBOUND_MODAL, + MIN_OUTBOUND_MODAL + ] + } + } +} + +impl ModalContainer for P2PSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + PORT_MODAL => self.port_modal(ui, modal, cb), + CUSTOM_SEED_MODAL => self.peer_modal(ui, modal, cb), + ALLOW_PEER_MODAL => self.peer_modal(ui, modal, cb), + DENY_PEER_MODAL => self.peer_modal(ui, modal, cb), + PREFER_PEER_MODAL => self.peer_modal(ui, modal, cb), + BAN_WINDOW_MODAL => self.ban_window_modal(ui, modal, cb), + MAX_INBOUND_MODAL => self.max_inbound_modal(ui, modal, cb), + MAX_OUTBOUND_MODAL => self.max_outbound_modal(ui, modal, cb), + MIN_OUTBOUND_MODAL => self.min_outbound_modal(ui, modal, cb), + _ => {} } } } impl P2PSetup { - /// Identifier for port value [`Modal`]. - pub const PORT_MODAL: &'static str = "p2p_port"; - /// Identifier for custom seed [`Modal`]. - pub const CUSTOM_SEED_MODAL: &'static str = "p2p_custom_seed"; - /// Identifier for allowed peer [`Modal`]. - pub const ALLOW_PEER_MODAL: &'static str = "p2p_allow_peer"; - /// Identifier for denied peer [`Modal`]. - pub const DENY_PEER_MODAL: &'static str = "p2p_deny_peer"; - /// Identifier for preferred peer [`Modal`]. - pub const PREFER_PEER_MODAL: &'static str = "p2p_prefer_peer"; - /// Identifier for ban window [`Modal`]. - pub const BAN_WINDOW_MODAL: &'static str = "p2p_ban_window"; - /// Identifier for maximum number of inbound peers [`Modal`]. - pub const MAX_INBOUND_MODAL: &'static str = "p2p_max_inbound"; - /// Identifier for maximum number of outbound peers [`Modal`]. - pub const MAX_OUTBOUND_MODAL: &'static str = "p2p_max_outbound"; - /// Identifier for minimum number of outbound peers [`Modal`]. - pub const MIN_OUTBOUND_MODAL: &'static str = "p2p_min_outbound"; - /// Title for custom DNS Seeds setup section. const DNS_SEEDS_TITLE: &'static str = "DNS Seeds"; - pub fn ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); + View::sub_title(ui, format!("{} {}", HANDSHAKE, t!("network_settings.p2p_server"))); View::horizontal_line(ui, Colors::STROKE); ui.add_space(6.0); @@ -199,7 +242,7 @@ impl P2PSetup { } /// Draw p2p port setup content. - fn port_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn port_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.p2p_port")) .size(16.0) .color(Colors::GRAY) @@ -212,7 +255,7 @@ impl P2PSetup { self.port_edit = port; self.port_available_edit = self.is_port_available; // Show p2p port modal. - Modal::new(Self::PORT_MODAL) + Modal::new(PORT_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -231,7 +274,7 @@ impl P2PSetup { } /// Draw p2p port [`Modal`] content. - pub fn port_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.p2p_port")) @@ -300,7 +343,7 @@ impl P2PSetup { } /// Draw peer list content based on provided [`PeerType`]. - fn peer_list_ui(&mut self, ui: &mut Ui, peer_type: &PeerType, cb: &dyn PlatformCallbacks) { + fn peer_list_ui(&mut self, ui: &mut egui::Ui, peer_type: &PeerType, cb: &dyn PlatformCallbacks) { let peers = match peer_type { PeerType::DefaultSeed => { if AppConfig::chain_type() == ChainTypes::Testnet { @@ -317,7 +360,7 @@ impl P2PSetup { for (index, peer) in peers.iter().enumerate() { ui.horizontal_wrapped(|ui| { // Draw peer list item. - Self::peer_item_ui(ui, peer, peer_type, View::item_rounding(index, peers.len())); + peer_item_ui(ui, peer, peer_type, View::item_rounding(index, peers.len())); }); } @@ -349,10 +392,10 @@ impl P2PSetup { self.peer_edit = "".to_string(); // Select modal id. let modal_id = match peer_type { - PeerType::Allowed => Self::ALLOW_PEER_MODAL, - PeerType::Denied => Self::DENY_PEER_MODAL, - PeerType::Preferred => Self::PREFER_PEER_MODAL, - _ => Self::CUSTOM_SEED_MODAL + PeerType::Allowed => ALLOW_PEER_MODAL, + PeerType::Denied => DENY_PEER_MODAL, + PeerType::Preferred => PREFER_PEER_MODAL, + _ => CUSTOM_SEED_MODAL }; // Select modal title. let modal_title = match peer_type { @@ -373,11 +416,11 @@ impl P2PSetup { } /// Draw peer creation [`Modal`] content. - pub fn peer_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn peer_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { let label_text = match modal.id { - Self::CUSTOM_SEED_MODAL => t!("network_settings.seed_address"), + CUSTOM_SEED_MODAL => t!("network_settings.seed_address"), &_ => t!("network_settings.peer_address") }; ui.label(RichText::new(label_text).size(17.0).color(Colors::GRAY)); @@ -442,10 +485,10 @@ impl P2PSetup { // Save peer at config. if is_correct_address { match modal.id { - Self::CUSTOM_SEED_MODAL => NodeConfig::save_custom_seed(peer), - Self::ALLOW_PEER_MODAL => NodeConfig::allow_peer(peer), - Self::DENY_PEER_MODAL => NodeConfig::deny_peer(peer), - Self::PREFER_PEER_MODAL => NodeConfig::prefer_peer(peer), + CUSTOM_SEED_MODAL => NodeConfig::save_custom_seed(peer), + ALLOW_PEER_MODAL => NodeConfig::allow_peer(peer), + DENY_PEER_MODAL => NodeConfig::deny_peer(peer), + PREFER_PEER_MODAL => NodeConfig::prefer_peer(peer), &_ => {} } @@ -472,55 +515,8 @@ impl P2PSetup { }); } - /// Draw peer list item. - fn peer_item_ui(ui: &mut Ui, peer_addr: &String, peer_type: &PeerType, rounding: Rounding) { - // Setup layout size. - let mut rect = ui.available_rect_before_wrap(); - rect.set_height(42.0); - - // Draw round background. - let mut bg_rect = rect.clone(); - bg_rect.min += egui::emath::vec2(6.0, 0.0); - ui.painter().rect(bg_rect, rounding, Colors::WHITE, View::ITEM_STROKE); - - ui.vertical(|ui| { - ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { - // Draw delete button for non-default seed peers. - if peer_type != &PeerType::DefaultSeed { - View::item_button(ui, [false, true], TRASH, || { - match peer_type { - PeerType::CustomSeed => { - NodeConfig::remove_custom_seed(peer_addr); - } - PeerType::Allowed => { - NodeConfig::remove_allowed_peer(peer_addr); - } - PeerType::Denied => { - NodeConfig::remove_denied_peer(peer_addr); - } - PeerType::Preferred => { - NodeConfig::remove_preferred_peer(peer_addr); - } - PeerType::DefaultSeed => {} - } - }); - } - - let layout_size = ui.available_size(); - ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { - ui.add_space(12.0); - // Draw peer address. - let peer_text = format!("{} {}", GLOBE_SIMPLE, &peer_addr); - ui.label(RichText::new(peer_text) - .color(Colors::TEXT_BUTTON) - .size(16.0)); - }); - }); - }); - } - /// Draw seeding type setup content. - fn seeding_type_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn seeding_type_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let title = Self::DNS_SEEDS_TITLE; ui.label(RichText::new(title).size(16.0).color(Colors::GRAY)); ui.add_space(2.0); @@ -540,7 +536,7 @@ impl P2PSetup { } /// Draw ban window setup content. - fn ban_window_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn ban_window_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.ban_window")) .size(16.0) .color(Colors::GRAY) @@ -552,7 +548,7 @@ impl P2PSetup { // Setup values for modal. self.ban_window_edit = ban_window; // Show ban window period setup modal. - Modal::new(Self::BAN_WINDOW_MODAL) + Modal::new(BAN_WINDOW_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -567,7 +563,7 @@ impl P2PSetup { } /// Draw ban window [`Modal`] content. - pub fn ban_window_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn ban_window_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.ban_window")) @@ -630,7 +626,7 @@ impl P2PSetup { } /// Draw maximum number of inbound peers setup content. - fn max_inbound_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn max_inbound_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.max_inbound_count")) .size(16.0) .color(Colors::GRAY) @@ -643,7 +639,7 @@ impl P2PSetup { // Setup values for modal. self.max_inbound_count = max_inbound; // Show maximum number of inbound peers setup modal. - Modal::new(Self::MAX_INBOUND_MODAL) + Modal::new(MAX_INBOUND_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -653,7 +649,7 @@ impl P2PSetup { } /// Draw maximum number of inbound peers [`Modal`] content. - pub fn max_inbound_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn max_inbound_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.max_inbound_count")) @@ -716,7 +712,7 @@ impl P2PSetup { } /// Draw maximum number of outbound peers setup content. - fn max_outbound_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn max_outbound_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.max_outbound_count")) .size(16.0) .color(Colors::GRAY) @@ -729,7 +725,7 @@ impl P2PSetup { // Setup values for modal. self.max_outbound_count = max_outbound; // Show maximum number of outbound peers setup modal. - Modal::new(Self::MAX_OUTBOUND_MODAL) + Modal::new(MAX_OUTBOUND_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -739,7 +735,7 @@ impl P2PSetup { } /// Draw maximum number of outbound peers [`Modal`] content. - pub fn max_outbound_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn max_outbound_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.max_outbound_count")) @@ -802,7 +798,7 @@ impl P2PSetup { } /// Draw minimum number of outbound peers setup content. - fn min_outbound_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn min_outbound_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.min_outbound_count")) .size(16.0) .color(Colors::GRAY) @@ -815,7 +811,7 @@ impl P2PSetup { // Setup values for modal. self.min_outbound_count = min_outbound; // Show maximum number of outbound peers setup modal. - Modal::new(Self::MIN_OUTBOUND_MODAL) + Modal::new(MIN_OUTBOUND_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -829,7 +825,7 @@ impl P2PSetup { } /// Draw minimum number of outbound peers [`Modal`] content. - pub fn min_outbound_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn min_outbound_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.min_outbound_count")) @@ -890,4 +886,51 @@ impl P2PSetup { ui.add_space(6.0); }); } +} + +/// Draw peer list item. +fn peer_item_ui(ui: &mut egui::Ui, peer_addr: &String, peer_type: &PeerType, rounding: Rounding) { + // Setup layout size. + let mut rect = ui.available_rect_before_wrap(); + rect.set_height(42.0); + + // Draw round background. + let mut bg_rect = rect.clone(); + bg_rect.min += egui::emath::vec2(6.0, 0.0); + ui.painter().rect(bg_rect, rounding, Colors::WHITE, View::ITEM_STROKE); + + ui.vertical(|ui| { + ui.allocate_ui_with_layout(rect.size(), Layout::right_to_left(Align::Center), |ui| { + // Draw delete button for non-default seed peers. + if peer_type != &PeerType::DefaultSeed { + View::item_button(ui, [false, true], TRASH, || { + match peer_type { + PeerType::CustomSeed => { + NodeConfig::remove_custom_seed(peer_addr); + } + PeerType::Allowed => { + NodeConfig::remove_allowed_peer(peer_addr); + } + PeerType::Denied => { + NodeConfig::remove_denied_peer(peer_addr); + } + PeerType::Preferred => { + NodeConfig::remove_preferred_peer(peer_addr); + } + PeerType::DefaultSeed => {} + } + }); + } + + let layout_size = ui.available_size(); + ui.allocate_ui_with_layout(layout_size, Layout::left_to_right(Align::Center), |ui| { + ui.add_space(12.0); + // Draw peer address. + let peer_text = format!("{} {}", GLOBE_SIMPLE, &peer_addr); + ui.label(RichText::new(peer_text) + .color(Colors::TEXT_BUTTON) + .size(16.0)); + }); + }); + }); } \ No newline at end of file diff --git a/src/gui/views/network/setup/pool.rs b/src/gui/views/network/setup/pool.rs index 21b7577..6491fec 100644 --- a/src/gui/views/network/setup/pool.rs +++ b/src/gui/views/network/setup/pool.rs @@ -12,16 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Id, RichText, TextStyle, Ui, Widget}; +use egui::{Id, RichText, TextStyle, Widget}; use crate::gui::Colors; use crate::gui::icons::{BEZIER_CURVE, BOUNDING_BOX, CHART_SCATTER, CIRCLES_THREE, CLOCK_COUNTDOWN, HAND_COINS}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; use crate::gui::views::network::settings::NetworkSettings; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::NodeConfig; -/// Memory pool setup ui section. +/// Memory pool setup section content. pub struct PoolSetup { /// Base fee value that's accepted into the pool. fee_base_edit: String, @@ -37,8 +38,22 @@ pub struct PoolSetup { /// Maximum total weight of transactions to build a block. max_weight_edit: String, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str>, } +/// Identifier for base fee value [`Modal`]. +pub const FEE_BASE_MODAL: &'static str = "fee_base"; +/// Identifier for reorg cache retention period value [`Modal`]. +pub const REORG_PERIOD_MODAL: &'static str = "reorg_period"; +/// Identifier for maximum number of transactions in the pool [`Modal`]. +pub const POOL_SIZE_MODAL: &'static str = "pool_size"; +/// Identifier for maximum number of transactions in the stempool [`Modal`]. +pub const STEMPOOL_SIZE_MODAL: &'static str = "stempool_size"; +/// Identifier for maximum total weight of transactions [`Modal`]. +pub const MAX_WEIGHT_MODAL: &'static str = "max_weight"; + impl Default for PoolSetup { fn default() -> Self { Self { @@ -47,23 +62,43 @@ impl Default for PoolSetup { pool_size_edit: NodeConfig::get_max_pool_size(), stempool_size_edit: NodeConfig::get_max_stempool_size(), max_weight_edit: NodeConfig::get_mineable_max_weight(), + modal_ids: vec![ + FEE_BASE_MODAL, + REORG_PERIOD_MODAL, + POOL_SIZE_MODAL, + STEMPOOL_SIZE_MODAL, + MAX_WEIGHT_MODAL + ] + } + } +} + +impl ModalContainer for PoolSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + FEE_BASE_MODAL => self.fee_base_modal(ui, modal, cb), + REORG_PERIOD_MODAL => self.reorg_period_modal(ui, modal, cb), + POOL_SIZE_MODAL => self.pool_size_modal(ui, modal, cb), + STEMPOOL_SIZE_MODAL => self.stem_size_modal(ui, modal, cb), + MAX_WEIGHT_MODAL => self.max_weight_modal(ui, modal, cb), + _ => {} } } } impl PoolSetup { - /// Identifier for base fee value [`Modal`]. - pub const FEE_BASE_MODAL: &'static str = "fee_base"; - /// Identifier for reorg cache retention period value [`Modal`]. - pub const REORG_PERIOD_MODAL: &'static str = "reorg_period"; - /// Identifier for maximum number of transactions in the pool [`Modal`]. - pub const POOL_SIZE_MODAL: &'static str = "pool_size"; - /// Identifier for maximum number of transactions in the stempool [`Modal`]. - pub const STEMPOOL_SIZE_MODAL: &'static str = "stempool_size"; - /// Identifier for maximum total weight of transactions [`Modal`]. - pub const MAX_WEIGHT_MODAL: &'static str = "max_weight"; + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); - pub fn ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", CHART_SCATTER, t!("network_settings.tx_pool"))); View::horizontal_line(ui, Colors::STROKE); ui.add_space(6.0); @@ -91,7 +126,7 @@ impl PoolSetup { ui.add_space(6.0); // Show stem pool size setup. - self.stempool_size_ui(ui, cb); + self.stem_size_ui(ui, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::ITEM_STROKE); @@ -103,7 +138,7 @@ impl PoolSetup { } /// Draw fee base setup content. - fn fee_base_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn fee_base_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.pool_fee")) .size(16.0) .color(Colors::GRAY) @@ -115,7 +150,7 @@ impl PoolSetup { // Setup values for modal. self.fee_base_edit = fee; // Show fee setup modal. - Modal::new(Self::FEE_BASE_MODAL) + Modal::new(FEE_BASE_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -125,7 +160,7 @@ impl PoolSetup { } /// Draw fee base [`Modal`] content. - pub fn fee_base_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn fee_base_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.pool_fee")) @@ -188,7 +223,7 @@ impl PoolSetup { } /// Draw reorg cache retention period setup content. - fn reorg_period_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn reorg_period_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.reorg_period")) .size(16.0) .color(Colors::GRAY) @@ -200,7 +235,7 @@ impl PoolSetup { // Setup values for modal. self.reorg_period_edit = period; // Show reorg period setup modal. - Modal::new(Self::REORG_PERIOD_MODAL) + Modal::new(REORG_PERIOD_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -210,7 +245,7 @@ impl PoolSetup { } /// Draw reorg cache retention period [`Modal`] content. - pub fn reorg_period_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn reorg_period_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.reorg_period")) @@ -273,7 +308,7 @@ impl PoolSetup { } /// Draw maximum number of transactions in the pool setup content. - fn pool_size_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn pool_size_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.max_tx_pool")) .size(16.0) .color(Colors::GRAY) @@ -285,7 +320,7 @@ impl PoolSetup { // Setup values for modal. self.pool_size_edit = size; // Show pool size setup modal. - Modal::new(Self::POOL_SIZE_MODAL) + Modal::new(POOL_SIZE_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -295,7 +330,7 @@ impl PoolSetup { } /// Draw maximum number of transactions in the pool [`Modal`] content. - pub fn pool_size_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn pool_size_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.max_tx_pool")) @@ -358,7 +393,7 @@ impl PoolSetup { } /// Draw maximum number of transactions in the stempool setup content. - fn stempool_size_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn stem_size_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.max_tx_stempool")) .size(16.0) .color(Colors::GRAY) @@ -370,7 +405,7 @@ impl PoolSetup { // Setup values for modal. self.stempool_size_edit = size; // Show stempool size setup modal. - Modal::new(Self::STEMPOOL_SIZE_MODAL) + Modal::new(STEMPOOL_SIZE_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -380,7 +415,7 @@ impl PoolSetup { } /// Draw maximum number of transactions in the stempool [`Modal`] content. - pub fn stempool_size_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn stem_size_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.max_tx_stempool")) @@ -443,7 +478,7 @@ impl PoolSetup { } /// Draw maximum total weight of transactions setup content. - fn max_weight_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn max_weight_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.max_tx_weight")) .size(16.0) .color(Colors::GRAY) @@ -455,7 +490,7 @@ impl PoolSetup { // Setup values for modal. self.max_weight_edit = weight; // Show total tx weight setup modal. - Modal::new(Self::MAX_WEIGHT_MODAL) + Modal::new(MAX_WEIGHT_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -465,7 +500,7 @@ impl PoolSetup { } /// Draw maximum total weight of transactions [`Modal`] content. - pub fn max_weight_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn max_weight_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.max_tx_weight")) diff --git a/src/gui/views/network/setup/stratum.rs b/src/gui/views/network/setup/stratum.rs index 0163af7..51ae461 100644 --- a/src/gui/views/network/setup/stratum.rs +++ b/src/gui/views/network/setup/stratum.rs @@ -12,16 +12,17 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Id, RichText, TextStyle, Ui, Widget}; +use egui::{Id, RichText, TextStyle, Widget}; use crate::gui::Colors; use crate::gui::icons::{BARBELL, HARD_DRIVES, PLUG, TIMER}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; use crate::gui::views::network::settings::NetworkSettings; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::node::{Node, NodeConfig}; -/// Stratum server setup ui section. +/// Stratum server setup section content. pub struct StratumSetup { /// IP Addresses available at system. available_ips: Vec, @@ -38,9 +39,19 @@ pub struct StratumSetup { attempt_time_edit: String, /// Minimum share difficulty value to request from miners. - min_share_diff_edit: String + min_share_diff_edit: String, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str> } +/// Identifier for stratum port [`Modal`]. +const STRATUM_PORT_MODAL: &'static str = "stratum_port"; +/// Identifier for attempt time [`Modal`]. +const ATTEMPT_TIME_MODAL: &'static str = "stratum_attempt_time"; +/// Identifier for minimum share difficulty [`Modal`]. +const MIN_SHARE_DIFF_MODAL: &'static str = "stratum_min_share_diff"; + impl Default for StratumSetup { fn default() -> Self { let (ip, port) = NodeConfig::get_stratum_address(); @@ -51,20 +62,40 @@ impl Default for StratumSetup { stratum_port_available_edit: is_port_available, is_port_available, attempt_time_edit: NodeConfig::get_stratum_attempt_time(), - min_share_diff_edit: NodeConfig::get_stratum_min_share_diff() + min_share_diff_edit: NodeConfig::get_stratum_min_share_diff(), + modal_ids: vec![ + STRATUM_PORT_MODAL, + ATTEMPT_TIME_MODAL, + MIN_SHARE_DIFF_MODAL + ] + } + } +} + +impl ModalContainer for StratumSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + STRATUM_PORT_MODAL => self.port_modal(ui, modal, cb), + ATTEMPT_TIME_MODAL => self.attempt_modal(ui, modal, cb), + MIN_SHARE_DIFF_MODAL => self.min_diff_modal(ui, modal, cb), + _ => {} } } } impl StratumSetup { - /// Identifier for stratum port [`Modal`]. - pub const STRATUM_PORT_MODAL: &'static str = "stratum_port"; - /// Identifier for attempt time [`Modal`]. - pub const ATTEMPT_TIME_MODAL: &'static str = "stratum_attempt_time"; - /// Identifier for minimum share difficulty [`Modal`]. - pub const MIN_SHARE_DIFF_MODAL: &'static str = "stratum_min_share_diff"; + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); - pub fn ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", HARD_DRIVES, t!("network_mining.server"))); View::horizontal_line(ui, Colors::STROKE); ui.add_space(6.0); @@ -150,7 +181,7 @@ impl StratumSetup { } /// Draw stratum port value setup content. - fn port_setup_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn port_setup_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.stratum_port")) .size(16.0) .color(Colors::GRAY) @@ -163,7 +194,7 @@ impl StratumSetup { self.stratum_port_edit = port; self.stratum_port_available_edit = self.is_port_available; // Show stratum port modal. - Modal::new(Self::STRATUM_PORT_MODAL) + Modal::new(STRATUM_PORT_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -182,7 +213,7 @@ impl StratumSetup { } /// Draw stratum port [`Modal`] content. - pub fn port_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn port_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.stratum_port")) @@ -257,7 +288,7 @@ impl StratumSetup { } /// Draw attempt time value setup content. - fn attempt_time_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn attempt_time_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.attempt_time")) .size(16.0) .color(Colors::GRAY) @@ -270,7 +301,7 @@ impl StratumSetup { self.attempt_time_edit = time; // Show attempt time modal. - Modal::new(Self::ATTEMPT_TIME_MODAL) + Modal::new(ATTEMPT_TIME_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -285,7 +316,7 @@ impl StratumSetup { } /// Draw attempt time [`Modal`] content. - pub fn attempt_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn attempt_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.attempt_time")) @@ -348,7 +379,7 @@ impl StratumSetup { } /// Draw minimum share difficulty value setup content. - fn min_diff_ui(&mut self, ui: &mut Ui, cb: &dyn PlatformCallbacks) { + fn min_diff_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.label(RichText::new(t!("network_settings.min_share_diff")) .size(16.0) .color(Colors::GRAY) @@ -361,7 +392,7 @@ impl StratumSetup { self.min_share_diff_edit = diff; // Show share difficulty setup modal. - Modal::new(Self::MIN_SHARE_DIFF_MODAL) + Modal::new(MIN_SHARE_DIFF_MODAL) .position(ModalPosition::CenterTop) .title(t!("network_settings.change_value")) .show(); @@ -371,7 +402,7 @@ impl StratumSetup { } /// Draw minimum acceptable share difficulty [`Modal`] content. - pub fn min_diff_modal(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn min_diff_modal(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.min_share_diff")) @@ -435,7 +466,7 @@ impl StratumSetup { } /// Reminder to restart enabled node to show on edit setting at [`Modal`]. -pub fn server_restart_required_ui(ui: &mut Ui) { +pub fn server_restart_required_ui(ui: &mut egui::Ui) { if Node::get_stratum_stats().is_running { ui.add_space(12.0); ui.label(RichText::new(t!("network_mining.restart_server_required")) diff --git a/src/gui/views/network/types.rs b/src/gui/views/network/types.rs index b809f6e..c368951 100644 --- a/src/gui/views/network/types.rs +++ b/src/gui/views/network/types.rs @@ -13,13 +13,11 @@ // limitations under the License. use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::Modal; /// Network tab content interface. pub trait NetworkTab { fn get_type(&self) -> NetworkTabType; - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks); - fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks); + fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks); } /// Type of [`NetworkTab`] content. diff --git a/src/gui/views/root.rs b/src/gui/views/root.rs index b33916c..8fe41ec 100644 --- a/src/gui/views/root.rs +++ b/src/gui/views/root.rs @@ -20,7 +20,8 @@ use lazy_static::lazy_static; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalContainer, NetworkContent, View, WalletsContent}; +use crate::gui::views::{Modal, NetworkContent, View, WalletsContent}; +use crate::gui::views::types::ModalContainer; use crate::node::Node; lazy_static! { @@ -66,6 +67,17 @@ impl ModalContainer for Root { fn modal_ids(&self) -> &Vec<&'static str> { &self.allowed_modal_ids } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + modal: &Modal, + _: &dyn PlatformCallbacks) { + match modal.id { + Self::EXIT_MODAL_ID => self.exit_modal_content(ui, frame, modal), + _ => {} + } + } } impl Root { @@ -76,10 +88,8 @@ impl Root { pub const SIDE_PANEL_WIDTH: f32 = 400.0; pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { - // Show opened exit confirmation modal content. - if self.can_draw_modal() { - self.exit_modal_content(ui, frame); - } + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); let (is_panel_open, panel_width) = Self::network_panel_state_width(frame); // Show network content. @@ -148,59 +158,57 @@ impl Root { } /// Draw exit confirmation modal content. - fn exit_modal_content(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame) { - Modal::ui(ui.ctx(), |ui, modal| { - if self.show_exit_progress { - if !Node::is_running() { - self.exit(frame); - modal.close(); - } - ui.add_space(16.0); - ui.vertical_centered(|ui| { - View::small_loading_spinner(ui); - ui.add_space(12.0); - ui.label(RichText::new(t!("sync_status.shutdown")) - .size(17.0) - .color(Colors::TEXT)); - }); - ui.add_space(10.0); - } else { - ui.add_space(8.0); - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("modal_exit.description")) - .size(17.0) - .color(Colors::TEXT)); - }); - ui.add_space(10.0); + fn exit_modal_content(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, modal: &Modal) { + if self.show_exit_progress { + if !Node::is_running() { + self.exit(frame); + modal.close(); + } + ui.add_space(16.0); + ui.vertical_centered(|ui| { + View::small_loading_spinner(ui); + ui.add_space(12.0); + ui.label(RichText::new(t!("sync_status.shutdown")) + .size(17.0) + .color(Colors::TEXT)); + }); + ui.add_space(10.0); + } else { + ui.add_space(8.0); + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("modal_exit.description")) + .size(17.0) + .color(Colors::TEXT)); + }); + ui.add_space(10.0); - // Show modal buttons. - ui.scope(|ui| { - // Setup spacing between buttons. - ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); + // Show modal buttons. + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); - ui.columns(2, |columns| { - columns[0].vertical_centered_justified(|ui| { - View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || { - if !Node::is_running() { - self.exit(frame); - modal.close(); - } else { - Node::stop(true); - modal.disable_closing(); - self.show_exit_progress = true; - } - }); - }); - columns[1].vertical_centered_justified(|ui| { - View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("modal_exit.exit"), Colors::WHITE, || { + if !Node::is_running() { + self.exit(frame); modal.close(); - }); + } else { + Node::stop(true); + modal.disable_closing(); + self.show_exit_progress = true; + } + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("modal.cancel"), Colors::WHITE, || { + modal.close(); }); }); - ui.add_space(6.0); }); - } - }); + ui.add_space(6.0); + }); + } } /// Exit from the application. diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index 14770f3..7107bcd 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -17,18 +17,14 @@ use egui::style::Margin; use egui_extras::{Size, StripBuilder}; use crate::gui::Colors; +use crate::gui::views::types::TitleType; use crate::gui::views::View; -/// Represents title content, can be single title or with animated sub-title. -pub enum TitleType { - Single(String), - WithSubTitle(String, String, bool) -} - /// Title panel with left/right action buttons and text in the middle. pub struct TitlePanel; impl TitlePanel { + /// Default [`TitlePanel`] content height. pub const DEFAULT_HEIGHT: f32 = 54.0; pub fn ui(title: TitleType, @@ -94,7 +90,7 @@ impl TitlePanel { } } - /// Draw title text for [`TitleType::WithSubTitle`] type. + /// Draw content for [`TitleType::WithSubTitle`] type. fn with_sub_title(builder: StripBuilder, title: String, subtitle: String, animate_sub: bool) { builder .size(Size::remainder()) diff --git a/src/gui/views/types.rs b/src/gui/views/types.rs new file mode 100644 index 0000000..d08311c --- /dev/null +++ b/src/gui/views/types.rs @@ -0,0 +1,63 @@ +// 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. + +use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::Modal; + +/// Title content type, can be single title or with animated subtitle. +pub enum TitleType { + /// Single text. + Single(String), + /// With animated subtitle text. + WithSubTitle(String, String, bool) +} + +/// Position of [`Modal`] on the screen. +pub enum ModalPosition { + CenterTop, + Center +} + +/// Global [`Modal`] state. +#[derive(Default)] +pub struct ModalState { + pub modal: Option +} + +/// Contains identifiers to draw opened [`Modal`] content for current ui container. +pub trait ModalContainer { + /// List of allowed [`Modal`] identifiers. + fn modal_ids(&self) -> &Vec<&'static str>; + + /// Draw modal ui content. + fn modal_ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks); + + /// Draw [`Modal`] for current ui container if it's possible. + fn current_modal_ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + cb: &dyn PlatformCallbacks) { + let modal_id = Modal::opened(); + let draw = modal_id.is_some() && self.modal_ids().contains(&modal_id.unwrap()); + if draw { + Modal::ui(ui.ctx(), |ui, modal| { + self.modal_ui(ui, frame, modal, cb); + }); + } + } +} \ No newline at end of file diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 8d8a6f6..43a488a 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -20,9 +20,9 @@ use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, EYE, EYE_SLASH, FOLDER_LOCK, FOLDER_OPEN, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalContainer, ModalPosition, Root, TitlePanel, TitleType, View}; -use crate::gui::views::wallets::creation::{MnemonicSetup, WalletCreation}; -use crate::gui::views::wallets::setup::ConnectionSetup; +use crate::gui::views::{Modal, Root, TitlePanel, View}; +use crate::gui::views::types::{ModalContainer, ModalPosition, TitleType}; +use crate::gui::views::wallets::creation::WalletCreation; use crate::gui::views::wallets::WalletContent; use crate::wallet::{Wallet, Wallets}; @@ -48,10 +48,13 @@ pub struct WalletsContent { /// Flag to show [`Wallet`] list at dual panel mode. show_list_at_dual_panel: bool, - /// [`Modal`] ids allowed at this ui container. + /// [`Modal`] identifiers allowed at this ui container. modal_ids: Vec<&'static str> } +/// Identifier for wallet opening [`Modal`]. +const OPEN_WALLET_MODAL: &'static str = "open_wallet_modal"; + impl Default for WalletsContent { fn default() -> Self { Self { @@ -64,10 +67,8 @@ impl Default for WalletsContent { creation_content: WalletCreation::default(), show_list_at_dual_panel: true, modal_ids: vec![ - Self::OPEN_WALLET_MODAL, - WalletCreation::NAME_PASS_MODAL, - MnemonicSetup::WORD_INPUT_MODAL, - ConnectionSetup::ADD_CONNECTION_URL_MODAL + OPEN_WALLET_MODAL, + WalletCreation::NAME_PASS_MODAL ] } } @@ -77,38 +78,31 @@ impl ModalContainer for WalletsContent { fn modal_ids(&self) -> &Vec<&'static str> { &self.modal_ids } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + OPEN_WALLET_MODAL => self.open_wallet_modal_ui(ui, modal, cb), + WalletCreation::NAME_PASS_MODAL => { + self.creation_content.name_pass_modal_ui(ui, modal, cb) + }, + _ => {} + } + } } impl WalletsContent { - /// Identifier for wallet opening [`Modal`]. - const OPEN_WALLET_MODAL: &'static str = "open_wallet_modal"; - pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { - // Show modal content for current ui container. - if self.can_draw_modal() { - Modal::ui(ui.ctx(), |ui, modal| { - match modal.id { - Self::OPEN_WALLET_MODAL => { - self.open_wallet_modal_ui(ui, modal, cb); - }, - WalletCreation::NAME_PASS_MODAL => { - self.creation_content.modal_ui(ui, modal, cb); - }, - MnemonicSetup::WORD_INPUT_MODAL => { - self.creation_content.mnemonic_setup.modal_ui(ui, modal, cb); - } - ConnectionSetup::ADD_CONNECTION_URL_MODAL => { - self.creation_content.network_setup.modal_ui(ui, modal, cb); - } - _ => {} - } - }); - } + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); // Setup list of wallets if chain type was changed. let chain_type = AppConfig::chain_type(); if self.chain_type != chain_type { - self.wallets.reinit(&chain_type); + self.wallets.reinit(chain_type); self.chain_type = chain_type; } let empty_list = self.wallets.list.is_empty(); @@ -146,7 +140,7 @@ impl WalletsContent { } if create_wallet || !show_wallet { // Show wallet creation content. - self.creation_content.ui(ui, cb, |wallet| { + self.creation_content.ui(ui, frame, cb, |wallet| { // Add created wallet to list. self.wallets.add(wallet); }); @@ -207,7 +201,7 @@ impl WalletsContent { let mut right_margin = if dual_panel { wallet_panel_width } else { 0.0 }; if scroll { right_margin += 6.0 } // Show wallet creation button. - self.create_wallet_btn_ui(ui, right_margin); + self.create_wallet_btn_ui(ui, right_margin, cb); } }); } @@ -407,7 +401,10 @@ impl WalletsContent { } /// Draw floating button to show wallet creation [`Modal`]. - fn create_wallet_btn_ui(&mut self, ui: &mut egui::Ui, right_margin: f32) { + fn create_wallet_btn_ui(&mut self, + ui: &mut egui::Ui, + right_margin: f32, + cb: &dyn PlatformCallbacks) { egui::Window::new("create_wallet_button") .title_bar(false) .resizable(false) @@ -416,7 +413,7 @@ impl WalletsContent { .frame(egui::Frame::default()) .show(ui.ctx(), |ui| { View::circle_button(ui, PLUS, || { - self.creation_content.show_name_pass_modal(); + self.creation_content.show_name_pass_modal(cb); }); }); } @@ -428,7 +425,7 @@ impl WalletsContent { self.pass_edit = String::from(""); self.wrong_pass = false; // Show modal. - Modal::new(Self::OPEN_WALLET_MODAL) + Modal::new(OPEN_WALLET_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.open")) .show(); diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index e12f0b8..0154921 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -19,7 +19,8 @@ use crate::built_info; use crate::gui::Colors; use crate::gui::icons::{CHECK, EYE, EYE_SLASH, FOLDER_PLUS, SHARE_FAT}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; +use crate::gui::views::types::ModalPosition; use crate::gui::views::wallets::creation::MnemonicSetup; use crate::gui::views::wallets::creation::types::Step; use crate::gui::views::wallets::setup::ConnectionSetup; @@ -71,8 +72,10 @@ impl WalletCreation { /// Wallet name/password input modal identifier. pub const NAME_PASS_MODAL: &'static str = "name_pass_modal"; + /// Draw wallet creation content. pub fn ui(&mut self, ui: &mut egui::Ui, + frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks, on_create: impl FnOnce(Wallet)) { // Show wallet creation step description and confirmation panel. @@ -109,7 +112,7 @@ impl WalletCreation { ..Default::default() }) .show_inside(ui, |ui| { - self.step_content_ui(ui, cb); + self.step_content_ui(ui, frame, cb); }); } @@ -184,7 +187,10 @@ impl WalletCreation { } /// Draw wallet creation [`Step`] content. - fn step_content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + fn step_content_ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + cb: &dyn PlatformCallbacks) { match &self.step { None => { // Show wallet creation message if step is empty. @@ -210,15 +216,15 @@ impl WalletCreation { ui.add_space(8.0); let add_text = format!("{} {}", FOLDER_PLUS, t!("wallets.add")); View::button(ui, add_text, Colors::BUTTON, || { - self.show_name_pass_modal(); + self.show_name_pass_modal(cb); }); }); } Some(step) => { match step { - Step::EnterMnemonic => self.mnemonic_setup.ui(ui), - Step::ConfirmMnemonic => self.mnemonic_setup.confirm_ui(ui), - Step::SetupConnection => self.network_setup.ui(ui, cb) + Step::EnterMnemonic => self.mnemonic_setup.ui(ui, frame, cb), + Step::ConfirmMnemonic => self.mnemonic_setup.confirm_ui(ui, frame, cb), + Step::SetupConnection => self.network_setup.ui(ui, frame, cb) } } } @@ -271,7 +277,7 @@ impl WalletCreation { wallet.open(pass).unwrap(); // Pass created wallet to callback. (on_create.unwrap())(wallet); - // Reset creation data. + // Reset input data. self.reset(); None } @@ -282,7 +288,7 @@ impl WalletCreation { } /// Start wallet creation from showing [`Modal`] to enter name and password. - pub fn show_name_pass_modal(&mut self) { + pub fn show_name_pass_modal(&mut self, cb: &dyn PlatformCallbacks) { // Reset modal values. self.hide_pass = true; self.modal_just_opened = true; @@ -293,10 +299,14 @@ impl WalletCreation { .position(ModalPosition::CenterTop) .title(t!("wallets.add")) .show(); + cb.show_keyboard(); } - /// Draw wallet creation [`Modal`] content. - pub fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + /// Draw creating wallet name/password input [`Modal`] content. + pub fn name_pass_modal_ui(&mut self, + ui: &mut egui::Ui, + modal: &Modal, + cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("wallets.name")) diff --git a/src/gui/views/wallets/creation/mnemonic.rs b/src/gui/views/wallets/creation/mnemonic.rs index 2e319bd..5594639 100644 --- a/src/gui/views/wallets/creation/mnemonic.rs +++ b/src/gui/views/wallets/creation/mnemonic.rs @@ -17,7 +17,8 @@ use egui::{Id, RichText, ScrollArea, TextStyle, Widget}; use crate::gui::Colors; use crate::gui::icons::PENCIL; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, Root, View}; +use crate::gui::views::{Modal, Root, View}; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::wallet::Mnemonic; use crate::wallet::types::{PhraseMode, PhraseSize}; @@ -34,9 +35,15 @@ pub struct MnemonicSetup { /// Entered word value for [`Modal`]. word_edit: String, /// Flag to check if entered word is valid. - valid_word_edit: bool + valid_word_edit: bool, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str> } +/// Identifier for word input [`Modal`]. +pub const WORD_INPUT_MODAL: &'static str = "word_input_modal"; + impl Default for MnemonicSetup { fn default() -> Self { Self { @@ -44,17 +51,37 @@ impl Default for MnemonicSetup { valid_phrase: true, word_num_edit: 0, word_edit: String::from(""), - valid_word_edit: true + valid_word_edit: true, + modal_ids: vec![ + WORD_INPUT_MODAL + ] + } + } +} + +impl ModalContainer for MnemonicSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + WORD_INPUT_MODAL => self.word_modal_ui(ui, modal, cb), + _ => {} } } } impl MnemonicSetup { - /// Identifier for word input [`Modal`]. - pub const WORD_INPUT_MODAL: &'static str = "word_input_modal"; + /// Draw content for phrase input step. + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); - /// Draw content for input step. - pub fn ui(&mut self, ui: &mut egui::Ui) { ScrollArea::vertical() .id_source("input_mnemonic_words_list") .auto_shrink([false; 2]) @@ -69,12 +96,18 @@ impl MnemonicSetup { ui.add_space(6.0); // Show words setup. - self.word_list_ui(ui, self.mnemonic.mode == PhraseMode::Import); + self.word_list_ui(ui, self.mnemonic.mode == PhraseMode::Import, cb); }); } - /// Draw content for confirmation step. - pub fn confirm_ui(&mut self, ui: &mut egui::Ui) { + /// Draw content for phrase confirmation step. + pub fn confirm_ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); + ui.add_space(4.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("wallets.saved_phrase")).size(16.0).color(Colors::GRAY)); @@ -85,7 +118,7 @@ impl MnemonicSetup { .auto_shrink([false; 2]) .show(ui, |ui| { // Show words setup. - self.word_list_ui(ui, true); + self.word_list_ui(ui, true, cb); }); } @@ -134,7 +167,7 @@ impl MnemonicSetup { } /// Draw list of words for mnemonic phrase. - fn word_list_ui(&mut self, ui: &mut egui::Ui, edit_words: bool) { + fn word_list_ui(&mut self, ui: &mut egui::Ui, edit_words: bool, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.scope(|ui| { // Setup spacing between columns. @@ -161,25 +194,25 @@ impl MnemonicSetup { ui.columns(cols, |columns| { columns[0].horizontal(|ui| { let word = chunk.get(0).unwrap(); - self.word_item_ui(ui, word_number, word, edit_words); + self.word_item_ui(ui, word_number, word, edit_words, cb); }); columns[1].horizontal(|ui| { word_number += 1; let word = chunk.get(1).unwrap(); - self.word_item_ui(ui, word_number, word, edit_words); + self.word_item_ui(ui, word_number, word, edit_words, cb); }); if size > 2 { columns[2].horizontal(|ui| { word_number += 1; let word = chunk.get(2).unwrap(); - self.word_item_ui(ui, word_number, word, edit_words); + self.word_item_ui(ui, word_number, word, edit_words, cb); }); } if size > 3 { columns[3].horizontal(|ui| { word_number += 1; let word = chunk.get(3).unwrap(); - self.word_item_ui(ui, word_number, word, edit_words); + self.word_item_ui(ui, word_number, word, edit_words, cb); }); } }); @@ -187,7 +220,7 @@ impl MnemonicSetup { ui.columns(cols, |columns| { columns[0].horizontal(|ui| { let word = chunk.get(0).unwrap(); - self.word_item_ui(ui, word_number, word, edit_words); + self.word_item_ui(ui, word_number, word, edit_words, cb); }); }); } @@ -197,7 +230,12 @@ impl MnemonicSetup { } /// Draw word list item for current mode. - fn word_item_ui(&mut self, ui: &mut egui::Ui, num: usize, word: &String, edit: bool) { + fn word_item_ui(&mut self, + ui: &mut egui::Ui, + num: usize, + word: &String, + edit: bool, + cb: &dyn PlatformCallbacks) { if edit { ui.add_space(6.0); View::button(ui, PENCIL.to_string(), Colors::BUTTON, || { @@ -206,10 +244,11 @@ impl MnemonicSetup { self.word_edit = word.clone(); self.valid_word_edit = true; // Show word edit modal. - Modal::new(MnemonicSetup::WORD_INPUT_MODAL) + Modal::new(WORD_INPUT_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.saved_phrase")) .show(); + cb.show_keyboard(); }); ui.label(RichText::new(format!("#{} {}", num, word)) .size(17.0) @@ -226,8 +265,8 @@ impl MnemonicSetup { self.mnemonic = Mnemonic::default(); } - /// Show word input [`Modal`] content. - pub fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + /// Draw word input [`Modal`] content. + fn word_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("wallets.enter_word", "number" => self.word_num_edit)) diff --git a/src/gui/views/wallets/setup/connection.rs b/src/gui/views/wallets/setup/connection.rs index 4dda7bb..38b3afc 100644 --- a/src/gui/views/wallets/setup/connection.rs +++ b/src/gui/views/wallets/setup/connection.rs @@ -18,7 +18,8 @@ use url::Url; use crate::gui::Colors; use crate::gui::icons::{GLOBE, GLOBE_SIMPLE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, ModalPosition, View}; +use crate::gui::views::{Modal, View}; +use crate::gui::views::types::{ModalContainer, ModalPosition}; use crate::gui::views::wallets::setup::ConnectionMethod; use crate::wallet::{ConnectionsConfig, ExternalConnection}; @@ -29,14 +30,20 @@ pub struct ConnectionSetup { /// Flag to check if modal was just opened. first_modal_launch: bool, - /// External node connection URL value for [`Modal`]. + /// External connection URL value for [`Modal`]. ext_node_url_edit: String, - /// External node connection API secret value for [`Modal`]. + /// External connection API secret value for [`Modal`]. ext_node_secret_edit: String, /// Flag to show URL format error. ext_node_url_error: bool, + + /// [`Modal`] identifiers allowed at this ui container. + modal_ids: Vec<&'static str> } +/// External connection [`Modal`] identifier. +pub const EXT_CONNECTION_MODAL: &'static str = "ext_connection_modal"; + impl Default for ConnectionSetup { fn default() -> Self { Self { @@ -44,21 +51,38 @@ impl Default for ConnectionSetup { first_modal_launch: true, ext_node_url_edit: "".to_string(), ext_node_secret_edit: "".to_string(), - ext_node_url_error: false + ext_node_url_error: false, + modal_ids: vec![ + EXT_CONNECTION_MODAL + ] + } + } +} + +impl ModalContainer for ConnectionSetup { + fn modal_ids(&self) -> &Vec<&'static str> { + &self.modal_ids + } + + fn modal_ui(&mut self, + ui: &mut egui::Ui, + _: &mut eframe::Frame, + modal: &Modal, + cb: &dyn PlatformCallbacks) { + match modal.id { + EXT_CONNECTION_MODAL => self.ext_conn_modal_ui(ui, modal, cb), + _ => {} } } } impl ConnectionSetup { - /// External node connection [`Modal`] identifier. - pub const ADD_CONNECTION_URL_MODAL: &'static str = "add_connection_url_modal"; - //TODO: Setup for provided wallet // pub fn new() -> Self { // Self { method: ConnectionMethod::Integrated } // } - /// Get external node connection URL. + /// Get external connection URL. pub fn get_ext_conn_url(&self) -> Option { match &self.method { ConnectionMethod::Integrated => None, @@ -66,7 +90,10 @@ impl ConnectionSetup { } } - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + // Draw modal content for current ui container. + self.current_modal_ui(ui, frame, cb); + ScrollArea::vertical() .id_source("wallet_connection_setup") .auto_shrink([false; 2]) @@ -96,7 +123,7 @@ impl ConnectionSetup { self.ext_node_secret_edit = "".to_string(); self.ext_node_url_error = false; // Show modal. - Modal::new(Self::ADD_CONNECTION_URL_MODAL) + Modal::new(EXT_CONNECTION_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.add_node")) .show(); @@ -116,8 +143,11 @@ impl ConnectionSetup { }); } - /// Draw modal content. - pub fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + /// Draw external connection [`Modal`] content. + pub fn ext_conn_modal_ui(&mut self, + ui: &mut egui::Ui, + modal: &Modal, + cb: &dyn PlatformCallbacks) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("wallets.node_url")) @@ -197,7 +227,7 @@ impl ConnectionSetup { let ext_conn = ExternalConnection::new(url.clone(), secret); ConnectionsConfig::add_external_connection(ext_conn); - // Set added method as current. + // Set added connection as current. self.method = ConnectionMethod::External(url); // Close modal. diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 49d25ff..d7f13fe 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -19,7 +19,7 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::View; use crate::wallet::Wallet; -/// Selected wallet list item content. +/// Selected and opened wallet content. pub struct WalletContent { } diff --git a/src/node/config.rs b/src/node/config.rs index f639036..afa02e4 100644 --- a/src/node/config.rs +++ b/src/node/config.rs @@ -45,8 +45,9 @@ impl PeersConfig { /// Save peers config to the file. pub fn save(&self) { let chain_type = AppConfig::chain_type(); - let chain_name = Some(chain_type.shortname()); - let config_path = Settings::get_config_path(Self::FILE_NAME, chain_name); + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let config_path = Settings::get_config_path(Self::FILE_NAME, sub_dir); Settings::write_to_file(self, config_path); } @@ -146,8 +147,9 @@ impl NodeConfig { // Initialize peers config. let peers_config = { - let chain_name = Some(chain_type.shortname()); - let path = Settings::get_config_path(PeersConfig::FILE_NAME, chain_name); + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let path = Settings::get_config_path(PeersConfig::FILE_NAME, sub_dir); let config = Settings::read_from_file::(path.clone()); if !path.exists() || config.is_err() { Self::save_default_peers_config(chain_type) @@ -158,8 +160,9 @@ impl NodeConfig { // Initialize node config. let node_config = { - let chain_name = Some(chain_type.shortname()); - let path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, chain_name); + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, sub_dir); let config = Settings::read_from_file::(path.clone()); if !path.exists() || config.is_err() { Self::save_default_node_server_config(chain_type) @@ -173,10 +176,11 @@ impl NodeConfig { /// Save default node config for specified [`ChainTypes`]. fn save_default_node_server_config(chain_type: &ChainTypes) -> ConfigMembers { - let chain_name = Some(chain_type.shortname()); - let path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, chain_name.clone()); + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, sub_dir); let mut default_config = GlobalConfig::for_chain(chain_type); - default_config.update_paths(&Settings::get_base_path(chain_name)); + default_config.update_paths(&Settings::get_base_path(sub_dir)); let config = default_config.members.unwrap(); Settings::write_to_file(&config, path); config @@ -184,8 +188,9 @@ impl NodeConfig { /// Save default peers config for specified [`ChainTypes`]. fn save_default_peers_config(chain_type: &ChainTypes) -> PeersConfig { - let chain_name = Some(chain_type.shortname()); - let path = Settings::get_config_path(PeersConfig::FILE_NAME, chain_name); + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let path = Settings::get_config_path(PeersConfig::FILE_NAME, sub_dir); let config = PeersConfig::default(); Settings::write_to_file(&config, path); config @@ -193,8 +198,9 @@ impl NodeConfig { /// Save node config to the file. pub fn save(&self) { - let chain_name = Some(self.node.server.chain_type.shortname()); - let config_path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, chain_name); + let chain_name = self.node.server.chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let config_path = Settings::get_config_path(SERVER_CONFIG_FILE_NAME, sub_dir); Settings::write_to_file(&self.node, config_path); } @@ -235,7 +241,9 @@ impl NodeConfig { /// Get path for secret file. fn get_secret_path(chain_type: &ChainTypes, secret_file_name: &str) -> PathBuf { - let grin_path = Settings::get_base_path(Some(chain_type.shortname())); + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let grin_path = Settings::get_base_path(sub_dir); let mut api_secret_path = grin_path; api_secret_path.push(secret_file_name); api_secret_path diff --git a/src/settings.rs b/src/settings.rs index 7395833..485394a 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -120,7 +120,7 @@ impl Settings { } } - /// Initialize config from provided file path or load default if file not exists. + /// Initialize config from provided file path or set [`Default`] if file not exists. pub fn init_config(path: PathBuf) -> T { let parsed = Self::read_from_file::(path.clone()); if !path.exists() || !parsed.is_err() { @@ -154,7 +154,7 @@ impl Settings { } /// Get base directory path for config. - pub fn get_base_path(sub_dir: Option) -> PathBuf { + pub fn get_base_path(sub_dir: Option<&str>) -> PathBuf { // Check if dir exists. let mut path = match dirs::home_dir() { Some(p) => p, @@ -172,7 +172,7 @@ impl Settings { } /// Get config file path from provided name and sub-directory if needed. - pub fn get_config_path(config_name: &str, sub_dir: Option) -> PathBuf { + pub fn get_config_path(config_name: &str, sub_dir: Option<&str>) -> PathBuf { let mut settings_path = Self::get_base_path(sub_dir); settings_path.push(config_name); settings_path diff --git a/src/wallet/config.rs b/src/wallet/config.rs index 1102980..25d1ade 100644 --- a/src/wallet/config.rs +++ b/src/wallet/config.rs @@ -43,7 +43,7 @@ impl WalletConfig { pub fn create(name: String, external_node_url: Option) -> WalletConfig { let id = chrono::Utc::now().timestamp(); let chain_type = AppConfig::chain_type(); - let config_path = Self::get_config_file_path(&chain_type, id); + let config_path = Self::get_config_file_path(chain_type, id); let config = WalletConfig { chain_type, id, name, external_node_url }; Settings::write_to_file(&config, config_path); @@ -61,8 +61,10 @@ impl WalletConfig { } /// Get wallets base directory path for provided [`ChainTypes`]. - pub fn get_base_path(chain_type: &ChainTypes) -> PathBuf { - let mut wallets_path = Settings::get_base_path(Some(chain_type.shortname())); + pub fn get_base_path(chain_type: ChainTypes) -> PathBuf { + let chain_name = chain_type.shortname(); + let sub_dir = Some(chain_name.as_str()); + let mut wallets_path = Settings::get_base_path(sub_dir); wallets_path.push(BASE_DIR_NAME); // Create wallets base directory if it doesn't exist. if !wallets_path.exists() { @@ -72,7 +74,7 @@ impl WalletConfig { } /// Get config file path for provided [`ChainTypes`] and wallet identifier. - fn get_config_file_path(chain_type: &ChainTypes, id: i64) -> PathBuf { + fn get_config_file_path(chain_type: ChainTypes, id: i64) -> PathBuf { let mut config_path = Self::get_base_path(chain_type); config_path.push(id.to_string()); // Create if the config path doesn't exist. @@ -86,14 +88,14 @@ impl WalletConfig { /// Get current wallet data path. pub fn get_data_path(&self) -> String { let chain_type = AppConfig::chain_type(); - let mut config_path = Self::get_base_path(&chain_type); + let mut config_path = Self::get_base_path(chain_type); config_path.push(self.id.to_string()); config_path.to_str().unwrap().to_string() } /// Save wallet config. fn save(&self) { - let config_path = Self::get_config_file_path(&self.chain_type, self.id); + let config_path = Self::get_config_file_path(self.chain_type, self.id); Settings::write_to_file(self, config_path); } diff --git a/src/wallet/connections/config.rs b/src/wallet/connections/config.rs index ff86f54..0f421e1 100644 --- a/src/wallet/connections/config.rs +++ b/src/wallet/connections/config.rs @@ -18,13 +18,13 @@ use lazy_static::lazy_static; use serde_derive::{Deserialize, Serialize}; use crate::Settings; -use crate::wallet::ExternalConnection; +use crate::wallet::{BASE_DIR_NAME, ExternalConnection}; lazy_static! { - /// Static settings state to be accessible globally. + /// Static connections state to be accessible globally. static ref CONNECTIONS_STATE: Arc> = Arc::new( RwLock::new( - Settings::init_config(Settings::get_config_path(CONFIG_FILE_NAME, None)) + Settings::init_config(Settings::get_config_path(CONFIG_FILE_NAME, Some(BASE_DIR_NAME))) ) ); } @@ -36,9 +36,6 @@ pub struct ConnectionsConfig { external: Vec } -/// Wallet configuration file name. -const CONFIG_FILE_NAME: &'static str = "connections.toml"; - impl Default for ConnectionsConfig { fn default() -> Self { Self { @@ -49,6 +46,9 @@ impl Default for ConnectionsConfig { } } +/// Wallet configuration file name. +const CONFIG_FILE_NAME: &'static str = "connections.toml"; + impl ConnectionsConfig { /// Save connections config to file. pub fn save(&self) { @@ -90,4 +90,14 @@ impl ConnectionsConfig { } None } + + /// Remove external node connection. + pub fn remove_external_connection(conn: &ExternalConnection) { + let mut w_config = CONNECTIONS_STATE.write().unwrap(); + let index = w_config.external.iter().position(|c| c.url == conn.url); + if let Some(i) = index { + w_config.external.remove(i); + w_config.save(); + } + } } \ No newline at end of file diff --git a/src/wallet/wallets.rs b/src/wallet/wallets.rs index 1124baf..2c4eee1 100644 --- a/src/wallet/wallets.rs +++ b/src/wallet/wallets.rs @@ -46,7 +46,7 @@ pub struct Wallets { impl Default for Wallets { fn default() -> Self { Self { - list: Self::init(&AppConfig::chain_type()), + list: Self::init(AppConfig::chain_type()), selected_id: None } } @@ -54,7 +54,7 @@ impl Default for Wallets { impl Wallets { /// Initialize wallets from base directory for provided [`ChainType`]. - fn init(chain_type: &ChainTypes) -> Vec { + fn init(chain_type: ChainTypes) -> Vec { let mut wallets = Vec::new(); let wallets_dir = WalletConfig::get_base_path(chain_type); // Load wallets from base directory. @@ -71,7 +71,7 @@ impl Wallets { } /// Reinitialize wallets for provided [`ChainTypes`]. - pub fn reinit(&mut self, chain_type: &ChainTypes) { + pub fn reinit(&mut self, chain_type: ChainTypes) { self.list = Self::init(chain_type); } @@ -103,7 +103,7 @@ impl Wallets { /// Open selected wallet. pub fn open_selected(&mut self, password: String) -> Result<(), Error> { - for mut w in self.list.iter_mut() { + for w in self.list.iter_mut() { if Some(w.config.id) == self.selected_id { return w.open(password); }