Pull request #220: RED-1635

Merge in RED/ui from RED-1635 to master

* commit '8e995e2afed97cc82010c3765ee83549b7f59477':
  add min loading time to loading service
  recalculate data on edit and on delete
  skip requests for entries, show loading indicator when action finished
  refactor appLoadStateService
  update dialogs
  fix dictionary update
This commit is contained in:
Timo Bejan 2021-06-26 21:09:53 +02:00
commit a0e02acca3
21 changed files with 177 additions and 206 deletions

View File

@ -1,4 +1,4 @@
<router-outlet></router-outlet>
<redaction-full-page-loading-indicator
[displayed]="appLoadStateService.loading | async"
[displayed]="loadingService.isLoading | async"
></redaction-full-page-loading-indicator>

View File

@ -1,6 +1,5 @@
import { Component } from '@angular/core';
import { AppLoadStateService } from '@services/app-load-state.service';
import { RouterHistoryService } from '@services/router-history.service';
import { LoadingService } from '@services/loading.service';
@Component({
selector: 'redaction-root',
@ -8,8 +7,5 @@ import { RouterHistoryService } from '@services/router-history.service';
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(
public appLoadStateService: AppLoadStateService,
private readonly _routerHistoryService: RouterHistoryService
) {}
constructor(readonly loadingService: LoadingService) {}
}

View File

@ -1,26 +1,19 @@
import {
ActivatedRouteSnapshot,
CanActivate,
Router,
RouterStateSnapshot,
UrlTree
} from '@angular/router';
import { ActivatedRouteSnapshot, CanActivate, RouterStateSnapshot, UrlTree } from '@angular/router';
import { Injectable, Injector } from '@angular/core';
import { from, of } from 'rxjs';
import { AppLoadStateService } from '@services/app-load-state.service';
import { LoadingService } from '@services/loading.service';
@Injectable({
providedIn: 'root'
})
export class CompositeRouteGuard implements CanActivate {
constructor(
protected readonly _router: Router,
protected readonly _injector: Injector,
private readonly _appLoadStateService: AppLoadStateService
private readonly _loadingService: LoadingService
) {}
async canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Promise<boolean> {
this._appLoadStateService.pushLoadingEvent(true);
this._loadingService.start();
const routeGuards = route.data.routeGuards;
@ -40,13 +33,13 @@ export class CompositeRouteGuard implements CanActivate {
const result = await canActivateResult.toPromise();
if (!result) {
this._appLoadStateService.pushLoadingEvent(false);
this._loadingService.stop();
return false;
}
}
}
this._appLoadStateService.pushLoadingEvent(false);
this._loadingService.stop();
return true;
}

View File

@ -40,18 +40,16 @@ export class DossierTemplateActionsComponent {
);
}
openDeleteDossierTemplateDialog($event: any) {
this._dialogService.openDeleteDossierTemplateDialog(
$event,
this.dossierTemplate,
async () => {
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
await this._router.navigate(['main', 'admin']);
if (this.loadDossierTemplatesData) {
this.loadDossierTemplatesData.emit();
}
openDeleteDossierTemplateDialog($event?: MouseEvent) {
$event?.stopPropagation();
this._dialogService.openDeleteDossierTemplateDialog(this.dossierTemplate, async () => {
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
await this._router.navigate(['main', 'admin']);
if (this.loadDossierTemplatesData) {
this.loadDossierTemplatesData.emit();
}
);
});
}
}

View File

@ -1,19 +1,14 @@
<section class="dialog">
<div class="dialog-header heading-l">
{{
(dictionary ? 'add-edit-dictionary.title.edit' : 'add-edit-dictionary.title.new')
| translate: { name: dictionary?.type | humanize }
}}
{{ dialogHeader }}
</div>
<form (submit)="saveDictionary()" [formGroup]="dictionaryForm">
<div class="dialog-content">
<ng-container *ngIf="!!dictionary">
<div class="red-input-group mb-14">
<label translate="add-edit-dictionary.form.name"></label>
{{ dictionary.label }}
</div>
</ng-container>
<div class="red-input-group mb-14" *ngIf="!!dictionary">
<label translate="add-edit-dictionary.form.name"></label>
{{ dictionary.label }}
</div>
<div class="first-row">
<div *ngIf="!dictionary" class="red-input-group required">
@ -53,13 +48,7 @@
[style.background]="dictionaryForm.get('hexColor').value"
class="input-icon"
>
<mat-icon
*ngIf="
!dictionaryForm.get('hexColor').value ||
dictionaryForm.get('hexColor').value?.length === 0
"
svgIcon="red:color-picker"
></mat-icon>
<mat-icon *ngIf="hasColor" svgIcon="red:color-picker"></mat-icon>
</div>
</div>
</div>
@ -81,11 +70,11 @@
<div class="red-input-group slider-row">
<mat-button-toggle-group appearance="legacy" formControlName="hint" name="hint">
<mat-button-toggle [value]="false">
{{ 'add-edit-dictionary.form.redaction' | translate }}</mat-button-toggle
>
{{ 'add-edit-dictionary.form.redaction' | translate }}
</mat-button-toggle>
<mat-button-toggle [value]="true">
{{ 'add-edit-dictionary.form.hint' | translate }}</mat-button-toggle
>
{{ 'add-edit-dictionary.form.hint' | translate }}
</mat-button-toggle>
</mat-button-toggle-group>
</div>

View File

@ -1,5 +1,4 @@
import { Component, Inject } from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http';
@ -7,6 +6,7 @@ import { Observable } from 'rxjs';
import { NotificationService, NotificationType } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { humanize } from '../../../../utils/functions';
@Component({
selector: 'redaction-add-edit-dictionary-dialog',
@ -20,17 +20,16 @@ export class AddEditDictionaryDialogComponent {
constructor(
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _appStateService: AppStateService,
private readonly _formBuilder: FormBuilder,
private readonly _notificationService: NotificationService,
private readonly _translateService: TranslateService,
public dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>,
private readonly _dialogRef: MatDialogRef<AddEditDictionaryDialogComponent>,
@Inject(MAT_DIALOG_DATA)
public data: { dictionary: TypeValueWrapper; dossierTemplateId: string }
private readonly _data: { dictionary: TypeValueWrapper; dossierTemplateId: string }
) {
this.dictionary = data.dictionary;
this._dossierTemplateId = data.dossierTemplateId;
this.dictionaryForm = this._formBuilder.group({
this.dictionary = _data.dictionary;
this._dossierTemplateId = _data.dossierTemplateId;
this.dictionaryForm = _formBuilder.group({
type: [this.dictionary?.type, [Validators.required, Validators.minLength(3)]],
description: [this.dictionary?.description],
rank: [this.dictionary?.rank, Validators.required],
@ -41,7 +40,19 @@ export class AddEditDictionaryDialogComponent {
});
}
get dictCaseSensitive() {
get dialogHeader(): string {
const i18nString = 'add-edit-dictionary.title.' + (this.dictionary ? 'edit' : 'new');
return this._translateService.instant(i18nString, {
name: humanize(this.dictionary?.type)
});
}
get hasColor(): boolean {
const hexColorValue = this.dictionaryForm.get('hexColor').value;
return !hexColorValue || hexColorValue?.length === 0;
}
get dictCaseSensitive(): boolean {
return this.dictionary ? !this.dictionary.caseInsensitive : false;
}
@ -63,14 +74,14 @@ export class AddEditDictionaryDialogComponent {
async saveDictionary() {
const typeValue: TypeValue = this._formToObject();
let observable: Observable<any>;
if (this.dictionary) {
// edit mode
observable = this._dictionaryControllerService.updateType(
typeValue,
typeValue.type,
this._dossierTemplateId
this._dossierTemplateId,
typeValue.type
);
} else {
// create mode
@ -79,9 +90,7 @@ export class AddEditDictionaryDialogComponent {
}
observable.subscribe(
() => {
this.dialogRef.close({ dictionary: this.dictionary ? null : typeValue });
},
() => this._dialogRef.close(this.dictionary ? null : typeValue),
error => {
if (error.status === 409) {
this._notifyError('add-edit-dictionary.error.dictionary-already-exists');

View File

@ -115,7 +115,7 @@ export class AddEditDossierTemplateDialogComponent {
.toPromise();
await this._appStateService.loadAllDossierTemplates();
await this._appStateService.loadDictionaryData();
this.dialogRef.close({ dossierTemplate });
this.dialogRef.close(dossierTemplate);
}
private _applyValidityIntervalConstraints(value): boolean {

View File

@ -22,14 +22,14 @@ export class EditColorDialogComponent {
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _notificationService: NotificationService,
private readonly _translateService: TranslateService,
public dialogRef: MatDialogRef<EditColorDialogComponent>,
private readonly _dialogRef: MatDialogRef<EditColorDialogComponent>,
@Inject(MAT_DIALOG_DATA)
public data: { colors: Colors; colorKey: string; dossierTemplateId: string }
private readonly _data: { colors: Colors; colorKey: string; dossierTemplateId: string }
) {
this.colors = data.colors;
this.colorKey = data.colorKey;
this._dossierTemplateId = data.dossierTemplateId;
this._initialColor = data.colors[this.colorKey];
this.colors = _data.colors;
this.colorKey = _data.colorKey;
this._dossierTemplateId = _data.dossierTemplateId;
this._initialColor = _data.colors[this.colorKey];
this.colorForm = this._formBuilder.group({
color: [this.colors[this.colorKey], [Validators.required, Validators.minLength(7)]]
@ -50,7 +50,7 @@ export class EditColorDialogComponent {
await this._dictionaryControllerService
.setColors(colors, this._dossierTemplateId)
.toPromise();
this.dialogRef.close(true);
this._dialogRef.close(true);
this._notificationService.showToastNotification(
this._translateService.instant('edit-color-dialog.success', {
color: this._translateService.instant(

View File

@ -28,25 +28,17 @@ export class DefaultColorsScreenComponent extends BaseListingComponent<{
protected readonly _injector: Injector
) {
super(_injector);
this._appStateService.activateDossierTemplate(
_activatedRoute.snapshot.params.dossierTemplateId
);
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
this._loadColors();
}
async loadDossierTemplatesData(): Promise<void> {
await this._appStateService.loadAllDossierTemplates();
}
openEditColorDialog($event: any, color: { key: string; value: string }) {
$event.stopPropagation();
this._dialogService.openEditColorsDialog(
this._colorsObj,
color.key,
this._appStateService.activeDossierTemplateId,
async () => {
this._loadColors();
}
async () => this._loadColors()
);
}

View File

@ -30,23 +30,16 @@
</div>
<span class="all-caps-label">
{{
'dictionary-listing.table-header.title'
| translate: { length: displayedEntities.length }
}}
{{ tableHeader }}
</span>
<ng-container *ngIf="areSomeEntitiesSelected && !loading">
<redaction-circle-button
(action)="openDeleteDictionariesDialog($event)"
*ngIf="permissionsService.isAdmin()"
icon="red:trash"
tooltip="dictionary-listing.bulk.delete"
type="dark-bg"
></redaction-circle-button>
</ng-container>
<mat-spinner *ngIf="loading" diameter="15"></mat-spinner>
<redaction-circle-button
(action)="openDeleteDictionariesDialog($event)"
*ngIf="areSomeEntitiesSelected && permissionsService.isAdmin()"
icon="red:trash"
tooltip="dictionary-listing.bulk.delete"
type="dark-bg"
></redaction-circle-button>
<div class="attributes-actions-container">
<redaction-input-with-action
@ -160,25 +153,21 @@
></redaction-annotation-icon>
</div>
<div class="actions-container">
<div class="actions-container" *ngIf="permissionsService.isAdmin()">
<div class="action-buttons">
<redaction-circle-button
(action)="openDeleteDictionariesDialog($event, [dict.type])"
*ngIf="permissionsService.isAdmin()"
icon="red:trash"
tooltip="dictionary-listing.action.delete"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<redaction-circle-button
(action)="openAddEditDictionaryDialog($event, dict)"
*ngIf="permissionsService.isAdmin()"
icon="red:edit"
tooltip="dictionary-listing.action.edit"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
</div>
</div>
<div class="scrollbar-placeholder"></div>
@ -199,7 +188,3 @@
</div>
</div>
</section>
<redaction-full-page-loading-indicator
[displayed]="!viewReady"
></redaction-full-page-loading-indicator>

View File

@ -9,6 +9,15 @@ import { ActivatedRoute } from '@angular/router';
import { AdminDialogService } from '../../services/admin-dialog.service';
import { BaseListingComponent } from '@shared/base/base-listing.component';
import { TypeValueWrapper } from '../../../../models/file/type-value.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { LoadingService } from '../../../../services/loading.service';
const toChartConfig = (dict: TypeValueWrapper): DoughnutChartConfig => ({
value: dict.entries ? dict.entries.length : 0,
color: dict.hexColor,
label: dict.label,
key: dict.type
});
@Component({
selector: 'redaction-dictionary-listing-screen',
@ -19,9 +28,7 @@ export class DictionaryListingScreenComponent
extends BaseListingComponent<TypeValueWrapper>
implements OnInit
{
viewReady = false;
chartData: DoughnutChartConfig[] = [];
loading = false;
protected readonly _searchKey = 'label';
protected readonly _selectionKey = 'type';
protected readonly _sortKey = 'dictionary-listing';
@ -31,13 +38,20 @@ export class DictionaryListingScreenComponent
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _activatedRoute: ActivatedRoute,
private readonly _appStateService: AppStateService,
private readonly _loadingService: LoadingService,
private readonly _translateService: TranslateService,
readonly permissionsService: PermissionsService,
protected readonly _injector: Injector
) {
super(_injector);
this._appStateService.activateDossierTemplate(
_activatedRoute.snapshot.params.dossierTemplateId
);
_loadingService.start();
_appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId);
}
get tableHeader(): string {
return this._translateService.instant('dictionary-listing.table-header.title', {
length: this.displayedEntities.length
});
}
ngOnInit(): void {
@ -47,15 +61,15 @@ export class DictionaryListingScreenComponent
openDeleteDictionariesDialog($event?: MouseEvent, types = this.selectedEntitiesIds) {
$event?.stopPropagation();
this._dialogService.openDeleteDictionariesDialog(
$event,
types,
this._appStateService.activeDossierTemplateId,
async () => {
this.selectedEntitiesIds = [];
this.loading = true;
this._loadingService.start();
await this._appStateService.loadDictionaryData();
this._loadDictionaryData();
this.loading = false;
this._loadDictionaryData(false);
this._calculateData();
this._loadingService.stop();
}
);
}
@ -67,27 +81,37 @@ export class DictionaryListingScreenComponent
this._appStateService.activeDossierTemplateId,
async newDictionary => {
if (newDictionary) {
this._loadingService.start();
await this._appStateService.loadDictionaryData();
this._loadDictionaryData();
this._loadDictionaryData(false);
this._calculateData();
this._loadingService.stop();
}
}
);
}
private _loadDictionaryData() {
private _loadDictionaryData(loadEntries = true): void {
const appStateDictionaryData =
this._appStateService.dictionaryData[this._appStateService.activeDossierTemplateId];
this.allEntities = Object.keys(appStateDictionaryData)
.map(key => appStateDictionaryData[key])
.filter(d => !d.virtual);
const entities = Object.values(appStateDictionaryData).filter(d => !d.virtual);
if (!loadEntries)
this.allEntities = entities.map(dict => {
dict.entries = this.allEntities.find(d => d.type === dict.type)?.entries || [];
return dict;
});
else this.allEntities = entities;
this.displayedEntities = [...this.allEntities];
if (!loadEntries) return;
const dataObs = this.allEntities.map(dict =>
this._dictionaryControllerService
.getDictionaryForType(this._appStateService.activeDossierTemplateId, dict.type)
.pipe(
tap(values => {
dict.entries = values.entries ? values.entries : [];
}),
tap(values => (dict.entries = values.entries ?? [])),
catchError(() => {
console.log('error');
dict.entries = [];
@ -95,24 +119,15 @@ export class DictionaryListingScreenComponent
})
)
);
forkJoin(dataObs)
.pipe(defaultIfEmpty(null))
.subscribe(() => {
this._calculateData();
});
.subscribe(() => this._calculateData());
}
private _calculateData() {
this.chartData = [];
for (const dict of this.allEntities) {
this.chartData.push({
value: dict.entries ? dict.entries.length : 0,
color: dict.hexColor,
label: dict.label,
key: dict.type
});
}
private _calculateData(): void {
this.chartData = this.allEntities.map(dict => toChartConfig(dict));
this.chartData.sort((a, b) => (a.label < b.label ? -1 : 1));
this.viewReady = true;
this._loadingService.stop();
}
}

View File

@ -10,8 +10,7 @@
tooltip="dictionary-overview.action.delete"
tooltipPosition="below"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<redaction-circle-button
(action)="openEditDictionaryDialog($event)"
@ -20,8 +19,7 @@
tooltip="dictionary-overview.action.edit"
tooltipPosition="below"
type="dark-bg"
>
</redaction-circle-button>
></redaction-circle-button>
<redaction-circle-button
(action)="download()"

View File

@ -64,14 +64,15 @@ export class DictionaryOverviewScreenComponent extends ComponentHasChanges imple
);
}
openDeleteDictionaryDialog($event: any) {
openDeleteDictionaryDialog($event?: MouseEvent) {
$event?.stopPropagation();
this._dialogService.openDeleteDictionariesDialog(
$event,
[this.dictionary.type],
this.dictionary.dossierTemplateId,
async () => {
await this._appStateService.loadDictionaryData();
this._router.navigate([
await this._router.navigate([
'/main',
'admin',
'dossier-templates',

View File

@ -36,8 +36,8 @@ export class DossierTemplatesListingScreenComponent
openDeleteTemplatesDialog($event?: MouseEvent) {
$event?.stopPropagation();
this._dialogService.openBulkDeleteDossierTemplatesDialog(
$event,
this.selectedEntitiesIds,
async () => {
this.selectedEntitiesIds = [];

View File

@ -7,20 +7,16 @@ import {
DossierTemplateModel,
FileAttributeConfig,
FileAttributesConfig,
FileManagementControllerService,
ManualRedactionControllerService,
SMTPConfigurationModel,
TypeValue,
User
} from '@redaction/red-ui-http';
import { AddEditFileAttributeDialogComponent } from '../dialogs/add-edit-file-attribute-dialog/add-edit-file-attribute-dialog.component';
import { AddEditDictionaryDialogComponent } from '../dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component';
import { AddEditDossierTemplateDialogComponent } from '../dialogs/add-edit-dossier-template-dialog/add-edit-dossier-template-dialog.component';
import { NotificationService } from '@services/notification.service';
import { ConfirmationDialogComponent } from '@shared/dialogs/confirmation-dialog/confirmation-dialog.component';
import { AppStateService } from '@state/app-state.service';
import { ConfirmDeleteFileAttributeDialogComponent } from '../dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component';
import { EditColorDialogComponent } from '../dialogs/edit-color-dialog/edit-color-dialog.component';
import { TranslateService } from '@ngx-translate/core';
import { SmtpAuthDialogComponent } from '../dialogs/smtp-auth-dialog/smtp-auth-dialog.component';
import { AddEditUserDialogComponent } from '../dialogs/add-edit-user-dialog/add-edit-user-dialog.component';
import { ConfirmDeleteUsersDialogComponent } from '../dialogs/confirm-delete-users-dialog/confirm-delete-users-dialog.component';
@ -44,22 +40,15 @@ const dialogConfig = {
export class AdminDialogService {
constructor(
private readonly _dialog: MatDialog,
private readonly _translateService: TranslateService,
private readonly _appStateService: AppStateService,
private readonly _dossierTemplateControllerService: DossierTemplateControllerService,
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _fileManagementControllerService: FileManagementControllerService,
private readonly _notificationService: NotificationService,
private readonly _manualRedactionControllerService: ManualRedactionControllerService
private readonly _dictionaryControllerService: DictionaryControllerService
) {}
openDeleteDictionariesDialog(
$event: MouseEvent,
dictionaryTypes: string[],
dossierTemplateId: string,
cb?: Function
cb?: () => void
): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(async result => {
if (result) {
@ -73,11 +62,9 @@ export class AdminDialogService {
}
openDeleteDossierTemplateDialog(
$event: MouseEvent,
dossierTemplate: DossierTemplateModel,
cb?: Function
cb?: () => void
): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(async result => {
if (result) {
@ -88,11 +75,9 @@ export class AdminDialogService {
}
openBulkDeleteDossierTemplatesDialog(
$event: MouseEvent,
dossierTemplateIds: string[],
cb?: Function
): MatDialogRef<ConfirmationDialogComponent> {
$event.stopPropagation();
const ref = this._dialog.open(ConfirmationDialogComponent, dialogConfig);
ref.afterClosed().subscribe(async result => {
if (result) {
@ -105,7 +90,7 @@ export class AdminDialogService {
openAddEditDictionaryDialog(
dictionary: TypeValueWrapper,
dossierTemplateId: string,
cb?: Function
cb?: (newDictionary: TypeValue | null) => void
): MatDialogRef<AddEditDictionaryDialogComponent> {
const ref = this._dialog.open(AddEditDictionaryDialogComponent, {
...dialogConfig,
@ -113,10 +98,9 @@ export class AdminDialogService {
autoFocus: true
});
ref.afterClosed().subscribe(result => {
if (result && cb) {
cb(result);
}
ref.afterClosed().subscribe((newDictionary: TypeValue) => {
if (newDictionary && cb) cb(newDictionary);
else if (cb) cb(null);
});
return ref;
@ -126,7 +110,7 @@ export class AdminDialogService {
colors: Colors,
colorKey: string,
dossierTemplateId: string,
cb?: Function
cb?: (result: boolean) => void
): MatDialogRef<EditColorDialogComponent> {
const ref = this._dialog.open(EditColorDialogComponent, {
...dialogConfig,
@ -134,7 +118,7 @@ export class AdminDialogService {
autoFocus: true
});
ref.afterClosed().subscribe(result => {
ref.afterClosed().subscribe((result: boolean) => {
if (result && cb) {
cb(result);
}

View File

@ -2,7 +2,6 @@ import { Inject, Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, Router, RouterStateSnapshot } from '@angular/router';
import { KeycloakAuthGuard, KeycloakService } from 'keycloak-angular';
import { UserService } from '@services/user.service';
import { AppLoadStateService } from '@services/app-load-state.service';
import { AppConfigKey, AppConfigService } from '@app-config/app-config.service';
import { BASE_HREF } from '../../tokens';
@ -15,7 +14,6 @@ export class AuthGuard extends KeycloakAuthGuard {
protected readonly _router: Router,
protected readonly _keycloak: KeycloakService,
private readonly _appConfigService: AppConfigService,
private readonly _appLoadStateService: AppLoadStateService,
private readonly _userService: UserService
) {
super(_router, _keycloak);

View File

@ -1,7 +1,7 @@
import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { UserService } from '@services/user.service';
import { AppLoadStateService } from '@services/app-load-state.service';
import { LoadingService } from '@services/loading.service';
import { Observable } from 'rxjs';
@Injectable({
@ -10,7 +10,7 @@ import { Observable } from 'rxjs';
export class RedRoleGuard implements CanActivate {
constructor(
protected readonly _router: Router,
private readonly _appLoadStateService: AppLoadStateService,
private readonly _loadingService: LoadingService,
private readonly _userService: UserService
) {}
@ -18,7 +18,7 @@ export class RedRoleGuard implements CanActivate {
return new Observable(obs => {
if (!this._userService.user.hasAnyREDRoles) {
this._router.navigate(['/auth-error']);
this._appLoadStateService.pushLoadingEvent(false);
this._loadingService.stop();
obs.next(false);
obs.complete();
} else {

View File

@ -162,14 +162,13 @@ export class DictionaryManagerComponent implements OnChanges, OnInit {
}
private _applySearchDecorations() {
this._searchDecorations = this._codeEditor?.deltaDecorations(this._searchDecorations, []);
this._searchDecorations =
this._codeEditor?.deltaDecorations(this._searchDecorations, []) || [];
const decorations = this.findMatches.map(match => this._getSearchDecoration(match));
this._searchDecorations = this._codeEditor?.deltaDecorations(
this._searchDecorations,
decorations
);
this._searchDecorations =
this._codeEditor?.deltaDecorations(this._searchDecorations, decorations) || [];
}
private _getMatches(text: string): FindMatch[] {

View File

@ -1,17 +0,0 @@
import { EventEmitter, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class AppLoadStateService {
private _loadingEvent = new EventEmitter();
get loading(): Observable<boolean> {
return this._loadingEvent;
}
pushLoadingEvent(event: boolean) {
this._loadingEvent.next(event);
}
}

View File

@ -0,0 +1,33 @@
import { EventEmitter, Injectable } from '@angular/core';
import { Observable } from 'rxjs';
const MIN_LOADING_TIME = 300;
@Injectable({
providedIn: 'root'
})
export class LoadingService {
private readonly _loadingEvent = new EventEmitter();
private _loadingStarted: number;
get isLoading(): Observable<boolean> {
return this._loadingEvent;
}
start(): void {
this._loadingEvent.next(true);
this._loadingStarted = new Date().getTime();
}
stop(): void {
const timeDelta = new Date().getTime() - this._loadingStarted;
if (timeDelta < MIN_LOADING_TIME) {
setTimeout(() => {
this._loadingEvent.next(false);
}, MIN_LOADING_TIME - timeDelta);
return;
}
this._loadingEvent.next(false);
}
}

View File

@ -17,7 +17,7 @@ export type ScreenName =
providedIn: 'root'
})
export class SortingService {
private _options: { [key: string]: SortingOption } = {
private readonly _options: { [key: string]: SortingOption } = {
'dossier-listing': { column: 'dossier.dossierName', order: 'asc' },
'dossier-overview': { column: 'filename', order: 'asc' },
'dictionary-listing': { column: 'label', order: 'asc' },
@ -26,8 +26,6 @@ export class SortingService {
'file-attributes-listing': { column: 'label', order: 'asc' }
};
constructor() {}
toggleSort(screen: ScreenName, column: string) {
if (this._options[screen].column === column) {
const currentOrder = this._options[screen].order;