From f326d6f12e07d50d4da39213af29a733e712d1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Thu, 11 Sep 2014 17:55:34 +0200 Subject: [PATCH] Delete comments functionality --- app/coffee/modules/common/history.coffee | 91 ++++++++++++++++++--- app/coffee/modules/resources.coffee | 1 + app/coffee/modules/resources/history.coffee | 50 +++++++++++ app/styles/dependencies/colors.scss | 2 +- app/styles/modules/common/history.scss | 86 +++++++++++++++---- 5 files changed, 198 insertions(+), 32 deletions(-) create mode 100644 app/coffee/modules/resources/history.coffee diff --git a/app/coffee/modules/common/history.coffee b/app/coffee/modules/common/history.coffee index 81e9fb98..5af10bcf 100644 --- a/app/coffee/modules/common/history.coffee +++ b/app/coffee/modules/common/history.coffee @@ -32,19 +32,16 @@ module = angular.module("taigaCommon") class HistoryController extends taiga.Controller - @.$inject = ["$scope", "$tgRepo"] + @.$inject = ["$scope", "$tgRepo", "$tgResources"] - constructor: (@scope, @repo) -> + constructor: (@scope, @repo, @rs) -> initialize: (type, objectId) -> @.type = type @.objectId = objectId - getHistory: (type, objectId) -> - return @repo.queryOneRaw("history/#{type}", objectId) - - loadHistory: -> - return @.getHistory(@.type, @.objectId).then (history) => + loadHistory: (type, objectId) -> + return @rs.history.get(type, objectId).then (history) => for historyResult in history # If description was modified take only the description_html field if historyResult.values_diff.description_diff? @@ -56,6 +53,12 @@ class HistoryController extends taiga.Controller @scope.history = history @scope.comments = _.filter(history, (item) -> item.comment != "") + deleteComment: (type, objectId, activityId) -> + return @rs.history.deleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId) + + undeleteComment: (type, objectId, activityId) -> + return @rs.history.undeleteComment(type, objectId, activityId).then => @.loadHistory(type, objectId) + HistoryDirective = ($log) -> templateChangeDiff = _.template(""" @@ -128,6 +131,21 @@ HistoryDirective = ($log) -> """) + templateDeletedComment = _.template(""" +
+
+ Comment deleted by <%- deleteCommentUser %> on <%- deleteCommentDate %> + (Show deleted comment) + +
<%= deleteComment %>
+
+ + + Restore comment + +
+ """) + templateActivity = _.template("""
@@ -146,9 +164,17 @@ HistoryDirective = ($log) ->
<% if (comment.length > 0) { %> -
- <%= comment %> -
+ <% if ((deleteCommentDate || deleteCommentUser)) { %> +
+ Comment deleted by <%- deleteCommentUser %> on <%- deleteCommentDate %> +
+ <% } %> +
+ <%= comment %> +
+ <% if (!deleteCommentDate && mode !== "activity") { %> + + <% } %> <% } %> <% if(changes.length > 0) { %> @@ -170,6 +196,7 @@ HistoryDirective = ($log) -> """) templateBaseEntries = _.template(""" + <% if (showMore > 0) { %> + Show previous entries (<%- showMore %> more) @@ -226,7 +253,7 @@ HistoryDirective = ($log) -> objectId = model.id $ctrl.initialize(type, objectId) - $ctrl.loadHistory() + $ctrl.loadHistory(type, objectId) # Helpers getHumanizedFieldName = (field) -> @@ -332,6 +359,14 @@ HistoryDirective = ($log) -> return "Made #{size} changes" # TODO: i18n renderComment = (comment) -> + if (comment.delete_comment_date or comment.delete_comment_user) + return templateDeletedComment({ + deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") + deleteCommentUser: getUserFullName(comment.delete_comment_user) + deleteComment: comment.comment_html + activityId: comment.id + }) + return templateActivity({ avatar: getUserAvatar(comment.user.pk) userFullName: getUserFullName(comment.user.pk) @@ -340,6 +375,9 @@ HistoryDirective = ($log) -> changesText: renderChangesHelperText(comment) changes: renderChangeEntries(comment, false) mode: "comment" + deleteCommentDate: moment(comment.delete_comment_date).format("DD MMM YYYY HH:mm") if comment.delete_comment_date + deleteCommentUser: getUserFullName(comment.delete_comment_user) if comment.delete_comment_user + activityId: comment.id }) renderChange = (change) -> @@ -351,6 +389,9 @@ HistoryDirective = ($log) -> changes: renderChangeEntries(change, false) changesText: "" mode: "activity" + deleteCommentDate: moment(change.delete_comment_date).format("DD MMM YYYY HH:mm") if change.delete_comment_date + deleteCommentUser: getUserFullName(change.delete_comment_user) if change.delete_comment_user + activityId: change.id }) renderHistory = (entries, totalEntries) -> @@ -388,7 +429,7 @@ HistoryDirective = ($log) -> $scope.$watch("comments", renderComments) $scope.$watch("history", renderActivity) - $scope.$on("history:reload", -> $ctrl.loadHistory()) + $scope.$on("history:reload", -> $ctrl.loadHistory(type, objectId)) # Events @@ -397,7 +438,7 @@ HistoryDirective = ($log) -> $el.find(".comment-list").addClass("activeanimation") onSuccess = -> - $ctrl.loadHistory() + $ctrl.loadHistory(type, objectId) onError = -> $confirm.notify("error") @@ -416,6 +457,20 @@ HistoryDirective = ($log) -> showAllComments = not showAllComments renderComments() + $el.on "click", ".show-deleted-comment", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + target.parents('.activity-single').find('.hide-deleted-comment').show() + target.parents('.activity-single').find('.show-deleted-comment').hide() + target.parents('.activity-single').find('.comment-body').show() + + $el.on "click", ".hide-deleted-comment", (event) -> + event.preventDefault() + target = angular.element(event.currentTarget) + target.parents('.activity-single').find('.hide-deleted-comment').hide() + target.parents('.activity-single').find('.show-deleted-comment').show() + target.parents('.activity-single').find('.comment-body').hide() + $el.on "click", ".changes-title", (event) -> event.preventDefault() target = angular.element(event.currentTarget) @@ -428,6 +483,16 @@ HistoryDirective = ($log) -> $el.find(".history-tabs li a").toggleClass("active") $el.find(".history section").toggleClass("hidden") + $el.on "click", ".comment-delete", (event) -> + target = angular.element(event.currentTarget) + activityId = target.data('activity-id') + $ctrl.deleteComment(type, objectId, activityId) + + $el.on "click", ".comment-restore", (event) -> + target = angular.element(event.currentTarget) + activityId = target.data('activity-id') + $ctrl.undeleteComment(type, objectId, activityId) + $scope.$on "$destroy", -> $el.off() diff --git a/app/coffee/modules/resources.coffee b/app/coffee/modules/resources.coffee index f5d2e97e..df3755e7 100644 --- a/app/coffee/modules/resources.coffee +++ b/app/coffee/modules/resources.coffee @@ -130,5 +130,6 @@ module.run([ "$tgSearchResourcesProvider", "$tgAttachmentsResourcesProvider", "$tgMdRenderResourcesProvider", + "$tgHistoryResourcesProvider", initResources ]) diff --git a/app/coffee/modules/resources/history.coffee b/app/coffee/modules/resources/history.coffee new file mode 100644 index 00000000..3e61a688 --- /dev/null +++ b/app/coffee/modules/resources/history.coffee @@ -0,0 +1,50 @@ +### +# Copyright (C) 2014 Andrey Antukh +# Copyright (C) 2014 Jesús Espino Garcia +# Copyright (C) 2014 David Barragán Merino +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +# File: modules/resources/history.coffee +### + + +taiga = @.taiga + +resourceProvider = ($repo, $http, $urls) -> + service = {} + + service.get = (type, objectId) -> + return $repo.queryOneRaw("history/#{type}", objectId) + + service.deleteComment = (type, objectId, activityId) -> + url = $urls.resolve("history/#{type}") + url = "#{url}/#{objectId}/delete_comment" + params = {id: activityId} + return $http.post(url, null, params).then (data) => + return data.data + + service.undeleteComment = (type, objectId, activityId) -> + url = $urls.resolve("history/#{type}") + url = "#{url}/#{objectId}/undelete_comment" + params = {id: activityId} + return $http.post(url, null, params).then (data) => + return data.data + + return (instance) -> + instance.history = service + + +module = angular.module("taigaResources") +module.factory("$tgHistoryResourcesProvider", ["$tgRepo", "$tgHttp", "$tgUrls", resourceProvider]) diff --git a/app/styles/dependencies/colors.scss b/app/styles/dependencies/colors.scss index 1df4e5c5..2efb3ae5 100755 --- a/app/styles/dependencies/colors.scss +++ b/app/styles/dependencies/colors.scss @@ -4,7 +4,7 @@ $black: #000; $blackish: #050505; $gray: #555; $grayer: #444; -$gray-light: #cdcdcd; +$gray-light: #b8b8b8; $whitish: #f5f5f5; $very-light-gray: #fcfcfc; $white: #fff; diff --git a/app/styles/modules/common/history.scss b/app/styles/modules/common/history.scss index c89dffd6..0e5e68d2 100644 --- a/app/styles/modules/common/history.scss +++ b/app/styles/modules/common/history.scss @@ -2,26 +2,23 @@ margin-bottom: 1rem; padding: 0 1rem; } - - .changes-title { display: block; padding: .5rem; &:hover { .icon { @include transform(rotate(180deg)); - @include transition(all.2s linear); + @include transition(all .2s linear); color: $green-taiga; } } .icon { @include transform(rotate(0)); - @include transition(all.2s linear); + @include transition(all .2s linear); color: $grayer; float: right; } } - .change-entry { @include table-flex; border-bottom: 1px solid $gray-light; @@ -40,7 +37,6 @@ @extend %small; } } - .history-tabs { @extend %title; border-bottom: 3px solid $gray-light; @@ -68,7 +64,6 @@ margin-right: .5rem; } } - .add-comment { @include clearfix; textarea { @@ -94,7 +89,6 @@ } } } - a.show-more-comments { @extend %small; border-bottom: 1px solid $gray-light; @@ -107,7 +101,6 @@ a.show-more-comments { background: lighten($green-taiga, 60%); } } - .more-comments { @extend %small; border-bottom: 1px solid $gray-light; @@ -119,7 +112,6 @@ a.show-more-comments { margin-left: .5rem; } } - .comment-list { &.activeanimation { .comment-single.ng-enter:last-child, @@ -136,15 +128,66 @@ a.show-more-comments { } } } - .activity-single { @include table-flex; - border-bottom: 2px solid $gray-light; + border-bottom: 1px solid $gray-light; padding: 2rem 0; position: relative; + &:hover { + .comment-delete { + @include transition(opacity .2s linear); + opacity: 1; + } + .comment-restore { + @include transition(opacity .2s linear); + opacity: 1; + } + } &:first-child { margin-top: 0; } + &:last-child { + border-bottom: 0; + } + &.deleted-comment, + .deleted-comment { + @extend %small; + color: $gray-light; + padding: .5rem; + a { + color: $gray-light; + margin-left: .3rem; + &:hover { + @include transition(color .2s linear); + color: $green-taiga; + } + } + img { + @include filter(grayscale(100%)); + opacity: .5; + } + .comment-body { + display: none; + margin: .2rem 0 .5rem; + p { + @extend %medium; + } + } + } + .comment-restore { + @extend %small; + color: $gray-light; + position: absolute; + right: 0; + top: .4rem; + .icon { + vertical-align: baseline; + } + &:hover { + @include transition(color .2s linear); + color: $green-taiga; + } + } .username { color: $green-taiga; margin-bottom: .5rem; @@ -158,18 +201,16 @@ a.show-more-comments { } .activity-username { color: $green-taiga; - margin-bottom: 1rem; + margin-bottom: .5rem; } .activity-content { @include table-flex-child(20, 150px, 0); } - .changes { background: $whitish; .change-entry { display: none; - &.active { display: flex; } @@ -180,18 +221,27 @@ a.show-more-comments { color: $gray-light; margin-left: 1rem; } - .wysiwyg { margin-bottom: 0; } - + .comment-delete { + @include transition(all .2s linear); + color: $red; + opacity: 0; + position: absolute; + right: 0; + top: 2rem; + &:hover { + @include transition(color .2s linear); + color: $red-light; + } + } &.activity { .change-entry { display: flex; } } } - .more-activity { @extend %small; border-bottom: 1px solid $gray-light;