From c64c47f0163a03ce7c9dfdcf0a74651301e225ce Mon Sep 17 00:00:00 2001 From: Anton Yemelyanov Date: Sun, 21 Jan 2024 06:03:54 +0200 Subject: [PATCH] popup ids, server selection, other misc updates --- README.md | 15 +++++ Servers.toml | 27 ++++++++- core/src/egui/popup.rs | 34 ++++++++++- core/src/menu.rs | 3 +- core/src/modules/account_manager/menus.rs | 6 +- .../modules/account_manager/transactions.rs | 9 +-- core/src/modules/account_manager/transfer.rs | 3 +- core/src/modules/block_dag.rs | 22 +++++-- core/src/modules/metrics.rs | 2 +- core/src/modules/overview.rs | 4 ++ core/src/modules/settings/mod.rs | 2 +- core/src/modules/welcome.rs | 45 ++++++++------ core/src/notifications.rs | 3 +- core/src/primitives/block.rs | 11 +++- core/src/servers.rs | 59 ++++++++++++++----- core/src/status.rs | 29 +++++---- core/src/storage.rs | 6 +- resources/i18n/i18n.json | 27 +++++---- 18 files changed, 217 insertions(+), 90 deletions(-) diff --git a/README.md b/README.md index daf506a..768fa31 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,21 @@ Once you have Rusty Kaspa built, you will be able to build and run this project ```bash cargo run --release ``` +Once you have Rusty Kaspa built, you will be able to build and run this project as follows: + +#### Running as Native App +```bash +cargo run --release +``` + +#### Running as Web App +```bash +cargo install trunk +trunk serve --release +``` +Access via [https://localhost:8080](https://localhost:8080) + +While the application is a static serve, you can not load it from the local file system due to CORS restrictions. Due to this, a web server is required. This application is designed to be built with [Trunk](https://trunkrs.dev/) and is served from the `dist/` folder. This is a self-contained client-side application - once the application is loaded, the web server is no longer required. ## License diff --git a/Servers.toml b/Servers.toml index 1ae0ea9..1755668 100644 --- a/Servers.toml +++ b/Servers.toml @@ -4,21 +4,42 @@ name = "kaspa-ng.org" location = "EU" protocol = "borsh" network = "mainnet" -address = "wss://eu-1.kaspa-ng.org:443/mainnet" +address = "wss://eu-1.kaspa-ng.org/mainnet" [[server]] name = "kaspa-ng.org" location = "EU" protocol = "borsh" network = "testnet-10" -address = "wss://eu-1.kaspa-ng.org:443/testnet-10" +address = "wss://eu-1.kaspa-ng.org/testnet-10" [[server]] name = "kaspa-ng.org" location = "EU" protocol = "borsh" network = "testnet-11" -address = "wss://eu-1.kaspa-ng.org:443/testnet-11" +address = "wss://eu-1.kaspa-ng.org/testnet-11" + +[[server]] +name = "kaspa-ng.org" +location = "US" +protocol = "borsh" +network = "mainnet" +address = "wss://us-west-1.kaspa-ng.org/mainnet" + +[[server]] +name = "kaspa-ng.org" +location = "US" +protocol = "borsh" +network = "testnet-10" +address = "wss://us-west-1.kaspa-ng.org/testnet-10" + +[[server]] +name = "kaspa-ng.org" +location = "US" +protocol = "borsh" +network = "testnet-11" +address = "wss://us-west-1.kaspa-ng.org/testnet-11" [[server]] name = "kaspa-ng.io" diff --git a/core/src/egui/popup.rs b/core/src/egui/popup.rs index 0b825a3..749bcf0 100644 --- a/core/src/egui/popup.rs +++ b/core/src/egui/popup.rs @@ -23,14 +23,14 @@ impl<'panel> PopupPanel<'panel> { } pub fn new( - ui: &mut Ui, - id: impl Into, + id: Id, widget: impl FnOnce(&mut Ui) -> Response + 'panel, content: impl FnOnce(&mut Ui, &mut bool) + 'panel, ) -> Self { - let id = ui.make_persistent_id(id.into()); + // let id = ui.make_persistent_id(id.into()); Self { + // id : id.into(), id, min_width: None, max_height: None, @@ -45,6 +45,34 @@ impl<'panel> PopupPanel<'panel> { } } + // pub fn new_with_string_id( + // ui: &mut Ui, + // id: impl Into, + // // id: Id, + // widget: impl FnOnce(&mut Ui) -> Response + 'panel, + // content: impl FnOnce(&mut Ui, &mut bool) + 'panel, + // ) -> Self { + // let id = ui.make_persistent_id(id.into()); + + // Self { + // id, + // min_width: None, + // max_height: None, + // widget: Box::new(widget), + // content: Box::new(content), + // caption: None, + // with_close_button: false, + // close_on_interaction: false, + // close_on_escape: true, + // above_or_below: AboveOrBelow::Below, + // with_padding: true, + // } + // } + + pub fn is_open(ui: &mut Ui, popup_id: Id) -> bool { + ui.memory(|mem| mem.is_popup_open(popup_id)) + } + pub fn with_min_width(mut self, min_width: f32) -> Self { self.min_width = Some(min_width); self diff --git a/core/src/menu.rs b/core/src/menu.rs index dc1fb6c..0df015d 100644 --- a/core/src/menu.rs +++ b/core/src/menu.rs @@ -64,8 +64,7 @@ impl<'core> Menu<'core> { ui.separator(); PopupPanel::new( - ui, - "display_settings", + PopupPanel::id(ui, "display_settings"), |ui| { ui.add( Label::new(RichText::new(egui_phosphor::light::MONITOR).size(16.)) diff --git a/core/src/modules/account_manager/menus.rs b/core/src/modules/account_manager/menus.rs index e07c844..c5ac854 100644 --- a/core/src/modules/account_manager/menus.rs +++ b/core/src/modules/account_manager/menus.rs @@ -19,7 +19,7 @@ impl WalletMenu { return; }; - PopupPanel::new(ui, "wallet_selector_popup",|ui|{ ui.add(Label::new(format!("{} {} ⏷", i18n("Wallet:"), wallet_name)).sense(Sense::click())) }, |ui, _| { + PopupPanel::new(PopupPanel::id(ui,"wallet_selector_popup"),|ui|{ ui.add(Label::new(format!("{} {} ⏷", i18n("Wallet:"), wallet_name)).sense(Sense::click())) }, |ui, _| { ScrollArea::vertical() .id_source("wallet_selector_popup_scroll") @@ -99,7 +99,7 @@ impl AccountMenu { pub fn render(&mut self, core: &mut Core, ui : &mut Ui, account_manager : &mut AccountManager, rc : &RenderContext<'_>, max_height: f32) { let RenderContext { account, network_type, .. } = rc; - PopupPanel::new(ui, "account_selector_popup",|ui|{ ui.add(Label::new(format!("{} {} ⏷",i18n("Account:"), account.name_or_id())).sense(Sense::click())) }, |ui, close| { + PopupPanel::new(PopupPanel::id(ui,"account_selector_popup"),|ui|{ ui.add(Label::new(format!("{} {} ⏷",i18n("Account:"), account.name_or_id())).sense(Sense::click())) }, |ui, close| { egui::ScrollArea::vertical() .id_source("account_selector_popup_scroll") @@ -187,7 +187,7 @@ impl ToolsMenu { } pub fn render(&mut self, core: &mut Core, ui : &mut Ui, _account_manager : &mut AccountManager, _rc : &RenderContext<'_>, max_height: f32) { - PopupPanel::new(ui, "tools_popup",|ui|{ ui.add(Label::new(i18n("Tools ⏷")).sense(Sense::click())) }, |ui, _| { + PopupPanel::new(PopupPanel::id(ui,"tools_popup"),|ui|{ ui.add(Label::new(i18n("Tools ⏷")).sense(Sense::click())) }, |ui, _| { egui::ScrollArea::vertical() .id_source("tools_popup_scroll") diff --git a/core/src/modules/account_manager/transactions.rs b/core/src/modules/account_manager/transactions.rs index 8d54c2e..409ceb1 100644 --- a/core/src/modules/account_manager/transactions.rs +++ b/core/src/modules/account_manager/transactions.rs @@ -14,13 +14,10 @@ impl Transactions { egui::ScrollArea::vertical().auto_shrink([false,false]).show(ui, |ui| { let transactions = account.transactions(); if transactions.is_empty() { - ui.label(i18n("No transactions")); - - #[cfg(target_arch = "wasm32")] { - ui.label(""); - ui.label("Please note that transaction storage support in the browser is not yet implemented. Only transactions from the available UTXOs will be shown once the wallet is reloaded."); + ui.vertical_centered(|ui| { ui.label(""); - } + ui.label(RichText::new(i18n("No transactions")).size(16.)); + }); } else { let total: u64 = transactions.iter().map(|transaction|transaction.aggregate_input_value()).sum(); transactions.iter().for_each(|transaction| { diff --git a/core/src/modules/account_manager/transfer.rs b/core/src/modules/account_manager/transfer.rs index ce337ee..c0d5077 100644 --- a/core/src/modules/account_manager/transfer.rs +++ b/core/src/modules/account_manager/transfer.rs @@ -38,8 +38,7 @@ impl<'context> Transfer<'context> { let transfer_to_account = self.context.transfer_to_account.clone(); - - PopupPanel::new(ui, "transfer_selector_popup",|ui|{ + PopupPanel::new(PopupPanel::id(ui,"transfer_selector_popup"),|ui|{ let response = ui.vertical_centered(|ui| { if let Some(account) = transfer_to_account { let response = ui.add(Label::new(format!("Transferring funds to: {} ⏷", account.name_or_id())).sense(Sense::click())); diff --git a/core/src/modules/block_dag.rs b/core/src/modules/block_dag.rs index db10fe1..38f4ec0 100644 --- a/core/src/modules/block_dag.rs +++ b/core/src/modules/block_dag.rs @@ -13,6 +13,7 @@ pub struct Preset { daa_range : f64, daa_offset : f64, spread : f64, + noise : f64, block_scale : f64, } @@ -22,6 +23,7 @@ const PRESETS: &[Preset] = &[ daa_range : 36.0, daa_offset : 10.0, spread : 10.0, + noise : 0.0, block_scale : 1.0, }, Preset { @@ -29,6 +31,7 @@ const PRESETS: &[Preset] = &[ daa_range : 100.0, daa_offset : 16.0, spread : 36.0, + noise : 0.0, block_scale : 1.0, }, Preset { @@ -36,6 +39,7 @@ const PRESETS: &[Preset] = &[ daa_range : 80.0, daa_offset : 16.0, spread : 22.0, + noise : 0.0, block_scale : 1.2, }, Preset { @@ -43,6 +47,7 @@ const PRESETS: &[Preset] = &[ daa_range : 180.0, daa_offset : 32.0, spread : 36.0, + noise : 1.0, block_scale : 1.4, }, ]; @@ -113,8 +118,9 @@ impl BlockDag { pub fn load_preset(&mut self, preset : &Preset) { self.daa_range = preset.daa_range; self.daa_offset = preset.daa_offset; - self.settings.y_dist = preset.spread; self.block_scale = preset.block_scale; + self.settings.y_dist = preset.spread; + self.settings.noise = preset.noise; } fn reset_state(&mut self) { @@ -152,6 +158,7 @@ impl ModuleT for BlockDag { let theme_color = theme_color(); let y_dist = self.settings.y_dist; + let noise = self.settings.noise; let vspc_center = self.settings.center_vspc; if core.settings.node.network != self.network { @@ -164,7 +171,7 @@ impl ModuleT for BlockDag { ui.heading(i18n("Block DAG")); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - PopupPanel::new(ui, "block_dag_settings",|ui|{ ui.add(Label::new("Settings ⏷").sense(Sense::click())) }, |ui, _| { + PopupPanel::new(PopupPanel::id(ui,"block_dag_settings"),|ui|{ ui.add(Label::new("Settings ⏷").sense(Sense::click())) }, |ui, _| { CollapsingHeader::new(i18n("Dimensions")) .open(Some(true)) @@ -193,6 +200,12 @@ impl ModuleT for BlockDag { .text(i18n("Spread")) ); ui.space(); + ui.add( + Slider::new(&mut self.settings.noise, 0.0..=10.0) + .clamp_to_range(true) + .text(i18n("Noise")) + ); + ui.space(); ui.add( Slider::new(&mut self.block_scale, 0.1..=2.5) .clamp_to_range(true) @@ -257,8 +270,7 @@ impl ModuleT for BlockDag { let response = ui .add(Label::new(RichText::new(format!("{} ⏷", i18n("Presets")))).sense(Sense::click())); PopupPanel::new( - ui, - "network_selector_popup", + PopupPanel::id(ui,"network_selector_popup"), |_ui| response, |ui, close| { set_menu_style(ui.style_mut()); @@ -276,7 +288,7 @@ impl ModuleT for BlockDag { }); ui.separator(); - if y_dist != self.settings.y_dist || vspc_center != self.settings.center_vspc { + if y_dist != self.settings.y_dist || noise != self.settings.noise || vspc_center != self.settings.center_vspc { runtime().block_dag_monitor_service().update_settings(self.settings.clone()); } diff --git a/core/src/modules/metrics.rs b/core/src/modules/metrics.rs index a1dd15f..194ceae 100644 --- a/core/src/modules/metrics.rs +++ b/core/src/modules/metrics.rs @@ -66,7 +66,7 @@ impl ModuleT for Metrics { ui.horizontal(|ui|{ ui.heading(i18n("Metrics")); ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| { - PopupPanel::new(ui, "metrics_settings",|ui|{ ui.add(Label::new("Settings ⏷").sense(Sense::click())) }, |ui, _| { + PopupPanel::new(PopupPanel::id(ui,"metrics_settings"),|ui|{ ui.add(Label::new("Settings ⏷").sense(Sense::click())) }, |ui, _| { ui.add( Slider::new(&mut graph_columns, 1..=8) .text("Columns") diff --git a/core/src/modules/overview.rs b/core/src/modules/overview.rs index 89378b6..30a5846 100644 --- a/core/src/modules/overview.rs +++ b/core/src/modules/overview.rs @@ -166,6 +166,10 @@ impl Overview { .show(ui, |ui| { // egui::special_emojis // use egui_phosphor::light::{DISCORD_LOGO,GITHUB_LOGO}; + ui.hyperlink_to_tab( + format!("• {}",i18n("Kaspa NG Web App")), + "https://kaspa-ng.org" + ); ui.hyperlink_to_tab( format!("• {}",i18n("Kaspa NG on GitHub")), "https://github.com/aspectron/kaspa-ng" diff --git a/core/src/modules/settings/mod.rs b/core/src/modules/settings/mod.rs index 8478faf..fad723a 100644 --- a/core/src/modules/settings/mod.rs +++ b/core/src/modules/settings/mod.rs @@ -417,7 +417,7 @@ impl Settings { CollapsingHeader::new(i18n("Check for Updates")) .default_open(true) .show(ui, |ui| { - if ui.checkbox(&mut self.settings.update_monitor, i18n("Check for Software Updates via GitHub")).changed() { + if ui.checkbox(&mut self.settings.update_monitor, i18n("Check for Software Updates on GitHub")).changed() { core.settings.update_monitor = self.settings.update_monitor; self.runtime.update_monitor_service().enable(core.settings.update_monitor); core.store_settings(); diff --git a/core/src/modules/welcome.rs b/core/src/modules/welcome.rs index e7ec6d2..619aa0d 100644 --- a/core/src/modules/welcome.rs +++ b/core/src/modules/welcome.rs @@ -28,6 +28,8 @@ impl Welcome { ui: &mut egui::Ui, ) { + let mut error = None; + ui.heading(i18n("Welcome to Kaspa NG")); ui.add_space(16.0); ui.label(i18n("Please configure your Kaspa NG settings")); @@ -74,7 +76,7 @@ impl Welcome { }); if self.settings.node.node_kind == KaspadNodeKind::Remote { - crate::modules::settings::Settings::render_remote_settings(core,ui,&mut self.settings.node); + error = crate::modules::settings::Settings::render_remote_settings(core,ui,&mut self.settings.node); } }); @@ -140,22 +142,31 @@ impl Welcome { }); ui.add_space(32.0); - ui.horizontal(|ui| { - ui.add_space( - ui.available_width() - - 16. - - (theme_style().medium_button_size.x + ui.spacing().item_spacing.x), - ); - if ui.medium_button(format!("{} {}", egui_phosphor::light::CHECK, i18n("Apply"))).clicked() { - let mut settings = self.settings.clone(); - settings.initialized = true; - settings.store_sync().expect("Unable to store settings"); - self.runtime.kaspa_service().update_services(&self.settings.node, None); - core.settings = settings.clone(); - core.get_mut::().load(settings); - core.select::(); - } - }); + if let Some(error) = error { + ui.vertical_centered(|ui| { + ui.colored_label(theme_color().alert_color, error); + }); + ui.add_space(32.0); + } else { + + ui.horizontal(|ui| { + ui.add_space( + ui.available_width() + - 16. + - (theme_style().medium_button_size.x + ui.spacing().item_spacing.x), + ); + if ui.medium_button(format!("{} {}", egui_phosphor::light::CHECK, i18n("Apply"))).clicked() { + let mut settings = self.settings.clone(); + settings.initialized = true; + settings.store_sync().expect("Unable to store settings"); + self.runtime.kaspa_service().update_services(&self.settings.node, None); + core.settings = settings.clone(); + core.get_mut::().load(settings); + core.select::(); + } + }); + } + ui.separator(); }); diff --git a/core/src/notifications.rs b/core/src/notifications.rs index 2269c7c..e8a7673 100644 --- a/core/src/notifications.rs +++ b/core/src/notifications.rs @@ -204,8 +204,7 @@ impl Notifications { let height = (screen_rect.height() / 4.).min(240.); PopupPanel::new( - ui, - "notification_popup", + PopupPanel::id(ui, "notification_popup"), |ui| ui.add(Label::new(icon.size(16.)).sense(Sense::click())), |ui, close| { egui::ScrollArea::vertical() diff --git a/core/src/primitives/block.rs b/core/src/primitives/block.rs index 7fd9d0a..2453a57 100644 --- a/core/src/primitives/block.rs +++ b/core/src/primitives/block.rs @@ -5,6 +5,7 @@ use kaspa_rpc_core::RpcBlock; pub struct BlockDagGraphSettings { pub y_scale: f64, pub y_dist: f64, + pub noise: f64, pub graph_length_daa: usize, pub center_vspc: bool, pub balance_vspc: bool, @@ -18,6 +19,7 @@ impl Default for BlockDagGraphSettings { Self { y_scale: 10.0, y_dist: 7.0, + noise: 0.0, graph_length_daa: 1024, center_vspc: false, balance_vspc: true, @@ -42,6 +44,7 @@ pub struct DagBlock { pub data: Arc, pub src_y: f64, pub dst_y: f64, + pub offset_y: f64, vspc: bool, settled: bool, } @@ -53,6 +56,7 @@ impl DagBlock { data, src_y: y, dst_y: y, + offset_y: y, vspc: false, settled: false, } @@ -105,6 +109,7 @@ impl DaaBucket { // .sort_by(|a, b| a.src_y.partial_cmp(&b.src_y).unwrap()); .sort_by(|a, b| a.dst_y.partial_cmp(&b.dst_y).unwrap()); let y_distance = settings.y_dist; + let noise = settings.noise; let len = self.blocks.len(); if settings.balance_vspc { @@ -134,13 +139,13 @@ impl DaaBucket { (0..vspc_idx).rev().for_each(|idx| { let block = &mut self.blocks[idx]; y -= y_distance; - block.dst_y = y; + block.dst_y = y - block.offset_y * noise; }); y = vspc_y; ((vspc_idx + 1)..len).for_each(|idx| { let block = &mut self.blocks[idx]; y += y_distance; - block.dst_y = y; + block.dst_y = y + block.offset_y * noise; }); } else { if len > 1 { @@ -148,7 +153,7 @@ impl DaaBucket { (0..len).for_each(|idx| { let block = &mut self.blocks[idx]; y += y_distance; - block.dst_y = y; + block.dst_y = y + block.offset_y * noise; }); } } diff --git a/core/src/servers.rs b/core/src/servers.rs index cf08913..1e07f15 100644 --- a/core/src/servers.rs +++ b/core/src/servers.rs @@ -7,22 +7,35 @@ pub fn public_server_config() -> &'static ServerCollection { SERVERS.get_or_init(|| Arc::new(Mutex::new(parse_default_servers().clone()))) } -#[derive(Clone, Debug, Serialize, Deserialize, Eq, PartialEq)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Server { pub name: Option, + pub ident: Option, pub location: Option, pub protocol: WrpcEncoding, - pub port: Option, pub address: String, pub enable: Option, pub link: Option, pub network: Network, + pub bias: Option, + pub manual: Option, + pub version: Option, +} + +impl Eq for Server {} + +impl PartialEq for Server { + fn eq(&self, other: &Self) -> bool { + self.address == other.address + } } impl std::fmt::Display for Server { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let mut title = self.name.clone().unwrap_or(self.address.to_string()); - if let Some(location) = self.location.as_ref() { + if let Some(ident) = self.ident.as_ref() { + title += format!(" ({ident})").as_str(); + } else if let Some(location) = self.location.as_ref() { title += format!(" ({location})").as_str(); } @@ -32,11 +45,7 @@ impl std::fmt::Display for Server { impl Server { pub fn address(&self) -> String { - if let Some(port) = self.port { - format!("{}:{port}", self.address) - } else { - self.address.clone() - } + self.address.clone() } pub fn wrpc_encoding(&self) -> WrpcEncoding { @@ -159,10 +168,11 @@ pub fn random_public_server(network: &Network, options: Option) -> O if let Some(servers) = servers.get(network) { #[allow(clippy::nonminimal_bool)] - let servers = servers + let mut servers = servers .iter() .filter(|server| { server.enable.unwrap_or(true) + && !server.manual.unwrap_or(false) && !server.address.contains("localhost") && !server.address.contains("127.0.0.1") && !(tls() @@ -170,13 +180,31 @@ pub fn random_public_server(network: &Network, options: Option) -> O || server.address.starts_with("wrpcs://"))) && !blacklist_servers.contains(&server.address) }) + .cloned() .collect::>(); + let max = servers + .iter() + .map(|server| server.bias.unwrap_or(1.)) + .max_by(|a, b| a.total_cmp(b)) + .unwrap_or(1.); + servers.iter_mut().for_each(|server| { + server.bias = Some(server.bias.unwrap_or(1.) / max); + }); + if servers.is_empty() { log_error!("Unable to select random public server: no servers available"); None } else { - Some(servers[rand::thread_rng().gen::() % servers.len()].clone()) + let mut server = None; + while server.is_none() { + let selected = &servers[rand::thread_rng().gen::() % servers.len()]; + let f = rand::thread_rng().gen_range(0.0..1.0); + if f < selected.bias.unwrap_or(1.) { + server = Some(selected.clone()); + } + } + server } } else { log_error!("Unable to select random public server: no servers available for this network"); @@ -198,7 +226,9 @@ pub fn render_public_server_selector( let (text, _secondary) = if let Some(server) = settings.public_servers.get(&settings.network) { (server.to_string(), Option::::None) } else { - node_settings_error = Some(i18n("No public node selected")); + node_settings_error = Some(i18n( + "No public node selected - please select a public node", + )); (i18n("Select Public Node").to_string(), None) }; @@ -209,8 +239,7 @@ pub fn render_public_server_selector( ); PopupPanel::new( - ui, - "server_selector_popup", + PopupPanel::id(ui, "server_selector_popup"), |_ui| response, |ui, close| { egui::ScrollArea::vertical() @@ -241,11 +270,11 @@ pub fn render_public_server_selector( *close = true; } - ui.add_space(4.); if let Some(link) = server.link.as_ref() { + ui.add_space(4.); ui.hyperlink_url_to_tab(link); + ui.add_space(4.); } - ui.add_space(4.); } }); }, diff --git a/core/src/status.rs b/core/src/status.rs index 65ba6cd..bfa9aea 100644 --- a/core/src/status.rs +++ b/core/src/status.rs @@ -138,20 +138,23 @@ impl<'core> Status<'core> { } }); } else { - let response = + let mut response = ui.add(Label::new(RichText::new(i18n("CONNECTED"))).sense(Sense::click())); - let response = response.on_hover_ui(|ui| { - if let Some(wrpc_url) = runtime().kaspa_service().rpc_url() { - ui.horizontal(|ui| { - ui.label(wrpc_url); - }); - } - }); + let popup_id = PopupPanel::id(ui, "node_connection_selector_popup"); + + if !PopupPanel::is_open(ui, popup_id) { + response = response.on_hover_ui(|ui| { + if let Some(wrpc_url) = runtime().kaspa_service().rpc_url() { + ui.horizontal(|ui| { + ui.label(wrpc_url); + }); + } + }); + } PopupPanel::new( - ui, - "node_connection_selector_popup", + popup_id, |_ui| response, |ui, close| { set_menu_style(ui.style_mut()); @@ -210,9 +213,9 @@ impl<'core> Status<'core> { Label::new(RichText::new(self.settings().node.network.to_string())) .sense(Sense::click()), ); + let id = PopupPanel::id(ui, "network_selector_popup"); PopupPanel::new( - ui, - "network_selector_popup", + id, |_ui| response, |ui, close| { set_menu_style(ui.style_mut()); @@ -311,7 +314,7 @@ impl<'core> Status<'core> { .as_ref() { let elapsed = instant.elapsed(); - if elapsed.as_millis() > 2_500 { + if elapsed.as_millis() > 3_500 { if ui .add( Label::new(RichText::new(i18n( diff --git a/core/src/storage.rs b/core/src/storage.rs index f2dfd1b..565c085 100644 --- a/core/src/storage.rs +++ b/core/src/storage.rs @@ -210,12 +210,12 @@ impl Storage { let is_running = core.settings.node.network == *network && core.settings.node.node_kind.is_local(); ui.horizontal(|ui|{ - if ui.medium_button(i18n("Open Folder")).clicked() { + if ui.medium_button(i18n("Open Data Folder")).clicked() { if let Err(err) = open::that(path) { runtime().error(format!("Error opening folder: {:?}", err)); } } - if ui.medium_button_enabled(!is_running && !*confirm_deletion, i18n("Delete Database & Logs")).clicked() { + if ui.medium_button_enabled(!is_running && !*confirm_deletion, i18n("Delete Data Folder")).clicked() { *confirm_deletion = true; } }); @@ -227,6 +227,8 @@ impl Storage { if *confirm_deletion { ui.add_sized(vec2(260.,4.), Separator::default()); + ui.label(i18n("This action will erase Kaspa database and logs")); + ui.label(""); ui.colored_label(theme_color().alert_color, i18n("Please Confirm Deletion")); if let Some(response) = ui.confirm_medium_apply_cancel(Align::Min) { match response { diff --git a/resources/i18n/i18n.json b/resources/i18n/i18n.json index bcc7bda..a530657 100644 --- a/resources/i18n/i18n.json +++ b/resources/i18n/i18n.json @@ -15,12 +15,12 @@ "vi": "Vietnamese", "fil": "Filipino", "lt": "Lithuanian", + "fi": "Finnish", + "sv": "Swedish", "pa": "Panjabi", "fa": "Farsi", - "es": "Español", - "sv": "Swedish", "uk": "Ukrainian", - "fi": "Finnish", + "es": "Español", "af": "Afrikaans", "et": "Esti", "en": "English", @@ -60,14 +60,14 @@ "pt": {}, "hi": {}, "fi": {}, - "nl": {}, "vi": {}, "fil": {}, "lt": {}, + "nl": {}, + "sv": {}, + "es": {}, "pa": {}, "fa": {}, - "es": {}, - "sv": {}, "uk": {}, "af": {}, "et": {}, @@ -145,31 +145,31 @@ "Your wallet has been created and is ready to use.": "Your wallet has been created and is ready to use.", "Please create a stronger password": "Please create a stronger password", "Disabled": "Disabled", + "Open Data Folder": "Open Data Folder", "Very dangerous (may be cracked within few seconds)": "Very dangerous (may be cracked within few seconds)", "Secret is too weak": "Secret is too weak", "Please note, copying to clipboard carries a risk of exposing your mnemonic to malware.": "Please note, copying to clipboard carries a risk of exposing your mnemonic to malware.", + "No public node selected - please select a public node": "No public node selected - please select a public node", "Resulting daemon arguments:": "Resulting daemon arguments:", - "Delete Database & Logs": "Delete Database & Logs", + "Network": "Network", "Time Offset:": "Time Offset:", "Market Monitor": "Market Monitor", "Unlock Wallet": "Unlock Wallet", "Wallet Encryption Password": "Wallet Encryption Password", - "Network": "Network", "Please note that this is an alpha release. Until this message is removed, avoid using this software with mainnet funds.": "Please note that this is an alpha release. Until this message is removed, avoid using this software with mainnet funds.", "You are currently not connected to the Kaspa node.": "You are currently not connected to the Kaspa node.", "Dependencies": "Dependencies", "Active p2p Peers": "Active p2p Peers", "Updating...": "Updating...", "Chain Blocks": "Chain Blocks", - "Check for Software Updates via GitHub": "Check for Software Updates via GitHub", "p2p Tx": "p2p Tx", "Advanced": "Advanced", "Payment Request": "Payment Request", "The balance may be out of date during node sync": "The balance may be out of date during node sync", "Testnet-10": "Testnet-10", "Clear": "Clear", - "Metrics are not currently available": "Metrics are not currently available", "Syncing UTXO entries...": "Syncing UTXO entries...", + "Metrics are not currently available": "Metrics are not currently available", "p2p Tx/s": "p2p Tx/s", "wRPC Encoding:": "wRPC Encoding:", "License Information": "License Information", @@ -181,7 +181,6 @@ "User Agent": "User Agent", "Welcome to Kaspa NG": "Welcome to Kaspa NG", "Please connect to Kaspa p2p node": "Please connect to Kaspa p2p node", - "No public node selected": "No public node selected", "bye!": "bye!", "Json Handshake Failures": "Json Handshake Failures", "The node is spawned as a child daemon process (recommended).": "The node is spawned as a child daemon process (recommended).", @@ -278,7 +277,6 @@ "Type": "Type", "Filename:": "Filename:", "Please wait...": "Please wait...", - "Open Folder": "Open Folder", "DAA Range": "DAA Range", "Volume": "Volume", "You can create multiple wallets, but only one wallet can be open at a time.": "You can create multiple wallets, but only one wallet can be open at a time.", @@ -289,6 +287,7 @@ "Export Wallet Data": "Export Wallet Data", "Please select the private key to export": "Please select the private key to export", "Enter your wallet secret": "Enter your wallet secret", + "Kaspa NG Web App": "Kaspa NG Web App", "Reset Settings": "Reset Settings", "Copied to clipboard": "Copied to clipboard", "CONNECTED": "CONNECTED", @@ -350,6 +349,7 @@ "p2p Rx": "p2p Rx", "Other operations": "Other operations", "12 word mnemonic": "12 word mnemonic", + "Noise": "Noise", "ECDSA": "ECDSA", "Derivation Indexes": "Derivation Indexes", "Continue": "Continue", @@ -375,6 +375,7 @@ "Phishing Hint": "Phishing Hint", "UTXO Manager": "UTXO Manager", "Recommended arguments for the remote node: ": "Recommended arguments for the remote node: ", + "Please note that this is an alpha release. Until this message is removed, please avoid using the wallet with mainnet funds.": "Please note that this is an alpha release. Until this message is removed, please avoid using the wallet with mainnet funds.", "Outbound": "Outbound", "Wallet Created": "Wallet Created", "Build": "Build", @@ -384,6 +385,7 @@ "Block Scale": "Block Scale", "Protocol:": "Protocol:", "Please wait for the node to sync or connect to a remote node.": "Please wait for the node to sync or connect to a remote node.", + "Delete Data Folder": "Delete Data Folder", "Create New Wallet": "Create New Wallet", "Presets": "Presets", "Opening wallet:": "Opening wallet:", @@ -395,6 +397,7 @@ "Select Available Server": "Select Available Server", "Invalid network type - expected: testnet-10 connected to: testnet-11": "Invalid network type - expected: testnet-10 connected to: testnet-11", "p2p RPC": "p2p RPC", + "Check for Software Updates on GitHub": "Check for Software Updates on GitHub", "wRPC Borsh Tx/s": "wRPC Borsh Tx/s", "Private Key Mnemonic": "Private Key Mnemonic", "Center VSPC": "Center VSPC",