loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at forgejo 181 lines 5.5 kB view raw
1// Copyright 2022 The Gitea Authors. All rights reserved. 2// SPDX-License-Identifier: MIT 3 4package issues 5 6import ( 7 "context" 8 9 "forgejo.org/models/db" 10 user_model "forgejo.org/models/user" 11 "forgejo.org/modules/markup" 12 "forgejo.org/modules/markup/markdown" 13 14 "xorm.io/builder" 15) 16 17// CodeConversation contains the comment of a given review 18type CodeConversation []*Comment 19 20// CodeConversationsAtLine contains the conversations for a given line 21type CodeConversationsAtLine map[int64][]CodeConversation 22 23// CodeConversationsAtLineAndTreePath contains the conversations for a given TreePath and line 24type CodeConversationsAtLineAndTreePath map[string]CodeConversationsAtLine 25 26func newCodeConversationsAtLineAndTreePath(comments []*Comment) CodeConversationsAtLineAndTreePath { 27 tree := make(CodeConversationsAtLineAndTreePath) 28 for _, comment := range comments { 29 tree.insertComment(comment) 30 } 31 return tree 32} 33 34func (tree CodeConversationsAtLineAndTreePath) insertComment(comment *Comment) { 35 // attempt to append comment to existing conversations (i.e. list of comments belonging to the same review) 36 for i, conversation := range tree[comment.TreePath][comment.Line] { 37 if conversation[0].ReviewID == comment.ReviewID { 38 tree[comment.TreePath][comment.Line][i] = append(conversation, comment) 39 return 40 } 41 } 42 43 // no previous conversation was found at this line, create it 44 if tree[comment.TreePath] == nil { 45 tree[comment.TreePath] = make(map[int64][]CodeConversation) 46 } 47 48 tree[comment.TreePath][comment.Line] = append(tree[comment.TreePath][comment.Line], CodeConversation{comment}) 49} 50 51// FetchCodeConversations will return a 2d-map: ["Path"]["Line"] = List of CodeConversation (one per review) for this line 52func FetchCodeConversations(ctx context.Context, issue *Issue, doer *user_model.User, showOutdatedComments bool) (CodeConversationsAtLineAndTreePath, error) { 53 opts := FindCommentsOptions{ 54 Type: CommentTypeCode, 55 IssueID: issue.ID, 56 } 57 comments, err := findCodeComments(ctx, opts, issue, doer, nil, showOutdatedComments) 58 if err != nil { 59 return nil, err 60 } 61 62 return newCodeConversationsAtLineAndTreePath(comments), nil 63} 64 65// CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS 66type CodeComments map[string]map[int64][]*Comment 67 68func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) { 69 pathToLineToComment := make(CodeComments) 70 if review == nil { 71 review = &Review{ID: 0} 72 } 73 opts := FindCommentsOptions{ 74 Type: CommentTypeCode, 75 IssueID: issue.ID, 76 ReviewID: review.ID, 77 } 78 79 comments, err := findCodeComments(ctx, opts, issue, doer, review, showOutdatedComments) 80 if err != nil { 81 return nil, err 82 } 83 84 for _, comment := range comments { 85 if pathToLineToComment[comment.TreePath] == nil { 86 pathToLineToComment[comment.TreePath] = make(map[int64][]*Comment) 87 } 88 pathToLineToComment[comment.TreePath][comment.Line] = append(pathToLineToComment[comment.TreePath][comment.Line], comment) 89 } 90 return pathToLineToComment, nil 91} 92 93func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) (CommentList, error) { 94 var comments CommentList 95 if review == nil { 96 review = &Review{ID: 0} 97 } 98 conds := opts.ToConds() 99 100 if !showOutdatedComments && review.ID == 0 { 101 conds = conds.And(builder.Eq{"invalidated": false}) 102 } 103 104 e := db.GetEngine(ctx) 105 if err := e.Where(conds). 106 Asc("comment.created_unix"). 107 Asc("comment.id"). 108 Find(&comments); err != nil { 109 return nil, err 110 } 111 112 if err := issue.LoadRepo(ctx); err != nil { 113 return nil, err 114 } 115 116 if err := comments.LoadPosters(ctx); err != nil { 117 return nil, err 118 } 119 120 if err := comments.LoadAttachments(ctx); err != nil { 121 return nil, err 122 } 123 124 // Find all reviews by ReviewID 125 reviews := make(map[int64]*Review) 126 ids := make([]int64, 0, len(comments)) 127 for _, comment := range comments { 128 if comment.ReviewID != 0 { 129 ids = append(ids, comment.ReviewID) 130 } 131 } 132 if err := e.In("id", ids).Find(&reviews); err != nil { 133 return nil, err 134 } 135 136 n := 0 137 for _, comment := range comments { 138 if re, ok := reviews[comment.ReviewID]; ok && re != nil { 139 // If the review is pending only the author can see the comments (except if the review is set) 140 if review.ID == 0 && re.Type == ReviewTypePending && 141 (doer == nil || doer.ID != re.ReviewerID) { 142 continue 143 } 144 comment.Review = re 145 } 146 comments[n] = comment 147 n++ 148 149 if err := comment.LoadResolveDoer(ctx); err != nil { 150 return nil, err 151 } 152 153 if err := comment.LoadReactions(ctx, issue.Repo); err != nil { 154 return nil, err 155 } 156 157 var err error 158 if comment.RenderedContent, err = markdown.RenderString(&markup.RenderContext{ 159 Ctx: ctx, 160 Links: markup.Links{ 161 Base: issue.Repo.Link(), 162 }, 163 Metas: issue.Repo.ComposeMetas(ctx), 164 }, comment.Content); err != nil { 165 return nil, err 166 } 167 } 168 return comments[:n], nil 169} 170 171// FetchCodeConversation fetches the code conversation of a given comment (same review, treePath and line number) 172func FetchCodeConversation(ctx context.Context, comment *Comment, doer *user_model.User) (CommentList, error) { 173 opts := FindCommentsOptions{ 174 Type: CommentTypeCode, 175 IssueID: comment.IssueID, 176 ReviewID: comment.ReviewID, 177 TreePath: comment.TreePath, 178 Line: comment.Line, 179 } 180 return findCodeComments(ctx, opts, comment.Issue, doer, nil, true) 181}