Do not reload page after adding comments in Pull Request reviews (#13877)
Fixed #8861 * use ajax on PR review page * handle review comments * extract duplicate code FetchCodeCommentsByLine was initially more or less copied from fetchCodeCommentsByReview. Now they both use a common findCodeComments function instead * use the Engine that was passed into the method Co-authored-by: 6543 <6543@obermui.de> Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com> Co-authored-by: Lauris BH <lauris@nix.lv>
This commit is contained in:
		
							parent
							
								
									ce43d38b4f
								
							
						
					
					
						commit
						bcb7f35221
					
				|  | @ -979,7 +979,7 @@ func (opts *FindCommentsOptions) toConds() builder.Cond { | ||||||
| 	if opts.Type != CommentTypeUnknown { | 	if opts.Type != CommentTypeUnknown { | ||||||
| 		cond = cond.And(builder.Eq{"comment.type": opts.Type}) | 		cond = cond.And(builder.Eq{"comment.type": opts.Type}) | ||||||
| 	} | 	} | ||||||
| 	if opts.Line > 0 { | 	if opts.Line != 0 { | ||||||
| 		cond = cond.And(builder.Eq{"comment.line": opts.Line}) | 		cond = cond.And(builder.Eq{"comment.line": opts.Line}) | ||||||
| 	} | 	} | ||||||
| 	if len(opts.TreePath) > 0 { | 	if len(opts.TreePath) > 0 { | ||||||
|  | @ -1078,18 +1078,35 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review | ||||||
| 	if review == nil { | 	if review == nil { | ||||||
| 		review = &Review{ID: 0} | 		review = &Review{ID: 0} | ||||||
| 	} | 	} | ||||||
| 	//Find comments
 |  | ||||||
| 	opts := FindCommentsOptions{ | 	opts := FindCommentsOptions{ | ||||||
| 		Type:     CommentTypeCode, | 		Type:     CommentTypeCode, | ||||||
| 		IssueID:  issue.ID, | 		IssueID:  issue.ID, | ||||||
| 		ReviewID: review.ID, | 		ReviewID: review.ID, | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	comments, err := findCodeComments(e, opts, issue, currentUser, review) | ||||||
|  | 	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 | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func findCodeComments(e Engine, opts FindCommentsOptions, issue *Issue, currentUser *User, review *Review) ([]*Comment, error) { | ||||||
|  | 	var comments []*Comment | ||||||
|  | 	if review == nil { | ||||||
|  | 		review = &Review{ID: 0} | ||||||
|  | 	} | ||||||
| 	conds := opts.toConds() | 	conds := opts.toConds() | ||||||
| 	if review.ID == 0 { | 	if review.ID == 0 { | ||||||
| 		conds = conds.And(builder.Eq{"invalidated": false}) | 		conds = conds.And(builder.Eq{"invalidated": false}) | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	var comments []*Comment |  | ||||||
| 	if err := e.Where(conds). | 	if err := e.Where(conds). | ||||||
| 		Asc("comment.created_unix"). | 		Asc("comment.created_unix"). | ||||||
| 		Asc("comment.id"). | 		Asc("comment.id"). | ||||||
|  | @ -1117,7 +1134,19 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	n := 0 | ||||||
| 	for _, comment := range comments { | 	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 && | ||||||
|  | 				(currentUser == nil || currentUser.ID != re.ReviewerID) { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			comment.Review = re | ||||||
|  | 		} | ||||||
|  | 		comments[n] = comment | ||||||
|  | 		n++ | ||||||
|  | 
 | ||||||
| 		if err := comment.LoadResolveDoer(); err != nil { | 		if err := comment.LoadResolveDoer(); err != nil { | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
|  | @ -1126,25 +1155,21 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review | ||||||
| 			return nil, err | 			return nil, err | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		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 review.ID == 0 { |  | ||||||
| 				if re.Type == ReviewTypePending && |  | ||||||
| 					(currentUser == nil || currentUser.ID != re.ReviewerID) { |  | ||||||
| 					continue |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 			comment.Review = re |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		comment.RenderedContent = string(markdown.Render([]byte(comment.Content), issue.Repo.Link(), | 		comment.RenderedContent = string(markdown.Render([]byte(comment.Content), issue.Repo.Link(), | ||||||
| 			issue.Repo.ComposeMetas())) | 			issue.Repo.ComposeMetas())) | ||||||
| 		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 | 	return comments[:n], nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // FetchCodeCommentsByLine fetches the code comments for a given treePath and line number
 | ||||||
|  | func FetchCodeCommentsByLine(issue *Issue, currentUser *User, treePath string, line int64) ([]*Comment, error) { | ||||||
|  | 	opts := FindCommentsOptions{ | ||||||
|  | 		Type:     CommentTypeCode, | ||||||
|  | 		IssueID:  issue.ID, | ||||||
|  | 		TreePath: treePath, | ||||||
|  | 		Line:     line, | ||||||
|  | 	} | ||||||
|  | 	return findCodeComments(x, opts, issue, currentUser, nil) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line
 | // FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line
 | ||||||
|  |  | ||||||
|  | @ -548,6 +548,7 @@ func (f *MergePullRequestForm) Validate(ctx *macaron.Context, errs binding.Error | ||||||
| 
 | 
 | ||||||
| // CodeCommentForm form for adding code comments for PRs
 | // CodeCommentForm form for adding code comments for PRs
 | ||||||
| type CodeCommentForm struct { | type CodeCommentForm struct { | ||||||
|  | 	Origin         string `binding:"Required;In(timeline,diff)"` | ||||||
| 	Content        string `binding:"Required"` | 	Content        string `binding:"Required"` | ||||||
| 	Side           string `binding:"Required;In(previous,proposed)"` | 	Side           string `binding:"Required;In(previous,proposed)"` | ||||||
| 	Line           int64 | 	Line           int64 | ||||||
|  |  | ||||||
|  | @ -9,11 +9,40 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"code.gitea.io/gitea/models" | 	"code.gitea.io/gitea/models" | ||||||
| 	"code.gitea.io/gitea/modules/auth" | 	"code.gitea.io/gitea/modules/auth" | ||||||
|  | 	"code.gitea.io/gitea/modules/base" | ||||||
| 	"code.gitea.io/gitea/modules/context" | 	"code.gitea.io/gitea/modules/context" | ||||||
| 	"code.gitea.io/gitea/modules/log" | 	"code.gitea.io/gitea/modules/log" | ||||||
| 	pull_service "code.gitea.io/gitea/services/pull" | 	pull_service "code.gitea.io/gitea/services/pull" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | const ( | ||||||
|  | 	tplConversation base.TplName = "repo/diff/conversation" | ||||||
|  | 	tplNewComment   base.TplName = "repo/diff/new_comment" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // RenderNewCodeCommentForm will render the form for creating a new review comment
 | ||||||
|  | func RenderNewCodeCommentForm(ctx *context.Context) { | ||||||
|  | 	issue := GetActionIssue(ctx) | ||||||
|  | 	if !issue.IsPull { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	currentReview, err := models.GetCurrentReview(ctx.User, issue) | ||||||
|  | 	if err != nil && !models.IsErrReviewNotExist(err) { | ||||||
|  | 		ctx.ServerError("GetCurrentReview", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["PageIsPullFiles"] = true | ||||||
|  | 	ctx.Data["Issue"] = issue | ||||||
|  | 	ctx.Data["CurrentReview"] = currentReview | ||||||
|  | 	pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(issue.PullRequest.GetGitRefName()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("GetRefCommitID", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["AfterCommitID"] = pullHeadCommitID | ||||||
|  | 	ctx.HTML(200, tplNewComment) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // CreateCodeComment will create a code comment including an pending review if required
 | // CreateCodeComment will create a code comment including an pending review if required
 | ||||||
| func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) { | func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) { | ||||||
| 	issue := GetActionIssue(ctx) | 	issue := GetActionIssue(ctx) | ||||||
|  | @ -58,11 +87,17 @@ func CreateCodeComment(ctx *context.Context, form auth.CodeCommentForm) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	log.Trace("Comment created: %-v #%d[%d] Comment[%d]", ctx.Repo.Repository, issue.Index, issue.ID, comment.ID) | 	log.Trace("Comment created: %-v #%d[%d] Comment[%d]", ctx.Repo.Repository, issue.Index, issue.ID, comment.ID) | ||||||
|  | 
 | ||||||
|  | 	if form.Origin == "diff" { | ||||||
|  | 		renderConversation(ctx, comment) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	ctx.Redirect(comment.HTMLURL()) | 	ctx.Redirect(comment.HTMLURL()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // UpdateResolveConversation add or remove an Conversation resolved mark
 | // UpdateResolveConversation add or remove an Conversation resolved mark
 | ||||||
| func UpdateResolveConversation(ctx *context.Context) { | func UpdateResolveConversation(ctx *context.Context) { | ||||||
|  | 	origin := ctx.Query("origin") | ||||||
| 	action := ctx.Query("action") | 	action := ctx.Query("action") | ||||||
| 	commentID := ctx.QueryInt64("comment_id") | 	commentID := ctx.QueryInt64("comment_id") | ||||||
| 
 | 
 | ||||||
|  | @ -103,11 +138,38 @@ func UpdateResolveConversation(ctx *context.Context) { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	if origin == "diff" { | ||||||
|  | 		renderConversation(ctx, comment) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	ctx.JSON(200, map[string]interface{}{ | 	ctx.JSON(200, map[string]interface{}{ | ||||||
| 		"ok": true, | 		"ok": true, | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func renderConversation(ctx *context.Context, comment *models.Comment) { | ||||||
|  | 	comments, err := models.FetchCodeCommentsByLine(comment.Issue, ctx.User, comment.TreePath, comment.Line) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("FetchCodeCommentsByLine", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["PageIsPullFiles"] = true | ||||||
|  | 	ctx.Data["comments"] = comments | ||||||
|  | 	ctx.Data["CanMarkConversation"] = true | ||||||
|  | 	ctx.Data["Issue"] = comment.Issue | ||||||
|  | 	if err = comment.Issue.LoadPullRequest(); err != nil { | ||||||
|  | 		ctx.ServerError("comment.Issue.LoadPullRequest", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	pullHeadCommitID, err := ctx.Repo.GitRepo.GetRefCommitID(comment.Issue.PullRequest.GetGitRefName()) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.ServerError("GetRefCommitID", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["AfterCommitID"] = pullHeadCommitID | ||||||
|  | 	ctx.HTML(200, tplConversation) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // 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) | ||||||
|  |  | ||||||
|  | @ -856,6 +856,7 @@ func RegisterMacaronRoutes(m *macaron.Macaron) { | ||||||
| 			m.Group("/files", func() { | 			m.Group("/files", func() { | ||||||
| 				m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles) | 				m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles) | ||||||
| 				m.Group("/reviews", func() { | 				m.Group("/reviews", func() { | ||||||
|  | 					m.Get("/new_comment", repo.RenderNewCodeCommentForm) | ||||||
| 					m.Post("/comments", bindIgnErr(auth.CodeCommentForm{}), repo.CreateCodeComment) | 					m.Post("/comments", bindIgnErr(auth.CodeCommentForm{}), repo.CreateCodeComment) | ||||||
| 					m.Post("/submit", bindIgnErr(auth.SubmitReviewForm{}), repo.SubmitReview) | 					m.Post("/submit", bindIgnErr(auth.SubmitReviewForm{}), repo.SubmitReview) | ||||||
| 				}, context.RepoMustNotBeArchived()) | 				}, context.RepoMustNotBeArchived()) | ||||||
|  |  | ||||||
|  | @ -144,28 +144,25 @@ | ||||||
| 		{{end}} | 		{{end}} | ||||||
| 
 | 
 | ||||||
| 		{{if not $.Repository.IsArchived}} | 		{{if not $.Repository.IsArchived}} | ||||||
| 			<div id="pull_review_add_comment" class="hide"> | 			<div class="hide" id="edit-content-form"> | ||||||
| 					{{template "repo/diff/new_comment" dict "root" .}} | 				<div class="ui comment form"> | ||||||
|  | 					<div class="ui top attached tabular menu"> | ||||||
|  | 						<a class="active write item">{{$.i18n.Tr "write"}}</a> | ||||||
|  | 						<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> | ||||||
| 					</div> | 					</div> | ||||||
| 					<div class="hide" id="edit-content-form"> | 					<div class="ui bottom attached active write tab segment"> | ||||||
| 							<div class="ui comment form"> | 						<textarea class="review-textarea" tabindex="1" name="content"></textarea> | ||||||
| 									<div class="ui top attached tabular menu"> |  | ||||||
| 											<a class="active write item">{{$.i18n.Tr "write"}}</a> |  | ||||||
| 											<a class="preview item" data-url="{{$.Repository.APIURL}}/markdown" data-context="{{$.RepoLink}}">{{$.i18n.Tr "preview"}}</a> |  | ||||||
| 									</div> |  | ||||||
| 									<div class="ui bottom attached active write tab segment"> |  | ||||||
| 											<textarea class="review-textarea" tabindex="1" name="content"></textarea> |  | ||||||
| 									</div> |  | ||||||
| 									<div class="ui bottom attached tab preview segment markdown"> |  | ||||||
| 									{{$.i18n.Tr "loading"}} |  | ||||||
| 									</div> |  | ||||||
| 									<div class="text right edit buttons"> |  | ||||||
| 											<div class="ui basic blue cancel button" tabindex="3">{{.i18n.Tr "repo.issues.cancel"}}</div> |  | ||||||
| 											<div class="ui green save button" tabindex="2">{{.i18n.Tr "repo.issues.save"}}</div> |  | ||||||
| 									</div> |  | ||||||
| 							</div> |  | ||||||
| 					</div> | 					</div> | ||||||
| 		 {{end}} | 					<div class="ui bottom attached tab preview segment markdown"> | ||||||
|  | 					{{$.i18n.Tr "loading"}} | ||||||
|  | 					</div> | ||||||
|  | 					<div class="text right edit buttons"> | ||||||
|  | 						<div class="ui basic blue cancel button" tabindex="3">{{.i18n.Tr "repo.issues.cancel"}}</div> | ||||||
|  | 						<div class="ui green save button" tabindex="2">{{.i18n.Tr "repo.issues.save"}}</div> | ||||||
|  | 					</div> | ||||||
|  | 				</div> | ||||||
|  | 			</div> | ||||||
|  | 		{{end}} | ||||||
| 
 | 
 | ||||||
| 		{{if .IsSplitStyle}} | 		{{if .IsSplitStyle}} | ||||||
| 			<script> | 			<script> | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ | ||||||
| 	{{end}} | 	{{end}} | ||||||
| 	<form class="ui form {{if $.hidden}}hide comment-form comment-form-reply{{end}}" action="{{$.root.Issue.HTMLURL}}/files/reviews/comments" method="post"> | 	<form class="ui form {{if $.hidden}}hide comment-form comment-form-reply{{end}}" action="{{$.root.Issue.HTMLURL}}/files/reviews/comments" method="post"> | ||||||
| 	{{$.root.CsrfTokenHtml}} | 	{{$.root.CsrfTokenHtml}} | ||||||
|  | 		<input type="hidden" name="origin" value="{{if $.root.PageIsPullFiles}}diff{{else}}timeline{{end}}"> | ||||||
| 		<input type="hidden" name="latest_commit_id" value="{{$.root.AfterCommitID}}"/> | 		<input type="hidden" name="latest_commit_id" value="{{$.root.AfterCommitID}}"/> | ||||||
| 		<input type="hidden" name="side" value="{{if $.Side}}{{$.Side}}{{end}}"> | 		<input type="hidden" name="side" value="{{if $.Side}}{{$.Side}}{{end}}"> | ||||||
| 		<input type="hidden" name="line" value="{{if $.Line}}{{$.Line}}{{end}}"> | 		<input type="hidden" name="line" value="{{if $.Line}}{{$.Line}}{{end}}"> | ||||||
|  | @ -29,7 +30,7 @@ | ||||||
| 			<span class="markdown-info">{{svg "octicon-markdown"}} {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span> | 			<span class="markdown-info">{{svg "octicon-markdown"}} {{$.root.i18n.Tr "repo.diff.comment.markdown_info"}}</span> | ||||||
| 			<div class="ui right"> | 			<div class="ui right"> | ||||||
| 				{{if $.reply}} | 				{{if $.reply}} | ||||||
| 					<button class="ui submit green tiny button btn-reply" onclick="window.submitReply(this);">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button> | 					<button class="ui submit green tiny button btn-reply" type="submit">{{$.root.i18n.Tr "repo.diff.comment.reply"}}</button> | ||||||
| 					<input type="hidden" name="reply" value="{{$.reply}}"> | 					<input type="hidden" name="reply" value="{{$.reply}}"> | ||||||
| 				{{else}} | 				{{else}} | ||||||
| 					{{if $.root.CurrentReview}} | 					{{if $.root.CurrentReview}} | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| {{$resolved := (index .comments 0).IsResolved}} | {{$resolved := (index .comments 0).IsResolved}} | ||||||
| {{$resolveDoer := (index .comments 0).ResolveDoer}} | {{$resolveDoer := (index .comments 0).ResolveDoer}} | ||||||
| {{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}} | {{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}} | ||||||
| <div class="conversation-holder"> | <div class="conversation-holder" data-path="{{(index .comments 0).TreePath}}" data-side="{{if lt (index .comments 0).Line 0}}left{{else}}right{{end}}" data-idx="{{(index .comments 0).UnsignedLine}}"> | ||||||
| 	{{if $resolved}} | 	{{if $resolved}} | ||||||
| 		<div class="ui attached header resolved-placeholder"> | 		<div class="ui attached header resolved-placeholder"> | ||||||
| 			<span class="ui grey text left"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span> | 			<span class="ui grey text left"><b>{{$resolveDoer.Name}}</b> {{$.i18n.Tr "repo.issues.review.resolved_by"}}</span> | ||||||
|  | @ -23,7 +23,7 @@ | ||||||
| 		</div> | 		</div> | ||||||
| 		{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index .comments 0).ReviewID "root" $ "comment" (index .comments 0)}} | 		{{template "repo/diff/comment_form_datahandler" dict "hidden" true "reply" (index .comments 0).ReviewID "root" $ "comment" (index .comments 0)}} | ||||||
| 		{{if and $.CanMarkConversation $isNotPending}} | 		{{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 .comments 0).ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation" > | 			<button class="ui icon tiny button resolve-conversation" data-origin="diff" data-action="{{if not $resolved}}Resolve{{else}}UnResolve{{end}}" data-comment-id="{{(index .comments 0).ID}}" data-update-url="{{$.RepoLink}}/issues/resolve_conversation" > | ||||||
| 				{{if $resolved}} | 				{{if $resolved}} | ||||||
| 					{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | 					{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | ||||||
| 				{{else}} | 				{{else}} | ||||||
|  |  | ||||||
|  | @ -1,3 +1,5 @@ | ||||||
| <div class="field comment-code-cloud"> | <div class="conversation-holder"> | ||||||
| 	{{template "repo/diff/comment_form_datahandler" .}} | 	<div class="field comment-code-cloud"> | ||||||
|  | 		{{template "repo/diff/comment_form_datahandler" .}} | ||||||
|  | 	</div> | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -1,7 +1,7 @@ | ||||||
| {{$file := .file}} | {{$file := .file}} | ||||||
| {{range $j, $section := $file.Sections}} | {{range $j, $section := $file.Sections}} | ||||||
| 	{{range $k, $line := $section.Lines}} | 	{{range $k, $line := $section.Lines}} | ||||||
| 		<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | 		<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}" data-line-type="{{DiffLineTypeToStr .GetType}}"> | ||||||
| 			{{if eq .GetType 4}} | 			{{if eq .GetType 4}} | ||||||
| 				<td class="lines-num lines-num-old"> | 				<td class="lines-num lines-num-old"> | ||||||
| 					{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | 					{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | ||||||
|  | @ -24,14 +24,14 @@ | ||||||
| 			{{else}} | 			{{else}} | ||||||
| 				<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td> | 				<td class="lines-num lines-num-old" data-line-num="{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}"><span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}"></span></td> | ||||||
| 				<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | 				<td class="lines-type-marker lines-type-marker-old">{{if $line.LeftIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | ||||||
| 				<td class="lines-code lines-code-old halfwidth">{{if and $.root.SignedUserID $line.CanComment $.root.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui primary button add-code-comment add-code-comment-left" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}">{{svg "octicon-plus"}}</a>{{end}}<code class="code-inner">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></td> | 				<td class="lines-code lines-code-old halfwidth">{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 2))}}<a class="ui primary button add-code-comment add-code-comment-left{{if (not $line.CanComment)}} invisible{{end}}" data-path="{{$file.Name}}" data-side="left" data-idx="{{$line.LeftIdx}}" data-new-comment-url="{{$.root.Issue.HTMLURL}}/files/reviews/new_comment">{{svg "octicon-plus"}}</a>{{end}}<code class="code-inner">{{if $line.LeftIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></td> | ||||||
| 				<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td> | 				<td class="lines-num lines-num-new" data-line-num="{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}"><span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}"></span></td> | ||||||
| 				<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | 				<td class="lines-type-marker lines-type-marker-new">{{if $line.RightIdx}}<span class="mono" data-type-marker="{{$line.GetLineTypeMarker}}"></span>{{end}}</td> | ||||||
| 				<td class="lines-code lines-code-new halfwidth">{{if and $.root.SignedUserID $line.CanComment $.root.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui primary button add-code-comment add-code-comment-right" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}">{{svg "octicon-plus"}}</a>{{end}}<code class="code-inner">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></td> | 				<td class="lines-code lines-code-new halfwidth">{{if and $.root.SignedUserID $.root.PageIsPullFiles (not (eq .GetType 3))}}<a class="ui primary button add-code-comment add-code-comment-right{{if (not $line.CanComment)}} invisible{{end}}" data-path="{{$file.Name}}" data-side="right" data-idx="{{$line.RightIdx}}" data-new-comment-url="{{$.root.Issue.HTMLURL}}/files/reviews/new_comment">{{svg "octicon-plus"}}</a>{{end}}<code class="code-inner">{{if $line.RightIdx}}{{$section.GetComputedInlineDiffFor $line}}{{end}}</code></td> | ||||||
| 			{{end}} | 			{{end}} | ||||||
| 		</tr> | 		</tr> | ||||||
| 		{{if gt (len $line.Comments) 0}} | 		{{if gt (len $line.Comments) 0}} | ||||||
| 			<tr class="add-code-comment"> | 			<tr class="add-comment" data-line-type="{{DiffLineTypeToStr .GetType}}"> | ||||||
| 				<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"> | ||||||
|  |  | ||||||
|  | @ -2,7 +2,7 @@ | ||||||
| {{range $j, $section := $file.Sections}} | {{range $j, $section := $file.Sections}} | ||||||
| 	{{range $k, $line := $section.Lines}} | 	{{range $k, $line := $section.Lines}} | ||||||
| 		{{if or $.root.AfterCommitID (ne .GetType 4)}} | 		{{if or $.root.AfterCommitID (ne .GetType 4)}} | ||||||
| 			<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}"> | 			<tr class="{{DiffLineTypeToStr .GetType}}-code nl-{{$k}} ol-{{$k}}" data-line-type="{{DiffLineTypeToStr .GetType}}"> | ||||||
| 				{{if eq .GetType 4}} | 				{{if eq .GetType 4}} | ||||||
| 					<td colspan="2" class="lines-num"> | 					<td colspan="2" class="lines-num"> | ||||||
| 						{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | 						{{if or (eq $line.GetExpandDirection 3) (eq $line.GetExpandDirection 5) }} | ||||||
|  | @ -29,11 +29,11 @@ | ||||||
| 				{{if eq .GetType 4}} | 				{{if eq .GetType 4}} | ||||||
| 					<td class="chroma lines-code blob-hunk"><code class="code-inner">{{$section.GetComputedInlineDiffFor $line}}</code></td> | 					<td class="chroma lines-code blob-hunk"><code class="code-inner">{{$section.GetComputedInlineDiffFor $line}}</code></td> | ||||||
| 				{{else}} | 				{{else}} | ||||||
| 					<td class="chroma lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}">{{if and $.root.SignedUserID $line.CanComment $.root.PageIsPullFiles}}<a class="ui primary 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}}">{{svg "octicon-plus"}}</a>{{end}}<code class="code-inner">{{$section.GetComputedInlineDiffFor $line}}</code></td> | 					<td class="chroma lines-code{{if (not $line.RightIdx)}} lines-code-old{{end}}">{{if and $.root.SignedUserID $.root.PageIsPullFiles}}<a class="ui primary button add-code-comment add-code-comment-{{if $line.RightIdx}}right{{else}}left{{end}}{{if (not $line.CanComment)}} invisible{{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}}" data-new-comment-url="{{$.root.Issue.HTMLURL}}/files/reviews/new_comment">{{svg "octicon-plus"}}</a>{{end}}<code class="code-inner">{{$section.GetComputedInlineDiffFor $line}}</code></td> | ||||||
| 				{{end}} | 				{{end}} | ||||||
| 			</tr> | 			</tr> | ||||||
| 			{{if gt (len $line.Comments) 0}} | 			{{if gt (len $line.Comments) 0}} | ||||||
| 				<tr> | 				<tr class="add-comment" data-line-type="{{DiffLineTypeToStr .GetType}}"> | ||||||
| 					<td colspan="2" class="lines-num"></td> | 					<td colspan="2" class="lines-num"></td> | ||||||
| 					<td class="add-comment-left add-comment-right" colspan="2"> | 					<td class="add-comment-left add-comment-right" colspan="2"> | ||||||
| 						{{template "repo/diff/conversation" mergeinto $.root "comments" $line.Comments}} | 						{{template "repo/diff/conversation" mergeinto $.root "comments" $line.Comments}} | ||||||
|  |  | ||||||
|  | @ -530,7 +530,7 @@ | ||||||
| 									{{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}} | 									{{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" > | 										<button class="ui tiny button resolve-conversation" data-origin="timeline" 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}} | 											{{if $resolved}} | ||||||
| 												{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | 												{{$.i18n.Tr "repo.issues.review.un_resolve_conversation"}} | ||||||
| 											{{else}} | 											{{else}} | ||||||
|  |  | ||||||
|  | @ -907,7 +907,7 @@ async function initRepository() { | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Quote reply
 |     // Quote reply
 | ||||||
|     $('.quote-reply').on('click', function (event) { |     $(document).on('click', '.quote-reply', function (event) { | ||||||
|       $(this).closest('.dropdown').find('.menu').toggle('visible'); |       $(this).closest('.dropdown').find('.menu').toggle('visible'); | ||||||
|       const target = $(this).data('target'); |       const target = $(this).data('target'); | ||||||
|       const quote = $(`#comment-${target}`).text().replace(/\n/g, '\n> '); |       const quote = $(`#comment-${target}`).text().replace(/\n/g, '\n> '); | ||||||
|  | @ -933,7 +933,7 @@ async function initRepository() { | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Edit issue or comment content
 |     // Edit issue or comment content
 | ||||||
|     $('.edit-content').on('click', async function (event) { |     $(document).on('click', '.edit-content', async function (event) { | ||||||
|       $(this).closest('.dropdown').find('.menu').toggle('visible'); |       $(this).closest('.dropdown').find('.menu').toggle('visible'); | ||||||
|       const $segment = $(this).closest('.header').next(); |       const $segment = $(this).closest('.header').next(); | ||||||
|       const $editContentZone = $segment.find('.edit-content-zone'); |       const $editContentZone = $segment.find('.edit-content-zone'); | ||||||
|  | @ -1096,7 +1096,7 @@ async function initRepository() { | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     // Delete comment
 |     // Delete comment
 | ||||||
|     $('.delete-comment').on('click', function () { |     $(document).on('click', '.delete-comment', function () { | ||||||
|       const $this = $(this); |       const $this = $(this); | ||||||
|       if (window.confirm($this.data('locale'))) { |       if (window.confirm($this.data('locale'))) { | ||||||
|         $.post($this.data('url'), { |         $.post($this.data('url'), { | ||||||
|  | @ -1105,6 +1105,15 @@ async function initRepository() { | ||||||
|           const $conversationHolder = $this.closest('.conversation-holder'); |           const $conversationHolder = $this.closest('.conversation-holder'); | ||||||
|           $(`#${$this.data('comment-id')}`).remove(); |           $(`#${$this.data('comment-id')}`).remove(); | ||||||
|           if ($conversationHolder.length && !$conversationHolder.find('.comment').length) { |           if ($conversationHolder.length && !$conversationHolder.find('.comment').length) { | ||||||
|  |             const path = $conversationHolder.data('path'); | ||||||
|  |             const side = $conversationHolder.data('side'); | ||||||
|  |             const idx = $conversationHolder.data('idx'); | ||||||
|  |             const lineType = $conversationHolder.closest('tr').data('line-type'); | ||||||
|  |             if (lineType === 'same') { | ||||||
|  |               $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).removeClass('invisible'); | ||||||
|  |             } else { | ||||||
|  |               $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).removeClass('invisible'); | ||||||
|  |             } | ||||||
|             $conversationHolder.remove(); |             $conversationHolder.remove(); | ||||||
|           } |           } | ||||||
|         }); |         }); | ||||||
|  | @ -1235,7 +1244,7 @@ function initPullRequestReview() { | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   $('.show-outdated').on('click', function (e) { |   $(document).on('click', '.show-outdated', function (e) { | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     const id = $(this).data('comment'); |     const id = $(this).data('comment'); | ||||||
|     $(this).addClass('hide'); |     $(this).addClass('hide'); | ||||||
|  | @ -1244,7 +1253,7 @@ function initPullRequestReview() { | ||||||
|     $(`#hide-outdated-${id}`).removeClass('hide'); |     $(`#hide-outdated-${id}`).removeClass('hide'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   $('.hide-outdated').on('click', function (e) { |   $(document).on('click', '.hide-outdated', function (e) { | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     const id = $(this).data('comment'); |     const id = $(this).data('comment'); | ||||||
|     $(this).addClass('hide'); |     $(this).addClass('hide'); | ||||||
|  | @ -1253,7 +1262,7 @@ function initPullRequestReview() { | ||||||
|     $(`#show-outdated-${id}`).removeClass('hide'); |     $(`#show-outdated-${id}`).removeClass('hide'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   $('button.comment-form-reply').on('click', function (e) { |   $(document).on('click', 'button.comment-form-reply', function (e) { | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     $(this).hide(); |     $(this).hide(); | ||||||
|     const form = $(this).parent().find('.comment-form'); |     const form = $(this).parent().find('.comment-form'); | ||||||
|  | @ -1284,7 +1293,7 @@ function initPullRequestReview() { | ||||||
|     $(this).closest('.menu').toggle('visible'); |     $(this).closest('.menu').toggle('visible'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   $('.add-code-comment').on('click', function (e) { |   $('a.add-code-comment').on('click', async function (e) { | ||||||
|     if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
 |     if ($(e.target).hasClass('btn-add-single')) return; // https://github.com/go-gitea/gitea/issues/4745
 | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
| 
 | 
 | ||||||
|  | @ -1292,18 +1301,13 @@ function initPullRequestReview() { | ||||||
|     const side = $(this).data('side'); |     const side = $(this).data('side'); | ||||||
|     const idx = $(this).data('idx'); |     const idx = $(this).data('idx'); | ||||||
|     const path = $(this).data('path'); |     const path = $(this).data('path'); | ||||||
|     const form = $('#pull_review_add_comment').html(); |  | ||||||
|     const tr = $(this).closest('tr'); |     const tr = $(this).closest('tr'); | ||||||
| 
 |     const lineType = tr.data('line-type'); | ||||||
|     const oldLineNum = tr.find('.lines-num-old').data('line-num'); |  | ||||||
|     const newLineNum = tr.find('.lines-num-new').data('line-num'); |  | ||||||
|     const addCommentKey = `${oldLineNum}|${newLineNum}`; |  | ||||||
|     if (document.querySelector(`[data-add-comment-key="${addCommentKey}"]`)) return; // don't add same comment box twice
 |  | ||||||
| 
 | 
 | ||||||
|     let ntr = tr.next(); |     let ntr = tr.next(); | ||||||
|     if (!ntr.hasClass('add-comment')) { |     if (!ntr.hasClass('add-comment')) { | ||||||
|       ntr = $(` |       ntr = $(` | ||||||
|         <tr class="add-comment" data-add-comment-key="${addCommentKey}"> |         <tr class="add-comment" data-line-type="${lineType}"> | ||||||
|           ${isSplit ? ` |           ${isSplit ? ` | ||||||
|             <td class="lines-num"></td> |             <td class="lines-num"></td> | ||||||
|             <td class="lines-type-marker"></td> |             <td class="lines-type-marker"></td> | ||||||
|  | @ -1312,8 +1316,7 @@ function initPullRequestReview() { | ||||||
|             <td class="lines-type-marker"></td> |             <td class="lines-type-marker"></td> | ||||||
|             <td class="add-comment-right"></td> |             <td class="add-comment-right"></td> | ||||||
|           ` : ` |           ` : ` | ||||||
|             <td class="lines-num"></td> |             <td colspan="2" class="lines-num"></td> | ||||||
|             <td class="lines-num"></td> |  | ||||||
|             <td class="add-comment-left add-comment-right" colspan="2"></td> |             <td class="add-comment-left add-comment-right" colspan="2"></td> | ||||||
|           `}
 |           `}
 | ||||||
|         </tr>`); |         </tr>`); | ||||||
|  | @ -1322,21 +1325,20 @@ function initPullRequestReview() { | ||||||
| 
 | 
 | ||||||
|     const td = ntr.find(`.add-comment-${side}`); |     const td = ntr.find(`.add-comment-${side}`); | ||||||
|     let commentCloud = td.find('.comment-code-cloud'); |     let commentCloud = td.find('.comment-code-cloud'); | ||||||
|     if (commentCloud.length === 0) { |     if (commentCloud.length === 0 && !ntr.find('button[name="is_review"]').length) { | ||||||
|       td.html(form); |       const data = await $.get($(this).data('new-comment-url')); | ||||||
|  |       td.html(data); | ||||||
|       commentCloud = td.find('.comment-code-cloud'); |       commentCloud = td.find('.comment-code-cloud'); | ||||||
|       assingMenuAttributes(commentCloud.find('.menu')); |       assingMenuAttributes(commentCloud.find('.menu')); | ||||||
| 
 |  | ||||||
|       td.find("input[name='line']").val(idx); |       td.find("input[name='line']").val(idx); | ||||||
|       td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); |       td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); | ||||||
|       td.find("input[name='path']").val(path); |       td.find("input[name='path']").val(path); | ||||||
|  |       const $textarea = commentCloud.find('textarea'); | ||||||
|  |       attachTribute($textarea.get(), {mentions: true, emoji: true}); | ||||||
|  |       const $simplemde = setCommentSimpleMDE($textarea); | ||||||
|  |       $textarea.focus(); | ||||||
|  |       $simplemde.codemirror.focus(); | ||||||
|     } |     } | ||||||
|     const $textarea = commentCloud.find('textarea'); |  | ||||||
|     attachTribute($textarea.get(), {mentions: true, emoji: true}); |  | ||||||
| 
 |  | ||||||
|     const $simplemde = setCommentSimpleMDE($textarea); |  | ||||||
|     $textarea.focus(); |  | ||||||
|     $simplemde.codemirror.focus(); |  | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -2497,17 +2499,24 @@ $(document).ready(async () => { | ||||||
|     $(e).trigger('click'); |     $(e).trigger('click'); | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   $('.resolve-conversation').on('click', function (e) { |   $(document).on('click', '.resolve-conversation', async function (e) { | ||||||
|     e.preventDefault(); |     e.preventDefault(); | ||||||
|     const id = $(this).data('comment-id'); |     const comment_id = $(this).data('comment-id'); | ||||||
|  |     const origin = $(this).data('origin'); | ||||||
|     const action = $(this).data('action'); |     const action = $(this).data('action'); | ||||||
|     const url = $(this).data('update-url'); |     const url = $(this).data('update-url'); | ||||||
| 
 | 
 | ||||||
|     $.post(url, { |     const data = await $.post(url, {_csrf: csrf, origin, action, comment_id}); | ||||||
|       _csrf: csrf, | 
 | ||||||
|       action, |     if ($(this).closest('.conversation-holder').length) { | ||||||
|       comment_id: id, |       const conversation = $(data); | ||||||
|     }).then(reload); |       $(this).closest('.conversation-holder').replaceWith(conversation); | ||||||
|  |       conversation.find('.dropdown').dropdown(); | ||||||
|  |       initReactionSelector(conversation); | ||||||
|  |       initClipboard(); | ||||||
|  |     } else { | ||||||
|  |       reload(); | ||||||
|  |     } | ||||||
|   }); |   }); | ||||||
| 
 | 
 | ||||||
|   buttonsClickOnEnter(); |   buttonsClickOnEnter(); | ||||||
|  | @ -3626,6 +3635,28 @@ function initIssueList() { | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | $(document).on('click', 'button[name="is_review"]', (e) => { | ||||||
|  |   $(e.target).closest('form').append('<input type="hidden" name="is_review" value="true">'); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | $(document).on('submit', '.conversation-holder form', async (e) => { | ||||||
|  |   e.preventDefault(); | ||||||
|  |   const form = $(e.target); | ||||||
|  |   const newConversationHolder = $(await $.post(form.attr('action'), form.serialize())); | ||||||
|  |   const {path, side, idx} = newConversationHolder.data(); | ||||||
|  | 
 | ||||||
|  |   form.closest('.conversation-holder').replaceWith(newConversationHolder); | ||||||
|  |   if (form.closest('tr').data('line-type') === 'same') { | ||||||
|  |     $(`a.add-code-comment[data-path="${path}"][data-idx="${idx}"]`).addClass('invisible'); | ||||||
|  |   } else { | ||||||
|  |     $(`a.add-code-comment[data-path="${path}"][data-side="${side}"][data-idx="${idx}"]`).addClass('invisible'); | ||||||
|  |   } | ||||||
|  |   newConversationHolder.find('.dropdown').dropdown(); | ||||||
|  |   initReactionSelector(newConversationHolder); | ||||||
|  |   initClipboard(); | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| window.cancelCodeComment = function (btn) { | window.cancelCodeComment = function (btn) { | ||||||
|   const form = $(btn).closest('form'); |   const form = $(btn).closest('form'); | ||||||
|   if (form.length > 0 && form.hasClass('comment-form')) { |   if (form.length > 0 && form.hasClass('comment-form')) { | ||||||
|  | @ -3636,13 +3667,6 @@ window.cancelCodeComment = function (btn) { | ||||||
|   } |   } | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| window.submitReply = function (btn) { |  | ||||||
|   const form = $(btn).closest('form'); |  | ||||||
|   if (form.length > 0 && form.hasClass('comment-form')) { |  | ||||||
|     form.trigger('submit'); |  | ||||||
|   } |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| window.onOAuthLoginClick = function () { | window.onOAuthLoginClick = function () { | ||||||
|   const oauthLoader = $('#oauth2-login-loader'); |   const oauthLoader = $('#oauth2-login-loader'); | ||||||
|   const oauthNav = $('#oauth2-login-navigator'); |   const oauthNav = $('#oauth2-login-navigator'); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue