Browse Source

app: add /setting:chat.is_enabled property which persists whether darkirc chat is enabled or disabled

darkfi 2 days ago
parent
commit
e183adf170

+ 7 - 11
bin/app/src/app/schema/mod.rs

@@ -763,6 +763,11 @@ pub async fn make(
         })
         })
         .await;
         .await;
     let toggle_text = PropertyStr::wrap(&node, Role::App, "text", 0).unwrap();
     let toggle_text = PropertyStr::wrap(&node, Role::App, "text", 0).unwrap();
+    let setting_node = app.sg_root.lookup_node("/setting").unwrap();
+    let chat_is_enabled = setting_node.get_property("chat.is_enabled").unwrap();
+    if !chat_is_enabled.get_bool(0).unwrap() {
+        toggle_text.set(atom, "off");
+    }
     overlay_node.link(node);
     overlay_node.link(node);
 
 
     // Create the p2p toggle button
     // Create the p2p toggle button
@@ -777,10 +782,9 @@ pub async fn make(
     let (slot, recvr) = Slot::new("toggle_p2p");
     let (slot, recvr) = Slot::new("toggle_p2p");
     node.register("click", slot).unwrap();
     node.register("click", slot).unwrap();
     let redraw = app.redraw_trigger.clone();
     let redraw = app.redraw_trigger.clone();
-    let sg_root = app.sg_root.clone();
     let listen_click = ex.spawn(async move {
     let listen_click = ex.spawn(async move {
         while let Ok(_) = recvr.recv().await {
         while let Ok(_) = recvr.recv().await {
-            let is_enabled = toggle_text.get() == "on";
+            let is_enabled = chat_is_enabled.get_bool(0).unwrap();
             i!("toggle_p2p from {is_enabled} to {}", !is_enabled);
             i!("toggle_p2p from {is_enabled} to {}", !is_enabled);
             let atom = &mut redraw.make_guard(gfxtag!("toggle_p2p"));
             let atom = &mut redraw.make_guard(gfxtag!("toggle_p2p"));
             if is_enabled {
             if is_enabled {
@@ -788,15 +792,7 @@ pub async fn make(
             } else {
             } else {
                 toggle_text.set(atom, "on");
                 toggle_text.set(atom, "on");
             }
             }
-            let Some(darkirc) = sg_root.lookup_node("/plugin/darkirc") else {
-                e!("DarkIrc plugin has not been loaded");
-                continue
-            };
-            if is_enabled {
-                darkirc.call_method("stop", vec![]).await.unwrap();
-            } else {
-                darkirc.call_method("start", vec![]).await.unwrap();
-            }
+            chat_is_enabled.set_bool(atom, Role::User, 0, !is_enabled).unwrap();
         }
         }
     });
     });
     overlay_node.push_task(listen_click);
     overlay_node.push_task(listen_click);

+ 0 - 2
bin/app/src/main.rs

@@ -796,8 +796,6 @@ pub fn create_darkirc(name: &str) -> SceneNode {
     )
     )
     .unwrap();
     .unwrap();
 
 
-    node.add_method("start", vec![], None).unwrap();
-    node.add_method("stop", vec![], None).unwrap();
     node.add_method("rescan", vec![("channel", "Channel", CallArgType::Str)], None).unwrap();
     node.add_method("rescan", vec![("channel", "Channel", CallArgType::Str)], None).unwrap();
 
 
     node
     node

+ 93 - 122
bin/app/src/plugin/darkirc.rs

@@ -59,7 +59,7 @@ use crate::{
     },
     },
     db::AppDbPtr,
     db::AppDbPtr,
     error::{Error, Result},
     error::{Error, Result},
-    prop::{BatchGuardPtr, PropertyAtomicGuard, PropertyPtr, PropertyStr, Role},
+    prop::{BatchGuardPtr, PropertyAtomicGuard, PropertyBool, PropertyPtr, PropertyStr, Role},
     scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak, Slot},
     scene::{MethodCallSub, Pimpl, SceneNodePtr, SceneNodeWeak, Slot},
     ui::{
     ui::{
         chatview::{MessageId, Timestamp},
         chatview::{MessageId, Timestamp},
@@ -171,6 +171,7 @@ pub struct DarkIrc {
     event_graph: EventGraphPtr,
     event_graph: EventGraphPtr,
     seen_msgs: SyncMutex<SeenMessages>,
     seen_msgs: SyncMutex<SeenMessages>,
     nick: PropertyStr,
     nick: PropertyStr,
+    chat_is_enabled: PropertyBool,
     pub channels: RwLock<HashMap<String, IrcChannel>>,
     pub channels: RwLock<HashMap<String, IrcChannel>>,
     pub contacts: RwLock<HashMap<String, IrcContact>>,
     pub contacts: RwLock<HashMap<String, IrcContact>>,
     app_db: AppDbPtr,
     app_db: AppDbPtr,
@@ -188,6 +189,9 @@ impl DarkIrc {
     ) -> Result<Pimpl> {
     ) -> Result<Pimpl> {
         let node_ref = &node.upgrade().unwrap();
         let node_ref = &node.upgrade().unwrap();
         let nick = PropertyStr::wrap(node_ref, Role::Internal, "nick", 0).unwrap();
         let nick = PropertyStr::wrap(node_ref, Role::Internal, "nick", 0).unwrap();
+        let setting_node = sg_root.lookup_node("/setting").unwrap();
+        let chat_is_enabled =
+            PropertyBool::wrap(&setting_node, Role::User, "chat.is_enabled", 0).unwrap();
 
 
         i!("Starting DarkIRC backend");
         i!("Starting DarkIRC backend");
 
 
@@ -296,6 +300,7 @@ impl DarkIrc {
 
 
             seen_msgs: SyncMutex::new(SeenMessages::new()),
             seen_msgs: SyncMutex::new(SeenMessages::new()),
             nick,
             nick,
+            chat_is_enabled,
 
 
             channels: RwLock::new(HashMap::new()),
             channels: RwLock::new(HashMap::new()),
             contacts: RwLock::new(HashMap::new()),
             contacts: RwLock::new(HashMap::new()),
@@ -315,15 +320,6 @@ impl DarkIrc {
     }
     }
 
 
     async fn dag_sync(self: Arc<Self>, channel_sub: Subscription<DarkFiResult<ChannelPtr>>) {
     async fn dag_sync(self: Arc<Self>, channel_sub: Subscription<DarkFiResult<ChannelPtr>>) {
-        i!("Starting p2p network");
-        while let Err(err) = self.p2p.clone().start().await {
-            // This usually means we cannot listen on the inbound ports
-            e!("Failed to start p2p network: {err}!");
-            e!("Usually this means there is another process listening on the same ports.");
-            e!("Trying again in {P2P_RETRY_TIME} secs");
-            sleep(P2P_RETRY_TIME).await;
-        }
-
         i!("Waiting for some P2P connections...");
         i!("Waiting for some P2P connections...");
 
 
         let mut sync_attempt = 0;
         let mut sync_attempt = 0;
@@ -331,75 +327,67 @@ impl DarkIrc {
         let fast_mode = false;
         let fast_mode = false;
         let mut newest_synced = false;
         let mut newest_synced = false;
         loop {
         loop {
-            if self.p2p.is_connected() {
-                let peers_count = self.p2p.peers_count();
-                self.notify_connect(peers_count, self.event_graph.is_synced()).await;
-
-                // Wait until we have enough connections
-                if peers_count < SYNC_MIN_PEERS {
-                    i!("Connected to {peers_count} peers. Waiting for more connections.");
-                    let conn_sub = self.p2p.hosts().subscribe_channel().await;
-                    loop {
-                        if let Err(err) = conn_sub.receive().await {
-                            w!("Error while waiting for new connections: {err}");
-                            continue
-                        }
+            let peers_count = self.p2p.peers_count();
+            self.notify_connect(peers_count, self.event_graph.is_synced()).await;
 
 
-                        if self.p2p.peers_count() >= SYNC_MIN_PEERS {
-                            break
-                        }
+            // Wait until we have enough connections
+            if peers_count < SYNC_MIN_PEERS {
+                i!("Connected to {peers_count} peers. Waiting for more connections.");
+                let conn_sub = self.p2p.hosts().subscribe_channel().await;
+                loop {
+                    if let Err(err) = conn_sub.receive().await {
+                        w!("Error while waiting for new connections: {err}");
+                        continue
                     }
                     }
 
 
-                    drop(conn_sub);
-                    continue
+                    if self.p2p.peers_count() >= SYNC_MIN_PEERS {
+                        break
+                    }
                 }
                 }
+            }
 
 
-                i!("Got peer connection");
-                sync_attempt += 1;
-                // Cool off periodically
-                if sync_attempt > COOLOFF_SYNC_ATTEMPTS {
-                    i!("Wasn't able to sync yet. Cooling off for {COOLOFF_SLEEP_TIME} then will try again.");
-                    sleep(COOLOFF_SLEEP_TIME).await;
-                    sync_attempt = 0;
-                }
+            i!("Got peer connection");
+            sync_attempt += 1;
+            // Cool off periodically
+            if sync_attempt > COOLOFF_SYNC_ATTEMPTS {
+                i!("Wasn't able to sync yet. Cooling off for {COOLOFF_SLEEP_TIME} then will try again.");
+                sleep(COOLOFF_SLEEP_TIME).await;
+                sync_attempt = 0;
+            }
 
 
-                i!("Syncing static DAG");
-                match self.event_graph.static_sync().await {
-                    Ok(()) => {
-                        i!("Static synced successfully");
-                        // log_memory("after static sync");
-                    }
-                    Err(e) => {
-                        e!("Failed syncing static graph: {e}");
-                        self.p2p.stop().await;
-                        break
-                    }
+            i!("Syncing static DAG");
+            match self.event_graph.static_sync().await {
+                Ok(()) => {
+                    i!("Static synced successfully");
+                    // log_memory("after static sync");
                 }
                 }
-                // Sync only the newest DAG first. Older DAGs are caught up in
-                // the background by `catch_up_sync`, so the `synced` flag (and
-                // the `connect` notification) is reached after the first DAG
-                // instead of after all `DAGS_COUNT` of them.
-                let latest_ts = self.event_graph.current_genesis.read().await.header.timestamp;
-                i!("Syncing newest event DAG ({latest_ts}) (attempt #{sync_attempt})");
-                let sync_result = self.sync_dag_slot(latest_ts, fast_mode).await;
-                match sync_result {
-                    Ok(()) => {
-                        i!(
-                            "Newest event DAG synced successfully ({} mode)",
-                            if fast_mode { "fast" } else { "full" },
-                        );
-                        newest_synced = true;
-                        break
-                    }
-                    Err(e) => {
-                        // TODO: Maybe at this point we should prune or something?
-                        // TODO: Or maybe just tell the user to delete the DAG from FS.
-                        e!("Failed syncing newest DAG ({e}), retrying...");
-                    }
+                Err(e) => {
+                    e!("Failed syncing static graph: {e}");
+                    self.p2p.stop().await;
+                    break
+                }
+            }
+            // Sync only the newest DAG first. Older DAGs are caught up in
+            // the background by `catch_up_sync`, so the `synced` flag (and
+            // the `connect` notification) is reached after the first DAG
+            // instead of after all `DAGS_COUNT` of them.
+            let latest_ts = self.event_graph.current_genesis.read().await.header.timestamp;
+            i!("Syncing newest event DAG ({latest_ts}) (attempt #{sync_attempt})");
+            let sync_result = self.sync_dag_slot(latest_ts, fast_mode).await;
+            match sync_result {
+                Ok(()) => {
+                    i!(
+                        "Newest event DAG synced successfully ({} mode)",
+                        if fast_mode { "fast" } else { "full" },
+                    );
+                    newest_synced = true;
+                    break
+                }
+                Err(e) => {
+                    // TODO: Maybe at this point we should prune or something?
+                    // TODO: Or maybe just tell the user to delete the DAG from FS.
+                    e!("Failed syncing newest DAG ({e}), retrying...");
                 }
                 }
-            } else {
-                i!("Waiting for some P2P connections...");
-                sleep(COOLOFF_SLEEP_TIME).await;
             }
             }
         }
         }
 
 
@@ -514,9 +502,16 @@ impl DarkIrc {
     }
     }
 
 
     /// Send a notification when there's a change in number of peers or the DAG sync status
     /// Send a notification when there's a change in number of peers or the DAG sync status
+    ///
+    /// The node is only borrowed to grab the signal, so no strong node reference
+    /// is held across the trigger below. Otherwise a notify racing `SceneNode::setup`'s
+    /// strong_count assertion would panic the app.
     pub async fn notify_connect(&self, peers_count: usize, is_dag_synced: bool) {
     pub async fn notify_connect(&self, peers_count: usize, is_dag_synced: bool) {
-        let node = self.node.upgrade().unwrap();
-        node.trigger("connect", serialize(&(peers_count as u32, is_dag_synced))).await.unwrap();
+        let sig = {
+            let node = self.node.upgrade().unwrap();
+            node.get_signal("connect").unwrap()
+        };
+        sig.trigger(serialize(&(peers_count as u32, is_dag_synced))).await;
     }
     }
 
 
     /// Update the `outbound_peers` property with the outgoing connection slots addrs.
     /// Update the `outbound_peers` property with the outgoing connection slots addrs.
@@ -642,8 +637,11 @@ impl DarkIrc {
         nick.encode(&mut arg_data).unwrap();
         nick.encode(&mut arg_data).unwrap();
         msg.encode(&mut arg_data).unwrap();
         msg.encode(&mut arg_data).unwrap();
 
 
-        let node = self.node.upgrade().unwrap();
-        node.trigger("recv", arg_data).await.unwrap();
+        let sig = {
+            let node = self.node.upgrade().unwrap();
+            node.get_signal("recv").unwrap()
+        };
+        sig.trigger(arg_data).await;
     }
     }
 
 
     async fn process_send(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
     async fn process_send(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
@@ -856,25 +854,7 @@ impl DarkIrc {
         true
         true
     }
     }
 
 
-    async fn process_start(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
-        let Ok(method_call) = sub.receive().await else {
-            d!("Start method closed");
-            return false
-        };
-
-        t!("method called: start({method_call:?})");
-
-        let Some(self_) = me.upgrade() else {
-            e!("DarkIrc destroyed before start completed");
-            return false
-        };
-
-        self_.handle_start().await;
-
-        true
-    }
-
-    /// User requested to start the P2P network
+    /// `chat.is_enabled` was switched on
     async fn handle_start(&self) {
     async fn handle_start(&self) {
         i!("Manual P2P start triggered");
         i!("Manual P2P start triggered");
 
 
@@ -890,25 +870,7 @@ impl DarkIrc {
         i!("P2P start completed");
         i!("P2P start completed");
     }
     }
 
 
-    async fn process_stop(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
-        let Ok(method_call) = sub.receive().await else {
-            d!("Stop method closed");
-            return false
-        };
-
-        t!("method called: stop({method_call:?})");
-
-        let Some(self_) = me.upgrade() else {
-            e!("DarkIrc destroyed before stop completed");
-            return false
-        };
-
-        self_.handle_stop().await;
-
-        true
-    }
-
-    /// User requested to stop the P2P network
+    /// `chat.is_enabled` was switched off
     async fn handle_stop(&self) {
     async fn handle_stop(&self) {
         i!("Manual P2P stop triggered");
         i!("Manual P2P stop triggered");
         self.p2p.clone().stop().await;
         self.p2p.clone().stop().await;
@@ -956,15 +918,25 @@ impl DarkIrc {
         let send_method_task =
         let send_method_task =
             ex.spawn(async move { while Self::process_send(&me2, &method_sub).await {} });
             ex.spawn(async move { while Self::process_send(&me2, &method_sub).await {} });
 
 
-        let start_method_sub = node.subscribe_method_call("start").unwrap();
+        let chat_is_enabled = self.chat_is_enabled.clone();
+        let chat_is_enabled_sub = chat_is_enabled.prop().subscribe_modify();
         let me2 = me.clone();
         let me2 = me.clone();
-        let start_method_task =
-            ex.spawn(async move { while Self::process_start(&me2, &start_method_sub).await {} });
+        let setting_task = ex.spawn(async move {
+            if chat_is_enabled.get() {
+                let Some(self_) = me2.upgrade() else { return };
+                self_.handle_start().await;
+            }
 
 
-        let stop_method_sub = node.subscribe_method_call("stop").unwrap();
-        let me2 = me.clone();
-        let stop_method_task =
-            ex.spawn(async move { while Self::process_stop(&me2, &stop_method_sub).await {} });
+            while let Ok(_) = chat_is_enabled_sub.receive().await {
+                let Some(self_) = me2.upgrade() else { break };
+
+                if chat_is_enabled.get() {
+                    self_.handle_start().await;
+                } else {
+                    self_.handle_stop().await;
+                }
+            }
+        });
 
 
         let rescan_method_sub = node.subscribe_method_call("rescan").unwrap();
         let rescan_method_sub = node.subscribe_method_call("rescan").unwrap();
         let me2 = me.clone();
         let me2 = me.clone();
@@ -1033,8 +1005,7 @@ impl DarkIrc {
 
 
         let mut tasks = vec![
         let mut tasks = vec![
             send_method_task,
             send_method_task,
-            start_method_task,
-            stop_method_task,
+            setting_task,
             rescan_method_task,
             rescan_method_task,
             ev_task,
             ev_task,
             dag_task,
             dag_task,

+ 1 - 0
bin/app/src/prop/mod.rs

@@ -74,6 +74,7 @@ pub enum PropertySubType {
     Pixel = 2,
     Pixel = 2,
     ResourceId = 3,
     ResourceId = 3,
     Locale = 4,
     Locale = 4,
+    Flag = 5
 }
 }
 
 
 #[derive(Debug, Copy, Clone, PartialEq)]
 #[derive(Debug, Copy, Clone, PartialEq)]

+ 15 - 11
bin/app/src/scene.rs

@@ -391,17 +391,7 @@ impl SceneNode {
     pub async fn trigger(&self, sig_name: &str, data: Vec<u8>) -> Result<()> {
     pub async fn trigger(&self, sig_name: &str, data: Vec<u8>) -> Result<()> {
         t!("trigger({sig_name}, {data:?}) [node={self:?}]");
         t!("trigger({sig_name}, {data:?}) [node={self:?}]");
         let sig = self.get_signal(sig_name).ok_or(Error::SignalNotFound)?;
         let sig = self.get_signal(sig_name).ok_or(Error::SignalNotFound)?;
-        let futures = FuturesUnordered::new();
-        let slots: Vec<_> = sig.slots.read().unwrap().values().cloned().collect();
-        // TODO: autoremove failed slots
-        for slot in slots {
-            t!("  triggering {}", slot.name);
-            // Trigger the slot
-            let data = data.clone();
-            futures.push(async move { slot.notify.send(data).await.is_ok() });
-        }
-        let success: Vec<_> = futures.collect().await;
-        t!("trigger success: {success:?}");
+        sig.trigger(data).await;
         Ok(())
         Ok(())
     }
     }
 
 
@@ -539,6 +529,20 @@ impl Signal {
         let slots = self.slots.read().unwrap();
         let slots = self.slots.read().unwrap();
         slots.iter().map(|(id, slot)| (*id, slot.clone())).collect()
         slots.iter().map(|(id, slot)| (*id, slot.clone())).collect()
     }
     }
+
+    pub async fn trigger(&self, data: Vec<u8>) {
+        let futures = FuturesUnordered::new();
+        let slots: Vec<_> = self.slots.read().unwrap().values().cloned().collect();
+        // TODO: autoremove failed slots
+        for slot in slots {
+            t!("  triggering {}", slot.name);
+            // Trigger the slot
+            let data = data.clone();
+            futures.push(async move { slot.notify.send(data).await.is_ok() });
+        }
+        let success: Vec<_> = futures.collect().await;
+        t!("trigger success: {success:?}");
+    }
 }
 }
 
 
 #[derive(Clone, Debug)]
 #[derive(Clone, Debug)]

+ 3 - 3
bin/app/src/setting.rs

@@ -48,13 +48,13 @@ use crate::{
 ///
 ///
 /// In both cases modifying the setting should propagate the changes to that node.
 /// In both cases modifying the setting should propagate the changes to that node.
 ///
 ///
-/// Although the `/setting2` root has no knowledge of property paths underneath there
+/// Although the `/setting` root has no knowledge of property paths underneath there
 /// is a convention of using `foo.bar.baz` to namespace the settings.
 /// is a convention of using `foo.bar.baz` to namespace the settings.
 pub fn create_setting(name: &str) -> SceneNode {
 pub fn create_setting(name: &str) -> SceneNode {
     let mut node = SceneNode::new(name, SceneNodeType::Setting);
     let mut node = SceneNode::new(name, SceneNodeType::Setting);
 
 
-    // Example
-    let prop = Property::new("net.enable_tor", PropertyType::Bool, PropertySubType::Null);
+    let mut prop = Property::new("chat.is_enabled", PropertyType::Bool, PropertySubType::Flag);
+    prop.set_defaults_bool(vec![true]).unwrap();
     node.add_property(prop).unwrap();
     node.add_property(prop).unwrap();
 
 
     let mut prop = Property::new("win.scale", PropertyType::Float32, PropertySubType::Null);
     let mut prop = Property::new("win.scale", PropertyType::Float32, PropertySubType::Null);