diff --git a/src/gui/app.rs b/src/gui/app.rs index 85b6b92a..8c21472d 100755 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -21,6 +21,7 @@ use crate::gui::views::{Content, KeyboardContent, Modal, TitlePanel, View}; use crate::gui::Colors; use crate::wallet::ExternalConnection; use crate::AppConfig; +use crate::gui::views::types::ContentContainer; /// Implements ui entry point and contains platform-specific callbacks. pub struct App { @@ -69,9 +70,7 @@ impl App { // Handle Esc keyboard key event. if ctx.input_mut(|i| i.consume_key(Modifiers::NONE, egui::Key::Escape)) { - if Modal::on_back() { - self.content.on_back(&self.platform); - } + self.content.on_back(&self.platform); // Request repaint to update previous content. ctx.request_repaint(); } @@ -122,13 +121,6 @@ impl App { Self::title_panel_bg(ui, false); self.content.ui(ui, &self.platform); } - - // Provide incoming data to wallets. - if let Some(data) = crate::consume_incoming_data() { - if !data.is_empty() { - self.content.wallets.on_data(ui, Some(data)); - } - } }); // Check if desktop window was focused after requested attention. @@ -268,22 +260,7 @@ impl App { } // Paint the title. - let dual_wallets_panel = ui.available_width() >= (Content::SIDE_PANEL_WIDTH * 3.0) + - View::get_right_inset() + View::get_left_inset(); - let wallet_panel_opened = self.content.wallets.showing_wallet(); - let show_app_name = if dual_wallets_panel { - wallet_panel_opened && !AppConfig::show_wallets_at_dual_panel() - } else if Content::is_dual_panel_mode(ui.ctx()) { - wallet_panel_opened - } else { - Content::is_network_panel_open() || wallet_panel_opened - }; - let creating_wallet = self.content.wallets.creating_wallet(); - let title_text = if creating_wallet || show_app_name { - format!("Grim {}", crate::VERSION) - } else { - "ツ".to_string() - }; + let title_text = format!("Grim {}", crate::VERSION); painter.text( title_rect.center(), egui::Align2::CENTER_CENTER, diff --git a/src/gui/views/content.rs b/src/gui/views/content.rs index cfaa346c..02e5eee3 100644 --- a/src/gui/views/content.rs +++ b/src/gui/views/content.rs @@ -21,7 +21,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use crate::gui::icons::FILE_X; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::network::NetworkContent; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::gui::views::wallets::WalletsContent; use crate::gui::views::{Modal, View}; use crate::gui::Colors; @@ -39,7 +39,7 @@ pub struct Content { network: NetworkContent, /// Central panel [`WalletsContent`] content. - pub wallets: WalletsContent, + wallets: WalletsContent, /// Check if app exit is allowed on Desktop close event. pub exit_allowed: bool, @@ -48,16 +48,8 @@ pub struct Content { /// Flag to check it's first draw of content. first_draw: bool, - - /// List of allowed [`Modal`] ids for this [`ModalContainer`]. - allowed_modal_ids: Vec<&'static str> } -/// Identifier for integrated node warning [`Modal`] on Android. -const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal"; -/// Identifier for crash report [`Modal`]. -const CRASH_REPORT_MODAL: &'static str = "crash_report_modal"; - impl Default for Content { fn default() -> Self { // Exit from eframe only for non-mobile platforms. @@ -69,24 +61,25 @@ impl Default for Content { exit_allowed, show_exit_progress: false, first_draw: true, - allowed_modal_ids: vec![ - Self::EXIT_CONFIRMATION_MODAL, - ANDROID_INTEGRATED_NODE_WARNING_MODAL, - CRASH_REPORT_MODAL - ], } } } -impl ModalContainer for Content { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.allowed_modal_ids +/// Identifier for integrated node warning [`Modal`] on Android. +const ANDROID_INTEGRATED_NODE_WARNING_MODAL: &'static str = "android_node_warning_modal"; +/// Identifier for crash report [`Modal`]. +const CRASH_REPORT_MODAL: &'static str = "crash_report_modal"; + +impl ContentContainer for Content { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + Self::EXIT_CONFIRMATION_MODAL, + ANDROID_INTEGRATED_NODE_WARNING_MODAL, + CRASH_REPORT_MODAL + ] } - fn modal_ui(&mut self, - ui: &mut egui::Ui, - modal: &Modal, - cb: &dyn PlatformCallbacks) { + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { match modal.id { Self::EXIT_CONFIRMATION_MODAL => self.exit_modal_content(ui, modal, cb), ANDROID_INTEGRATED_NODE_WARNING_MODAL => self.android_warning_modal_ui(ui), @@ -94,12 +87,23 @@ impl ModalContainer for Content { _ => {} } } + + fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool { + if Modal::on_back() { + if self.wallets.on_back(cb) { + Self::show_exit_modal(); + return false; + } + } + true + } + + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + self.content_ui(ui, cb); + } } impl Content { - /// Identifier for exit confirmation [`Modal`]. - pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal"; - /// Default width of side panel at application UI. pub const SIDE_PANEL_WIDTH: f32 = 400.0; /// Desktop window title height. @@ -107,9 +111,10 @@ impl Content { /// Margin of window frame at desktop. pub const WINDOW_FRAME_MARGIN: f32 = 6.0; - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - self.current_modal_ui(ui, cb); + /// Identifier for exit confirmation [`Modal`]. + pub const EXIT_CONFIRMATION_MODAL: &'static str = "exit_confirmation_modal"; + fn content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { let dual_panel = Self::is_dual_panel_mode(ui.ctx()); let (is_panel_open, mut panel_width) = network_panel_state_width(ui.ctx(), dual_panel); if self.network.showing_settings() { @@ -237,13 +242,6 @@ impl Content { } } - /// Handle Back key event. - pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) { - if self.wallets.on_back(cb) { - Self::show_exit_modal() - } - } - /// Draw content for integrated node warning [`Modal`] on Android. fn android_warning_modal_ui(&mut self, ui: &mut egui::Ui) { ui.add_space(6.0); diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 11a253b8..9ff9ff32 100755 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -23,6 +23,7 @@ use std::sync::Arc; use crate::gui::views::types::{ModalPosition, ModalState}; use crate::gui::views::{Content, View}; use crate::gui::Colors; +use crate::gui::platform::PlatformCallbacks; lazy_static! { /// Showing [`Modal`] state to be accessible from different ui parts. @@ -173,8 +174,9 @@ impl Modal { modal.first_draw.load(Ordering::Relaxed) } - /// Draw opened [`Modal`] content. - pub fn ui(ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { + pub fn ui(ctx: &egui::Context, + cb: &dyn PlatformCallbacks, + add_content: impl FnOnce(&mut egui::Ui, &Modal, &dyn PlatformCallbacks)) { let has_modal = { MODAL_STATE.read().modal.is_some() }; @@ -183,12 +185,15 @@ impl Modal { let r_state = MODAL_STATE.read(); r_state.modal.clone().unwrap() }; - modal.window_ui(ctx, add_content); + modal.window_ui(ctx, cb, add_content); } } /// Draw [`egui::Window`] with provided content. - fn window_ui(&self, ctx: &egui::Context, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { + fn window_ui(&self, + ctx: &egui::Context, + cb: &dyn PlatformCallbacks, + add_content: impl FnOnce(&mut egui::Ui, &Modal, &dyn PlatformCallbacks)) { let is_fullscreen = ctx.input(|i| { i.viewport().fullscreen.unwrap_or(false) }); @@ -249,7 +254,7 @@ impl Modal { if let Some(title) = &self.title { title_ui(title, ui); } - self.content_ui(ui, add_content); + self.content_ui(ui, cb, add_content); }); // Setup first draw flag. @@ -289,7 +294,10 @@ impl Modal { } /// Draw provided content. - fn content_ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) { + fn content_ui(&self, + ui: &mut egui::Ui, + cb: &dyn PlatformCallbacks, + add_content: impl FnOnce(&mut egui::Ui, &Modal, &dyn PlatformCallbacks)) { let mut rect = ui.available_rect_before_wrap(); // Create background shape. @@ -308,7 +316,7 @@ impl Modal { rect.min += egui::emath::vec2(6.0, 0.0); rect.max -= egui::emath::vec2(6.0, 0.0); let resp = ui.scope_builder(UiBuilder::new().max_rect(rect), |ui| { - (add_content)(ui, self); + (add_content)(ui, self, cb); }).response; // Setup background size. diff --git a/src/gui/views/network/connections.rs b/src/gui/views/network/connections.rs index 59241049..82fc9bdf 100644 --- a/src/gui/views/network/connections.rs +++ b/src/gui/views/network/connections.rs @@ -21,7 +21,7 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, View}; use crate::gui::views::network::modals::ExternalConnectionModal; use crate::gui::views::network::NodeSetup; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::node::{Node, NodeConfig}; use crate::wallet::{ConnectionsConfig, ExternalConnection}; @@ -29,25 +29,21 @@ use crate::wallet::{ConnectionsConfig, ExternalConnection}; pub struct ConnectionsContent { /// External connection [`Modal`] content. ext_conn_modal: ExternalConnectionModal, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } impl Default for ConnectionsContent { fn default() -> Self { Self { ext_conn_modal: ExternalConnectionModal::new(None), - modal_ids: vec![ - ExternalConnectionModal::NETWORK_ID - ], } } } -impl ModalContainer for ConnectionsContent { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for ConnectionsContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + ExternalConnectionModal::NETWORK_ID + ] } fn modal_ui(&mut self, @@ -61,13 +57,12 @@ impl ModalContainer for ConnectionsContent { _ => {} } } -} -impl ConnectionsContent { - /// Draw connections content. - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - self.current_modal_ui(ui, cb); + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { ui.add_space(2.0); // Show network type selection. @@ -121,7 +116,9 @@ impl ConnectionsContent { } } } +} +impl ConnectionsContent { /// Draw integrated node connection item content. pub fn integrated_node_item_ui(ui: &mut egui::Ui, custom_button: impl FnOnce(&mut egui::Ui)) { // Draw round background. diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index cdf2dbd2..4bbe676e 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -23,7 +23,7 @@ use crate::gui::views::{Content, TitlePanel, View}; use crate::gui::views::network::{ConnectionsContent, NetworkMetrics, NetworkMining, NetworkNode, NetworkSettings}; use crate::gui::views::network::types::{NodeTab, NodeTabType}; use crate::gui::views::settings::SettingsContent; -use crate::gui::views::types::{LinePosition, TitleContentType, TitleType}; +use crate::gui::views::types::{ContentContainer, LinePosition, TitleContentType, TitleType}; use crate::node::{Node, NodeConfig, NodeError}; use crate::wallet::ExternalConnection; @@ -111,7 +111,7 @@ impl NetworkContent { let rect = ui.available_rect_before_wrap(); if let Some(c) = &mut self.settings_content { View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - c.ui(ui); + c.ui(ui, cb); }); } else if self.node_tab_content.get_type() != NodeTabType::Settings { View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { @@ -124,11 +124,11 @@ impl NetworkContent { Node::is_stopping() { NetworkContent::loading_ui(ui, None); } else { - self.node_tab_content.ui(ui, cb); + self.node_tab_content.tab_ui(ui, cb); } }); } else { - self.node_tab_content.ui(ui, cb); + self.node_tab_content.tab_ui(ui, cb); } // Draw content divider line. diff --git a/src/gui/views/network/metrics.rs b/src/gui/views/network/metrics.rs index 975d1e4b..b51952e4 100644 --- a/src/gui/views/network/metrics.rs +++ b/src/gui/views/network/metrics.rs @@ -38,7 +38,7 @@ impl NodeTab for NetworkMetrics { NodeTabType::Metrics } - fn ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { + fn tab_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { let server_stats = Node::get_stats(); let stats = server_stats.as_ref().unwrap(); if stats.diff_stats.height == 0 { diff --git a/src/gui/views/network/mining.rs b/src/gui/views/network/mining.rs index acfaf2c8..dbb56ce5 100644 --- a/src/gui/views/network/mining.rs +++ b/src/gui/views/network/mining.rs @@ -24,6 +24,7 @@ use crate::gui::views::{Content, View}; use crate::gui::views::network::NetworkContent; use crate::gui::views::network::setup::StratumSetup; use crate::gui::views::network::types::{NodeTab, NodeTabType}; +use crate::gui::views::types::ContentContainer; use crate::node::{Node, NodeConfig}; /// Mining tab content. @@ -45,7 +46,7 @@ impl NodeTab for NetworkMining { NodeTabType::Mining } - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + fn tab_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { if Node::is_stratum_starting() || Node::get_sync_status().unwrap() != SyncStatus::NoSync { NetworkContent::loading_ui(ui, Some(t!("network_mining.loading"))); return; diff --git a/src/gui/views/network/modals/ext_conn.rs b/src/gui/views/network/modals/ext_conn.rs index fae993da..10cae1bc 100644 --- a/src/gui/views/network/modals/ext_conn.rs +++ b/src/gui/views/network/modals/ext_conn.rs @@ -22,6 +22,9 @@ use crate::wallet::{ConnectionsConfig, ExternalConnection}; /// Content to create or update external wallet connection. pub struct ExternalConnectionModal { + /// Flag to check if content was just rendered. + first_draw: bool, + /// External connection URL value for [`Modal`]. ext_node_url_edit: String, /// External connection API secret value for [`Modal`]. @@ -46,6 +49,7 @@ impl ExternalConnectionModal { ("".to_string(), "".to_string(), None) }; Self { + first_draw: true, ext_node_url_edit, ext_node_secret_edit, ext_node_url_error: false, @@ -103,7 +107,7 @@ impl ExternalConnectionModal { let url_edit_id = Id::from(modal.id).with(self.ext_conn_id).with("node_url"); let mut url_edit = TextEdit::new(url_edit_id) .paste() - .focus(Modal::first_draw()); + .focus(self.first_draw); url_edit.ui(ui, &mut self.ext_node_url_edit, cb); ui.add_space(8.0); @@ -163,5 +167,7 @@ impl ExternalConnectionModal { }); ui.add_space(6.0); }); + + self.first_draw = false; } } \ No newline at end of file diff --git a/src/gui/views/network/node.rs b/src/gui/views/network/node.rs index 9054b0ea..81af752b 100644 --- a/src/gui/views/network/node.rs +++ b/src/gui/views/network/node.rs @@ -32,7 +32,7 @@ impl NodeTab for NetworkNode { NodeTabType::Info } - fn ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { + fn tab_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { ScrollArea::vertical() .id_salt("integrated_node_info_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) diff --git a/src/gui/views/network/settings.rs b/src/gui/views/network/settings.rs index d22bd567..7fa83cde 100644 --- a/src/gui/views/network/settings.rs +++ b/src/gui/views/network/settings.rs @@ -21,7 +21,7 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; use crate::gui::views::network::types::{NodeTab, NodeTabType}; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::node::{Node, NodeConfig}; /// Integrated node settings tab content. @@ -36,9 +36,6 @@ pub struct NetworkSettings { pool: PoolSetup, /// Dandelion server setup content. dandelion: DandelionSetup, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } /// Identifier for settings reset confirmation [`Modal`]. @@ -52,16 +49,15 @@ impl Default for NetworkSettings { stratum: StratumSetup::default(), pool: PoolSetup::default(), dandelion: DandelionSetup::default(), - modal_ids: vec![ - RESET_SETTINGS_CONFIRMATION_MODAL - ] } } } -impl ModalContainer for NetworkSettings { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for NetworkSettings { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + RESET_SETTINGS_CONFIRMATION_MODAL + ] } fn modal_ui(&mut self, @@ -73,17 +69,12 @@ impl ModalContainer for NetworkSettings { _ => {} } } -} -impl NodeTab for NetworkSettings { - fn get_type(&self) -> NodeTabType { - NodeTabType::Settings + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true } - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); - + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ScrollArea::vertical() .id_salt("node_settings_scroll") .scroll_bar_visibility(ScrollBarVisibility::AlwaysHidden) @@ -135,6 +126,16 @@ impl NodeTab for NetworkSettings { } } +impl NodeTab for NetworkSettings { + fn get_type(&self) -> NodeTabType { + NodeTabType::Settings + } + + fn tab_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + self.ui(ui, cb); + } +} + impl NetworkSettings { /// Reminder to restart enabled node to show on edit setting at [`Modal`]. pub fn node_restart_required_ui(ui: &mut egui::Ui) { diff --git a/src/gui/views/network/setup/dandelion.rs b/src/gui/views/network/setup/dandelion.rs index 27b7b80c..24c608b8 100644 --- a/src/gui/views/network/setup/dandelion.rs +++ b/src/gui/views/network/setup/dandelion.rs @@ -16,7 +16,7 @@ use egui::{Id, RichText}; use crate::gui::icons::{CLOCK_COUNTDOWN, GRAPH, TIMER, WATCH}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::gui::views::{Modal, TextEdit, View}; use crate::gui::Colors; use crate::gui::views::network::NetworkSettings; @@ -35,9 +35,6 @@ 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`]. @@ -56,19 +53,18 @@ impl Default for DandelionSetup { embargo_edit: NodeConfig::get_reorg_cache_period(), aggregation_edit: NodeConfig::get_dandelion_aggregation(), 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 +impl ContentContainer for DandelionSetup { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + EPOCH_MODAL, + EMBARGO_MODAL, + AGGREGATION_MODAL, + STEM_PROBABILITY_MODAL + ] } fn modal_ui(&mut self, @@ -83,13 +79,12 @@ impl ModalContainer for DandelionSetup { _ => {} } } -} -impl DandelionSetup { - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", GRAPH, "Dandelion")); View::horizontal_line(ui, Colors::stroke()); ui.add_space(6.0); @@ -131,7 +126,9 @@ impl DandelionSetup { ui.add_space(6.0); }); } +} +impl DandelionSetup { /// Draw epoch duration setup content. fn epoch_ui(&mut self, ui: &mut egui::Ui) { ui.label(RichText::new(t!("network_settings.epoch_duration")) diff --git a/src/gui/views/network/setup/node.rs b/src/gui/views/network/setup/node.rs index 056514b4..b7735d19 100644 --- a/src/gui/views/network/setup/node.rs +++ b/src/gui/views/network/setup/node.rs @@ -19,7 +19,7 @@ use crate::gui::icons::{CLOCK_CLOCKWISE, COMPUTER_TOWER, PLUG, POWER, SHIELD, SH use crate::gui::platform::PlatformCallbacks; use crate::gui::views::network::settings::NetworkSettings; use crate::gui::views::network::NetworkContent; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::gui::views::{Modal, TextEdit, View}; use crate::gui::Colors; use crate::node::{Node, NodeConfig}; @@ -43,9 +43,6 @@ 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`]. @@ -68,19 +65,18 @@ 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 +impl ContentContainer for NodeSetup { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + API_PORT_MODAL, + API_SECRET_MODAL, + FOREIGN_API_SECRET_MODAL, + FTL_MODAL + ] } fn modal_ui(&mut self, @@ -95,13 +91,12 @@ impl ModalContainer for NodeSetup { _ => {} } } -} -impl NodeSetup { - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server"))); View::horizontal_line(ui, Colors::stroke()); ui.add_space(6.0); @@ -218,7 +213,9 @@ impl NodeSetup { self.archive_mode_ui(ui); }); } +} +impl NodeSetup { /// Draw [`ChainTypes`] setup content. pub fn chain_type_ui(ui: &mut egui::Ui) { ui.vertical_centered(|ui| { diff --git a/src/gui/views/network/setup/p2p.rs b/src/gui/views/network/setup/p2p.rs index e0b931ee..01a3c536 100644 --- a/src/gui/views/network/setup/p2p.rs +++ b/src/gui/views/network/setup/p2p.rs @@ -21,7 +21,7 @@ use crate::gui::icons::{ARROW_FAT_LINES_DOWN, ARROW_FAT_LINES_UP, GLOBE_SIMPLE, use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, TextEdit, View}; use crate::gui::views::network::settings::NetworkSettings; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::node::{Node, NodeConfig, PeersConfig}; /// Type of peer. @@ -65,9 +65,6 @@ pub struct P2PSetup { /// Flag to check if reset of peers was called. peers_reset: bool, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } /// Identifier for port value [`Modal`]. @@ -111,23 +108,22 @@ impl Default for P2PSetup { max_inbound_count: NodeConfig::get_max_inbound_peers(), max_outbound_count: NodeConfig::get_max_outbound_peers(), peers_reset: false, - 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 - ] } } } -impl ModalContainer for P2PSetup { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for P2PSetup { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + PORT_MODAL, + CUSTOM_SEED_MODAL, + ALLOW_PEER_MODAL, + DENY_PEER_MODAL, + PREFER_PEER_MODAL, + BAN_WINDOW_MODAL, + MAX_INBOUND_MODAL, + MAX_OUTBOUND_MODAL + ] } fn modal_ui(&mut self, @@ -146,16 +142,12 @@ impl ModalContainer for P2PSetup { _ => {} } } -} -impl P2PSetup { - /// Title for custom DNS Seeds setup section. - const DNS_SEEDS_TITLE: &'static str = "DNS Seeds"; - - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", HANDSHAKE, t!("network_settings.p2p_server"))); View::horizontal_line(ui, Colors::stroke()); ui.add_space(6.0); @@ -236,7 +228,12 @@ impl P2PSetup { } }); } +} +/// Title for custom DNS Seeds setup section. +const DNS_SEEDS_TITLE: &'static str = "DNS Seeds"; + +impl P2PSetup { /// Draw p2p port setup content. fn port_ui(&mut self, ui: &mut egui::Ui) { ui.label(RichText::new(t!("network_settings.p2p_port")) @@ -398,7 +395,7 @@ impl P2PSetup { PeerType::Allowed => t!("network_settings.allow_list"), PeerType::Denied => t!("network_settings.deny_list"), PeerType::Preferred => t!("network_settings.favourites"), - _ => Self::DNS_SEEDS_TITLE.to_string() + _ => DNS_SEEDS_TITLE.to_string() }; // Show modal to add peer. Modal::new(modal_id) @@ -483,8 +480,7 @@ impl P2PSetup { /// Draw seeding type setup content. fn seeding_type_ui(&mut self, ui: &mut egui::Ui) { - let title = Self::DNS_SEEDS_TITLE; - ui.label(RichText::new(title).size(16.0).color(Colors::gray())); + ui.label(RichText::new(DNS_SEEDS_TITLE).size(16.0).color(Colors::gray())); ui.add_space(2.0); let default_seeding = NodeConfig::is_default_seeding_type(); diff --git a/src/gui/views/network/setup/pool.rs b/src/gui/views/network/setup/pool.rs index 0d0ae10b..4fabcb7a 100644 --- a/src/gui/views/network/setup/pool.rs +++ b/src/gui/views/network/setup/pool.rs @@ -19,40 +19,33 @@ use crate::gui::icons::{BEZIER_CURVE, BOUNDING_BOX, CHART_SCATTER, CIRCLES_THREE use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, TextEdit, View}; use crate::gui::views::network::settings::NetworkSettings; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::node::NodeConfig; /// Memory pool setup section content. pub struct PoolSetup { /// Base fee value that's accepted into the pool. fee_base_edit: String, - /// Reorg cache retention period value in minutes. reorg_period_edit: String, - /// Maximum number of transactions allowed in the pool. pool_size_edit: String, - /// Maximum number of transactions allowed in the stempool. stempool_size_edit: String, - /// 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"; +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"; +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"; +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"; +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"; +const MAX_WEIGHT_MODAL: &'static str = "max_weight"; impl Default for PoolSetup { fn default() -> Self { @@ -62,20 +55,19 @@ 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 +impl ContentContainer for PoolSetup { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + FEE_BASE_MODAL, + REORG_PERIOD_MODAL, + POOL_SIZE_MODAL, + STEMPOOL_SIZE_MODAL, + MAX_WEIGHT_MODAL + ] } fn modal_ui(&mut self, @@ -91,13 +83,12 @@ impl ModalContainer for PoolSetup { _ => {} } } -} -impl PoolSetup { - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + fn container_ui(&mut self, ui: &mut egui::Ui, _: &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); @@ -135,7 +126,9 @@ impl PoolSetup { self.max_weight_ui(ui); }); } +} +impl PoolSetup { /// Draw fee base setup content. fn fee_base_ui(&mut self, ui: &mut egui::Ui) { ui.label(RichText::new(t!("network_settings.pool_fee")) diff --git a/src/gui/views/network/setup/stratum.rs b/src/gui/views/network/setup/stratum.rs index c02c1a3e..2c1030c8 100644 --- a/src/gui/views/network/setup/stratum.rs +++ b/src/gui/views/network/setup/stratum.rs @@ -20,7 +20,7 @@ use crate::gui::icons::{BARBELL, HARD_DRIVES, PLUG, POWER, TIMER}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, TextEdit, View}; use crate::gui::views::network::settings::NetworkSettings; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::gui::views::wallets::modals::WalletsModal; use crate::node::{Node, NodeConfig}; use crate::wallet::{WalletConfig, WalletList}; @@ -51,9 +51,6 @@ pub struct StratumSetup { /// Minimum share difficulty value to request from miners. min_share_diff_edit: String, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } /// Identifier for wallet selection [`Modal`]. @@ -91,19 +88,18 @@ impl Default for StratumSetup { wallet_name, attempt_time_edit: NodeConfig::get_stratum_attempt_time(), min_share_diff_edit: NodeConfig::get_stratum_min_share_diff(), - modal_ids: vec![ - WALLET_SELECTION_MODAL, - STRATUM_PORT_MODAL, - ATTEMPT_TIME_MODAL, - MIN_SHARE_DIFF_MODAL - ] } } } -impl ModalContainer for StratumSetup { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for StratumSetup { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + WALLET_SELECTION_MODAL, + STRATUM_PORT_MODAL, + ATTEMPT_TIME_MODAL, + MIN_SHARE_DIFF_MODAL + ] } fn modal_ui(&mut self, @@ -112,7 +108,7 @@ impl ModalContainer for StratumSetup { cb: &dyn PlatformCallbacks) { match modal.id { WALLET_SELECTION_MODAL => { - self.wallets_modal.ui(ui, modal, &mut self.wallets, cb, |wallet, _| { + self.wallets_modal.ui(ui, &mut self.wallets, |wallet, _| { let id = wallet.get_config().id; NodeConfig::save_stratum_wallet_id(id); self.wallet_name = WalletConfig::read_name_by_id(id); @@ -124,13 +120,12 @@ impl ModalContainer for StratumSetup { _ => {} } } -} -impl StratumSetup { - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { View::sub_title(ui, format!("{} {}", HARD_DRIVES, t!("network_mining.server"))); View::horizontal_line(ui, Colors::stroke()); ui.add_space(6.0); @@ -192,8 +187,8 @@ impl StratumSetup { View::button(ui, t!("network_settings.choose_wallet"), Colors::white_or_black(false), || { - self.show_wallets_modal(); - }); + self.show_wallets_modal(); + }); ui.add_space(12.0); if self.wallet_name.is_some() { @@ -242,7 +237,9 @@ impl StratumSetup { self.min_diff_ui(ui); }); } +} +impl StratumSetup { /// Show wallet selection [`Modal`]. fn show_wallets_modal(&mut self) { self.wallets_modal = WalletsModal::new(NodeConfig::get_stratum_wallet_id(), None, false); diff --git a/src/gui/views/network/types.rs b/src/gui/views/network/types.rs index 1d29eb7f..9f9f9a17 100644 --- a/src/gui/views/network/types.rs +++ b/src/gui/views/network/types.rs @@ -17,7 +17,7 @@ use crate::gui::platform::PlatformCallbacks; /// Integrated node tab content interface. pub trait NodeTab { fn get_type(&self) -> NodeTabType; - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks); + fn tab_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks); } /// Type of [`NodeTab`] content. diff --git a/src/gui/views/settings/content.rs b/src/gui/views/settings/content.rs index 9120870e..4c3ecc53 100644 --- a/src/gui/views/settings/content.rs +++ b/src/gui/views/settings/content.rs @@ -18,7 +18,9 @@ use egui::{Layout, RichText}; use crate::AppConfig; use crate::gui::Colors; use crate::gui::icons::{CHECK, CHECK_FAT}; +use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, View}; +use crate::gui::views::types::ContentContainer; /// Application settings content. pub struct SettingsContent { @@ -34,9 +36,19 @@ impl Default for SettingsContent { } } -impl SettingsContent { - /// Draw settings content. - pub fn ui(&mut self, ui: &mut egui::Ui) { +impl ContentContainer for SettingsContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![] + } + + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + } + + fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool { + true + } + + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.add_space(6.0); // Show theme selection. @@ -60,7 +72,9 @@ impl SettingsContent { Self::language_item_ui(locale, ui, index, locales.len()); } } +} +impl SettingsContent { /// Draw theme selection content. fn theme_selection_ui(ui: &mut egui::Ui) { ui.vertical_centered(|ui| { diff --git a/src/gui/views/types.rs b/src/gui/views/types.rs index fcaaf121..809e141a 100644 --- a/src/gui/views/types.rs +++ b/src/gui/views/types.rs @@ -51,28 +51,27 @@ pub struct ModalState { pub modal: Option, } -/// Contains identifiers to draw opened [`Modal`] content for current ui container. -pub trait ModalContainer { +/// Content container to simplify modals management and navigation. +pub trait ContentContainer { /// 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, - 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, - 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, modal, cb); - }); + fn modal_ids(&self) -> Vec<&'static str>; + /// Draw modal content. + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks); + /// Handle back key action, return `false` when consumed inside container. + fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool; + /// Draw container content. + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks); + /// Draw content, to call by parent container. + fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + // Draw modal content. + if let Some(id) = Modal::opened() { + if self.modal_ids().contains(&id) { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { + self.modal_ui(ui, modal, cb); + }); + } } + self.container_ui(ui, cb); } } diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index 710e80d8..ce39c2c1 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -12,24 +12,24 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::Duration; -use egui::{Align, CornerRadius, Id, Layout, Margin, RichText, ScrollArea, StrokeKind}; use egui::scroll_area::ScrollBarVisibility; +use egui::{Align, CornerRadius, Id, Layout, Margin, RichText, ScrollArea, StrokeKind}; +use std::time::Duration; -use crate::AppConfig; -use crate::gui::Colors; use crate::gui::icons::{ARROW_LEFT, CARET_RIGHT, COMPUTER_TOWER, FOLDER_OPEN, FOLDER_PLUS, GEAR, GLOBE, GLOBE_SIMPLE, LOCK_KEY, PLUS, SIDEBAR_SIMPLE, SUITCASE}; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, Content, TitlePanel, View}; use crate::gui::views::settings::SettingsContent; -use crate::gui::views::types::{ModalContainer, ModalPosition, LinePosition, TitleContentType, TitleType}; -use crate::gui::views::wallets::creation::WalletCreation; +use crate::gui::views::types::{ContentContainer, LinePosition, ModalPosition, TitleContentType, TitleType}; +use crate::gui::views::wallets::creation::WalletCreationContent; use crate::gui::views::wallets::modals::{AddWalletModal, OpenWalletModal, WalletConnectionModal, WalletsModal}; use crate::gui::views::wallets::types::WalletTabType; use crate::gui::views::wallets::wallet::types::wallet_status_text; use crate::gui::views::wallets::WalletContent; -use crate::wallet::{ExternalConnection, Wallet, WalletList}; +use crate::gui::views::{Content, Modal, TitlePanel, View}; +use crate::gui::Colors; use crate::wallet::types::ConnectionMethod; +use crate::wallet::{ExternalConnection, Wallet, WalletList}; +use crate::AppConfig; /// Wallets content. pub struct WalletsContent { @@ -37,27 +37,21 @@ pub struct WalletsContent { wallets: WalletList, /// Initial wallet creation [`Modal`] content. - add_wallet_modal_content: Option, + add_wallet_modal_content: AddWalletModal, /// Wallet opening [`Modal`] content. - open_wallet_content: Option, - /// Wallet connection selection content. - conn_selection_content: Option, + open_wallet_content: OpenWalletModal, + /// Wallet connection selection [`Modal`] content. + conn_selection_content: WalletConnectionModal, /// Wallet selection [`Modal`] content. - wallet_selection_content: Option, + wallet_selection_content: WalletsModal, /// Selected [`Wallet`] content. wallet_content: Option, /// Wallet creation content. - creation_content: Option, + creation_content: Option, /// Settings content. settings_content: Option, - - /// Flag to show [`Wallet`] list at dual panel mode. - show_wallets_at_dual_panel: bool, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } const ADD_WALLET_MODAL: &'static str = "wallets_add_modal"; @@ -69,97 +63,121 @@ impl Default for WalletsContent { fn default() -> Self { Self { wallets: WalletList::default(), - wallet_selection_content: None, - open_wallet_content: None, - conn_selection_content: None, + wallet_selection_content: WalletsModal::new(None, None, true), + open_wallet_content: OpenWalletModal::new(None), + add_wallet_modal_content: AddWalletModal::default(), + conn_selection_content: WalletConnectionModal::new(ConnectionMethod::Integrated), wallet_content: None, creation_content: None, settings_content: None, - show_wallets_at_dual_panel: AppConfig::show_wallets_at_dual_panel(), - modal_ids: vec![ - ADD_WALLET_MODAL, - OPEN_WALLET_MODAL, - SELECT_CONNECTION_MODAL, - SELECT_WALLET_MODAL, - ], - add_wallet_modal_content: None, } } } -impl ModalContainer for WalletsContent { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for WalletsContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + ADD_WALLET_MODAL, + OPEN_WALLET_MODAL, + SELECT_CONNECTION_MODAL, + SELECT_WALLET_MODAL, + ] } - fn modal_ui(&mut self, - ui: &mut egui::Ui, - modal: &Modal, - cb: &dyn PlatformCallbacks) { + fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { match modal.id { ADD_WALLET_MODAL => { - if let Some(content) = self.add_wallet_modal_content.as_mut() { - content.ui(ui, modal, cb, |name, pass| { - self.creation_content = Some( - WalletCreation::new(name.clone(), pass.clone()) - ); - }); - } - if self.creating_wallet() { - self.add_wallet_modal_content = None; - } + self.add_wallet_modal_content.ui(ui, modal, cb, |name, pass| { + self.creation_content = Some( + WalletCreationContent::new(name.clone(), pass.clone()) + ); + }); }, OPEN_WALLET_MODAL => { - let mut open = false; - if let Some(open_content) = self.open_wallet_content.as_mut() { - open_content.ui(ui, modal, cb, |wallet, data| { - self.wallet_content = Some(WalletContent::new(wallet, data)); - open = true; - }); - } - if open { - self.open_wallet_content = None; - } + self.open_wallet_content.ui(ui, modal, cb, |pass, _| { + if let Some(content) = &self.wallet_content { + return match content.wallet.open(pass) { + Ok(_) => true, + Err(_) => false + }; + } + true + }); }, SELECT_CONNECTION_MODAL => { - if let Some(content) = self.conn_selection_content.as_mut() { - content.ui(ui, modal, cb, |conn| { - if let Some(wallet_content) = &self.wallet_content { - wallet_content.wallet.update_connection(&conn); - } - }); - } + self.conn_selection_content.ui(ui, modal, cb, |conn| { + if let Some(wallet_content) = &self.wallet_content { + wallet_content.wallet.update_connection(&conn); + } + }); } SELECT_WALLET_MODAL => { - let mut select = false; - if let Some(content) = self.wallet_selection_content.as_mut() { - content.ui(ui, modal, &mut self.wallets, cb, |wallet, data| { - self.wallet_content = Some(WalletContent::new(wallet, data)); - select = true; - }); - } - if select { - self.wallet_selection_content = None; - } + self.wallet_selection_content.ui(ui, &mut self.wallets, |wallet, data| { + if !wallet.is_open() { + self.open_wallet_content = OpenWalletModal::new(data.clone()); + Modal::new(OPEN_WALLET_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.open")) + .show(); + } + self.wallet_content = Some(WalletContent::new(wallet, data)); + }); } _ => {} } } + + fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool { + if self.showing_settings() { + // Close settings. + self.settings_content = None; + return false; + } else if self.creating_wallet() { + // Close wallet creation. + let creation = self.creation_content.as_mut().unwrap(); + if creation.on_back() { + self.creation_content = None; + } + return false; + } else { + if self.showing_wallet() { + let content = self.wallet_content.as_mut().unwrap(); + // Close opened QR code scanner. + if content.qr_scan_content.is_some() { + cb.stop_camera(); + content.qr_scan_content = None; + return false; + } + // Close opened wallet. + self.wallet_content = None; + return false; + } + } + true + } + + fn container_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + self.content_ui(ui, cb); + } } impl WalletsContent { /// Draw wallets content. - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - self.current_modal_ui(ui, cb); + fn content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + if let Some(data) = crate::consume_incoming_data() { + if !data.is_empty() { + self.on_data(ui, Some(data)); + } + } let showing_settings = self.showing_settings(); let creating_wallet = self.creating_wallet(); let showing_wallet = self.showing_wallet() && !creating_wallet && !showing_settings; - let dual_panel = Self::is_dual_panel_mode(ui); + let dual_panel = is_dual_panel_mode(ui); let content_width = ui.available_width(); let list_hidden = showing_settings || creating_wallet || self.wallets.list().is_empty() || (showing_wallet && self.wallet_content.as_ref().unwrap().qr_scan_content.is_some()) - || (dual_panel && showing_wallet && !self.show_wallets_at_dual_panel) + || (dual_panel && showing_wallet && !AppConfig::show_wallets_at_dual_panel()) || (!dual_panel && showing_wallet); // Show title panel. @@ -262,7 +280,7 @@ impl WalletsContent { if self.showing_settings() { if let Some(c) = &mut self.settings_content { View::max_width_ui(ui, Content::SIDE_PANEL_WIDTH * 1.3, |ui| { - c.ui(ui); + c.ui(ui, cb); }); } } else if self.creating_wallet() { @@ -270,7 +288,7 @@ impl WalletsContent { let pass = creation.pass.clone(); let mut created = false; // Show wallet creation content. - creation.ui(ui, cb, |wallet| { + creation.content_ui(ui, cb, |wallet| { self.wallets.add(wallet.clone()); if let Ok(_) = wallet.open(pass.clone()) { self.wallet_content = Some(WalletContent::new(wallet, None)); @@ -324,7 +342,7 @@ impl WalletsContent { } /// Handle data from deeplink or opened file. - pub fn on_data(&mut self, ui: &mut egui::Ui, data: Option) { + fn on_data(&mut self, ui: &mut egui::Ui, data: Option) { let wallets_size = self.wallets.list().len(); if wallets_size == 0 { return; @@ -348,7 +366,7 @@ impl WalletsContent { /// Show initial wallet creation [`Modal`]. pub fn show_add_wallet_modal(&mut self) { - self.add_wallet_modal_content = Some(AddWalletModal::default()); + self.add_wallet_modal_content = AddWalletModal::default(); Modal::new(ADD_WALLET_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.add")) @@ -357,50 +375,20 @@ impl WalletsContent { /// Show wallet selection with provided optional data. fn show_wallet_selection_modal(&mut self, data: Option) { - self.wallet_selection_content = Some(WalletsModal::new(None, data, true)); + self.wallet_selection_content = WalletsModal::new(None, data, true); Modal::new(SELECT_WALLET_MODAL) .position(ModalPosition::Center) .title(t!("network_settings.choose_wallet")) .show(); } - /// Handle Back key event returning `false` when event was handled. - pub fn on_back(&mut self, cb: &dyn PlatformCallbacks) -> bool { - if self.showing_settings() { - // Close settings. - self.settings_content = None; - return false; - } else if self.creating_wallet() { - // Close wallet creation. - let creation = self.creation_content.as_mut().unwrap(); - if creation.on_back() { - self.creation_content = None; - } - return false; - } else { - if self.showing_wallet() { - let content = self.wallet_content.as_mut().unwrap(); - // Close opened QR code scanner. - if content.qr_scan_content.is_some() { - cb.stop_camera(); - content.qr_scan_content = None; - return false; - } - // Close opened wallet. - self.wallet_content = None; - return false; - } - } - true - } - /// Draw [`TitlePanel`] content. fn title_ui(&mut self, ui: &mut egui::Ui, dual_panel: bool, show_wallet: bool, cb: &dyn PlatformCallbacks) { - let show_list = self.show_wallets_at_dual_panel; + let show_list = AppConfig::show_wallets_at_dual_panel(); let showing_settings = self.showing_settings(); let creating_wallet = self.creating_wallet(); let qr_scan = { @@ -501,7 +489,6 @@ impl WalletsContent { SUITCASE }; View::title_button_big(ui, list_icon, |_| { - self.show_wallets_at_dual_panel = !show_list; AppConfig::toggle_show_wallets_at_dual_panel(); }); } @@ -586,9 +573,8 @@ impl WalletsContent { let mut show_selection = false; View::item_button(ui, CornerRadius::default(), GLOBE, None, || { self.wallet_content = Some(WalletContent::new(wallet.clone(), None)); - self.conn_selection_content = Some( - WalletConnectionModal::new(wallet.get_current_connection()) - ); + self.conn_selection_content = + WalletConnectionModal::new(wallet.get_current_connection()); // Show connection selection modal. Modal::new(SELECT_CONNECTION_MODAL) .position(ModalPosition::CenterTop) @@ -657,17 +643,17 @@ impl WalletsContent { /// Show [`Modal`] to select and open wallet. fn show_opening_modal(&mut self, wallet: Wallet, data: Option) { self.wallet_content = Some(WalletContent::new(wallet.clone(), None)); - self.open_wallet_content = Some(OpenWalletModal::new(wallet, data)); + self.open_wallet_content = OpenWalletModal::new(data); Modal::new(OPEN_WALLET_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.open")) .show(); } +} - /// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time. - fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool { - let dual_panel_root = Content::is_dual_panel_mode(ui.ctx()); - let max_width = ui.available_width(); - dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset() - } +/// Check if it's possible to show [`WalletsContent`] and [`WalletContent`] panels at same time. +fn is_dual_panel_mode(ui: &mut egui::Ui) -> bool { + let dual_panel_root = Content::is_dual_panel_mode(ui.ctx()); + let max_width = ui.available_width(); + dual_panel_root && max_width >= (Content::SIDE_PANEL_WIDTH * 2.0) + View::get_right_inset() } \ No newline at end of file diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/content.rs similarity index 94% rename from src/gui/views/wallets/creation/creation.rs rename to src/gui/views/wallets/creation/content.rs index 8c4db277..2d72150d 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/content.rs @@ -20,7 +20,7 @@ use crate::gui::Colors; use crate::gui::icons::{CHECK, CLIPBOARD_TEXT, COPY, SCAN}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, View, CameraScanModal}; -use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition, QrScanResult}; +use crate::gui::views::types::{LinePosition, ContentContainer, ModalPosition, QrScanResult}; use crate::gui::views::wallets::creation::MnemonicSetup; use crate::gui::views::wallets::creation::types::Step; use crate::gui::views::wallets::ConnectionSettings; @@ -29,7 +29,7 @@ use crate::wallet::{ExternalConnection, Wallet}; use crate::wallet::types::PhraseMode; /// Wallet creation content. -pub struct WalletCreation { +pub struct WalletCreationContent { /// Wallet name. pub name: String, /// Wallet password. @@ -48,16 +48,15 @@ pub struct WalletCreation { /// Flag to check if an error occurred during wallet creation. creation_error: Option, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } const QR_CODE_PHRASE_SCAN_MODAL: &'static str = "qr_code_rec_phrase_scan_modal"; -impl ModalContainer for WalletCreation { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for WalletCreationContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + QR_CODE_PHRASE_SCAN_MODAL + ] } fn modal_ui(&mut self, @@ -86,10 +85,16 @@ impl ModalContainer for WalletCreation { _ => {} } } + + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + + fn container_ui(&mut self, _: &mut egui::Ui, _: &dyn PlatformCallbacks) {} } -impl WalletCreation { - /// Create new wallet creation instance from name and password. +impl WalletCreationContent { + /// Create new wallet creation content from name and password. pub fn new(name: String, pass: ZeroingString) -> Self { Self { name, @@ -99,19 +104,15 @@ impl WalletCreation { mnemonic_setup: MnemonicSetup::default(), network_setup: ConnectionSettings::default(), creation_error: None, - modal_ids: vec![ - QR_CODE_PHRASE_SCAN_MODAL - ], } } /// Draw wallet creation content. - pub fn ui(&mut self, - ui: &mut egui::Ui, - cb: &dyn PlatformCallbacks, - on_create: impl FnMut(Wallet)) { - self.current_modal_ui(ui, cb); - + pub fn content_ui(&mut self, + ui: &mut egui::Ui, + cb: &dyn PlatformCallbacks, + on_create: impl FnMut(Wallet)) { + self.ui(ui, cb); egui::TopBottomPanel::bottom("wallet_creation_step_panel") .frame(egui::Frame { inner_margin: Margin { @@ -350,14 +351,14 @@ impl WalletCreation { /// Draw wallet creation [`Step`] content. fn step_content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { match &self.step { - Step::EnterMnemonic => self.mnemonic_setup.ui(ui, cb), + Step::EnterMnemonic => self.mnemonic_setup.import_ui(ui, cb), Step::ConfirmMnemonic => self.mnemonic_setup.confirm_ui(ui, cb), Step::SetupConnection => { // Redraw if node is running. if Node::is_running() && !Content::is_dual_panel_mode(ui.ctx()) { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); } - self.network_setup.create_ui(ui, cb) + self.network_setup.content_ui(ui, cb); } } } diff --git a/src/gui/views/wallets/creation/mnemonic.rs b/src/gui/views/wallets/creation/mnemonic.rs index c7d7da12..2e1aaf92 100644 --- a/src/gui/views/wallets/creation/mnemonic.rs +++ b/src/gui/views/wallets/creation/mnemonic.rs @@ -18,7 +18,7 @@ use crate::gui::Colors; use crate::gui::icons::PENCIL; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, View, TextEdit}; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::wallet::Mnemonic; use crate::wallet::types::{PhraseMode, PhraseSize, PhraseWord}; @@ -33,9 +33,6 @@ pub struct MnemonicSetup { word_edit: String, /// Flag to check if entered word is valid at [`Modal`]. valid_word_edit: bool, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } /// Identifier for word input [`Modal`]. @@ -48,16 +45,15 @@ impl Default for MnemonicSetup { word_index_edit: 0, word_edit: String::from(""), valid_word_edit: true, - modal_ids: vec![ - WORD_INPUT_MODAL - ] } } } -impl ModalContainer for MnemonicSetup { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for MnemonicSetup { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + WORD_INPUT_MODAL + ] } fn modal_ui(&mut self, @@ -69,14 +65,19 @@ impl ModalContainer for MnemonicSetup { _ => {} } } + + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + + fn container_ui(&mut self, ui: &mut egui::Ui, _: &dyn PlatformCallbacks) { + + } } impl MnemonicSetup { - /// Draw content for phrase input step. - pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); - + /// Draw content for phrase import step. + pub fn import_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.add_space(10.0); // Show mode and type setup. @@ -89,12 +90,8 @@ impl MnemonicSetup { // Show words setup. self.word_list_ui(ui, self.mnemonic.mode() == PhraseMode::Import); } - /// Draw content for phrase confirmation step. pub fn confirm_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); - ui.add_space(4.0); ui.vertical_centered(|ui| { let text = format!("{}:", t!("wallets.recovery_phrase")); diff --git a/src/gui/views/wallets/creation/mod.rs b/src/gui/views/wallets/creation/mod.rs index 05ebd278..4f021b63 100644 --- a/src/gui/views/wallets/creation/mod.rs +++ b/src/gui/views/wallets/creation/mod.rs @@ -15,7 +15,7 @@ mod mnemonic; pub use mnemonic::MnemonicSetup; -mod creation; -pub use creation::WalletCreation; +mod content; +pub use content::WalletCreationContent; pub mod types; \ No newline at end of file diff --git a/src/gui/views/wallets/modals/open.rs b/src/gui/views/wallets/modals/open.rs index 85bd946a..4a117691 100644 --- a/src/gui/views/wallets/modals/open.rs +++ b/src/gui/views/wallets/modals/open.rs @@ -18,13 +18,9 @@ use grin_util::ZeroingString; use crate::gui::Colors; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, TextEdit, View}; -use crate::wallet::Wallet; /// Wallet opening [`Modal`] content. pub struct OpenWalletModal { - /// Wallet to open. - wallet: Wallet, - /// Password to open wallet. pass_edit: String, /// Flag to check if wrong password was entered. @@ -36,9 +32,8 @@ pub struct OpenWalletModal { impl OpenWalletModal { /// Create new content instance. - pub fn new(wallet: Wallet, data: Option) -> Self { + pub fn new(data: Option) -> Self { Self { - wallet, pass_edit: "".to_string(), wrong_pass: false, data, @@ -49,20 +44,17 @@ impl OpenWalletModal { ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks, - mut on_continue: impl FnMut(Wallet, Option)) { + mut on_continue: impl FnMut(ZeroingString, &Option) -> bool) { // Callback for button to continue. let mut on_continue = |m: &mut OpenWalletModal| { let pass = m.pass_edit.clone(); if pass.is_empty() { return; } - match m.wallet.open(ZeroingString::from(pass)) { - Ok(_) => { - m.pass_edit = "".to_string(); - Modal::close(); - on_continue(m.wallet.clone(), m.data.clone()); - } - Err(_) => m.wrong_pass = true + m.wrong_pass = !on_continue(ZeroingString::from(pass), &m.data); + if !m.wrong_pass { + m.pass_edit = "".to_string(); + Modal::close(); } }; diff --git a/src/gui/views/wallets/modals/wallets.rs b/src/gui/views/wallets/modals/wallets.rs index 80f812c2..606040f2 100644 --- a/src/gui/views/wallets/modals/wallets.rs +++ b/src/gui/views/wallets/modals/wallets.rs @@ -15,15 +15,12 @@ use egui::scroll_area::ScrollBarVisibility; use egui::{Align, Layout, RichText, ScrollArea, StrokeKind}; -use crate::gui::Colors; use crate::gui::icons::{CHECK, CHECK_FAT, COMPUTER_TOWER, FOLDER_OPEN, GLOBE_SIMPLE, PLUGS_CONNECTED}; -use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, View}; -use crate::gui::views::types::ModalPosition; -use crate::gui::views::wallets::modals::OpenWalletModal; use crate::gui::views::wallets::wallet::types::wallet_status_text; -use crate::wallet::{Wallet, WalletList}; +use crate::gui::views::{Modal, View}; +use crate::gui::Colors; use crate::wallet::types::ConnectionMethod; +use crate::wallet::{Wallet, WalletList}; /// Wallet list [`Modal`] content pub struct WalletsModal { @@ -35,32 +32,19 @@ pub struct WalletsModal { /// Flag to check if wallet can be opened from the list. can_open: bool, - /// Wallet opening content. - open_wallet_content: Option, } impl WalletsModal { /// Create new content instance. pub fn new(selected_id: Option, data: Option, can_open: bool) -> Self { - Self { selected_id, data, can_open, open_wallet_content: None } + Self { selected_id, data, can_open } } /// Draw content. pub fn ui(&mut self, ui: &mut egui::Ui, - modal: &Modal, wallets: &WalletList, - cb: &dyn PlatformCallbacks, mut on_select: impl FnMut(Wallet, Option)) { - // Draw wallet opening modal content. - if let Some(open_content) = self.open_wallet_content.as_mut() { - open_content.ui(ui, modal, cb, |wallet, data| { - on_select(wallet, data); - self.data = None; - }); - return; - } - ui.add_space(4.0); ScrollArea::vertical() .max_height(373.0) @@ -123,14 +107,7 @@ impl WalletsModal { FOLDER_OPEN }; View::item_button(ui, View::item_rounding(0, 1, true), icon, None, || { - if wallet.is_open() { - on_select(); - } else { - Modal::change_position(ModalPosition::CenterTop); - self.open_wallet_content = Some( - OpenWalletModal::new(wallet.clone(), self.data.clone()) - ); - } + on_select(); }); } else { // Draw button to select wallet. diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index 83244d22..47e5a24f 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -23,7 +23,7 @@ use crate::gui::Colors; use crate::gui::icons::{ARROWS_CLOCKWISE, BRIDGE, CAMERA_ROTATE, CHAT_CIRCLE_TEXT, FOLDER_USER, GEAR_FINE, GRAPH, PACKAGE, POWER, SCAN, SPINNER, USERS_THREE}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, Content, View, CameraContent}; -use crate::gui::views::types::{LinePosition, ModalContainer, ModalPosition, QrScanResult}; +use crate::gui::views::types::{LinePosition, ContentContainer, ModalPosition, QrScanResult}; use crate::gui::views::wallets::{WalletTransactions, WalletMessages, WalletTransport}; use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::modals::WalletAccountsModal; @@ -40,48 +40,47 @@ pub struct WalletContent { pub current_tab: Box, /// Wallet accounts [`Modal`] content. - accounts_modal_content: Option, + accounts_modal_content: WalletAccountsModal, /// QR code scan content. pub qr_scan_content: Option, - - /// List of allowed [`Modal`] ids for this [`ModalContainer`]. - allowed_modal_ids: Vec<&'static str> } /// Identifier for account list [`Modal`]. const ACCOUNT_LIST_MODAL: &'static str = "account_list_modal"; -impl ModalContainer for WalletContent { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.allowed_modal_ids +impl ContentContainer for WalletContent { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + ACCOUNT_LIST_MODAL, + ] } fn modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { match modal.id { ACCOUNT_LIST_MODAL => { - if let Some(content) = self.accounts_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { - content.ui(ui, &self.wallet, modal, cb); - }); - } + self.accounts_modal_content.ui(ui, &self.wallet, modal, cb); } _ => {} } } + + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + + fn container_ui(&mut self, _: &mut egui::Ui, _: &dyn PlatformCallbacks) {} } impl WalletContent { /// Create new instance with optional data. pub fn new(wallet: Wallet, data: Option) -> Self { + let accounts_modal = WalletAccountsModal::new(wallet.accounts()); let mut content = Self { wallet, - accounts_modal_content: None, + accounts_modal_content: accounts_modal, qr_scan_content: None, current_tab: Box::new(WalletTransactions::default()), - allowed_modal_ids: vec![ - ACCOUNT_LIST_MODAL, - ], }; if data.is_some() { content.on_data(data); @@ -97,7 +96,6 @@ impl WalletContent { /// Draw wallet content. pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ui.ctx().request_repaint_after(Duration::from_millis(1000)); - self.current_modal_ui(ui, cb); let dual_panel = Content::is_dual_panel_mode(ui.ctx()); let show_wallets_dual = AppConfig::show_wallets_at_dual_panel(); @@ -302,9 +300,7 @@ impl WalletContent { // Draw button to show list of accounts. View::item_button(ui, View::item_rounding(1, 3, true), USERS_THREE, None, || { - self.accounts_modal_content = Some( - WalletAccountsModal::new(self.wallet.accounts()) - ); + self.accounts_modal_content = WalletAccountsModal::new(self.wallet.accounts()); Modal::new(ACCOUNT_LIST_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.accounts")) diff --git a/src/gui/views/wallets/wallet/messages/content.rs b/src/gui/views/wallets/wallet/messages/content.rs index 68521c60..1df6c3f6 100644 --- a/src/gui/views/wallets/wallet/messages/content.rs +++ b/src/gui/views/wallets/wallet/messages/content.rs @@ -37,10 +37,10 @@ pub struct WalletMessages { first_draw: bool, /// Invoice or sending request creation [`Modal`] content. - request_modal_content: Option, + request_modal_content: MessageRequestModal, /// Wallet transaction [`Modal`] content. - tx_info_content: Option, + tx_info_content: WalletTransactionModal, /// Slatepacks message input text. message_edit: String, @@ -85,8 +85,8 @@ impl WalletMessages { message_loading: false, message_error: "".to_string(), message_result: Arc::new(Default::default()), - tx_info_content: None, - request_modal_content: None, + tx_info_content: WalletTransactionModal::new(None, false), + request_modal_content: MessageRequestModal::new(false), file_pick_button: FilePickButton::default(), scan_modal_content: None, } @@ -129,23 +129,19 @@ impl WalletMessages { Some(id) => { match id { REQUEST_MODAL => { - if let Some(content) = self.request_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { - content.ui(ui, wallet, modal, cb); - }); - } + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { + self.request_modal_content.ui(ui, wallet, modal, cb); + }); } TX_INFO_MODAL => { - if let Some(content) = self.tx_info_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { - content.ui(ui, wallet, modal, cb); - }); - } + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { + self.tx_info_content.ui(ui, wallet, modal, cb); + }); } SCAN_QR_MODAL => { let mut result = None; if let Some(content) = self.scan_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, _| { + Modal::ui(ui.ctx(), cb, |ui, _, cb| { content.ui(ui, cb, |res| { result = Some(res.clone()); Modal::close(); @@ -217,7 +213,7 @@ impl WalletMessages { /// Show [`Modal`] to create invoice or sending request. fn show_request_modal(&mut self, invoice: bool) { - self.request_modal_content = Some(MessageRequestModal::new(invoice)); + self.request_modal_content = MessageRequestModal::new(invoice); let title = if invoice { t!("wallets.receive") } else { @@ -299,7 +295,7 @@ impl WalletMessages { Ok(tx) => { self.message_edit.clear(); // Show transaction modal on success. - self.tx_info_content = Some(WalletTransactionModal::new(wallet, tx, false)); + self.tx_info_content = WalletTransactionModal::new(Some(tx.data.id), false); Modal::new(TX_INFO_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.tx")) @@ -322,9 +318,8 @@ impl WalletMessages { // Show tx modal or show default error message. if let Some(tx) = wallet.tx_by_slate(&slate).as_ref() { self.message_edit.clear(); - self.tx_info_content = Some( - WalletTransactionModal::new(wallet, tx, false) - ); + self.tx_info_content = + WalletTransactionModal::new(Some(tx.data.id), false); Modal::new(TX_INFO_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.tx")) @@ -427,7 +422,7 @@ impl WalletMessages { if exists { if let Some(tx) = wallet.tx_by_slate(&slate).as_ref() { self.message_edit.clear(); - self.tx_info_content = Some(WalletTransactionModal::new(wallet, tx, false)); + self.tx_info_content = WalletTransactionModal::new(Some(tx.data.id), false); Modal::new(TX_INFO_MODAL) .position(ModalPosition::CenterTop) .title(t!("wallets.tx")) diff --git a/src/gui/views/wallets/wallet/messages/request.rs b/src/gui/views/wallets/wallet/messages/request.rs index 98913d9c..31f6b385 100644 --- a/src/gui/views/wallets/wallet/messages/request.rs +++ b/src/gui/views/wallets/wallet/messages/request.rs @@ -229,7 +229,8 @@ impl MessageRequestModal { let result = r_request.as_ref().unwrap(); match result { Ok(tx) => { - self.result_tx_content = Some(WalletTransactionModal::new(wallet, tx, false)); + self.result_tx_content = + Some(WalletTransactionModal::new(Some(tx.data.id), false)); } Err(err) => { match err { diff --git a/src/gui/views/wallets/wallet/settings/common.rs b/src/gui/views/wallets/wallet/settings/common.rs index 02720851..5ac1550a 100644 --- a/src/gui/views/wallets/wallet/settings/common.rs +++ b/src/gui/views/wallets/wallet/settings/common.rs @@ -146,17 +146,17 @@ impl CommonSettings { Some(id) => { match id { NAME_EDIT_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { self.name_modal_ui(ui, wallet, modal, cb); }); } PASS_EDIT_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { self.pass_modal_ui(ui, wallet, modal, cb); }); } MIN_CONFIRMATIONS_EDIT_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { self.min_conf_modal_ui(ui, wallet, modal, cb); }); } diff --git a/src/gui/views/wallets/wallet/settings/connection.rs b/src/gui/views/wallets/wallet/settings/connection.rs index 3d53644d..b96f094d 100644 --- a/src/gui/views/wallets/wallet/settings/connection.rs +++ b/src/gui/views/wallets/wallet/settings/connection.rs @@ -20,7 +20,7 @@ use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, View}; use crate::gui::views::network::ConnectionsContent; use crate::gui::views::network::modals::ExternalConnectionModal; -use crate::gui::views::types::{ModalContainer, ModalPosition}; +use crate::gui::views::types::{ContentContainer, ModalPosition}; use crate::wallet::{ConnectionsConfig, ExternalConnection, Wallet}; use crate::wallet::types::ConnectionMethod; @@ -31,9 +31,6 @@ pub struct ConnectionSettings { /// External connection [`Modal`] content. ext_conn_modal: ExternalConnectionModal, - - /// [`Modal`] identifiers allowed at this ui container. - modal_ids: Vec<&'static str> } impl Default for ConnectionSettings { @@ -41,16 +38,15 @@ impl Default for ConnectionSettings { Self { method: ConnectionMethod::Integrated, ext_conn_modal: ExternalConnectionModal::new(None), - modal_ids: vec![ - ExternalConnectionModal::WALLET_ID - ] } } } -impl ModalContainer for ConnectionSettings { - fn modal_ids(&self) -> &Vec<&'static str> { - &self.modal_ids +impl ContentContainer for ConnectionSettings { + fn modal_ids(&self) -> Vec<&'static str> { + vec![ + ExternalConnectionModal::WALLET_ID + ] } fn modal_ui(&mut self, @@ -64,20 +60,21 @@ impl ModalContainer for ConnectionSettings { _ => {} } } + + fn on_back(&mut self, _: &dyn PlatformCallbacks) -> bool { + true + } + + fn container_ui(&mut self, _: &mut egui::Ui, _: &dyn PlatformCallbacks) {} } impl ConnectionSettings { - /// Draw wallet creation setup content. - pub fn create_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - self.ui(ui, cb); - } - /// Draw existing wallet connection setup content. pub fn wallet_ui(&mut self, ui: &mut egui::Ui, wallet: &Wallet, cb: &dyn PlatformCallbacks) { self.method = wallet.get_current_connection(); // Draw setup content. - let changed = self.ui(ui, cb); + let changed = self.content_ui(ui, cb); if changed { wallet.update_connection(&self.method); @@ -90,10 +87,9 @@ impl ConnectionSettings { } /// Draw connection setup content, returning `true` if connection was changed. - fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) -> bool { + pub fn content_ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) -> bool { + self.ui(ui, cb); let mut changed = false; - // Draw modal content for current ui container. - self.current_modal_ui(ui, cb); ui.add_space(2.0); View::sub_title(ui, format!("{} {}", GLOBE, t!("wallets.conn_method"))); diff --git a/src/gui/views/wallets/wallet/settings/recovery.rs b/src/gui/views/wallets/wallet/settings/recovery.rs index 9027b0c6..8ff30d1b 100644 --- a/src/gui/views/wallets/wallet/settings/recovery.rs +++ b/src/gui/views/wallets/wallet/settings/recovery.rs @@ -148,12 +148,12 @@ impl RecoverySettings { Some(id) => { match id { RECOVERY_PHRASE_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { self.recovery_phrase_modal_ui(ui, wallet, modal, cb); }); } DELETE_CONFIRMATION_MODAL => { - Modal::ui(ui.ctx(), |ui, _| { + Modal::ui(ui.ctx(), cb, |ui, _, _| { self.deletion_modal_ui(ui, wallet); }); } diff --git a/src/gui/views/wallets/wallet/transport/content.rs b/src/gui/views/wallets/wallet/transport/content.rs index 94154c07..5809c2e4 100644 --- a/src/gui/views/wallets/wallet/transport/content.rs +++ b/src/gui/views/wallets/wallet/transport/content.rs @@ -96,20 +96,20 @@ impl WalletTransport { match id { SEND_TOR_MODAL => { if let Some(content) = self.send_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { content.ui(ui, wallet, modal, cb); }); } } TOR_SETTINGS_MODAL => { if let Some(content) = self.settings_modal_content.as_mut() { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { content.ui(ui, wallet, modal, cb); }); } } QR_ADDRESS_MODAL => { - Modal::ui(ui.ctx(), |ui, _| { + Modal::ui(ui.ctx(), cb, |ui, _, cb| { self.qr_address_modal_ui(ui, cb); }); } diff --git a/src/gui/views/wallets/wallet/transport/send.rs b/src/gui/views/wallets/wallet/transport/send.rs index 5eab4474..0dc1707b 100644 --- a/src/gui/views/wallets/wallet/transport/send.rs +++ b/src/gui/views/wallets/wallet/transport/send.rs @@ -342,7 +342,7 @@ impl TransportSendModal { match res { Ok(tx) => { self.tx_info_content = - Some(WalletTransactionModal::new(wallet, &tx, false)); + Some(WalletTransactionModal::new(Some(tx.data.id), false)); } Err(_) => { self.error = true; diff --git a/src/gui/views/wallets/wallet/txs/content.rs b/src/gui/views/wallets/wallet/txs/content.rs index 0e6636e5..dc1d1860 100644 --- a/src/gui/views/wallets/wallet/txs/content.rs +++ b/src/gui/views/wallets/wallet/txs/content.rs @@ -165,7 +165,7 @@ impl WalletTransactions { r.nw = 0.0 as u8; r.sw = 0.0 as u8; View::item_button(ui, r, FILE_TEXT, None, || { - self.show_tx_info_modal(wallet, tx, false); + self.show_tx_info_modal(tx, false); }); } @@ -175,7 +175,7 @@ impl WalletTransactions { if wallet_loaded && tx.can_finalize { let (icon, color) = (CHECK, Some(Colors::green())); View::item_button(ui, CornerRadius::default(), icon, color, || { - self.show_tx_info_modal(wallet, tx, true); + self.show_tx_info_modal(tx, true); }); } @@ -246,14 +246,14 @@ impl WalletTransactions { Some(id) => { match id { TX_INFO_MODAL => { - Modal::ui(ui.ctx(), |ui, modal| { + Modal::ui(ui.ctx(), cb, |ui, modal, cb| { if let Some(content) = self.tx_info_content.as_mut() { content.ui(ui, wallet, modal, cb); } }); } CANCEL_TX_CONFIRMATION_MODAL => { - Modal::ui(ui.ctx(), |ui, _| { + Modal::ui(ui.ctx(), cb, |ui, _, cb| { self.cancel_confirmation_modal(ui, wallet); }); } @@ -453,8 +453,8 @@ impl WalletTransactions { } /// Show transaction information [`Modal`]. - fn show_tx_info_modal(&mut self, wallet: &Wallet, tx: &WalletTransaction, finalize: bool) { - let modal = WalletTransactionModal::new(wallet, tx, finalize); + fn show_tx_info_modal(&mut self, tx: &WalletTransaction, finalize: bool) { + let modal = WalletTransactionModal::new(Some(tx.data.id), finalize); self.tx_info_content = Some(modal); Modal::new(TX_INFO_MODAL) .position(ModalPosition::CenterTop) diff --git a/src/gui/views/wallets/wallet/txs/tx.rs b/src/gui/views/wallets/wallet/txs/tx.rs index 6960ed4a..a4358ce1 100644 --- a/src/gui/views/wallets/wallet/txs/tx.rs +++ b/src/gui/views/wallets/wallet/txs/tx.rs @@ -36,7 +36,7 @@ pub struct WalletTransactionModal { tx_id: u32, /// Response Slatepack message input value. - response_edit: String, + response_edit: Option, /// Flag to show transaction finalization input. show_finalization: bool, @@ -61,32 +61,10 @@ pub struct WalletTransactionModal { impl WalletTransactionModal { /// Create new content instance with [`Wallet`] from provided [`WalletTransaction`]. - pub fn new(wallet: &Wallet, tx: &WalletTransaction, show_finalization: bool) -> Self { + pub fn new(tx_id: Option, show_finalization: bool) -> Self { Self { - tx_id: tx.data.id, - response_edit: if !tx.cancelling && !tx.finalizing && !tx.data.confirmed && - tx.data.tx_slate_id.is_some() && - (tx.data.tx_type == TxLogEntryType::TxSent || - tx.data.tx_type == TxLogEntryType::TxReceived) { - let mut slate = Slate::blank(1, false); - slate.state = if tx.can_finalize { - if tx.data.tx_type == TxLogEntryType::TxSent { - SlateState::Standard1 - } else { - SlateState::Invoice1 - } - } else { - if tx.data.tx_type == TxLogEntryType::TxReceived { - SlateState::Standard2 - } else { - SlateState::Invoice2 - } - }; - slate.id = tx.data.tx_slate_id.unwrap(); - wallet.read_slatepack(&slate).unwrap_or("".to_string()) - } else { - "".to_string() - }, + tx_id: tx_id.unwrap(), + response_edit: None, finalize_edit: "".to_string(), finalize_error: false, show_finalization, @@ -121,13 +99,42 @@ impl WalletTransactionModal { } let tx = txs.get(0).unwrap(); + // Check if response was loaded. + if self.response_edit.is_none() { + self.response_edit = Some( + if !tx.cancelling && !tx.finalizing && !tx.data.confirmed && + tx.data.tx_slate_id.is_some() && + (tx.data.tx_type == TxLogEntryType::TxSent || + tx.data.tx_type == TxLogEntryType::TxReceived) { + let mut slate = Slate::blank(1, false); + slate.state = if tx.can_finalize { + if tx.data.tx_type == TxLogEntryType::TxSent { + SlateState::Standard1 + } else { + SlateState::Invoice1 + } + } else { + if tx.data.tx_type == TxLogEntryType::TxReceived { + SlateState::Standard2 + } else { + SlateState::Invoice2 + } + }; + slate.id = tx.data.tx_slate_id.unwrap(); + wallet.read_slatepack(&slate).unwrap_or("".to_string()) + } else { + "".to_string() + } + ); + } + // Show transaction information. if self.qr_code_content.is_none() && self.scan_qr_content.is_none() { self.info_ui(ui, tx, wallet, cb); } // Show Slatepack message interaction. - if !self.response_edit.is_empty() { + if !self.response_edit.as_ref().unwrap().is_empty() { self.message_ui(ui, tx, wallet, modal, cb); } @@ -201,7 +208,7 @@ impl WalletTransactionModal { if let Ok(_) = res { self.show_finalization = false; self.finalize_edit = "".to_string(); - self.response_edit = "".to_string(); + self.response_edit = Some("".to_string()); } else { self.finalize_error = true; } @@ -370,7 +377,7 @@ impl WalletTransactionModal { let message_edit = if self.show_finalization { &mut self.finalize_edit } else { - &mut self.response_edit + &mut self.response_edit.as_mut().unwrap() }; let message_before = message_edit.clone(); @@ -471,14 +478,14 @@ impl WalletTransactionModal { let qr_text = format!("{} {}", QR_CODE, t!("qr_code")); View::button(ui, qr_text.clone(), Colors::white_or_black(false), || { let text = self.response_edit.clone(); - self.qr_code_content = Some(QrCodeContent::new(text, true)); + self.qr_code_content = Some(QrCodeContent::new(text.unwrap(), true)); }); }); columns[1].vertical_centered_justified(|ui| { // Draw copy button. let copy_text = format!("{} {}", COPY, t!("copy")); View::button(ui, copy_text, Colors::white_or_black(false), || { - cb.copy_string_to_buffer(self.response_edit.clone()); + cb.copy_string_to_buffer(self.response_edit.clone().unwrap()); self.finalize_edit = "".to_string(); if tx.can_finalize { self.show_finalization = true; @@ -499,7 +506,7 @@ impl WalletTransactionModal { Colors::white_or_black(false), || { if let Some((s, _)) = wallet.read_slate_by_tx(tx) { let name = format!("{}.{}.slatepack", s.id, s.state); - let data = self.response_edit.as_bytes().to_vec(); + let data = self.response_edit.as_ref().unwrap().as_bytes().to_vec(); cb.share_data(name, data).unwrap_or_default(); } });