cob: Move comment reactions to comment

To simplify the code, we move comment reactions to the comment struct
itself.
This commit is contained in:
Alexis Sellier 2023-06-26 23:56:44 +02:00
parent 8b010c27a2
commit e4ab523669
No known key found for this signature in database
3 changed files with 25 additions and 34 deletions

View File

@ -11,7 +11,7 @@ use radicle::cob::patch::Merge;
use radicle::cob::patch::Review; use radicle::cob::patch::Review;
use radicle::cob::patch::{Patch, PatchId}; use radicle::cob::patch::{Patch, PatchId};
use radicle::cob::thread; use radicle::cob::thread;
use radicle::cob::thread::{CommentId, Thread}; use radicle::cob::thread::CommentId;
use radicle::cob::{ActorId, Author, Reaction, Timestamp}; use radicle::cob::{ActorId, Author, Reaction, Timestamp};
use radicle::git::RefString; use radicle::git::RefString;
use radicle::node::AliasStore; use radicle::node::AliasStore;
@ -105,7 +105,7 @@ pub(crate) fn issue(id: IssueId, issue: Issue, aliases: &impl AliasStore) -> Val
"assignees": issue.assigned().collect::<Vec<_>>(), "assignees": issue.assigned().collect::<Vec<_>>(),
"discussion": issue "discussion": issue
.comments() .comments()
.map(|(id, comment)| Comment::new(id, comment, issue.thread(), aliases)) .map(|(id, comment)| Comment::new(id, comment, aliases))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
"tags": issue.tags().collect::<Vec<_>>(), "tags": issue.tags().collect::<Vec<_>>(),
}) })
@ -136,7 +136,7 @@ pub(crate) fn patch(
"oid": rev.head(), "oid": rev.head(),
"refs": get_refs(repo, patch.author().id(), &rev.head()).unwrap_or(vec![]), "refs": get_refs(repo, patch.author().id(), &rev.head()).unwrap_or(vec![]),
"discussions": rev.discussion().comments() "discussions": rev.discussion().comments()
.map(|(id, comment)| Comment::new(id, comment, rev.discussion(), aliases)) .map(|(id, comment)| Comment::new(id, comment, aliases))
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
"timestamp": rev.timestamp(), "timestamp": rev.timestamp(),
"reviews": rev.reviews().map(|(nid, _review)| review(nid, aliases.alias(nid), _review)).collect::<Vec<_>>(), "reviews": rev.reviews().map(|(nid, _review)| review(nid, aliases.alias(nid), _review)).collect::<Vec<_>>(),
@ -245,18 +245,13 @@ struct Comment<'a> {
} }
impl<'a> Comment<'a> { impl<'a> Comment<'a> {
fn new( fn new(id: &'a CommentId, comment: &'a thread::Comment, aliases: &impl AliasStore) -> Self {
id: &'a CommentId,
comment: &'a thread::Comment,
thread: &'a Thread,
aliases: &impl AliasStore,
) -> Self {
let comment_author = Author::new(comment.author()); let comment_author = Author::new(comment.author());
Self { Self {
id: *id, id: *id,
author: author(&comment_author, aliases.alias(comment_author.id())), author: author(&comment_author, aliases.alias(comment_author.id())),
body: comment.body(), body: comment.body(),
reactions: thread.reactions(id).collect::<Vec<_>>(), reactions: comment.reactions().collect::<Vec<_>>(),
timestamp: comment.timestamp(), timestamp: comment.timestamp(),
reply_to: comment.reply_to(), reply_to: comment.reply_to(),
} }

View File

@ -836,7 +836,7 @@ mod test {
let id = issue.id; let id = issue.id;
let issue = issues.get(&id).unwrap().unwrap(); 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); assert_eq!(r, &reaction);

View File

@ -61,6 +61,8 @@ pub struct Comment {
author: ActorId, author: ActorId,
/// The comment body. /// The comment body.
edits: GMap<Lamport, Max<Edit>>, edits: GMap<Lamport, Max<Edit>>,
/// Reactions to this comment.
reactions: LWWSet<(ActorId, Reaction)>,
/// Comment this is a reply to. /// Comment this is a reply to.
/// Should always be set, except for the root comment. /// Should always be set, except for the root comment.
reply_to: Option<CommentId>, reply_to: Option<CommentId>,
@ -78,6 +80,7 @@ impl Comment {
Self { Self {
author, author,
reactions: LWWSet::default(),
edits: GMap::singleton(Lamport::initial(), Max::from(edit)), edits: GMap::singleton(Lamport::initial(), Max::from(edit)),
reply_to, reply_to,
} }
@ -124,6 +127,11 @@ impl Comment {
pub fn edit(&mut self, clock: Lamport, body: String, timestamp: Timestamp) { pub fn edit(&mut self, clock: Lamport, body: String, timestamp: Timestamp) {
self.edits.insert(clock, Edit { body, timestamp }.into()) self.edits.insert(clock, Edit { body, timestamp }.into())
} }
/// Comment reactions.
pub fn reactions(&self) -> impl Iterator<Item = (&ActorId, &Reaction)> {
self.reactions.iter().map(|(a, r)| (a, r))
}
} }
impl PartialOrd for Comment { impl PartialOrd for Comment {
@ -175,16 +183,13 @@ impl From<Action> for nonempty::NonEmpty<Action> {
pub struct Thread { pub struct Thread {
/// The comments under the thread. /// The comments under the thread.
comments: GMap<CommentId, Redactable<Comment>>, comments: GMap<CommentId, Redactable<Comment>>,
/// Reactions to changes.
reactions: GMap<CommentId, LWWSet<(ActorId, Reaction), Lamport>>,
/// Comment timeline. /// Comment timeline.
timeline: GSet<(Lamport, EntryId)>, timeline: GSet<(Lamport, CommentId)>,
} }
impl Semilattice for Thread { impl Semilattice for Thread {
fn merge(&mut self, other: Self) { fn merge(&mut self, other: Self) {
self.comments.merge(other.comments); self.comments.merge(other.comments);
self.reactions.merge(other.reactions);
self.timeline.merge(other.timeline); self.timeline.merge(other.timeline);
} }
} }
@ -193,7 +198,6 @@ impl Thread {
pub fn new(id: CommentId, comment: Comment) -> Self { pub fn new(id: CommentId, comment: Comment) -> Self {
Self { Self {
comments: GMap::singleton(id, Redactable::Present(comment)), comments: GMap::singleton(id, Redactable::Present(comment)),
reactions: GMap::default(),
timeline: GSet::default(), timeline: GSet::default(),
} }
} }
@ -244,17 +248,6 @@ impl Thread {
}) })
} }
pub fn reactions<'a>(
&'a self,
to: &'a CommentId,
) -> impl Iterator<Item = (&ActorId, &Reaction)> {
self.reactions
.get(to)
.into_iter()
.flat_map(move |rs| rs.iter())
.map(|(a, r)| (a, r))
}
pub fn comments(&self) -> impl DoubleEndedIterator<Item = (&CommentId, &Comment)> + '_ { pub fn comments(&self) -> impl DoubleEndedIterator<Item = (&CommentId, &Comment)> + '_ {
self.timeline.iter().filter_map(|(_, id)| { self.timeline.iter().filter_map(|(_, id)| {
self.comments self.comments
@ -335,14 +328,17 @@ impl cob::store::FromHistory for Thread {
active, active,
} => { } => {
let key = (op.author, reaction); let key = (op.author, reaction);
let reactions = if active { if let Some(redactable) = self.comments.get_mut(&to) {
LWWSet::singleton(key, op.clock) if let Redactable::Present(comment) = redactable {
if active {
comment.reactions.insert(key, op.clock);
} else {
comment.reactions.remove(key, op.clock);
}
}
} else { } else {
let mut set = LWWSet::default(); return Err(Error::Missing(id));
set.remove(key, op.clock); }
set
};
self.reactions.insert(to, reactions);
} }
} }
} }