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();
}