Add new notification component.
This commit is contained in:
@@ -35,16 +35,6 @@
|
||||
required />
|
||||
<label for="email">Email</label>
|
||||
</div>
|
||||
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{modelError}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="resultMsg" class="card green lighten-2 text-center z-depth-2" >
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{result}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col submitFormArea">
|
||||
<a routerLink="/accountSettings" class="indigo-text">
|
||||
Annuler
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ProfilEditionService } from './profil-edition.service';
|
||||
import { HttpEventType, HttpResponse } from '@angular/common/http';
|
||||
import { User } from '../../core/entities';
|
||||
import { AuthService } from '../../core/services/auth.service';
|
||||
import { NotificationsComponent } from 'src/app/core/notifications/notifications.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-profil-edition',
|
||||
@@ -33,8 +34,6 @@ export class ProfilEditionComponent implements OnInit {
|
||||
selectedFiles: FileList;
|
||||
currentFileUpload: File;
|
||||
progress: { percentage: number } = { percentage: 0 };
|
||||
modelError: string;
|
||||
result: string;
|
||||
|
||||
constructor(
|
||||
private profilEditionService: ProfilEditionService,
|
||||
@@ -69,32 +68,21 @@ export class ProfilEditionComponent implements OnInit {
|
||||
console.log('File ' + result.body + ' completely uploaded!');
|
||||
this.model.image = result.body as string;
|
||||
this.authService.setAuthenticated(this.model);
|
||||
this.setMessage('Image de profil modifiée.', false);
|
||||
NotificationsComponent.success('Image de profil modifiée.');
|
||||
}
|
||||
this.selectedFiles = undefined;
|
||||
}, error => {
|
||||
console.log(error);
|
||||
NotificationsComponent.error('Une erreur est survenue lors de l\'upload de votre image de profil.');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
this.profilEditionService.updateUser(this.model).subscribe(() => {
|
||||
this.setMessage('Modification enregistrée.', false);
|
||||
NotificationsComponent.success('Modification enregistrée.');
|
||||
}, error => {
|
||||
this.setMessage('L\'adresse mail saisie n\'est pas disponible.', true);
|
||||
NotificationsComponent.error('L\'adresse mail saisie n\'est pas disponible.');
|
||||
});
|
||||
}
|
||||
|
||||
setMessage(message: string, error: boolean): void {
|
||||
this[error ? 'modelError' : 'result'] = message;
|
||||
|
||||
const resultMsgDiv = document.getElementById(error ? 'errorMsg' : 'resultMsg');
|
||||
resultMsgDiv.style.maxHeight = '64px';
|
||||
|
||||
setTimeout(() => {
|
||||
resultMsgDiv.style.maxHeight = '0px';
|
||||
setTimeout(() => {
|
||||
this[error ? 'modelError' : 'result'] = undefined;
|
||||
}, 550);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<app-header></app-header>
|
||||
<app-notifications></app-notifications>
|
||||
<main class="container">
|
||||
<router-outlet></router-outlet>
|
||||
</main>
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { NgModule } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpModule } from '@angular/http';
|
||||
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
|
||||
import { RouterModule } from '@angular/router';
|
||||
|
||||
@@ -37,6 +36,8 @@ import { SearchComponent } from './search/search.component';
|
||||
import { SigninComponent } from './signin/signin.component';
|
||||
import { VersionRevisionComponent } from './version-revisions/version-revisions.component';
|
||||
import { HealthCheckComponent } from './health-check/health-check.component';
|
||||
import { NotificationElement } from './core/notifications/notification-element/notification-element.component';
|
||||
import { NotificationsComponent } from './core/notifications/notifications.component';
|
||||
|
||||
// Reusable components
|
||||
import { PostCardComponent } from './core/post-card/post-card.component';
|
||||
@@ -63,6 +64,8 @@ import { HealthCheckService } from './health-check/health-check.service';
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent,
|
||||
NotificationsComponent,
|
||||
NotificationElement,
|
||||
HeaderComponent,
|
||||
FooterComponent,
|
||||
LoginComponent,
|
||||
@@ -84,12 +87,11 @@ import { HealthCheckService } from './health-check/health-check.service';
|
||||
SearchBarComponent,
|
||||
ProgressBarComponent,
|
||||
ForbiddenComponent,
|
||||
HealthCheckComponent
|
||||
HealthCheckComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
FormsModule,
|
||||
HttpModule,
|
||||
HttpClientModule,
|
||||
MDBBootstrapModule.forRoot(),
|
||||
RouterModule.forRoot(
|
||||
|
||||
@@ -19,7 +19,7 @@ export class UnauthorizedInterceptor implements HttpInterceptor {
|
||||
this.router.navigate(['/login']);
|
||||
}
|
||||
|
||||
const error = err.error.message || err.statusText;
|
||||
const error = (err.error && err.error.message) || err.statusText;
|
||||
return throwError(error);
|
||||
}));
|
||||
}
|
||||
|
||||
26
src/main/ts/src/app/core/notifications/notification-class.ts
Normal file
26
src/main/ts/src/app/core/notifications/notification-class.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Class which represents a notification class.
|
||||
* It serves to set the notification appearence.
|
||||
*/
|
||||
export class NotificationClass {
|
||||
/**
|
||||
* Default constructor.
|
||||
* @param {string} icon Class name of font-awsome icon.
|
||||
* @param {string} clazz The class to set notification style.
|
||||
*/
|
||||
constructor(
|
||||
public icon: string,
|
||||
public clazz: string,
|
||||
) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constant instances of NotificationClass.
|
||||
*/
|
||||
export const NotificationClasses = Object.freeze({
|
||||
'Error': new NotificationClass('exclamation-circle ', 'alert-danger'),
|
||||
'Warn': new NotificationClass('exclamation-triangle', 'alert-warning'),
|
||||
'Info': new NotificationClass('info-circle', 'alert-info'),
|
||||
'Success': new NotificationClass('check-circle', 'alert-success')
|
||||
});
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<div id="notification" #notification class="alert {{model.notificationClass.clazz}}" role="alert">
|
||||
<mdb-icon fas [icon]="model.notificationClass.icon"></mdb-icon> {{model.content}}
|
||||
<span id="close">
|
||||
<i class="fa fa-window-close fas close"
|
||||
(click)="model.hide()"></i>
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,78 @@
|
||||
import { NotificationClass } from './../notification-class';
|
||||
import { Component, Input, OnInit, ViewChild, ElementRef } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Class which represents a notification in the notifications list.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-notification-element',
|
||||
templateUrl: 'notification-element.component.html',
|
||||
styles: [`
|
||||
#notification {
|
||||
transition: all 0.7s ease-out;
|
||||
position: relative;
|
||||
}
|
||||
.close {
|
||||
position: absolute;
|
||||
right: 7px;
|
||||
top: 12px;
|
||||
font-size: 19px;
|
||||
opacity: 0;
|
||||
}
|
||||
#notification:hover .close {
|
||||
opacity: 0.5;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class NotificationElement implements OnInit {
|
||||
/**
|
||||
* The notification model.
|
||||
*/
|
||||
@Input() model: NotificationModel;
|
||||
/**
|
||||
* The notification DOM element.
|
||||
*/
|
||||
@ViewChild('notification') notification: ElementRef;
|
||||
|
||||
/**
|
||||
* Sets the DOM element in the model object and plays with opacity.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
this.model.notification = this.notification;
|
||||
|
||||
this.notification.nativeElement.style.opacity = 0;
|
||||
setTimeout(() => {
|
||||
this.notification.nativeElement.style.opacity = 1;
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which represents the notification model.
|
||||
*/
|
||||
export class NotificationModel {
|
||||
/**
|
||||
* Element which represents the DOM element of the notification element.
|
||||
*/
|
||||
notification: ElementRef;
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
* @param {string} content The message of the notification.
|
||||
* @param {NotificationClass} notificationClass The category of the notification (info, error...).
|
||||
*/
|
||||
constructor(
|
||||
public content: string,
|
||||
public notificationClass: NotificationClass
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Hides the notification DOM element.
|
||||
*/
|
||||
public hide(): void {
|
||||
this.notification.nativeElement.style.opacity = 0;
|
||||
setTimeout(() => {
|
||||
this.notification.nativeElement.style.display = 'none';
|
||||
}, 800);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<div id="notification-container">
|
||||
<app-notification-element *ngFor="let notification of notificationList"
|
||||
[model]="notification"></app-notification-element>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
import { NotificationClass, NotificationClasses } from './notification-class';
|
||||
import { NotificationModel } from './notification-element/notification-element.component';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Class which offers the notifications service.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'app-notifications',
|
||||
templateUrl: 'notifications.component.html',
|
||||
styles: [`
|
||||
#notification-container {
|
||||
position: fixed;
|
||||
top: 50px;
|
||||
right: 20px;
|
||||
width: 300px;
|
||||
z-index: 1100;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class NotificationsComponent implements OnInit {
|
||||
/**
|
||||
* Singleton of the notification service.
|
||||
*/
|
||||
private static component: NotificationsComponent;
|
||||
|
||||
/**
|
||||
* List of notifications model.
|
||||
*/
|
||||
notificationList: Array<NotificationModel> = [];
|
||||
|
||||
/**
|
||||
* Creates an error notification.
|
||||
* @param {string} message The content of the notification.
|
||||
*/
|
||||
public static error(message: string): void {
|
||||
NotificationsComponent.notif(message, NotificationClasses.Error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a warning notification.
|
||||
* @param {string} message The content of the notification.
|
||||
*/
|
||||
public static warn(message: string): void {
|
||||
NotificationsComponent.notif(message, NotificationClasses.Warn);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an info notification.
|
||||
* @param {string} message The content of the notification.
|
||||
*/
|
||||
public static info(message: string): void {
|
||||
NotificationsComponent.notif(message, NotificationClasses.Info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a success notification.
|
||||
* @param {string} message The content of the notification.
|
||||
*/
|
||||
public static success(message: string): void {
|
||||
NotificationsComponent.notif(message, NotificationClasses.Success);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a notification. The {@code notifClass} param defines the category of
|
||||
* the notification (info, error...).
|
||||
* @param {string} message The content of the notification.
|
||||
* @param {NotificationClass} notifClass The category of the notification.
|
||||
*/
|
||||
private static notif(message: string, notifClass: NotificationClass): void {
|
||||
const elem = new NotificationModel(message, notifClass);
|
||||
|
||||
NotificationsComponent.component.notificationList.push(elem);
|
||||
|
||||
setTimeout(() => {
|
||||
elem.hide();
|
||||
|
||||
setTimeout(() => {
|
||||
NotificationsComponent.clearNotificationList();
|
||||
}, 900);
|
||||
}, 4500);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the consumed notifications in the list.
|
||||
* When a notification is created, a cooldown is set to hide it after a certain time period.
|
||||
* In this cooldown, the notification have only its display as {@code none}, but the
|
||||
* notification isn't remove from the list. This method removes it.
|
||||
*/
|
||||
private static clearNotificationList(): void {
|
||||
NotificationsComponent.component.notificationList.forEach(elem => {
|
||||
if (elem.notification.nativeElement.style.display === 'none') {
|
||||
const index = NotificationsComponent.component.notificationList.indexOf(elem);
|
||||
if (index > -1) {
|
||||
NotificationsComponent.component.notificationList.splice(index, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the reference of the singleton here because this component
|
||||
* is created at the application startup.
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
NotificationsComponent.component = this;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { Http } from '@angular/http';
|
||||
|
||||
import { AuthService } from '../core/services/auth.service';
|
||||
|
||||
@@ -13,7 +13,7 @@ export class DisconnectionComponent implements OnInit {
|
||||
constructor(
|
||||
private authService: AuthService,
|
||||
private router: Router,
|
||||
private http: Http
|
||||
private http: HttpClient
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
@@ -30,11 +30,6 @@
|
||||
required />
|
||||
<label for="password">Mot de passe</label>
|
||||
</div>
|
||||
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{loginError}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col submitFormArea">
|
||||
<a routerLink="/signin" class="indigo-text">
|
||||
Je n'ai pas de compte
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Router } from '@angular/router';
|
||||
import { User } from '../core/entities';
|
||||
import { LoginService } from './login.service';
|
||||
import { AuthService } from '../core/services/auth.service';
|
||||
import { NotificationsComponent } from '../core/notifications/notifications.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-login',
|
||||
@@ -26,7 +27,6 @@ import { AuthService } from '../core/services/auth.service';
|
||||
})
|
||||
export class LoginComponent {
|
||||
model: User = new User('', '', '', '', '', null, null, '');
|
||||
loginError: string;
|
||||
|
||||
constructor(
|
||||
private router: Router,
|
||||
@@ -36,31 +36,15 @@ export class LoginComponent {
|
||||
|
||||
onSubmit(): void {
|
||||
this.loginService.login(this.model).subscribe((pUser: User) => {
|
||||
console.log('Login success.');
|
||||
NotificationsComponent.success('Connexion réussie.');
|
||||
this.authService.setAuthenticated(pUser);
|
||||
this.router.navigate(['/myPosts']);
|
||||
}, (error) => {
|
||||
if (error.status === 401) {
|
||||
console.log('Login attempt failed.');
|
||||
this.setMessage('Adresse email ou mot de passe incorrect.');
|
||||
if (error === 'Unauthorized' || error.status === 401) {
|
||||
NotificationsComponent.error('Adresse email ou mot de passe incorrect.');
|
||||
} else {
|
||||
console.error('Error during login attempt.', error);
|
||||
this.setMessage('Une erreur est survenue lors de la connexion.');
|
||||
NotificationsComponent.error('Une erreur est survenue lors de la connexion.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setMessage(message: string): void {
|
||||
this.loginError = message;
|
||||
|
||||
const resultMsgDiv = document.getElementById('errorMsg');
|
||||
resultMsgDiv.style.maxHeight = '64px';
|
||||
|
||||
setTimeout(() => {
|
||||
resultMsgDiv.style.maxHeight = '0px';
|
||||
setTimeout(() => {
|
||||
this.loginError = undefined;
|
||||
}, 550);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,11 +57,6 @@
|
||||
required />
|
||||
<label for="confirmPassword">Confirmez votre mot de passe</label>
|
||||
</div>
|
||||
<div id="errorMsg" class="card red lighten-2 text-center z-depth-2">
|
||||
<div class="card-body">
|
||||
<p class="white-text mb-0">{{errorMsg}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col submitFormArea">
|
||||
<a routerLink="/login" class="indigo-text">
|
||||
Connexion
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { NotificationsComponent } from './../core/notifications/notifications.component';
|
||||
import { Component } from '@angular/core';
|
||||
import { User } from '../core/entities';
|
||||
import { SigninService } from './signin.service';
|
||||
@@ -14,19 +15,11 @@ import { Router } from '@angular/router';
|
||||
.submitFormArea {
|
||||
line-height: 50px;
|
||||
}
|
||||
|
||||
#errorMsg {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
transition: max-height 0.5s ease-out;
|
||||
margin: 0;
|
||||
}
|
||||
`]
|
||||
})
|
||||
export class SigninComponent {
|
||||
model: User = new User('', '', '', '', '', null, null, '');
|
||||
confirmPassword: string;
|
||||
errorMsg: string;
|
||||
|
||||
constructor(
|
||||
private signinService: SigninService,
|
||||
@@ -36,34 +29,21 @@ export class SigninComponent {
|
||||
onSubmit(): void {
|
||||
if (this.confirmPassword && this.confirmPassword === this.model.password) {
|
||||
this.signinService.signin(this.model).subscribe(() => {
|
||||
NotificationsComponent.success('Inscription réussie.');
|
||||
this.router.navigate(['/login']);
|
||||
}, error => {
|
||||
// FIXME: Type the error to get the status.
|
||||
switch (error.status) {
|
||||
case 409:
|
||||
this.setMessage('L\'adresse mail saisie n\'est pas disponible');
|
||||
NotificationsComponent.error('L\'adresse mail saisie n\'est pas disponible');
|
||||
break;
|
||||
default:
|
||||
this.setMessage('Une erreur est survenue lors de l\'inscription, veuillez réessayer plus tard');
|
||||
NotificationsComponent.error('Une erreur est survenue lors de l\'inscription, veuillez réessayer plus tard');
|
||||
break;
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.setMessage('Les mots de passe saisis ne correspondent pas');
|
||||
NotificationsComponent.error('Les mots de passe saisis ne correspondent pas');
|
||||
}
|
||||
}
|
||||
|
||||
setMessage(message: string): void {
|
||||
this.errorMsg = message;
|
||||
|
||||
const resultMsgDiv = document.getElementById('errorMsg');
|
||||
resultMsgDiv.style.maxHeight = '64px';
|
||||
|
||||
setTimeout(() => {
|
||||
resultMsgDiv.style.maxHeight = '0px';
|
||||
setTimeout(() => {
|
||||
this.errorMsg = undefined;
|
||||
}, 550);
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user