// 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 egui::{RichText, ScrollArea}; use crate::gui::Colors; use crate::gui::icons::ARROW_COUNTER_CLOCKWISE; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{Modal, ModalPosition, View}; use crate::gui::views::network::setup::{DandelionSetup, NodeSetup, P2PSetup, PoolSetup, StratumSetup}; use crate::gui::views::types::{NetworkTab, NetworkTabType}; use crate::node::{Node, NodeConfig}; /// Integrated node settings tab content. #[derive(Default)] pub struct NetworkSettings { node: NodeSetup, p2p: P2PSetup, stratum: StratumSetup, pool: PoolSetup, dandelion: DandelionSetup } impl NetworkTab for NetworkSettings { fn get_type(&self) -> NetworkTabType { NetworkTabType::Settings } fn ui(&mut self, ui: &mut egui::Ui, cb: &dyn PlatformCallbacks) { ScrollArea::vertical() .id_source("network_settings") .auto_shrink([false; 2]) .show(ui, |ui| { self.node.ui(ui, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); self.p2p.ui(ui, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); self.stratum.ui(ui, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); self.pool.ui(ui, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(4.0); self.dandelion.ui(ui, cb); ui.add_space(6.0); View::horizontal_line(ui, Colors::STROKE); ui.add_space(6.0); self.reset_settings_ui(ui); }); } fn on_modal_ui(&mut self, ui: &mut egui::Ui, modal: &Modal, cb: &dyn PlatformCallbacks) { match modal.id { // Settings modals. Self::NODE_RESTART_REQUIRED_MODAL => self.node_restart_required_modal(ui, modal), Self::RESET_SETTINGS_MODAL => self.reset_settings_confirmation_modal(ui, modal), // Node server setup modals. NodeSetup::API_PORT_MODAL => self.node.api_port_modal(ui, modal, cb), NodeSetup::API_SECRET_MODAL => self.node.secret_modal(ui, modal, cb), NodeSetup::FOREIGN_API_SECRET_MODAL => self.node.secret_modal(ui, modal, cb), NodeSetup::FTL_MODAL => self.node.ftl_modal(ui, modal, cb), // P2P setup modals. P2PSetup::PORT_MODAL => self.p2p.port_modal(ui, modal, cb), P2PSetup::CUSTOM_SEED_MODAL => self.p2p.peer_modal(ui, modal, cb), P2PSetup::ALLOW_PEER_MODAL => self.p2p.peer_modal(ui, modal, cb), P2PSetup::DENY_PEER_MODAL => self.p2p.peer_modal(ui, modal, cb), P2PSetup::PREFER_PEER_MODAL => self.p2p.peer_modal(ui, modal, cb), P2PSetup::BAN_WINDOW_MODAL => self.p2p.ban_window_modal(ui, modal, cb), P2PSetup::MAX_INBOUND_MODAL => self.p2p.max_inbound_modal(ui, modal, cb), P2PSetup::MAX_OUTBOUND_MODAL => self.p2p.max_outbound_modal(ui, modal, cb), P2PSetup::MIN_OUTBOUND_MODAL => self.p2p.min_outbound_modal(ui, modal, cb), // Stratum server setup modals. StratumSetup::STRATUM_PORT_MODAL => self.stratum.port_modal(ui, modal, cb), StratumSetup::ATTEMPT_TIME_MODAL => self.stratum.attempt_modal(ui, modal, cb), StratumSetup::MIN_SHARE_DIFF_MODAL => self.stratum.min_diff_modal(ui, modal, cb), // Pool setup modals. PoolSetup::FEE_BASE_MODAL => self.pool.fee_base_modal(ui, modal, cb), PoolSetup::REORG_PERIOD_MODAL => self.pool.reorg_period_modal(ui, modal, cb), PoolSetup::POOL_SIZE_MODAL => self.pool.pool_size_modal(ui, modal, cb), PoolSetup::STEMPOOL_SIZE_MODAL => self.pool.stempool_size_modal(ui, modal, cb), PoolSetup::MAX_WEIGHT_MODAL => self.pool.max_weight_modal(ui, modal, cb), // Dandelion setup modals. DandelionSetup::EPOCH_MODAL => self.dandelion.epoch_modal(ui, modal, cb), DandelionSetup::EMBARGO_MODAL => self.dandelion.embargo_modal(ui, modal, cb), DandelionSetup::AGGREGATION_MODAL => self.dandelion.aggregation_modal(ui, modal, cb), DandelionSetup::STEM_PROBABILITY_MODAL => self.dandelion.stem_prob_modal(ui, modal, cb), _ => {} } } } impl NetworkSettings { /// Identifier for node restart confirmation [`Modal`]. pub const NODE_RESTART_REQUIRED_MODAL: &'static str = "node_restart_required"; /// Identifier for settings reset confirmation [`Modal`]. pub const RESET_SETTINGS_MODAL: &'static str = "reset_settings"; /// Draw button to reset integrated node settings to default values. fn reset_settings_ui(&self, ui: &mut egui::Ui) { ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.reset_settings_desc")) .size(16.0) .color(Colors::TEXT)); ui.add_space(8.0); let button_text = format!("{} {}", ARROW_COUNTER_CLOCKWISE, t!("network_settings.reset_settings")); View::button(ui, button_text, Colors::GOLD, || { // Show modal to confirm settings reset. Modal::new(Self::RESET_SETTINGS_MODAL) .position(ModalPosition::Center) .title(t!("modal.confirmation")) .show(); }); // Show reminder to restart enabled node. if Node::is_running() { ui.add_space(12.0); ui.label(RichText::new(t!("network_settings.restart_node_required")) .size(16.0) .color(Colors::GRAY) ); } ui.add_space(12.0); }); } /// Confirmation to reset settings to default values. fn reset_settings_confirmation_modal(&self, ui: &mut egui::Ui, modal: &Modal) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.reset_settings_desc")) .size(17.0) .color(Colors::TEXT)); ui.add_space(8.0); }); // Show modal buttons. ui.scope(|ui| { // Setup spacing between buttons. ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { View::button(ui, t!("network_settings.reset"), Colors::WHITE, || { NodeConfig::reset_to_default(); modal.close(); }); }); columns[1].vertical_centered_justified(|ui| { View::button(ui, t!("modal.cancel"), Colors::WHITE, || { modal.close(); }); }); }); ui.add_space(6.0); }); } /// Reminder to restart enabled node to show on edit setting at [`Modal`]. pub fn node_restart_required_ui(ui: &mut egui::Ui) { if Node::is_running() { ui.add_space(12.0); ui.label(RichText::new(t!("network_settings.restart_node_required")) .size(16.0) .color(Colors::GREEN) ); } } /// Show [`Modal`] to ask node restart if setting is changed on enabled node. pub fn show_node_restart_required_modal() { if Node::is_running() { // Show modal to apply changes by node restart. Modal::new(Self::NODE_RESTART_REQUIRED_MODAL) .position(ModalPosition::Center) .title(t!("network.settings")) .show(); } } /// Node restart reminder modal content. fn node_restart_required_modal(&self, ui: &mut egui::Ui, modal: &Modal) { ui.add_space(6.0); ui.vertical_centered(|ui| { ui.label(RichText::new(t!("network_settings.restart_node_required")) .size(17.0) .color(Colors::GRAY)); ui.add_space(8.0); }); // Show modal buttons. ui.scope(|ui| { // Setup spacing between buttons. ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { View::button(ui, t!("network_settings.restart"), Colors::WHITE, || { Node::restart(); modal.close(); }); }); columns[1].vertical_centered_justified(|ui| { View::button(ui, t!("modal.cancel"), Colors::WHITE, || { modal.close(); }); }); }); ui.add_space(6.0); }); } /// Draw IP addresses as radio buttons. pub fn ip_addrs_ui(ui: &mut egui::Ui, saved_ip: &String, ips: &Vec, on_change: impl FnOnce(&String)) { let mut selected_ip = saved_ip; // Set first IP address as current if saved is not present at system. if !ips.contains(saved_ip) { selected_ip = ips.get(0).unwrap(); } ui.add_space(2.0); // Show available IP addresses on the system. let _ = ips.chunks(2).map(|x| { if x.len() == 2 { ui.columns(2, |columns| { let ip_left = x.get(0).unwrap(); columns[0].vertical_centered(|ui| { View::radio_value(ui, &mut selected_ip, ip_left, ip_left.to_string()); }); let ip_right = x.get(1).unwrap(); columns[1].vertical_centered(|ui| { View::radio_value(ui, &mut selected_ip, ip_right, ip_right.to_string()); }) }); } else { let ip = x.get(0).unwrap(); View::radio_value(ui, &mut selected_ip, ip, ip.to_string()); } ui.add_space(12.0); }).collect::>(); if saved_ip != selected_ip { (on_change)(&selected_ip.to_string()); } } /// 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_ips")) .size(16.0) .color(Colors::INACTIVE_TEXT) ); ui.add_space(6.0); }); } }