app: make connect status indicator clickable- doing so will force a reconnect

This commit is contained in:
jkds
2026-01-07 04:56:46 +01:00
parent 1f8445bbf4
commit 9f716a283c
2 changed files with 123 additions and 3 deletions

View File

@@ -16,6 +16,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
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
/*

View File

@@ -517,6 +517,33 @@ impl DarkIrc {
self_.settings.update_p2p_settings(&mut write_guard);
}
async fn process_reconnect(me: &Weak<Self>, 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<Self>, 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<DarkIrc>, _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();
}