Просмотр исходного кода

wallet: immediately show the message when sent but in grey until the server sends it back again

darkfi 1 год назад
Родитель
Сommit
ab13cfae9e

+ 1 - 0
bin/darkwallet/Cargo.toml

@@ -44,6 +44,7 @@ semver = "1.0.23"
 chrono = "0.4.38"
 async-gen = "0.2"
 async-trait = "0.1.82"
+blake3 = "1.5.4"
 
 [patch.crates-io]
 freetype-rs = { git = "https://github.com/narodnik/freetype-rs" }

+ 12 - 0
bin/darkwallet/src/app/node.rs

@@ -312,5 +312,17 @@ pub fn create_chatview(name: &str) -> SceneNode {
     )
     .unwrap();
 
+    node.add_method(
+        "insert_unconf_line",
+        vec![
+            ("timestamp", "Timestamp", CallArgType::Uint64),
+            ("id", "Message ID", CallArgType::Hash),
+            ("nick", "Nickname", CallArgType::Str),
+            ("text", "Text", CallArgType::Str),
+        ],
+        None,
+    )
+    .unwrap();
+
     node
 }

+ 35 - 17
bin/darkwallet/src/darkirc2.rs

@@ -52,6 +52,7 @@ use url::Url;
 use crate::{
     prop::{PropertyBool, PropertyFloat32, PropertyStr, Role},
     scene::{SceneNodePtr, Slot},
+    ui::chatview::MessageId,
 };
 
 #[cfg(target_os = "android")]
@@ -78,6 +79,15 @@ impl Privmsg {
     pub fn new(channel: String, nick: String, msg: String) -> Self {
         Self { channel, nick, msg }
     }
+
+    pub fn msg_id(&self, timest: u64) -> MessageId {
+        let mut hasher = blake3::Hasher::new();
+        timest.encode(&mut hasher).unwrap();
+        self.channel.encode(&mut hasher).unwrap();
+        self.nick.encode(&mut hasher).unwrap();
+        self.msg.encode(&mut hasher).unwrap();
+        MessageId(hasher.finalize().into())
+    }
 }
 
 pub type LocalDarkIRCPtr = Arc<LocalDarkIRC>;
@@ -303,19 +313,6 @@ impl LocalDarkIRC {
 
         let ev = event_graph::Event::decode_async(stream).await?;
 
-        let genesis_timestamp = self.evgr.current_genesis.read().await.clone().timestamp;
-        let ev_id = ev.id();
-        if self.evgr.dag.contains_key(ev_id.as_bytes()).unwrap() ||
-            !ev.validate(&self.evgr.dag, genesis_timestamp, self.evgr.days_rotation, None)
-                .await?
-        {
-            error!(target: "darkirc", "Event is invalid! {ev:?}");
-            return Ok(())
-        }
-
-        debug!(target: "darkirc", "got {ev:?}");
-        self.evgr.dag_insert(&[ev.clone()]).await.unwrap();
-
         let privmsg: Privmsg = match deserialize_async_partial(ev.content()).await {
             Ok((v, _)) => v,
             Err(e) => {
@@ -330,7 +327,20 @@ impl LocalDarkIRC {
         }
         debug!(target: "darkirc", "Recv privmsg: <{timest}> {privmsg:?}");
 
+        let genesis_timestamp = self.evgr.current_genesis.read().await.clone().timestamp;
+        let ev_id = ev.id();
+        if self.evgr.dag.contains_key(ev_id.as_bytes()).unwrap() ||
+            !ev.validate(&self.evgr.dag, genesis_timestamp, self.evgr.days_rotation, None)
+                .await?
+        {
+            error!(target: "darkirc", "Event is invalid! {ev:?}");
+            return Ok(())
+        }
+
+        self.evgr.dag_insert(&[ev.clone()]).await.unwrap();
+
         if privmsg.channel != CHANNEL {
+            //debug!(target: "darkirc", "{} != {CHANNEL}", privmsg.channel);
             return Ok(())
         }
 
@@ -343,7 +353,7 @@ impl LocalDarkIRC {
 
         let mut arg_data = vec![];
         timest.encode_async(&mut arg_data).await.unwrap();
-        ev.id().as_bytes().encode_async(&mut arg_data).await.unwrap();
+        privmsg.msg_id(timest).encode_async(&mut arg_data).await.unwrap();
         privmsg.nick.encode_async(&mut arg_data).await.unwrap();
         privmsg.msg.encode_async(&mut arg_data).await.unwrap();
 
@@ -360,10 +370,18 @@ impl LocalDarkIRC {
         self.chatview_scroll.set(0.);
 
         // Send text to channel
-        let timestamp = UNIX_EPOCH.elapsed().unwrap().as_millis() as u64;
-        debug!(target: "darkirc", "Sending privmsg: <{timestamp}> {text}");
+        let timest = UNIX_EPOCH.elapsed().unwrap().as_millis() as u64;
+        debug!(target: "darkirc", "Sending privmsg: <{timest}> {text}");
         let msg = Privmsg::new(CHANNEL.to_string(), "anon".to_string(), text);
 
-        self.send_sender.send((timestamp, msg)).await.unwrap();
+        let mut arg_data = vec![];
+        timest.encode_async(&mut arg_data).await.unwrap();
+        msg.msg_id(timest).encode_async(&mut arg_data).await.unwrap();
+        msg.nick.encode_async(&mut arg_data).await.unwrap();
+        msg.msg.encode_async(&mut arg_data).await.unwrap();
+
+        self.send_sender.send((timest, msg)).await.unwrap();
+
+        self.chatview_node.call_method("insert_unconf_line", arg_data).await.unwrap();
     }
 }

+ 72 - 4
bin/darkwallet/src/ui/chatview/mod.rs

@@ -82,8 +82,8 @@ pub struct ChatMsg {
 
 type Timestamp = u64;
 
-#[derive(Clone, SerialEncodable, SerialDecodable)]
-struct MessageId([u8; 32]);
+#[derive(Clone, SerialEncodable, SerialDecodable, PartialEq)]
+pub struct MessageId(pub [u8; 32]);
 
 impl std::fmt::Display for MessageId {
     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
@@ -231,6 +231,12 @@ impl ChatView {
                 while Self::process_insert_line_method(&me2, &method_sub).await {}
             });
 
+            let method_sub = node_ref.subscribe_method_call("insert_unconf_line").unwrap();
+            let me2 = me.clone();
+            let insert_unconf_line_method_task = ex.spawn(async move {
+                while Self::process_insert_unconf_line_method(&me2, &method_sub).await {}
+            });
+
             let me2 = me.clone();
             let motion_cv = Arc::new(CondVar::new());
             let cv = motion_cv.clone();
@@ -284,7 +290,7 @@ impl ChatView {
             on_modify.when_change(rect.prop(), redraw);
             on_modify.when_change(debug.prop(), redraw);
 
-            let mut tasks = vec![insert_line_method_task, motion_task, bgload_task];
+            let mut tasks = vec![insert_line_method_task, insert_unconf_line_method_task, motion_task, bgload_task];
             tasks.append(&mut on_modify.tasks);
 
             Self {
@@ -369,6 +375,37 @@ impl ChatView {
         self_.handle_insert_line(timestamp, msg_id, nick, text).await;
         true
     }
+    async fn process_insert_unconf_line_method(me: &Weak<Self>, sub: &MethodCallSub) -> bool {
+        let Ok(method_call) = sub.receive().await else {
+            debug!(target: "ui::chatview", "Event relayer closed");
+            return false
+        };
+
+        //debug!(target: "ui::chatview", "method called: insert_line({method_call:?})");
+        assert!(method_call.send_res.is_none());
+
+        fn decode_data(data: &[u8]) -> std::io::Result<(Timestamp, MessageId, String, String)> {
+            let mut cur = Cursor::new(&data);
+            let timestamp = Timestamp::decode(&mut cur)?;
+            let msg_id = MessageId::decode(&mut cur)?;
+            let nick = String::decode(&mut cur)?;
+            let text = String::decode(&mut cur)?;
+            Ok((timestamp, msg_id, nick, text))
+        }
+
+        let Ok((timestamp, msg_id, nick, text)) = decode_data(&method_call.data) else {
+            error!(target: "ui::chatview", "insert_unconf_line() method invalid arg data");
+            return true
+        };
+
+        let Some(self_) = me.upgrade() else {
+            // Should not happen
+            panic!("self destroyed before touch_task was stopped!");
+        };
+
+        self_.handle_insert_unconf_line(timestamp, msg_id, nick, text).await;
+        true
+    }
 
     /// Mark line as selected
     async fn select_line(&self, mut y: f32) {
@@ -466,7 +503,38 @@ impl ChatView {
 
         // Add message to page
         let mut msgbuf = self.msgbuf.lock().await;
-        msgbuf.insert_privmsg(timest, msg_id, nick, text).await;
+        if msgbuf.mark_confirmed(&msg_id) {
+            // Message already exists. Which means it must be an unconfirmed sent message.
+            // Mark it as confirmed.
+            debug!(target: "ui::chatview", "Mark sent message as confirmed");
+        } else {
+            // Insert the privmsg since it doesn't already exist
+            if msgbuf.insert_privmsg(timest, msg_id, nick, text).await.is_none() {
+                // Not visible so no need to redraw
+                return
+            }
+        }
+
+        self.redraw_cached(&mut msgbuf).await;
+        self.bgload_cv.notify();
+    }
+    async fn handle_insert_unconf_line(
+        &self,
+        timest: Timestamp,
+        msg_id: MessageId,
+        nick: String,
+        text: String,
+    ) {
+        debug!(target: "ui::chatview", "handle_insert_unconf_line({timest}, {msg_id}, {nick}, {text})");
+
+        // We don't add unconfirmed lines to the db. Maybe we should?
+
+        // Add message to page
+        let mut msgbuf = self.msgbuf.lock().await;
+        let Some(privmsg) = msgbuf.insert_privmsg(timest, msg_id, nick, text).await else {
+            return
+        };
+        privmsg.confirmed = false;
         self.redraw_cached(&mut msgbuf).await;
         self.bgload_cv.notify();
     }

+ 46 - 4
bin/darkwallet/src/ui/chatview/page.rs

@@ -47,6 +47,8 @@ use crate::{
 const PAGE_SIZE: usize = 10;
 const PRELOAD_PAGES: usize = 10;
 
+const UNCONF_COLOR: [f32; 4] = [0.4, 0.4, 0.4, 1.];
+
 fn is_whitespace(s: &str) -> bool {
     s.chars().all(char::is_whitespace)
 }
@@ -61,6 +63,7 @@ pub struct PrivMessage {
     id: MessageId,
     nick: String,
     text: String,
+    pub confirmed: bool,
 
     is_selected: bool,
 
@@ -108,6 +111,7 @@ impl PrivMessage {
             id,
             nick,
             text,
+            confirmed: true,
             is_selected: false,
             time_glyphs,
             unwrapped_glyphs,
@@ -257,7 +261,7 @@ impl PrivMessage {
 
             let color = match section {
                 0 => nick_color,
-                _ => text_color,
+                _ => if self.confirmed { text_color } else { UNCONF_COLOR },
             };
 
             //if debug_render {
@@ -577,6 +581,19 @@ impl Message {
             Self::Date(_) => {}
         }
     }
+
+    fn get_privmsg(&self) -> Option<&PrivMessage> {
+        match self {
+            Message::Priv(msg) => Some(msg),
+            _ => None
+        }
+    }
+    fn get_privmsg_mut(&mut self) -> Option<&mut PrivMessage> {
+        match self {
+            Message::Priv(msg) => Some(msg),
+            _ => None
+        }
+    }
 }
 
 fn select_nick_color(nick: &str, nick_colors: &[Color]) -> Color {
@@ -756,13 +773,36 @@ impl MessageBuffer {
         height
     }
 
+    fn find_privmsg_mut(&mut self, msg_id: &MessageId) -> Option<&mut PrivMessage> {
+        for (idx, msg) in enumerate_mut(&mut self.msgs) {
+            let Some(privmsg) = msg.get_privmsg_mut() else { continue };
+            if privmsg.id == *msg_id {
+                return Some(privmsg)
+            }
+        }
+        None
+    }
+    pub fn mark_confirmed(&mut self, msg_id: &MessageId) -> bool {
+        let Some(privmsg) = self.find_privmsg_mut(msg_id) else {
+            return false
+        };
+
+        assert_eq!(privmsg.confirmed, false);
+        privmsg.confirmed = true;
+        if let Some(mesh) = privmsg.clear_mesh() {
+            self.freed.add_mesh(mesh);
+        }
+
+        return true
+    }
+
     pub async fn insert_privmsg(
         &mut self,
         timest: Timestamp,
         msg_id: MessageId,
         nick: String,
         text: String,
-    ) {
+    ) -> Option<&mut PrivMessage> {
         //debug!(target: "ui::chatview", "MessageBuffer::insert_privmsg()");
         let font_size = self.font_size.get();
         let timestamp_font_size = self.timestamp_font_size.get();
@@ -785,8 +825,9 @@ impl MessageBuffer {
         .await;
 
         if self.msgs.is_empty() {
+            let msg_idx = self.msgs.len();
             self.msgs.push(msg);
-            return
+            return self.msgs.last_mut().unwrap().get_privmsg_mut();
         }
 
         // We only add lines inside pages.
@@ -794,7 +835,7 @@ impl MessageBuffer {
         // When a line is before the first page, it will get preloaded as a new page.
         let oldest_timest = self.oldest_timestamp().unwrap();
         if timest < oldest_timest {
-            return;
+            return None;
         }
 
         // Timestamps go from most recent backwards
@@ -816,6 +857,7 @@ impl MessageBuffer {
         };
 
         self.msgs.insert(idx, msg);
+        return self.msgs[idx].get_privmsg_mut();
     }
 
     pub async fn push_privmsg(