Commit ade7c078b4032f09e1e33e6c2e4c00906939724c

Authored by Francisco Júnior
2 parents 0b078626 9ab71161

Merge branch 'fix-edit-mode-state' into 'master'

Fix edit mode state

See merge request !46
src/app/admin/layout-edit/designMode.service.spec.ts
1 import {DesignModeService} from './designMode.service'; 1 import {DesignModeService} from './designMode.service';
  2 +import {INoosferoLocalStorage} from "./../../shared/models/interfaces";
2 3
3 describe('DesignMode Service', () => { 4 describe('DesignMode Service', () => {
4 let service: DesignModeService; 5 let service: DesignModeService;
5 6
  7 + let $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: { designMode: false } };
6 beforeEach(() => { 8 beforeEach(() => {
7 - service = new DesignModeService(); 9 + service = new DesignModeService($localStorage);
8 }); 10 });
9 11
10 it('has the designModeOn equals false as default', () => { 12 it('has the designModeOn equals false as default', () => {
@@ -18,12 +20,14 @@ describe(&#39;DesignMode Service&#39;, () =&gt; { @@ -18,12 +20,14 @@ describe(&#39;DesignMode Service&#39;, () =&gt; {
18 }); 20 });
19 21
20 it('emits the onToggle event when changing the designModeOn property', () => { 22 it('emits the onToggle event when changing the designModeOn property', () => {
  23 + service.setInDesignMode(false);
21 spyOn(service.onToggle, 'next').and.stub(); 24 spyOn(service.onToggle, 'next').and.stub();
22 service.setInDesignMode(true); 25 service.setInDesignMode(true);
23 expect(service.onToggle.next).toHaveBeenCalled(); 26 expect(service.onToggle.next).toHaveBeenCalled();
24 }); 27 });
25 28
26 it('does not emit onToggle event when there is no change on designModeOn property', () => { 29 it('does not emit onToggle event when there is no change on designModeOn property', () => {
  30 + service.setInDesignMode(false);
27 spyOn(service.onToggle, 'next').and.stub(); 31 spyOn(service.onToggle, 'next').and.stub();
28 service.setInDesignMode(false); 32 service.setInDesignMode(false);
29 expect(service.onToggle.next).not.toHaveBeenCalled(); 33 expect(service.onToggle.next).not.toHaveBeenCalled();
src/app/admin/layout-edit/designMode.service.ts
1 -import {Injectable, Output, EventEmitter} from 'ng-forward'; 1 +import {Injectable, Output, EventEmitter, Inject} from 'ng-forward';
  2 +import {INoosferoLocalStorage} from "./../../shared/models/interfaces";
2 3
3 @Injectable() 4 @Injectable()
  5 +@Inject("$localStorage")
4 export class DesignModeService { 6 export class DesignModeService {
5 @Output() onToggle: EventEmitter<boolean> = new EventEmitter<boolean>(); 7 @Output() onToggle: EventEmitter<boolean> = new EventEmitter<boolean>();
6 8
7 - private designModeOn: boolean = false;  
8 -  
9 isInDesignMode(): boolean { 9 isInDesignMode(): boolean {
10 - return this.designModeOn; 10 + return this.$localStorage.settings.designModeOn;
  11 + }
  12 +
  13 + destroy() {
  14 + delete this.$localStorage.settings;
  15 + this.$localStorage.settings = {};
11 } 16 }
12 17
13 setInDesignMode(value: boolean) { 18 setInDesignMode(value: boolean) {
14 - if (this.designModeOn !== value) {  
15 - this.designModeOn = value;  
16 - this.onToggle.next(this.designModeOn); 19 + if (this.$localStorage.settings.designModeOn !== value) {
  20 + this.$localStorage.settings.designModeOn = value;
  21 + this.onToggle.next(value);
17 } 22 }
18 } 23 }
19 24
20 - constructor() { 25 + constructor(private $localStorage: INoosferoLocalStorage) {
  26 + if (!this.$localStorage.settings) {
  27 + this.$localStorage.settings = {};
  28 + }
21 } 29 }
22 } 30 }
src/app/admin/layout-edit/designModeToggler.component.spec.ts
@@ -2,6 +2,7 @@ import {ComponentTestHelper, createClass} from &#39;../../../spec/component-test-hel @@ -2,6 +2,7 @@ import {ComponentTestHelper, createClass} from &#39;../../../spec/component-test-hel
2 import * as helpers from '../../../spec/helpers'; 2 import * as helpers from '../../../spec/helpers';
3 import {DesignModeTogglerComponent} from './designModeToggler.component'; 3 import {DesignModeTogglerComponent} from './designModeToggler.component';
4 import {DesignModeService} from './designMode.service'; 4 import {DesignModeService} from './designMode.service';
  5 +import {INoosferoLocalStorage} from "./../../shared/models/interfaces";
5 6
6 describe('DesignModeToggler Component', () => { 7 describe('DesignModeToggler Component', () => {
7 const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>'; 8 const htmlTemplate: string = '<noosfero-design-toggler></noosfero-design-toggler>';
@@ -14,13 +15,15 @@ describe(&#39;DesignModeToggler Component&#39;, () =&gt; { @@ -14,13 +15,15 @@ describe(&#39;DesignModeToggler Component&#39;, () =&gt; {
14 }); 15 });
15 16
16 let designModeService: DesignModeService; 17 let designModeService: DesignModeService;
  18 + let $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: { designMode: false } };
17 beforeEach((done) => { 19 beforeEach((done) => {
18 - designModeService = new DesignModeService(); 20 + designModeService = new DesignModeService($localStorage);
19 let cls = createClass({ 21 let cls = createClass({
20 template: htmlTemplate, 22 template: htmlTemplate,
21 directives: [DesignModeTogglerComponent], 23 directives: [DesignModeTogglerComponent],
22 providers: [ 24 providers: [
23 - helpers.createProviderToValue('DesignModeService', designModeService) 25 + helpers.createProviderToValue('DesignModeService', designModeService),
  26 + helpers.createProviderToValue('AuthService', helpers.mocks.authService),
24 ] 27 ]
25 }); 28 });
26 helper = new ComponentTestHelper<DesignModeTogglerComponent>(cls, done); 29 helper = new ComponentTestHelper<DesignModeTogglerComponent>(cls, done);
@@ -36,6 +39,7 @@ describe(&#39;DesignModeToggler Component&#39;, () =&gt; { @@ -36,6 +39,7 @@ describe(&#39;DesignModeToggler Component&#39;, () =&gt; {
36 }); 39 });
37 40
38 it('emits event with value "true" when changing inDesignMode to On', (done) => { 41 it('emits event with value "true" when changing inDesignMode to On', (done) => {
  42 + designModeService.setInDesignMode(false);
39 designModeService.onToggle.subscribe((designModeOn: boolean) => { 43 designModeService.onToggle.subscribe((designModeOn: boolean) => {
40 expect(designModeOn).toBeTruthy(); 44 expect(designModeOn).toBeTruthy();
41 done(); 45 done();
src/app/admin/layout-edit/designModeToggler.component.ts
1 import {Component, Inject} from 'ng-forward'; 1 import {Component, Inject} from 'ng-forward';
2 import {DesignModeService} from './designMode.service'; 2 import {DesignModeService} from './designMode.service';
  3 +import {AuthService, AuthEvents} from '../../login'
  4 +
3 @Component({ 5 @Component({
4 selector: 'noosfero-design-toggler', 6 selector: 'noosfero-design-toggler',
5 templateUrl: 'app/admin/layout-edit/designModeToggler.html' 7 templateUrl: 'app/admin/layout-edit/designModeToggler.html'
6 }) 8 })
7 -@Inject(DesignModeService) 9 +@Inject(DesignModeService, AuthService)
8 export class DesignModeTogglerComponent { 10 export class DesignModeTogglerComponent {
9 11
10 icon: string = "&nbsp;<i class='glyphicon glyphicon-wrench'></i>&nbsp;"; 12 icon: string = "&nbsp;<i class='glyphicon glyphicon-wrench'></i>&nbsp;";
11 13
12 - constructor(private designModeService: DesignModeService) { 14 + constructor(private designModeService: DesignModeService, private authService: AuthService) {
  15 + this.authService.subscribe(AuthEvents[AuthEvents.logoutSuccess], () => {
  16 + this.designModeService.destroy();
  17 + });
13 } 18 }
14 19
15 private _inDesignMode: boolean = false; 20 private _inDesignMode: boolean = false;
src/app/layout/blocks/block.component.spec.ts
@@ -2,6 +2,7 @@ import {Component} from &#39;ng-forward&#39;; @@ -2,6 +2,7 @@ import {Component} from &#39;ng-forward&#39;;
2 import {BlockComponent} from './block.component'; 2 import {BlockComponent} from './block.component';
3 import * as helpers from "../../../spec/helpers"; 3 import * as helpers from "../../../spec/helpers";
4 import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; 4 import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper';
  5 +import {DesignModeService} from '../../admin/layout-edit/designMode.service';
5 6
6 const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.profile"></noosfero-block>'; 7 const htmlTemplate: string = '<noosfero-block [block]="ctrl.block" [owner]="ctrl.profile"></noosfero-block>';
7 8
@@ -32,16 +33,17 @@ describe(&quot;Boxes Component&quot;, () =&gt; { @@ -32,16 +33,17 @@ describe(&quot;Boxes Component&quot;, () =&gt; {
32 helpers.createProviderToValue('TranslatorService', translatorService), 33 helpers.createProviderToValue('TranslatorService', translatorService),
33 helpers.createProviderToValue('$uibModal', helpers.mocks.$modal), 34 helpers.createProviderToValue('$uibModal', helpers.mocks.$modal),
34 helpers.createProviderToValue('BlockService', blockService), 35 helpers.createProviderToValue('BlockService', blockService),
35 - helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService) 36 + helpers.createProviderToValue('NotificationService', helpers.mocks.notificationService),
  37 + helpers.createProviderToValue('DesignModeService', helpers.mocks.designModeService)
36 ] 38 ]
37 }); 39 });
38 helper = new ComponentTestHelper<BlockComponent>(cls, done); 40 helper = new ComponentTestHelper<BlockComponent>(cls, done);
39 }); 41 });
40 -  
41 let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]); 42 let translatorService = jasmine.createSpyObj("translatorService", ["currentLanguage"]);
42 let blockService = jasmine.createSpyObj("blockService", ["update"]); 43 let blockService = jasmine.createSpyObj("blockService", ["update"]);
43 let state = jasmine.createSpyObj("state", ["current"]); 44 let state = jasmine.createSpyObj("state", ["current"]);
44 state.current = { name: "" }; 45 state.current = { name: "" };
  46 +
45 47
46 it("set isHomepage as false by default", () => { 48 it("set isHomepage as false by default", () => {
47 expect(helper.component.isHomepage).toBeFalsy(); 49 expect(helper.component.isHomepage).toBeFalsy();
src/app/layout/blocks/block.component.ts
@@ -11,7 +11,8 @@ import { DesignModeService } from &quot;../../admin/layout-edit/designMode.service&quot;; @@ -11,7 +11,8 @@ import { DesignModeService } from &quot;../../admin/layout-edit/designMode.service&quot;;
11 templateUrl: 'app/layout/blocks/block.html', 11 templateUrl: 'app/layout/blocks/block.html',
12 directives: [BlockEditionComponent] 12 directives: [BlockEditionComponent]
13 }) 13 })
14 -@Inject("$uibModal", "$scope", "$state", "$rootScope", BlockService, NotificationService, AuthService, SessionService, TranslatorService, DesignModeService) 14 +@Inject("$uibModal", "$scope", "$state", "$rootScope", BlockService, NotificationService,
  15 +AuthService, SessionService, TranslatorService, DesignModeService)
15 export class BlockComponent { 16 export class BlockComponent {
16 17
17 @Input() block: noosfero.Block; 18 @Input() block: noosfero.Block;
src/app/layout/blocks/block.html
1 -<div ng-show="ctrl.canDisplay() || ctrl.editionMode || ctrl.designMode" ng-class="{'invisible-block': !ctrl.canDisplay()}" class="noosfero-block" ng-mouseover="displayActions = true" ng-mouseleave="displayActions = false"> 1 +<div ng-show="ctrl.canDisplay() || ctrl.inEditMode() || ctrl.designMode" ng-class="{'invisible-block': !ctrl.canDisplay()}" class="noosfero-block" ng-mouseover="displayActions = true" ng-mouseleave="displayActions = false">
2 <div ng-show="displayActions" class="actions block-actions" permission="ctrl.block.permissions" permission-action="allow_edit"> 2 <div ng-show="displayActions" class="actions block-actions" permission="ctrl.block.permissions" permission-action="allow_edit">
3 <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i></button> 3 <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i></button>
4 </div> 4 </div>
src/app/layout/navbar/navbar.spec.ts
@@ -9,6 +9,8 @@ import {SessionService, AuthService, AuthController, AuthEvents} from &quot;./../../l @@ -9,6 +9,8 @@ import {SessionService, AuthService, AuthController, AuthEvents} from &quot;./../../l
9 9
10 import events from 'ng-forward/cjs/events/events'; 10 import events from 'ng-forward/cjs/events/events';
11 11
  12 +import {DesignModeService} from '../../admin/layout-edit/designMode.service';
  13 +
12 describe("Components", () => { 14 describe("Components", () => {
13 15
14 describe("Navbar Component", () => { 16 describe("Navbar Component", () => {
@@ -22,6 +24,7 @@ describe(&quot;Components&quot;, () =&gt; { @@ -22,6 +24,7 @@ describe(&quot;Components&quot;, () =&gt; {
22 let authService: any; 24 let authService: any;
23 let stateService: any; 25 let stateService: any;
24 let sessionService: SessionService; 26 let sessionService: SessionService;
  27 + let designModeService: DesignModeService;
25 28
26 let provideFunc = provide; 29 let provideFunc = provide;
27 30
@@ -37,6 +40,7 @@ describe(&quot;Components&quot;, () =&gt; { @@ -37,6 +40,7 @@ describe(&quot;Components&quot;, () =&gt; {
37 authService = helpers.mocks.authService; 40 authService = helpers.mocks.authService;
38 stateService = jasmine.createSpyObj("$state", ["go"]); 41 stateService = jasmine.createSpyObj("$state", ["go"]);
39 sessionService = <any>helpers.mocks.sessionWithCurrentUser(user); 42 sessionService = <any>helpers.mocks.sessionWithCurrentUser(user);
  43 + designModeService = helpers.mocks.designModeService;
40 }); 44 });
41 45
42 46
@@ -76,6 +80,9 @@ describe(&quot;Components&quot;, () =&gt; { @@ -76,6 +80,9 @@ describe(&quot;Components&quot;, () =&gt; {
76 }), 80 }),
77 provide('TranslatorService', { 81 provide('TranslatorService', {
78 useValue: helpers.mocks.translatorService 82 useValue: helpers.mocks.translatorService
  83 + }),
  84 + provide('DesignModeService', {
  85 + useValue: helpers.mocks.designModeService
79 }) 86 })
80 ].concat(helpers.provideFilters("translateFilter")), 87 ].concat(helpers.provideFilters("translateFilter")),
81 directives: [Navbar], 88 directives: [Navbar],
src/app/layout/services/body-state-classes.service.spec.ts
@@ -5,11 +5,13 @@ import {AuthEvents} from &quot;./../../login/auth-events&quot;; @@ -5,11 +5,13 @@ import {AuthEvents} from &quot;./../../login/auth-events&quot;;
5 5
6 import {EventEmitter} from 'ng-forward'; 6 import {EventEmitter} from 'ng-forward';
7 import {DesignModeService} from './../../admin/layout-edit/designMode.service'; 7 import {DesignModeService} from './../../admin/layout-edit/designMode.service';
  8 +import {INoosferoLocalStorage} from "./../../shared/models/interfaces";
8 9
9 describe("BodyStateClasses Service", () => { 10 describe("BodyStateClasses Service", () => {
10 11
11 let currentStateName = "main"; 12 let currentStateName = "main";
12 let bodyStateClasseService: BodyStateClassesService; 13 let bodyStateClasseService: BodyStateClassesService;
  14 + let $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: { designMode: false } };
13 let $rootScope: ng.IRootScopeService = <any>{}, 15 let $rootScope: ng.IRootScopeService = <any>{},
14 $document: ng.IDocumentService = <any>{}, 16 $document: ng.IDocumentService = <any>{},
15 $state: ng.ui.IStateService = <any>{ 17 $state: ng.ui.IStateService = <any>{
@@ -20,7 +22,8 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; { @@ -20,7 +22,8 @@ describe(&quot;BodyStateClasses Service&quot;, () =&gt; {
20 authService: any = helpers.mocks.authService, 22 authService: any = helpers.mocks.authService,
21 bodyEl: { className: string }, 23 bodyEl: { className: string },
22 bodyElJq: any, 24 bodyElJq: any,
23 - designModeService = new DesignModeService(); 25 +
  26 + designModeService = new DesignModeService($localStorage);
24 27
25 28
26 29
src/app/login/session.service.spec.ts
@@ -13,7 +13,7 @@ describe(&quot;Services&quot;, () =&gt; { @@ -13,7 +13,7 @@ describe(&quot;Services&quot;, () =&gt; {
13 let $log: any; 13 let $log: any;
14 14
15 beforeEach(() => { 15 beforeEach(() => {
16 - $localStorage = <INoosferoLocalStorage>{ currentUser: null }; 16 + $localStorage = <INoosferoLocalStorage>{ currentUser: null, settings: null };
17 $log = jasmine.createSpyObj('$log', ['debug']); 17 $log = jasmine.createSpyObj('$log', ['debug']);
18 }); 18 });
19 19
src/app/login/session.service.ts
@@ -16,6 +16,7 @@ export class SessionService { @@ -16,6 +16,7 @@ export class SessionService {
16 16
17 destroy() { 17 destroy() {
18 delete this.$localStorage.currentUser; 18 delete this.$localStorage.currentUser;
  19 + delete this.$localStorage.settings;
19 }; 20 };
20 21
21 currentUser(): noosfero.User { 22 currentUser(): noosfero.User {
src/app/profile/custom-content/custom-content.component.spec.ts
1 import {CustomContentComponent} from './custom-content.component'; 1 import {CustomContentComponent} from './custom-content.component';
2 import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper'; 2 import {ComponentTestHelper, createClass} from '../../../spec/component-test-helper';
3 import * as helpers from "../../../spec/helpers"; 3 import * as helpers from "../../../spec/helpers";
  4 +import {DesignModeService} from '../../admin/layout-edit/designMode.service';
4 5
5 const htmlTemplate: string = '<custom-content [attribute]="\'custom_footer\'" [profile]="ctrl.profile"></custom-content>'; 6 const htmlTemplate: string = '<custom-content [attribute]="\'custom_footer\'" [profile]="ctrl.profile"></custom-content>';
6 7
@@ -14,6 +15,7 @@ describe(&quot;Components&quot;, () =&gt; { @@ -14,6 +15,7 @@ describe(&quot;Components&quot;, () =&gt; {
14 beforeEach((done) => { 15 beforeEach((done) => {
15 let profileService = jasmine.createSpyObj("profileService", ["update"]); 16 let profileService = jasmine.createSpyObj("profileService", ["update"]);
16 let notificationService = jasmine.createSpyObj("notificationService", ["success"]); 17 let notificationService = jasmine.createSpyObj("notificationService", ["success"]);
  18 + let designModeService = { isInDesignMode: () => { return true; }};
17 let properties = { profile: { custom_footer: "footer" } }; 19 let properties = { profile: { custom_footer: "footer" } };
18 let cls = createClass({ 20 let cls = createClass({
19 template: htmlTemplate, 21 template: htmlTemplate,
@@ -22,7 +24,8 @@ describe(&quot;Components&quot;, () =&gt; { @@ -22,7 +24,8 @@ describe(&quot;Components&quot;, () =&gt; {
22 providers: [ 24 providers: [
23 helpers.createProviderToValue("$uibModal", helpers.mocks.$modal), 25 helpers.createProviderToValue("$uibModal", helpers.mocks.$modal),
24 helpers.createProviderToValue("ProfileService", profileService), 26 helpers.createProviderToValue("ProfileService", profileService),
25 - helpers.createProviderToValue("NotificationService", notificationService) 27 + helpers.createProviderToValue("NotificationService", notificationService),
  28 + helpers.createProviderToValue("DesignModeService", designModeService)
26 ] 29 ]
27 }); 30 });
28 helper = new ComponentTestHelper<CustomContentComponent>(cls, done); 31 helper = new ComponentTestHelper<CustomContentComponent>(cls, done);
src/app/profile/custom-content/custom-content.component.ts
@@ -20,7 +20,6 @@ export class CustomContentComponent { @@ -20,7 +20,6 @@ export class CustomContentComponent {
20 20
21 content: string; 21 content: string;
22 originalContent: string; 22 originalContent: string;
23 - editionMode = false;  
24 private modalInstance: any = null; 23 private modalInstance: any = null;
25 24
26 constructor(private $uibModal: any, 25 constructor(private $uibModal: any,
@@ -35,9 +34,10 @@ export class CustomContentComponent { @@ -35,9 +34,10 @@ export class CustomContentComponent {
35 }, () => { 34 }, () => {
36 if (this.profile) this.content = (<any>this.profile)[this.attribute]; 35 if (this.profile) this.content = (<any>this.profile)[this.attribute];
37 }); 36 });
38 - this.designModeService.onToggle.subscribe((designModeOn: boolean) => {  
39 - this.editionMode = designModeOn;  
40 - }); 37 + }
  38 +
  39 + inEditMode() {
  40 + return this.designModeService.isInDesignMode();
41 } 41 }
42 42
43 openEdit() { 43 openEdit() {
src/app/profile/custom-content/custom-content.html
1 <div class="custom-content"> 1 <div class="custom-content">
2 - <div class="actions" permission="ctrl.profile.permissions" permission-action="allow_edit" ng-show="ctrl.editionMode"> 2 + <div class="actions" permission="ctrl.profile.permissions" permission-action="allow_edit" ng-show="ctrl.inEditMode()">
3 <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i> {{ctrl.label | translate}}</button> 3 <button type="submit" class="btn btn-xs btn-default" ng-click="ctrl.openEdit()"><i class="fa fa-edit fa-fw"></i> {{ctrl.label | translate}}</button>
4 </div> 4 </div>
5 <div class="content" ng-bind-html="ctrl.content"></div> 5 <div class="content" ng-bind-html="ctrl.content"></div>
src/app/shared/models/interfaces.ts
@@ -8,4 +8,5 @@ export interface UserResponse { @@ -8,4 +8,5 @@ export interface UserResponse {
8 8
9 export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService { 9 export interface INoosferoLocalStorage extends angular.storage.ILocalStorageService {
10 currentUser: noosfero.User; 10 currentUser: noosfero.User;
  11 + settings: any;
11 } 12 }
src/spec/mocks.ts
@@ -193,6 +193,17 @@ export var mocks: any = { @@ -193,6 +193,17 @@ export var mocks: any = {
193 currentUser: () => { return user; } 193 currentUser: () => { return user; }
194 }; 194 };
195 }, 195 },
  196 + designModeService: {
  197 + modeFn: null,
  198 + onToggle: {
  199 + subscribe: (fn: Function) => {
  200 + mocks.designModeService.modeFn = fn;
  201 + },
  202 + next: (param: any) => {
  203 + mocks.designModeService.modeFn(param);
  204 + }
  205 + }
  206 + },
196 $translate: { 207 $translate: {
197 use: (lang?: string) => { 208 use: (lang?: string) => {
198 return lang ? Promise.resolve(lang) : "en"; 209 return lang ? Promise.resolve(lang) : "en";