Commit f3cb95031661c0c9545ba4371117fe02f8a6b95f
Exists in
master
and in
7 other branches
Merge branch 'join-community' into 'master'
Join community Add a button to join/leave communities. See merge request !54
Showing
7 changed files
with
169 additions
and
1 deletions
Show diff stats
src/app/layout/blocks/profile-image/profile-image-block.component.spec.ts
@@ -15,11 +15,18 @@ describe("Components", () => { | @@ -15,11 +15,18 @@ describe("Components", () => { | ||
15 | 15 | ||
16 | beforeEach(angular.mock.module("templates")); | 16 | beforeEach(angular.mock.module("templates")); |
17 | 17 | ||
18 | + let profileService = jasmine.createSpyObj("ProfileService", ["isMember", "addMember", "removeMember"]); | ||
19 | + profileService.isMember = jasmine.createSpy("isMember").and.returnValue(Promise.resolve(false)); | ||
20 | + | ||
18 | @Component({ | 21 | @Component({ |
19 | selector: 'test-container-component', | 22 | selector: 'test-container-component', |
20 | template: htmlTemplate, | 23 | template: htmlTemplate, |
21 | directives: [ProfileImageBlockComponent], | 24 | directives: [ProfileImageBlockComponent], |
22 | - providers: helpers.provideFilters("translateFilter") | 25 | + providers: [ |
26 | + helpers.createProviderToValue('SessionService', helpers.mocks.sessionWithCurrentUser({})), | ||
27 | + helpers.createProviderToValue('ProfileService', profileService), | ||
28 | + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService) | ||
29 | + ].concat(helpers.provideFilters("translateFilter")) | ||
23 | }) | 30 | }) |
24 | class BlockContainerComponent { | 31 | class BlockContainerComponent { |
25 | block = { type: 'Block' }; | 32 | block = { type: 'Block' }; |
@@ -42,5 +49,42 @@ describe("Components", () => { | @@ -42,5 +49,42 @@ describe("Components", () => { | ||
42 | }); | 49 | }); |
43 | }); | 50 | }); |
44 | 51 | ||
52 | + it("display button to join community", (done: Function) => { | ||
53 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | ||
54 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | ||
55 | + expect(elProfile.query('.actions .join').length).toEqual(1); | ||
56 | + done(); | ||
57 | + }); | ||
58 | + }); | ||
59 | + | ||
60 | + it("display button to leave community", (done: Function) => { | ||
61 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | ||
62 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | ||
63 | + elProfile.componentInstance['isMember'] = true; | ||
64 | + fixture.detectChanges(); | ||
65 | + expect(elProfile.query('.actions .leave').length).toEqual(1); | ||
66 | + done(); | ||
67 | + }); | ||
68 | + }); | ||
69 | + | ||
70 | + it("join community", (done: Function) => { | ||
71 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | ||
72 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | ||
73 | + profileService.addMember = jasmine.createSpy("addMember").and.returnValue(Promise.resolve({ data: {} })); | ||
74 | + elProfile.componentInstance.join(); | ||
75 | + expect(profileService.addMember).toHaveBeenCalled(); | ||
76 | + done(); | ||
77 | + }); | ||
78 | + }); | ||
79 | + | ||
80 | + it("leave community", (done: Function) => { | ||
81 | + helpers.tcb.createAsync(BlockContainerComponent).then(fixture => { | ||
82 | + let elProfile = fixture.debugElement.componentViewChildren[0]; | ||
83 | + profileService.removeMember = jasmine.createSpy("removeMember").and.returnValue(Promise.resolve({ data: {} })); | ||
84 | + elProfile.componentInstance.leave(); | ||
85 | + expect(profileService.removeMember).toHaveBeenCalled(); | ||
86 | + done(); | ||
87 | + }); | ||
88 | + }); | ||
45 | }); | 89 | }); |
46 | }); | 90 | }); |
src/app/layout/blocks/profile-image/profile-image-block.component.ts
1 | import {Inject, Input, Component} from "ng-forward"; | 1 | import {Inject, Input, Component} from "ng-forward"; |
2 | import {ProfileImageComponent} from "./../../../profile/image/image.component"; | 2 | import {ProfileImageComponent} from "./../../../profile/image/image.component"; |
3 | +import {ProfileService} from "../../../../lib/ng-noosfero-api/http/profile.service"; | ||
4 | +import {SessionService} from "./../../../login"; | ||
5 | +import {NotificationService} from "../../../shared/services/notification.service"; | ||
3 | 6 | ||
4 | @Component({ | 7 | @Component({ |
5 | selector: "noosfero-profile-image-block", | 8 | selector: "noosfero-profile-image-block", |
6 | templateUrl: 'app/layout/blocks/profile-image/profile-image-block.html', | 9 | templateUrl: 'app/layout/blocks/profile-image/profile-image-block.html', |
7 | directives: [ProfileImageComponent] | 10 | directives: [ProfileImageComponent] |
8 | }) | 11 | }) |
12 | +@Inject(ProfileService, SessionService, NotificationService) | ||
9 | export class ProfileImageBlockComponent { | 13 | export class ProfileImageBlockComponent { |
10 | 14 | ||
11 | @Input() block: noosfero.Block; | 15 | @Input() block: noosfero.Block; |
12 | @Input() owner: noosfero.Profile; | 16 | @Input() owner: noosfero.Profile; |
13 | 17 | ||
18 | + private isMember: boolean; | ||
19 | + | ||
20 | + constructor(private profileService: ProfileService, private session: SessionService, private notificationService: NotificationService) { | ||
21 | + } | ||
22 | + | ||
23 | + ngOnInit() { | ||
24 | + this.loadMembership(); | ||
25 | + } | ||
26 | + | ||
27 | + loadMembership() { | ||
28 | + let person = this.session.currentUser() ? this.session.currentUser().person : null; | ||
29 | + this.profileService.isMember(person, this.owner).then((val: boolean) => { | ||
30 | + this.isMember = val; | ||
31 | + }); | ||
32 | + } | ||
33 | + | ||
34 | + join() { | ||
35 | + let person = this.session.currentUser() ? this.session.currentUser().person : null; | ||
36 | + this.profileService.addMember(person, this.owner).then((result: any) => { | ||
37 | + if (result.data.pending) { | ||
38 | + this.notificationService.success({ title: "blocks.profile_image.join.moderation.title", message: "blocks.profile_image.join.moderation.message" }); | ||
39 | + } else { | ||
40 | + this.loadMembership(); | ||
41 | + } | ||
42 | + }); | ||
43 | + } | ||
44 | + | ||
45 | + leave() { | ||
46 | + let person = this.session.currentUser() ? this.session.currentUser().person : null; | ||
47 | + this.profileService.removeMember(person, this.owner).then(() => { | ||
48 | + this.loadMembership(); | ||
49 | + }); | ||
50 | + } | ||
14 | } | 51 | } |
src/app/layout/blocks/profile-image/profile-image-block.html
@@ -3,4 +3,8 @@ | @@ -3,4 +3,8 @@ | ||
3 | <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> | 3 | <noosfero-profile-image [profile]="ctrl.owner"></noosfero-profile-image> |
4 | </a> | 4 | </a> |
5 | <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> | 5 | <a class="settings-link" target="_self" ui-sref="main.profile.settings({profile: ctrl.owner.identifier})">{{"blocks.profile_image.control_panel" | translate}}</a> |
6 | + <div class="actions" ng-show="ctrl.isMember!=null"> | ||
7 | + <a ng-if="!ctrl.isMember" ng-click="ctrl.join()" class="btn btn-primary btn-sm join" href="#">{{"blocks.profile_image.join" | translate}}</a> | ||
8 | + <a ng-if="ctrl.isMember" ng-click="ctrl.leave()" class="btn btn-warning btn-sm leave" href="#">{{"blocks.profile_image.leave" | translate}}</a> | ||
9 | + </div> | ||
6 | </div> | 10 | </div> |
src/languages/en.json
1 | { | 1 | { |
2 | "noosfero.name" : "Noosfero", | 2 | "noosfero.name" : "Noosfero", |
3 | "blocks.profile_image.control_panel": "Control Panel", | 3 | "blocks.profile_image.control_panel": "Control Panel", |
4 | + "blocks.profile_image.join": "Join community", | ||
5 | + "blocks.profile_image.leave": "Leave community", | ||
6 | + "blocks.profile_image.join.moderation.title": "Good job!", | ||
7 | + "blocks.profile_image.join.moderation.message": "Your membership is waiting for approval", | ||
4 | "navbar.profile": "Profile", | 8 | "navbar.profile": "Profile", |
5 | "navbar.settings": "Settings", | 9 | "navbar.settings": "Settings", |
6 | "navbar.logout": "Log Out", | 10 | "navbar.logout": "Log Out", |
src/languages/pt.json
1 | { | 1 | { |
2 | "noosfero.name" : "Noosfero", | 2 | "noosfero.name" : "Noosfero", |
3 | "blocks.profile_image.control_panel": "Painel de Controle", | 3 | "blocks.profile_image.control_panel": "Painel de Controle", |
4 | + "blocks.profile_image.join": "Entrar na comunidade", | ||
5 | + "blocks.profile_image.leave": "Sair da comunidade", | ||
6 | + "blocks.profile_image.join.moderation.title": "Bom trabalho!", | ||
7 | + "blocks.profile_image.join.moderation.message": "Sua participação está pendente de aprovação", | ||
4 | "navbar.profile": "Perfil", | 8 | "navbar.profile": "Perfil", |
5 | "navbar.settings": "Configurações", | 9 | "navbar.settings": "Configurações", |
6 | "navbar.logout": "Sair", | 10 | "navbar.logout": "Sair", |
src/lib/ng-noosfero-api/http/profile.service.spec.ts
@@ -111,6 +111,56 @@ describe("Services", () => { | @@ -111,6 +111,56 @@ describe("Services", () => { | ||
111 | }); | 111 | }); |
112 | $httpBackend.flush(); | 112 | $httpBackend.flush(); |
113 | }); | 113 | }); |
114 | + | ||
115 | + it("should return the profile members", (done) => { | ||
116 | + let profileId = 1; | ||
117 | + $httpBackend.expectGET(`/api/v1/profiles/${profileId}/members`).respond(200, { people: [{ id: 2 }] }); | ||
118 | + profileService.getMembers(<any>{ id: profileId }).then((response: restangular.IResponse) => { | ||
119 | + expect(response.data.people).toEqual([{ id: 2 }]); | ||
120 | + done(); | ||
121 | + }); | ||
122 | + $httpBackend.flush(); | ||
123 | + }); | ||
124 | + | ||
125 | + it("should return true if the person is a profile member", (done) => { | ||
126 | + let profileId = 1; | ||
127 | + $httpBackend.expectGET(`/api/v1/profiles/${profileId}/members`).respond(200, { people: [{ id: 2 }] }); | ||
128 | + profileService.isMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => { | ||
129 | + expect(response).toEqual(true); | ||
130 | + done(); | ||
131 | + }); | ||
132 | + $httpBackend.flush(); | ||
133 | + }); | ||
134 | + | ||
135 | + it("should return false if the person is a profile member", (done) => { | ||
136 | + let profileId = 1; | ||
137 | + $httpBackend.expectGET(`/api/v1/profiles/${profileId}/members`).respond(200, { people: [] }); | ||
138 | + profileService.isMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => { | ||
139 | + expect(response).toEqual(false); | ||
140 | + done(); | ||
141 | + }); | ||
142 | + $httpBackend.flush(); | ||
143 | + }); | ||
144 | + | ||
145 | + it("should add member to profile", (done) => { | ||
146 | + let profileId = 1; | ||
147 | + $httpBackend.expectPOST(`/api/v1/profiles/${profileId}/members`).respond(200, { pending: false }); | ||
148 | + profileService.addMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => { | ||
149 | + expect(response.data.pending).toEqual(false); | ||
150 | + done(); | ||
151 | + }); | ||
152 | + $httpBackend.flush(); | ||
153 | + }); | ||
154 | + | ||
155 | + it("should remove member from profile", (done) => { | ||
156 | + let profileId = 1; | ||
157 | + $httpBackend.expectDELETE(`/api/v1/profiles/${profileId}/members`).respond(200, { person: { id: 2 } }); | ||
158 | + profileService.removeMember(<any>{ id: 2 }, <any>{ id: profileId }).then((response: restangular.IResponse) => { | ||
159 | + expect(response.data.person).toEqual({ id: 2 }); | ||
160 | + done(); | ||
161 | + }); | ||
162 | + $httpBackend.flush(); | ||
163 | + }); | ||
114 | }); | 164 | }); |
115 | 165 | ||
116 | 166 |
src/lib/ng-noosfero-api/http/profile.service.ts
@@ -64,4 +64,29 @@ export class ProfileService { | @@ -64,4 +64,29 @@ export class ProfileService { | ||
64 | let headers = { 'Content-Type': 'application/json' }; | 64 | let headers = { 'Content-Type': 'application/json' }; |
65 | return this.get(profile.id).customPOST({ profile: profile }, null, null, headers); | 65 | return this.get(profile.id).customPOST({ profile: profile }, null, null, headers); |
66 | } | 66 | } |
67 | + | ||
68 | + getMembers(profile: noosfero.Profile, params?: any) { | ||
69 | + let p = this.get(profile.id); | ||
70 | + return p.customGET('members', params); | ||
71 | + } | ||
72 | + | ||
73 | + isMember(person: noosfero.Person, profile: noosfero.Profile) { | ||
74 | + let deferred = this.$q.defer(); | ||
75 | + if (person) { | ||
76 | + this.getMembers(profile, { identifier: person.identifier }).then((result: any) => { | ||
77 | + deferred.resolve(result.data.people.length > 0); | ||
78 | + }); | ||
79 | + } else { | ||
80 | + deferred.resolve(false); | ||
81 | + } | ||
82 | + return deferred.promise; | ||
83 | + } | ||
84 | + | ||
85 | + addMember(person: noosfero.Person, profile: noosfero.Profile) { | ||
86 | + return this.get(profile.id).customPOST({}, "members", null, null); | ||
87 | + } | ||
88 | + | ||
89 | + removeMember(person: noosfero.Person, profile: noosfero.Profile) { | ||
90 | + return this.get(profile.id).customDELETE("members", null, null); | ||
91 | + } | ||
67 | } | 92 | } |