[fractal] Track relations in MessageList



commit 6b10fa3ecc99b99dcb93b07720e40d73a21102aa
Author: Kai A. Hiller <V02460 gmail com>
Date:   Sat Jan 9 22:31:13 2021 +0100

    Track relations in MessageList

 fractal-gtk/src/model/message.rs      |  9 +++++
 fractal-gtk/src/model/message_list.rs | 65 +++++++++++++++++++++++++++++++++++
 2 files changed, 74 insertions(+)
---
diff --git a/fractal-gtk/src/model/message.rs b/fractal-gtk/src/model/message.rs
index 1bfa753b..b06e6468 100644
--- a/fractal-gtk/src/model/message.rs
+++ b/fractal-gtk/src/model/message.rs
@@ -433,6 +433,15 @@ impl Message {
         }
     }
 
+    /// Returns all event IDs this message relates to.
+    pub fn relations(&self) -> Vec<EventId> {
+        vec![self.in_reply_to.as_ref(), self.replace.as_ref()]
+            .into_iter()
+            .flat_map(|r| r.into_iter())
+            .cloned()
+            .collect()
+    }
+
     /// Generates an unique transaction id for this message
     /// The txn_id is generated using the md5sum of a concatenation of the message room id, the
     /// message body and the date.
diff --git a/fractal-gtk/src/model/message_list.rs b/fractal-gtk/src/model/message_list.rs
index ac476449..7f944324 100644
--- a/fractal-gtk/src/model/message_list.rs
+++ b/fractal-gtk/src/model/message_list.rs
@@ -1,5 +1,7 @@
 use crate::model::message::Message;
 use matrix_sdk::identifiers::EventId;
+use std::collections::{HashMap, HashSet};
+use std::iter;
 use std::iter::FromIterator;
 use std::slice::Iter;
 
@@ -7,6 +9,7 @@ use std::slice::Iter;
 #[derive(Debug, Default, Clone)]
 pub struct MessageList {
     messages: Vec<Message>,
+    relating_messages: HashMap<EventId, HashSet<EventId>>,
 }
 
 impl MessageList {
@@ -34,6 +37,11 @@ impl MessageList {
     /// Inserts the message at the correct position replacing its older version.
     pub fn add(&mut self, msg: Message) {
         assert!(msg.id.is_some());
+        let id = msg.id.clone().unwrap();
+
+        if msg.redacted {
+            self.remove_relations(&id);
+        }
 
         // Deduplication only happens for messages with the same date, so we have
         // to manually go through the message list and remove possible duplicates.
@@ -45,6 +53,10 @@ impl MessageList {
         // brute-force-fix this by searching all messages for duplicates.
         self.messages.retain(|m| m.id != msg.id);
 
+        if !msg.redacted {
+            self.populate_relations(&msg);
+        }
+
         match self.messages.binary_search(&msg) {
             Ok(idx) => self.messages[idx] = msg,
             Err(idx) => self.messages.insert(idx, msg),
@@ -52,6 +64,59 @@ impl MessageList {
         // TODO: Use is_sorted (https://github.com/rust-lang/rust/issues/53485)
         // debug_assert!(self.messages.is_sorted());
     }
+
+    /// Updates records of those relations the message is involved in.
+    ///
+    /// This updates both, relating and related, messages.
+    fn populate_relations(&mut self, msg: &Message) {
+        // Other messages relate to `msg`
+        let id = msg.id.as_ref().cloned().unwrap();
+        let relating = self.find_and_get_relating(&id);
+        self.relating_messages.insert(id.clone(), relating);
+
+        // `msg` relates to other messages
+        if let Some(replace_id) = &msg.replace {
+            self.update_relating(replace_id, iter::once(&id).cloned().collect());
+        }
+    }
+
+    /// Remove all outgoing relations for the given event.
+    fn remove_relations(&mut self, event_id: &EventId) {
+        let msg = unwrap_or_unit_return!(self.get(event_id));
+        let relations = msg.relations();
+
+        let event_sets = self.relating_messages.iter_mut().filter_map(|(id, rs)| {
+            if relations.contains(&id) {
+                Some(rs)
+            } else {
+                None
+            }
+        });
+
+        for set in event_sets {
+            set.retain(|id| id != event_id);
+        }
+    }
+
+    /// Records new messages relating to the message with the given id.
+    ///
+    /// This does not remove other messages relating to the given id.
+    fn update_relating(&mut self, id: &EventId, relating: HashSet<EventId>) {
+        let new_relating = match self.relating_messages.remove(id) {
+            Some(old_relating) => old_relating.union(&relating).cloned().collect(),
+            None => relating,
+        };
+        self.relating_messages.insert(id.clone(), new_relating);
+    }
+
+    /// Finds and returns all messages relating to the given one.
+    fn find_and_get_relating(&self, id: &EventId) -> HashSet<EventId> {
+        self.messages
+            .iter()
+            .filter(|m| m.replace.as_ref() == Some(id) || m.in_reply_to.as_ref() == Some(id))
+            .map(|m| m.id.clone().unwrap())
+            .collect()
+    }
 }
 
 impl FromIterator<Message> for MessageList {


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]