2023-01-18 00:03:44 +03:00
// Copyright 2022 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package issues
import (
"context"
"code.gitea.io/gitea/models/db"
user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/markup"
"code.gitea.io/gitea/modules/markup/markdown"
"xorm.io/builder"
)
2024-02-16 15:16:11 +03:00
// 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 )
}
2023-01-18 00:03:44 +03:00
2024-02-16 15:16:11 +03:00
tree [ comment . TreePath ] [ comment . Line ] = append ( tree [ comment . TreePath ] [ comment . Line ] , CodeConversation { comment } )
2023-01-18 00:03:44 +03:00
}
2024-02-16 15:16:11 +03:00
// 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
func fetchCodeCommentsByReview ( ctx context . Context , issue * Issue , doer * user_model . User , review * Review , showOutdatedComments bool ) ( CodeComments , error ) {
2023-01-18 00:03:44 +03:00
pathToLineToComment := make ( CodeComments )
if review == nil {
review = & Review { ID : 0 }
}
opts := FindCommentsOptions {
Type : CommentTypeCode ,
IssueID : issue . ID ,
ReviewID : review . ID ,
}
2024-02-16 15:16:11 +03:00
comments , err := findCodeComments ( ctx , opts , issue , doer , review , showOutdatedComments )
2023-01-18 00:03:44 +03:00
if err != nil {
return nil , err
}
for _ , comment := range comments {
if pathToLineToComment [ comment . TreePath ] == nil {
pathToLineToComment [ comment . TreePath ] = make ( map [ int64 ] [ ] * Comment )
}
pathToLineToComment [ comment . TreePath ] [ comment . Line ] = append ( pathToLineToComment [ comment . TreePath ] [ comment . Line ] , comment )
}
return pathToLineToComment , nil
}
2024-03-12 10:23:44 +03:00
func findCodeComments ( ctx context . Context , opts FindCommentsOptions , issue * Issue , doer * user_model . User , review * Review , showOutdatedComments bool ) ( CommentList , error ) {
2023-05-21 15:48:28 +03:00
var comments CommentList
2023-01-18 00:03:44 +03:00
if review == nil {
review = & Review { ID : 0 }
}
conds := opts . ToConds ( )
2023-06-21 19:08:12 +03:00
if ! showOutdatedComments && review . ID == 0 {
2023-01-18 00:03:44 +03:00
conds = conds . And ( builder . Eq { "invalidated" : false } )
}
2023-06-21 19:08:12 +03:00
2023-01-18 00:03:44 +03:00
e := db . GetEngine ( ctx )
if err := e . Where ( conds ) .
Asc ( "comment.created_unix" ) .
Asc ( "comment.id" ) .
Find ( & comments ) ; err != nil {
return nil , err
}
if err := issue . LoadRepo ( ctx ) ; err != nil {
return nil , err
}
2023-05-21 15:48:28 +03:00
if err := comments . LoadPosters ( ctx ) ; err != nil {
2023-01-18 00:03:44 +03:00
return nil , err
}
2024-03-27 07:44:26 +03:00
if err := comments . LoadAttachments ( ctx ) ; err != nil {
return nil , err
}
2023-01-18 00:03:44 +03:00
// Find all reviews by ReviewID
reviews := make ( map [ int64 ] * Review )
ids := make ( [ ] int64 , 0 , len ( comments ) )
for _ , comment := range comments {
if comment . ReviewID != 0 {
ids = append ( ids , comment . ReviewID )
}
}
if err := e . In ( "id" , ids ) . Find ( & reviews ) ; err != nil {
return nil , err
}
n := 0
for _ , comment := range comments {
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 &&
2024-02-16 15:16:11 +03:00
( doer == nil || doer . ID != re . ReviewerID ) {
2023-01-18 00:03:44 +03:00
continue
}
comment . Review = re
}
comments [ n ] = comment
n ++
2023-09-29 15:12:54 +03:00
if err := comment . LoadResolveDoer ( ctx ) ; err != nil {
2023-01-18 00:03:44 +03:00
return nil , err
}
2023-09-29 15:12:54 +03:00
if err := comment . LoadReactions ( ctx , issue . Repo ) ; err != nil {
2023-01-18 00:03:44 +03:00
return nil , err
}
var err error
if comment . RenderedContent , err = markdown . RenderString ( & markup . RenderContext {
2024-01-15 11:49:24 +03:00
Ctx : ctx ,
Links : markup . Links {
Base : issue . Repo . Link ( ) ,
} ,
Metas : issue . Repo . ComposeMetas ( ctx ) ,
2023-01-18 00:03:44 +03:00
} , comment . Content ) ; err != nil {
return nil , err
}
}
return comments [ : n ] , nil
}
2024-02-16 15:16:11 +03:00
// FetchCodeConversation fetches the code conversation of a given comment (same review, treePath and line number)
2024-03-12 10:23:44 +03:00
func FetchCodeConversation ( ctx context . Context , comment * Comment , doer * user_model . User ) ( CommentList , error ) {
2023-01-18 00:03:44 +03:00
opts := FindCommentsOptions {
Type : CommentTypeCode ,
2024-02-16 15:16:11 +03:00
IssueID : comment . IssueID ,
ReviewID : comment . ReviewID ,
TreePath : comment . TreePath ,
Line : comment . Line ,
2023-01-18 00:03:44 +03:00
}
2024-02-16 15:16:11 +03:00
return findCodeComments ( ctx , opts , comment . Issue , doer , nil , true )
2023-01-18 00:03:44 +03:00
}