diff --git a/rhodecode/apps/debug_style/views.py b/rhodecode/apps/debug_style/views.py --- a/rhodecode/apps/debug_style/views.py +++ b/rhodecode/apps/debug_style/views.py @@ -252,7 +252,7 @@ But please check this code var comment = $('#comment-'+commentId); var commentData = comment.data(); if (commentData.commentInline) { - this.createComment(comment, commentId) + this.createComment(comment, f_path, line_no, commentId) } else { Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId) } diff --git a/rhodecode/apps/repository/views/repo_commits.py b/rhodecode/apps/repository/views/repo_commits.py --- a/rhodecode/apps/repository/views/repo_commits.py +++ b/rhodecode/apps/repository/views/repo_commits.py @@ -383,6 +383,9 @@ class RepoCommitsView(RepoAppView): text = self.request.POST.get('text') comment_type = self.request.POST.get('comment_type') resolves_comment_id = self.request.POST.get('resolves_comment_id', None) + f_path = self.request.POST.get('f_path') + line_no = self.request.POST.get('line') + target_elem_id = 'file-{}'.format(h.safeid(h.safe_unicode(f_path))) if status: text = text or (_('Status change %(transition_icon)s %(status)s') @@ -404,8 +407,8 @@ class RepoCommitsView(RepoAppView): repo=self.db_repo.repo_id, user=self._rhodecode_db_user.user_id, commit_id=current_id, - f_path=self.request.POST.get('f_path'), - line_no=self.request.POST.get('line'), + f_path=f_path, + line_no=line_no, status_change=(ChangesetStatus.get_status_lbl(status) if status else None), status_change_type=status, @@ -447,19 +450,20 @@ class RepoCommitsView(RepoAppView): # finalize, commit and redirect Session().commit() - data = { - 'target_id': h.safeid(h.safe_unicode( - self.request.POST.get('f_path'))), - } + data = {} if comment: + comment_id = comment.comment_id + data[comment_id] = { + 'target_id': target_elem_id + } c.co = comment c.at_version_num = 0 rendered_comment = render( 'rhodecode:templates/changeset/changeset_comment_block.mako', self._get_template_context(c), self.request) - data.update(comment.get_dict()) - data.update({'rendered_text': rendered_comment}) + data[comment_id].update(comment.get_dict()) + data[comment_id].update({'rendered_text': rendered_comment}) comment_broadcast_channel = channelstream.comment_channel( self.db_repo_name, commit_obj=commit) diff --git a/rhodecode/apps/repository/views/repo_pull_requests.py b/rhodecode/apps/repository/views/repo_pull_requests.py --- a/rhodecode/apps/repository/views/repo_pull_requests.py +++ b/rhodecode/apps/repository/views/repo_pull_requests.py @@ -983,8 +983,9 @@ class RepoPullRequestsView(RepoAppView, } return data - def _get_existing_ids(self, post_data): - return filter(lambda e: e, map(safe_int, aslist(post_data.get('comments'), ','))) + @classmethod + def get_comment_ids(cls, post_data): + return filter(lambda e: e > 0, map(safe_int, aslist(post_data.get('comments'), ','))) @LoginRequired() @NotAnonymous() @@ -1022,7 +1023,7 @@ class RepoPullRequestsView(RepoAppView, self.register_comments_vars(c, pull_request_latest, versions, include_drafts=False) all_comments = c.inline_comments_flat + c.comments - existing_ids = self._get_existing_ids(self.request.POST) + existing_ids = self.get_comment_ids(self.request.POST) return _render('comments_table', all_comments, len(all_comments), existing_ids=existing_ids) @@ -1064,7 +1065,7 @@ class RepoPullRequestsView(RepoAppView, .get_pull_request_resolved_todos(pull_request, include_drafts=False) all_comments = c.unresolved_comments + c.resolved_comments - existing_ids = self._get_existing_ids(self.request.POST) + existing_ids = self.get_comment_ids(self.request.POST) return _render('comments_table', all_comments, len(c.unresolved_comments), todo_comments=True, existing_ids=existing_ids) @@ -1518,6 +1519,150 @@ class RepoPullRequestsView(RepoAppView, self._rhodecode_user) raise HTTPNotFound() + def _pull_request_comments_create(self, pull_request, comments): + _ = self.request.translate + data = {} + pull_request_id = pull_request.pull_request_id + if not comments: + return + + all_drafts = len([x for x in comments if str2bool(x['is_draft'])]) == len(comments) + + for entry in comments: + c = self.load_default_context() + comment_type = entry['comment_type'] + text = entry['text'] + status = entry['status'] + is_draft = str2bool(entry['is_draft']) + resolves_comment_id = entry['resolves_comment_id'] + close_pull_request = entry['close_pull_request'] + f_path = entry['f_path'] + line_no = entry['line'] + target_elem_id = 'file-{}'.format(h.safeid(h.safe_unicode(f_path))) + + # the logic here should work like following, if we submit close + # pr comment, use `close_pull_request_with_comment` function + # else handle regular comment logic + + if close_pull_request: + # only owner or admin or person with write permissions + allowed_to_close = PullRequestModel().check_user_update( + pull_request, self._rhodecode_user) + if not allowed_to_close: + log.debug('comment: forbidden because not allowed to close ' + 'pull request %s', pull_request_id) + raise HTTPForbidden() + + # This also triggers `review_status_change` + comment, status = PullRequestModel().close_pull_request_with_comment( + pull_request, self._rhodecode_user, self.db_repo, message=text, + auth_user=self._rhodecode_user) + Session().flush() + is_inline = comment.is_inline + + PullRequestModel().trigger_pull_request_hook( + pull_request, self._rhodecode_user, 'comment', + data={'comment': comment}) + + else: + # regular comment case, could be inline, or one with status. + # for that one we check also permissions + # Additionally ENSURE if somehow draft is sent we're then unable to change status + allowed_to_change_status = PullRequestModel().check_user_change_status( + pull_request, self._rhodecode_user) and not is_draft + + if status and allowed_to_change_status: + message = (_('Status change %(transition_icon)s %(status)s') + % {'transition_icon': '>', + 'status': ChangesetStatus.get_status_lbl(status)}) + text = text or message + + comment = CommentsModel().create( + text=text, + repo=self.db_repo.repo_id, + user=self._rhodecode_user.user_id, + pull_request=pull_request, + f_path=f_path, + line_no=line_no, + status_change=(ChangesetStatus.get_status_lbl(status) + if status and allowed_to_change_status else None), + status_change_type=(status + if status and allowed_to_change_status else None), + comment_type=comment_type, + is_draft=is_draft, + resolves_comment_id=resolves_comment_id, + auth_user=self._rhodecode_user, + send_email=not is_draft, # skip notification for draft comments + ) + is_inline = comment.is_inline + + if allowed_to_change_status: + # calculate old status before we change it + old_calculated_status = pull_request.calculated_review_status() + + # get status if set ! + if status: + ChangesetStatusModel().set_status( + self.db_repo.repo_id, + status, + self._rhodecode_user.user_id, + comment, + pull_request=pull_request + ) + + Session().flush() + # this is somehow required to get access to some relationship + # loaded on comment + Session().refresh(comment) + + PullRequestModel().trigger_pull_request_hook( + pull_request, self._rhodecode_user, 'comment', + data={'comment': comment}) + + # we now calculate the status of pull request, and based on that + # calculation we set the commits status + calculated_status = pull_request.calculated_review_status() + if old_calculated_status != calculated_status: + PullRequestModel().trigger_pull_request_hook( + pull_request, self._rhodecode_user, 'review_status_change', + data={'status': calculated_status}) + + comment_id = comment.comment_id + data[comment_id] = { + 'target_id': target_elem_id + } + Session().flush() + + c.co = comment + c.at_version_num = None + c.is_new = True + rendered_comment = render( + 'rhodecode:templates/changeset/changeset_comment_block.mako', + self._get_template_context(c), self.request) + + data[comment_id].update(comment.get_dict()) + data[comment_id].update({'rendered_text': rendered_comment}) + + Session().commit() + + # skip channelstream for draft comments + if all_drafts: + comment_broadcast_channel = channelstream.comment_channel( + self.db_repo_name, pull_request_obj=pull_request) + + comment_data = data + comment_type = 'inline' if is_inline else 'general' + if len(data) == 1: + msg = _('posted {} new {} comment').format(len(data), comment_type) + else: + msg = _('posted {} new {} comments').format(len(data), comment_type) + + channelstream.comment_channelstream_push( + self.request, comment_broadcast_channel, self._rhodecode_user, msg, + comment_data=comment_data) + + return data + @LoginRequired() @NotAnonymous() @HasRepoPermissionAnyDecorator( @@ -1529,9 +1674,7 @@ class RepoPullRequestsView(RepoAppView, def pull_request_comment_create(self): _ = self.request.translate - pull_request = PullRequest.get_or_404( - self.request.matchdict['pull_request_id']) - pull_request_id = pull_request.pull_request_id + pull_request = PullRequest.get_or_404(self.request.matchdict['pull_request_id']) if pull_request.is_closed(): log.debug('comment: forbidden because pull request is closed') @@ -1543,130 +1686,17 @@ class RepoPullRequestsView(RepoAppView, log.debug('comment: forbidden because pull request is from forbidden repo') raise HTTPForbidden() - c = self.load_default_context() - - status = self.request.POST.get('changeset_status', None) - text = self.request.POST.get('text') - comment_type = self.request.POST.get('comment_type') - is_draft = str2bool(self.request.POST.get('draft')) - resolves_comment_id = self.request.POST.get('resolves_comment_id', None) - close_pull_request = self.request.POST.get('close_pull_request') - - # the logic here should work like following, if we submit close - # pr comment, use `close_pull_request_with_comment` function - # else handle regular comment logic - - if close_pull_request: - # only owner or admin or person with write permissions - allowed_to_close = PullRequestModel().check_user_update( - pull_request, self._rhodecode_user) - if not allowed_to_close: - log.debug('comment: forbidden because not allowed to close ' - 'pull request %s', pull_request_id) - raise HTTPForbidden() - - # This also triggers `review_status_change` - comment, status = PullRequestModel().close_pull_request_with_comment( - pull_request, self._rhodecode_user, self.db_repo, message=text, - auth_user=self._rhodecode_user) - Session().flush() - is_inline = comment.is_inline - - PullRequestModel().trigger_pull_request_hook( - pull_request, self._rhodecode_user, 'comment', - data={'comment': comment}) - - else: - # regular comment case, could be inline, or one with status. - # for that one we check also permissions - # Additionally ENSURE if somehow draft is sent we're then unable to change status - allowed_to_change_status = PullRequestModel().check_user_change_status( - pull_request, self._rhodecode_user) and not is_draft - - if status and allowed_to_change_status: - message = (_('Status change %(transition_icon)s %(status)s') - % {'transition_icon': '>', - 'status': ChangesetStatus.get_status_lbl(status)}) - text = text or message - - comment = CommentsModel().create( - text=text, - repo=self.db_repo.repo_id, - user=self._rhodecode_user.user_id, - pull_request=pull_request, - f_path=self.request.POST.get('f_path'), - line_no=self.request.POST.get('line'), - status_change=(ChangesetStatus.get_status_lbl(status) - if status and allowed_to_change_status else None), - status_change_type=(status - if status and allowed_to_change_status else None), - comment_type=comment_type, - is_draft=is_draft, - resolves_comment_id=resolves_comment_id, - auth_user=self._rhodecode_user, - send_email=not is_draft, # skip notification for draft comments - ) - is_inline = comment.is_inline - - if allowed_to_change_status: - # calculate old status before we change it - old_calculated_status = pull_request.calculated_review_status() - - # get status if set ! - if status: - ChangesetStatusModel().set_status( - self.db_repo.repo_id, - status, - self._rhodecode_user.user_id, - comment, - pull_request=pull_request - ) - - Session().flush() - # this is somehow required to get access to some relationship - # loaded on comment - Session().refresh(comment) - - PullRequestModel().trigger_pull_request_hook( - pull_request, self._rhodecode_user, 'comment', - data={'comment': comment}) - - # we now calculate the status of pull request, and based on that - # calculation we set the commits status - calculated_status = pull_request.calculated_review_status() - if old_calculated_status != calculated_status: - PullRequestModel().trigger_pull_request_hook( - pull_request, self._rhodecode_user, 'review_status_change', - data={'status': calculated_status}) - - Session().commit() - - data = { - 'target_id': h.safeid(h.safe_unicode( - self.request.POST.get('f_path'))), + comment_data = { + 'comment_type': self.request.POST.get('comment_type'), + 'text': self.request.POST.get('text'), + 'status': self.request.POST.get('changeset_status', None), + 'is_draft': self.request.POST.get('draft'), + 'resolves_comment_id': self.request.POST.get('resolves_comment_id', None), + 'close_pull_request': self.request.POST.get('close_pull_request'), + 'f_path': self.request.POST.get('f_path'), + 'line': self.request.POST.get('line'), } - - if comment: - c.co = comment - c.at_version_num = None - rendered_comment = render( - 'rhodecode:templates/changeset/changeset_comment_block.mako', - self._get_template_context(c), self.request) - - data.update(comment.get_dict()) - data.update({'rendered_text': rendered_comment}) - - # skip channelstream for draft comments - if not is_draft: - comment_broadcast_channel = channelstream.comment_channel( - self.db_repo_name, pull_request_obj=pull_request) - - comment_data = data - comment_type = 'inline' if is_inline else 'general' - channelstream.comment_channelstream_push( - self.request, comment_broadcast_channel, self._rhodecode_user, - _('posted a new {} comment').format(comment_type), - comment_data=comment_data) + data = self._pull_request_comments_create(pull_request, [comment_data]) return data diff --git a/rhodecode/lib/channelstream.py b/rhodecode/lib/channelstream.py --- a/rhodecode/lib/channelstream.py +++ b/rhodecode/lib/channelstream.py @@ -339,13 +339,12 @@ def comment_channelstream_push(request, comment_data = kwargs.pop('comment_data', {}) user_data = kwargs.pop('user_data', {}) - comment_id = comment_data.get('comment_id') + comment_id = comment_data.keys()[0] if comment_data else '' - message = '{} {} #{}, {}'.format( + message = '{} {} #{}'.format( user.username, msg, comment_id, - _reload_link(_('Reload page to see new comments')), ) message_obj = { diff --git a/rhodecode/lib/diffs.py b/rhodecode/lib/diffs.py --- a/rhodecode/lib/diffs.py +++ b/rhodecode/lib/diffs.py @@ -1148,7 +1148,7 @@ class DiffLimitExceeded(Exception): # NOTE(marcink): if diffs.mako change, probably this # needs a bump to next version -CURRENT_DIFF_VERSION = 'v4' +CURRENT_DIFF_VERSION = 'v5' def _cleanup_cache_file(cached_diff_file): diff --git a/rhodecode/public/css/buttons.less b/rhodecode/public/css/buttons.less --- a/rhodecode/public/css/buttons.less +++ b/rhodecode/public/css/buttons.less @@ -370,7 +370,7 @@ input[type="button"] { color: @alert2; &:hover { - color: darken(@alert2,30%); + color: darken(@alert2, 30%); } &:disabled { diff --git a/rhodecode/public/css/code-block.less b/rhodecode/public/css/code-block.less --- a/rhodecode/public/css/code-block.less +++ b/rhodecode/public/css/code-block.less @@ -1002,7 +1002,7 @@ input.filediff-collapse-state { .nav-chunk { position: absolute; right: 20px; - margin-top: -17px; + margin-top: -15px; } .nav-chunk.selected { diff --git a/rhodecode/public/css/comments.less b/rhodecode/public/css/comments.less --- a/rhodecode/public/css/comments.less +++ b/rhodecode/public/css/comments.less @@ -4,7 +4,7 @@ // Comments -@comment-outdated-opacity: 0.6; +@comment-outdated-opacity: 1.0; .comments { width: 100%; @@ -64,32 +64,34 @@ tr.inline-comments div { .comment-draft { float: left; margin-right: 10px; - font-weight: 600; - color: @alert3; + font-weight: 400; + color: @color-draft; +} + +.comment-new { + float: left; + margin-right: 10px; + font-weight: 400; + color: @color-new; } .comment-label { float: left; - padding: 0.4em 0.4em; - margin: 2px 4px 0px 0px; - display: inline-block; + padding: 0 8px 0 0; min-height: 0; text-align: center; font-size: 10px; - line-height: .8em; font-family: @text-italic; font-style: italic; background: #fff none; color: @grey3; - border: 1px solid @grey4; white-space: nowrap; text-transform: uppercase; min-width: 50px; - border-radius: 4px; &.todo { color: @color5; @@ -277,64 +279,165 @@ tr.inline-comments div { .comment-outdated { opacity: @comment-outdated-opacity; } + + .comment-outdated-label { + color: @grey3; + padding-right: 4px; + } } .inline-comments { - border-radius: @border-radius; + .comment { margin: 0; - border-radius: @border-radius; } + .comment-outdated { opacity: @comment-outdated-opacity; } + .comment-outdated-label { + color: @grey3; + padding-right: 4px; + } + .comment-inline { + + &:first-child { + margin: 4px 4px 0 4px; + border-top: 1px solid @grey5; + border-bottom: 0 solid @grey5; + border-left: 1px solid @grey5; + border-right: 1px solid @grey5; + .border-radius-top(4px); + } + + &:only-child { + margin: 4px 4px 0 4px; + border-top: 1px solid @grey5; + border-bottom: 0 solid @grey5; + border-left: 1px solid @grey5; + border-right: 1px solid @grey5; + .border-radius-top(4px); + } + background: white; padding: @comment-padding @comment-padding; - border: @comment-padding solid @grey6; + margin: 0 4px 0 4px; + border-top: 0 solid @grey5; + border-bottom: 0 solid @grey5; + border-left: 1px solid @grey5; + border-right: 1px solid @grey5; .text { border: none; } + .meta { border-bottom: 1px solid @grey6; margin: -5px 0px; line-height: 24px; } + } .comment-selected { border-left: 6px solid @comment-highlight-color; } + + .comment-inline-form-open { + display: block !important; + } + .comment-inline-form { - padding: @comment-padding; display: none; } - .cb-comment-add-button { - margin: @comment-padding; + + .comment-inline-form-edit { + padding: 0; + margin: 0px 4px 2px 4px; + } + + .reply-thread-container { + display: table; + width: 100%; + padding: 0px 4px 4px 4px; + } + + .reply-thread-container-wrapper { + margin: 0 4px 4px 4px; + border-top: 0 solid @grey5; + border-bottom: 1px solid @grey5; + border-left: 1px solid @grey5; + border-right: 1px solid @grey5; + .border-radius-bottom(4px); + } + + .reply-thread-gravatar { + display: table-cell; + width: 24px; + height: 24px; + padding-top: 10px; + padding-left: 10px; + background-color: #eeeeee; + vertical-align: top; } - /* hide add comment button when form is open */ + + .reply-thread-reply-button { + display: table-cell; + width: 100%; + height: 33px; + padding: 3px 8px; + margin-left: 8px; + background-color: #eeeeee; + } + + .reply-thread-reply-button .cb-comment-add-button { + border-radius: 4px; + width: 100%; + padding: 6px 2px; + text-align: left; + cursor: text; + color: @grey3; + } + .reply-thread-reply-button .cb-comment-add-button:hover { + background-color: white; + color: @grey2; + } + + .reply-thread-last { + display: table-cell; + width: 10px; + } + + /* Hide reply box when it's a first element, + can happen when drafts are saved but not shown to specific user, + or there are outdated comments hidden + */ + .reply-thread-container-wrapper:first-child:not(.comment-form-active) { + display: none; + } + + .reply-thread-container-wrapper.comment-outdated { + display: none + } + + /* hide add comment button when form is open */ .comment-inline-form-open ~ .cb-comment-add-button { display: none; } - .comment-inline-form-open { - display: block; - } - /* hide add comment button when form but no comments */ - .comment-inline-form:first-child + .cb-comment-add-button { - display: none; - } - /* hide add comment button when no comments or form */ - .cb-comment-add-button:first-child { - display: none; - } + /* hide add comment button when only comment is being deleted */ .comment-deleting:first-child + .cb-comment-add-button { display: none; } + + /* hide add comment button when form but no comments */ + .comment-inline-form:first-child + .cb-comment-add-button { + display: none; + } + } - .show-outdated-comments { display: inline; color: @rcblue; @@ -387,23 +490,40 @@ form.comment-form { } .comment-footer { - position: relative; + display: table; width: 100%; - min-height: 42px; + height: 42px; - .status_box, + .comment-status-box, .cancel-button { - float: left; display: inline-block; } - .status_box { + .comment-status-box { margin-left: 10px; } .action-buttons { - float: left; - display: inline-block; + display: table-cell; + padding: 5px 0 5px 2px; + } + + .toolbar-text { + height: 42px; + display: table-cell; + vertical-align: bottom; + font-size: 11px; + color: @grey4; + text-align: right; + + a { + color: @grey4; + } + + p { + padding: 0; + margin: 0; + } } .action-buttons-extra { @@ -434,10 +554,6 @@ form.comment-form { margin-right: 0; } - .comment-footer { - margin-bottom: 50px; - margin-top: 10px; - } } @@ -489,8 +605,8 @@ form.comment-form { .injected_diff .comment-inline-form, .comment-inline-form { background-color: white; - margin-top: 10px; - margin-bottom: 20px; + margin-top: 4px; + margin-bottom: 10px; } .inline-form { @@ -526,9 +642,6 @@ form.comment-form { margin: 0px; } -.comment-inline-form .comment-footer { - margin: 10px 0px 0px 0px; -} .hide-inline-form-button { margin-left: 5px; @@ -554,6 +667,7 @@ comment-area-text { .comment-area-header { height: 35px; + border-bottom: 1px solid @grey5; } .comment-area-header .nav-links { @@ -561,6 +675,7 @@ comment-area-text { flex-flow: row wrap; -webkit-flex-flow: row wrap; width: 100%; + border: none; } .comment-area-footer { @@ -629,14 +744,3 @@ comment-area-text { border-bottom: 2px solid transparent; } -.toolbar-text { - float: right; - font-size: 11px; - color: @grey4; - text-align: right; - - a { - color: @grey4; - } -} - diff --git a/rhodecode/public/css/main.less b/rhodecode/public/css/main.less --- a/rhodecode/public/css/main.less +++ b/rhodecode/public/css/main.less @@ -3212,7 +3212,12 @@ details:not([open]) > :not(summary) { .sidebar-element { margin-top: 20px; -} + + .icon-draft { + color: @color-draft + } +} + .right-sidebar-collapsed-state { display: flex; @@ -3235,5 +3240,4 @@ details:not([open]) > :not(summary) { .old-comments-marker td { padding-top: 15px; - border-bottom: 1px solid @grey5; -} +} diff --git a/rhodecode/public/css/variables.less b/rhodecode/public/css/variables.less --- a/rhodecode/public/css/variables.less +++ b/rhodecode/public/css/variables.less @@ -47,6 +47,8 @@ // Highlight color for lines and colors @comment-highlight-color: #ffd887; +@color-draft: darken(@alert3, 30%); +@color-new: darken(@alert1, 5%); // FONTS @basefontsize: 13px; diff --git a/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js --- a/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js +++ b/rhodecode/public/js/src/components/rhodecode-app/rhodecode-app.js @@ -71,14 +71,20 @@ export class RhodecodeApp extends Polyme if (elem) { elem.handleNotification(data); } - } handleComment(data) { - if (data.message.comment_id) { + + if (data.message.comment_data.length !== 0) { if (window.refreshAllComments !== undefined) { refreshAllComments() } + var json_data = data.message.comment_data; + + if (window.commentsController !== undefined) { + + window.commentsController.attachComment(json_data) + } } } diff --git a/rhodecode/public/js/src/rhodecode/comments.js b/rhodecode/public/js/src/rhodecode/comments.js --- a/rhodecode/public/js/src/rhodecode/comments.js +++ b/rhodecode/public/js/src/rhodecode/comments.js @@ -364,12 +364,15 @@ var _submitAjaxPOST = function(url, post postData['close_pull_request'] = true; } - var submitSuccessCallback = function(o) { + // submitSuccess for general comments + var submitSuccessCallback = function(json_data) { // reload page if we change status for single commit. if (status && self.commitId) { location.reload(true); } else { - $('#injected_page_comments').append(o.rendered_text); + // inject newly created comments, json_data is {: {}} + self.attachGeneralComment(json_data) + self.resetCommentFormState(); timeagoActivate(); tooltipActivate(); @@ -565,26 +568,6 @@ var CommentsController = function() { var mainComment = '#text'; var self = this; - this.cancelComment = function (node) { - var $node = $(node); - var edit = $(this).attr('edit'); - if (edit) { - var $general_comments = null; - var $inline_comments = $node.closest('div.inline-comments'); - if (!$inline_comments.length) { - $general_comments = $('#comments'); - var $comment = $general_comments.parent().find('div.comment:hidden'); - // show hidden general comment form - $('#cb-comment-general-form-placeholder').show(); - } else { - var $comment = $inline_comments.find('div.comment:hidden'); - } - $comment.show(); - } - $node.closest('.comment-inline-form').remove(); - return false; - }; - this.showVersion = function (comment_id, comment_history_id) { var historyViewUrl = pyroutes.url( @@ -682,6 +665,35 @@ var CommentsController = function() { return self.scrollToComment(node, -1, true); }; + this.cancelComment = function (node) { + var $node = $(node); + var edit = $(this).attr('edit'); + var $inlineComments = $node.closest('div.inline-comments'); + + if (edit) { + var $general_comments = null; + if (!$inlineComments.length) { + $general_comments = $('#comments'); + var $comment = $general_comments.parent().find('div.comment:hidden'); + // show hidden general comment form + $('#cb-comment-general-form-placeholder').show(); + } else { + var $comment = $inlineComments.find('div.comment:hidden'); + } + $comment.show(); + } + var $replyWrapper = $node.closest('.comment-inline-form').closest('.reply-thread-container-wrapper') + $replyWrapper.removeClass('comment-form-active'); + + var lastComment = $inlineComments.find('.comment-inline').last(); + if ($(lastComment).hasClass('comment-outdated')) { + $replyWrapper.hide(); + } + + $node.closest('.comment-inline-form').remove(); + return false; + }; + this._deleteComment = function(node) { var $node = $(node); var $td = $node.closest('td'); @@ -751,7 +763,7 @@ var CommentsController = function() { this.finalizeDrafts = function(commentIds) { SwalNoAnimation.fire({ - title: _ngettext('Submit {0} draft comment', 'Submit {0} draft comments', commentIds.length).format(commentIds.length), + title: _ngettext('Submit {0} draft comment.', 'Submit {0} draft comments.', commentIds.length).format(commentIds.length), icon: 'warning', showCancelButton: true, confirmButtonText: _gettext('Yes, finalize drafts'), @@ -764,6 +776,7 @@ var CommentsController = function() { }; this.toggleWideMode = function (node) { + if ($('#content').hasClass('wrapper')) { $('#content').removeClass("wrapper"); $('#content').addClass("wide-mode-wrapper"); @@ -778,16 +791,49 @@ var CommentsController = function() { }; - this.toggleComments = function(node, show) { + /** + * Turn off/on all comments in file diff + */ + this.toggleDiffComments = function(node) { + // Find closes filediff container var $filediff = $(node).closest('.filediff'); + if ($(node).hasClass('toggle-on')) { + var show = false; + } else if ($(node).hasClass('toggle-off')) { + var show = true; + } + + // Toggle each individual comment block, so we can un-toggle single ones + $.each($filediff.find('.toggle-comment-action'), function(idx, val) { + self.toggleLineComments($(val), show) + }) + + // since we change the height of the diff container that has anchor points for upper + // sticky header, we need to tell it to re-calculate those + if (window.updateSticky !== undefined) { + // potentially our comments change the active window size, so we + // notify sticky elements + updateSticky() + } + + return false; + } + + this.toggleLineComments = function(node, show) { + + var trElem = $(node).closest('tr') + if (show === true) { - $filediff.removeClass('hide-comments'); + // mark outdated comments as visible before the toggle; + $(trElem).find('.comment-outdated').show(); + $(trElem).removeClass('hide-line-comments'); } else if (show === false) { - $filediff.find('.hide-line-comments').removeClass('hide-line-comments'); - $filediff.addClass('hide-comments'); + $(trElem).find('.comment-outdated').hide(); + $(trElem).addClass('hide-line-comments'); } else { - $filediff.find('.hide-line-comments').removeClass('hide-line-comments'); - $filediff.toggleClass('hide-comments'); + // mark outdated comments as visible before the toggle; + $(trElem).find('.comment-outdated').show(); + $(trElem).toggleClass('hide-line-comments'); } // since we change the height of the diff container that has anchor points for upper @@ -798,15 +844,6 @@ var CommentsController = function() { updateSticky() } - return false; - }; - - this.toggleLineComments = function(node) { - self.toggleComments(node, true); - var $node = $(node); - // mark outdated comments as visible before the toggle; - $(node.closest('tr')).find('.comment-outdated').show(); - $node.closest('tr').toggleClass('hide-line-comments'); }; this.createCommentForm = function(formElement, lineno, placeholderText, initAutocompleteActions, resolvesCommentId, edit, comment_id){ @@ -960,64 +997,58 @@ var CommentsController = function() { return commentForm; }; - this.editComment = function(node) { + this.editComment = function(node, line_no, f_path) { + self.edit = true; var $node = $(node); + var $td = $node.closest('td'); + var $comment = $(node).closest('.comment'); var comment_id = $($comment).data('commentId'); var isDraft = $($comment).data('commentDraft'); - var $form = null + var $editForm = null var $comments = $node.closest('div.inline-comments'); var $general_comments = null; - var lineno = null; if($comments.length){ // inline comments setup - $form = $comments.find('.comment-inline-form'); - lineno = self.getLineNumber(node) + $editForm = $comments.find('.comment-inline-form'); + line_no = self.getLineNumber(node) } else{ // general comments setup $comments = $('#comments'); - $form = $comments.find('.comment-inline-form'); - lineno = $comment[0].id + $editForm = $comments.find('.comment-inline-form'); + line_no = $comment[0].id $('#cb-comment-general-form-placeholder').hide(); } - this.edit = true; + if ($editForm.length === 0) { - if (!$form.length) { - + // unhide all comments if they are hidden for a proper REPLY mode var $filediff = $node.closest('.filediff'); $filediff.removeClass('hide-comments'); - var f_path = $filediff.attr('data-f-path'); - - // create a new HTML from template - var tmpl = $('#cb-comment-inline-form-template').html(); - tmpl = tmpl.format(escapeHtml(f_path), lineno); - $form = $(tmpl); - $comment.after($form) + $editForm = self.createNewFormWrapper(f_path, line_no); + if(f_path && line_no) { + $editForm.addClass('comment-inline-form-edit') + } - var _form = $($form[0]).find('form'); + $comment.after($editForm) + + var _form = $($editForm[0]).find('form'); var autocompleteActions = ['as_note',]; var commentForm = this.createCommentForm( - _form, lineno, '', autocompleteActions, resolvesCommentId, + _form, line_no, '', autocompleteActions, resolvesCommentId, this.edit, comment_id); var old_comment_text_binary = $comment.attr('data-comment-text'); var old_comment_text = b64DecodeUnicode(old_comment_text_binary); commentForm.cm.setValue(old_comment_text); $comment.hide(); + tooltipActivate(); - $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({ - form: _form, - parent: $comments, - lineno: lineno, - f_path: f_path} - ); - - // set a CUSTOM submit handler for inline comments. - commentForm.setHandleFormSubmit(function(o) { + // set a CUSTOM submit handler for inline comment edit action. + commentForm.setHandleFormSubmit(function(o) { var text = commentForm.cm.getValue(); var commentType = commentForm.getCommentType(); @@ -1048,7 +1079,7 @@ var CommentsController = function() { var postData = { 'text': text, 'f_path': f_path, - 'line': lineno, + 'line': line_no, 'comment_type': commentType, 'draft': isDraft, 'version': version, @@ -1056,7 +1087,7 @@ var CommentsController = function() { }; var submitSuccessCallback = function(json_data) { - $form.remove(); + $editForm.remove(); $comment.show(); var postData = { 'text': text, @@ -1121,8 +1152,7 @@ var CommentsController = function() { 'commit_id': templateContext.commit_data.commit_id}); _submitAjaxPOST( - previewUrl, postData, successRenderCommit, - failRenderCommit + previewUrl, postData, successRenderCommit, failRenderCommit ); try { @@ -1178,49 +1208,103 @@ var CommentsController = function() { }); } - $form.addClass('comment-inline-form-open'); + $editForm.addClass('comment-inline-form-open'); }; - this.createComment = function(node, resolutionComment) { - var resolvesCommentId = resolutionComment || null; + this.attachComment = function(json_data) { + var self = this; + $.each(json_data, function(idx, val) { + var json_data_elem = [val] + var isInline = val.comment_f_path && val.comment_lineno + + if (isInline) { + self.attachInlineComment(json_data_elem) + } else { + self.attachGeneralComment(json_data_elem) + } + }) + + } + + this.attachGeneralComment = function(json_data) { + $.each(json_data, function(idx, val) { + $('#injected_page_comments').append(val.rendered_text); + }) + } + + this.attachInlineComment = function(json_data) { + + $.each(json_data, function (idx, val) { + var line_qry = '*[data-line-no="{0}"]'.format(val.line_no); + var html = val.rendered_text; + var $inlineComments = $('#' + val.target_id) + .find(line_qry) + .find('.inline-comments'); + + var lastComment = $inlineComments.find('.comment-inline').last(); + + if (lastComment.length === 0) { + // first comment, we append simply + $inlineComments.find('.reply-thread-container-wrapper').before(html); + } else { + $(lastComment).after(html) + } + + }) + + }; + + this.createNewFormWrapper = function(f_path, line_no) { + // create a new reply HTML form from template + var tmpl = $('#cb-comment-inline-form-template').html(); + tmpl = tmpl.format(escapeHtml(f_path), line_no); + return $(tmpl); + } + + this.createComment = function(node, f_path, line_no, resolutionComment) { + self.edit = false; var $node = $(node); var $td = $node.closest('td'); - var $form = $td.find('.comment-inline-form'); - this.edit = false; + var resolvesCommentId = resolutionComment || null; - if (!$form.length) { + var $replyForm = $td.find('.comment-inline-form'); - var $filediff = $node.closest('.filediff'); - $filediff.removeClass('hide-comments'); - var f_path = $filediff.attr('data-f-path'); - var lineno = self.getLineNumber(node); - // create a new HTML from template - var tmpl = $('#cb-comment-inline-form-template').html(); - tmpl = tmpl.format(escapeHtml(f_path), lineno); - $form = $(tmpl); + // if form isn't existing, we're generating a new one and injecting it. + if ($replyForm.length === 0) { + + // unhide/expand all comments if they are hidden for a proper REPLY mode + self.toggleLineComments($node, true); + + $replyForm = self.createNewFormWrapper(f_path, line_no); var $comments = $td.find('.inline-comments'); - if (!$comments.length) { - $comments = $( - $('#cb-comments-inline-container-template').html()); - $td.append($comments); + + // There aren't any comments, we init the `.inline-comments` with `reply-thread-container` first + if ($comments.length===0) { + var replBtn = ''.format(f_path, line_no) + var $reply_container = $('#cb-comments-inline-container-template') + $reply_container.find('button.cb-comment-add-button').replaceWith(replBtn); + $td.append($($reply_container).html()); } - $td.find('.cb-comment-add-button').before($form); + // default comment button exists, so we prepend the form for leaving initial comment + $td.find('.cb-comment-add-button').before($replyForm); + // set marker, that we have a open form + var $replyWrapper = $td.find('.reply-thread-container-wrapper') + $replyWrapper.addClass('comment-form-active'); - var placeholderText = _gettext('Leave a comment on line {0}.').format(lineno); - var _form = $($form[0]).find('form'); + var lastComment = $comments.find('.comment-inline').last(); + if ($(lastComment).hasClass('comment-outdated')) { + $replyWrapper.show(); + } + + var _form = $($replyForm[0]).find('form'); var autocompleteActions = ['as_note', 'as_todo']; var comment_id=null; - var commentForm = this.createCommentForm( - _form, lineno, placeholderText, autocompleteActions, resolvesCommentId, this.edit, comment_id); - - $.Topic('/ui/plugins/code/comment_form_built').prepareOrPublish({ - form: _form, - parent: $td[0], - lineno: lineno, - f_path: f_path} - ); + var placeholderText = _gettext('Leave a comment on file {0} line {1}.').format(f_path, line_no); + var commentForm = self.createCommentForm( + _form, line_no, placeholderText, autocompleteActions, resolvesCommentId, + self.edit, comment_id); // set a CUSTOM submit handler for inline comments. commentForm.setHandleFormSubmit(function(o) { @@ -1233,12 +1317,13 @@ var CommentsController = function() { return; } - if (lineno === undefined) { - alert('missing line !'); + if (line_no === undefined) { + alert('Error: unable to fetch line number for this inline comment !'); return; } + if (f_path === undefined) { - alert('missing file path !'); + alert('Error: unable to fetch file path for this inline comment !'); return; } @@ -1249,7 +1334,7 @@ var CommentsController = function() { var postData = { 'text': text, 'f_path': f_path, - 'line': lineno, + 'line': line_no, 'comment_type': commentType, 'draft': isDraft, 'csrf_token': CSRF_TOKEN @@ -1258,32 +1343,32 @@ var CommentsController = function() { postData['resolves_comment_id'] = resolvesCommentId; } + // submitSuccess for inline commits var submitSuccessCallback = function(json_data) { - $form.remove(); - try { - var html = json_data.rendered_text; - var lineno = json_data.line_no; - var target_id = json_data.target_id; + + $replyForm.remove(); + $td.find('.reply-thread-container-wrapper').removeClass('comment-form-active'); + + try { + + // inject newly created comments, json_data is {: {}} + self.attachInlineComment(json_data) - $comments.find('.cb-comment-add-button').before(html); + //mark visually which comment was resolved + if (resolvesCommentId) { + commentForm.markCommentResolved(resolvesCommentId); + } - //mark visually which comment was resolved - if (resolvesCommentId) { - commentForm.markCommentResolved(resolvesCommentId); + // run global callback on submit + commentForm.globalSubmitSuccessCallback({ + draft: isDraft, + comment_id: comment_id + }); + + } catch (e) { + console.error(e); } - // run global callback on submit - commentForm.globalSubmitSuccessCallback({draft: isDraft, comment_id: comment_id}); - - } catch (e) { - console.error(e); - } - - // re trigger the linkification of next/prev navigation - linkifyComments($('.inline-comment-injected')); - timeagoActivate(); - tooltipActivate(); - if (window.updateSticky !== undefined) { // potentially our comments change the active window size, so we // notify sticky elements @@ -1297,19 +1382,27 @@ var CommentsController = function() { commentForm.setActionButtonsDisabled(false); + // re trigger the linkification of next/prev navigation + linkifyComments($('.inline-comment-injected')); + timeagoActivate(); + tooltipActivate(); }; + var submitFailCallback = function(jqXHR, textStatus, errorThrown) { var prefix = "Error while submitting comment.\n" var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); ajaxErrorSwal(message); commentForm.resetCommentFormState(text) }; + commentForm.submitAjaxPOST( commentForm.submitUrl, postData, submitSuccessCallback, submitFailCallback); }); } - $form.addClass('comment-inline-form-open'); + // Finally "open" our reply form, since we know there are comments and we have the "attached" old form + $replyForm.addClass('comment-inline-form-open'); + tooltipActivate(); }; this.createResolutionComment = function(commentId){ @@ -1319,9 +1412,12 @@ var CommentsController = function() { var comment = $('#comment-'+commentId); var commentData = comment.data(); if (commentData.commentInline) { - this.createComment(comment, commentId) + var f_path = commentData.fPath; + var line_no = commentData.lineNo; + //TODO check this if we need to give f_path/line_no + this.createComment(comment, f_path, line_no, commentId) } else { - Rhodecode.comments.createGeneralComment('general', "$placeholder", commentId) + this.createGeneralComment('general', "$placeholder", commentId) } return false; @@ -1347,3 +1443,8 @@ var CommentsController = function() { }; }; + +window.commentHelp = function(renderer) { + var funcData = {'renderer': renderer} + return renderTemplate('commentHelpHovercard', funcData) +} \ No newline at end of file diff --git a/rhodecode/public/js/src/rhodecode/menus.js b/rhodecode/public/js/src/rhodecode/menus.js --- a/rhodecode/public/js/src/rhodecode/menus.js +++ b/rhodecode/public/js/src/rhodecode/menus.js @@ -42,12 +42,22 @@ window.toggleElement = function (elem, t var $elem = $(elem); var $target = $(target); - if ($target.is(':visible') || $target.length === 0) { + if (target !== undefined) { + var show = $target.is(':visible') || $target.length === 0; + } else { + var show = $elem.hasClass('toggle-off') + } + + if (show) { $target.hide(); $elem.html($elem.data('toggleOn')) + $elem.addClass('toggle-on') + $elem.removeClass('toggle-off') } else { $target.show(); $elem.html($elem.data('toggleOff')) + $elem.addClass('toggle-off') + $elem.removeClass('toggle-on') } return false diff --git a/rhodecode/public/js/topics_list.txt b/rhodecode/public/js/topics_list.txt --- a/rhodecode/public/js/topics_list.txt +++ b/rhodecode/public/js/topics_list.txt @@ -1,7 +1,5 @@ /__MAIN_APP__ - launched when rhodecode-app element is attached to DOM /plugins/__REGISTER__ - launched after the onDomReady() code from rhodecode.js is executed -/ui/plugins/code/anchor_focus - launched when rc starts to scroll on load to anchor on PR/Codeview -/ui/plugins/code/comment_form_built - launched when injectInlineForm() is executed and the form object is created /notifications - shows new event notifications /connection_controller/subscribe - subscribes user to new channels /connection_controller/presence - receives presence change messages diff --git a/rhodecode/templates/changeset/changeset_comment_block.mako b/rhodecode/templates/changeset/changeset_comment_block.mako --- a/rhodecode/templates/changeset/changeset_comment_block.mako +++ b/rhodecode/templates/changeset/changeset_comment_block.mako @@ -1,4 +1,4 @@ ## this is a dummy html file for partial rendering on server and sending ## generated output via ajax after comment submit <%namespace name="comment" file="/changeset/changeset_file_comment.mako"/> -${comment.comment_block(c.co, inline=c.co.is_inline)} +${comment.comment_block(c.co, inline=c.co.is_inline, is_new=c.is_new)} diff --git a/rhodecode/templates/changeset/changeset_file_comment.mako b/rhodecode/templates/changeset/changeset_file_comment.mako --- a/rhodecode/templates/changeset/changeset_file_comment.mako +++ b/rhodecode/templates/changeset/changeset_file_comment.mako @@ -10,7 +10,7 @@ %> -<%def name="comment_block(comment, inline=False, active_pattern_entries=None)"> +<%def name="comment_block(comment, inline=False, active_pattern_entries=None, is_new=False)"> <% from rhodecode.model.comment import CommentsModel @@ -40,6 +40,7 @@ data-comment-draft=${h.json.dumps(comment.draft)} data-comment-renderer="${comment.renderer}" data-comment-text="${comment.text | html_filters.base64,n}" + data-comment-f-path="${comment.f_path}" data-comment-line-no="${comment.line_no}" data-comment-inline=${h.json.dumps(inline)} style="${'display: none;' if outdated_at_ver else ''}"> @@ -47,8 +48,17 @@
% if comment.draft: -
DRAFT
+
+ DRAFT +
% endif + + % if is_new: +
+ NEW +
+ % endif +
## TODO COMMENT @@ -176,7 +186,7 @@ % if inline: % if outdated_at_ver: - outdated ${'v{}'.format(comment_ver)} + outdated ${'v{}'.format(comment_ver)} | % elif comment_ver: ${'v{}'.format(comment_ver)} @@ -222,12 +232,13 @@ %if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id): - % if comment.draft: + ## Only available in EE edition + % if comment.draft and c.rhodecode_edition_id == 'EE': @@ -391,7 +402,7 @@
-
+
${_('You need to be logged in to leave comments.')} ${_('Login now')}
@@ -450,7 +461,7 @@
-
+
- % if review_statuses: -
- -
- % endif -
<% renderer_url = '%s' % (h.route_url('%s_help' % c.visual.default_renderer), c.visual.default_renderer.upper()) %> - ${_('Comments parsed using {} syntax.').format(renderer_url)|n}
- @mention - ${_('and')} - `/` autocomplete - ${_('actions supported.')} +

${_('Styling with {} is supported.').format(renderer_url)|n} + + +

diff --git a/rhodecode/templates/codeblocks/diffs.mako b/rhodecode/templates/codeblocks/diffs.mako --- a/rhodecode/templates/codeblocks/diffs.mako +++ b/rhodecode/templates/codeblocks/diffs.mako @@ -1,3 +1,4 @@ +<%namespace name="base" file="/base/base.mako"/> <%namespace name="commentblock" file="/changeset/changeset_file_comment.mako"/> <%def name="diff_line_anchor(commit, filename, line, type)"><% @@ -74,24 +75,9 @@ return '%s_%s_%i' % (h.md5_safe(commit+f
- - %if c.rhodecode_user.username != h.DEFAULT_USER: + %if not c.rhodecode_user.is_default: ## render template for inline comments ${commentblock.comment_form(form_type='inline')} - %else: - ${h.form('', class_='inline-form comment-form-login', method='get')} -
-
- ${_('You need to be logged in to leave comments.')} ${_('Login now')} -
-
-
- -
-
- ${h.end_form()} %endif
@@ -327,7 +313,7 @@ return '%s_%s_%i' % (h.md5_safe(commit+f ${diff_menu(filediff, use_comments=use_comments)} - +
## new/deleted/empty content case % if not filediff.hunks: @@ -626,8 +612,10 @@ return '%s_%s_%i' % (h.md5_safe(commit+f % if use_comments: | - - ${_('Show comments')}${_('Hide comments')} + + ${_('Hide comments')} % endif @@ -637,23 +625,36 @@ return '%s_%s_%i' % (h.md5_safe(commit+f -<%def name="inline_comments_container(comments, active_pattern_entries=None)"> +<%def name="inline_comments_container(comments, active_pattern_entries=None, line_no='', f_path='')">
%for comment in comments: ${commentblock.comment_block(comment, inline=True, active_pattern_entries=active_pattern_entries)} %endfor - % if comments and comments[-1].outdated: - - % else: - - ${_('Add another comment')} - - % endif + + <% + extra_class = '' + extra_style = '' + + if comments and comments[-1].outdated: + extra_class = ' comment-outdated' + extra_style = 'display: none;' + %> +
+
+
+ ${base.gravatar(c.rhodecode_user.email, 20, tooltip=True, user=c.rhodecode_user)} +
+
+ ## initial reply button, some JS logic can append here a FORM to leave a first comment. + +
+
+
+
+ <%! @@ -721,9 +722,9 @@ def get_comments_for(diff_type, comments %endif %if line_old_comments_no_drafts: % if has_outdated: - + % else: - + % endif %endif @@ -737,16 +738,18 @@ def get_comments_for(diff_type, comments ${line.original.lineno} %endif + + <% line_no = 'o{}'.format(line.original.lineno) %> @@ -766,9 +769,9 @@ def get_comments_for(diff_type, comments %if line_new_comments_no_drafts: % if has_outdated: - + % else: - + % endif %endif @@ -783,22 +786,25 @@ def get_comments_for(diff_type, comments ${line.modified.lineno} %endif + + <% line_no = 'n{}'.format(line.modified.lineno) %> %endfor @@ -830,9 +836,9 @@ def get_comments_for(diff_type, comments % if comments_no_drafts: % if has_outdated: - + % else: - + % endif % endif @@ -857,15 +863,16 @@ def get_comments_for(diff_type, comments ${new_line_no} %endif + <% line_no = '{}{}'.format(new_line_no and 'n' or 'o', new_line_no or old_line_no) %> @@ -886,10 +893,12 @@ def get_comments_for(diff_type, comments file changes -<%def name="render_add_comment_button()"> - +% endif <%def name="render_diffset_menu(diffset, range_diff_on=None, commit=None, pull_request_menu=None)"> diff --git a/rhodecode/templates/data_table/_dt_elements.mako b/rhodecode/templates/data_table/_dt_elements.mako --- a/rhodecode/templates/data_table/_dt_elements.mako +++ b/rhodecode/templates/data_table/_dt_elements.mako @@ -456,7 +456,7 @@
-
+
diff --git a/rhodecode/templates/pullrequests/pullrequest_show.mako b/rhodecode/templates/pullrequests/pullrequest_show.mako --- a/rhodecode/templates/pullrequests/pullrequest_show.mako +++ b/rhodecode/templates/pullrequests/pullrequest_show.mako @@ -846,6 +846,7 @@ versionController.init(); reviewersController = new ReviewersController(); commitsController = new CommitsController(); +commentsController = new CommentsController(); updateController = new UpdatePrController(); @@ -1002,6 +1003,8 @@ window.setObserversData = ${c.pull_reque alert('okok !' + commentIds) } + // register globally so inject comment logic can re-use it. + window.commentsController = commentsController; })
%if use_comments and line.original.lineno: - ${render_add_comment_button()} + ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])} %endif ${line.original.content or '' | n} %if use_comments and line.original.lineno and line_old_comments: - ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries)} + ${inline_comments_container(line_old_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])} %endif %if use_comments and line.modified.lineno: - ${render_add_comment_button()} + ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])} %endif ${line.modified.content or '' | n} - %if use_comments and line.modified.lineno and line_new_comments: - ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries)} - %endif % if line_action in ['+', '-'] and prev_line_action not in ['+', '-']: <% chunk_count +=1 %> % endif + %if use_comments and line.modified.lineno and line_new_comments: + ${inline_comments_container(line_new_comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])} + %endif +
%if use_comments: - ${render_add_comment_button()} + ${render_add_comment_button(line_no=line_no, f_path=filediff.patch['filename'])} %endif ${content or '' | n} %if use_comments and comments: - ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries)} + ${inline_comments_container(comments, active_pattern_entries=active_pattern_entries, line_no=line_no, f_path=filediff.patch['filename'])} %endif