From 8fdc2c1e39f074ced20e242971e3a0e83bd58794 Mon Sep 17 00:00:00 2001 From: ardocrat Date: Tue, 27 Jun 2023 02:11:07 +0300 Subject: [PATCH] ui + node: initial node settings, node controls, fix node restart stats, update translations --- locales/en.yml | 10 +- locales/ru.yml | 12 +- src/gui/navigator.rs | 2 +- src/gui/views/mod.rs | 2 +- src/gui/views/network.rs | 34 +++-- src/gui/views/network_metrics.rs | 4 +- src/gui/views/network_mining.rs | 2 +- src/gui/views/network_node.rs | 8 +- src/gui/views/network_settings.rs | 25 ++-- src/gui/views/settings_node.rs | 183 ++++++++++++++++++++++++++ src/gui/views/settings_node_server.rs | 16 --- src/gui/views/settings_stratum.rs | 34 ++--- src/gui/views/views.rs | 5 +- src/node/node.rs | 6 - 14 files changed, 262 insertions(+), 81 deletions(-) create mode 100644 src/gui/views/settings_node.rs delete mode 100644 src/gui/views/settings_node_server.rs diff --git a/locales/en.yml b/locales/en.yml index cee9b39..b99a3dc 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -11,6 +11,7 @@ network: restart: Restart autorun: Autorun disabled_server: 'Enable integrated node or add another connection method by pressing %{dots} in the top-left corner of the screen.' + no_ip_addresses: There are no available IP addresses on your system, server cannot be started, check your network connectivity. sync_status: node_restarting: Node is restarting node_down: Node is down @@ -59,7 +60,6 @@ network_mining: enable_server: Enable server server_setting: 'Enable stratum server or change more settings by selecting %{settings} at the bottom of the screen. App restart is required to change settings of the running server.' info: 'Mining server is enabled, you can change its settings by selecting %{settings} at the bottom of the screen. Data is updating when devices are connected.' - no_ip_addresses: There are no available IP addresses on your system, stratum server cannot be started, check your network connectivity. port_unavailable: Specified port is unavailable rewards_wallet: Wallet for rewards server: Stratum server @@ -71,14 +71,20 @@ network_mining: connected: Connected disconnected: Disconnected network_settings: - server: Server port: Port ip_address: IP Address change_port: Change port enter_value: 'Enter value:' + server: + title: Server + enable: Enable + disable: Disable + restart: Restart + api_ip_address: API IP Address modal: cancel: Cancel save: Save + confirmation: Confirmation modal_exit: description: Are you sure you want to quit the application? exit: Exit \ No newline at end of file diff --git a/locales/ru.yml b/locales/ru.yml index 649d58f..4660588 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -11,6 +11,7 @@ network: restart: Перезапустить autorun: Автозапуск disabled_server: 'Включите встроенный узел или добавьте другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.' + no_ip_addresses: В вашей системе отсутствуют доступные IP адреса, запуск сервера невозможен, проверьте ваше подключение к сети. sync_status: node_restarting: Узел перезапускается node_down: Узел выключен @@ -59,7 +60,6 @@ network_mining: enable_server: Включить сервер server_setting: 'Включите stratum-сервер или измените больше настроек, выбрав %{settings} внизу экрана. Для изменения настроек запущенного сервера потребуется перезапуск приложения.' info: 'Сервер майнинга запущен, вы можете изменить его настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.' - no_ip_addresses: В вашей системе отсутствуют доступные IP адреса, запуск stratum-сервера невозможен, проверьте ваше подключение к сети. port_unavailable: Указанный порт недоступен rewards_wallet: Кошелёк для наград server: Stratum-сервер @@ -71,14 +71,20 @@ network_mining: connected: Подключен disconnected: Отключен network_settings: - server: Сервер ip_address: IP Адрес port: Порт change_port: Изменить порт enter_value: 'Введите значение:' + server: + title: Сервер + enable: Включить + disable: Выключить + restart: Перезапустить + api_ip_address: API IP Адрес modal: cancel: Отмена save: Сохранить + confirmation: Подтверждение modal_exit: description: Вы уверены, что хотите выйти из приложения? - exit: Выход \ No newline at end of file + exit: Выход diff --git a/src/gui/navigator.rs b/src/gui/navigator.rs index d282a59..b0b0689 100644 --- a/src/gui/navigator.rs +++ b/src/gui/navigator.rs @@ -97,7 +97,7 @@ impl Navigator { /// Set exit confirmation [`Modal`] with provided [NAVIGATOR_STATE] lock. fn show_exit_modal_nav(mut w_nav: RwLockWriteGuard) { - let m = Modal::new(Self::EXIT_MODAL).title(t!("modal_exit.exit")); + let m = Modal::new(Self::EXIT_MODAL).title(t!("modal.confirmation")); w_nav.modal = Some(m); } diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index b4cffbd..ab5fc41 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -29,4 +29,4 @@ mod network_settings; mod network_metrics; mod network_mining; mod settings_stratum; -mod settings_node_server; \ No newline at end of file +mod settings_node; \ No newline at end of file diff --git a/src/gui/views/network.rs b/src/gui/views/network.rs index 58ff91a..17643a2 100644 --- a/src/gui/views/network.rs +++ b/src/gui/views/network.rs @@ -105,7 +105,7 @@ impl Network { egui::TopBottomPanel::bottom("network_tabs") .frame(egui::Frame { outer_margin: Margin::same(5.0), - .. Default::default() + ..Default::default() }) .show_inside(ui, |ui| { self.draw_tabs(ui); @@ -116,7 +116,7 @@ impl Network { stroke: View::DEFAULT_STROKE, inner_margin: Margin::same(4.0), fill: Colors::WHITE, - .. Default::default() + ..Default::default() }) .show_inside(ui, |ui| { self.current_tab.ui(ui, cb); @@ -246,21 +246,22 @@ impl Network { .size(16.0) .color(Colors::INACTIVE_TEXT) ); - ui.add_space(10.0); - View::button(ui, t!("network.enable_node"), Colors::GOLD, || { Node::start(); }); - ui.add_space(2.0); + Self::autorun_node_checkbox(ui); + }); + } - let autostart: bool = Settings::app_config_to_read().auto_start_node; - View::checkbox(ui, autostart, t!("network.autorun"), || { - let mut w_app_config = Settings::app_config_to_update(); - w_app_config.auto_start_node = !autostart; - w_app_config.save(); - }); + /// Draw checkbox with setting to run node on app launch. + pub fn autorun_node_checkbox(ui: &mut egui::Ui) { + let autostart: bool = Settings::app_config_to_read().auto_start_node; + View::checkbox(ui, autostart, t!("network.autorun"), || { + let mut w_app_config = Settings::app_config_to_update(); + w_app_config.auto_start_node = !autostart; + w_app_config.save(); }); } @@ -277,6 +278,17 @@ impl Network { addresses } + /// Show message when IP addresses are not available at system. + pub fn no_ip_address_ui(ui: &mut egui::Ui) { + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("network.no_ip_addresses")) + .size(16.0) + .color(Colors::INACTIVE_TEXT) + ); + ui.add_space(6.0); + }); + } + /// Check whether a port is available on the provided host. pub fn is_port_available(host: &str, port: u16) -> bool { let ip_addr = Ipv4Addr::from_str(host).unwrap(); diff --git a/src/gui/views/network_metrics.rs b/src/gui/views/network_metrics.rs index 8bc7620..9a71239 100644 --- a/src/gui/views/network_metrics.rs +++ b/src/gui/views/network_metrics.rs @@ -13,7 +13,7 @@ // limitations under the License. use chrono::{DateTime, NaiveDateTime, Utc}; -use egui::{RichText, Rounding, ScrollArea, Stroke, Ui}; +use egui::{RichText, Rounding, ScrollArea, Stroke}; use grin_servers::DiffBlock; use crate::gui::Colors; @@ -140,7 +140,7 @@ impl NetworkTab for NetworkMetrics { ); } - fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {} + fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {} } const DIFF_BLOCK_UI_HEIGHT: f32 = 76.60; diff --git a/src/gui/views/network_mining.rs b/src/gui/views/network_mining.rs index 613b30b..b46e148 100644 --- a/src/gui/views/network_mining.rs +++ b/src/gui/views/network_mining.rs @@ -77,7 +77,7 @@ impl NetworkTab for NetworkMining { ui.add_space(4.0); // Show button to enable stratum server if port is available. - if self.stratum_server_setup.stratum_port_available { + if self.stratum_server_setup.is_stratum_port_available { ui.add_space(6.0); View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || { Node::start_stratum_server(); diff --git a/src/gui/views/network_node.rs b/src/gui/views/network_node.rs index 0db2bd6..67f8488 100644 --- a/src/gui/views/network_node.rs +++ b/src/gui/views/network_node.rs @@ -13,7 +13,7 @@ // limitations under the License. use eframe::epaint::Stroke; -use egui::{RichText, Rounding, ScrollArea, Ui}; +use egui::{RichText, Rounding, ScrollArea}; use grin_servers::PeerStats; use crate::gui::Colors; @@ -182,13 +182,13 @@ impl NetworkTab for NetworkNode { }); } - fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {} + fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) {} } fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) { ui.vertical(|ui| { let mut rect = ui.available_rect_before_wrap(); - rect.set_height(78.0); + rect.set_height(77.3); ui.painter().rect( rect, @@ -251,6 +251,6 @@ fn draw_peer_stats(ui: &mut egui::Ui, peer: &PeerStats, rounding: [bool; 2]) { // Add space after last item if rounding[1] { - ui.add_space(3.0); + ui.add_space(2.0); } } \ No newline at end of file diff --git a/src/gui/views/network_settings.rs b/src/gui/views/network_settings.rs index 0e1bd2b..99deadf 100644 --- a/src/gui/views/network_settings.rs +++ b/src/gui/views/network_settings.rs @@ -12,16 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::Ui; -use grin_core::global::ChainTypes; -use crate::gui::Colors; -use crate::gui::icons::COMPUTER_TOWER; +use egui::ScrollArea; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::{Modal, NetworkTab, NetworkTabType, View}; -use crate::Settings; +use crate::gui::views::{Modal, NetworkTab, NetworkTabType}; +use crate::gui::views::settings_node::NodeSetup; #[derive(Default)] -pub struct NetworkSettings; +pub struct NetworkSettings { + node_setup: NodeSetup +} impl NetworkTab for NetworkSettings { fn get_type(&self) -> NetworkTabType { @@ -29,13 +28,15 @@ impl NetworkTab for NetworkSettings { } fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { - View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server"))); - View::horizontal_line(ui, Colors::ITEM_STROKE); - ui.add_space(4.0); - + ScrollArea::vertical() + .id_source("network_settings") + .auto_shrink([false; 2]) + .show(ui, |ui| { + self.node_setup.ui(ui, cb); + }); } - fn on_modal_ui(&mut self, ui: &mut Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { + fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { } } \ No newline at end of file diff --git a/src/gui/views/settings_node.rs b/src/gui/views/settings_node.rs new file mode 100644 index 0000000..385e683 --- /dev/null +++ b/src/gui/views/settings_node.rs @@ -0,0 +1,183 @@ +// Copyright 2023 The Grim Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::net::IpAddr; +use std::str::FromStr; +use egui::RichText; +use crate::gui::Colors; +use crate::gui::icons::COMPUTER_TOWER; +use crate::gui::platform::PlatformCallbacks; +use crate::gui::views::{Network, View}; +use crate::node::{Node, NodeConfig}; + +/// Integrated node server setup ui section. +pub struct NodeSetup { + /// API IP address to be used inside edit modal. + api_address_edit: String, + /// API port to be used inside edit modal. + api_port_edit: String, + /// Flag to check if API port is available inside edit modal. + api_port_available_edit: bool, + + /// Flag to check if API port is available from saved config value. + pub(crate) api_port_available: bool, + + /// API secret to be used inside edit modal. + api_secret_edit: String, + + /// Foreign API secret to be used inside edit modal. + foreign_api_secret_edit: String, + + /// Future Time Limit to be used inside edit modal. + ftl_edit: String, +} + +impl Default for NodeSetup { + fn default() -> Self { + let (api_address, api_port) = NodeConfig::get_api_address_port(); + let is_api_port_available = Network::is_port_available(api_address.as_str(), api_port); + Self { + api_address_edit: api_address, + api_port_edit: api_port.to_string(), + api_port_available_edit: is_api_port_available, + api_port_available: is_api_port_available, + api_secret_edit: "".to_string(), + foreign_api_secret_edit: "".to_string(), + ftl_edit: "".to_string(), + } + } +} + +const SECRET_SYMBOLS: &'static str = "••••••••••••"; + +impl NodeSetup { + pub const API_PORT_MODAL: &'static str = "stratum_port"; + + pub fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { + View::sub_title(ui, format!("{} {}", COMPUTER_TOWER, t!("network_settings.server.title"))); + View::horizontal_line(ui, Colors::ITEM_STROKE); + ui.add_space(4.0); + + // Show loading indicator or controls to stop/start/restart node. + if Node::is_stopping() || Node::is_restarting() || Node::is_starting() { + ui.vertical_centered(|ui| { + ui.add_space(6.0); + View::small_loading_spinner(ui); + }); + } else { + if Node::is_running() { + ui.scope(|ui| { + // Setup spacing between buttons. + ui.spacing_mut().item_spacing = egui::Vec2::new(8.0, 0.0); + ui.add_space(6.0); + + ui.columns(2, |columns| { + columns[0].vertical_centered_justified(|ui| { + View::button(ui, t!("network_settings.server.disable"), Colors::GOLD, || { + Node::stop(false); + }); + }); + columns[1].vertical_centered_justified(|ui| { + View::button(ui, t!("network_settings.server.restart"), Colors::GOLD, || { + Node::restart(); + }); + }); + }); + }); + } else { + ui.add_space(6.0); + ui.vertical_centered(|ui| { + View::button(ui, t!("network_settings.server.enable"), Colors::GOLD, || { + Node::start(); + }); + }); + } + } + + ui.add_space(4.0); + ui.vertical_centered(|ui| { + Network::autorun_node_checkbox(ui); + }); + ui.add_space(4.0); + + let addrs = Network::get_ip_list(); + // Show error message when IP addresses are not available on the system. + if addrs.is_empty() { + Network::no_ip_address_ui(ui); + ui.add_space(4.0); + } else { + View::horizontal_line(ui, Colors::ITEM_STROKE); + ui.add_space(4.0); + + ui.vertical_centered(|ui| { + ui.label(RichText::new(t!("network_settings.server.api_ip_address")) + .size(16.0) + .color(Colors::GRAY) + ); + ui.add_space(6.0); + }); + + // Show API IP address setup. + self.api_ip_address_setup_ui(ui, addrs) + } + } + + /// Show API IP address setup. + fn api_ip_address_setup_ui(&mut self, ui: &mut egui::Ui, addrs: Vec) { + let (addr, port) = NodeConfig::get_api_address_port(); + let saved_ip_addr = &IpAddr::from_str(addr.as_str()).unwrap(); + let mut selected_addr = saved_ip_addr; + + // Set first IP address as current if saved is not present at system. + if !addrs.contains(selected_addr) { + selected_addr = addrs.get(0).unwrap(); + } + + // Show available IP addresses on the system. + let _ = addrs.chunks(2).map(|x| { + if x.len() == 2 { + ui.columns(2, |columns| { + let addr0 = x.get(0).unwrap(); + columns[0].vertical_centered(|ui| { + View::radio_value(ui, + &mut selected_addr, + addr0, + addr0.to_string()); + }); + let addr1 = x.get(1).unwrap(); + columns[1].vertical_centered(|ui| { + View::radio_value(ui, + &mut selected_addr, + addr1, + addr1.to_string()); + }) + }); + } else { + let addr = x.get(0).unwrap(); + View::radio_value(ui, + &mut selected_addr, + addr, + addr.to_string()); + } + ui.add_space(10.0); + }).collect::>(); + + // Save stratum server address at config if it was changed and check port availability. + if saved_ip_addr != selected_addr { + NodeConfig::save_api_server_address_port(selected_addr.to_string(), port.to_string()); + let available = Network::is_port_available(selected_addr.to_string().as_str(), port); + self.api_port_available = available; + } + } +} \ No newline at end of file diff --git a/src/gui/views/settings_node_server.rs b/src/gui/views/settings_node_server.rs deleted file mode 100644 index aac9adb..0000000 --- a/src/gui/views/settings_node_server.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2023 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// /// Integrated node server setup ui section. -// struct \ No newline at end of file diff --git a/src/gui/views/settings_stratum.rs b/src/gui/views/settings_stratum.rs index 06896a1..a0a375c 100644 --- a/src/gui/views/settings_stratum.rs +++ b/src/gui/views/settings_stratum.rs @@ -25,15 +25,15 @@ use crate::node::NodeConfig; /// Stratum server setup ui section. pub struct StratumServerSetup { - /// Stratum address to be used inside edit modal. + /// Stratum IP address to be used inside edit modal. stratum_address_edit: String, /// Stratum port to be used inside edit modal. stratum_port_edit: String, /// Flag to check if stratum port is available inside edit modal. - port_available_edit: bool, + stratum_port_available_edit: bool, /// Flag to check if stratum port is available from saved config value. - pub(crate) stratum_port_available: bool + pub(crate) is_stratum_port_available: bool } impl Default for StratumServerSetup { @@ -43,8 +43,8 @@ impl Default for StratumServerSetup { Self { stratum_address_edit: stratum_address, stratum_port_edit: stratum_port.to_string(), - port_available_edit: is_port_available, - stratum_port_available: is_port_available + stratum_port_available_edit: is_port_available, + is_stratum_port_available: is_port_available } } } @@ -61,13 +61,7 @@ impl StratumServerSetup { // Show error message when IP addresses are not available on the system. let addrs = Network::get_ip_list(); if addrs.is_empty() { - ui.vertical_centered(|ui| { - ui.label(RichText::new(t!("network_mining.no_ip_addresses")) - .size(16.0) - .color(Colors::INACTIVE_TEXT) - ); - ui.add_space(6.0); - }); + Network::no_ip_address_ui(ui); return; } @@ -96,7 +90,7 @@ impl StratumServerSetup { // Setup values for modal. self.stratum_address_edit = stratum_address.clone(); self.stratum_port_edit = stratum_port.to_string(); - self.port_available_edit = Network::is_port_available( + self.stratum_port_available_edit = Network::is_port_available( stratum_address.as_str(), stratum_port ); @@ -111,7 +105,7 @@ impl StratumServerSetup { ui.add_space(14.0); // Show error when stratum server port is unavailable. - if !self.stratum_port_available { + if !self.is_stratum_port_available { ui.label(RichText::new(t!("network_mining.port_unavailable")) .size(16.0) .color(Colors::RED)); @@ -147,7 +141,7 @@ impl StratumServerSetup { } // Show error when specified port is unavailable. - if !self.port_available_edit { + if !self.stratum_port_available_edit { ui.add_space(12.0); ui.label(RichText::new(t!("network_mining.port_unavailable")) .size(16.0) @@ -177,16 +171,16 @@ impl StratumServerSetup { self.stratum_address_edit.as_str(), port_parse.unwrap() ); - self.port_available_edit = is_available; + self.stratum_port_available_edit = is_available; // Save port at config if it's available. - if self.port_available_edit { + if self.stratum_port_available_edit { NodeConfig::save_stratum_address_port( self.stratum_address_edit.clone(), self.stratum_port_edit.clone() ); - self.stratum_port_available = true; + self.is_stratum_port_available = true; cb.hide_keyboard(); modal.close(); } @@ -238,11 +232,11 @@ impl StratumServerSetup { ui.add_space(10.0); }).collect::>(); - // Save stratum server address at config if it was changed. + // Save stratum server address at config if it was changed and check port availability. if saved_ip_addr != selected_addr { NodeConfig::save_stratum_address_port(selected_addr.to_string(), port.to_string()); let available = Network::is_port_available(selected_addr.to_string().as_str(), port); - self.stratum_port_available = available; + self.is_stratum_port_available = available; } } } \ No newline at end of file diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index a72578d..1dae7e4 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -43,7 +43,7 @@ impl View { /// Show and cut long text with ﹍ character. pub fn ellipsize_text(ui: &mut egui::Ui, text: String, size: f32, color: Color32) { let mut job = LayoutJob::single_section(text, TextFormat { - font_id: FontId::proportional(size), color, .. Default::default() + font_id: FontId::proportional(size), color, ..Default::default() }); job.wrap = TextWrapping { max_rows: 1, @@ -201,7 +201,7 @@ impl View { /// Draw small gold loading spinner. pub fn small_loading_spinner(ui: &mut egui::Ui) { - Spinner::new().size(48.0).color(Colors::GOLD).ui(ui); + Spinner::new().size(42.0).color(Colors::GOLD).ui(ui); } /// Draw the button that looks like checkbox with callback on check. @@ -210,6 +210,7 @@ impl View { true => { (format!("{} {}", CHECK_SQUARE, text), Colors::TEXT_BUTTON) } false => { (format!("{} {}", SQUARE, text), Colors::TEXT) } }; + let br = Button::new(RichText::new(text_value).size(18.0).color(color)) .frame(false) .stroke(Stroke::NONE) diff --git a/src/node/node.rs b/src/node/node.rs index c919aa2..1f60ba9 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -151,12 +151,6 @@ impl Node { loop { if Self::is_restarting() { - // Clean server stats. - { - let mut w_stats = NODE_STATE.stats.write().unwrap(); - *w_stats = None; - } - // Stop the server. server.stop();