diff --git a/services/web/app/coffee/Features/Chat/ChatApiHandler.coffee b/services/web/app/coffee/Features/Chat/ChatApiHandler.coffee index e21e94d232..aa4b75ce11 100644 --- a/services/web/app/coffee/Features/Chat/ChatApiHandler.coffee +++ b/services/web/app/coffee/Features/Chat/ChatApiHandler.coffee @@ -45,4 +45,17 @@ module.exports = ChatApiHandler = url: "#{settings.apis.chat.internal_url}/project/#{project_id}/threads" method: "GET" json: true + }, callback + + resolveThread: (project_id, thread_id, user_id, callback = (error) ->) -> + ChatApiHandler._apiRequest { + url: "#{settings.apis.chat.internal_url}/project/#{project_id}/thread/#{thread_id}/resolve" + method: "POST" + json: {user_id} + }, callback + + reopenThread: (project_id, thread_id, callback = (error) ->) -> + ChatApiHandler._apiRequest { + url: "#{settings.apis.chat.internal_url}/project/#{project_id}/thread/#{thread_id}/reopen" + method: "POST" }, callback \ No newline at end of file diff --git a/services/web/app/coffee/Features/Comments/CommentsController.coffee b/services/web/app/coffee/Features/Comments/CommentsController.coffee index 0e9658f1d3..07974d6872 100644 --- a/services/web/app/coffee/Features/Comments/CommentsController.coffee +++ b/services/web/app/coffee/Features/Comments/CommentsController.coffee @@ -14,7 +14,7 @@ module.exports = CommentsController = logger.log {project_id, thread_id, user_id, content}, "sending comment" ChatApiHandler.sendComment project_id, thread_id, user_id, content, (err, comment) -> return next(err) if err? - EditorRealTimeController.emitToRoom project_id, "new-comment", thread_id, comment, (err)-> + EditorRealTimeController.emitToRoom project_id, "new-comment", thread_id, comment, (err) -> res.send 204 getThreads: (req, res, next) -> @@ -22,4 +22,21 @@ module.exports = CommentsController = logger.log {project_id}, "getting comment threads for project" ChatApiHandler.getThreads project_id, (err, threads) -> return next(err) if err? - res.json threads \ No newline at end of file + res.json threads + + resolveThread: (req, res, next) -> + {project_id, thread_id} = req.params + user_id = AuthenticationController.getLoggedInUserId(req) + logger.log {project_id, thread_id, user_id}, "resolving comment thread" + ChatApiHandler.resolveThread project_id, thread_id, user_id, (err, threads) -> + return next(err) if err? + EditorRealTimeController.emitToRoom project_id, "resolve-thread", thread_id, user_id, (err)-> + res.send 204 + + reopenThread: (req, res, next) -> + {project_id, thread_id} = req.params + logger.log {project_id, thread_id}, "reopening comment thread" + ChatApiHandler.reopenThread project_id, thread_id, (err, threads) -> + return next(err) if err? + EditorRealTimeController.emitToRoom project_id, "reopen-thread", thread_id, (err)-> + res.send 204 \ No newline at end of file diff --git a/services/web/app/coffee/router.coffee b/services/web/app/coffee/router.coffee index 0ad7f74c4f..13c8568f17 100644 --- a/services/web/app/coffee/router.coffee +++ b/services/web/app/coffee/router.coffee @@ -232,6 +232,8 @@ module.exports = class Router webRouter.post "/project/:project_id/thread/:thread_id/messages", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, CommentsController.sendComment webRouter.get "/project/:project_id/threads", AuthorizationMiddlewear.ensureUserCanReadProject, CommentsController.getThreads + webRouter.post "/project/:project_id/thread/:thread_id/resolve", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, CommentsController.resolveThread + webRouter.post "/project/:project_id/thread/:thread_id/reopen", AuthorizationMiddlewear.ensureUserCanWriteProjectContent, CommentsController.reopenThread webRouter.post "/project/:Project_id/references/index", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.index webRouter.post "/project/:Project_id/references/indexAll", AuthorizationMiddlewear.ensureUserCanReadProject, ReferencesController.indexAll diff --git a/services/web/app/views/project/editor/review-panel.jade b/services/web/app/views/project/editor/review-panel.jade index 6ffc1bdbb9..16f003633a 100644 --- a/services/web/app/views/project/editor/review-panel.jade +++ b/services/web/app/views/project/editor/review-panel.jade @@ -30,7 +30,7 @@ entry="entry" threads="reviewPanel.commentThreads" on-resolve="resolveComment(entry, entry_id)" - on-unresolve="unresolveComment(entry_id)" + on-unresolve="unresolveComment(entry, entry_id)" on-show-thread="showThread(entry)" on-hide-thread="hideThread(entry)" on-delete="deleteComment(entry_id)" @@ -77,7 +77,7 @@ entry="entry" users="users" on-resolve="resolveComment(entry, entry.id)" - on-unresolve="unresolveComment(entry.id)" + on-unresolve="unresolveComment(entry, entry.id)" on-delete="deleteComment(entry.id)" on-reply="submitReply(entry, entry_id);" on-indicator-click="toggleReviewPanel();" @@ -139,18 +139,18 @@ script(type='text/ng-template', id='changeEntryTemplate') script(type='text/ng-template', id='commentEntryTemplate') div - .rp-entry-callout.rp-entry-callout-comment(ng-if="!entry.resolved") + .rp-entry-callout.rp-entry-callout-comment(ng-if="!threads[entry.thread_id].resolved") .rp-entry-indicator( ng-class="{ 'rp-entry-indicator-focused': entry.focused }" ng-click="onIndicatorClick();" ) i.fa.fa-comment .rp-entry.rp-entry-comment( - ng-class="{ 'rp-entry-focused': entry.focused, 'rp-entry-comment-resolved': entry.resolved}" + ng-class="{ 'rp-entry-focused': entry.focused, 'rp-entry-comment-resolved': threads[entry.thread_id].resolved}" ) .rp-comment( - ng-if="!entry.resolved || entry.showWhenResolved" - ng-repeat="comment in threads[entry.thread_id]" + ng-if="!threads[entry.thread_id].resolved || entry.showWhenResolved" + ng-repeat="comment in threads[entry.thread_id].messages" ng-class="comment.user.isSelf ? 'rp-comment-self' : '';" ) .rp-avatar( @@ -163,29 +163,29 @@ script(type='text/ng-template', id='commentEntryTemplate') | {{ comment.timestamp | date : 'MMM d, y h:mm a' }} |  •  span(style="color: hsl({{ comment.user.hue }}, 70%, 40%);") {{ comment.user.name }} - .rp-comment-reply(ng-if="!entry.resolved || entry.showWhenResolved") + .rp-comment-reply(ng-if="!threads[entry.thread_id].resolved || entry.showWhenResolved") textarea.rp-comment-input( ng-model="entry.replyContent" ng-keypress="handleCommentReplyKeyPress($event);" stop-propagation="click" placeholder="{{ 'Hit \"Enter\" to reply' + (entry.resolved ? ' and re-open' : '') }}" ) - .rp-comment-resolved-description(ng-if="entry.resolved && !entry.showWhenResolved") + .rp-comment-resolved-description(ng-if="threads[entry.thread_id].resolved && !entry.showWhenResolved") div | Comment resolved by - span(style="color: hsl({{ users[entry.resolved_data.user_id].hue }}, 70%, 40%);") {{ users[entry.resolved_data.user_id].name }} - div {{ entry.resolved_data.ts | date : 'MMM d, y h:mm a' }} + span(style="color: hsl({{ threads[entry.thread_id].resolved_by_user.hue }}, 70%, 40%);") {{ threads[entry.thread_id].resolved_by_user.name }} + div {{ threads[entry.thread_id].resolved_at | date : 'MMM d, y h:mm a' }} .rp-entry-actions - a.rp-entry-button(href, ng-click="onResolve();", ng-if="!entry.resolved") + a.rp-entry-button(href, ng-click="onResolve();", ng-if="!threads[entry.thread_id].resolved") i.fa.fa-check |  Mark as resolved - a.rp-entry-button(href, ng-click="onShowThread();", ng-if="entry.resolved && !entry.showWhenResolved") + a.rp-entry-button(href, ng-click="onShowThread();", ng-if="threads[entry.thread_id].resolved && !entry.showWhenResolved") |  Show - a.rp-entry-button(href, ng-click="onHideThread();", ng-if="entry.resolved && entry.showWhenResolved") + a.rp-entry-button(href, ng-click="onHideThread();", ng-if="threads[entry.thread_id].resolved && entry.showWhenResolved") |  Hide - a.rp-entry-button(href, ng-click="onUnresolve();", ng-if="entry.resolved") + a.rp-entry-button(href, ng-click="onUnresolve();", ng-if="threads[entry.thread_id].resolved") |  Re-open - a.rp-entry-button(href, ng-click="onDelete();", ng-if="entry.resolved") + a.rp-entry-button(href, ng-click="onDelete();", ng-if="threads[entry.thread_id].resolved") |  Delete diff --git a/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee b/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee index cff77f5e1e..55ea4a46e1 100644 --- a/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee +++ b/services/web/public/coffee/ide/review-panel/controllers/ReviewPanelController.coffee @@ -28,14 +28,16 @@ define [ $http.get "/project/#{$scope.project_id}/threads" .success (threads) -> - for thread_id, comments of threads - for comment in comments + for thread_id, thread of threads + for comment in thread.messages formatComment(comment) + if thread.resolved_by_user? + formatUser(thread.resolved_by_user) $scope.reviewPanel.commentThreads = threads ide.socket.on "new-comment", (thread_id, comment) -> - $scope.reviewPanel.commentThreads[thread_id] ?= [] - $scope.reviewPanel.commentThreads[thread_id].push(formatComment(comment)) + $scope.reviewPanel.commentThreads[thread_id] ?= { messages: [] } + $scope.reviewPanel.commentThreads[thread_id].messages.push(formatComment(comment)) $scope.$apply() $timeout () -> $scope.$broadcast "review-panel:layout" @@ -236,9 +238,19 @@ define [ $scope.resolveComment = (entry, entry_id) -> entry.showWhenResolved = false entry.focused = false + thread = $scope.reviewPanel.commentThreads[entry.thread_id] + thread.resolved = true + thread.resolved_by_user = $scope.users[window.user_id] + thread.resolved_at = new Date() + $http.post "/project/#{$scope.project_id}/thread/#{entry.thread_id}/resolve", {_csrf: window.csrfToken} $scope.$broadcast "comment:resolve", entry_id, window.user_id - $scope.unresolveComment = (entry_id) -> + $scope.unresolveComment = (entry, entry_id) -> + thread = $scope.reviewPanel.commentThreads[entry.thread_id] + delete thread.resolved + delete thread.resolved_by_user + delete thread.resolved_at + $http.post "/project/#{$scope.project_id}/thread/#{entry.thread_id}/reopen", {_csrf: window.csrfToken} $scope.$broadcast "comment:unresolve", entry_id $scope.deleteComment = (entry_id) ->