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 | } |