ui: external connection check and ui repaint fix, tab button callback argument

This commit is contained in:
ardocrat 2024-09-20 13:42:45 +03:00
parent 2b83944f34
commit 2adb29f4ee
11 changed files with 121 additions and 101 deletions

View file

@ -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<Platform: PlatformCallbacks> App<Platform> {
/// 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;
}

View file

@ -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.

View file

@ -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());
}
});
}

View file

@ -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")

View file

@ -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()) {

View file

@ -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,

View file

@ -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)

View file

@ -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,

View file

@ -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());
});
});

View file

@ -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),

View file

@ -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<i64>) {
pub fn check(id: Option<i64>, 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();
});
});
}