network + ui: stratum settings setup and list ui, pass platform callbacks to network tabs, add horizontal line view
This commit is contained in:
parent
7dba27e6d1
commit
4ad134c5e3
10 changed files with 249 additions and 140 deletions
|
@ -12,10 +12,10 @@ network:
|
||||||
autorun: Autorun
|
autorun: Autorun
|
||||||
disabled_server: 'Enable integrated node or choose another connection method by pressing %{dots} in the top-left corner of the screen.'
|
disabled_server: 'Enable integrated node or choose another connection method by pressing %{dots} in the top-left corner of the screen.'
|
||||||
sync_status:
|
sync_status:
|
||||||
server_restarting: Server is restarting
|
node_restarting: Node is restarting
|
||||||
server_down: Server is down
|
node_down: Node is down
|
||||||
initial: Server is starting
|
initial: Node is starting
|
||||||
no_sync: Server is running
|
no_sync: Node is running
|
||||||
awaiting_peers: Waiting for peers
|
awaiting_peers: Waiting for peers
|
||||||
header_sync: Downloading headers
|
header_sync: Downloading headers
|
||||||
header_sync_percent: 'Downloading headers: %{percent}%'
|
header_sync_percent: 'Downloading headers: %{percent}%'
|
||||||
|
@ -31,7 +31,7 @@ sync_status:
|
||||||
tx_hashset_save: Finalizing chain state
|
tx_hashset_save: Finalizing chain state
|
||||||
body_sync: Downloading blocks
|
body_sync: Downloading blocks
|
||||||
body_sync_percent: 'Downloading blocks: %{percent}%'
|
body_sync_percent: 'Downloading blocks: %{percent}%'
|
||||||
shutdown: Server is shutting down
|
shutdown: Node is shutting down
|
||||||
network_node:
|
network_node:
|
||||||
header: Header
|
header: Header
|
||||||
block: Block
|
block: Block
|
||||||
|
@ -55,11 +55,15 @@ network_metrics:
|
||||||
difficulty_window: 'Difficulty window %{size}'
|
difficulty_window: 'Difficulty window %{size}'
|
||||||
network_mining:
|
network_mining:
|
||||||
loading: Mining will be available after the synchronization
|
loading: Mining will be available after the synchronization
|
||||||
|
server_setup: Stratum server setup
|
||||||
enable_server: Enable server
|
enable_server: Enable server
|
||||||
disabled_server: 'Enable stratum server at %{address} or change settings by selecting %{settings} at the bottom of the screen.'
|
server_setting: 'Enable stratum server or change more settings by selecting %{settings} at the bottom of the screen.'
|
||||||
info: 'Mining server is enabled, you can change settings by selecting %{settings} at the bottom of the screen. Data is updating when devices are connected.'
|
info: 'Mining server is enabled, you can change settings by selecting %{settings} at the bottom of the screen. Data is updating when devices are connected.'
|
||||||
address: IP Address
|
no_ip_addresses: There are no available IP addresses on your system, the server cannot be started, check your network connectivity.
|
||||||
wallet: Wallet Address
|
choose_ip_address: 'Choose IP Address:'
|
||||||
|
change_port: 'Change port:'
|
||||||
|
ip_address: IP Address
|
||||||
|
rewards_wallet: Wallet for rewards
|
||||||
server: Stratum server
|
server: Stratum server
|
||||||
miners: Miners
|
miners: Miners
|
||||||
devices: Devices
|
devices: Devices
|
||||||
|
|
|
@ -12,10 +12,10 @@ network:
|
||||||
autorun: Автозапуск
|
autorun: Автозапуск
|
||||||
disabled_server: 'Включите встроенный узел или выберите другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.'
|
disabled_server: 'Включите встроенный узел или выберите другой способ подключения, нажав %{dots} в левом-верхнем углу экрана.'
|
||||||
sync_status:
|
sync_status:
|
||||||
server_restarting: Сервер перезапускается
|
node_restarting: Узел перезапускается
|
||||||
server_down: Сервер выключен
|
node_down: Узел выключен
|
||||||
initial: Запуск сервера
|
initial: Запуск узла
|
||||||
no_sync: Сервер запущен
|
no_sync: Узел запущен
|
||||||
awaiting_peers: Ожидание пиров
|
awaiting_peers: Ожидание пиров
|
||||||
header_sync: Загрузка заголовков
|
header_sync: Загрузка заголовков
|
||||||
header_sync_percent: 'Загрузка заголовков: %{percent}%'
|
header_sync_percent: 'Загрузка заголовков: %{percent}%'
|
||||||
|
@ -31,7 +31,7 @@ sync_status:
|
||||||
tx_hashset_save: Сохранение состояния цепи
|
tx_hashset_save: Сохранение состояния цепи
|
||||||
body_sync: Загрузка блоков
|
body_sync: Загрузка блоков
|
||||||
body_sync_percent: 'Загрузка блоков: %{percent}%'
|
body_sync_percent: 'Загрузка блоков: %{percent}%'
|
||||||
shutdown: Выключение сервера
|
shutdown: Выключение узла
|
||||||
network_node:
|
network_node:
|
||||||
header: Заголовок
|
header: Заголовок
|
||||||
block: Блок
|
block: Блок
|
||||||
|
@ -55,11 +55,15 @@ network_metrics:
|
||||||
difficulty_window: 'Окно сложности %{size}'
|
difficulty_window: 'Окно сложности %{size}'
|
||||||
network_mining:
|
network_mining:
|
||||||
loading: Майнинг будет доступен после синхронизации
|
loading: Майнинг будет доступен после синхронизации
|
||||||
|
server_setup: Настройка stratum-сервера
|
||||||
enable_server: Включить сервер
|
enable_server: Включить сервер
|
||||||
disabled_server: 'Включите stratum-сервер по адресу %{address} или измените настройки, выбрав %{settings} внизу экрана.'
|
server_setting: 'Включите stratum-сервер или измените больше настроек, выбрав %{settings} внизу экрана.'
|
||||||
info: 'Сервер майнинга запущен, вы можете изменить настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
|
info: 'Сервер майнинга запущен, вы можете изменить настройки, выбрав %{settings} внизу экрана. Данные обновляются, когда устройства подключены.'
|
||||||
address: IP Адрес
|
no_ip_addresses: В вашей системе отсутствуют доступные IP адреса, запуск сервера невозможен, проверьте ваше подключение к сети.
|
||||||
wallet: Адрес кошелька
|
choose_ip_address: 'Выберите IP адрес:'
|
||||||
|
change_port: 'Измените порт:'
|
||||||
|
ip_address: IP Адрес
|
||||||
|
rewards_wallet: Кошелёк для наград
|
||||||
server: Stratum-сервер
|
server: Stratum-сервер
|
||||||
miners: Майнеры
|
miners: Майнеры
|
||||||
devices: Устройства
|
devices: Устройства
|
||||||
|
|
|
@ -64,7 +64,7 @@ impl Modal {
|
||||||
/// Default width of the content.
|
/// Default width of the content.
|
||||||
const DEFAULT_WIDTH: i64 = 380;
|
const DEFAULT_WIDTH: i64 = 380;
|
||||||
|
|
||||||
/// Create open and closeable [`Modal`] with center position.
|
/// Create open and closeable Modal with center position.
|
||||||
pub fn new(id: ModalId, location: ModalLocation) -> Self {
|
pub fn new(id: ModalId, location: ModalLocation) -> Self {
|
||||||
Self {
|
Self {
|
||||||
id,
|
id,
|
||||||
|
@ -76,45 +76,45 @@ impl Modal {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup position of [`Modal`] on the screen.
|
/// Setup position of Modal on the screen.
|
||||||
pub fn position(mut self, position: ModalPosition) -> Self {
|
pub fn position(mut self, position: ModalPosition) -> Self {
|
||||||
self.position = position;
|
self.position = position;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if [`Modal`] is open.
|
/// Check if Modal is open.
|
||||||
pub fn is_open(&self) -> bool {
|
pub fn is_open(&self) -> bool {
|
||||||
self.open.load(Ordering::Relaxed)
|
self.open.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark [`Modal`] closed.
|
/// Mark Modal closed.
|
||||||
pub fn close(&self) {
|
pub fn close(&self) {
|
||||||
self.open.store(false, Ordering::Relaxed);
|
self.open.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setup possibility to close [`Modal`].
|
/// Setup possibility to close Modal.
|
||||||
pub fn closeable(self, closeable: bool) -> Self {
|
pub fn closeable(self, closeable: bool) -> Self {
|
||||||
self.closeable.store(closeable, Ordering::Relaxed);
|
self.closeable.store(closeable, Ordering::Relaxed);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Disable possibility to close [`Modal`].
|
/// Disable possibility to close Modal.
|
||||||
pub fn disable_closing(&self) {
|
pub fn disable_closing(&self) {
|
||||||
self.closeable.store(false, Ordering::Relaxed);
|
self.closeable.store(false, Ordering::Relaxed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check if [`Modal`] is closeable.
|
/// Check if Modal is closeable.
|
||||||
pub fn is_closeable(&self) -> bool {
|
pub fn is_closeable(&self) -> bool {
|
||||||
self.closeable.load(Ordering::Relaxed)
|
self.closeable.load(Ordering::Relaxed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set title text.
|
/// Set title text on Modal creation.
|
||||||
pub fn title(mut self, title: String) -> Self {
|
pub fn title(mut self, title: String) -> Self {
|
||||||
self.title = Some(title.to_uppercase());
|
self.title = Some(title.to_uppercase());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show [`Modal`] with provided content.
|
/// Show Modal with provided content.
|
||||||
pub fn ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
pub fn ui(&self, ui: &mut egui::Ui, add_content: impl FnOnce(&mut egui::Ui, &Modal)) {
|
||||||
let width = min(ui.available_width() as i64 - 20, Self::DEFAULT_WIDTH) as f32;
|
let width = min(ui.available_width() as i64 - 20, Self::DEFAULT_WIDTH) as f32;
|
||||||
|
|
||||||
|
@ -252,11 +252,6 @@ impl Modal {
|
||||||
ui.painter().set(bg_idx, bg_shape);
|
ui.painter().set(bg_idx, bg_shape);
|
||||||
|
|
||||||
// Draw line below title.
|
// Draw line below title.
|
||||||
let line_size = Vec2::new(ui.available_width(), 1.0);
|
View::horizontal_line(ui, Colors::STROKE);
|
||||||
let (line_rect, _) = ui.allocate_exact_size(line_size, Sense::hover());
|
|
||||||
let painter = ui.painter();
|
|
||||||
painter.hline(line_rect.x_range(),
|
|
||||||
painter.round_to_pixel(line_rect.center().y),
|
|
||||||
View::DEFAULT_STROKE);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -32,7 +32,7 @@ use crate::Settings;
|
||||||
|
|
||||||
pub trait NetworkTab {
|
pub trait NetworkTab {
|
||||||
fn get_type(&self) -> NetworkTabType;
|
fn get_type(&self) -> NetworkTabType;
|
||||||
fn ui(&mut self, ui: &mut egui::Ui);
|
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
|
@ -67,7 +67,7 @@ impl Default for Network {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Network {
|
impl Network {
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, _: &dyn PlatformCallbacks) {
|
pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) {
|
||||||
egui::TopBottomPanel::top("network_title")
|
egui::TopBottomPanel::top("network_title")
|
||||||
.resizable(false)
|
.resizable(false)
|
||||||
.frame(egui::Frame {
|
.frame(egui::Frame {
|
||||||
|
@ -98,7 +98,7 @@ impl Network {
|
||||||
.. Default::default()
|
.. Default::default()
|
||||||
})
|
})
|
||||||
.show_inside(ui, |ui| {
|
.show_inside(ui, |ui| {
|
||||||
self.current_tab.ui(ui);
|
self.current_tab.ui(ui, cb);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ use grin_servers::DiffBlock;
|
||||||
|
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
|
use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER};
|
||||||
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
|
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
|
@ -33,7 +34,7 @@ impl NetworkTab for NetworkMetrics {
|
||||||
NetworkTabType::Metrics
|
NetworkTabType::Metrics
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
// Show message when node is not running or loading spinner when metrics are not available.
|
// Show message when node is not running or loading spinner when metrics are not available.
|
||||||
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 {
|
||||||
|
|
|
@ -12,13 +12,18 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::fmt::format;
|
||||||
|
use std::net::IpAddr;
|
||||||
|
use std::str::FromStr;
|
||||||
use chrono::{DateTime, NaiveDateTime, Utc};
|
use chrono::{DateTime, NaiveDateTime, Utc};
|
||||||
use egui::{RichText, Rounding, ScrollArea, Stroke};
|
use egui::{RichText, Rounding, ScrollArea, Stroke};
|
||||||
use grin_chain::SyncStatus;
|
use grin_chain::SyncStatus;
|
||||||
use grin_servers::WorkerStats;
|
use grin_servers::WorkerStats;
|
||||||
|
use pnet::ipnetwork::IpNetwork;
|
||||||
|
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, COMPUTER_TOWER, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, PLUGS, PLUGS_CONNECTED, POLYGON};
|
use crate::gui::icons::{BARBELL, CLOCK_AFTERNOON, COMPUTER_TOWER, CPU, CUBE, FADERS, FOLDER_DASHED, FOLDER_NOTCH_MINUS, FOLDER_NOTCH_PLUS, PLUGS, PLUGS_CONNECTED, POLYGON, WRENCH};
|
||||||
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
|
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
|
@ -31,8 +36,9 @@ impl NetworkTab for NetworkMining {
|
||||||
NetworkTabType::Mining
|
NetworkTabType::Mining
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
|
|
||||||
// Show message when node is not running or loading spinner when mining are not available.
|
// Show message when node is not running or loading spinner when mining are not available.
|
||||||
if !server_stats.is_some() || Node::get_sync_status().unwrap() != SyncStatus::NoSync {
|
if !server_stats.is_some() || Node::get_sync_status().unwrap() != SyncStatus::NoSync {
|
||||||
if !Node::is_running() {
|
if !Node::is_running() {
|
||||||
|
@ -50,63 +56,158 @@ impl NetworkTab for NetworkMining {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stratum mining server address.
|
let stratum_stats = &server_stats.as_ref().unwrap().stratum_stats;
|
||||||
let stratum_address = Settings::node_config_to_read()
|
|
||||||
|
// Stratum server address + port from config.
|
||||||
|
let saved_stratum_addr = Settings::node_config_to_read()
|
||||||
.members.clone()
|
.members.clone()
|
||||||
.server.stratum_mining_config.unwrap()
|
.server.stratum_mining_config.unwrap()
|
||||||
.stratum_server_addr.unwrap();
|
.stratum_server_addr.unwrap();
|
||||||
|
let (stratum_addr, stratum_port) = saved_stratum_addr.split_once(":").unwrap();
|
||||||
|
|
||||||
let stratum_stats = &server_stats.as_ref().unwrap().stratum_stats;
|
// List of available ip addresses.
|
||||||
if !stratum_stats.is_running && !Node::is_stratum_server_starting() {
|
let mut addresses = Vec::new();
|
||||||
// Show Stratum setup when mining server is not enabled.
|
for net_if in pnet::datalink::interfaces() {
|
||||||
View::center_content(ui, 162.0, |ui| {
|
for ip in net_if.ips {
|
||||||
let text = t!(
|
if ip.is_ipv4() {
|
||||||
"network_mining.disabled_server",
|
addresses.push(ip.ip());
|
||||||
"address" => stratum_address,
|
}
|
||||||
"settings" => FADERS
|
}
|
||||||
);
|
}
|
||||||
ui.label(RichText::new(text)
|
|
||||||
|
// Show error message when IP addresses are not available on the system.
|
||||||
|
if addresses.is_empty() {
|
||||||
|
View::center_content(ui, 52.0, |ui| {
|
||||||
|
ui.label(RichText::new(t!("network_mining.no_ip_addresses"))
|
||||||
.size(16.0)
|
.size(16.0)
|
||||||
.color(Colors::INACTIVE_TEXT)
|
.color(Colors::INACTIVE_TEXT)
|
||||||
);
|
);
|
||||||
|
|
||||||
let mut addresses = Vec::new();
|
|
||||||
for net_if in pnet::datalink::interfaces() {
|
|
||||||
for ip in net_if.ips {
|
|
||||||
if ip.is_ipv4() {
|
|
||||||
addresses.push(ip);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if addresses.len() != 0 {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
ui.add_space(10.0);
|
|
||||||
|
|
||||||
View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || {
|
|
||||||
Node::start_stratum_server();
|
|
||||||
});
|
|
||||||
|
|
||||||
ui.add_space(2.0);
|
|
||||||
|
|
||||||
// Check if stratum server is enabled at config.
|
|
||||||
let stratum_enabled = Settings::node_config_to_read()
|
|
||||||
.members.clone()
|
|
||||||
.server.stratum_mining_config.unwrap()
|
|
||||||
.enable_stratum_server.unwrap();
|
|
||||||
|
|
||||||
View::checkbox(ui, stratum_enabled, t!("network.autorun"), || {
|
|
||||||
let mut w_node_config = Settings::node_config_to_update();
|
|
||||||
w_node_config.members
|
|
||||||
.server.stratum_mining_config.as_mut().unwrap()
|
|
||||||
.enable_stratum_server = Some(!stratum_enabled);
|
|
||||||
w_node_config.save();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show stratum server setup when mining server is not enabled.
|
||||||
|
if !stratum_stats.is_running && !Node::is_stratum_server_starting() {
|
||||||
|
ScrollArea::vertical()
|
||||||
|
.auto_shrink([false; 2])
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.add_space(6.0);
|
||||||
|
View::sub_title(ui,
|
||||||
|
format!("{} {}", WRENCH, t!("network_mining.server_setup")));
|
||||||
|
|
||||||
|
ui.add_space(4.0);
|
||||||
|
View::horizontal_line(ui, Colors::ITEM_STROKE);
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
ui.vertical_centered(|ui| {
|
||||||
|
ui.label(RichText::new(t!("network_mining.choose_ip_address"))
|
||||||
|
.size(16.0)
|
||||||
|
.color(Colors::GRAY)
|
||||||
|
);
|
||||||
|
ui.add_space(10.0);
|
||||||
|
|
||||||
|
if addresses.len() != 0 {
|
||||||
|
let saved_ip_addr = &IpAddr::from_str(stratum_addr).unwrap();
|
||||||
|
let mut selected_addr = saved_ip_addr;
|
||||||
|
|
||||||
|
// Set first IP address as current if saved is not present at system.
|
||||||
|
if !addresses.contains(selected_addr) {
|
||||||
|
selected_addr = addresses.get(0).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show available IP addresses on the system.
|
||||||
|
let _ = addresses.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());
|
||||||
|
})
|
||||||
|
});
|
||||||
|
ui.add_space(12.0);
|
||||||
|
} else {
|
||||||
|
let addr = x.get(0).unwrap();
|
||||||
|
View::radio_value(ui,
|
||||||
|
&mut selected_addr,
|
||||||
|
addr,
|
||||||
|
addr.to_string());
|
||||||
|
ui.add_space(4.0);
|
||||||
|
}
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
// Save stratum server address at config if it was changed.
|
||||||
|
if saved_ip_addr != selected_addr {
|
||||||
|
let addr_to_save = format!("{}:{}", selected_addr, stratum_port);
|
||||||
|
let mut w_node_config = Settings::node_config_to_update();
|
||||||
|
w_node_config.members
|
||||||
|
.server.stratum_mining_config.as_mut().unwrap()
|
||||||
|
.stratum_server_addr = Some(addr_to_save);
|
||||||
|
w_node_config.save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ui.label(RichText::new(t!("network_mining.change_port"))
|
||||||
|
.size(16.0)
|
||||||
|
.color(Colors::GRAY)
|
||||||
|
);
|
||||||
|
|
||||||
|
// Show button to choose server port.
|
||||||
|
ui.add_space(6.0);
|
||||||
|
View::button(ui, stratum_port.to_string(), Colors::WHITE, || {
|
||||||
|
//TODO: Open modal to change value
|
||||||
|
cb.show_keyboard();
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.add_space(14.0);
|
||||||
|
View::horizontal_line(ui, Colors::ITEM_STROKE);
|
||||||
|
ui.add_space(6.0);
|
||||||
|
|
||||||
|
// Show message about stratum server config.
|
||||||
|
let text = t!(
|
||||||
|
"network_mining.server_setting",
|
||||||
|
"address" => saved_stratum_addr,
|
||||||
|
"settings" => FADERS
|
||||||
|
);
|
||||||
|
ui.label(RichText::new(text)
|
||||||
|
.size(16.0)
|
||||||
|
.color(Colors::INACTIVE_TEXT)
|
||||||
|
);
|
||||||
|
ui.add_space(8.0);
|
||||||
|
|
||||||
|
// Show button to enable server.
|
||||||
|
View::button(ui, t!("network_mining.enable_server"), Colors::GOLD, || {
|
||||||
|
Node::start_stratum_server();
|
||||||
|
});
|
||||||
|
ui.add_space(2.0);
|
||||||
|
|
||||||
|
let stratum_enabled = Settings::node_config_to_read()
|
||||||
|
.members.clone()
|
||||||
|
.server.stratum_mining_config.unwrap()
|
||||||
|
.enable_stratum_server.unwrap();
|
||||||
|
|
||||||
|
// Show stratum server autorun checkbox.
|
||||||
|
View::checkbox(ui, stratum_enabled, t!("network.autorun"), || {
|
||||||
|
let mut w_node_config = Settings::node_config_to_update();
|
||||||
|
w_node_config.members
|
||||||
|
.server.stratum_mining_config.as_mut().unwrap()
|
||||||
|
.enable_stratum_server = Some(!stratum_enabled);
|
||||||
|
w_node_config.save();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.add_space(6.0);
|
||||||
|
});
|
||||||
|
return;
|
||||||
} else if Node::is_stratum_server_starting() {
|
} else if Node::is_stratum_server_starting() {
|
||||||
// Show loading spinner when mining server is starting.
|
|
||||||
ui.centered_and_justified(|ui| {
|
ui.centered_and_justified(|ui| {
|
||||||
View::big_loading_spinner(ui);
|
View::big_loading_spinner(ui);
|
||||||
});
|
});
|
||||||
|
@ -118,8 +219,8 @@ impl NetworkTab for NetworkMining {
|
||||||
ui.columns(2, |columns| {
|
ui.columns(2, |columns| {
|
||||||
columns[0].vertical_centered(|ui| {
|
columns[0].vertical_centered(|ui| {
|
||||||
View::rounded_box(ui,
|
View::rounded_box(ui,
|
||||||
stratum_address,
|
saved_stratum_addr,
|
||||||
t!("network_mining.address"),
|
t!("network_mining.ip_address"),
|
||||||
[true, false, true, false]);
|
[true, false, true, false]);
|
||||||
});
|
});
|
||||||
columns[1].vertical_centered(|ui| {
|
columns[1].vertical_centered(|ui| {
|
||||||
|
@ -131,7 +232,7 @@ impl NetworkTab for NetworkMining {
|
||||||
.replace("http://", "");
|
.replace("http://", "");
|
||||||
View::rounded_box(ui,
|
View::rounded_box(ui,
|
||||||
wallet_address,
|
wallet_address,
|
||||||
t!("network_mining.wallet"),
|
t!("network_mining.rewards_wallet"),
|
||||||
[false, true, false, true]);
|
[false, true, false, true]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -198,6 +299,10 @@ impl NetworkTab for NetworkMining {
|
||||||
// Show workers stats or info text when possible.
|
// Show workers stats or info text when possible.
|
||||||
let workers_size = stratum_stats.worker_stats.len();
|
let workers_size = stratum_stats.worker_stats.len();
|
||||||
if workers_size != 0 && stratum_stats.num_workers > 0 {
|
if workers_size != 0 && stratum_stats.num_workers > 0 {
|
||||||
|
ui.add_space(4.0);
|
||||||
|
View::horizontal_line(ui, Colors::ITEM_STROKE);
|
||||||
|
ui.add_space(4.0);
|
||||||
|
|
||||||
ScrollArea::vertical()
|
ScrollArea::vertical()
|
||||||
.auto_shrink([false; 2])
|
.auto_shrink([false; 2])
|
||||||
.id_source("stratum_workers_scroll")
|
.id_source("stratum_workers_scroll")
|
||||||
|
@ -240,9 +345,8 @@ fn draw_worker_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal_wrapped(|ui| {
|
||||||
ui.add_space(6.0);
|
ui.vertical_centered_justified(|ui| {
|
||||||
ui.vertical(|ui| {
|
|
||||||
let mut rect = ui.available_rect_before_wrap();
|
let mut rect = ui.available_rect_before_wrap();
|
||||||
rect.set_height(WORKER_UI_HEIGHT);
|
rect.set_height(WORKER_UI_HEIGHT);
|
||||||
ui.painter().rect(
|
ui.painter().rect(
|
||||||
|
@ -259,25 +363,25 @@ fn draw_worker_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
|
||||||
|
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
ui.horizontal_top(|ui| {
|
ui.horizontal_top(|ui| {
|
||||||
let (status_text, status_icon) = match ws.is_connected {
|
let (status_text, status_icon, status_color) = match ws.is_connected {
|
||||||
true => { (t!("network_mining.connected"), PLUGS_CONNECTED) }
|
true => { (t!("network_mining.connected"), PLUGS_CONNECTED, Colors::BLACK) }
|
||||||
false => { (t!("network_mining.disconnected"), PLUGS) }
|
false => { (t!("network_mining.disconnected"), PLUGS, Colors::INACTIVE_TEXT) }
|
||||||
};
|
};
|
||||||
ui.add_space(5.0);
|
ui.add_space(5.0);
|
||||||
ui.heading(RichText::new(status_icon)
|
ui.heading(RichText::new(status_icon)
|
||||||
.color(Colors::BLACK)
|
.color(status_color)
|
||||||
.size(18.0));
|
.size(18.0));
|
||||||
ui.add_space(2.0);
|
ui.add_space(2.0);
|
||||||
|
|
||||||
// Draw worker ID.
|
// Draw worker ID.
|
||||||
ui.heading(RichText::new(&ws.id)
|
ui.heading(RichText::new(&ws.id)
|
||||||
.color(Colors::BLACK)
|
.color(status_color)
|
||||||
.size(18.0));
|
.size(18.0));
|
||||||
ui.add_space(3.0);
|
ui.add_space(3.0);
|
||||||
|
|
||||||
// Draw worker status.
|
// Draw worker status.
|
||||||
ui.heading(RichText::new(status_text)
|
ui.heading(RichText::new(status_text)
|
||||||
.color(Colors::BLACK)
|
.color(status_color)
|
||||||
.size(18.0));
|
.size(18.0));
|
||||||
});
|
});
|
||||||
ui.horizontal_top(|ui| {
|
ui.horizontal_top(|ui| {
|
||||||
|
@ -347,7 +451,6 @@ fn draw_worker_stats(ui: &mut egui::Ui, ws: &WorkerStats, rounding: [bool; 2]) {
|
||||||
.size(16.0));
|
.size(16.0));
|
||||||
|
|
||||||
});
|
});
|
||||||
ui.add_space(2.0);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
|
@ -18,6 +18,7 @@ use grin_servers::PeerStats;
|
||||||
|
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK};
|
||||||
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
|
use crate::gui::views::{Network, NetworkTab, NetworkTabType, View};
|
||||||
use crate::node::Node;
|
use crate::node::Node;
|
||||||
|
|
||||||
|
@ -29,7 +30,7 @@ impl NetworkTab for NetworkNode {
|
||||||
NetworkTabType::Node
|
NetworkTabType::Node
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||||
let server_stats = Node::get_stats();
|
let server_stats = Node::get_stats();
|
||||||
// Show message when node is not running or loading spinner when stats are not available.
|
// Show message when node is not running or loading spinner when stats are not available.
|
||||||
if !server_stats.is_some() {
|
if !server_stats.is_some() {
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use grin_core::global::ChainTypes;
|
use grin_core::global::ChainTypes;
|
||||||
|
use crate::gui::platform::PlatformCallbacks;
|
||||||
use crate::gui::views::{NetworkTab, NetworkTabType};
|
use crate::gui::views::{NetworkTab, NetworkTabType};
|
||||||
use crate::Settings;
|
use crate::Settings;
|
||||||
|
|
||||||
|
@ -24,6 +25,6 @@ impl NetworkTab for NetworkSettings {
|
||||||
NetworkTabType::Settings
|
NetworkTabType::Settings
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) {
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -12,13 +12,13 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use egui::{Button, PointerState, Response, RichText, Sense, Spinner, Widget};
|
use egui::{AboveOrBelow, Button, PointerState, Response, RichText, ScrollArea, Sense, Spinner, Widget, WidgetText};
|
||||||
use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke};
|
use egui::epaint::{Color32, FontId, RectShape, Rounding, Stroke};
|
||||||
use egui::epaint::text::TextWrapping;
|
use egui::epaint::text::TextWrapping;
|
||||||
use egui::text::{LayoutJob, TextFormat};
|
use egui::text::{LayoutJob, TextFormat};
|
||||||
|
|
||||||
use crate::gui::Colors;
|
use crate::gui::Colors;
|
||||||
use crate::gui::icons::{CHECK_SQUARE, CIRCLE, RADIO_BUTTON, SQUARE};
|
use crate::gui::icons::{CARET_DOWN, CHECK_SQUARE, CIRCLE, RADIO_BUTTON, SQUARE};
|
||||||
|
|
||||||
pub struct View;
|
pub struct View;
|
||||||
|
|
||||||
|
@ -62,17 +62,17 @@ impl View {
|
||||||
ui.add_space(4.0);
|
ui.add_space(4.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Temporary button click optimization for touch screens.
|
/// Temporary click optimization for touch screens, return `true` if it was clicked.
|
||||||
fn on_button_click(ui: &mut egui::Ui, resp: Response, action: impl FnOnce()) {
|
fn touched(ui: &mut egui::Ui, resp: Response) -> bool {
|
||||||
let drag_resp = resp.interact(Sense::click_and_drag());
|
let drag_resp = resp.interact(Sense::click_and_drag());
|
||||||
// Clear pointer event if dragging is out of button area
|
// Clear pointer event if dragging is out of button area
|
||||||
if drag_resp.dragged() && !ui.rect_contains_pointer(drag_resp.rect) {
|
if drag_resp.dragged() && !ui.rect_contains_pointer(drag_resp.rect) {
|
||||||
ui.input_mut().pointer = PointerState::default();
|
ui.input_mut().pointer = PointerState::default();
|
||||||
}
|
}
|
||||||
// Call click action if button is clicked or drag released
|
|
||||||
if drag_resp.drag_released() || drag_resp.clicked() {
|
if drag_resp.drag_released() || drag_resp.clicked() {
|
||||||
(action)();
|
return true
|
||||||
};
|
}
|
||||||
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Title button with transparent background fill color, contains only icon.
|
/// Title button with transparent background fill color, contains only icon.
|
||||||
|
@ -80,13 +80,13 @@ impl View {
|
||||||
ui.scope(|ui| {
|
ui.scope(|ui| {
|
||||||
// Disable stroke around title buttons on hover
|
// Disable stroke around title buttons on hover
|
||||||
ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE;
|
ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE;
|
||||||
|
|
||||||
let wt = RichText::new(icon.to_string()).size(24.0).color(Colors::TITLE);
|
let wt = RichText::new(icon.to_string()).size(24.0).color(Colors::TITLE);
|
||||||
let br = Button::new(wt)
|
let br = Button::new(wt)
|
||||||
.fill(Colors::TRANSPARENT)
|
.fill(Colors::TRANSPARENT)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
if Self::touched(ui, br) {
|
||||||
Self::on_button_click(ui, br, action);
|
(action)();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,34 +96,32 @@ impl View {
|
||||||
true => { Colors::TITLE }
|
true => { Colors::TITLE }
|
||||||
false => { Colors::TEXT }
|
false => { Colors::TEXT }
|
||||||
};
|
};
|
||||||
let wt = RichText::new(icon.to_string()).size(24.0).color(text_color);
|
|
||||||
|
|
||||||
let stroke = match active {
|
let stroke = match active {
|
||||||
true => { Stroke::NONE }
|
true => { Stroke::NONE }
|
||||||
false => { Self::DEFAULT_STROKE }
|
false => { Self::DEFAULT_STROKE }
|
||||||
};
|
};
|
||||||
|
|
||||||
let color = match active {
|
let color = match active {
|
||||||
true => { Colors::FILL }
|
true => { Colors::FILL }
|
||||||
false => { Colors::WHITE }
|
false => { Colors::WHITE }
|
||||||
};
|
};
|
||||||
let br = Button::new(wt)
|
let br = Button::new(RichText::new(icon.to_string()).size(24.0).color(text_color))
|
||||||
.stroke(stroke)
|
.stroke(stroke)
|
||||||
.fill(color)
|
.fill(color)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
if Self::touched(ui, br) {
|
||||||
Self::on_button_click(ui, br, action);
|
(action)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw [`Button`] with specified background fill color.
|
/// Draw [`Button`] with specified background fill color.
|
||||||
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
|
pub fn button(ui: &mut egui::Ui, text: String, fill_color: Color32, action: impl FnOnce()) {
|
||||||
let wt = RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON);
|
let br = Button::new(RichText::new(text.to_uppercase()).size(18.0).color(Colors::BUTTON))
|
||||||
let br = Button::new(wt)
|
|
||||||
.stroke(Self::DEFAULT_STROKE)
|
.stroke(Self::DEFAULT_STROKE)
|
||||||
.fill(fill_color)
|
.fill(fill_color)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
if Self::touched(ui, br) {
|
||||||
Self::on_button_click(ui, br, action);
|
(action)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw rounded box with some value and label in the middle,
|
/// Draw rounded box with some value and label in the middle,
|
||||||
|
@ -212,31 +210,33 @@ impl View {
|
||||||
true => { (format!("{} {}", CHECK_SQUARE, text), Colors::BUTTON) }
|
true => { (format!("{} {}", CHECK_SQUARE, text), Colors::BUTTON) }
|
||||||
false => { (format!("{} {}", SQUARE, text), Colors::TEXT) }
|
false => { (format!("{} {}", SQUARE, text), Colors::TEXT) }
|
||||||
};
|
};
|
||||||
|
let br = Button::new(RichText::new(text_value).size(18.0).color(color))
|
||||||
let wt = RichText::new(text_value).size(18.0).color(color);
|
|
||||||
let br = Button::new(wt)
|
|
||||||
.frame(false)
|
.frame(false)
|
||||||
.stroke(Stroke::NONE)
|
.stroke(Stroke::NONE)
|
||||||
.fill(Colors::TRANSPARENT)
|
.fill(Colors::TRANSPARENT)
|
||||||
.ui(ui);
|
.ui(ui);
|
||||||
|
if Self::touched(ui, br) {
|
||||||
Self::on_button_click(ui, br, callback);
|
(callback)();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draw the radio button with callback on select.
|
/// Show a [`RadioButton`]. It is selected if `*current_value == selected_value`.
|
||||||
pub fn radio_button(ui: &mut egui::Ui, selected: bool, text: String, callback: impl FnOnce()) {
|
/// If clicked, `selected_value` is assigned to `*current_value`.
|
||||||
let (text_value, color) = match selected {
|
pub fn radio_value<T: PartialEq>(ui: &mut egui::Ui, current: &mut T, value: T, text: String) {
|
||||||
true => { (format!("{} {}", RADIO_BUTTON, text), Colors::BUTTON) }
|
let mut response = ui.radio(*current == value, text);
|
||||||
false => { (format!("{} {}", CIRCLE, text), Colors::TEXT) }
|
if Self::touched(ui, response.clone()) && *current != value {
|
||||||
};
|
*current = value;
|
||||||
|
response.mark_changed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let wt = RichText::new(text_value).size(18.0).color(color);
|
/// Draw horizontal line
|
||||||
let br = Button::new(wt)
|
pub fn horizontal_line(ui: &mut egui::Ui, color: Color32) {
|
||||||
.frame(false)
|
let line_size = egui::Vec2::new(ui.available_width(), 1.0);
|
||||||
.stroke(Stroke::NONE)
|
let (line_rect, _) = ui.allocate_exact_size(line_size, Sense::hover());
|
||||||
.fill(Colors::TRANSPARENT)
|
let painter = ui.painter();
|
||||||
.ui(ui);
|
painter.hline(line_rect.x_range(),
|
||||||
|
painter.round_to_pixel(line_rect.center().y),
|
||||||
Self::on_button_click(ui, br, callback);
|
Stroke { width: 1.0, color });
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -221,13 +221,13 @@ impl Node {
|
||||||
};
|
};
|
||||||
|
|
||||||
if Node::is_restarting() {
|
if Node::is_restarting() {
|
||||||
return t!("sync_status.server_restarting")
|
return t!("sync_status.node_restarting")
|
||||||
}
|
}
|
||||||
|
|
||||||
let sync_status = Self::get_sync_status();
|
let sync_status = Self::get_sync_status();
|
||||||
|
|
||||||
if sync_status.is_none() {
|
if sync_status.is_none() {
|
||||||
return t!("sync_status.server_down")
|
return t!("sync_status.node_down")
|
||||||
}
|
}
|
||||||
|
|
||||||
match sync_status.unwrap() {
|
match sync_status.unwrap() {
|
||||||
|
|
Loading…
Reference in a new issue