diff --git a/Cargo.lock b/Cargo.lock index 1591555..b066c4c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -320,12 +320,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64ct" version = "1.6.0" @@ -678,12 +672,6 @@ dependencies = [ "unicode-width", ] -[[package]] -name = "color_quant" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" - [[package]] name = "combine" version = "4.6.6" @@ -946,15 +934,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "data-url" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" -dependencies = [ - "matches", -] - [[package]] name = "digest" version = "0.9.0" @@ -1151,10 +1130,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1975cd88ff7430f93b29e6b9868b648a8ff6a43b08b9ff8474ee0a648bd8f9a6" dependencies = [ "egui", - "resvg", "serde", - "tiny-skia 0.6.6", - "usvg", ] [[package]] @@ -1371,39 +1347,12 @@ dependencies = [ "miniz_oxide 0.6.2", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "fontconfig-parser" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ab2e12762761366dcb876ab8b6e0cfa4797ddcd890575919f008b5ba655672a" -dependencies = [ - "roxmltree 0.18.0", -] - -[[package]] -name = "fontdb" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52186a39c335aa6f79fc0bf1c3cf854870b6ad4e50a7bb8a59b4ba1331f478a" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "ttf-parser 0.17.1", -] - [[package]] name = "foreign-types" version = "0.3.2" @@ -1650,16 +1599,6 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gimli" version = "0.27.2" @@ -1818,12 +1757,15 @@ version = "0.1.0" dependencies = [ "android_logger", "built", + "chrono", + "dirs 2.0.2", "eframe", "egui", "egui_demo_lib", "egui_extras", "env_logger 0.10.0", "futures 0.3.28", + "grin_chain", "grin_config", "grin_core", "grin_servers", @@ -2441,12 +2383,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" - [[package]] name = "js-sys" version = "0.3.61" @@ -2678,12 +2614,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matches" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" - [[package]] name = "memchr" version = "2.5.0" @@ -3201,7 +3131,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228" dependencies = [ - "ttf-parser 0.18.1", + "ttf-parser", ] [[package]] @@ -3293,12 +3223,6 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" -[[package]] -name = "pico-args" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" - [[package]] name = "pin-project" version = "1.0.12" @@ -3572,12 +3496,6 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9" -[[package]] -name = "rctree" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae028b272a6e99d9f8260ceefa3caa09300a8d6c8d2b2001316474bc52122e9" - [[package]] name = "rdrand" version = "0.4.0" @@ -3645,33 +3563,6 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" -[[package]] -name = "resvg" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34489194784b86c03c3d688258e2ba73f3c82700ba4673ee2ecad5ae540b9438" -dependencies = [ - "gif", - "jpeg-decoder", - "log", - "pico-args", - "png", - "rgb", - "svgfilters", - "svgtypes", - "tiny-skia 0.6.6", - "usvg", -] - -[[package]] -name = "rgb" -version = "0.8.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20ec2d3e3fc7a92ced357df9cebd5a10b6fb2aa1ee797bf7e9ce2f17dffc8f59" -dependencies = [ - "bytemuck", -] - [[package]] name = "ring" version = "0.16.20" @@ -3698,24 +3589,6 @@ dependencies = [ "opaque-debug", ] -[[package]] -name = "roxmltree" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" -dependencies = [ - "xmlparser", -] - -[[package]] -name = "roxmltree" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8f595a457b6b8c6cda66a48503e92ee8d19342f905948f29c383200ec9eb1d8" -dependencies = [ - "xmlparser", -] - [[package]] name = "rust-i18n" version = "1.1.4" @@ -3842,22 +3715,6 @@ dependencies = [ "security-framework", ] -[[package]] -name = "rustybuzz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a617c811f5c9a7060fe511d35d13bf5b9f0463ce36d63ce666d05779df2b4eba" -dependencies = [ - "bitflags 1.3.2", - "bytemuck", - "smallvec", - "ttf-parser 0.15.2", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-general-category", - "unicode-script", -] - [[package]] name = "ryu" version = "1.0.13" @@ -3928,7 +3785,7 @@ dependencies = [ "crossfont", "log", "smithay-client-toolkit", - "tiny-skia 0.7.0", + "tiny-skia", ] [[package]] @@ -4071,15 +3928,6 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "238abfbb77c1915110ad968465608b68e869e0772622c9656714e73e5a1a522f" -[[package]] -name = "simplecss" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a11be7c62927d9427e9f40f3444d5499d868648e2edbc4e2116de69e7ec0e89d" -dependencies = [ - "log", -] - [[package]] name = "siphasher" version = "0.3.10" @@ -4196,25 +4044,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63" -[[package]] -name = "svgfilters" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce" -dependencies = [ - "float-cmp", - "rgb", -] - -[[package]] -name = "svgtypes" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564" -dependencies = [ - "siphasher", -] - [[package]] name = "syn" version = "0.15.44" @@ -4353,20 +4182,6 @@ dependencies = [ "winapi 0.3.9", ] -[[package]] -name = "tiny-skia" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d049bfef0eaa2521e75d9ffb5ce86ad54480932ae19b85f78bec6f52c4d30d78" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "bytemuck", - "cfg-if 1.0.0", - "png", - "safe_arch", -] - [[package]] name = "tiny-skia" version = "0.7.0" @@ -4567,18 +4382,6 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -[[package]] -name = "ttf-parser" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" - -[[package]] -name = "ttf-parser" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375812fa44dab6df41c195cd2f7fecb488f6c09fbaafb62807488cefab642bff" - [[package]] name = "ttf-parser" version = "0.18.1" @@ -4615,24 +4418,6 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" - -[[package]] -name = "unicode-ccc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" - -[[package]] -name = "unicode-general-category" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07547e3ee45e28326cc23faac56d44f58f16ab23e413db526debce3b0bfd2742" - [[package]] name = "unicode-ident" version = "1.0.8" @@ -4648,24 +4433,12 @@ dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-script" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d817255e1bed6dfd4ca47258685d14d2bdcfbc64fdc9e3819bd5848057b8ecc" - [[package]] name = "unicode-segmentation" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" version = "0.1.10" @@ -4716,33 +4489,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "usvg" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a82565b5c96dcbb58c9bdbb6aa3642abd395a6a6b480658532c6f74c3c4b7a" -dependencies = [ - "base64 0.13.1", - "data-url", - "flate2", - "float-cmp", - "fontdb", - "kurbo", - "log", - "pico-args", - "rctree", - "roxmltree 0.14.1", - "rustybuzz", - "simplecss", - "siphasher", - "svgtypes", - "ttf-parser 0.15.2", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", -] - [[package]] name = "vcpkg" version = "0.2.15" @@ -4981,12 +4727,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - [[package]] name = "wgpu" version = "0.14.2" @@ -5476,18 +5216,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" -[[package]] -name = "xmlparser" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" - [[package]] name = "yaml-rust" version = "0.4.5" diff --git a/Cargo.toml b/Cargo.toml index a570038..7bd9b4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,7 +14,7 @@ build = "src/build/build.rs" log = "0.4" #android-activity = { version = "0.4", features = ["game-activity"] } #grin_api = "5.1.2" -#grin_chain = "5.1.2" +grin_chain = "5.1.2" grin_config = "5.1.2" grin_core = "5.1.2" #grin_keychain = "5.1.2" @@ -33,17 +33,19 @@ openssl-sys = { version = "0.9.82", features = ["vendored"] } pollster = "0.3.0" wgpu = "0.14.0" egui = "0.20.1" -egui_extras = { version = "0.20.0", features = [ "svg" ] } +egui_extras = { version = "0.20.0" } eframe = { version = "0.20.1", features = [ "wgpu" ] } egui_demo_lib = "0.20.0" ## grin_servers futures = "0.3" +dirs = "2.0" ## other once_cell = "1.10.0" rust-i18n = "1.1.4" sys-locale = "0.3.0" +chrono = "0.4.11" [patch.crates-io] winit = { git = "https://github.com/rib/winit", branch = "android-activity" } diff --git a/locales/en.yml b/locales/en.yml index 76bc71a..78d86b9 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -1 +1,4 @@ -accounts: Accounts \ No newline at end of file +accounts: Accounts +node: Node +metrics: Metrics +settings: Settings \ No newline at end of file diff --git a/src/gui/app.rs b/src/gui/app.rs index 93ca7ce..c038601 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -56,3 +56,11 @@ impl App { } } +pub fn is_dual_panel_mode(frame: &mut Frame) -> bool { + is_landscape(frame) && frame.info().window_info.size.x > 400.0 +} + +pub fn is_landscape(frame: &mut Frame) -> bool { + return frame.info().window_info.size.x > frame.info().window_info.size.y +} + diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 13f70ce..b25625e 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -27,9 +27,10 @@ pub const COLOR_LIGHT: egui::Color32 = egui::Color32::from_gray(240); pub const COLOR_DARK: egui::Color32 = egui::Color32::from_gray(60); // Material icons chars -pub const SYM_ARROW_BACK: &str = "";//""; -pub const SYM_ADD: &str = "+"; +pub const SYM_ARROW_BACK: &str = ""; pub const SYM_ACCOUNTS: &str = ""; pub const SYM_NETWORK: &str = ""; -pub const SYM_SETTINGS: &str = "";//""; +pub const SYM_SETTINGS: &str = ""; +pub const SYM_TUNING: &str = ""; +pub const SYM_METRICS: &str = ""; diff --git a/src/gui/platform/android/mod.rs b/src/gui/platform/android/mod.rs index 861cd26..ad25b36 100644 --- a/src/gui/platform/android/mod.rs +++ b/src/gui/platform/android/mod.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::Context; +use eframe::epaint::Stroke; use winit::platform::android::activity::AndroidApp; use crate::gui::{App, PlatformApp}; @@ -85,7 +85,10 @@ impl PlatformApp { } fn setup_visuals(ctx: &egui::Context) { - ctx.set_visuals(egui::Visuals::light()); + let mut visuals = egui::Visuals::light(); + // Disable stroke around panels by default + visuals.widgets.noninteractive.bg_stroke = Stroke::NONE; + ctx.set_visuals(visuals); } fn setup_fonts(ctx: &egui::Context) { @@ -106,16 +109,6 @@ impl PlatformApp { // }, // }); - // fonts.font_data.insert( - // "material".to_owned(), - // egui::FontData::from_static(include_bytes!( - // "../../../../fonts/material-light.ttf" - // )).tweak(egui::FontTweak { - // scale: 1.0, - // y_offset_factor: 0.06, - // y_offset: 0.0 - // }), - // ); fonts.font_data.insert( "material".to_owned(), egui::FontData::from_static(include_bytes!( @@ -160,7 +153,7 @@ impl PlatformApp { (Heading, FontId::new(20.0, Proportional)), (Name("icon".into()), FontId::new(24.0, Proportional)), (Body, FontId::new(16.0, Proportional)), - (Button, FontId::new(20.0, Proportional)), + (Button, FontId::new(18.0, Proportional)), (Small, FontId::new(12.0, Proportional)), (Monospace, FontId::new(16.0, Proportional)), ].into(); diff --git a/src/gui/screens/account.rs b/src/gui/screens/account.rs index af5289f..a6d397b 100644 --- a/src/gui/screens/account.rs +++ b/src/gui/screens/account.rs @@ -32,11 +32,11 @@ impl super::Screen for Account { ScreenId::Account } - fn show(&mut self, - ui: &mut egui::Ui, - frame: &mut eframe::Frame, - nav: &mut Navigator, - cb: &dyn PlatformCallbacks) { + fn ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + nav: &mut Navigator, + cb: &dyn PlatformCallbacks) { } } \ No newline at end of file diff --git a/src/gui/screens/accounts.rs b/src/gui/screens/accounts.rs index 26739a2..2081feb 100644 --- a/src/gui/screens/accounts.rs +++ b/src/gui/screens/accounts.rs @@ -13,13 +13,15 @@ // limitations under the License. use std::ops::{Deref, DerefMut}; -use egui::Widget; +use eframe::epaint::{Color32, Stroke}; + +use egui::{Frame, Widget}; + +use crate::gui::{SYM_ARROW_BACK, SYM_NETWORK, SYM_SETTINGS}; +use crate::gui::app::is_dual_panel_mode; use crate::gui::platform::PlatformCallbacks; use crate::gui::screens::{Navigator, Screen, ScreenId}; -use crate::gui::{SYM_ACCOUNTS, SYM_ARROW_BACK, SYM_NETWORK, SYM_SETTINGS}; -use crate::gui::screens::root::dual_panel_mode; -use crate::gui::views::title_panel::{PanelAction, TitlePanel}; -use crate::gui::views::View; +use crate::gui::views::{TitlePanel, TitlePanelAction}; pub struct Accounts { title: String, @@ -38,42 +40,43 @@ impl Screen for Accounts { ScreenId::Accounts } - fn show(&mut self, - ui: &mut egui::Ui, - frame: &mut eframe::Frame, - nav: &mut Navigator, - cb: &dyn PlatformCallbacks) { + fn ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + nav: &mut Navigator, + cb: &dyn PlatformCallbacks) { let Self { title } = self; let mut panel: TitlePanel = TitlePanel::default() .title(title) - .right_action(PanelAction { + .right_action(TitlePanelAction { icon: SYM_SETTINGS.into(), - on_click: Box::new(on_settings_click), + on_click: Box::new(|nav| { + //TODO: open settings + }), }) .with_navigator(nav); - if !dual_panel_mode(frame) { - panel = panel.left_action(PanelAction { + if !is_dual_panel_mode(frame) { + panel = panel.left_action(TitlePanelAction { icon: SYM_NETWORK.into(), - on_click: Box::new(on_network_click), + on_click: Box::new(|nav|{ + nav.as_mut().unwrap().toggle_left_panel(); + }), }); } panel.ui(ui); - ui.label(format!("{}Here we go 10000 ツ", SYM_ARROW_BACK)); - if ui.button("TEST").clicked() { - nav.to(ScreenId::Account) - }; - if ui.button(format!("{}BACK ", SYM_ARROW_BACK)).clicked() { - nav.to(ScreenId::Account) - }; + egui::CentralPanel::default().frame(Frame { + stroke: Stroke::new(1.0, Color32::from_gray(190)), + .. Default::default() + }).show_inside(ui, |ui| { + ui.label(format!("{}Here we go 10000 ツ", SYM_ARROW_BACK)); + if ui.button("TEST").clicked() { + nav.to(ScreenId::Account) + }; + if ui.button(format!("{}BACK ", SYM_ARROW_BACK)).clicked() { + nav.to(ScreenId::Account) + }; + }); } -} - -fn on_network_click(nav: &mut Option<&mut Navigator>) { - nav.as_mut().unwrap().toggle_left_panel(); -} - -fn on_settings_click(nav: &mut Option<&mut Navigator>) { - //TODO: Open settings } \ No newline at end of file diff --git a/src/gui/screens/mod.rs b/src/gui/screens/mod.rs index 384c6c3..f48d44b 100644 --- a/src/gui/screens/mod.rs +++ b/src/gui/screens/mod.rs @@ -12,14 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub use account::Account; +pub use accounts::Accounts; pub use navigator::Navigator; pub use root::Root; -pub use accounts::Accounts; -pub use account::Account; use crate::gui::App; use crate::gui::platform::PlatformCallbacks; -use crate::gui::views::title_panel::PanelAction; +use crate::gui::views::TitlePanelAction; mod navigator; mod root; @@ -30,14 +30,14 @@ mod account; pub enum ScreenId { Root, Accounts, - Account + Account, } pub trait Screen { fn id(&self) -> ScreenId; - fn show(&mut self, - ui: &mut egui::Ui, - frame: &mut eframe::Frame, - navigator: &mut Navigator, - cb: &dyn PlatformCallbacks); + fn ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + navigator: &mut Navigator, + cb: &dyn PlatformCallbacks); } diff --git a/src/gui/screens/navigator.rs b/src/gui/screens/navigator.rs index ba1cfe6..4413d87 100644 --- a/src/gui/screens/navigator.rs +++ b/src/gui/screens/navigator.rs @@ -17,7 +17,7 @@ use std::collections::BTreeSet; use crate::gui::screens::ScreenId; pub struct Navigator { - pub(crate) stack: BTreeSet, + stack: BTreeSet, pub(crate) left_panel_open: bool, } @@ -33,6 +33,10 @@ impl Default for Navigator { } impl Navigator { + pub fn current(&mut self) -> &ScreenId { + self.stack.last().unwrap() + } + pub fn to(&mut self, id: ScreenId) { self.stack.insert(id); } diff --git a/src/gui/screens/root.rs b/src/gui/screens/root.rs index 7699771..5ad1a5e 100644 --- a/src/gui/screens/root.rs +++ b/src/gui/screens/root.rs @@ -13,17 +13,20 @@ // limitations under the License. use std::cmp::min; -use eframe::epaint::{Shadow, Stroke}; -use eframe::Frame; + +use eframe::epaint::{Color32, Shadow, Stroke}; use egui::style::Margin; -use egui::Ui; + use crate::gui::{App, COLOR_YELLOW}; +use crate::gui::app::is_dual_panel_mode; use crate::gui::platform::PlatformCallbacks; use crate::gui::screens::{Account, Accounts, Navigator, Screen, ScreenId}; +use crate::gui::views::Network; pub struct Root { navigator: Navigator, screens: Vec>, + network: Network } impl Default for Root { @@ -34,6 +37,7 @@ impl Default for Root { Box::new(Accounts::default()), Box::new(Account::default()) ]), + network: Network::default() } } } @@ -43,32 +47,24 @@ impl Root { ScreenId::Root } - pub fn ui(&mut self, ui: &mut Ui, frame: &mut Frame, cb: &dyn PlatformCallbacks) { - let is_network_panel_open = self.navigator.left_panel_open || dual_panel_mode(frame); + pub fn ui(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { + let is_network_panel_open = self.navigator.left_panel_open || is_dual_panel_mode(frame); egui::SidePanel::left("network_panel") .resizable(false) - .exact_width(if dual_panel_mode(frame) { + .exact_width(if is_dual_panel_mode(frame) { min(frame.info().window_info.size.x as i64, 400) as f32 } else { frame.info().window_info.size.x }) .frame(egui::Frame { - inner_margin: Margin::same(0.0), - outer_margin: Margin::same(0.0), - fill: COLOR_YELLOW, .. Default::default() }) .show_animated_inside(ui, is_network_panel_open, |ui| { - //TODO: Network content - ui.vertical_centered(|ui| { - ui.heading("🖧 Node"); - }); - - ui.separator(); + self.network.ui(ui, frame, &mut self.navigator, cb); }); - egui::CentralPanel::default().frame(egui::containers::Frame { + egui::CentralPanel::default().frame(egui::Frame { ..Default::default() }).show_inside(ui, |ui| { self.show_current_screen(ui, frame, cb); @@ -76,22 +72,17 @@ impl Root { } - pub fn show_current_screen(&mut self, ui: &mut Ui, frame: &mut Frame, cb: &dyn PlatformCallbacks) { + pub fn show_current_screen(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + cb: &dyn PlatformCallbacks) { let Self { navigator, screens, .. } = self; - let current = navigator.stack.last().unwrap(); + let current = navigator.current(); for screen in screens.iter_mut() { if screen.id() == *current { - screen.show(ui, frame, navigator, cb); + screen.ui(ui, frame, navigator, cb); break; } } } -} - -pub fn dual_panel_mode(frame: &mut Frame) -> bool { - is_landscape(frame) && frame.info().window_info.size.x > 400.0 -} - -pub fn is_landscape(frame: &mut Frame) -> bool { - return frame.info().window_info.size.x > frame.info().window_info.size.y } \ No newline at end of file diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index 9241613..9ccc115 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -13,8 +13,18 @@ // limitations under the License. pub mod buttons; -pub mod title_panel; -pub trait View { - fn ui(&mut self, ui: &mut egui::Ui); -} \ No newline at end of file +mod title_panel; +pub use crate::gui::views::title_panel::{TitlePanel, TitlePanelAction, TitlePanelActions}; + +mod network; +pub use crate::gui::views::network::Network; + +mod network_node; +mod network_tuning; +mod network_metrics; + +pub trait NetworkTab { + fn ui(&mut self, ui: &mut egui::Ui, node: &mut crate::node::Node); + fn title(&self) -> &String; +} diff --git a/src/gui/views/network.rs b/src/gui/views/network.rs new file mode 100644 index 0000000..afd7846 --- /dev/null +++ b/src/gui/views/network.rs @@ -0,0 +1,288 @@ +// 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::borrow::Cow; +use std::collections::hash_map::DefaultHasher; +use std::time::Duration; +use eframe::epaint::{Color32, FontId, Stroke}; +use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapping}; +use egui::{Response, RichText, Sense, Spinner, Widget}; +use egui::style::Margin; +use egui_extras::{Size, StripBuilder}; +use grin_chain::SyncStatus; + +use grin_core::global::ChainTypes; +use grin_servers::ServerStats; +use crate::gui::app::is_dual_panel_mode; +use crate::gui::platform::PlatformCallbacks; +use crate::gui::screens::Navigator; +use crate::gui::{COLOR_DARK, COLOR_LIGHT, COLOR_YELLOW, SYM_ACCOUNTS, SYM_METRICS, SYM_NETWORK}; + +use crate::gui::views::{NetworkTab, TitlePanel, TitlePanelAction}; +use crate::gui::views::network_node::NetworkNode; +use crate::node; +use crate::node::Node; + +enum Mode { + Node, + // Miner, + Metrics, + Tuning +} + +pub struct Network { + current_mode: Mode, + node: Node, + node_view: NetworkNode, +} + +impl Default for Network { + fn default() -> Self { + let node = Node::new(ChainTypes::Mainnet, true); + Self { + node, + current_mode: Mode::Node, + node_view: NetworkNode::default() + } + } +} + +impl Network { + pub fn ui(&mut self, + ui: &mut egui::Ui, + frame: &mut eframe::Frame, + nav: &mut Navigator, + cb: &dyn PlatformCallbacks) { + + egui::TopBottomPanel::top("network_title") + .resizable(false) + .frame(egui::Frame { + fill: COLOR_YELLOW, + inner_margin: Margin::same(0.0), + outer_margin: Margin::same(0.0), + stroke: Stroke::NONE, + ..Default::default() + }) + .show_inside(ui, |ui| { + self.draw_title(ui, frame, nav); + }); + + egui::CentralPanel::default().frame(egui::Frame { + stroke: Stroke::new(1.0, Color32::from_gray(190)), + fill: Color32::WHITE, + .. Default::default() + }).show_inside(ui, |ui| { + self.draw_tab_content(ui); + }); + + egui::TopBottomPanel::bottom("network_tabs") + .frame(egui::Frame { + stroke: Stroke::new(1.0, Color32::from_gray(190)), + .. Default::default() + }) + .resizable(false) + .show_inside(ui, |ui| { + self.draw_tabs(ui); + }); + + ui.ctx().request_repaint_after(Duration::from_millis(500)); + } + + fn draw_tabs(&self, ui: &mut egui::Ui) { + ui.vertical_centered(|ui| { + ui.columns(3, |columns| { + columns[0].horizontal_wrapped(|ui| { + }); + columns[1].vertical_centered(|ui| { + }); + columns[2].horizontal_wrapped(|ui| { + }); + }); + }); + } + + fn draw_tab_content(&mut self, ui: &mut egui::Ui) { + match self.current_mode { + Mode::Node => { + self.node_view.ui(ui, &mut self.node); + } + Mode::Metrics => {} + Mode::Tuning => {} + } + } + + fn draw_title(&mut self, ui: &mut egui::Ui, frame: &mut eframe::Frame, nav: &mut Navigator) { + // Disable stroke around title buttons on hover + ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE; + + StripBuilder::new(ui) + .size(Size::exact(52.0)) + .vertical(|mut strip| { + strip.strip(|builder| { + builder + .size(Size::exact(52.0)) + .size(Size::remainder()) + .size(Size::exact(52.0)) + .horizontal(|mut strip| { + strip.empty(); + strip.strip(|builder| { + self.draw_title_text(builder); + }); + strip.cell(|ui| { + if !is_dual_panel_mode(frame) { + ui.centered_and_justified(|ui| { + let b = egui::widgets::Button::new( + RichText::new(SYM_ACCOUNTS) + .size(24.0) + .color(COLOR_DARK) + ).fill(Color32::TRANSPARENT) + .ui(ui).interact(Sense::click_and_drag()); + if b.drag_released() || b.clicked() { + nav.toggle_left_panel(); + }; + }); + } + }); + }); + }); + }); + } + + fn draw_title_text(&self, mut builder: StripBuilder) { + let title_text = match &self.current_mode { + Mode::Node => { + self.node_view.title() + } + Mode::Metrics => { + self.node_view.title() + } + Mode::Tuning => { + self.node_view.title() + } + }; + + + let state = self.node.acquire_state(); + let syncing = state.stats.is_some() && + state.stats.as_ref().unwrap().sync_status != SyncStatus::NoSync; + + let mut b = builder.size(Size::remainder()); + if syncing { + b = b.size(Size::remainder()); + } + b.vertical(|mut strip| { + strip.cell(|ui| { + ui.centered_and_justified(|ui| { + ui.heading(title_text.to_uppercase()); + }); + }); + if syncing { + let stats = state.stats.as_ref().unwrap(); + strip.cell(|ui| { + ui.centered_and_justified(|ui| { + let status_text = if state.is_stopping() { + get_sync_status(SyncStatus::Shutdown).to_string() + } else if state.is_restarting() { + "Restarting".to_string() + } else { + get_sync_status(stats.sync_status).to_string() + }; + let mut job = LayoutJob::single_section(status_text, TextFormat { + font_id: FontId::proportional(15.0), + color: COLOR_DARK, + .. Default::default() + }); + job.wrap = TextWrapping { + max_rows: 1, + break_anywhere: false, + overflow_character: Option::from('…'), + ..Default::default() + }; + ui.label(job); + }); + }); + } + }); + } +} + +fn get_sync_status(sync_status: SyncStatus) -> Cow<'static, str> { + match sync_status { + SyncStatus::Initial => Cow::Borrowed("Initializing"), + SyncStatus::NoSync => Cow::Borrowed("Running"), + SyncStatus::AwaitingPeers(_) => Cow::Borrowed("Waiting for peers"), + SyncStatus::HeaderSync { + sync_head, + highest_height, + .. + } => { + if highest_height == 0 { + Cow::Borrowed("Downloading headers data") + } else { + let percent = sync_head.height * 100 / highest_height; + Cow::Owned(format!("Downloading headers: {}%", percent)) + } + } + SyncStatus::TxHashsetDownload(stat) => { + Cow::Borrowed("Downloading chain state") + } + SyncStatus::TxHashsetSetup => { + Cow::Borrowed("Preparing chain state for validation") + } + SyncStatus::TxHashsetRangeProofsValidation { + rproofs, + rproofs_total, + } => { + let r_percent = if rproofs_total > 0 { + (rproofs * 100) / rproofs_total + } else { + 0 + }; + Cow::Owned(format!("Validating state - range proofs: {}%", r_percent)) + } + SyncStatus::TxHashsetKernelsValidation { + kernels, + kernels_total, + } => { + let k_percent = if kernels_total > 0 { + (kernels * 100) / kernels_total + } else { + 0 + }; + Cow::Owned(format!("Validating state - kernels: {}%", k_percent)) + } + SyncStatus::TxHashsetSave => { + Cow::Borrowed("Finalizing chain state") + } + SyncStatus::TxHashsetDone => { + Cow::Borrowed("Finalized chain state") + } + SyncStatus::BodySync { + current_height, + highest_height, + } => { + if highest_height == 0 { + Cow::Borrowed("Downloading blocks data") + } else { + Cow::Owned(format!( + "Downloading blocks: {}%", + current_height * 100 / highest_height + )) + } + } + SyncStatus::Shutdown => Cow::Borrowed("Shutting down"), + } +} + + diff --git a/src/gui/views/network_metrics.rs b/src/gui/views/network_metrics.rs new file mode 100644 index 0000000..2035f55 --- /dev/null +++ b/src/gui/views/network_metrics.rs @@ -0,0 +1,13 @@ +// 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. diff --git a/src/gui/views/network_node.rs b/src/gui/views/network_node.rs new file mode 100644 index 0000000..bbd4f34 --- /dev/null +++ b/src/gui/views/network_node.rs @@ -0,0 +1,76 @@ +// 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::borrow::Cow; +use std::ptr::null; +use std::sync::mpsc; + +use chrono::Utc; +use egui::{Ui, Widget}; +use grin_chain::SyncStatus; +use grin_core::global::ChainTypes; +use grin_servers::ServerStats; + +use crate::gui::views::NetworkTab; +use crate::node::Node; + + +pub struct NetworkNode { + title: String +} + +impl Default for NetworkNode { + fn default() -> Self { + Self { + title: t!("node"), + } + } +} + +impl NetworkTab for NetworkNode { + fn ui(&mut self, ui: &mut Ui, node: &mut Node) { + // ui.vertical_centered_justified(|ui| { + // let node_state = node.acquire_state(); + // let stats = &node_state.stats; + // if stats.is_some() { + // ui.horizontal_wrapped(|ui| { + // let sync_status = stats.as_ref().unwrap().sync_status; + // ui.label(get_sync_progress_status(sync_status)); + // ui.spinner(); + // }); + // } else { + // if node.stop_state.is_stopped() { + // ui.label("Stopped"); + // } else { + // ui.label(get_sync_progress_status(SyncStatus::Initial)); + // } + // } + // }); + if ui.button("stop").clicked() { + node.stop(); + } + + if ui.button("re-start").clicked() { + node.restart(ChainTypes::Mainnet); + } + + if ui.button("start").clicked() { + node.start(ChainTypes::Mainnet); + } + } + + fn title(&self) -> &String { + &self.title + } +} \ No newline at end of file diff --git a/src/gui/views/network_tuning.rs b/src/gui/views/network_tuning.rs new file mode 100644 index 0000000..2035f55 --- /dev/null +++ b/src/gui/views/network_tuning.rs @@ -0,0 +1,13 @@ +// 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. diff --git a/src/gui/views/title_panel.rs b/src/gui/views/title_panel.rs index e18ea68..0d7fa1b 100644 --- a/src/gui/views/title_panel.rs +++ b/src/gui/views/title_panel.rs @@ -12,61 +12,55 @@ // See the License for the specific language governing permissions and // limitations under the License. - use eframe::epaint::text::{LayoutJob, TextFormat, TextWrapping}; use egui::{Color32, FontId, RichText, Sense, Stroke, Widget}; use egui::style::Margin; use egui_extras::{Size, StripBuilder}; + use crate::gui::{COLOR_DARK, COLOR_YELLOW}; use crate::gui::screens::Navigator; -use crate::gui::views::View; -pub struct PanelAction { +pub struct TitlePanelAction { pub(crate) icon: Box, pub(crate) on_click: Box)>, } #[derive(Default)] -pub struct PanelActions { - left: Option, - right: Option +pub struct TitlePanelActions { + left: Option, + right: Option } #[derive(Default)] -pub struct TitlePanel<'screen> { - title: Option<&'screen String>, - actions: PanelActions, - navigator: Option<&'screen mut Navigator> +pub struct TitlePanel<'nav> { + title: Option<&'nav str>, + actions: TitlePanelActions, + navigator: Option<&'nav mut Navigator> } -impl<'screen> TitlePanel<'screen> { - pub fn title(mut self, title: &'screen String) -> Self { +impl<'nav> TitlePanel<'nav> { + pub fn title(mut self, title: &'nav str) -> Self { self.title = Some(title); self } - pub fn left_action(mut self, action: PanelAction) -> Self { + pub fn left_action(mut self, action: TitlePanelAction) -> Self { self.actions.left = Some(action); self } - pub fn right_action(mut self, action: PanelAction) -> Self { + pub fn right_action(mut self, action: TitlePanelAction) -> Self { self.actions.right = Some(action); self } - pub fn with_navigator(mut self, nav: &'screen mut Navigator) -> Self { + pub fn with_navigator(mut self, nav: &'nav mut Navigator) -> Self { self.navigator = Some(nav); self } -} -impl View for TitlePanel<'_> { - fn ui(&mut self, ui: &mut egui::Ui) { - // Disable stroke around panel - ui.style_mut().visuals.widgets.noninteractive.bg_stroke = Stroke::NONE; - - // Disable stroke around buttons on hover + pub fn ui(&mut self, ui: &mut egui::Ui) { + // Disable stroke around panel buttons on hover ui.style_mut().visuals.widgets.active.bg_stroke = Stroke::NONE; let Self { actions, title, navigator } = self; @@ -114,7 +108,7 @@ impl View for TitlePanel<'_> { strip.cell(|ui| { if title.is_some() { ui.centered_and_justified(|ui| { - show_title(title.as_ref().unwrap(), ui); + Self::show_title(title.unwrap(), ui); }); } }); @@ -141,19 +135,20 @@ impl View for TitlePanel<'_> { }); }); } + + fn show_title(title: &str, ui: &mut egui::Ui) { + let mut job = LayoutJob::single_section(title.to_uppercase(), TextFormat { + font_id: FontId::proportional(20.0), + color: COLOR_DARK, + .. Default::default() + }); + job.wrap = TextWrapping { + max_rows: 1, + break_anywhere: false, + overflow_character: Option::from('…'), + ..Default::default() + }; + ui.label(job); + } } -fn show_title(title: &String, ui: &mut egui::Ui) { - let mut job = LayoutJob::single_section(title.to_uppercase(), TextFormat { - font_id: FontId::proportional(20.0), - color: COLOR_DARK, - .. Default::default() - }); - job.wrap = TextWrapping { - max_rows: 1, - break_anywhere: false, - overflow_character: Option::from('…'), - ..Default::default() - }; - ui.label(job); -} \ No newline at end of file diff --git a/src/node/mod.rs b/src/node/mod.rs index 7cfeade..75a956b 100644 --- a/src/node/mod.rs +++ b/src/node/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -pub mod node; +mod node; -pub use self::node::start; \ No newline at end of file +pub use self::node::Node; +pub use self::node::NodeState; \ No newline at end of file diff --git a/src/node/node.rs b/src/node/node.rs index 61b388f..2b6a6c5 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -12,63 +12,231 @@ // See the License for the specific language governing permissions and // limitations under the License. -use std::sync::mpsc; -use grin_config::{config, GlobalConfig}; +use std::{fs, thread}; +use std::fmt::format; +use std::ops::Deref; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, LockResult, mpsc, Mutex, MutexGuard}; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::thread::JoinHandle; +use std::time::Duration; + +use futures::channel::oneshot; +use grin_chain::SyncStatus; +use grin_config::config; use grin_core::global; use grin_core::global::ChainTypes; +use grin_servers::{Server, ServerStats}; +use grin_servers::common::types::Error; use grin_util::logger::LogEntry; +use grin_util::StopState; use log::info; -use futures::channel::oneshot; -pub fn start(chain_type: &ChainTypes) { - let node_config = Some( - config::initial_setup_server(&ChainTypes::Mainnet).unwrap_or_else(|e| { - //TODO: Error handling - panic!("Error loading server configuration: {}", e); - }), - ); +pub struct Node { + /// Node state updated from the separate thread + node_state: Arc>, +} +impl Node { + /// Instantiate new node with provided chain type, start server if needed + pub fn new(chain_type: ChainTypes, start: bool) -> Self { + let stop_state = Arc::new(StopState::new()); + let mut state = NodeState::new(chain_type, stop_state.clone()); + let node_state = Arc::new(Mutex::new(state)); + if start { + let server = start_server(&chain_type, stop_state); + start_server_thread(node_state.clone(), server); + } else { + stop_state.stop(); + } + Self { node_state } + } + + /// Acquire node state to be used by a current thread + pub fn acquire_state(&self) -> MutexGuard<'_, NodeState> { + self.node_state.lock().unwrap() + } + + /// Stop server + pub fn stop(&self) { + self.acquire_state().stop_needed = true; + } + + /// Start server with provided chain type + pub fn start(&self, chain_type: ChainTypes) { + let mut state = self.node_state.lock().unwrap(); + if state.stop_state.is_stopped() { + self.start_with_acquired_state(state, chain_type); + } + } + + /// Restart server with provided chain type + pub fn restart(&mut self, chain_type: ChainTypes) { + let mut state = self.acquire_state(); + if !state.stop_state.is_stopped() { + state.chain_type = chain_type; + state.restart_needed = true; + } else { + self.start_with_acquired_state(state, chain_type); + } + } + + /// Start server with provided acquired state + fn start_with_acquired_state(&self, mut state: MutexGuard, chain_type: ChainTypes) { + state.chain_type = chain_type; + state.stop_state = Arc::new(StopState::new()); + + let server = start_server(&chain_type, state.stop_state.clone()); + start_server_thread(self.node_state.clone(), server); + } +} + +pub struct NodeState { + /// To check server state + stop_state: Arc, + /// Data for UI, None means server is not started + pub(crate) stats: Option, + /// Chain type of launched server + chain_type: ChainTypes, + /// Thread flag to stop the server and start it again + restart_needed: bool, + /// Thread flag to stop the server + stop_needed: bool, +} + +impl NodeState { + /// Instantiate new node state with provided chain type and server state + pub fn new(chain_type: ChainTypes, stop_state: Arc) -> Self { + Self { + stop_state, + stats: None, + chain_type, + restart_needed: false, + stop_needed: false, + } + } + + /// Check if server is stopping at separate thread + pub fn is_stopping(&self) -> bool { + return self.stop_needed + } + + /// Check if server is restarting at separate thread + pub fn is_restarting(&self) -> bool { + return self.restart_needed + } +} + +/// Start server with provided chain type and node state +fn start_server(chain_type: &ChainTypes, stop_state: Arc) -> Server { + let mut node_config_result = config::initial_setup_server(chain_type); + if node_config_result.is_err() { + // Remove config file on init error + let mut grin_path = dirs::home_dir().unwrap(); + grin_path.push(".grin"); + grin_path.push(chain_type.shortname()); + grin_path.push(config::SERVER_CONFIG_FILE_NAME); + fs::remove_file(grin_path).unwrap(); + + // Reinit config + node_config_result = config::initial_setup_server(chain_type); + } + + let node_config = node_config_result.ok(); let config = node_config.clone().unwrap(); let server_config = config.members.as_ref().unwrap().server.clone(); - // Initialize our global chain_type, feature flags (NRD kernel support currently), accept_fee_base, and future_time_limit. + let mut db_path = PathBuf::from(&server_config.db_root); + db_path.push("grin.lock"); + fs::remove_file(db_path).unwrap(); + + // Initialize our global chain_type, feature flags (NRD kernel support currently), + // accept_fee_base, and future_time_limit. // These are read via global and not read from config beyond this point. - global::init_global_chain_type(config.members.as_ref().unwrap().server.chain_type); + if !global::GLOBAL_CHAIN_TYPE.is_init() { + global::init_global_chain_type(config.members.as_ref().unwrap().server.chain_type); + } info!("Chain: {:?}", global::get_chain_type()); - match global::get_chain_type() { - ChainTypes::Mainnet => { - // Set various mainnet specific feature flags. - global::init_global_nrd_enabled(false); - } - _ => { - // Set various non-mainnet feature flags. - global::init_global_nrd_enabled(true); + + if !global::GLOBAL_NRD_FEATURE_ENABLED.is_init() { + match global::get_chain_type() { + ChainTypes::Mainnet => { + // Set various mainnet specific feature flags. + global::init_global_nrd_enabled(false); + } + _ => { + // Set various non-mainnet feature flags. + global::init_global_nrd_enabled(true); + } } } - let afb = config - .members - .as_ref() - .unwrap() - .server - .pool_config - .accept_fee_base; - global::init_global_accept_fee_base(afb); - info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); - global::init_global_future_time_limit(config.members.unwrap().server.future_time_limit); - info!("Future Time Limit: {:?}", global::get_future_time_limit()); + if !global::GLOBAL_ACCEPT_FEE_BASE.is_init() { + let afb = config + .members + .as_ref() + .unwrap() + .server + .pool_config + .accept_fee_base; + global::init_global_accept_fee_base(afb); + info!("Accept Fee Base: {:?}", global::get_accept_fee_base()); + } + if !global::GLOBAL_FUTURE_TIME_LIMIT.is_init() { + global::init_global_future_time_limit(config.members.unwrap().server.future_time_limit); + info!("Future Time Limit: {:?}", global::get_future_time_limit()); + } let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) = Box::leak(Box::new(oneshot::channel::<()>())); - grin_servers::Server::start( - server_config, - None, - |serv: grin_servers::Server, info: Option>| { - serv.get_server_stats(); - info!("Info callback") - //serv.stop(); - }, - None, - api_chan - ) - .unwrap(); + let mut server_result = Server::new(server_config.clone(), Some(stop_state.clone()), api_chan); + if server_result.is_err() { + let mut db_path = PathBuf::from(&server_config.db_root); + db_path.push("grin.lock"); + fs::remove_file(db_path).unwrap(); + + // Remove chain data on server start error + let dirs_to_remove: Vec<&str> = vec!["header", "lmdb", "txhashset", "peer"]; + for dir in dirs_to_remove { + let mut path = PathBuf::from(&server_config.db_root); + path.push(dir); + fs::remove_dir_all(path).unwrap(); + } + + // Recreate server + let config = node_config.clone().unwrap(); + let server_config = config.members.as_ref().unwrap().server.clone(); + let api_chan: &'static mut (oneshot::Sender<()>, oneshot::Receiver<()>) = + Box::leak(Box::new(oneshot::channel::<()>())); + server_result = Server::new(server_config.clone(), Some(stop_state.clone()), api_chan); + } + + server_result.unwrap() +} + +/// Start a thread to launch server and update node state with server stats +fn start_server_thread(node_state: Arc>, mut server: Server) -> JoinHandle<()> { + thread::spawn(move || loop { + thread::sleep(Duration::from_millis(500)); + let mut state = node_state.lock().unwrap(); + if state.restart_needed { + server.stop(); + + // Create new server with new stop state + state.stop_state = Arc::new(StopState::new()); + server = start_server(&state.chain_type, state.stop_state.clone()); + + state.restart_needed = false; + } else if state.stop_needed { + server.stop(); + state.stats = None; + state.stop_needed = false; + break; + } + if !state.stop_state.is_stopped() { + let stats = server.get_server_stats(); + if stats.is_ok() { + state.stats = Some(stats.as_ref().ok().unwrap().clone()); + } + } + }) } \ No newline at end of file