diff --git a/radicle-httpd/src/api/json.rs b/radicle-httpd/src/api/json.rs index 1a2dbe62..4cb5c395 100644 --- a/radicle-httpd/src/api/json.rs +++ b/radicle-httpd/src/api/json.rs @@ -11,7 +11,7 @@ use radicle::cob::patch::Merge; use radicle::cob::patch::Review; use radicle::cob::patch::{Patch, PatchId}; use radicle::cob::thread; -use radicle::cob::thread::{CommentId, Thread}; +use radicle::cob::thread::CommentId; use radicle::cob::{ActorId, Author, Reaction, Timestamp}; use radicle::git::RefString; use radicle::node::AliasStore; @@ -105,7 +105,7 @@ pub(crate) fn issue(id: IssueId, issue: Issue, aliases: &impl AliasStore) -> Val "assignees": issue.assigned().collect::>(), "discussion": issue .comments() - .map(|(id, comment)| Comment::new(id, comment, issue.thread(), aliases)) + .map(|(id, comment)| Comment::new(id, comment, aliases)) .collect::>(), "tags": issue.tags().collect::>(), }) @@ -136,7 +136,7 @@ pub(crate) fn patch( "oid": rev.head(), "refs": get_refs(repo, patch.author().id(), &rev.head()).unwrap_or(vec![]), "discussions": rev.discussion().comments() - .map(|(id, comment)| Comment::new(id, comment, rev.discussion(), aliases)) + .map(|(id, comment)| Comment::new(id, comment, aliases)) .collect::>(), "timestamp": rev.timestamp(), "reviews": rev.reviews().map(|(nid, _review)| review(nid, aliases.alias(nid), _review)).collect::>(), @@ -245,18 +245,13 @@ struct Comment<'a> { } impl<'a> Comment<'a> { - fn new( - id: &'a CommentId, - comment: &'a thread::Comment, - thread: &'a Thread, - aliases: &impl AliasStore, - ) -> Self { + fn new(id: &'a CommentId, comment: &'a thread::Comment, aliases: &impl AliasStore) -> Self { let comment_author = Author::new(comment.author()); Self { id: *id, author: author(&comment_author, aliases.alias(comment_author.id())), body: comment.body(), - reactions: thread.reactions(id).collect::>(), + reactions: comment.reactions().collect::>(), timestamp: comment.timestamp(), reply_to: comment.reply_to(), } diff --git a/radicle/src/cob/issue.rs b/radicle/src/cob/issue.rs index 11c016c9..8c53bbba 100644 --- a/radicle/src/cob/issue.rs +++ b/radicle/src/cob/issue.rs @@ -836,7 +836,7 @@ mod test { let id = issue.id; let issue = issues.get(&id).unwrap().unwrap(); - let (_, r) = issue.reactions(&comment).next().unwrap(); + let (_, r) = issue.comment(&comment).unwrap().reactions().next().unwrap(); assert_eq!(r, &reaction); diff --git a/radicle/src/cob/thread.rs b/radicle/src/cob/thread.rs index 319f465b..81bcf494 100644 --- a/radicle/src/cob/thread.rs +++ b/radicle/src/cob/thread.rs @@ -61,6 +61,8 @@ pub struct Comment { author: ActorId, /// The comment body. edits: GMap>, + /// Reactions to this comment. + reactions: LWWSet<(ActorId, Reaction)>, /// Comment this is a reply to. /// Should always be set, except for the root comment. reply_to: Option, @@ -78,6 +80,7 @@ impl Comment { Self { author, + reactions: LWWSet::default(), edits: GMap::singleton(Lamport::initial(), Max::from(edit)), reply_to, } @@ -124,6 +127,11 @@ impl Comment { pub fn edit(&mut self, clock: Lamport, body: String, timestamp: Timestamp) { self.edits.insert(clock, Edit { body, timestamp }.into()) } + + /// Comment reactions. + pub fn reactions(&self) -> impl Iterator { + self.reactions.iter().map(|(a, r)| (a, r)) + } } impl PartialOrd for Comment { @@ -175,16 +183,13 @@ impl From for nonempty::NonEmpty { pub struct Thread { /// The comments under the thread. comments: GMap>, - /// Reactions to changes. - reactions: GMap>, /// Comment timeline. - timeline: GSet<(Lamport, EntryId)>, + timeline: GSet<(Lamport, CommentId)>, } impl Semilattice for Thread { fn merge(&mut self, other: Self) { self.comments.merge(other.comments); - self.reactions.merge(other.reactions); self.timeline.merge(other.timeline); } } @@ -193,7 +198,6 @@ impl Thread { pub fn new(id: CommentId, comment: Comment) -> Self { Self { comments: GMap::singleton(id, Redactable::Present(comment)), - reactions: GMap::default(), timeline: GSet::default(), } } @@ -244,17 +248,6 @@ impl Thread { }) } - pub fn reactions<'a>( - &'a self, - to: &'a CommentId, - ) -> impl Iterator { - self.reactions - .get(to) - .into_iter() - .flat_map(move |rs| rs.iter()) - .map(|(a, r)| (a, r)) - } - pub fn comments(&self) -> impl DoubleEndedIterator + '_ { self.timeline.iter().filter_map(|(_, id)| { self.comments @@ -335,14 +328,17 @@ impl cob::store::FromHistory for Thread { active, } => { let key = (op.author, reaction); - let reactions = if active { - LWWSet::singleton(key, op.clock) + if let Some(redactable) = self.comments.get_mut(&to) { + if let Redactable::Present(comment) = redactable { + if active { + comment.reactions.insert(key, op.clock); + } else { + comment.reactions.remove(key, op.clock); + } + } } else { - let mut set = LWWSet::default(); - set.remove(key, op.clock); - set - }; - self.reactions.insert(to, reactions); + return Err(Error::Missing(id)); + } } } }