diff --git a/bin/app/src/app/schema/mod.rs b/bin/app/src/app/schema/mod.rs index 9317b5270..d3a0609e3 100644 --- a/bin/app/src/app/schema/mod.rs +++ b/bin/app/src/app/schema/mod.rs @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +use darkfi::system::msleep; use darkfi_serial::Encodable; use indoc::indoc; use sled_overlay::sled; @@ -23,7 +24,7 @@ use std::fs::File; use crate::{ app::{ - node::{create_layer, create_shortcut, create_vector_art, create_video}, + node::{create_button, create_layer, create_shortcut, create_vector_art, create_video}, App, }, expr::{self, Compiler}, @@ -31,7 +32,7 @@ use crate::{ prop::{PropertyAtomicGuard, Role}, scene::{SceneNodePtr, Slot}, shape, - ui::{emoji_picker, Layer, Shortcut, VectorArt, VectorShape, Video}, + ui::{emoji_picker, Button, Layer, ShapeVertex, Shortcut, VectorArt, VectorShape, Video}, util::{i18n::I18nBabelFish, spawn_thread}, }; @@ -40,6 +41,9 @@ mod menu; //mod settings; pub mod test; +macro_rules! i { ($($arg:tt)*) => { info!(target: "app::schema", $($arg)*); } } +macro_rules! e { ($($arg:tt)*) => { error!(target: "app::schema", $($arg)*); } } + const COLOR_SCHEME: ColorScheme = ColorScheme::DarkMode; //const COLOR_SCHEME: ColorScheme = ColorScheme::PaperLight; @@ -411,6 +415,87 @@ pub async fn make(app: &App, window: SceneNodePtr, i18n_fish: &I18nBabelFish) { let net3_node = node.setup(|me| VectorArt::new(me, shape, app.render_api.clone())).await; netlayer_node.link(net3_node); + // netstat-klik icon (visual feedback when reconnect button is clicked) + let klik_color = [0., 0.5, 1., 1.]; // Blue + let node = create_vector_art("netstat_klik"); + let prop = node.get_property("rect").unwrap(); + prop.set_f32(atom, Role::App, 0, 0.).unwrap(); + prop.set_f32(atom, Role::App, 1, 0.).unwrap(); + prop.set_f32(atom, Role::App, 2, NETSTATUS_ICON_SIZE).unwrap(); + prop.set_f32(atom, Role::App, 3, NETSTATUS_ICON_SIZE).unwrap(); + node.set_property_bool(atom, Role::App, "is_visible", false).unwrap(); + // Above other icons + node.set_property_u32(atom, Role::App, "z_index", 1).unwrap(); + let mut shape = VectorShape::new(); + shape.add_filled_box( + expr::const_f32(0.), + expr::const_f32(0.), + expr::const_f32(NETSTATUS_ICON_SIZE), + expr::const_f32(NETSTATUS_ICON_SIZE), + klik_color, + ); + let netstat_klik_node = + node.setup(|me| VectorArt::new(me, shape, app.render_api.clone())).await; + netlayer_node.link(netstat_klik_node.clone()); + + // Reconnect Button (overlaid on netstatus icons) + let node = create_button("reconnect_btn"); + node.set_property_bool(atom, Role::App, "is_active", true).unwrap(); + let prop = node.get_property("rect").unwrap(); + prop.set_f32(atom, Role::App, 0, 0.).unwrap(); + prop.set_f32(atom, Role::App, 1, 0.).unwrap(); + prop.set_f32(atom, Role::App, 2, NETSTATUS_ICON_SIZE).unwrap(); + prop.set_f32(atom, Role::App, 3, NETSTATUS_ICON_SIZE).unwrap(); + + let sg_root = app.sg_root.clone(); + let render_api = app.render_api.clone(); + let (slot, recvr) = Slot::new("reconnect_clicked"); + node.register("click", slot).unwrap(); + let reconnect_task = app.ex.spawn(async move { + while let Ok(_) = recvr.recv().await { + i!("Reconnect button clicked"); + + // Show netstat-klik icon + let netstat_klik = + sg_root.lookup_node("/window/content/netstatus_layer/netstat_klik").unwrap(); + + { + let atom = &mut render_api.make_guard(gfxtag!("netstat_klik_show")); + if let Err(e) = netstat_klik.set_property_bool(atom, Role::App, "is_visible", true) + { + e!("Failed to show netstat_klik: {e}"); + } + } + + // Trigger reconnect + match sg_root.lookup_node("/plugin/darkirc") { + Some(darkirc) => { + if let Err(e) = darkirc.call_method("reconnect", vec![]).await { + e!("Failed to trigger reconnect: {e}"); + } + } + None => { + e!("DarkIrc plugin has not been loaded"); + } + } + + msleep(200).await; + + // Hide netstat-klik icon + { + let atom = &mut render_api.make_guard(gfxtag!("netstat_klik_hide")); + if let Err(e) = netstat_klik.set_property_bool(atom, Role::App, "is_visible", false) + { + e!("Failed to hide netstat_klik: {e}"); + } + } + } + }); + app.tasks.lock().unwrap().push(reconnect_task); + + let node = node.setup(Button::new).await; + netlayer_node.link(node); + // Navbar Settings Button /* diff --git a/bin/app/src/plugin/darkirc.rs b/bin/app/src/plugin/darkirc.rs index f32a08725..80dfc11ea 100644 --- a/bin/app/src/plugin/darkirc.rs +++ b/bin/app/src/plugin/darkirc.rs @@ -517,6 +517,33 @@ impl DarkIrc { self_.settings.update_p2p_settings(&mut write_guard); } + async fn process_reconnect(me: &Weak, sub: &MethodCallSub) -> bool { + let Ok(method_call) = sub.receive().await else { + d!("Reconnect method closed"); + return false + }; + + t!("method called: reconnect({method_call:?})"); + + let Some(self_) = me.upgrade() else { + e!("DarkIrc destroyed before reconnect completed"); + return false + }; + + i!("Manual P2P reconnection triggered"); + self_.p2p.clone().stop().await; + + while let Err(err) = self_.p2p.clone().start().await { + e!("Failed to start P2P network: {err}!"); + e!("Retrying in {P2P_RETRY_TIME} secs"); + sleep(P2P_RETRY_TIME).await; + } + + i!("P2P reconnection completed"); + + true + } + async fn start(self: Arc, sg_root: SceneNodePtr, ex: ExecutorPtr) { i!("Registering EventGraph P2P protocol"); let event_graph_ = Arc::clone(&self.event_graph); @@ -537,6 +564,13 @@ impl DarkIrc { let send_method_task = ex.spawn(async move { while Self::process_send(&me2, &method_sub).await {} }); + let reconnect_method_sub = node.subscribe_method_call("reconnect").unwrap(); + let me2 = me.clone(); + let reconnect_method_task = + ex.spawn( + async move { while Self::process_reconnect(&me2, &reconnect_method_sub).await {} }, + ); + let mut on_modify = OnModify::new(ex.clone(), self.node.clone(), me.clone()); async fn save_nick(self_: Arc, _batch: BatchGuardPtr) { let _ = std::fs::write(nick_filename(), self_.nick.get()); @@ -583,7 +617,8 @@ impl DarkIrc { } }); - let mut tasks = vec![send_method_task, ev_task, dag_task, start_task, stop_task]; + let mut tasks = + vec![send_method_task, reconnect_method_task, ev_task, dag_task, start_task, stop_task]; tasks.append(&mut on_modify.tasks); self.tasks.set(tasks).unwrap(); }