Commit 88c2b5831b8e61923ca9b3f07340b7906c591a3e

Authored by Victor Costa
1 parent 4000a698

Add button to remove comments from articles

src/app/article/comment/comment.component.spec.ts
... ... @@ -18,7 +18,11 @@ describe("Components", () => {
18 18 });
19 19  
20 20 function createComponent() {
21   - let providers = helpers.provideFilters("translateFilter");
  21 + let commentService = jasmine.createSpyObj("commentService", ["removeFromArticle"]);
  22 + let providers = [
  23 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService),
  24 + helpers.createProviderToValue("CommentService", commentService)
  25 + ].concat(helpers.provideFilters("translateFilter"));
22 26  
23 27 @Component({ selector: 'test-container-component', directives: [CommentComponent], template: htmlTemplate, providers: providers })
24 28 class ContainerComponent {
... ...
src/app/article/comment/comment.component.ts
1 1 import { Inject, Input, Component, Output, EventEmitter } from 'ng-forward';
2 2 import { PostCommentComponent } from "./post-comment/post-comment.component";
  3 +import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service";
  4 +import { NotificationService } from "../../shared/services/notification.service";
3 5  
4 6 @Component({
5 7 selector: 'noosfero-comment',
6 8 templateUrl: 'app/article/comment/comment.html'
7 9 })
  10 +@Inject(CommentService, NotificationService)
8 11 export class CommentComponent {
9 12  
10 13 @Input() comment: noosfero.CommentViewModel;
... ... @@ -16,11 +19,23 @@ export class CommentComponent {
16 19 return this.comment && this.comment.__show_reply === true;
17 20 }
18 21  
19   - constructor() {
20   - }
21   -
  22 + constructor(private commentService: CommentService,
  23 + private notificationService: NotificationService) { }
22 24  
23 25 reply() {
24 26 this.comment.__show_reply = !this.comment.__show_reply;
25 27 }
  28 +
  29 + allowRemove() {
  30 + return true;
  31 + }
  32 +
  33 + remove() {
  34 + this.notificationService.confirmation({ title: "comment.remove.confirmation.title", message: "comment.remove.confirmation.message" }, () => {
  35 + this.commentService.removeFromArticle(this.article, this.comment).then((result: noosfero.RestResult<noosfero.Comment>) => {
  36 + // FIXME send event
  37 + this.notificationService.success({ title: "comment.remove.success.title", message: "comment.remove.success.message" });
  38 + });
  39 + });
  40 + }
26 41 }
... ...
src/app/article/comment/comment.html
... ... @@ -18,9 +18,14 @@
18 18 <div class="title">{{ctrl.comment.title}}</div>
19 19 <div class="body">{{ctrl.comment.body}}</div>
20 20 <div class="actions" ng-if="ctrl.displayActions">
21   - <a href="#" (click)="ctrl.reply()" class="small text-muted reply" ng-if="ctrl.article.accept_comments">
  21 + <a href="#" (click)="ctrl.reply()" class="action small text-muted reply" ng-if="ctrl.article.accept_comments">
  22 + <span class="bullet-separator">•</span>
22 23 {{"comment.reply" | translate}}
23 24 </a>
  25 + <a href="#" (click)="ctrl.remove()" class="action small text-muted remove" ng-if="ctrl.allowRemove()">
  26 + <span class="bullet-separator">•</span>
  27 + {{"comment.remove" | translate}}
  28 + </a>
24 29 </div>
25 30 </div>
26 31 <noosfero-comments [show-form]="ctrl.showReply()" [article]="ctrl.article" [parent]="ctrl.comment" ng-if="ctrl.displayReplies"></noosfero-comments>
... ...
src/app/article/comment/comment.scss
... ... @@ -10,6 +10,22 @@
10 10 .title {
11 11 font-weight: bold;
12 12 }
  13 + .actions {
  14 + .action {
  15 + text-decoration: none;
  16 + &:first-child {
  17 + .bullet-separator {
  18 + display: none;
  19 + }
  20 + }
  21 + .bullet-separator {
  22 + font-size: 8px;
  23 + vertical-align: middle;
  24 + margin: 3px;
  25 + color: #B6C2CA;
  26 + }
  27 + }
  28 + }
13 29 .media-left {
14 30 min-width: 40px;
15 31 }
... ...
src/app/shared/services/notification.service.spec.ts
... ... @@ -20,10 +20,10 @@ describe(&quot;Components&quot;, () =&gt; {
20 20 let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService);
21 21 component.error({ message: "message", title: "title" });
22 22 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
23   - text: "message",
24 23 title: "title",
  24 + text: "message",
25 25 type: "error"
26   - }));
  26 + }), null);
27 27 done();
28 28 });
29 29  
... ... @@ -36,7 +36,7 @@ describe(&quot;Components&quot;, () =&gt; {
36 36 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
37 37 text: NotificationService.DEFAULT_ERROR_MESSAGE,
38 38 type: "error"
39   - }));
  39 + }), null);
40 40 done();
41 41 });
42 42  
... ... @@ -48,7 +48,7 @@ describe(&quot;Components&quot;, () =&gt; {
48 48 component.success({ title: "title", message: "message", timer: 1000 });
49 49 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
50 50 type: "success"
51   - }));
  51 + }), null);
52 52 done();
53 53 });
54 54  
... ... @@ -60,7 +60,7 @@ describe(&quot;Components&quot;, () =&gt; {
60 60 component.httpError(500, {});
61 61 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
62 62 text: "notification.http_error.500.message"
63   - }));
  63 + }), null);
64 64 done();
65 65 });
66 66  
... ... @@ -73,7 +73,22 @@ describe(&quot;Components&quot;, () =&gt; {
73 73 expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
74 74 type: "success",
75 75 timer: NotificationService.DEFAULT_SUCCESS_TIMER
76   - }));
  76 + }), null);
  77 + done();
  78 + });
  79 +
  80 + it("display a confirmation dialog when call confirmation method", done => {
  81 + let sweetAlert = jasmine.createSpyObj("sweetAlert", ["swal"]);
  82 + sweetAlert.swal = jasmine.createSpy("swal");
  83 +
  84 + let component: NotificationService = new NotificationService(<any>helpers.mocks.$log, <any>sweetAlert, <any>helpers.mocks.translatorService);
  85 + let func = () => { };
  86 + component.confirmation({ title: "title", message: "message" }, func);
  87 + expect(sweetAlert.swal).toHaveBeenCalledWith(jasmine.objectContaining({
  88 + title: "title",
  89 + text: "message",
  90 + type: "warning"
  91 + }), jasmine.any(Function));
77 92 done();
78 93 });
79 94 });
... ...
src/app/shared/services/notification.service.ts
... ... @@ -36,15 +36,23 @@ export class NotificationService {
36 36 this.showMessage({ title: title, text: message, timer: timer });
37 37 }
38 38  
39   - private showMessage({title, text, type = "success", timer = null, showConfirmButton = true}) {
  39 + confirmation({title, message, showCancelButton = true, type = "warning"}, confirmationFunction: Function) {
  40 + this.showMessage({ title: title, text: message, showCancelButton: showCancelButton, type: type, closeOnConfirm: false }, confirmationFunction);
  41 + }
  42 +
  43 + private showMessage({title, text, type = "success", timer = null, showConfirmButton = true, showCancelButton = false, closeOnConfirm = true}, confirmationFunction: Function = null) {
40 44 this.$log.debug("Notification message:", title, text, type, this.translatorService.currentLanguage());
41 45 this.SweetAlert.swal({
42 46 title: this.translatorService.translate(title),
43 47 text: this.translatorService.translate(text),
44 48 type: type,
45 49 timer: timer,
46   - showConfirmButton: showConfirmButton
47   - });
  50 + showConfirmButton: showConfirmButton,
  51 + showCancelButton: showCancelButton,
  52 + closeOnConfirm: closeOnConfirm
  53 + }, confirmationFunction ? (isConfirm: boolean) => {
  54 + if (isConfirm) confirmationFunction();
  55 + } : null);
48 56 }
49 57  
50 58 }
... ...
src/languages/en.json
... ... @@ -35,7 +35,12 @@
35 35 "comment.pagination.more": "More",
36 36 "comment.post.success.title": "Good job!",
37 37 "comment.post.success.message": "Comment saved!",
  38 + "comment.remove.success.title": "Good job!",
  39 + "comment.remove.success.message": "Comment removed!",
  40 + "comment.remove.confirmation.title": "Are you sure?",
  41 + "comment.remove.confirmation.message": "You will not be able to recover this comment!",
38 42 "comment.reply": "reply",
  43 + "comment.remove": "remove",
39 44 "article.actions.edit": "Edit",
40 45 "article.actions.read_more": "Read More",
41 46 "article.basic_editor.title": "Title",
... ...
src/languages/pt.json
... ... @@ -35,7 +35,12 @@
35 35 "comment.pagination.more": "Mais",
36 36 "comment.post.success.title": "Bom trabalho!",
37 37 "comment.post.success.message": "Comentário salvo com sucesso!",
  38 + "comment.remove.success.title": "Bom trabalho!",
  39 + "comment.remove.success.message": "Comentário removido com sucesso!",
  40 + "comment.remove.confirmation.title": "Tem certeza?",
  41 + "comment.remove.confirmation.message": "Você não poderá recuperar o comentário removido!",
38 42 "comment.reply": "responder",
  43 + "comment.remove": "remover",
39 44 "article.actions.edit": "Editar",
40 45 "article.actions.read_more": "Ler mais",
41 46 "article.basic_editor.title": "Título",
... ...
src/lib/ng-noosfero-api/http/comment.service.spec.ts
... ... @@ -40,6 +40,17 @@ describe(&quot;Services&quot;, () =&gt; {
40 40 });
41 41 $httpBackend.flush();
42 42 });
  43 +
  44 + it("should remove a comment from an article", (done) => {
  45 + let articleId = 1;
  46 + let comment: noosfero.Comment = <any>{ id: 2 };
  47 + $httpBackend.expectDELETE(`/api/v1/articles/${articleId}/comments/${comment.id}`).respond(200, { comment: { id: 2 } });
  48 + commentService.removeFromArticle(<noosfero.Article>{ id: articleId }, comment).then((result: noosfero.RestResult<noosfero.Comment>) => {
  49 + expect(result.data).toEqual({ id: 2 });
  50 + done();
  51 + });
  52 + $httpBackend.flush();
  53 + });
43 54 });
44 55  
45 56  
... ...
src/lib/ng-noosfero-api/http/comment.service.ts
... ... @@ -31,4 +31,9 @@ export class CommentService extends RestangularService&lt;noosfero.Comment&gt; {
31 31 let articleElement = this.articleService.getElement(<number>article.id);
32 32 return this.create(comment, articleElement, null, { 'Content-Type': 'application/json' }, false);
33 33 }
  34 +
  35 + removeFromArticle(article: noosfero.Article, comment: noosfero.Comment): ng.IPromise<noosfero.RestResult<noosfero.Comment>> {
  36 + let articleElement = this.articleService.getElement(<number>article.id);
  37 + return this.remove(comment, articleElement);
  38 + }
34 39 }
... ...
src/plugins/comment_paragraph/allow-comment/allow-comment.component.spec.ts
... ... @@ -57,7 +57,7 @@ describe(&quot;Components&quot;, () =&gt; {
57 57 });
58 58  
59 59 it('display button to side comments', () => {
60   - expect(helper.all(".paragraph .actions a").length).toEqual(1);
  60 + expect(helper.all(".paragraph .paragraph-actions a").length).toEqual(1);
61 61 });
62 62  
63 63 it('set display to true when click in show paragraph', () => {
... ...
src/plugins/comment_paragraph/allow-comment/allow-comment.html
1 1 <div class="paragraph" ng-class="{'active' : ctrl.display}">
2 2 <div class="paragraph-content" ng-bind-html="ctrl.content" ng-class="{'active' : ctrl.display}"></div>
3   - <div ng-if="ctrl.isActivated()" class="actions">
  3 + <div ng-if="ctrl.isActivated()" class="paragraph-actions">
4 4 <a href="#" popover-placement="right-top" popover-trigger="none"
5 5 uib-popover-template="'plugins/comment_paragraph/allow-comment/popover.html'"
6 6 (click)="ctrl.showParagraphComments()" popover-is-open="ctrl.display">
... ...
src/plugins/comment_paragraph/allow-comment/allow-comment.scss
... ... @@ -15,7 +15,7 @@ comment-paragraph-plugin-allow-comment {
15 15 width: 95%;
16 16 display: inline-block;
17 17 }
18   - .actions {
  18 + .paragraph-actions {
19 19 width: 3%;
20 20 display: inline-block;
21 21 vertical-align: top;
... ...