Commit 45e136413c812b5d1d447f75930635a6260d5621

Authored by ABNER SILVA DE OLIVEIRA
2 parents ea217b65 00882cc3

Merge branch 'comments'

src/app/article/article-default-view-component.spec.ts
@@ -27,7 +27,8 @@ describe("Components", () => { @@ -27,7 +27,8 @@ describe("Components", () => {
27 providers: [ 27 providers: [
28 helpers.createProviderToValue('CommentService', helpers.mocks.commentService), 28 helpers.createProviderToValue('CommentService', helpers.mocks.commentService),
29 helpers.provideFilters("translateFilter"), 29 helpers.provideFilters("translateFilter"),
30 - helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService) 30 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService),
  31 + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({}))
31 ] 32 ]
32 }) 33 })
33 class ArticleContainerComponent { 34 class ArticleContainerComponent {
@@ -64,7 +65,8 @@ describe("Components", () => { @@ -64,7 +65,8 @@ describe("Components", () => {
64 providers: [ 65 providers: [
65 helpers.createProviderToValue('CommentService', helpers.mocks.commentService), 66 helpers.createProviderToValue('CommentService', helpers.mocks.commentService),
66 helpers.provideFilters("translateFilter"), 67 helpers.provideFilters("translateFilter"),
67 - helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService) 68 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService),
  69 + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({}))
68 ] 70 ]
69 }) 71 })
70 class ArticleContainerComponent { 72 class ArticleContainerComponent {
src/app/article/comment/comment.component.spec.ts
1 import {Provider, provide, Component} from 'ng-forward'; 1 import {Provider, provide, Component} from 'ng-forward';
2 import * as helpers from "../../../spec/helpers"; 2 import * as helpers from "../../../spec/helpers";
3 -  
4 import {CommentComponent} from './comment.component'; 3 import {CommentComponent} from './comment.component';
5 4
6 const htmlTemplate: string = '<noosfero-comment [article]="ctrl.article" [comment]="ctrl.comment"></noosfero-comment>'; 5 const htmlTemplate: string = '<noosfero-comment [article]="ctrl.article" [comment]="ctrl.comment"></noosfero-comment>';
@@ -10,35 +9,48 @@ describe(&quot;Components&quot;, () =&gt; { @@ -10,35 +9,48 @@ describe(&quot;Components&quot;, () =&gt; {
10 9
11 beforeEach(angular.mock.module("templates")); 10 beforeEach(angular.mock.module("templates"));
12 11
13 - @Component({ selector: 'test-container-component', directives: [CommentComponent], template: htmlTemplate, providers: helpers.provideFilters("translateFilter") })  
14 - class ContainerComponent {  
15 - article = { id: 1 };  
16 - comment = { title: "title", body: "body" }; 12 +
  13 + function createComponent() {
  14 + let providers = helpers.provideFilters("translateFilter");
  15 +
  16 + @Component({ selector: 'test-container-component', directives: [CommentComponent], template: htmlTemplate, providers: providers })
  17 + class ContainerComponent {
  18 + article = { id: 1 };
  19 + comment = { title: "title", body: "body" };
  20 + }
  21 + return helpers.createComponentFromClass(ContainerComponent);
17 } 22 }
18 23
19 it("render a comment", done => { 24 it("render a comment", done => {
20 - helpers.createComponentFromClass(ContainerComponent).then(fixture => { 25 + createComponent().then(fixture => {
21 expect(fixture.debugElement.queryAll(".comment").length).toEqual(1); 26 expect(fixture.debugElement.queryAll(".comment").length).toEqual(1);
22 done(); 27 done();
23 }); 28 });
24 }); 29 });
25 30
26 it("not render a post comment tag in the beginning", done => { 31 it("not render a post comment tag in the beginning", done => {
27 - helpers.createComponentFromClass(ContainerComponent).then(fixture => { 32 + createComponent().then(fixture => {
28 expect(fixture.debugElement.queryAll("noosfero-post-comment").length).toEqual(0); 33 expect(fixture.debugElement.queryAll("noosfero-post-comment").length).toEqual(0);
29 done(); 34 done();
30 }); 35 });
31 }); 36 });
32 37
33 - it("render a post comment tag when click in reply", done => {  
34 - helpers.createComponentFromClass(ContainerComponent).then(fixture => { 38 + it("set show reply to true when click reply", done => {
  39 + createComponent().then(fixture => {
35 let component: CommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance; 40 let component: CommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
36 component.reply(); 41 component.reply();
37 - fixture.debugElement.getLocal("$rootScope").$apply();  
38 - expect(fixture.debugElement.queryAll("noosfero-post-comment").length).toEqual(1); 42 + expect(component.showReply()).toBeTruthy("Reply was expected to be true");
39 done(); 43 done();
40 }); 44 });
41 }); 45 });
42 46
  47 + it("show reply relies on current comment __showReply attribute", done => {
  48 + createComponent().then(fixture => {
  49 + let component = fixture.debugElement.componentViewChildren[0];
  50 + component.componentInstance.comment.__showReply = false;
  51 + expect(component.componentInstance.showReply()).toEqual(false);
  52 + done();
  53 + });
  54 + });
43 }); 55 });
44 }); 56 });
src/app/article/comment/comment.component.ts
1 -import { Input, Component } from 'ng-forward'; 1 +import { Inject, Input, Component, Output, EventEmitter } from 'ng-forward';
  2 +import { PostCommentComponent } from "./post-comment/post-comment.component";
2 3
3 @Component({ 4 @Component({
4 selector: 'noosfero-comment', 5 selector: 'noosfero-comment',
@@ -6,12 +7,18 @@ import { Input, Component } from &#39;ng-forward&#39;; @@ -6,12 +7,18 @@ import { Input, Component } from &#39;ng-forward&#39;;
6 }) 7 })
7 export class CommentComponent { 8 export class CommentComponent {
8 9
9 - @Input() comment: noosfero.Comment; 10 + @Input() comment: noosfero.CommentViewModel;
10 @Input() article: noosfero.Article; 11 @Input() article: noosfero.Article;
11 12
12 - showReply: boolean = false; 13 + showReply() {
  14 + return this.comment && this.comment.__show_reply === true;
  15 + }
  16 +
  17 + constructor() {
  18 + }
  19 +
13 20
14 reply() { 21 reply() {
15 - this.showReply = true; 22 + this.comment.__show_reply = !this.comment.__show_reply;
16 } 23 }
17 } 24 }
src/app/article/comment/comment.html
@@ -10,16 +10,12 @@ @@ -10,16 +10,12 @@
10 <h4 class="media-heading">{{ctrl.comment.author.name}}</h4> 10 <h4 class="media-heading">{{ctrl.comment.author.name}}</h4>
11 </a> 11 </a>
12 <span class="date" am-time-ago="ctrl.comment.created_at | dateFormat"></span> 12 <span class="date" am-time-ago="ctrl.comment.created_at | dateFormat"></span>
13 - <a href="#" (click)="ctrl.reply()">  
14 - <span class="pull-right small text-muted">  
15 - {{"comment.reply" | translate}}  
16 - </span>  
17 - </a>  
18 </div> 13 </div>
19 <div class="title">{{ctrl.comment.title}}</div> 14 <div class="title">{{ctrl.comment.title}}</div>
20 <div class="body">{{ctrl.comment.body}}</div> 15 <div class="body">{{ctrl.comment.body}}</div>
  16 + <a href="#" (click)="ctrl.reply()" class="small text-muted">
  17 + {{"comment.reply" | translate}}
  18 + </a>
21 </div> 19 </div>
22 -  
23 - <noosfero-post-comment ng-if="ctrl.showReply" [article]="ctrl.article" [reply-of]="ctrl.comment"></noosfero-post-comment>  
24 - 20 + <noosfero-comments [show-form]="ctrl.showReply()" [article]="ctrl.article" [parent]="ctrl.comment"></noosfero-comments>
25 </div> 21 </div>
src/app/article/comment/comment.scss
@@ -13,8 +13,7 @@ @@ -13,8 +13,7 @@
13 min-width: 40px; 13 min-width: 40px;
14 } 14 }
15 .media-body { 15 .media-body {
16 - background-color: #F9F9F9;  
17 - padding: 10px; 16 + padding: 0 10px 10px 10px;
18 } 17 }
19 noosfero-profile-image { 18 noosfero-profile-image {
20 img { 19 img {
@@ -28,5 +27,8 @@ @@ -28,5 +27,8 @@
28 font-size: 1.7em; 27 font-size: 1.7em;
29 } 28 }
30 } 29 }
  30 + .comments {
  31 + margin-left: 30px;
  32 + }
31 } 33 }
32 } 34 }
src/app/article/comment/comments.component.spec.ts
@@ -17,38 +17,71 @@ describe(&quot;Components&quot;, () =&gt; { @@ -17,38 +17,71 @@ describe(&quot;Components&quot;, () =&gt; {
17 commentService.getByArticle = jasmine.createSpy("getByArticle") 17 commentService.getByArticle = jasmine.createSpy("getByArticle")
18 .and.returnValue(helpers.mocks.promiseResultTemplate({ data: comments })); 18 .and.returnValue(helpers.mocks.promiseResultTemplate({ data: comments }));
19 19
20 - let providers = [  
21 - new Provider('CommentService', { useValue: commentService }),  
22 - new Provider('NotificationService', { useValue: helpers.mocks.notificationService })  
23 - ].concat(helpers.provideFilters("translateFilter"));  
24 -  
25 - @Component({ selector: 'test-container-component', directives: [CommentsComponent], template: htmlTemplate, providers: providers })  
26 - class ContainerComponent {  
27 - article = { id: 1 }; 20 + let properties = { article: { id: 1 }, parent: <any>null };
  21 + function createComponent() {
  22 + // postCommentEventService = jasmine.createSpyObj("postCommentEventService", ["subscribe"]);
  23 + let providers = [
  24 + helpers.createProviderToValue('CommentService', commentService),
  25 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService),
  26 + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({}))
  27 + ].concat(helpers.provideFilters("translateFilter"));
  28 +
  29 + return helpers.quickCreateComponent({
  30 + providers: providers,
  31 + directives: [CommentsComponent],
  32 + template: htmlTemplate,
  33 + properties: properties
  34 + });
28 } 35 }
29 36
  37 +
30 it("render comments associated to an article", done => { 38 it("render comments associated to an article", done => {
31 - helpers.createComponentFromClass(ContainerComponent).then(fixture => { 39 + createComponent().then(fixture => {
32 expect(fixture.debugElement.queryAll("noosfero-comment").length).toEqual(2); 40 expect(fixture.debugElement.queryAll("noosfero-comment").length).toEqual(2);
33 done(); 41 done();
34 }); 42 });
35 }); 43 });
36 44
37 it("render a post comment tag", done => { 45 it("render a post comment tag", done => {
38 - helpers.createComponentFromClass(ContainerComponent).then(fixture => { 46 + createComponent().then(fixture => {
39 expect(fixture.debugElement.queryAll("noosfero-post-comment").length).toEqual(1); 47 expect(fixture.debugElement.queryAll("noosfero-post-comment").length).toEqual(1);
40 done(); 48 done();
41 }); 49 });
42 }); 50 });
43 51
44 - it("update comments list when receive an event", done => {  
45 - helpers.createComponentFromClass(ContainerComponent).then(fixture => {  
46 - fixture.debugElement.getLocal("$rootScope").$emit(PostCommentComponent.EVENT_COMMENT_RECEIVED, { id: 1 });  
47 - fixture.debugElement.getLocal("$rootScope").$apply(); 52 + it("update comments list when receive an reply", done => {
  53 + properties.parent = { id: 3 };
  54 + createComponent().then(fixture => {
  55 + (<CommentsComponent>fixture.debugElement.componentViewChildren[0].componentInstance).commentAdded(<noosfero.Comment>{ id: 1, reply_of: { id: 3 } });
  56 + fixture.detectChanges();
48 expect(fixture.debugElement.queryAll("noosfero-comment").length).toEqual(3); 57 expect(fixture.debugElement.queryAll("noosfero-comment").length).toEqual(3);
49 done(); 58 done();
50 }); 59 });
51 }); 60 });
52 61
  62 + it("load comments for next page", done => {
  63 + createComponent().then(fixture => {
  64 + let headers = jasmine.createSpy("headers").and.returnValue(3);
  65 + commentService.getByArticle = jasmine.createSpy("getByArticle")
  66 + .and.returnValue(helpers.mocks.promiseResultTemplate({ data: { id: 4 }, headers: headers }));
  67 + let component: CommentsComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  68 + component.loadNextPage();
  69 + expect(component['page']).toEqual(3);
  70 + expect(component.comments.length).toEqual(3);
  71 + expect(component['total']).toEqual(3);
  72 + done();
  73 + });
  74 + });
  75 +
  76 + it("not display more when there is no more comments to load", done => {
  77 + createComponent().then(fixture => {
  78 + let component: CommentsComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  79 + component['total'] = 0;
  80 + component.parent = null;
  81 + expect(component.displayMore()).toBeFalsy();
  82 + done();
  83 + });
  84 + });
  85 +
53 }); 86 });
54 }); 87 });
src/app/article/comment/comments.component.ts
1 -import { Inject, Input, Component, provide } from 'ng-forward'; 1 +import { Inject, Input, Output, Component, provide, EventEmitter } from 'ng-forward';
  2 +import {INgForwardJQuery} from "ng-forward/cjs/util/jqlite-extensions";
  3 +
  4 +
2 import { PostCommentComponent } from "./post-comment/post-comment.component"; 5 import { PostCommentComponent } from "./post-comment/post-comment.component";
3 import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service"; 6 import { CommentService } from "../../../lib/ng-noosfero-api/http/comment.service";
4 import { CommentComponent } from "./comment.component"; 7 import { CommentComponent } from "./comment.component";
@@ -6,23 +9,59 @@ import { CommentComponent } from &quot;./comment.component&quot;; @@ -6,23 +9,59 @@ import { CommentComponent } from &quot;./comment.component&quot;;
6 @Component({ 9 @Component({
7 selector: 'noosfero-comments', 10 selector: 'noosfero-comments',
8 templateUrl: 'app/article/comment/comments.html', 11 templateUrl: 'app/article/comment/comments.html',
9 - directives: [PostCommentComponent, CommentComponent] 12 + directives: [PostCommentComponent, CommentComponent],
  13 + outputs: ['commentAdded']
10 }) 14 })
11 -@Inject(CommentService, "$rootScope") 15 +@Inject(CommentService, "$element")
12 export class CommentsComponent { 16 export class CommentsComponent {
13 17
14 - comments: noosfero.Comment[] = []; 18 + comments: noosfero.CommentViewModel[] = [];
  19 + @Input() showForm = true;
15 @Input() article: noosfero.Article; 20 @Input() article: noosfero.Article;
  21 + @Input() parent: noosfero.CommentViewModel;
  22 +
  23 + protected page = 1;
  24 + protected perPage = 5;
  25 + protected total = 0;
  26 +
  27 + constructor(protected commentService: CommentService) { }
  28 +
  29 + ngOnInit() {
  30 + if (this.parent) {
  31 + this.comments = this.parent.replies;
  32 + } else {
  33 + this.loadNextPage();
  34 + }
  35 + }
16 36
17 - constructor(private commentService: CommentService, private $rootScope: ng.IScope) {  
18 - $rootScope.$on(PostCommentComponent.EVENT_COMMENT_RECEIVED, (event: ng.IAngularEvent, comment: noosfero.Comment) => {  
19 - this.comments.push(comment); 37 + commentAdded(comment: noosfero.Comment): void {
  38 + this.comments.push(comment);
  39 + this.resetShowReply();
  40 + }
  41 +
  42 + private resetShowReply() {
  43 + this.comments.forEach((comment: noosfero.CommentViewModel) => {
  44 + comment.__show_reply = false;
20 }); 45 });
  46 + if (this.parent) {
  47 + this.parent.__show_reply = false;
  48 + }
21 } 49 }
22 50
23 - ngOnInit() {  
24 - this.commentService.getByArticle(this.article).then((result: noosfero.RestResult<noosfero.Comment[]>) => {  
25 - this.comments = result.data; 51 + loadComments() {
  52 + return this.commentService.getByArticle(this.article, { page: this.page, per_page: this.perPage });
  53 + }
  54 +
  55 + loadNextPage() {
  56 + this.loadComments().then((result: noosfero.RestResult<noosfero.Comment[]>) => {
  57 + this.comments = this.comments.concat(result.data);
  58 + this.total = result.headers ? result.headers("total") : this.comments.length;
  59 + this.page++;
26 }); 60 });
27 } 61 }
  62 +
  63 + displayMore() {
  64 + let pages = Math.ceil(this.total / this.perPage);
  65 + return !this.parent && pages >= this.page;
  66 + }
28 } 67 }
src/app/article/comment/comments.html
1 <div class="comments"> 1 <div class="comments">
2 - <noosfero-post-comment [article]="ctrl.article"></noosfero-post-comment> 2 + <noosfero-post-comment (comment-saved)="ctrl.commentAdded($event.detail)" ng-if="ctrl.showForm" [article]="ctrl.article" [parent]="ctrl.parent"></noosfero-post-comment>
3 3
4 <div class="comments-list"> 4 <div class="comments-list">
5 - <noosfero-comment ng-repeat="comment in ctrl.comments" [comment]="comment" [article]="ctrl.article"></noosfero-comment> 5 + <noosfero-comment ng-repeat="comment in ctrl.comments | orderBy: 'created_at':true" [comment]="comment" [article]="ctrl.article"></noosfero-comment>
6 </div> 6 </div>
  7 + <button type="button" ng-if="ctrl.displayMore()" class="more-comments btn btn-default btn-block" ng-click="ctrl.loadNextPage()">{{"comment.pagination.more" | translate}}</button>
7 </div> 8 </div>
src/app/article/comment/post-comment/post-comment.component.spec.ts
1 import {Provider, provide, Component} from 'ng-forward'; 1 import {Provider, provide, Component} from 'ng-forward';
2 import * as helpers from "../../../../spec/helpers"; 2 import * as helpers from "../../../../spec/helpers";
3 -  
4 import {PostCommentComponent} from './post-comment.component'; 3 import {PostCommentComponent} from './post-comment.component';
5 4
6 const htmlTemplate: string = '<noosfero-post-comment [article]="ctrl.article" [reply-of]="ctrl.comment"></noosfero-post-comment>'; 5 const htmlTemplate: string = '<noosfero-post-comment [article]="ctrl.article" [reply-of]="ctrl.comment"></noosfero-post-comment>';
@@ -11,9 +10,11 @@ describe(&quot;Components&quot;, () =&gt; { @@ -11,9 +10,11 @@ describe(&quot;Components&quot;, () =&gt; {
11 beforeEach(angular.mock.module("templates")); 10 beforeEach(angular.mock.module("templates"));
12 11
13 let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]); 12 let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]);
  13 + let user = {};
14 let providers = [ 14 let providers = [
15 new Provider('CommentService', { useValue: commentService }), 15 new Provider('CommentService', { useValue: commentService }),
16 - new Provider('NotificationService', { useValue: helpers.mocks.notificationService }) 16 + new Provider('NotificationService', { useValue: helpers.mocks.notificationService }),
  17 + new Provider('SessionService', { useValue: helpers.mocks.sessionWithCurrentUser(user) })
17 ].concat(helpers.provideFilters("translateFilter")); 18 ].concat(helpers.provideFilters("translateFilter"));
18 19
19 @Component({ selector: 'test-container-component', directives: [PostCommentComponent], template: htmlTemplate, providers: providers }) 20 @Component({ selector: 'test-container-component', directives: [PostCommentComponent], template: htmlTemplate, providers: providers })
@@ -32,10 +33,10 @@ describe(&quot;Components&quot;, () =&gt; { @@ -32,10 +33,10 @@ describe(&quot;Components&quot;, () =&gt; {
32 it("emit an event when create comment", done => { 33 it("emit an event when create comment", done => {
33 helpers.createComponentFromClass(ContainerComponent).then(fixture => { 34 helpers.createComponentFromClass(ContainerComponent).then(fixture => {
34 let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance; 35 let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
  36 + component.commentSaved.next = jasmine.createSpy("next");
35 commentService.createInArticle = jasmine.createSpy("createInArticle").and.returnValue(helpers.mocks.promiseResultTemplate({ data: {} })); 37 commentService.createInArticle = jasmine.createSpy("createInArticle").and.returnValue(helpers.mocks.promiseResultTemplate({ data: {} }));
36 - component["$rootScope"].$emit = jasmine.createSpy("$emit");  
37 component.save(); 38 component.save();
38 - expect(component["$rootScope"].$emit).toHaveBeenCalledWith(PostCommentComponent.EVENT_COMMENT_RECEIVED, jasmine.any(Object)); 39 + expect(component.commentSaved.next).toHaveBeenCalled();
39 done(); 40 done();
40 }); 41 });
41 }); 42 });
@@ -55,9 +56,9 @@ describe(&quot;Components&quot;, () =&gt; { @@ -55,9 +56,9 @@ describe(&quot;Components&quot;, () =&gt; {
55 helpers.createComponentFromClass(ContainerComponent).then(fixture => { 56 helpers.createComponentFromClass(ContainerComponent).then(fixture => {
56 let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance; 57 let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance;
57 component.comment = <any>{ reply_of_id: null }; 58 component.comment = <any>{ reply_of_id: null };
58 - component.replyOf = <any>{ id: 10 }; 59 + component.parent = <any>{ id: 10 };
59 component.save(); 60 component.save();
60 - expect(component.comment.reply_of_id).toEqual(component.replyOf.id); 61 + expect(component.comment.reply_of_id).toEqual(component.parent.id);
61 done(); 62 done();
62 }); 63 });
63 }); 64 });
src/app/article/comment/post-comment/post-comment.component.ts
1 -import { Inject, Input, Component } from 'ng-forward'; 1 +import { Inject, Input, Output, EventEmitter, Component } from 'ng-forward';
2 import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service"; 2 import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service";
3 import { NotificationService } from "../../../shared/services/notification.service"; 3 import { NotificationService } from "../../../shared/services/notification.service";
  4 +import { SessionService } from "../../../login";
4 5
5 @Component({ 6 @Component({
6 selector: 'noosfero-post-comment', 7 selector: 'noosfero-post-comment',
7 - templateUrl: 'app/article/comment/post-comment/post-comment.html' 8 + templateUrl: 'app/article/comment/post-comment/post-comment.html',
  9 + outputs: ['commentSaved']
8 }) 10 })
9 -@Inject(CommentService, NotificationService, "$rootScope") 11 +@Inject(CommentService, NotificationService, SessionService)
10 export class PostCommentComponent { 12 export class PostCommentComponent {
11 13
12 - public static EVENT_COMMENT_RECEIVED = "comment.received";  
13 -  
14 @Input() article: noosfero.Article; 14 @Input() article: noosfero.Article;
15 - @Input() replyOf: noosfero.Comment; 15 + @Input() parent: noosfero.Comment;
  16 + @Output() commentSaved: EventEmitter<Comment> = new EventEmitter<Comment>();
16 17
17 - comment: noosfero.Comment; 18 + comment = <noosfero.Comment>{};
  19 + private currentUser: noosfero.User;
18 20
19 - constructor(private commentService: CommentService, private notificationService: NotificationService, private $rootScope: ng.IScope) { } 21 + constructor(private commentService: CommentService,
  22 + private notificationService: NotificationService,
  23 + private session: SessionService) {
  24 + this.currentUser = this.session.currentUser();
  25 + }
20 26
21 save() { 27 save() {
22 - if (this.replyOf && this.comment) {  
23 - this.comment.reply_of_id = this.replyOf.id; 28 + if (this.parent && this.comment) {
  29 + this.comment.reply_of_id = this.parent.id;
24 } 30 }
25 this.commentService.createInArticle(this.article, this.comment).then((result: noosfero.RestResult<noosfero.Comment>) => { 31 this.commentService.createInArticle(this.article, this.comment).then((result: noosfero.RestResult<noosfero.Comment>) => {
26 - this.$rootScope.$emit(PostCommentComponent.EVENT_COMMENT_RECEIVED, result.data);  
27 - this.notificationService.success({ title: "Good job!", message: "Comment saved!" }); 32 + this.commentSaved.next(result.data);
  33 + this.comment.body = "";
  34 + this.notificationService.success({ title: "comment.post.success.title", message: "comment.post.success.message" });
28 }); 35 });
29 } 36 }
30 } 37 }
src/app/article/comment/post-comment/post-comment.html
1 -<form> 1 +<form class="clearfix post-comment">
2 <div class="form-group"> 2 <div class="form-group">
3 - <textarea class="form-control custom-control" rows="3" ng-model="ctrl.comment.body"></textarea> 3 + <div class="comment media">
  4 + <div class="media-left">
  5 + <a ui-sref="main.profile.home({profile: ctrl.currentUser.person.identifier})">
  6 + <noosfero-profile-image [profile]="ctrl.currentUser.person"></noosfero-profile-image>
  7 + </a>
  8 + </div>
  9 + <div class="media-body">
  10 + <textarea class="form-control custom-control" rows="1" ng-model="ctrl.comment.body" placeholder="{{'comment.post.placeholder' | translate}}"></textarea>
  11 + <button ng-show="ctrl.comment.body" type="submit" class="btn btn-default pull-right ng-hide" ng-click="ctrl.save()">{{"comment.post" | translate}}</button>
  12 + </div>
  13 + </div>
4 </div> 14 </div>
5 - <button type="submit" class="btn btn-default" ng-click="ctrl.save()">{{"comment.post" | translate}}</button>  
6 </form> 15 </form>
src/app/article/comment/post-comment/post-comment.scss 0 → 100644
@@ -0,0 +1,20 @@ @@ -0,0 +1,20 @@
  1 +.comments {
  2 + .post-comment {
  3 + .media {
  4 + border-top: 2px solid #F3F3F3;
  5 + padding-top: 10px;
  6 + .media-left {
  7 + padding: 10px 0;
  8 + }
  9 + button {
  10 + margin-top: 10px;
  11 + &.ng-hide-add {
  12 + animation: 0.5s lightSpeedOut ease;
  13 + }
  14 + &.ng-hide-remove {
  15 + animation: 0.5s lightSpeedIn ease;
  16 + }
  17 + }
  18 + }
  19 + }
  20 +}
src/app/shared/services/notification.service.ts
@@ -20,13 +20,7 @@ export class NotificationService { @@ -20,13 +20,7 @@ export class NotificationService {
20 title = NotificationService.DEFAULT_ERROR_TITLE, 20 title = NotificationService.DEFAULT_ERROR_TITLE,
21 showConfirmButton = true 21 showConfirmButton = true
22 } = {}) { 22 } = {}) {
23 - this.$log.debug("Notification error:", title, message, this.translatorService.currentLanguage());  
24 - this.SweetAlert.swal({  
25 - title: this.translatorService.translate(title),  
26 - text: this.translatorService.translate(message),  
27 - type: "error",  
28 - showConfirmButton: showConfirmButton  
29 - }); 23 + this.showMessage({ title: title, text: message, showConfirmButton: showConfirmButton, type: "error" });
30 } 24 }
31 25
32 httpError(status: number, data: any): boolean { 26 httpError(status: number, data: any): boolean {
@@ -39,11 +33,17 @@ export class NotificationService { @@ -39,11 +33,17 @@ export class NotificationService {
39 message, 33 message,
40 timer = NotificationService.DEFAULT_SUCCESS_TIMER 34 timer = NotificationService.DEFAULT_SUCCESS_TIMER
41 }) { 35 }) {
  36 + this.showMessage({ title: title, text: message, timer: timer });
  37 + }
  38 +
  39 + private showMessage({title, text, type = "success", timer = null, showConfirmButton = true}) {
  40 + this.$log.debug("Notification message:", title, text, type, this.translatorService.currentLanguage());
42 this.SweetAlert.swal({ 41 this.SweetAlert.swal({
43 - title: title,  
44 - text: message,  
45 - type: "success",  
46 - timer: timer 42 + title: this.translatorService.translate(title),
  43 + text: this.translatorService.translate(text),
  44 + type: type,
  45 + timer: timer,
  46 + showConfirmButton: showConfirmButton
47 }); 47 });
48 } 48 }
49 49
src/languages/en.json
@@ -31,5 +31,9 @@ @@ -31,5 +31,9 @@
31 "notification.http_error.401.message": "Unauthorized", 31 "notification.http_error.401.message": "Unauthorized",
32 "notification.http_error.500.message": "Server error", 32 "notification.http_error.500.message": "Server error",
33 "comment.post": "Post a comment", 33 "comment.post": "Post a comment",
  34 + "comment.post.placeholder": "Join the discussion...",
  35 + "comment.pagination.more": "More",
  36 + "comment.post.success.title": "Good job!",
  37 + "comment.post.success.message": "Comment saved!",
34 "comment.reply": "reply" 38 "comment.reply": "reply"
35 } 39 }
src/languages/pt.json
@@ -31,5 +31,9 @@ @@ -31,5 +31,9 @@
31 "notification.http_error.401.message": "Não autorizado", 31 "notification.http_error.401.message": "Não autorizado",
32 "notification.http_error.500.message": "Erro no servidor", 32 "notification.http_error.500.message": "Erro no servidor",
33 "comment.post": "Commentar", 33 "comment.post": "Commentar",
  34 + "comment.post.placeholder": "Participe da discussão...",
  35 + "comment.pagination.more": "Mais",
  36 + "comment.post.success.title": "Bom trabalho!",
  37 + "comment.post.success.message": "Comentário salvo com sucesso!",
34 "comment.reply": "responder" 38 "comment.reply": "responder"
35 } 39 }
src/lib/ng-noosfero-api/http/comment.service.spec.ts
@@ -22,8 +22,8 @@ describe(&quot;Services&quot;, () =&gt; { @@ -22,8 +22,8 @@ describe(&quot;Services&quot;, () =&gt; {
22 22
23 it("should return comments by article", (done) => { 23 it("should return comments by article", (done) => {
24 let articleId = 1; 24 let articleId = 1;
25 - $httpBackend.expectGET(`/api/v1/articles/${articleId}/comments`).respond(200, { comments: [{ name: "comment1" }] });  
26 - commentService.getByArticle(<noosfero.Article>{id: articleId}).then((result: noosfero.RestResult<noosfero.Comment[]>) => { 25 + $httpBackend.expectGET(`/api/v1/articles/${articleId}/comments?without_reply=true`).respond(200, { comments: [{ name: "comment1" }] });
  26 + commentService.getByArticle(<noosfero.Article>{ id: articleId }).then((result: noosfero.RestResult<noosfero.Comment[]>) => {
27 expect(result.data).toEqual([{ name: "comment1" }]); 27 expect(result.data).toEqual([{ name: "comment1" }]);
28 done(); 28 done();
29 }); 29 });
@@ -32,9 +32,9 @@ describe(&quot;Services&quot;, () =&gt; { @@ -32,9 +32,9 @@ describe(&quot;Services&quot;, () =&gt; {
32 32
33 it("should create a comment in an article", (done) => { 33 it("should create a comment in an article", (done) => {
34 let articleId = 1; 34 let articleId = 1;
35 - let comment: noosfero.Comment = <any>{ id: null};  
36 - $httpBackend.expectPOST(`/api/v1/articles/${articleId}/comments`, comment ).respond(200, {comment: { id: 2 }});  
37 - commentService.createInArticle(<noosfero.Article>{id: articleId}, comment).then((result: noosfero.RestResult<noosfero.Comment>) => { 35 + let comment: noosfero.Comment = <any>{ id: null };
  36 + $httpBackend.expectPOST(`/api/v1/articles/${articleId}/comments`, comment).respond(200, { comment: { id: 2 } });
  37 + commentService.createInArticle(<noosfero.Article>{ id: articleId }, comment).then((result: noosfero.RestResult<noosfero.Comment>) => {
38 expect(result.data).toEqual({ id: 2 }); 38 expect(result.data).toEqual({ id: 2 });
39 done(); 39 done();
40 }); 40 });
src/lib/ng-noosfero-api/http/comment.service.ts
@@ -21,9 +21,10 @@ export class CommentService extends RestangularService&lt;noosfero.Comment&gt; { @@ -21,9 +21,10 @@ export class CommentService extends RestangularService&lt;noosfero.Comment&gt; {
21 }; 21 };
22 } 22 }
23 23
24 - getByArticle(article: noosfero.Article, params?: any): ng.IPromise<noosfero.RestResult<noosfero.Comment[]>> { 24 + getByArticle(article: noosfero.Article, params: any = {}): ng.IPromise<noosfero.RestResult<noosfero.Comment[]>> {
  25 + params['without_reply'] = true;
25 let articleElement = this.articleService.getElement(<number>article.id); 26 let articleElement = this.articleService.getElement(<number>article.id);
26 - return this.list(articleElement); 27 + return this.list(articleElement, params);
27 } 28 }
28 29
29 createInArticle(article: noosfero.Article, comment: noosfero.Comment): ng.IPromise<noosfero.RestResult<noosfero.Comment>> { 30 createInArticle(article: noosfero.Article, comment: noosfero.Comment): ng.IPromise<noosfero.RestResult<noosfero.Comment>> {
src/lib/ng-noosfero-api/interfaces/comment.ts
1 namespace noosfero { 1 namespace noosfero {
2 export interface Comment extends RestModel { 2 export interface Comment extends RestModel {
3 reply_of_id: number; 3 reply_of_id: number;
  4 + reply_of: Comment;
  5 + replies: Comment[];
  6 + body: string;
  7 + }
  8 +
  9 + export interface CommentViewModel extends Comment {
  10 + __show_reply?: boolean;
4 } 11 }
5 } 12 }