Skip to content

Commit

Permalink
Login Success Message (#295)
Browse files Browse the repository at this point in the history
* enhancement (header): a login success message is now shown below the header when a user successfully logs in

* fix packages

* fix (header): added onDestroy method to unsubscribe from subscription and added tests
  • Loading branch information
mdelez committed Sep 16, 2020
1 parent f756054 commit 188eeb4
Show file tree
Hide file tree
Showing 9 changed files with 119 additions and 10 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -46,3 +46,4 @@ docs/bin
# System Files
.DS_Store
Thumbs.db
*.code-workspace
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -20,6 +20,7 @@
},
"private": true,
"dependencies": {
"3d-force-graph": "^1.60.12",
"@angular/animations": "^9.1.12",
"@angular/cdk": "^9.2.4",
"@angular/common": "~9.1.12",
Expand All @@ -34,7 +35,6 @@
"@dasch-swiss/dsp-ui": "1.0.0-rc.7",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"3d-force-graph": "^1.60.12",
"core-js": "^3.6.5",
"d3": "^5.15.1",
"d3-force-3d": "^2.1.0",
Expand Down
2 changes: 1 addition & 1 deletion src/app/app.component.html
Expand Up @@ -8,4 +8,4 @@
<router-outlet></router-outlet>
</div>

</div>
</div>
4 changes: 3 additions & 1 deletion src/app/main/header/header.component.html
Expand Up @@ -47,7 +47,9 @@
</span>

</mat-toolbar>

<div *ngIf="showMessage">
<dsp-message [message]="successMessage" [short]="true" [duration]="2500"></dsp-message>
</div>
<!-- search-panel (in phone version) -->
<div class="search-panel-phone" *ngIf="show">
<dsp-fulltext-search class="dsp-fulltext-search" (search)="doSearch($event)" [projectfilter]="true">
Expand Down
27 changes: 25 additions & 2 deletions src/app/main/header/header.component.spec.ts
Expand Up @@ -13,17 +13,20 @@ import {
DspApiConfigToken,
DspApiConnectionToken,
DspCoreModule,
DspSearchModule
DspSearchModule,
MessageComponent
} from '@dasch-swiss/dsp-ui';
import { TranslateModule } from '@ngx-translate/core';
import { UserMenuComponent } from 'src/app/user/user-menu/user-menu.component';
import { TestConfig } from 'test.config';
import { SelectLanguageComponent } from '../select-language/select-language.component';
import { HeaderComponent } from './header.component';
import { ComponentCommunicationEventService, EmitEvent, Events } from 'src/app/services/component-communication-event.service';

describe('HeaderComponent', () => {
let component: HeaderComponent;
let fixture: ComponentFixture<HeaderComponent>;
let componentCommsService: ComponentCommunicationEventService;

beforeEach(async(() => {
TestBed.configureTestingModule({
Expand Down Expand Up @@ -53,9 +56,12 @@ describe('HeaderComponent', () => {
{
provide: DspApiConnectionToken,
useValue: new KnoraApiConnection(TestConfig.ApiConfig)
}
},
ComponentCommunicationEventService
]
}).compileComponents();

componentCommsService = TestBed.inject(ComponentCommunicationEventService);
}));

beforeEach(() => {
Expand Down Expand Up @@ -94,4 +100,21 @@ describe('HeaderComponent', () => {
expect(searchPanel).toBeDefined();
});

it('should display the login success message when the LoginSuccess event is emitted', () => {
componentCommsService.emit(new EmitEvent(Events.LoginSuccess));
fixture.detectChanges();
const message = fixture.debugElement.query(By.directive(MessageComponent));
expect(message).toBeTruthy();
});

it('should unsubscribe from from changes on destruction', () => {

expect(component.componentCommsSubscription.closed).toBe(false);

fixture.destroy();

expect(component.componentCommsSubscription.closed).toBe(true);

});

});
32 changes: 28 additions & 4 deletions src/app/main/header/header.component.ts
@@ -1,25 +1,37 @@
import { Component } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { MatIconRegistry } from '@angular/material/icon';
import { DomSanitizer } from '@angular/platform-browser';
import { NavigationStart, Router } from '@angular/router';
import { SearchParams, SessionService } from '@dasch-swiss/dsp-ui';
import { DspMessageData, SearchParams, SessionService } from '@dasch-swiss/dsp-ui';
import { ComponentCommunicationEventService, Events } from 'src/app/services/component-communication-event.service';
import { Subscription } from 'rxjs';

@Component({
selector: 'app-header',
templateUrl: './header.component.html',
styleUrls: ['./header.component.scss']
})
export class HeaderComponent {
export class HeaderComponent implements OnInit, OnDestroy{

session: boolean = false;
show: boolean = false;
searchParams: SearchParams;

successMessage: DspMessageData = {
status: 200,
statusText: 'Login successful'
};

showMessage = false;

componentCommsSubscription: Subscription;

constructor(
private _session: SessionService,
private _domSanitizer: DomSanitizer,
private _matIconRegistry: MatIconRegistry,
private _router: Router) {
private _router: Router,
private _componentCommsService: ComponentCommunicationEventService) {

// create tool icons to use them in mat-icons
// knora-app icon with text
Expand All @@ -45,6 +57,18 @@ export class HeaderComponent {
});
}

ngOnInit() {
this.componentCommsSubscription = this._componentCommsService.on(
Events.LoginSuccess, () => this.showMessage = true);
}

ngOnDestroy() {
// unsubscribe from the ValueOperationEventService when component is destroyed
if (this.componentCommsSubscription !== undefined) {
this.componentCommsSubscription.unsubscribe();
}
}

/**
* Navigate to the login page
*/
Expand Down
5 changes: 4 additions & 1 deletion src/app/main/login/login.component.ts
@@ -1,6 +1,7 @@
import { Component, OnInit } from '@angular/core';
import { Title } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
import { ComponentCommunicationEventService, EmitEvent, Events } from 'src/app/services/component-communication-event.service';

@Component({
selector: 'app-login',
Expand All @@ -13,7 +14,8 @@ export class LoginComponent implements OnInit {

constructor (private _titleService: Title,
private _route: ActivatedRoute,
private _router: Router) {
private _router: Router,
private _componentCommsService: ComponentCommunicationEventService) {

// set the page title
this._titleService.setTitle('Login');
Expand All @@ -29,6 +31,7 @@ export class LoginComponent implements OnInit {
// go to previous route:
if (status) {
this._router.navigate([this.returnUrl]);
this._componentCommsService.emit(new EmitEvent(Events.LoginSuccess, true));
}
}

Expand Down
16 changes: 16 additions & 0 deletions src/app/services/component-communication-event.service.spec.ts
@@ -0,0 +1,16 @@
import { TestBed } from '@angular/core/testing';

import { ComponentCommunicationEventService } from './component-communication-event.service';

describe('ComponentCommunicationEventService', () => {
let service: ComponentCommunicationEventService;

beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ComponentCommunicationEventService);
});

it('should be created', () => {
expect(service).toBeTruthy();
});
});
40 changes: 40 additions & 0 deletions src/app/services/component-communication-event.service.ts
@@ -0,0 +1,40 @@
import { Subject, Subscription } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class ComponentCommunicationEventService {

// create a subject to hold data which can be subscribed to.
// you only get the data after you subscribe.
private _subject$ = new Subject();

// used in the listening component.
// i.e. this.componentCommunicationEventService = this._componentCommunicationEventService.on(Events.LoginSuccess, () => doSomething());
on(event: Events, action: () => void): Subscription {
return this._subject$
.pipe(
// filter down based on event name to any events that are emitted out of the subject from the emit method below.
filter((e: EmitEvent) => e.name === event),
map((e: EmitEvent) => e.value)
)
.subscribe(action); // subscribe to the subject to get the data.
}

// used in the emitting component.
// i.e. this.componentCommunicationEventService.emit(new EmitEvent(Events.LoginSuccess));
emit(event: EmitEvent) {
this._subject$.next(event);
}
}

export class EmitEvent {
constructor(public name: any, public value?: any) { }
}

// possible events that can be emitted.
export enum Events {
LoginSuccess
}

0 comments on commit 188eeb4

Please sign in to comment.