Commit fb1e923a176adbb9d007beba3747eef35e892d2a

Authored by Victor Costa
2 parents 8784fe3c 328a7a82

Merge branch 'events-hub' into 'master'

Events hub

Added a service called EventsHubService to serve as central point for events propagated through the Noosfero Angular Application.

See merge request !53
src/app/index.ts
1 -import {bootstrap} from "ng-forward"; 1 +import {bootstrap, provide} from "ng-forward";
2 import {noosferoModuleConfig} from "./index.config"; 2 import {noosferoModuleConfig} from "./index.config";
3 import {noosferoAngularRunBlock} from "./index.run"; 3 import {noosferoAngularRunBlock} from "./index.run";
4 import {MainComponent} from "./main/main.component"; 4 import {MainComponent} from "./main/main.component";
@@ -22,4 +22,13 @@ angular.module('noosfero.init', ['noosfero.templates.app', 'noosfero.templates.p @@ -22,4 +22,13 @@ angular.module('noosfero.init', ['noosfero.templates.app', 'noosfero.templates.p
22 run(noosferoAngularRunBlock). 22 run(noosferoAngularRunBlock).
23 constant("moment", moment). 23 constant("moment", moment).
24 constant("AuthEvents", AuthEvents); 24 constant("AuthEvents", AuthEvents);
25 -bootstrap(MainComponent); 25 +
  26 +
  27 +import { EVENTS_HUB_KNOW_EVENT_NAMES } from './shared/services/events-hub.service';
  28 +import { NoosferoKnownEvents } from './known-events';
  29 +
  30 +bootstrap(MainComponent,
  31 + [
  32 + provide(EVENTS_HUB_KNOW_EVENT_NAMES, { useClass: NoosferoKnownEvents })
  33 + ]
  34 +);
src/app/known-events.ts 0 → 100644
@@ -0,0 +1,14 @@ @@ -0,0 +1,14 @@
  1 +import { EventsHubKnownEventNames } from './shared/services/events-hub.service';
  2 +
  3 +export class NoosferoKnownEvents implements EventsHubKnownEventNames {
  4 + IMAGE_PROFILE_UPDATED: string = 'IMAGE_PROFILE_UPDATED';
  5 + PROFILE_INFO_UPDATED: string = 'PROFILE_INFO_UPDATED';
  6 + ARTICLE_UPDATED: string = 'ARTICLE_UPDATED';
  7 +
  8 + constructor() {
  9 + }
  10 +
  11 + getNames() {
  12 + return Object.getOwnPropertyNames(this);
  13 + }
  14 +}
0 \ No newline at end of file 15 \ No newline at end of file
src/app/main/main.component.spec.ts
@@ -4,8 +4,9 @@ import {TestComponentBuilder, ComponentFixture} from "ng-forward/cjs/testing/tes @@ -4,8 +4,9 @@ import {TestComponentBuilder, ComponentFixture} from "ng-forward/cjs/testing/tes
4 4
5 import {quickCreateComponent} from "../../spec/helpers"; 5 import {quickCreateComponent} from "../../spec/helpers";
6 import {getAngularServiceFactory} from "../../spec/helpers"; 6 import {getAngularServiceFactory} from "../../spec/helpers";
  7 +import { EVENTS_HUB_KNOW_EVENT_NAMES } from "../shared/services/events-hub.service";
7 8
8 -describe("MainComponent", function() { 9 +describe("MainComponent", function () {
9 10
10 let localFixture: ComponentFixture; 11 let localFixture: ComponentFixture;
11 let $state: angular.ui.IStateService; 12 let $state: angular.ui.IStateService;
@@ -34,6 +35,14 @@ describe("MainComponent", function() { @@ -34,6 +35,14 @@ describe("MainComponent", function() {
34 { 35 {
35 useValue: environmentService 36 useValue: environmentService
36 }), 37 }),
  38 + provide(EVENTS_HUB_KNOW_EVENT_NAMES,
  39 + {
  40 + useValue: [
  41 + 'IMAGE_PROFILE_UPDATED',
  42 + 'PROFILE_INFO_UPDATED',
  43 + 'ARTICLE_UPDATED'
  44 + ]
  45 + }),
37 ] 46 ]
38 }) 47 })
39 class MainComponentParent { 48 class MainComponentParent {
@@ -57,7 +66,7 @@ describe("MainComponent", function() { @@ -57,7 +66,7 @@ describe("MainComponent", function() {
57 // navigates to the environment home 66 // navigates to the environment home
58 $state.go("main.environment.home"); 67 $state.go("main.environment.home");
59 localFixture.detectChanges(); 68 localFixture.detectChanges();
60 - // after changes were detected it checks the current $state route 69 + // after changes were detected it checks the current $state route
61 expect($state.current.name).toEqual("main.environment.home"); 70 expect($state.current.name).toEqual("main.environment.home");
62 done(); 71 done();
63 }); 72 });
src/app/main/main.component.ts
1 import * as plugins from "../../plugins"; 1 import * as plugins from "../../plugins";
2 -import {bundle, Component, StateConfig, Inject} from "ng-forward";  
3 -import {ArticleBlogComponent} from "./../article/types/blog/blog.component";  
4 -  
5 -import {ArticleViewComponent} from "./../article/article-default-view.component";  
6 -  
7 -import {ProfileComponent} from "../profile/profile.component";  
8 -import {BoxesComponent} from "../layout/boxes/boxes.component";  
9 -import {BlockContentComponent} from "../layout/blocks/block-content.component";  
10 -import {BlockComponent} from "../layout/blocks/block.component";  
11 -import {EnvironmentComponent} from "../environment/environment.component";  
12 -import {EnvironmentHomeComponent} from "../environment/environment-home.component";  
13 -import {PeopleBlockComponent} from "../layout/blocks/people/people-block.component";  
14 -import {DisplayContentBlockComponent} from "../layout/blocks/display-content/display-content-block.component";  
15 -import {LinkListBlockComponent} from "../layout/blocks/link-list/link-list-block.component";  
16 -import {RecentDocumentsBlockComponent} from "../layout/blocks/recent-documents/recent-documents-block.component";  
17 -import {ProfileImageBlockComponent} from "../layout/blocks/profile-image/profile-image-block.component";  
18 -import {RawHTMLBlockComponent} from "../layout/blocks/raw-html/raw-html-block.component";  
19 -import {StatisticsBlockComponent} from "../layout/blocks/statistics/statistics-block.component";  
20 -import {PersonTagsPluginInterestsBlockComponent} from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";  
21 -import {TagsBlockComponent} from "../layout/blocks/tags/tags-block.component";  
22 -import {CustomContentComponent} from "../profile/custom-content/custom-content.component";  
23 -import {RecentActivitiesPluginActivitiesBlockComponent} from "../layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component";  
24 -  
25 -import {MembersBlockComponent} from "../layout/blocks/members/members-block.component";  
26 -import {CommunitiesBlockComponent} from "../layout/blocks/communities/communities-block.component";  
27 -  
28 -import {LoginBlockComponent} from "../layout/blocks/login-block/login-block.component";  
29 -  
30 -import {NoosferoTemplate} from "../shared/pipes/noosfero-template.filter";  
31 -import {DateFormat} from "../shared/pipes/date-format.filter";  
32 -  
33 -import {AuthService} from "../login/auth.service";  
34 -import {SessionService} from "../login/session.service";  
35 -import {EnvironmentService} from "./../../lib/ng-noosfero-api/http/environment.service";  
36 -import {NotificationService} from "../shared/services/notification.service";  
37 -  
38 -import {BodyStateClassesService} from "./../layout/services/body-state-classes.service";  
39 -  
40 -import {Navbar} from "../layout/navbar/navbar";  
41 -  
42 -import {SidebarComponent} from "../layout/sidebar/sidebar.component";  
43 -  
44 -import {MainBlockComponent} from "../layout/blocks/main/main-block.component";  
45 -import {HtmlEditorComponent} from "../shared/components/html-editor/html-editor.component";  
46 -import {PermissionDirective} from "../shared/components/permission/permission.directive";  
47 -import {SearchComponent} from "../search/search.component";  
48 -import {SearchFormComponent} from "../search/search-form/search-form.component";  
49 - 2 +import { bundle, Component, StateConfig, Inject } from "ng-forward";
  3 +import { ArticleBlogComponent } from "./../article/types/blog/blog.component";
  4 +
  5 +import { ArticleViewComponent } from "./../article/article-default-view.component";
  6 +
  7 +import { ProfileComponent } from "../profile/profile.component";
  8 +import { BoxesComponent } from "../layout/boxes/boxes.component";
  9 +import { BlockContentComponent } from "../layout/blocks/block-content.component";
  10 +import { BlockComponent } from "../layout/blocks/block.component";
  11 +import { EnvironmentComponent } from "../environment/environment.component";
  12 +import { EnvironmentHomeComponent } from "../environment/environment-home.component";
  13 +import { PeopleBlockComponent } from "../layout/blocks/people/people-block.component";
  14 +import { DisplayContentBlockComponent } from "../layout/blocks/display-content/display-content-block.component";
  15 +import { LinkListBlockComponent } from "../layout/blocks/link-list/link-list-block.component";
  16 +import { RecentDocumentsBlockComponent } from "../layout/blocks/recent-documents/recent-documents-block.component";
  17 +import { ProfileImageBlockComponent } from "../layout/blocks/profile-image/profile-image-block.component";
  18 +import { RawHTMLBlockComponent } from "../layout/blocks/raw-html/raw-html-block.component";
  19 +import { StatisticsBlockComponent } from "../layout/blocks/statistics/statistics-block.component";
  20 +import { PersonTagsPluginInterestsBlockComponent } from "../layout/blocks/person-tags-plugin-interests/person-tags-plugin-interests-block.component";
  21 +import { TagsBlockComponent } from "../layout/blocks/tags/tags-block.component";
  22 +import { CustomContentComponent } from "../profile/custom-content/custom-content.component";
  23 +import { RecentActivitiesPluginActivitiesBlockComponent } from "../layout/blocks/recent-activities-plugin-activities/recent-activities-plugin-activities-block.component";
  24 +
  25 +import { MembersBlockComponent } from "../layout/blocks/members/members-block.component";
  26 +import { CommunitiesBlockComponent } from "../layout/blocks/communities/communities-block.component";
  27 +
  28 +import { LoginBlockComponent } from "../layout/blocks/login-block/login-block.component";
  29 +
  30 +import { NoosferoTemplate } from "../shared/pipes/noosfero-template.filter";
  31 +import { DateFormat } from "../shared/pipes/date-format.filter";
  32 +
  33 +import { AuthService } from "../login/auth.service";
  34 +import { SessionService } from "../login/session.service";
  35 +import { EnvironmentService } from "./../../lib/ng-noosfero-api/http/environment.service";
  36 +import { NotificationService } from "../shared/services/notification.service";
  37 +
  38 +import { BodyStateClassesService } from "./../layout/services/body-state-classes.service";
  39 +
  40 +import { Navbar } from "../layout/navbar/navbar";
  41 +
  42 +import { SidebarComponent } from "../layout/sidebar/sidebar.component";
  43 +
  44 +import { MainBlockComponent } from "../layout/blocks/main/main-block.component";
  45 +import { HtmlEditorComponent } from "../shared/components/html-editor/html-editor.component";
  46 +import { PermissionDirective } from "../shared/components/permission/permission.directive";
  47 +import { SearchComponent } from "../search/search.component";
  48 +import { SearchFormComponent } from "../search/search-form/search-form.component";
  49 +
  50 +import { EVENTS_HUB_KNOW_EVENT_NAMES, EventsHubService } from "../shared/services/events-hub.service";
  51 +import { NoosferoKnownEvents } from "../known-events";
50 /** 52 /**
51 * @ngdoc controller 53 * @ngdoc controller
52 * @name main.MainContentComponent 54 * @name main.MainContentComponent
@@ -62,12 +64,16 @@ import {SearchFormComponent} from "../search/search-form/search-form.component"; @@ -62,12 +64,16 @@ import {SearchFormComponent} from "../search/search-form/search-form.component";
62 templateUrl: "app/main/main.html", 64 templateUrl: "app/main/main.html",
63 providers: [AuthService, SessionService] 65 providers: [AuthService, SessionService]
64 }) 66 })
65 -@Inject(BodyStateClassesService) 67 +@Inject(BodyStateClassesService, EVENTS_HUB_KNOW_EVENT_NAMES)
66 export class MainContentComponent { 68 export class MainContentComponent {
67 69
68 public themeSkin: string = 'skin-whbl'; 70 public themeSkin: string = 'skin-whbl';
69 71
70 - constructor(private bodyStateClassesService: BodyStateClassesService) { 72 + constructor(
  73 + private bodyStateClassesService: BodyStateClassesService,
  74 + eventsNames: NoosferoKnownEvents,
  75 + eventsHubService: EventsHubService
  76 + ) {
71 bodyStateClassesService.start({ 77 bodyStateClassesService.start({
72 skin: this.themeSkin 78 skin: this.themeSkin
73 }); 79 });
src/app/shared/services/events-hub.service.spec.ts 0 → 100644
@@ -0,0 +1,46 @@ @@ -0,0 +1,46 @@
  1 +import { OpaqueToken } from 'ng-forward';
  2 +import { EventsHubService, EventsHubKnownEventNames } from './events-hub.service';
  3 +
  4 +
  5 +describe("EventsHubService", () => {
  6 + let eventsHubService: EventsHubService;
  7 + let event1 = 'Event 1';
  8 + let eventsHubKnownEventNames = <EventsHubKnownEventNames>{ getNames: () => { return [ event1]; }};
  9 + it("emits events for the known events", (done) => {
  10 +
  11 + let eventListener = () => {
  12 + };
  13 + // creates the events hub service which known the event "Event1"
  14 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  15 + // subscribe to the event passing the done Function as the eventListener
  16 + // if the event emits works the done function is called and the
  17 + // test will pass
  18 + eventsHubService.subscribeToEvent<any>(event1, done);
  19 + // emits the event
  20 + eventsHubService.emitEvent(event1, null);
  21 + });
  22 +
  23 + it("throws error when trying to emit an unknow event", () => {
  24 + let eventListener = () => {
  25 + };
  26 + // creates the events hub service which known the event "Event1"
  27 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  28 +
  29 + // emits the event
  30 + expect(
  31 + () => { eventsHubService.emitEvent('NotKnownEvent', null); }
  32 + ).toThrowError('Unknown event named NotKnownEvent');
  33 + });
  34 +
  35 + it("throws error when trying to subscribe to an unknow event", () => {
  36 + let eventListener = () => {
  37 + };
  38 + // creates the events hub service which known the event "Event1"
  39 + eventsHubService = new EventsHubService(eventsHubKnownEventNames);
  40 +
  41 + // emits the event
  42 + expect(
  43 + () => { eventsHubService.subscribeToEvent<void>('NotKnownEvent', () => {}); }
  44 + ).toThrowError('Unknown event named NotKnownEvent');
  45 + });
  46 +});
0 \ No newline at end of file 47 \ No newline at end of file
src/app/shared/services/events-hub.service.ts 0 → 100644
@@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
  1 +import { Injectable, Inject, OpaqueToken, EventEmitter } from 'ng-forward';
  2 +
  3 +export const EVENTS_HUB_KNOW_EVENT_NAMES = new OpaqueToken('EVENTS_HUB_KNOW_EVENT_NAMES');
  4 +
  5 +export interface EventsHubKnownEventNames {
  6 + getNames(): string[];
  7 +}
  8 +
  9 +function isEventsHubKnownEventNames(object: any): object is EventsHubKnownEventNames {
  10 + return 'getNames' in object;
  11 +}
  12 +
  13 +@Injectable()
  14 +@Inject(EVENTS_HUB_KNOW_EVENT_NAMES)
  15 +export class EventsHubService {
  16 +
  17 + private emitters: Map<string, EventEmitter<any>>;
  18 + private knownEvents: string[] = [];
  19 +
  20 + constructor(private eventsHubKnownEventNames: EventsHubKnownEventNames | string[]) {
  21 + if (isEventsHubKnownEventNames(eventsHubKnownEventNames)) {
  22 + this.knownEvents = eventsHubKnownEventNames.getNames();
  23 + } else if (Array.isArray(eventsHubKnownEventNames)) {
  24 + this.knownEvents = eventsHubKnownEventNames;
  25 + }
  26 +
  27 + this.emitters = new Map<string, EventEmitter<any>>();
  28 + this.setupEmitters();
  29 + }
  30 +
  31 + emitEvent(eventType: string, payload?: any) {
  32 + this.checkKnownEvent(eventType);
  33 + let event = this.emitters.get(eventType);
  34 + if ( event ) this.emitters.get(eventType).next(payload);
  35 + }
  36 +
  37 + subscribeToEvent<T>(eventType: string, generatorOrNext?: ((p?: T) => void), error?: any, complete?: any) {
  38 + this.checkKnownEvent(eventType);
  39 + let event = this.emitters.get(eventType);
  40 + if (event) event.subscribe(generatorOrNext, error, complete);
  41 + }
  42 +
  43 + private setupEmitters() {
  44 + for (let i: number = 0; i < this.knownEvents.length; i++) {
  45 + this.emitters.set(this.knownEvents[i], new EventEmitter<any>());
  46 + }
  47 + }
  48 +
  49 + private checkKnownEvent(eventType: string) {
  50 + if (!this.emitters.has(eventType)) {
  51 + throw new Error('Unknown event named ' + eventType.toString());
  52 + }
  53 + }
  54 +
  55 +
  56 +}
0 \ No newline at end of file 57 \ No newline at end of file