[BUG] split code conversations in diff tab (#2306)

Follow-up of #2282 and #2296 (which tried to address #2278)

One of the issue with the previous PR is that when a conversation on the Files tab was marked as "resolved", it would fetch all the comments for that line (even the outdated ones, which should not be shown on this page - except when explicitly activated).

To properly fix this, I have changed `FetchCodeCommentsByLine` to `FetchCodeConversation`. Its role is to fetch all comments related to a given (review, path, line) and reverted my changes in the template (which were based on a misunderstanding).

Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2306
Reviewed-by: Earl Warren <earl-warren@noreply.codeberg.org>
Reviewed-by: Gusted <gusted@noreply.codeberg.org>
Co-authored-by: oliverpool <git@olivier.pfad.fr>
Co-committed-by: oliverpool <git@olivier.pfad.fr>
This commit is contained in:
oliverpool 2024-02-16 12:16:11 +00:00 committed by oliverpool
parent 1e364cc21f
commit 0fc61c8836
11 changed files with 348 additions and 144 deletions

View file

@ -14,15 +14,58 @@ import (
"xorm.io/builder"
)
// CodeConversation contains the comment of a given review
type CodeConversation []*Comment
// CodeConversationsAtLine contains the conversations for a given line
type CodeConversationsAtLine map[int64][]CodeConversation
// CodeConversationsAtLineAndTreePath contains the conversations for a given TreePath and line
type CodeConversationsAtLineAndTreePath map[string]CodeConversationsAtLine
func newCodeConversationsAtLineAndTreePath(comments []*Comment) CodeConversationsAtLineAndTreePath {
tree := make(CodeConversationsAtLineAndTreePath)
for _, comment := range comments {
tree.insertComment(comment)
}
return tree
}
func (tree CodeConversationsAtLineAndTreePath) insertComment(comment *Comment) {
// attempt to append comment to existing conversations (i.e. list of comments belonging to the same review)
for i, conversation := range tree[comment.TreePath][comment.Line] {
if conversation[0].ReviewID == comment.ReviewID {
tree[comment.TreePath][comment.Line][i] = append(conversation, comment)
return
}
}
// no previous conversation was found at this line, create it
if tree[comment.TreePath] == nil {
tree[comment.TreePath] = make(map[int64][]CodeConversation)
}
tree[comment.TreePath][comment.Line] = append(tree[comment.TreePath][comment.Line], CodeConversation{comment})
}
// FetchCodeConversations will return a 2d-map: ["Path"]["Line"] = List of CodeConversation (one per review) for this line
func FetchCodeConversations(ctx context.Context, issue *Issue, doer *user_model.User, showOutdatedComments bool) (CodeConversationsAtLineAndTreePath, error) {
opts := FindCommentsOptions{
Type: CommentTypeCode,
IssueID: issue.ID,
}
comments, err := findCodeComments(ctx, opts, issue, doer, nil, showOutdatedComments)
if err != nil {
return nil, err
}
return newCodeConversationsAtLineAndTreePath(comments), nil
}
// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS
type CodeComments map[string]map[int64][]*Comment
// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line
func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool) (CodeComments, error) {
return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments)
}
func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) {
func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) {
pathToLineToComment := make(CodeComments)
if review == nil {
review = &Review{ID: 0}
@ -33,7 +76,7 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u
ReviewID: review.ID,
}
comments, err := findCodeComments(ctx, opts, issue, currentUser, review, showOutdatedComments)
comments, err := findCodeComments(ctx, opts, issue, doer, review, showOutdatedComments)
if err != nil {
return nil, err
}
@ -47,7 +90,7 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u
return pathToLineToComment, nil
}
func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) {
func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) {
var comments CommentList
if review == nil {
review = &Review{ID: 0}
@ -91,7 +134,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
if re, ok := reviews[comment.ReviewID]; ok && re != nil {
// If the review is pending only the author can see the comments (except if the review is set)
if review.ID == 0 && re.Type == ReviewTypePending &&
(currentUser == nil || currentUser.ID != re.ReviewerID) {
(doer == nil || doer.ID != re.ReviewerID) {
continue
}
comment.Review = re
@ -121,13 +164,14 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
return comments[:n], nil
}
// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number
func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) ([]*Comment, error) {
// FetchCodeConversation fetches the code conversation of a given comment (same review, treePath and line number)
func FetchCodeConversation(ctx context.Context, comment *Comment, doer *user_model.User) ([]*Comment, error) {
opts := FindCommentsOptions{
Type: CommentTypeCode,
IssueID: issue.ID,
TreePath: treePath,
Line: line,
IssueID: comment.IssueID,
ReviewID: comment.ReviewID,
TreePath: comment.TreePath,
Line: comment.Line,
}
return findCodeComments(ctx, opts, issue, currentUser, nil, showOutdatedComments)
return findCodeComments(ctx, opts, comment.Issue, doer, nil, true)
}