From 2adb29f4eea2efd5ae33fbc9edc1085a8781733d Mon Sep 17 00:00:00 2001 From: ardocrat Date: Fri, 20 Sep 2024 13:42:45 +0300 Subject: [PATCH] ui: external connection check and ui repaint fix, tab button callback argument --- src/gui/app.rs | 7 +- src/gui/views/network/connections.rs | 3 +- src/gui/views/network/content.rs | 15 +-- src/gui/views/network/modals/ext_conn.rs | 15 ++- src/gui/views/views.rs | 19 ++- src/gui/views/wallets/content.rs | 33 +++--- src/gui/views/wallets/creation/creation.rs | 6 +- src/gui/views/wallets/modals/conn.rs | 3 +- src/gui/views/wallets/wallet/content.rs | 11 +- .../wallets/wallet/settings/connection.rs | 1 - src/wallet/connections/external.rs | 109 +++++++++--------- 11 files changed, 121 insertions(+), 101 deletions(-) diff --git a/src/gui/app.rs b/src/gui/app.rs index e6370e6..ea4ade2 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -23,6 +23,7 @@ use crate::gui::Colors; use crate::gui::icons::{ARROWS_IN, ARROWS_OUT, CARET_DOWN, MOON, SUN, X}; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Content, TitlePanel, View}; +use crate::wallet::ExternalConnection; lazy_static! { /// State to check if platform Back button was pressed. @@ -56,11 +57,15 @@ impl App { /// Draw application content. pub fn ui(&mut self, ctx: &Context) { - // Set platform context on first draw. if self.first_draw { + // Set platform context. if View::is_desktop() { self.platform.set_context(ctx); } + + // Check external connections availability. + ExternalConnection::check(None, ctx); + self.first_draw = false; } diff --git a/src/gui/views/network/connections.rs b/src/gui/views/network/connections.rs index 5b10bc1..6d928f7 100644 --- a/src/gui/views/network/connections.rs +++ b/src/gui/views/network/connections.rs @@ -36,7 +36,6 @@ pub struct ConnectionsContent { impl Default for ConnectionsContent { fn default() -> Self { - ExternalConnection::check_ext_conn_availability(None); Self { ext_conn_modal: ExternalConnectionModal::new(None), modal_ids: vec![ @@ -78,7 +77,7 @@ impl ConnectionsContent { // Check connections availability. if saved_chain_type != AppConfig::chain_type() { - ExternalConnection::check_ext_conn_availability(None); + ExternalConnection::check(None, ui.ctx()); } // Show integrated node info content. diff --git a/src/gui/views/network/content.rs b/src/gui/views/network/content.rs index 54b00c7..791d4ee 100644 --- a/src/gui/views/network/content.rs +++ b/src/gui/views/network/content.rs @@ -12,7 +12,6 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::time::Duration; use egui::{Id, Margin, RichText, ScrollArea}; use egui::scroll_area::ScrollBarVisibility; @@ -149,8 +148,6 @@ impl NetworkContent { // Redraw after delay. if Node::is_running() { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); - } else if show_connections { - ui.ctx().request_repaint_after(Duration::from_millis(1000)); } } @@ -166,22 +163,22 @@ impl NetworkContent { let current_type = self.node_tab_content.get_type(); ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { - View::tab_button(ui, DATABASE, current_type == NetworkTabType::Node, || { + View::tab_button(ui, DATABASE, current_type == NetworkTabType::Node, |_| { self.node_tab_content = Box::new(NetworkNode::default()); }); }); columns[1].vertical_centered_justified(|ui| { - View::tab_button(ui, GAUGE, current_type == NetworkTabType::Metrics, || { + View::tab_button(ui, GAUGE, current_type == NetworkTabType::Metrics, |_| { self.node_tab_content = Box::new(NetworkMetrics::default()); }); }); columns[2].vertical_centered_justified(|ui| { - View::tab_button(ui, FACTORY, current_type == NetworkTabType::Mining, || { + View::tab_button(ui, FACTORY, current_type == NetworkTabType::Mining, |_| { self.node_tab_content = Box::new(NetworkMining::default()); }); }); columns[3].vertical_centered_justified(|ui| { - View::tab_button(ui, FADERS, current_type == NetworkTabType::Settings, || { + View::tab_button(ui, FADERS, current_type == NetworkTabType::Settings, |_| { self.node_tab_content = Box::new(NetworkSettings::default()); }); }); @@ -204,10 +201,10 @@ impl NetworkContent { // Draw title panel. TitlePanel::new(Id::from("network_title_panel")).ui(TitleType::Single(title_content), |ui| { if !show_connections { - View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |_| { + View::title_button_big(ui, DOTS_THREE_OUTLINE_VERTICAL, |ui| { AppConfig::toggle_show_connections_network_panel(); if AppConfig::show_connections_network_panel() { - ExternalConnection::check_ext_conn_availability(None); + ExternalConnection::check(None, ui.ctx()); } }); } diff --git a/src/gui/views/network/modals/ext_conn.rs b/src/gui/views/network/modals/ext_conn.rs index 62ba6e7..10c3d74 100644 --- a/src/gui/views/network/modals/ext_conn.rs +++ b/src/gui/views/network/modals/ext_conn.rs @@ -119,7 +119,7 @@ impl ExternalConnectionModal { }); columns[1].vertical_centered_justified(|ui| { // Add connection button callback. - let mut on_add = || { + let mut on_add = |ui: &mut egui::Ui| { if !self.ext_node_url_edit.starts_with("http") { self.ext_node_url_edit = format!("http://{}", self.ext_node_url_edit) } @@ -139,7 +139,7 @@ impl ExternalConnectionModal { ext_conn.id = id; } ConnectionsConfig::add_ext_conn(ext_conn.clone()); - ExternalConnection::check_ext_conn_availability(Some(ext_conn.id)); + ExternalConnection::check(Some(ext_conn.id), ui.ctx()); on_save(ext_conn); // Close modal. @@ -150,10 +150,17 @@ impl ExternalConnectionModal { modal.close(); } }; + + // Handle Enter key press. + let mut enter = false; View::on_enter_key(ui, || { - (on_add)(); + enter = true; }); - View::button(ui, if self.ext_conn_id.is_some() { + if enter { + (on_add)(ui); + } + + View::button_ui(ui, if self.ext_conn_id.is_some() { t!("modal.save") } else { t!("modal.add") diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index 4f91528..21b25bd 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -217,7 +217,10 @@ impl View { pub const TAB_ITEMS_PADDING: f32 = 5.0; /// Tab button with white background fill color, contains only icon. - pub fn tab_button(ui: &mut egui::Ui, icon: &str, active: bool, action: impl FnOnce()) { + pub fn tab_button(ui: &mut egui::Ui, + icon: &str, + active: bool, + action: impl FnOnce(&mut egui::Ui)) { ui.scope(|ui| { let text_color = match active { true => Colors::title(false), @@ -245,7 +248,7 @@ impl View { let br = button.ui(ui).on_hover_cursor(CursorIcon::PointingHand); br.surrender_focus(); if Self::touched(ui, br) { - (action)(); + (action)(ui); } }); } @@ -280,6 +283,18 @@ impl View { } } + /// Draw [`Button`] with specified background fill color and text color. + pub fn colored_text_button_ui(ui: &mut egui::Ui, + text: String, + text_color: Color32, + fill: Color32, + action: impl FnOnce(&mut egui::Ui)) { + let br = Self::button_resp(ui, text, text_color, fill); + if Self::touched(ui, br) { + (action)(ui); + } + } + /// Draw gold action [`Button`]. pub fn action_button(ui: &mut egui::Ui, text: String, action: impl FnOnce()) { diff --git a/src/gui/views/wallets/content.rs b/src/gui/views/wallets/content.rs index f9cf10b..be6d28f 100644 --- a/src/gui/views/wallets/content.rs +++ b/src/gui/views/wallets/content.rs @@ -27,7 +27,7 @@ use crate::gui::views::wallets::modals::{AddWalletModal, OpenWalletModal, Wallet 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::{Wallet, WalletList}; +use crate::wallet::{ExternalConnection, Wallet, WalletList}; use crate::wallet::types::ConnectionMethod; /// Wallets content. @@ -199,7 +199,7 @@ impl WalletsContent { ui.vertical_centered(|ui| { let pressed = Modal::opened() == Some(ADD_WALLET_MODAL); - View::tab_button(ui, PLUS, pressed, || { + View::tab_button(ui, PLUS, pressed, |_| { self.show_add_wallet_modal(cb); }); }); @@ -224,7 +224,7 @@ impl WalletsContent { ..Default::default() }) .show_inside(ui, |ui| { - if !list_hidden && !dual_panel { + if !list_hidden && !dual_panel && !showing_wallet && !creating_wallet { ui.ctx().request_repaint_after(Duration::from_millis(1000)); } // Show wallet list. @@ -518,12 +518,24 @@ impl WalletsContent { View::item_button(ui, View::item_rounding(0, 1, true), FOLDER_OPEN, None, || { self.show_opening_modal(wallet.clone(), None, cb); }); - // Show button to select connection if not syncing. if !wallet.syncing() { + let mut show_selection = false; View::item_button(ui, Rounding::default(), GLOBE, None, || { self.wallet_content = Some(WalletContent::new(wallet.clone(), None)); - self.show_connection_selection_modal(wallet); + self.conn_selection_content = Some( + WalletConnectionModal::new(wallet.get_current_connection()) + ); + // Show connection selection modal. + Modal::new(SELECT_CONNECTION_MODAL) + .position(ModalPosition::CenterTop) + .title(t!("wallets.conn_method")) + .show(); + show_selection = true; }); + if show_selection { + self.conn_selection_content = None; + ExternalConnection::check(None, ui.ctx()); + } } } else { if !current { @@ -579,17 +591,6 @@ impl WalletsContent { }); } - /// Show [`Modal`] to select connection for the wallet. - fn show_connection_selection_modal(&mut self, wallet: &Wallet) { - let ext_conn = wallet.get_current_connection(); - self.conn_selection_content = Some(WalletConnectionModal::new(ext_conn)); - // Show modal. - Modal::new(SELECT_CONNECTION_MODAL) - .position(ModalPosition::CenterTop) - .title(t!("wallets.conn_method")) - .show(); - } - /// Show [`Modal`] to select and open wallet. fn show_opening_modal(&mut self, wallet: Wallet, diff --git a/src/gui/views/wallets/creation/creation.rs b/src/gui/views/wallets/creation/creation.rs index 4b5b2a7..fd1f898 100644 --- a/src/gui/views/wallets/creation/creation.rs +++ b/src/gui/views/wallets/creation/creation.rs @@ -315,7 +315,7 @@ impl WalletCreation { }; // Show next step button. - View::colored_text_button(ui, next_text.to_uppercase(), text_color, bg_color, || { + View::colored_text_button_ui(ui, next_text.to_uppercase(), text_color, bg_color, |ui| { self.step = match self.step { Step::EnterMnemonic => { if self.mnemonic_setup.mnemonic.mode() == PhraseMode::Generate { @@ -349,7 +349,7 @@ impl WalletCreation { // Check external connections availability on connection setup. if self.step == Step::SetupConnection { - ExternalConnection::check_ext_conn_availability(None); + ExternalConnection::check(None, ui.ctx()); } }); } @@ -361,7 +361,7 @@ impl WalletCreation { Step::ConfirmMnemonic => self.mnemonic_setup.confirm_ui(ui, cb), Step::SetupConnection => { // Redraw if node is running. - if Node::is_running() { + if Node::is_running() && !Content::is_dual_panel_mode(ui) { ui.ctx().request_repaint_after(Node::STATS_UPDATE_DELAY); } self.network_setup.create_ui(ui, cb) diff --git a/src/gui/views/wallets/modals/conn.rs b/src/gui/views/wallets/modals/conn.rs index 19f3c0a..138b263 100644 --- a/src/gui/views/wallets/modals/conn.rs +++ b/src/gui/views/wallets/modals/conn.rs @@ -21,7 +21,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::wallet::{ConnectionsConfig, ExternalConnection}; +use crate::wallet::ConnectionsConfig; use crate::wallet::types::ConnectionMethod; /// Wallet connection selection [`Modal`] content. @@ -36,7 +36,6 @@ pub struct WalletConnectionModal { impl WalletConnectionModal { /// Create from provided wallet connection. pub fn new(conn: ConnectionMethod) -> Self { - ExternalConnection::check_ext_conn_availability(None); Self { conn, ext_conn_content: None, diff --git a/src/gui/views/wallets/wallet/content.rs b/src/gui/views/wallets/wallet/content.rs index fd21394..086a630 100644 --- a/src/gui/views/wallets/wallet/content.rs +++ b/src/gui/views/wallets/wallet/content.rs @@ -28,7 +28,7 @@ use crate::gui::views::wallets::types::{GRIN, WalletTab, WalletTabType}; use crate::gui::views::wallets::wallet::modals::WalletAccountsModal; use crate::gui::views::wallets::wallet::WalletSettings; use crate::node::Node; -use crate::wallet::{Wallet, WalletConfig}; +use crate::wallet::{ExternalConnection, Wallet, WalletConfig}; use crate::wallet::types::{ConnectionMethod, WalletData}; /// Wallet content. @@ -358,25 +358,26 @@ impl WalletContent { let current_type = self.current_tab.get_type(); ui.columns(4, |columns| { columns[0].vertical_centered_justified(|ui| { - View::tab_button(ui, GRAPH, current_type == WalletTabType::Txs, || { + View::tab_button(ui, GRAPH, current_type == WalletTabType::Txs, |_| { self.current_tab = Box::new(WalletTransactions::default()); }); }); columns[1].vertical_centered_justified(|ui| { let is_messages = current_type == WalletTabType::Messages; - View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, || { + View::tab_button(ui, CHAT_CIRCLE_TEXT, is_messages, |_| { self.current_tab = Box::new( WalletMessages::new(None) ); }); }); columns[2].vertical_centered_justified(|ui| { - View::tab_button(ui, BRIDGE, current_type == WalletTabType::Transport, || { + View::tab_button(ui, BRIDGE, current_type == WalletTabType::Transport, |_| { self.current_tab = Box::new(WalletTransport::default()); }); }); columns[3].vertical_centered_justified(|ui| { - View::tab_button(ui, GEAR_FINE, current_type == WalletTabType::Settings, || { + View::tab_button(ui, GEAR_FINE, current_type == WalletTabType::Settings, |ui| { + ExternalConnection::check(None, ui.ctx()); self.current_tab = Box::new(WalletSettings::default()); }); }); diff --git a/src/gui/views/wallets/wallet/settings/connection.rs b/src/gui/views/wallets/wallet/settings/connection.rs index eaf5f64..5aa49e0 100644 --- a/src/gui/views/wallets/wallet/settings/connection.rs +++ b/src/gui/views/wallets/wallet/settings/connection.rs @@ -38,7 +38,6 @@ pub struct ConnectionSettings { impl Default for ConnectionSettings { fn default() -> Self { - ExternalConnection::check_ext_conn_availability(None); Self { method: ConnectionMethod::Integrated, ext_conn_modal: ExternalConnectionModal::new(None), diff --git a/src/wallet/connections/external.rs b/src/wallet/connections/external.rs index 05371c9..28d1872 100644 --- a/src/wallet/connections/external.rs +++ b/src/wallet/connections/external.rs @@ -78,70 +78,67 @@ impl ExternalConnection { } } - /// Check connection availability. - fn check_conn_availability(&self) { - let conn = self.clone(); - ConnectionsConfig::update_ext_conn_status(conn.id, None); - std::thread::spawn(move || { - tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap() - .block_on(async { - let url = url::Url::parse(conn.url.as_str()).unwrap(); - if let Ok(_) = url.socket_addrs(|| None) { - let addr = format!("{}v2/foreign", url.to_string()); - // Setup http client. - let client = hyper::Client::builder() - .build::<_, hyper::Body>(hyper_tls::HttpsConnector::new()); - let mut req_setup = hyper::Request::builder() - .method(hyper::Method::POST) - .uri(addr.clone()); - // Setup secret key auth. - if let Some(key) = conn.secret { - let basic_auth = format!( - "Basic {}", - to_base64(&format!("grin:{}", key)) - ); - req_setup = req_setup - .header(hyper::header::AUTHORIZATION, basic_auth.clone()); - } - let req = req_setup.body(hyper::Body::from( - r#"{"id":1,"jsonrpc":"2.0","method":"get_version","params":{} }"#) - ).unwrap(); - // Send request. - match client.request(req).await { - Ok(res) => { - let status = res.status().as_u16(); - // Available on 200 HTTP status code. - if status == 200 { - ConnectionsConfig::update_ext_conn_status(conn.id, Some(true)); - } else { - ConnectionsConfig::update_ext_conn_status(conn.id, Some(false)); - } - } - Err(_) => { - ConnectionsConfig::update_ext_conn_status(conn.id, Some(false)); - } - } - } else { - ConnectionsConfig::update_ext_conn_status(conn.id, Some(false)); - } - }); - }); - } - /// Check external connections availability. - pub fn check_ext_conn_availability(id: Option) { + pub fn check(id: Option, ui_ctx: &egui::Context) { let conn_list = ConnectionsConfig::ext_conn_list(); for conn in conn_list { if let Some(id) = id { if id == conn.id { - conn.check_conn_availability(); + check_ext_conn(&conn, ui_ctx); } } else { - conn.check_conn_availability(); + check_ext_conn(&conn, ui_ctx); } } } +} + +/// Check connection availability. +fn check_ext_conn(conn: &ExternalConnection, ui_ctx: &egui::Context) { + let conn = conn.clone(); + let ui_ctx = ui_ctx.clone(); + ConnectionsConfig::update_ext_conn_status(conn.id, None); + std::thread::spawn(move || { + tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap() + .block_on(async { + let url = url::Url::parse(conn.url.as_str()).unwrap(); + if let Ok(_) = url.socket_addrs(|| None) { + let addr = format!("{}v2/foreign", url.to_string()); + // Setup http client. + let client = hyper::Client::builder() + .build::<_, hyper::Body>(hyper_tls::HttpsConnector::new()); + let mut req_setup = hyper::Request::builder() + .method(hyper::Method::POST) + .uri(addr.clone()); + // Setup secret key auth. + if let Some(key) = conn.secret { + let basic_auth = format!( + "Basic {}", + to_base64(&format!("grin:{}", key)) + ); + req_setup = req_setup + .header(hyper::header::AUTHORIZATION, basic_auth.clone()); + } + let req = req_setup.body(hyper::Body::from( + r#"{"id":1,"jsonrpc":"2.0","method":"get_version","params":{} }"#) + ).unwrap(); + // Send request. + match client.request(req).await { + Ok(res) => { + let status = res.status().as_u16(); + // Available on 200 HTTP status code. + ConnectionsConfig::update_ext_conn_status(conn.id, Some(status == 200)); + } + Err(_) => ConnectionsConfig::update_ext_conn_status(conn.id, Some(false)) + } + } else { + ConnectionsConfig::update_ext_conn_status(conn.id, Some(false)); + } + // Repaint ui on change. + ui_ctx.request_repaint(); + }); + }); } \ No newline at end of file