Add a way to mark Conversation (code comment) resolved (#11037)
* Add a way to mark Conversation (code comment) resolved mark Conversation is a way to mark a Conversation is stale or be solved. when it's marked as stale, will be hided like stale. all Pull Request writer , Offical Reviewers and poster can add or remove Conversation resolved mark. Signed-off-by: a1012112796 <1012112796@qq.com> * fix lint * Apply suggestions from code review * Add ResolveDoer * fix ui Co-Authored-By: Lauris BH <lauris@nix.lv> Co-Authored-By: 6543 <6543@obermui.de> * change IsResolved to an function Add permission check in UpdateResolveConversation * Apply suggestions from code review * change return error for permisson check * add default message for deleted user * get issue message from comment * add migration for ``ResolveDoerID`` column another change: * block mark pending review as resolved because it's not necessary Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> * change button color * resolve button size * fix code style * remove unusefull code Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com> Co-authored-by: Lauris BH <lauris@nix.lv> Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: guillep2k <18600385+guillep2k@users.noreply.github.com>
This commit is contained in:
		
							parent
							
								
									38d5f88a81
								
							
						
					
					
						commit
						1b86f174ce
					
				
					 13 changed files with 301 additions and 9 deletions
				
			
		|  | @ -122,6 +122,8 @@ type Comment struct { | ||||||
| 	AssigneeID       int64 | 	AssigneeID       int64 | ||||||
| 	RemovedAssignee  bool | 	RemovedAssignee  bool | ||||||
| 	Assignee         *User `xorm:"-"` | 	Assignee         *User `xorm:"-"` | ||||||
|  | 	ResolveDoerID    int64 | ||||||
|  | 	ResolveDoer      *User `xorm:"-"` | ||||||
| 	OldTitle         string | 	OldTitle         string | ||||||
| 	NewTitle         string | 	NewTitle         string | ||||||
| 	OldRef           string | 	OldRef           string | ||||||
|  | @ -420,6 +422,26 @@ func (c *Comment) LoadAssigneeUser() error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // LoadResolveDoer if comment.Type is CommentTypeCode and ResolveDoerID not zero, then load resolveDoer
 | ||||||
|  | func (c *Comment) LoadResolveDoer() (err error) { | ||||||
|  | 	if c.ResolveDoerID == 0 || c.Type != CommentTypeCode { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	c.ResolveDoer, err = getUserByID(x, c.ResolveDoerID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if IsErrUserNotExist(err) { | ||||||
|  | 			c.ResolveDoer = NewGhostUser() | ||||||
|  | 			err = nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // IsResolved check if an code comment is resolved
 | ||||||
|  | func (c *Comment) IsResolved() bool { | ||||||
|  | 	return c.ResolveDoerID != 0 && c.Type == CommentTypeCode | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // LoadDepIssueDetails loads Dependent Issue Details
 | // LoadDepIssueDetails loads Dependent Issue Details
 | ||||||
| func (c *Comment) LoadDepIssueDetails() (err error) { | func (c *Comment) LoadDepIssueDetails() (err error) { | ||||||
| 	if c.DependentIssueID <= 0 || c.DependentIssue != nil { | 	if c.DependentIssueID <= 0 || c.DependentIssue != nil { | ||||||
|  | @ -943,7 +965,12 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review | ||||||
| 	if err := e.In("id", ids).Find(&reviews); err != nil { | 	if err := e.In("id", ids).Find(&reviews); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
| 	for _, comment := range comments { | 	for _, comment := range comments { | ||||||
|  | 		if err := comment.LoadResolveDoer(); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 		if re, ok := reviews[comment.ReviewID]; ok && re != nil { | 		if re, ok := reviews[comment.ReviewID]; ok && re != nil { | ||||||
| 			// If the review is pending only the author can see the comments (except the review is set)
 | 			// If the review is pending only the author can see the comments (except the review is set)
 | ||||||
| 			if review.ID == 0 { | 			if review.ID == 0 { | ||||||
|  |  | ||||||
|  | @ -208,6 +208,8 @@ var migrations = []Migration{ | ||||||
| 	NewMigration("Add CommitsAhead and CommitsBehind Column to PullRequest Table", addCommitDivergenceToPulls), | 	NewMigration("Add CommitsAhead and CommitsBehind Column to PullRequest Table", addCommitDivergenceToPulls), | ||||||
| 	// v137 -> v138
 | 	// v137 -> v138
 | ||||||
| 	NewMigration("Add Branch Protection Block Outdated Branch", addBlockOnOutdatedBranch), | 	NewMigration("Add Branch Protection Block Outdated Branch", addBlockOnOutdatedBranch), | ||||||
|  | 	// v138 -> v139
 | ||||||
|  | 	NewMigration("Add ResolveDoerID to Comment table", addResolveDoerIDCommentColumn), | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetCurrentDBVersion returns the current db version
 | // GetCurrentDBVersion returns the current db version
 | ||||||
|  |  | ||||||
							
								
								
									
										22
									
								
								models/migrations/v138.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								models/migrations/v138.go
									
									
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | // Copyright 2020 The Gitea Authors. All rights reserved.
 | ||||||
|  | // Use of this source code is governed by a MIT-style
 | ||||||
|  | // license that can be found in the LICENSE file.
 | ||||||
|  | 
 | ||||||
|  | package migrations | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"xorm.io/xorm" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func addResolveDoerIDCommentColumn(x *xorm.Engine) error { | ||||||
|  | 	type Comment struct { | ||||||
|  | 		ResolveDoerID int64 | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err := x.Sync2(new(Comment)); err != nil { | ||||||
|  | 		return fmt.Errorf("Sync2: %v", err) | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -5,6 +5,7 @@ | ||||||
| package models | package models | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/modules/timeutil" | 	"code.gitea.io/gitea/modules/timeutil" | ||||||
|  | @ -594,3 +595,62 @@ func RemoveRewiewRequest(issue *Issue, reviewer *User, doer *User) (comment *Com | ||||||
| 
 | 
 | ||||||
| 	return comment, sess.Commit() | 	return comment, sess.Commit() | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // MarkConversation Add or remove Conversation mark for a code comment
 | ||||||
|  | func MarkConversation(comment *Comment, doer *User, isResolve bool) (err error) { | ||||||
|  | 	if comment.Type != CommentTypeCode { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if isResolve { | ||||||
|  | 		if comment.ResolveDoerID != 0 { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if _, err = x.Exec("UPDATE `comment` SET resolve_doer_id=? WHERE id=?", doer.ID, comment.ID); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		if comment.ResolveDoerID == 0 { | ||||||
|  | 			return nil | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if _, err = x.Exec("UPDATE `comment` SET resolve_doer_id=? WHERE id=?", 0, comment.ID); err != nil { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // CanMarkConversation  Add or remove Conversation mark for a code comment permission check
 | ||||||
|  | // the PR writer , offfcial reviewer and poster can do it
 | ||||||
|  | func CanMarkConversation(issue *Issue, doer *User) (permResult bool, err error) { | ||||||
|  | 	if doer == nil || issue == nil { | ||||||
|  | 		return false, fmt.Errorf("issue or doer is nil") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if doer.ID != issue.PosterID { | ||||||
|  | 		if err = issue.LoadRepo(); err != nil { | ||||||
|  | 			return false, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		perm, err := GetUserRepoPermission(issue.Repo, doer) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return false, err | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		permResult = perm.CanAccess(AccessModeWrite, UnitTypePullRequests) | ||||||
|  | 		if !permResult { | ||||||
|  | 			if permResult, err = IsOfficialReviewer(issue, doer); err != nil { | ||||||
|  | 				return false, err | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		if !permResult { | ||||||
|  | 			return false, nil | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return true, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1067,6 +1067,11 @@ issues.review.review = Review | ||||||
| issues.review.reviewers = Reviewers | issues.review.reviewers = Reviewers | ||||||
| issues.review.show_outdated = Show outdated | issues.review.show_outdated = Show outdated | ||||||
| issues.review.hide_outdated = Hide outdated | issues.review.hide_outdated = Hide outdated | ||||||
|  | issues.review.show_resolved = Show resolved | ||||||
|  | issues.review.hide_resolved = Hide resolved | ||||||
|  | issues.review.resolve_conversation = Resolve conversation | ||||||
|  | issues.review.un_resolve_conversation = Unresolve conversation | ||||||
|  | issues.review.resolved_by = marked this conversation as resolved | ||||||
| issues.assignee.error = Not all assignees was added due to an unexpected error. | issues.assignee.error = Not all assignees was added due to an unexpected error. | ||||||
| 
 | 
 | ||||||
| pulls.desc = Enable pull requests and code reviews. | pulls.desc = Enable pull requests and code reviews. | ||||||
|  |  | ||||||
|  | @ -990,6 +990,11 @@ func ViewIssue(ctx *context.Context) { | ||||||
| 				ctx.ServerError("Review.LoadCodeComments", err) | 				ctx.ServerError("Review.LoadCodeComments", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			if err = comment.LoadResolveDoer(); err != nil { | ||||||
|  | 				ctx.ServerError("LoadResolveDoer", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -1033,6 +1038,11 @@ func ViewIssue(ctx *context.Context) { | ||||||
| 				ctx.ServerError("IsUserAllowedToMerge", err) | 				ctx.ServerError("IsUserAllowedToMerge", err) | ||||||
| 				return | 				return | ||||||
| 			} | 			} | ||||||
|  | 
 | ||||||
|  | 			if ctx.Data["CanMarkConversation"], err = models.CanMarkConversation(issue, ctx.User); err != nil { | ||||||
|  | 				ctx.ServerError("CanMarkConversation", err) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		prUnit, err := repo.GetUnit(models.UnitTypePullRequests) | 		prUnit, err := repo.GetUnit(models.UnitTypePullRequests) | ||||||
|  |  | ||||||
|  | @ -624,6 +624,13 @@ func ViewPullFiles(ctx *context.Context) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if ctx.IsSigned && ctx.User != nil { | ||||||
|  | 		if ctx.Data["CanMarkConversation"], err = models.CanMarkConversation(issue, ctx.User); err != nil { | ||||||
|  | 			ctx.ServerError("CanMarkConversation", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	setImageCompareContext(ctx, baseCommit, commit) | 	setImageCompareContext(ctx, baseCommit, commit) | ||||||
| 	setPathsCompareContext(ctx, baseCommit, commit, headTarget) | 	setPathsCompareContext(ctx, baseCommit, commit, headTarget) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -61,6 +61,53 @@ func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) { | ||||||
| 	ctx.Redirect(comment.HTMLURL()) | 	ctx.Redirect(comment.HTMLURL()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // UpdateResolveConversation add or remove an Conversation resolved mark
 | ||||||
|  | func UpdateResolveConversation(ctx *context.Context) { | ||||||
|  | 	action := ctx.Query("action") | ||||||
|  | 	commentID := ctx.QueryInt64("comment_id") | ||||||
|  | 
 | ||||||
|  | 	comment, err := models.GetCommentByID(commentID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("GetIssueByID", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if err = comment.LoadIssue(); err != nil { | ||||||
|  | 		ctx.ServerError("comment.LoadIssue", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var permResult bool | ||||||
|  | 	if permResult, err = models.CanMarkConversation(comment.Issue, ctx.User); err != nil { | ||||||
|  | 		ctx.ServerError("CanMarkConversation", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	if !permResult { | ||||||
|  | 		ctx.Error(403) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if !comment.Issue.IsPull { | ||||||
|  | 		ctx.Error(400) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if action == "Resolve" || action == "UnResolve" { | ||||||
|  | 		err = models.MarkConversation(comment, ctx.User, action == "Resolve") | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.ServerError("MarkConversation", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		ctx.Error(400) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	ctx.JSON(200, map[string]interface{}{ | ||||||
|  | 		"ok": true, | ||||||
|  | 	}) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
 | // SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
 | ||||||
| func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) { | func SubmitReview(ctx *context.Context, form auth.SubmitReviewForm) { | ||||||
| 	issue := GetActionIssue(ctx) | 	issue := GetActionIssue(ctx) | ||||||
|  |  | ||||||
|  | @ -739,6 +739,7 @@ func RegisterRoutes(m *macaron.Macaron) { | ||||||
| 			m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) | 			m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) | ||||||
| 			m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) | 			m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) | ||||||
| 			m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) | 			m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) | ||||||
|  | 			m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation) | ||||||
| 		}, context.RepoMustNotBeArchived()) | 		}, context.RepoMustNotBeArchived()) | ||||||
| 		m.Group("/comments/:id", func() { | 		m.Group("/comments/:id", func() { | ||||||
| 			m.Post("", repo.UpdateCommentContent) | 			m.Post("", repo.UpdateCommentContent) | ||||||
|  |  | ||||||
|  | @ -147,32 +147,79 @@ | ||||||
| 															{{end}} | 															{{end}} | ||||||
| 														</tr> | 														</tr> | ||||||
| 														{{if gt (len $line.Comments) 0}} | 														{{if gt (len $line.Comments) 0}} | ||||||
|  | 															{{$resolved := (index $line.Comments 0).IsResolved}} | ||||||
|  | 															{{$resolveDoer := (index $line.Comments 0).ResolveDoer}} | ||||||
|  | 															{{$isNotPending := (not (eq (index $line.Comments 0).Review.Type 0))}} | ||||||
| 															<tr class="add-code-comment"> | 															<tr class="add-code-comment"> | ||||||
| 																<td class="lines-num"></td> | 																<td class="lines-num"></td> | ||||||
| 																<td class="lines-type-marker"></td> | 																<td class="lines-type-marker"></td> | ||||||
| 																<td class="add-comment-left"> | 																<td class="add-comment-left"> | ||||||
|  | 																	{{if and $resolved  (eq $line.GetCommentSide "previous")}} | ||||||
|  | 																		<div class="ui top attached header"> | ||||||
|  | 																			<span class="ui grey text left"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span> | ||||||
|  | 																			<button id="show-outdated-{{(index $line.Comments 0).ID}}" data-comment="{{(index $line.Comments 0).ID}}" class="ui compact right labeled button show-outdated"> | ||||||
|  | 																				{{svg "octicon-unfold" 16}} | ||||||
|  | 																				{{$.i18n.Tr "repo.issues.review.show_resolved"}} | ||||||
|  | 																			</button> | ||||||
|  | 																			<button id="hide-outdated-{{(index $line.Comments 0).ID}}" data-comment="{{(index $line.Comments 0).ID}}" class="hide ui compact right labeled button hide-outdated"> | ||||||
|  | 																				{{svg "octicon-fold" 16}} | ||||||
|  | 																				{{$.i18n.Tr "repo.issues.review.hide_resolved"}} | ||||||
|  | 																			</button> | ||||||
|  | 																		</div> | ||||||
|  | 																	{{end}} | ||||||
| 																	{{if eq $line.GetCommentSide "previous"}} | 																	{{if eq $line.GetCommentSide "previous"}} | ||||||
| 																		<div class="field comment-code-cloud"> | 																		<div id="code-comments-{{(index  $line.Comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}"> | ||||||
| 																			<div class="comment-list"> | 																			<div class="comment-list"> | ||||||
| 																				<ui class="ui comments"> | 																				<ui class="ui comments"> | ||||||
| 																				{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}} | 																				{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}} | ||||||
| 																				</ui> | 																				</ui> | ||||||
| 																			</div> | 																			</div> | ||||||
| 																		{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}} | 																		{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}} | ||||||
|  | 																			{{if and $.CanMarkConversation $isNotPending}} | ||||||
|  | 																				<button class="ui icon tiny button resolve-conversation" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{(index $line.Comments 0).ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation" > | ||||||
|  | 																					{{if $resolved}} | ||||||
|  | 																						{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | ||||||
|  | 																					{{else}} | ||||||
|  | 																						{{$.i18n.Tr "repo.issues.review.resolve_conversation"}} | ||||||
|  | 																					{{end}} | ||||||
|  | 																				</button> | ||||||
|  | 																			{{end}} | ||||||
| 																		</div> | 																		</div> | ||||||
| 																	{{end}} | 																	{{end}} | ||||||
| 																</td> | 																</td> | ||||||
| 																<td class="lines-num"></td> | 																<td class="lines-num"></td> | ||||||
| 																<td class="lines-type-marker"></td> | 																<td class="lines-type-marker"></td> | ||||||
| 																<td class="add-comment-right"> | 																<td class="add-comment-right"> | ||||||
|  | 																	{{if and $resolved (eq $line.GetCommentSide "proposed")}} | ||||||
|  | 																		<div class="ui top attached header"> | ||||||
|  | 																			<span class="ui grey text left"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span>	 | ||||||
|  | 																			<button id="show-outdated-{{(index $line.Comments 0).ID}}" data-comment="{{(index $line.Comments 0).ID}}" class="ui compact right labeled button show-outdated"> | ||||||
|  | 																				{{svg "octicon-unfold" 16}} | ||||||
|  | 																				{{$.i18n.Tr "repo.issues.review.show_resolved"}} | ||||||
|  | 																			</button> | ||||||
|  | 																			<button id="hide-outdated-{{(index $line.Comments 0).ID}}" data-comment="{{(index $line.Comments 0).ID}}" class="hide ui compact right labeled button hide-outdated"> | ||||||
|  | 																				{{svg "octicon-fold" 16}} | ||||||
|  | 																				{{$.i18n.Tr "repo.issues.review.hide_resolved"}} | ||||||
|  | 																			</button>	 | ||||||
|  | 																		</div> | ||||||
|  | 																	{{end}} | ||||||
| 																	{{if eq $line.GetCommentSide "proposed"}} | 																	{{if eq $line.GetCommentSide "proposed"}} | ||||||
| 																		<div class="field comment-code-cloud"> | 																		<div id="code-comments-{{(index  $line.Comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}"> | ||||||
| 																			<div class="comment-list"> | 																			<div class="comment-list"> | ||||||
| 																				<ui class="ui comments"> | 																				<ui class="ui comments"> | ||||||
| 																				{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}} | 																				{{ template "repo/diff/comments" dict "root" $ "comments" $line.Comments}} | ||||||
| 																				</ui> | 																				</ui> | ||||||
| 																			</div> | 																			</div> | ||||||
| 																			{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}} | 																			{{template "repo/diff/comment_form_datahandler" dict "reply" (index $line.Comments 0).ReviewID "hidden" true "root" $ "comment" (index $line.Comments 0)}} | ||||||
|  | 																			{{if and $.CanMarkConversation $isNotPending}} | ||||||
|  | 																				<button class="ui icon tiny button resolve-conversation" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{(index $line.Comments 0).ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation" > | ||||||
|  | 																					{{if $resolved}} | ||||||
|  | 																						{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | ||||||
|  | 																					{{else}} | ||||||
|  | 																						{{$.i18n.Tr "repo.issues.review.resolve_conversation"}} | ||||||
|  | 																					{{end}} | ||||||
|  | 																				</button> | ||||||
|  | 																			{{end}} | ||||||
| 																		</div> | 																		</div> | ||||||
| 																	{{end}} | 																	{{end}} | ||||||
| 																</td> | 																</td> | ||||||
|  |  | ||||||
|  | @ -23,17 +23,42 @@ | ||||||
| 			<td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}">{{if and $.root.SignedUserID $line.CanComment $.root.PageIsPullFiles}}<a class="ui green button add-code-comment add-code-comment-{{if $line.RightIdx}}right{{else}}left{{end}}" data-path="{{$file.Name}}" data-side="{{if $line.RightIdx}}right{{else}}left{{end}}" data-idx="{{if $line.RightIdx}}{{$line.RightIdx}}{{else}}{{$line.LeftIdx}}{{end}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</span></td> | 			<td class="lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}">{{if and $.root.SignedUserID $line.CanComment $.root.PageIsPullFiles}}<a class="ui green button add-code-comment add-code-comment-{{if $line.RightIdx}}right{{else}}left{{end}}" data-path="{{$file.Name}}" data-side="{{if $line.RightIdx}}right{{else}}left{{end}}" data-idx="{{if $line.RightIdx}}{{$line.RightIdx}}{{else}}{{$line.LeftIdx}}{{end}}">+</a>{{end}}<span class="mono wrap{{if $highlightClass}} language-{{$highlightClass}}{{else}} nohighlight{{end}}">{{$section.GetComputedInlineDiffFor $line}}</span></td> | ||||||
| 		</tr> | 		</tr> | ||||||
| 		{{if gt (len $line.Comments) 0}} | 		{{if gt (len $line.Comments) 0}} | ||||||
|  | 			{{$resolved := (index $line.Comments 0).IsResolved}} | ||||||
|  | 			{{$resolveDoer := (index $line.Comments 0).ResolveDoer}} | ||||||
|  | 			{{$isNotPending := (not (eq (index $line.Comments 0).Review.Type 0))}} | ||||||
| 		<tr> | 		<tr> | ||||||
| 			<td colspan="2" class="lines-num"></td> | 			<td colspan="2" class="lines-num"></td> | ||||||
| 			<td class="lines-type-marker"></td> | 			<td class="lines-type-marker"></td> | ||||||
| 			<td class="add-comment-left add-comment-right"> | 			<td class="add-comment-left add-comment-right"> | ||||||
| 				<div class="field comment-code-cloud"> | 				{{if $resolved}} | ||||||
|  | 					<div class = "ui attached header"> | ||||||
|  | 						<span class="ui grey text left"><b>{{$resolveDoer.Name}}</b> {{$.root.i18n.Tr "repo.issues.review.resolved_by"}}</span> | ||||||
|  | 						<button id="show-outdated-{{(index $line.Comments 0).ID}}" data-comment="{{(index $line.Comments 0).ID}}" class="ui compact right labeled button show-outdated"> | ||||||
|  | 							{{svg "octicon-unfold" 16}} | ||||||
|  | 								{{$.root.i18n.Tr "repo.issues.review.show_resolved"}} | ||||||
|  | 						</button> | ||||||
|  | 						<button id="hide-outdated-{{(index $line.Comments 0).ID}}" data-comment="{{(index $line.Comments 0).ID}}" class="hide ui compact right labeled button hide-outdated"> | ||||||
|  | 							{{svg "octicon-fold" 16}} | ||||||
|  | 							{{$.root.i18n.Tr "repo.issues.review.hide_resolved"}} | ||||||
|  | 						</button> | ||||||
|  | 					</div> | ||||||
|  | 				{{end}} | ||||||
|  | 				<div id="code-comments-{{(index  $line.Comments 0).ID}}" class="field comment-code-cloud {{if $resolved}}hide{{end}}"> | ||||||
| 					<div class="comment-list"> | 					<div class="comment-list"> | ||||||
| 						<ui class="ui comments"> | 						<ui class="ui comments"> | ||||||
| 						{{ template "repo/diff/comments" dict "root" $.root "comments" $line.Comments}} | 						{{ template "repo/diff/comments" dict "root" $.root "comments" $line.Comments}} | ||||||
| 						</ui> | 						</ui> | ||||||
| 					</div> | 					</div> | ||||||
| 					{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index $line.Comments 0).ReviewID "root" $.root "comment" (index $line.Comments 0)}} | 					{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index $line.Comments 0).ReviewID "root" $.root "comment" (index $line.Comments 0)}} | ||||||
|  | 					{{if and $.root.CanMarkConversation $isNotPending}} | ||||||
|  | 						<button class="ui icon tiny button resolve-conversation" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{(index $line.Comments 0).ID}}" data-update-url="{{$.root.RepoLink}}/issues/resolve_conversation" > | ||||||
|  | 							{{if $resolved}} | ||||||
|  | 								{{$.root.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | ||||||
|  | 							{{else}} | ||||||
|  | 								{{$.root.i18n.Tr "repo.issues.review.resolve_conversation"}} | ||||||
|  | 							{{end}} | ||||||
|  | 						</button> | ||||||
|  | 					{{end}} | ||||||
| 				</div> | 				</div> | ||||||
| 			</td> | 			</td> | ||||||
| 		</tr> | 		</tr> | ||||||
|  |  | ||||||
|  | @ -381,6 +381,7 @@ | ||||||
| 				</div> | 				</div> | ||||||
| 			</div> | 			</div> | ||||||
| 			{{end}} | 			{{end}} | ||||||
|  | 
 | ||||||
| 			{{if .Review.CodeComments}} | 			{{if .Review.CodeComments}} | ||||||
| 			<div class="timeline-item event"> | 			<div class="timeline-item event"> | ||||||
| 				{{ range $filename, $lines := .Review.CodeComments}} | 				{{ range $filename, $lines := .Review.CodeComments}} | ||||||
|  | @ -388,14 +389,25 @@ | ||||||
| 							<div class="ui segments"> | 							<div class="ui segments"> | ||||||
| 								<div class="ui segment"> | 								<div class="ui segment"> | ||||||
| 									{{$invalid := (index $comms 0).Invalidated}} | 									{{$invalid := (index $comms 0).Invalidated}} | ||||||
| 								{{if $invalid}} | 									{{$resolved := (index $comms 0).IsResolved}} | ||||||
|  | 									{{$resolveDoer := (index $comms 0).ResolveDoer}} | ||||||
|  | 									{{$isNotPending := (not (eq (index $comms 0).Review.Type 0))}} | ||||||
|  | 								{{if or $invalid $resolved}} | ||||||
| 									<button id="show-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="ui compact right labeled button show-outdated"> | 									<button id="show-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="ui compact right labeled button show-outdated"> | ||||||
| 										{{svg "octicon-fold" 16}} | 										{{svg "octicon-unfold" 16}} | ||||||
|  | 										{{if $invalid }} | ||||||
| 											{{$.i18n.Tr "repo.issues.review.show_outdated"}} | 											{{$.i18n.Tr "repo.issues.review.show_outdated"}} | ||||||
|  | 										{{else}} | ||||||
|  | 											{{$.i18n.Tr "repo.issues.review.show_resolved"}} | ||||||
|  | 										{{end}} | ||||||
| 									</button> | 									</button> | ||||||
| 									<button id="hide-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="hide ui compact right labeled button hide-outdated"> | 									<button id="hide-outdated-{{(index $comms 0).ID}}" data-comment="{{(index $comms 0).ID}}" class="hide ui compact right labeled button hide-outdated"> | ||||||
| 										{{svg "octicon-fold" 16}} | 										{{svg "octicon-fold" 16}} | ||||||
|  | 										{{if $invalid}} | ||||||
| 											{{$.i18n.Tr "repo.issues.review.hide_outdated"}} | 											{{$.i18n.Tr "repo.issues.review.hide_outdated"}} | ||||||
|  | 										{{else}} | ||||||
|  | 											{{$.i18n.Tr "repo.issues.review.hide_resolved"}} | ||||||
|  | 										{{end}} | ||||||
| 									</button> | 									</button> | ||||||
| 								{{end}} | 								{{end}} | ||||||
| 									<a href="{{(index $comms 0).CodeCommentURL}}" class="file-comment">{{$filename}}</a> | 									<a href="{{(index $comms 0).CodeCommentURL}}" class="file-comment">{{$filename}}</a> | ||||||
|  | @ -403,7 +415,7 @@ | ||||||
| 								{{$diff := (CommentMustAsDiff (index $comms 0))}} | 								{{$diff := (CommentMustAsDiff (index $comms 0))}} | ||||||
| 								{{if $diff}} | 								{{if $diff}} | ||||||
| 									{{$file := (index $diff.Files 0)}} | 									{{$file := (index $diff.Files 0)}} | ||||||
| 									<div id="code-preview-{{(index $comms 0).ID}}" class="ui table segment{{if $invalid}} hide{{end}}"> | 									<div id="code-preview-{{(index $comms 0).ID}}" class="ui table segment{{if or $invalid $resolved}} hide{{end}}"> | ||||||
| 										<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}"> | 										<div class="diff-file-box diff-box file-content {{TabSizeClass $.Editorconfig $file.Name}}"> | ||||||
| 											<div class="file-body file-code code-view code-diff code-diff-unified"> | 											<div class="file-body file-code code-view code-diff code-diff-unified"> | ||||||
| 												<table> | 												<table> | ||||||
|  | @ -415,7 +427,7 @@ | ||||||
| 										</div> | 										</div> | ||||||
| 									</div> | 									</div> | ||||||
| 								{{end}} | 								{{end}} | ||||||
| 								<div id="code-comments-{{(index $comms 0).ID}}" class="ui segment{{if $invalid}} hide{{end}}"> | 								<div id="code-comments-{{(index $comms 0).ID}}" class="ui segment{{if or $invalid $resolved}} hide{{end}}"> | ||||||
| 									<div class="ui comments"> | 									<div class="ui comments"> | ||||||
| 										{{range $comms}} | 										{{range $comms}} | ||||||
| 											{{ $createdSubStr:= TimeSinceUnix .CreatedUnix $.Lang }} | 											{{ $createdSubStr:= TimeSinceUnix .CreatedUnix $.Lang }} | ||||||
|  | @ -445,6 +457,20 @@ | ||||||
| 										{{end}} | 										{{end}} | ||||||
| 									</div> | 									</div> | ||||||
| 									{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index $comms 0).ReviewID "root" $ "comment" (index $comms 0)}} | 									{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index $comms 0).ReviewID "root" $ "comment" (index $comms 0)}} | ||||||
|  | 
 | ||||||
|  | 									{{if and $.CanMarkConversation $isNotPending}} | ||||||
|  | 										<button class="ui tiny button resolve-conversation" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{(index $comms 0).ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation" > | ||||||
|  | 											{{if $resolved}} | ||||||
|  | 												{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | ||||||
|  | 											{{else}} | ||||||
|  | 												{{$.i18n.Tr "repo.issues.review.resolve_conversation"}} | ||||||
|  | 											{{end}} | ||||||
|  | 										</button> | ||||||
|  | 									{{end}} | ||||||
|  | 
 | ||||||
|  | 									{{if $resolved}} | ||||||
|  | 										<span class="ui grey text"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span> | ||||||
|  | 									{{end}} | ||||||
| 								</div> | 								</div> | ||||||
| 							</div> | 							</div> | ||||||
| 					{{end}} | 					{{end}} | ||||||
|  |  | ||||||
|  | @ -2566,6 +2566,19 @@ $(document).ready(async () => { | ||||||
|     $(e).click(); |     $(e).click(); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|  |   $('.resolve-conversation').on('click', function (e) { | ||||||
|  |     e.preventDefault(); | ||||||
|  |     const id = $(this).data('comment-id'); | ||||||
|  |     const action = $(this).data('action'); | ||||||
|  |     const url = $(this).data('update-url'); | ||||||
|  | 
 | ||||||
|  |     $.post(url, { | ||||||
|  |       _csrf: csrf, | ||||||
|  |       action, | ||||||
|  |       comment_id: id, | ||||||
|  |     }).then(reload); | ||||||
|  |   }); | ||||||
|  | 
 | ||||||
|   buttonsClickOnEnter(); |   buttonsClickOnEnter(); | ||||||
|   searchUsers(); |   searchUsers(); | ||||||
|   searchTeams(); |   searchTeams(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue