From ffc68bc6ae867e48cffdc694b56a289615a596ba Mon Sep 17 00:00:00 2001 From: ardocrat Date: Fri, 2 Jun 2023 21:19:34 +0300 Subject: [PATCH] ui: setup buttons style, add server loading progress for node and metrics, optimize node status --- .../java/mw/gri/android/MainActivity.java | 4 +- locales/en.yml | 4 + locales/ru.yml | 4 + src/gui/app.rs | 31 ++++--- src/gui/platform/android/mod.rs | 16 ++-- src/gui/views/mod.rs | 6 +- src/gui/views/modal.rs | 11 ++- src/gui/views/network.rs | 42 +++++++--- src/gui/views/network_metrics.rs | 25 ++++-- src/gui/views/network_mining.rs | 25 ++++++ src/gui/views/network_node.rs | 18 ++-- src/gui/views/progress_loading.rs | 40 --------- src/gui/views/views.rs | 30 ++++--- src/node/node.rs | 82 ++++++++++++------- 14 files changed, 203 insertions(+), 135 deletions(-) delete mode 100644 src/gui/views/progress_loading.rs diff --git a/app/src/main/java/mw/gri/android/MainActivity.java b/app/src/main/java/mw/gri/android/MainActivity.java index 1015845..993e4f1 100644 --- a/app/src/main/java/mw/gri/android/MainActivity.java +++ b/app/src/main/java/mw/gri/android/MainActivity.java @@ -5,6 +5,7 @@ import android.os.Bundle; import android.os.Process; import android.system.ErrnoException; import android.system.Os; +import android.util.Log; import android.view.KeyEvent; import android.view.OrientationEventListener; import com.google.androidgamesdk.GameActivity; @@ -29,7 +30,7 @@ public class MainActivity extends GameActivity { // Callback to update display cutouts at native code. OrientationEventListener orientationEventListener = new OrientationEventListener(this, - SensorManager.SENSOR_DELAY_GAME) { + SensorManager.SENSOR_DELAY_FASTEST) { @Override public void onOrientationChanged(int orientation) { onDisplayCutoutsChanged(Utils.getDisplayCutouts(MainActivity.this)); @@ -82,6 +83,7 @@ public class MainActivity extends GameActivity { // Called from native code public void onExit() { + Log.d("12345", "onExit"); mManualExit = true; BackgroundService.stop(this); finish(); diff --git a/locales/en.yml b/locales/en.yml index c92a05a..a8aa622 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -5,6 +5,9 @@ network: metrics: Metrics mining: Mining settings: Server settings + enable: Enable + disable: Disable + restart: Restart sync_status: server_restarting: Server is restarting server_down: Server is down @@ -36,6 +39,7 @@ network_node: size: Size (GB) peers: Peers network_metrics: + loading: Metrics will be available after the synchronization emission: Emission inflation: Inflation supply: Supply diff --git a/locales/ru.yml b/locales/ru.yml index 6403468..4d4b78e 100644 --- a/locales/ru.yml +++ b/locales/ru.yml @@ -5,6 +5,9 @@ network: metrics: Метрики mining: Майнинг settings: Настройки сервера + enable: Включить + disable: Выключить + restart: Перезапустить sync_status: server_restarting: Сервер перезапускается server_down: Сервер выключен @@ -36,6 +39,7 @@ network_node: size: Размер (ГБ) peers: Пиры network_metrics: + loading: Метрики будут доступны после синхронизации emission: Эмиссия inflation: Инфляция supply: Предложение diff --git a/src/gui/app.rs b/src/gui/app.rs index 318cb1f..88974ec 100644 --- a/src/gui/app.rs +++ b/src/gui/app.rs @@ -12,15 +12,15 @@ // See the License for the specific language governing permissions and // limitations under the License. -use egui::{Context, Stroke, Widget}; +use egui::{Color32, Context, RichText, Spinner, Stroke, Widget}; use egui::os::OperatingSystem; use egui::style::Margin; -use crate::gui::colors::COLOR_LIGHT; +use crate::gui::colors::{COLOR_DARK, COLOR_LIGHT, COLOR_YELLOW}; use crate::gui::Navigator; use crate::gui::platform::PlatformCallbacks; use crate::gui::screens::Root; -use crate::gui::views::{Modal, ModalId, ModalLocation, ProgressLoading, View}; +use crate::gui::views::{Modal, ModalId, ModalLocation, View}; use crate::node::Node; pub struct PlatformApp { @@ -36,18 +36,17 @@ pub struct App { impl App { pub fn ui(&mut self, ctx: &Context, frame: &mut eframe::Frame, cb: &dyn PlatformCallbacks) { - let modal_open = Navigator::is_modal_open(ModalLocation::Global); egui::CentralPanel::default() .frame(egui::Frame { fill: COLOR_LIGHT, .. Default::default() }) .show(ctx, |ui| { - if modal_open { + if Navigator::is_modal_open(ModalLocation::Global) { self.show_global_modal(ui, frame, cb); } self.root.ui(ui, frame, cb); - }).response.enabled = !modal_open; + }); } fn show_global_modal(&mut self, @@ -60,12 +59,18 @@ impl App { ModalId::Exit => { if self.show_exit_progress { if !Node::is_running() { - modal.close(); Self::exit(frame, cb); + modal.close(); } - ui.add_space(12.0); - let text = Node::get_sync_status_text(Node::get_sync_status()); - ProgressLoading::new(text).ui(ui); + ui.add_space(16.0); + ui.vertical_centered(|ui| { + Spinner::new().size(42.0).color(COLOR_YELLOW).ui(ui); + ui.add_space(10.0); + ui.label(RichText::new(Node::get_sync_status_text()) + .size(18.0) + .color(COLOR_DARK) + ); + }); ui.add_space(12.0); } else { ui.add_space(8.0); @@ -77,19 +82,19 @@ impl App { ui.spacing_mut().item_spacing = egui::Vec2::new(6.0, 0.0); ui.columns(2, |columns| { columns[0].vertical_centered_justified(|ui| { - View::modal_button(ui, t!("modal_exit.exit"), || { + View::button(ui, t!("modal_exit.exit"), Color32::WHITE, || { if !Node::is_running() { Self::exit(frame, cb); modal.close(); } else { - modal.disable_closing(); Node::stop(); + modal.disable_closing(); self.show_exit_progress = true; } }); }); columns[1].vertical_centered_justified(|ui| { - View::modal_button(ui, t!("modal.cancel"), || { + View::button(ui, t!("modal.cancel"), Color32::WHITE, || { modal.close(); }); }); diff --git a/src/gui/platform/android/mod.rs b/src/gui/platform/android/mod.rs index a96ee98..04a8fe3 100644 --- a/src/gui/platform/android/mod.rs +++ b/src/gui/platform/android/mod.rs @@ -96,11 +96,13 @@ impl PlatformApp { fn setup_visuals(ctx: &egui::Context) { // Setup style let mut style = (*ctx.style()).clone(); - - // Make scroll-bar thinner + // Setup spacing for buttons. + style.spacing.button_padding = egui::vec2(12.0, 8.0); + // Make scroll-bar thinner. style.spacing.scroll_bar_width = 4.0; - // + // Disable spacing between items. style.spacing.item_spacing = egui::vec2(0.0, 0.0); + ctx.set_style(style); // Setup visuals @@ -183,7 +185,7 @@ fn padding_panels(ctx: &egui::Context) { .show_separator_line(false) .resizable(false) .exact_height(DISPLAY_CUTOUT_TOP.load(Ordering::Relaxed) as f32) - .show(ctx, |ui| {}); + .show(ctx, |_ui| {}); egui::TopBottomPanel::bottom("bottom_padding_panel") .frame(egui::Frame { @@ -194,7 +196,7 @@ fn padding_panels(ctx: &egui::Context) { .show_separator_line(false) .resizable(false) .exact_height(DISPLAY_CUTOUT_BOTTOM.load(Ordering::Relaxed) as f32) - .show(ctx, |ui| {}); + .show(ctx, |_ui| {}); egui::SidePanel::right("right_padding_panel") .frame(egui::Frame { @@ -205,7 +207,7 @@ fn padding_panels(ctx: &egui::Context) { .show_separator_line(false) .resizable(false) .max_width(DISPLAY_CUTOUT_RIGHT.load(Ordering::Relaxed) as f32) - .show(ctx, |ui| {}); + .show(ctx, |_ui| {}); egui::SidePanel::left("left_padding_panel") .frame(egui::Frame { @@ -216,7 +218,7 @@ fn padding_panels(ctx: &egui::Context) { .show_separator_line(false) .resizable(false) .max_width(DISPLAY_CUTOUT_LEFT.load(Ordering::Relaxed) as f32) - .show(ctx, |ui| {}); + .show(ctx, |_ui| {}); } lazy_static! { diff --git a/src/gui/views/mod.rs b/src/gui/views/mod.rs index 93c6733..e11405b 100644 --- a/src/gui/views/mod.rs +++ b/src/gui/views/mod.rs @@ -22,14 +22,12 @@ mod modal; pub use modal::*; mod network; +pub use network::Network; + mod network_node; mod network_settings; mod network_metrics; mod network_mining; -pub use network::Network; - -mod progress_loading; -pub use progress_loading::ProgressLoading; pub trait NetworkTab { fn name(&self) -> &String; diff --git a/src/gui/views/modal.rs b/src/gui/views/modal.rs index 5b7adf3..3f8d2b2 100644 --- a/src/gui/views/modal.rs +++ b/src/gui/views/modal.rs @@ -15,7 +15,7 @@ use std::cmp::min; use std::sync::atomic::{AtomicBool, Ordering}; -use egui::{Align2, Color32, RichText, Rounding, Sense, Stroke, Vec2}; +use egui::{Align2, Color32, RichText, Rounding, Sense, Separator, Stroke, Vec2, Widget}; use egui::epaint::RectShape; use egui::style::Margin; use egui_extras::{Size, StripBuilder}; @@ -29,7 +29,6 @@ pub enum ModalId { } /// Location for [`Modal`] at application UI. -#[derive(Clone, Copy)] pub enum ModalLocation { /// To draw globally above side panel and screen. Global, @@ -204,7 +203,7 @@ impl Modal { rect, rounding, fill: COLOR_LIGHT, - stroke: View::DEFAULT_STROKE, + stroke: Stroke::NONE, }; let bg_idx = ui.painter().add(bg_shape); @@ -250,5 +249,11 @@ impl Modal { // Setup background shape to be painted behind title content. bg_shape.rect = title_resp.rect; ui.painter().set(bg_idx, bg_shape); + + let (rect, _) = ui.allocate_exact_size(Vec2::new(ui.available_width(), 1.0),Sense::hover()); + let painter = ui.painter(); + painter.hline(rect.x_range(), + painter.round_to_pixel(rect.center().y), + View::DEFAULT_STROKE); } } \ No newline at end of file diff --git a/src/gui/views/network.rs b/src/gui/views/network.rs index cf2b238..62b3bc1 100644 --- a/src/gui/views/network.rs +++ b/src/gui/views/network.rs @@ -20,8 +20,8 @@ use egui_extras::{Size, StripBuilder}; use grin_chain::SyncStatus; use grin_core::global::ChainTypes; -use crate::gui::colors::{COLOR_DARK, COLOR_GRAY_DARK, COLOR_YELLOW}; -use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE}; +use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_DARK, COLOR_YELLOW}; +use crate::gui::icons::{CARDHOLDER, DATABASE, DOTS_THREE_OUTLINE_VERTICAL, FACTORY, FADERS, GAUGE, PLUGS, POWER}; use crate::gui::Navigator; use crate::gui::platform::PlatformCallbacks; use crate::gui::views::{NetworkTab, View}; @@ -71,7 +71,7 @@ impl Network { egui::TopBottomPanel::bottom("network_tabs") .frame(egui::Frame { - outer_margin: Margin::same(6.0), + outer_margin: Margin::same(5.0), .. Default::default() }) .resizable(false) @@ -93,26 +93,28 @@ impl Network { fn draw_tabs(&mut self, ui: &mut egui::Ui) { ui.scope(|ui| { - //Setup spacing between tabs - ui.style_mut().spacing.item_spacing = egui::vec2(6.0, 0.0); + // Setup spacing between tabs. + ui.style_mut().spacing.item_spacing = egui::vec2(5.0, 0.0); + // Setup vertical padding inside tab button. + ui.style_mut().spacing.button_padding = egui::vec2(0.0, 3.0); ui.columns(4, |columns| { - columns[0].vertical_centered(|ui| { + columns[0].vertical_centered_justified(|ui| { View::tab_button(ui, DATABASE, self.current_mode == Mode::Node, || { self.current_mode = Mode::Node; }); }); - columns[1].vertical_centered(|ui| { + columns[1].vertical_centered_justified(|ui| { View::tab_button(ui, GAUGE, self.current_mode == Mode::Metrics, || { self.current_mode = Mode::Metrics; }); }); - columns[2].vertical_centered(|ui| { + columns[2].vertical_centered_justified(|ui| { View::tab_button(ui, FACTORY, self.current_mode == Mode::Miner, || { self.current_mode = Mode::Miner; }); }); - columns[3].vertical_centered(|ui| { + columns[3].vertical_centered_justified(|ui| { View::tab_button(ui, FADERS, self.current_mode == Mode::Tuning, || { self.current_mode = Mode::Tuning; }); @@ -200,9 +202,7 @@ impl Network { }); strip.cell(|ui| { ui.centered_and_justified(|ui| { - // Select sync status text let sync_status = Node::get_sync_status(); - let status_text = Node::get_sync_status_text(sync_status); // Setup text color animation based on sync status let idle = match sync_status { @@ -219,7 +219,7 @@ impl Network { // Draw sync text let status_color_rgba = Rgba::from(COLOR_GRAY_DARK) * color_factor as f32; let status_color = Color32::from(status_color_rgba); - View::ellipsize_text(ui, status_text, 15.0, status_color); + View::ellipsize_text(ui, Node::get_sync_status_text(), 15.0, status_color); // Repaint based on sync status if idle { @@ -231,5 +231,23 @@ impl Network { }); }); } + + pub fn server_off_content(ui: &mut egui::Ui) { + View::center_content(ui, [240.0, 214.0], |ui| { + ui.label(RichText::new(PLUGS) + .size(84.0) + .color(Color32::from_gray(200)) + ); + ui.add_space(-16.0); + ui.label(RichText::new(Node::get_sync_status_text()) + .size(19.0) + .color(Color32::from_gray(150)) + ); + ui.add_space(12.0); + View::button(ui, format!("{} {}", POWER, t!("network.enable")), COLOR_YELLOW, || { + Node::start(ChainTypes::Mainnet); + }); + }); + } } diff --git a/src/gui/views/network_metrics.rs b/src/gui/views/network_metrics.rs index fcde1ae..0fff062 100644 --- a/src/gui/views/network_metrics.rs +++ b/src/gui/views/network_metrics.rs @@ -15,11 +15,12 @@ use chrono::{DateTime, NaiveDateTime, Utc}; use eframe::epaint::{Color32, Rounding, Stroke}; use egui::{RichText, ScrollArea, Spinner, Widget}; +use grin_core::global::ChainTypes; use grin_servers::DiffBlock; -use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT}; -use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, TIMER}; -use crate::gui::views::{NetworkTab, View}; +use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT, COLOR_YELLOW}; +use crate::gui::icons::{AT, COINS, CUBE_TRANSPARENT, HASH, HOURGLASS_LOW, HOURGLASS_MEDIUM, PLUGS, POWER, TIMER}; +use crate::gui::views::{Network, NetworkTab, View}; use crate::node::Node; pub struct NetworkMetrics { @@ -45,11 +46,19 @@ impl NetworkTab for NetworkMetrics { fn ui(&mut self, ui: &mut egui::Ui) { let server_stats = Node::get_stats(); - // Show loading widget if server is not working or difficulty height is zero. - if !server_stats.is_some() || server_stats.as_ref().unwrap().diff_stats.height == 0 { - ui.centered_and_justified(|ui| { - Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui); - }); + if server_stats.is_none() || server_stats.as_ref().unwrap().diff_stats.height == 0 { + if !Node::is_running() { + Network::server_off_content(ui); + } else { + View::center_content(ui, [280.0, 160.0], |ui| { + Spinner::new().size(104.0).color(COLOR_YELLOW).ui(ui); + ui.add_space(18.0); + ui.label(RichText::new(t!("network_metrics.loading")) + .size(16.0) + .color(Color32::from_gray(150)) + ); + }); + } return; } diff --git a/src/gui/views/network_mining.rs b/src/gui/views/network_mining.rs index 2035f55..8a5dd15 100644 --- a/src/gui/views/network_mining.rs +++ b/src/gui/views/network_mining.rs @@ -11,3 +11,28 @@ // 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::Ui; +use crate::gui::views::NetworkTab; + +pub struct NetworkMining { + title: String +} + +impl Default for NetworkMining { + fn default() -> Self { + Self { + title: t!("network.mining").to_uppercase(), + } + } +} + +impl NetworkTab for NetworkMining { + fn name(&self) -> &String { + &self.title + } + + fn ui(&mut self, ui: &mut Ui) { + todo!() + } +} \ No newline at end of file diff --git a/src/gui/views/network_node.rs b/src/gui/views/network_node.rs index d37881d..ad6285b 100644 --- a/src/gui/views/network_node.rs +++ b/src/gui/views/network_node.rs @@ -14,11 +14,13 @@ use eframe::epaint::Stroke; use egui::{Color32, RichText, Rounding, ScrollArea, Spinner, Widget}; +use egui_extras::{Size, StripBuilder}; +use grin_core::global::ChainTypes; use grin_servers::PeerStats; -use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT}; -use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS_CONNECTED, SHARE_NETWORK}; -use crate::gui::views::{NetworkTab, View}; +use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_GRAY_LIGHT, COLOR_YELLOW}; +use crate::gui::icons::{AT, CUBE, DEVICES, FLOW_ARROW, HANDSHAKE, PACKAGE, PLUGS, PLUGS_CONNECTED, POWER, SHARE_NETWORK}; +use crate::gui::views::{Network, NetworkTab, View}; use crate::node::Node; pub struct NetworkNode { @@ -41,9 +43,13 @@ impl NetworkTab for NetworkNode { fn ui(&mut self, ui: &mut egui::Ui) { let server_stats = Node::get_stats(); if !server_stats.is_some() { - ui.centered_and_justified(|ui| { - Spinner::new().size(42.0).color(COLOR_GRAY).ui(ui); - }); + if !Node::is_running() { + Network::server_off_content(ui); + } else { + ui.centered_and_justified(|ui| { + Spinner::new().size(104.0).color(COLOR_YELLOW).ui(ui); + }); + } return; } diff --git a/src/gui/views/progress_loading.rs b/src/gui/views/progress_loading.rs deleted file mode 100644 index b8362fd..0000000 --- a/src/gui/views/progress_loading.rs +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2023 The Grim Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -use egui::{Response, RichText, Spinner, Ui, Widget}; - -use crate::gui::colors::COLOR_DARK; - -pub struct ProgressLoading { - text: String -} - -impl ProgressLoading { - pub fn new(text: String) -> Self { - Self { - text - } - } -} - -impl Widget for ProgressLoading { - fn ui(self, ui: &mut Ui) -> Response { - ui.vertical_centered_justified(|ui| { - ui.add_space(10.0); - Spinner::new().size(36.0).color(COLOR_DARK).ui(ui); - ui.add_space(10.0); - ui.label(RichText::new(self.text).size(18.0).color(COLOR_DARK)); - }).response - } -} \ No newline at end of file diff --git a/src/gui/views/views.rs b/src/gui/views/views.rs index a8b871c..0837e3d 100644 --- a/src/gui/views/views.rs +++ b/src/gui/views/views.rs @@ -14,10 +14,11 @@ use egui::epaint::{Color32, FontId, Rounding, Stroke}; use egui::text::{LayoutJob, TextFormat}; -use egui::{Button, PointerState, Response, RichText, Sense, Widget}; +use egui::{Button, PointerState, Response, RichText, Sense, Vec2, Widget}; use egui::epaint::text::TextWrapping; +use egui_extras::{Size, StripBuilder}; -use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT, COLOR_GRAY_LIGHT, COLOR_GRAY_DARK}; +use crate::gui::colors::{COLOR_DARK, COLOR_GRAY, COLOR_LIGHT, COLOR_GRAY_LIGHT, COLOR_GRAY_DARK, COLOR_YELLOW}; pub struct View; @@ -103,7 +104,6 @@ impl View { false => { Color32::WHITE } }; let br = Button::new(wt) - .min_size(ui.available_size_before_wrap()) .stroke(stroke) .fill(color) .ui(ui).interact(Sense::click_and_drag()); @@ -111,16 +111,12 @@ impl View { Self::on_button_click(ui, br, action); } - /// Modal button with white background fill color, contains text. - pub fn modal_button(ui: &mut egui::Ui, text: String, action: impl FnOnce()) { - let mut size = ui.available_size_before_wrap(); - size.y = 36.0; - + /// Draw [`Button`] with specified background fill color. + 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(COLOR_GRAY_DARK); let br = Button::new(wt) .stroke(Self::DEFAULT_STROKE) - .min_size(size) - .fill(Color32::WHITE) + .fill(fill_color) .ui(ui).interact(Sense::click_and_drag()); Self::on_button_click(ui, br, action); @@ -169,4 +165,18 @@ impl View { ui.label(RichText::new(label).color(COLOR_GRAY).size(15.0)); }); } + + /// Draw content in the center of current layout with specified width and height. + pub fn center_content(ui: &mut egui::Ui, w_h: [f32; 2], content: impl FnOnce(&mut egui::Ui)) { + ui.vertical_centered(|ui| { + let mut rect = ui.available_rect_before_wrap(); + let side_margin = (ui.available_width() - w_h[0]) / 2.0; + rect.min += egui::emath::vec2(side_margin, ui.available_height() / 2.0 - w_h[1] / 2.0); + rect.max -= egui::emath::vec2(side_margin, 0.0); + // rect.set_width(w_h[0]); + ui.allocate_ui_at_rect(rect, |ui| { + (content)(ui); + }); + }); + } } \ No newline at end of file diff --git a/src/node/node.rs b/src/node/node.rs index a3dfb53..e8a2225 100644 --- a/src/node/node.rs +++ b/src/node/node.rs @@ -30,11 +30,11 @@ use lazy_static::lazy_static; use log::info; lazy_static! { - /// Static thread-aware state of [Node] to be updated from another thread. + /// Static thread-aware state of [`Node`] to be updated from another thread. static ref NODE_STATE: Arc = Arc::new(Node::default()); } -/// Provides [Server] control, holds current status and statistics. +/// Provides [`Server`] control, holds current status and statistics. pub struct Node { /// Statistics data for UI. stats: Arc>>, @@ -61,12 +61,12 @@ impl Default for Node { } impl Node { - /// Stop [Server]. + /// Stop the [`Server`]. pub fn stop() { NODE_STATE.stop_needed.store(true, Ordering::Relaxed); } - /// Start [Server] with provided chain type. + /// Start [`Server`] with provided chain type. pub fn start(chain_type: ChainTypes) { if !Self::is_running() { let mut w_chain_type = NODE_STATE.chain_type.write().unwrap(); @@ -75,7 +75,7 @@ impl Node { } } - /// Restart [Server] with provided chain type. + /// Restart [`Server`] with provided chain type. pub fn restart(chain_type: ChainTypes) { if Self::is_running() { let mut w_chain_type = NODE_STATE.chain_type.write().unwrap(); @@ -86,40 +86,40 @@ impl Node { } } - /// Check if [Server] is starting. + /// Check if [`Server`] is starting. pub fn is_starting() -> bool { NODE_STATE.starting.load(Ordering::Relaxed) } - /// Check if [Server] is running. + /// Check if [`Server`] is running. pub fn is_running() -> bool { - Self::get_stats().is_some() || Self::is_starting() + Self::get_sync_status().is_some() } - /// Check if [Server] is stopping. + /// Check if [`Server`] is stopping. pub fn is_stopping() -> bool { NODE_STATE.stop_needed.load(Ordering::Relaxed) } - /// Check if [Server] is restarting. + /// Check if [`Server`] is restarting. pub fn is_restarting() -> bool { NODE_STATE.restart_needed.load(Ordering::Relaxed) } - /// Get [Server] statistics. + /// Get [`Server`] statistics. pub fn get_stats() -> RwLockReadGuard<'static, Option> { NODE_STATE.stats.read().unwrap() } - /// Get [Server] synchronization status, empty when it is not running. + /// Get [`Server`] synchronization status, empty when Server is not running. pub fn get_sync_status() -> Option { // Return Shutdown status when node is stopping. if Self::is_stopping() { return Some(SyncStatus::Shutdown) } - // Return Initial status when node is starting. - if Self::is_starting() { + // Return Initial status when node is starting or restarting. + if Self::is_starting() || Self::is_restarting() { return Some(SyncStatus::Initial) } @@ -131,16 +131,24 @@ impl Node { None } - /// Start a thread to launch [Server] and update [NODE_STATE] with server statistics. - fn start_server_thread() -> JoinHandle<()> { + /// Start a thread to launch [`Server`] and update [`NODE_STATE`] with server statistics. + fn start_server_thread() { thread::spawn(move || { NODE_STATE.starting.store(true, Ordering::Relaxed); + // Start the server. let mut server = start_server(&NODE_STATE.chain_type.read().unwrap()); let mut first_start = true; loop { if Self::is_restarting() { + // Clean server stats. + { + let mut w_stats = NODE_STATE.stats.write().unwrap(); + *w_stats = None; + } + + // Stop the server. server.stop(); // Create new server with current chain type. @@ -148,10 +156,14 @@ impl Node { NODE_STATE.restart_needed.store(false, Ordering::Relaxed); } else if Self::is_stopping() { - server.stop(); + // Clean server stats. + { + let mut w_stats = NODE_STATE.stats.write().unwrap(); + *w_stats = None; + } - let mut w_stats = NODE_STATE.stats.write().unwrap(); - *w_stats = None; + // Stop the server. + server.stop(); NODE_STATE.starting.store(false, Ordering::Relaxed); NODE_STATE.stop_needed.store(false, Ordering::Relaxed); @@ -159,8 +171,11 @@ impl Node { } else { let stats = server.get_server_stats(); if stats.is_ok() { - let mut w_stats = NODE_STATE.stats.write().unwrap(); - *w_stats = Some(stats.as_ref().ok().unwrap().clone()); + // Update server stats. + { + let mut w_stats = NODE_STATE.stats.write().unwrap(); + *w_stats = Some(stats.as_ref().ok().unwrap().clone()); + } if first_start { NODE_STATE.starting.store(false, Ordering::Relaxed); @@ -170,18 +185,24 @@ impl Node { } thread::sleep(Duration::from_millis(250)); } - }) + }); } /// Get synchronization status i18n text. - pub fn get_sync_status_text(sync_status: Option) -> String { + pub fn get_sync_status_text() -> String { + if Node::is_starting() { + return t!("sync_status.initial") + }; + + if Node::is_stopping() { + return t!("sync_status.shutdown") + }; + if Node::is_restarting() { return t!("sync_status.server_restarting") } - if Node::is_stopping() { - return t!("sync_status.shutdown") - } + let sync_status = Self::get_sync_status(); if sync_status.is_none() { return t!("sync_status.server_down") @@ -256,7 +277,7 @@ impl Node { } -/// Start [Server] with provided chain type. +/// Start the [`Server`] with provided chain type. fn start_server(chain_type: &ChainTypes) -> Server { // Initialize config let mut node_config_result = config::initial_setup_server(chain_type); @@ -358,14 +379,13 @@ fn start_server(chain_type: &ChainTypes) -> Server { #[cfg(target_os = "android")] #[allow(non_snake_case)] #[no_mangle] -/// Get sync status text for Android notification from [NODE_STATE] in Java string format. +/// Get sync status text for Android notification from [`NODE_STATE`] in Java string format. pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncStatusText( _env: jni::JNIEnv, _class: jni::objects::JObject, _activity: jni::objects::JObject, ) -> jstring { - let sync_status = Node::get_sync_status(); - let status_text = Node::get_sync_status_text(sync_status); + let status_text = Node::get_sync_status_text(); let j_text = _env.new_string(status_text); return j_text.unwrap().into_raw(); } @@ -388,7 +408,7 @@ pub extern "C" fn Java_mw_gri_android_BackgroundService_getSyncTitle( #[cfg(target_os = "android")] #[allow(non_snake_case)] #[no_mangle] -/// Calling on unexpected application termination (removal from recent apps) +/// Calling on unexpected Android application termination (removal from recent apps). pub extern "C" fn Java_mw_gri_android_MainActivity_onTermination( _env: jni::JNIEnv, _class: jni::objects::JObject,