updated PendingChangesGuard and used it for user profile and notifications preferences screens
This commit is contained in:
parent
d30c270d72
commit
3c697c643c
@ -1,40 +1,32 @@
|
|||||||
import { CanDeactivate } from '@angular/router';
|
import { CanDeactivate } from '@angular/router';
|
||||||
import { Directive, HostListener, Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { Observable } from 'rxjs';
|
import { map, Observable } from 'rxjs';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { ConfirmationDialogService } from '../../../../../libs/common-ui/src/lib/dialog/confirmation-dialog.service';
|
||||||
|
import { FormGroup } from '@angular/forms';
|
||||||
|
import { ConfirmOptions } from '../../../../../libs/common-ui/src';
|
||||||
|
|
||||||
export interface ComponentCanDeactivate {
|
export interface ComponentCanDeactivate {
|
||||||
hasChanges: boolean;
|
changed: boolean;
|
||||||
}
|
form: FormGroup;
|
||||||
|
save: () => Promise<void>;
|
||||||
@Directive()
|
|
||||||
export abstract class ComponentHasChanges implements ComponentCanDeactivate {
|
|
||||||
abstract hasChanges: boolean;
|
|
||||||
|
|
||||||
protected constructor(protected _translateService: TranslateService) {}
|
|
||||||
|
|
||||||
@HostListener('window:beforeunload', ['$event'])
|
|
||||||
unloadNotification($event: any) {
|
|
||||||
if (this.hasChanges) {
|
|
||||||
// This message will be displayed in IE/Edge
|
|
||||||
$event.returnValue = this._translateService.instant('pending-changes-guard');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
|
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
|
||||||
constructor(private readonly _translateService: TranslateService) {}
|
constructor(private _dialogService: ConfirmationDialogService) {}
|
||||||
|
|
||||||
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
|
canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
|
||||||
// if there are no pending changes, just allow deactivation; else confirm first
|
if (component.changed) {
|
||||||
return !component.hasChanges
|
const dialogRef = this._dialogService.openDialog({ disableConfirm: component.form.invalid });
|
||||||
? true
|
return dialogRef.afterClosed().pipe(
|
||||||
: // NOTE: this warning message will only be shown when navigating elsewhere
|
map(result => {
|
||||||
// within your angular app;
|
if (result === ConfirmOptions.CONFIRM) {
|
||||||
// when navigating away from your angular app,
|
component.save();
|
||||||
// the browser will show a generic warning message
|
}
|
||||||
// see http://stackoverflow.com/a/42207299/7307355
|
return !!result;
|
||||||
confirm(this._translateService.instant('pending-changes-guard'));
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
<form (submit)="save()" [formGroup]="formGroup">
|
<form (submit)="save()" [formGroup]="form">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<div *ngFor="let category of notificationCategories">
|
<div *ngFor="let category of notificationCategories">
|
||||||
<div class="iqser-input-group header w-full">
|
<div class="iqser-input-group header w-full">
|
||||||
@ -36,7 +36,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="dialog-actions">
|
<div class="dialog-actions">
|
||||||
<button [disabled]="formGroup.invalid" color="primary" mat-flat-button type="submit">
|
<button [disabled]="form.invalid" color="primary" mat-flat-button type="submit">
|
||||||
{{ 'user-profile-screen.actions.save' | translate }}
|
{{ 'user-profile-screen.actions.save' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {
|
|||||||
NotificationGroupsKeys,
|
NotificationGroupsKeys,
|
||||||
NotificationGroupsValues,
|
NotificationGroupsValues,
|
||||||
} from '@red/domain';
|
} from '@red/domain';
|
||||||
|
import { BaseFormComponent } from '../../../../../../../../../libs/common-ui/src/lib/form/base-form.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-notifications-screen',
|
selector: 'redaction-notifications-screen',
|
||||||
@ -17,57 +18,57 @@ import {
|
|||||||
styleUrls: ['./notifications-screen.component.scss'],
|
styleUrls: ['./notifications-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class NotificationsScreenComponent implements OnInit {
|
export class NotificationsScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly emailNotificationScheduleTypes = EmailNotificationScheduleTypesValues;
|
readonly emailNotificationScheduleTypes = EmailNotificationScheduleTypesValues;
|
||||||
readonly notificationCategories = NotificationCategoriesValues;
|
readonly notificationCategories = NotificationCategoriesValues;
|
||||||
readonly notificationGroupsKeys = NotificationGroupsKeys;
|
readonly notificationGroupsKeys = NotificationGroupsKeys;
|
||||||
readonly notificationGroupsValues = NotificationGroupsValues;
|
readonly notificationGroupsValues = NotificationGroupsValues;
|
||||||
readonly translations = notificationsTranslations;
|
readonly translations = notificationsTranslations;
|
||||||
|
|
||||||
readonly formGroup: FormGroup = this._getForm();
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
private readonly _formBuilder: FormBuilder,
|
private readonly _formBuilder: FormBuilder,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
private readonly _notificationPreferencesService: NotificationPreferencesService,
|
private readonly _notificationPreferencesService: NotificationPreferencesService,
|
||||||
) {}
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
await this._initializeForm();
|
await this._initializeForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
isCategoryActive(category: string) {
|
isCategoryActive(category: string) {
|
||||||
return this.formGroup.get(`${category}Enabled`).value;
|
return this.form.get(`${category}Enabled`).value;
|
||||||
}
|
}
|
||||||
|
|
||||||
setEmailNotificationType(type: string) {
|
setEmailNotificationType(type: string) {
|
||||||
this.formGroup.get('emailNotificationType').setValue(type);
|
this.form.get('emailNotificationType').setValue(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEmailNotificationType() {
|
getEmailNotificationType() {
|
||||||
return this.formGroup.get('emailNotificationType').value;
|
return this.form.get('emailNotificationType').value;
|
||||||
}
|
}
|
||||||
|
|
||||||
isPreferenceChecked(category: string, preference: string) {
|
isPreferenceChecked(category: string, preference: string) {
|
||||||
return this.formGroup.get(category).value.includes(preference);
|
return this.form.get(category).value.includes(preference);
|
||||||
}
|
}
|
||||||
|
|
||||||
addRemovePreference(checked: boolean, category: string, preference: string) {
|
addRemovePreference(checked: boolean, category: string, preference: string) {
|
||||||
const preferences = this.formGroup.get(category).value;
|
const preferences = this.form.get(category).value;
|
||||||
if (checked) {
|
if (checked) {
|
||||||
preferences.push(preference);
|
preferences.push(preference);
|
||||||
} else {
|
} else {
|
||||||
const indexOfPreference = preferences.indexOf(preference);
|
const indexOfPreference = preferences.indexOf(preference);
|
||||||
preferences.splice(indexOfPreference, 1);
|
preferences.splice(indexOfPreference, 1);
|
||||||
}
|
}
|
||||||
this.formGroup.get(category).setValue(preferences);
|
this.form.get(category).setValue(preferences);
|
||||||
}
|
}
|
||||||
|
|
||||||
async save() {
|
async save() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
try {
|
try {
|
||||||
await this._notificationPreferencesService.update(this.formGroup.value).toPromise();
|
await this._notificationPreferencesService.update(this.form.value).toPromise();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this._toaster.error(_('notifications-screen.error.generic'));
|
this._toaster.error(_('notifications-screen.error.generic'));
|
||||||
}
|
}
|
||||||
@ -87,8 +88,10 @@ export class NotificationsScreenComponent implements OnInit {
|
|||||||
private async _initializeForm() {
|
private async _initializeForm() {
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
|
|
||||||
|
this.form = this._getForm();
|
||||||
const notificationPreferences = await this._notificationPreferencesService.get().toPromise();
|
const notificationPreferences = await this._notificationPreferencesService.get().toPromise();
|
||||||
this.formGroup.patchValue(notificationPreferences);
|
this.form.patchValue(notificationPreferences);
|
||||||
|
this.initialFormValue = JSON.parse(JSON.stringify(this.form.getRawValue()));
|
||||||
|
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,9 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SharedModule } from '@shared/shared.module';
|
import { SharedModule } from '@shared/shared.module';
|
||||||
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
import { NotificationsScreenComponent } from './notifications-screen/notifications-screen.component';
|
||||||
|
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
|
||||||
|
|
||||||
const routes = [{ path: '', component: NotificationsScreenComponent }];
|
const routes = [{ path: '', component: NotificationsScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [NotificationsScreenComponent],
|
declarations: [NotificationsScreenComponent],
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import { PermissionsService } from '../../../../../services/permissions.service'
|
|||||||
import { UserService } from '../../../../../services/user.service';
|
import { UserService } from '../../../../../services/user.service';
|
||||||
import { ConfigService } from '../../../../../services/config.service';
|
import { ConfigService } from '../../../../../services/config.service';
|
||||||
import { LanguageService } from '../../../../../i18n/language.service';
|
import { LanguageService } from '../../../../../i18n/language.service';
|
||||||
|
import { ConfirmationDialogService } from '../../../../../../../../../libs/common-ui/src/lib/dialog/confirmation-dialog.service';
|
||||||
|
import { BaseFormComponent } from '../../../../../../../../../libs/common-ui/src/lib/form/base-form.component';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-user-profile-screen',
|
selector: 'redaction-user-profile-screen',
|
||||||
@ -16,8 +18,7 @@ import { LanguageService } from '../../../../../i18n/language.service';
|
|||||||
styleUrls: ['./user-profile-screen.component.scss'],
|
styleUrls: ['./user-profile-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class UserProfileScreenComponent implements OnInit {
|
export class UserProfileScreenComponent extends BaseFormComponent implements OnInit {
|
||||||
readonly form: FormGroup = this._getForm();
|
|
||||||
changePasswordUrl: SafeResourceUrl;
|
changePasswordUrl: SafeResourceUrl;
|
||||||
translations = languagesTranslations;
|
translations = languagesTranslations;
|
||||||
|
|
||||||
@ -30,11 +31,12 @@ export class UserProfileScreenComponent implements OnInit {
|
|||||||
private readonly _configService: ConfigService,
|
private readonly _configService: ConfigService,
|
||||||
private readonly _languageService: LanguageService,
|
private readonly _languageService: LanguageService,
|
||||||
private readonly _domSanitizer: DomSanitizer,
|
private readonly _domSanitizer: DomSanitizer,
|
||||||
private readonly _translateService: TranslateService,
|
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
|
private readonly _dialogService: ConfirmationDialogService,
|
||||||
|
protected readonly _translateService: TranslateService,
|
||||||
) {
|
) {
|
||||||
|
super();
|
||||||
this._loadingService.start();
|
this._loadingService.start();
|
||||||
|
|
||||||
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
|
this.changePasswordUrl = this._domSanitizer.bypassSecurityTrustResourceUrl(
|
||||||
`${this._configService.values.OAUTH_URL}/account/password`,
|
`${this._configService.values.OAUTH_URL}/account/password`,
|
||||||
);
|
);
|
||||||
@ -100,6 +102,7 @@ export class UserProfileScreenComponent implements OnInit {
|
|||||||
|
|
||||||
private _initializeForm(): void {
|
private _initializeForm(): void {
|
||||||
try {
|
try {
|
||||||
|
this.form = this._getForm();
|
||||||
this._profileModel = {
|
this._profileModel = {
|
||||||
email: this._userService.currentUser.email,
|
email: this._userService.currentUser.email,
|
||||||
firstName: this._userService.currentUser.firstName,
|
firstName: this._userService.currentUser.firstName,
|
||||||
@ -111,6 +114,7 @@ export class UserProfileScreenComponent implements OnInit {
|
|||||||
this.form.get('email').disable();
|
this.form.get('email').disable();
|
||||||
}
|
}
|
||||||
this.form.patchValue(this._profileModel, { emitEvent: false });
|
this.form.patchValue(this._profileModel, { emitEvent: false });
|
||||||
|
this.initialFormValue = this.form.getRawValue();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
} finally {
|
} finally {
|
||||||
this._loadingService.stop();
|
this._loadingService.stop();
|
||||||
|
|||||||
@ -3,8 +3,9 @@ import { RouterModule } from '@angular/router';
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { SharedModule } from '@shared/shared.module';
|
import { SharedModule } from '@shared/shared.module';
|
||||||
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
import { UserProfileScreenComponent } from './user-profile-screen/user-profile-screen.component';
|
||||||
|
import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard';
|
||||||
|
|
||||||
const routes = [{ path: '', component: UserProfileScreenComponent }];
|
const routes = [{ path: '', component: UserProfileScreenComponent, canDeactivate: [PendingChangesGuard] }];
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [UserProfileScreenComponent],
|
declarations: [UserProfileScreenComponent],
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { AppStateService } from '@state/app-state.service';
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
|
||||||
import { AdminDialogService } from '../../services/admin-dialog.service';
|
import { AdminDialogService } from '../../services/admin-dialog.service';
|
||||||
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
import { DictionaryManagerComponent } from '@shared/components/dictionary-manager/dictionary-manager.component';
|
||||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||||
@ -16,7 +15,7 @@ import { Dictionary } from '@red/domain';
|
|||||||
templateUrl: './dictionary-overview-screen.component.html',
|
templateUrl: './dictionary-overview-screen.component.html',
|
||||||
styleUrls: ['./dictionary-overview-screen.component.scss'],
|
styleUrls: ['./dictionary-overview-screen.component.scss'],
|
||||||
})
|
})
|
||||||
export class DictionaryOverviewScreenComponent extends ComponentHasChanges implements OnInit, OnDestroy {
|
export class DictionaryOverviewScreenComponent implements OnInit, OnDestroy {
|
||||||
readonly circleButtonTypes = CircleButtonTypes;
|
readonly circleButtonTypes = CircleButtonTypes;
|
||||||
readonly currentUser = this._userService.currentUser;
|
readonly currentUser = this._userService.currentUser;
|
||||||
|
|
||||||
@ -37,9 +36,7 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
|
|||||||
private readonly _dialogService: AdminDialogService,
|
private readonly _dialogService: AdminDialogService,
|
||||||
protected readonly _translateService: TranslateService,
|
protected readonly _translateService: TranslateService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
) {
|
) {}
|
||||||
super(_translateService);
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasChanges() {
|
get hasChanges() {
|
||||||
return this._dictionaryManager.editor.hasChanges;
|
return this._dictionaryManager.editor.hasChanges;
|
||||||
|
|||||||
@ -3,7 +3,6 @@ import { PermissionsService } from '@services/permissions.service';
|
|||||||
import { Debounce, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
import { Debounce, IconButtonTypes, LoadingService, Toaster } from '@iqser/common-ui';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { ComponentHasChanges } from '@guards/can-deactivate.guard';
|
|
||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||||
import { RulesService } from '../../services/rules.service';
|
import { RulesService } from '../../services/rules.service';
|
||||||
@ -17,7 +16,7 @@ import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorCon
|
|||||||
styleUrls: ['./rules-screen.component.scss'],
|
styleUrls: ['./rules-screen.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
})
|
})
|
||||||
export class RulesScreenComponent extends ComponentHasChanges implements OnInit {
|
export class RulesScreenComponent implements OnInit {
|
||||||
readonly iconButtonTypes = IconButtonTypes;
|
readonly iconButtonTypes = IconButtonTypes;
|
||||||
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
readonly editorOptions: IStandaloneEditorConstructionOptions = {
|
||||||
theme: 'vs',
|
theme: 'vs',
|
||||||
@ -43,9 +42,7 @@ export class RulesScreenComponent extends ComponentHasChanges implements OnInit
|
|||||||
private readonly _toaster: Toaster,
|
private readonly _toaster: Toaster,
|
||||||
protected readonly _translateService: TranslateService,
|
protected readonly _translateService: TranslateService,
|
||||||
private readonly _loadingService: LoadingService,
|
private readonly _loadingService: LoadingService,
|
||||||
) {
|
) {}
|
||||||
super(_translateService);
|
|
||||||
}
|
|
||||||
|
|
||||||
get hasChanges(): boolean {
|
get hasChanges(): boolean {
|
||||||
return this.currentLines.toString() !== this.initialLines.toString();
|
return this.currentLines.toString() !== this.initialLines.toString();
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Subproject commit 2e36f3cac77411c4cef0ec5b51165aa62a9d075c
|
Subproject commit 867d7b089ee3d10abf42bf6957c02f7f48ffdb7f
|
||||||
Loading…
x
Reference in New Issue
Block a user