From 64b348aab7aefb9b7a076e2c8f44215e929d70c6 Mon Sep 17 00:00:00 2001 From: Michel Felipe Date: Tue, 24 May 2016 16:03:46 -0300 Subject: [PATCH] Closes #55 - EventEmitter by object structures using Typescript class Mixins --- src/app/article/comment/comments.component.spec.ts | 3 +-- src/app/article/comment/comments.component.ts | 21 ++++++++++++++++----- src/app/article/comment/post-comment/post-comment.component.spec.ts | 6 +++--- src/app/article/comment/post-comment/post-comment.component.ts | 18 +++++++++++++++++- src/lib/ng-noosfero-api/decorators/mixins.ts | 9 +++++++++ src/lib/ng-noosfero-api/http/comment.service.ts | 14 ++++++++++++-- src/lib/ng-noosfero-api/mixins/event-service.mixin.ts | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts | 23 +++++++++++++++++------ src/plugins/comment_paragraph/side-comments/side-comments.component.ts | 2 +- src/plugins/comment_paragraph/side-comments/side-comments.html | 8 ++++++++ src/spec/mocks.ts | 13 ++++--------- 11 files changed, 192 insertions(+), 29 deletions(-) create mode 100644 src/lib/ng-noosfero-api/decorators/mixins.ts create mode 100644 src/lib/ng-noosfero-api/mixins/event-service.mixin.ts create mode 100644 src/plugins/comment_paragraph/side-comments/side-comments.html diff --git a/src/app/article/comment/comments.component.spec.ts b/src/app/article/comment/comments.component.spec.ts index 6ce43c5..bc018c7 100644 --- a/src/app/article/comment/comments.component.spec.ts +++ b/src/app/article/comment/comments.component.spec.ts @@ -11,8 +11,7 @@ describe("Components", () => { beforeEach(angular.mock.module("templates")); - let commentService = jasmine.createSpyObj("commentService", ["getByArticle"]); - commentService.onSave = helpers.mocks.commentService.onSave; + let commentService = helpers.mocks.commentService; let comments = [{ id: 2 }, { id: 3 }]; commentService.getByArticle = jasmine.createSpy("getByArticle") diff --git a/src/app/article/comment/comments.component.ts b/src/app/article/comment/comments.component.ts index 5b85eb2..0c54f4b 100644 --- a/src/app/article/comment/comments.component.ts +++ b/src/app/article/comment/comments.component.ts @@ -8,7 +8,7 @@ import { CommentComponent } from "./comment.component"; templateUrl: 'app/article/comment/comments.html', directives: [PostCommentComponent, CommentComponent] }) -@Inject(CommentService, "$element") +@Inject(CommentService, "$scope") export class CommentsComponent { comments: noosfero.CommentViewModel[] = []; @@ -30,17 +30,28 @@ export class CommentsComponent { this.loadNextPage(); } - this.commentService.onSave.subscribe((comment: noosfero.Comment) => { - this.commentAdded(comment); + this.commentService.addEvent({ + event: 'onSave', + component: 'CommentsComponent', + callback: (comment: noosfero.Comment) => { + + if (this.commentService.called !== comment.id.toString()) { + + this.commentAdded(comment); + this.commentService.called = comment.id.toString(); + } + } }); + } - commentAdded(comment: noosfero.Comment): void { + commentAdded(comment: noosfero.CommentViewModel): void { + comment.__show_reply = false; this.comments.push(comment); this.resetShowReply(); + this.$scope.$apply(); } - loadComments() { return this.commentService.getByArticle(this.article, { page: this.page, per_page: this.perPage }); } diff --git a/src/app/article/comment/post-comment/post-comment.component.spec.ts b/src/app/article/comment/post-comment/post-comment.component.spec.ts index 634b280..b81d8e8 100644 --- a/src/app/article/comment/post-comment/post-comment.component.spec.ts +++ b/src/app/article/comment/post-comment/post-comment.component.spec.ts @@ -11,8 +11,8 @@ describe("Components", () => { beforeEach(angular.mock.module("templates")); - let commentService = jasmine.createSpyObj("commentService", ["createInArticle"]); - commentService.onSave = jasmine.createSpyObj("onSave", ["subscribe", "next"]); + let commentService = helpers.mocks.commentService; + commentService.emitEvent = jasmine.createSpy("emitEvent"); let user = {}; let providers = [ new Provider('CommentService', { useValue: commentService }), @@ -46,7 +46,7 @@ describe("Components", () => { let component: PostCommentComponent = fixture.debugElement.componentViewChildren[0].componentInstance; commentService.createInArticle = jasmine.createSpy("createInArticle").and.returnValue(helpers.mocks.promiseResultTemplate({ data: {} })); component.save(); - expect(component.commentService.onSave.next).toHaveBeenCalled(); + expect(component.commentService.emitEvent).toHaveBeenCalled(); done(); }); }); diff --git a/src/app/article/comment/post-comment/post-comment.component.ts b/src/app/article/comment/post-comment/post-comment.component.ts index 8392122..3e3c686 100644 --- a/src/app/article/comment/post-comment/post-comment.component.ts +++ b/src/app/article/comment/post-comment/post-comment.component.ts @@ -1,3 +1,4 @@ +import {ngClass} from 'ng-forward/cjs/testing/test-component-builder'; import { Inject, Input, Output, EventEmitter, Component } from 'ng-forward'; import { CommentService } from "../../../../lib/ng-noosfero-api/http/comment.service"; import { NotificationService } from "../../../shared/services/notification.service"; @@ -18,6 +19,7 @@ export class PostCommentComponent { @Input() article: noosfero.Article; @Input() parent: noosfero.Comment; @Input() comment = {}; + @Input() identifier: any; private currentUser: noosfero.User; constructor( @@ -33,7 +35,21 @@ export class PostCommentComponent { } this.commentService.createInArticle(this.article, this.comment).then((result: noosfero.RestResult) => { - this.commentService.onSave.next(result.data); + if (this.identifier) { + this.commentService.emitEvent({ + id: this.identifier, + event: 'onSave', + param: result.data + }); + } else { + this.commentService.emitEvent({ + id: result.data.id, + component: 'CommentsComponent', + event: 'onSave', + param: result.data + }); + } + this.comment.body = ""; this.notificationService.success({ title: "comment.post.success.title", message: "comment.post.success.message" }); }); diff --git a/src/lib/ng-noosfero-api/decorators/mixins.ts b/src/lib/ng-noosfero-api/decorators/mixins.ts new file mode 100644 index 0000000..9d3191d --- /dev/null +++ b/src/lib/ng-noosfero-api/decorators/mixins.ts @@ -0,0 +1,9 @@ +export function Mixins(...mixins: Function[]) { + return function(target: Function) { + mixins.forEach(mixin => { + Object.getOwnPropertyNames(mixin.prototype).forEach(name => { + target.prototype[name] = mixin.prototype[name]; + }); + }); + }; +} diff --git a/src/lib/ng-noosfero-api/http/comment.service.ts b/src/lib/ng-noosfero-api/http/comment.service.ts index e051007..f339ef1 100644 --- a/src/lib/ng-noosfero-api/http/comment.service.ts +++ b/src/lib/ng-noosfero-api/http/comment.service.ts @@ -1,12 +1,17 @@ import { Injectable, Inject, EventEmitter } from "ng-forward"; import {RestangularService} from "./restangular_service"; import {ArticleService} from "./article.service"; +import {Mixins, EventService, IEvent} from '../mixins/event-service.mixin'; + @Injectable() +@Mixins(EventService) @Inject("Restangular", "$q", "$log", ArticleService) -export class CommentService extends RestangularService { +export class CommentService extends RestangularService implements EventService { + + events: IEvent[] = []; - public onSave: EventEmitter = new EventEmitter(); + called: string; constructor(Restangular: restangular.IService, $q: ng.IQService, $log: ng.ILogService, protected articleService: ArticleService) { super(Restangular, $q, $log); @@ -33,4 +38,9 @@ export class CommentService extends RestangularService { let articleElement = this.articleService.getElement(article.id); return this.create(comment, articleElement, null, { 'Content-Type': 'application/json' }, false); } + + // Mixin "EventService" declarations + addEvent: (data: { event: string, callback: Function, id?: any, component?: string, scope?: any }) => void; + getEvents: (data: { event: string, id?: any, component?: string }) => void; + emitEvent: (data: { event: string, param: T, id?: any, component?: string }) => void; } diff --git a/src/lib/ng-noosfero-api/mixins/event-service.mixin.ts b/src/lib/ng-noosfero-api/mixins/event-service.mixin.ts new file mode 100644 index 0000000..23d33b7 --- /dev/null +++ b/src/lib/ng-noosfero-api/mixins/event-service.mixin.ts @@ -0,0 +1,104 @@ +import { EventEmitter } from "ng-forward"; +export {Mixins} from "../decorators/mixins"; + +export interface IEvent { + id: any; + component: string; + emitters: { + onSave?: EventEmitter + }; +} + +/** + * @ngdoc object + * @name mixins.EventService + * @see Typescript official documentation https://www.typescriptlang.org/docs/mixins.html + * @description + * + * A mixin class to be implemented by another class (e.g. Angular Services) + * to emit's and subscribe events + * + *
+ * @Mixins(EventService)
+ * export class MyService implements EventService {
+ *
+ * }
+ * 
+ */ +export class EventService { + + events: IEvent[] = []; + + called: string; + + addEvent(data: { event: string, callback: Function, id?: any, component?: string, scope?: any }) { + + let hasEvent = false; + + if (this.events.length > 0) { + + this.events.forEach((eventData: IEvent, index: number) => { + if (data.id === eventData.id) { + + if (!(this.events)[index].emitters[data.event]) { + (this.events)[index].emitters[data.event] = new EventEmitter(); + } else { + (>(this.events)[index].emitters[data.event]).subscribe(data.callback.bind(data.scope || this)); + } + + return (hasEvent = true); + } + }); + } + + if (!hasEvent) { + + let newEvent: IEvent = { + id: data.id, + component: data.component, + emitters: {} + }; + + (newEvent.emitters)[data.event] = >new EventEmitter(); + (newEvent.emitters)[data.event].subscribe(data.callback); + + this.events.push(newEvent); + } + } + + getEvents(data: { event: string, id?: any, component?: string }): any { + let result: any = {}; + + for (let i = 0; i < this.events.length; i++) { + if (data.id === (>this.events)[i].id && !data.component) { + + result = (this.events)[i].emitters[data.event]; + break; + } else if (data.component === (this.events)[i].component) { + result[data.component] = (this.events)[i].emitters[data.event]; + + break; + } else { + result[(this.events)[i].component] = (this.events)[i].emitters[data.event]; + } + } + + return result; + } + + emitEvent(data: { event: string, param: T, id?: any, component?: string }) { + + let eventOrEmitters = this.getEvents(data); + + if (eventOrEmitters instanceof EventEmitter) { + + eventOrEmitters.next(data.param); + } else { + + Object.keys(eventOrEmitters).forEach((emitter: string) => { + (>eventOrEmitters[emitter]).next(data.param); + }); + + } + } +} diff --git a/src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts b/src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts index 6ee559b..cc6c46a 100644 --- a/src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts +++ b/src/plugins/comment_paragraph/allow-comment/allow-comment.component.ts @@ -16,26 +16,37 @@ export class AllowCommentComponent { @Input() content: string; @Input() paragraphUuid: string; @Input() article: noosfero.Article; - commentsCount: number; + commentsCount: number = 0; display = false; - constructor(private $scope: ng.IScope, + constructor( + private $scope: ng.IScope, private commentParagraphEventService: CommentParagraphEventService, private commentParagraphService: CommentParagraphService, - private commentService: CommentService) { } + private commentService: CommentService + ) { } ngOnInit() { this.commentParagraphEventService.subscribeToggleCommentParagraph((article: noosfero.Article) => { this.article = article; this.$scope.$apply(); }); - this.commentParagraphService.commentParagraphCount(this.article, this.paragraphUuid).then((count: number) => { this.commentsCount = count; }); - this.commentService.onSave.subscribe((comment: noosfero.Comment) => { - this.commentsCount++; + this.commentService.addEvent({ + id: this.paragraphUuid, + event: 'onSave', + component: 'SideCommentsComponent', + scope: this, + callback: () => { + if (!this.commentsCount) { + this.commentsCount = 0; + } + this.commentsCount++; + this.$scope.$apply(); + } }); } diff --git a/src/plugins/comment_paragraph/side-comments/side-comments.component.ts b/src/plugins/comment_paragraph/side-comments/side-comments.component.ts index f8f019f..b5db748 100644 --- a/src/plugins/comment_paragraph/side-comments/side-comments.component.ts +++ b/src/plugins/comment_paragraph/side-comments/side-comments.component.ts @@ -5,7 +5,7 @@ import {CommentParagraphService} from "../http/comment-paragraph.service"; @Component({ selector: "comment-paragraph-side-comments", - templateUrl: 'app/article/comment/comments.html', + templateUrl: 'plugins/comment_paragraph/side-comments/side-comments.html', }) @Inject(CommentService, "$scope", CommentParagraphService) export class SideCommentsComponent extends CommentsComponent { diff --git a/src/plugins/comment_paragraph/side-comments/side-comments.html b/src/plugins/comment_paragraph/side-comments/side-comments.html new file mode 100644 index 0000000..6a59e68 --- /dev/null +++ b/src/plugins/comment_paragraph/side-comments/side-comments.html @@ -0,0 +1,8 @@ +
+ + +
+ +
+ +
diff --git a/src/spec/mocks.ts b/src/spec/mocks.ts index 20cf50e..01de9b8 100644 --- a/src/spec/mocks.ts +++ b/src/spec/mocks.ts @@ -122,18 +122,13 @@ export var mocks: any = { instant: () => { } }, commentService: { + events: [], + called: '', getByArticle: (article: noosfero.Article) => { return Promise.resolve({ data: {} }); }, - onSave: { - event: Function, - subscribe: (fn: Function) => { - mocks.commentService['onSave'].event = fn; - }, - next: (param: any) => { - mocks.commentService['onSave'].event(param); - } - } + addEvent: () => { }, + emitEvent: () => { } }, sessionWithCurrentUser: (user: any) => { return { -- libgit2 0.21.2