diff --git a/apps/red-ui/src/app/components/toast/toast.component.html b/apps/red-ui/src/app/components/toast/toast.component.html index 45f3d662b..cfffe69b4 100644 --- a/apps/red-ui/src/app/components/toast/toast.component.html +++ b/apps/red-ui/src/app/components/toast/toast.component.html @@ -2,24 +2,12 @@
{{ title }}
-
-
+
+
{{ message }}
-
+
{{ action.title }} diff --git a/apps/red-ui/src/app/components/toast/toast.component.ts b/apps/red-ui/src/app/components/toast/toast.component.ts index 9b4e233d8..e61e2b967 100644 --- a/apps/red-ui/src/app/components/toast/toast.component.ts +++ b/apps/red-ui/src/app/components/toast/toast.component.ts @@ -1,26 +1,21 @@ import { Component } from '@angular/core'; import { Toast, ToastPackage, ToastrService } from 'ngx-toastr'; +import { ToasterOptions } from '@services/toaster.service'; @Component({ - selector: 'redaction-toast', templateUrl: './toast.component.html', styleUrls: ['./toast.component.scss'] }) export class ToastComponent extends Toast { - constructor( - protected readonly _toastrService: ToastrService, - readonly toastPackage: ToastPackage - ) { + constructor(protected readonly _toastrService: ToastrService, readonly toastPackage: ToastPackage) { super(_toastrService, toastPackage); } get actions() { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - return this.options.actions; + return (this.options as ToasterOptions)?.actions; } - callAction($event: MouseEvent, action: Function) { + callAction($event: MouseEvent, action: () => void) { $event.stopPropagation(); if (action) { action(); diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts index f77311212..eb80f32d9 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dictionary-dialog/add-edit-dictionary-dialog.component.ts @@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { DictionaryControllerService, TypeValue } from '@redaction/red-ui-http'; import { Observable } from 'rxjs'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '@services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { TypeValueWrapper } from '@models/file/type-value.wrapper'; import { humanize } from '../../../../utils/functions'; @@ -21,7 +21,7 @@ export class AddEditDictionaryDialogComponent { constructor( private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _formBuilder: FormBuilder, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _translateService: TranslateService, private readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) @@ -89,20 +89,16 @@ export class AddEditDictionaryDialogComponent { () => this._dialogRef.close(true), error => { if (error.status === 409) { - this._notifyError('add-edit-dictionary.error.dictionary-already-exists'); + this._toaster.error('add-edit-dictionary.error.dictionary-already-exists'); } else if (error.status === 400) { - this._notifyError('add-edit-dictionary.error.invalid-color-or-rank'); + this._toaster.error('add-edit-dictionary.error.invalid-color-or-rank'); } else { - this._notifyError('add-edit-dictionary.error.generic'); + this._toaster.error('add-edit-dictionary.error.generic'); } } ); } - private _notifyError(message: string) { - this._notificationService.showToastNotification(this._translateService.instant(message), null, NotificationType.ERROR); - } - private _formToObject(): TypeValue { return { caseInsensitive: !this.dictionaryForm.get('caseSensitive').value, diff --git a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.ts index 498af6fba..62094361f 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/add-edit-dossier-attribute-dialog/add-edit-dossier-attribute-dialog.component.ts @@ -1,12 +1,12 @@ -import { Component, Inject } from '@angular/core'; +import { Component, Inject, OnDestroy } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { DossierAttributeConfig, FileAttributeConfig } from '@redaction/red-ui-http'; import { AppStateService } from '@state/app-state.service'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { LoadingService } from '@services/loading.service'; -import { ErrorMessageService } from '@services/error-message.service'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { LoadingService } from '../../../../services/loading.service'; import { HttpErrorResponse } from '@angular/common/http'; +import { Toaster } from '../../../../services/toaster.service'; +import { AutoUnsubscribeComponent } from '../../../shared/base/auto-unsubscribe.component'; import { DossierAttributesService } from '@shared/services/controller-wrappers/dossier-attributes.service'; @Component({ @@ -14,7 +14,7 @@ import { DossierAttributesService } from '@shared/services/controller-wrappers/d templateUrl: './add-edit-dossier-attribute-dialog.component.html', styleUrls: ['./add-edit-dossier-attribute-dialog.component.scss'] }) -export class AddEditDossierAttributeDialogComponent { +export class AddEditDossierAttributeDialogComponent extends AutoUnsubscribeComponent implements OnDestroy { dossierAttributeForm: FormGroup; dossierAttribute: DossierAttributeConfig; dossierTemplateId: string; @@ -30,12 +30,12 @@ export class AddEditDossierAttributeDialogComponent { private readonly _formBuilder: FormBuilder, private readonly _loadingService: LoadingService, private readonly _dossierAttributesService: DossierAttributesService, - private readonly _errorMessageService: ErrorMessageService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public data: { dossierAttribute: DossierAttributeConfig; dossierTemplateId: string } ) { + super(); this.dossierAttribute = data.dossierAttribute; this.dossierTemplateId = data.dossierTemplateId; @@ -76,14 +76,10 @@ export class AddEditDossierAttributeDialogComponent { () => { this.dialogRef.close(true); }, - (err: HttpErrorResponse) => { - this._loadingService.stop(); - this._notificationService.showToastNotification( - this._errorMessageService.getMessage(err, 'add-edit-dossier-attribute.error.generic'), - null, - NotificationType.ERROR - ); - } - ); + (error: HttpErrorResponse) => { + this._loadingService.stop(); + this._toaster.error('add-edit-dossier-attribute.error.generic', { error: error }); + } + ); } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts index 37ce4623e..7eacbff89 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/edit-color-dialog/edit-color-dialog.component.ts @@ -1,7 +1,7 @@ import { Component, Inject } from '@angular/core'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { Colors, DictionaryControllerService } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; @@ -20,7 +20,7 @@ export class EditColorDialogComponent { constructor( private readonly _formBuilder: FormBuilder, private readonly _dictionaryControllerService: DictionaryControllerService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _translateService: TranslateService, private readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) @@ -49,17 +49,10 @@ export class EditColorDialogComponent { try { await this._dictionaryControllerService.setColors(colors, this._dossierTemplateId).toPromise(); this._dialogRef.close(true); - this._notificationService.showToastNotification( - this._translateService.instant('edit-color-dialog.success', { - color: this._translateService.instant('default-colors-screen.types.' + this.colorKey) - }) - ); + const color = this._translateService.instant(`default-colors-screen.types.${this.colorKey}`); + this._toaster.info('edit-color-dialog.success', { params: { color: color } }); } catch (e) { - this._notificationService.showToastNotification( - this._translateService.instant('edit-color-dialog.error'), - null, - NotificationType.ERROR - ); + this._toaster.error('edit-color-dialog.error'); } } } diff --git a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts index b09b644ae..d933b71e8 100644 --- a/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts +++ b/apps/red-ui/src/app/modules/admin/dialogs/file-attributes-csv-import-dialog/file-attributes-csv-import-dialog.component.ts @@ -6,7 +6,7 @@ import * as Papa from 'papaparse'; import { FileAttributeConfig, FileAttributesConfig, FileAttributesControllerService } from '@redaction/red-ui-http'; import { Observable } from 'rxjs'; import { map, startWith } from 'rxjs/operators'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { FilterService } from '@shared/services/filter.service'; import { SearchService } from '@shared/services/search.service'; @@ -48,7 +48,7 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent private readonly _appStateService: AppStateService, private readonly _fileAttributesControllerService: FileAttributesControllerService, private readonly _translateService: TranslateService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _formBuilder: FormBuilder, public dialogRef: MatDialogRef, protected readonly _injector: Injector, @@ -202,19 +202,9 @@ export class FileAttributesCsvImportDialogComponent extends BaseListingComponent try { await this._fileAttributesControllerService.setFileAttributesConfig(fileAttributes, this.dossierTemplateId).toPromise(); - this._notificationService.showToastNotification( - this._translateService.instant('file-attributes-csv-import.save.success', { - count: this.activeFields.length - }), - null, - NotificationType.SUCCESS - ); + this._toaster.success('file-attributes-csv-import.save.success', { params: { count: this.activeFields.length } }); } catch (e) { - this._notificationService.showToastNotification( - this._translateService.instant('file-attributes-csv-import.save.error'), - null, - NotificationType.ERROR - ); + this._toaster.error('file-attributes-csv-import.save.error'); } this.dialogRef.close(true); diff --git a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.html b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.html index 18b923648..36b224fac 100644 --- a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.html +++ b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.html @@ -22,19 +22,8 @@
-
- + + -
- +
+ @@ -67,9 +48,7 @@ > @@ -78,9 +57,7 @@
@@ -89,9 +66,7 @@
@@ -100,9 +75,7 @@
@@ -139,6 +112,4 @@
- + diff --git a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts index 0ebbfb000..a5ac0a556 100644 --- a/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/digital-signature/digital-signature-screen.component.ts @@ -1,17 +1,17 @@ -import { Component } from '@angular/core'; +import { Component, OnDestroy } from '@angular/core'; import { DigitalSignature, DigitalSignatureControllerService } from '@redaction/red-ui-http'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; -import { NotificationService, NotificationType } from '@services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '../../../../services/toaster.service'; import { PermissionsService } from '@services/permissions.service'; import { lastIndexOfEnd } from '@utils/functions'; +import { AutoUnsubscribeComponent } from '../../../shared/base/auto-unsubscribe.component'; @Component({ selector: 'redaction-digital-signature-screen', templateUrl: './digital-signature-screen.component.html', styleUrls: ['./digital-signature-screen.component.scss'] }) -export class DigitalSignatureScreenComponent { +export class DigitalSignatureScreenComponent extends AutoUnsubscribeComponent implements OnDestroy { digitalSignature: DigitalSignature; digitalSignatureForm: FormGroup; @@ -20,11 +20,11 @@ export class DigitalSignatureScreenComponent { constructor( private readonly _digitalSignatureControllerService: DigitalSignatureControllerService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _formBuilder: FormBuilder, - private readonly _translateService: TranslateService, readonly permissionsService: PermissionsService ) { + super(); this.loadDigitalSignatureAndInitializeForm(); } @@ -43,50 +43,28 @@ export class DigitalSignatureScreenComponent { ? this._digitalSignatureControllerService.updateDigitalSignature(digitalSignature) : this._digitalSignatureControllerService.saveDigitalSignature(digitalSignature); - observable.subscribe( + this.addSubscription = observable.subscribe( () => { this.loadDigitalSignatureAndInitializeForm(); - this._notificationService.showToastNotification( - this._translateService.instant('digital-signature-screen.action.save-success'), - null, - NotificationType.SUCCESS - ); + this._toaster.success('digital-signature-screen.action.save-success'); }, error => { if (error.status === 400) { - this._notificationService.showToastNotification( - this._translateService.instant('digital-signature-screen.action.certificate-not-valid-error'), - null, - NotificationType.ERROR - ); + this._toaster.error('digital-signature-screen.action.certificate-not-valid-error'); } else { - this._notificationService.showToastNotification( - this._translateService.instant('digital-signature-screen.action.save-error'), - null, - NotificationType.ERROR - ); + this._toaster.error('digital-signature-screen.action.save-error'); } } ); } removeDigitalSignature() { - this._digitalSignatureControllerService.deleteDigitalSignature().subscribe( + this.addSubscription = this._digitalSignatureControllerService.deleteDigitalSignature().subscribe( () => { this.loadDigitalSignatureAndInitializeForm(); - this._notificationService.showToastNotification( - this._translateService.instant('digital-signature-screen.action.delete-success'), - null, - NotificationType.SUCCESS - ); + this._toaster.success('digital-signature-screen.action.delete-success'); }, - () => { - this._notificationService.showToastNotification( - this._translateService.instant('digital-signature-screen.action.delete-error'), - null, - NotificationType.ERROR - ); - } + () => this._toaster.error('digital-signature-screen.action.delete-error') ); } @@ -105,7 +83,7 @@ export class DigitalSignatureScreenComponent { loadDigitalSignatureAndInitializeForm() { this.viewReady = false; - this._digitalSignatureControllerService + this.addSubscription = this._digitalSignatureControllerService .getDigitalSignature() .subscribe( digitalSignature => { @@ -123,8 +101,6 @@ export class DigitalSignatureScreenComponent { }); } - formChanged() {} - private _initForm() { this.digitalSignatureForm = this._formBuilder.group({ certificateName: [this.digitalSignature.certificateName, Validators.required], diff --git a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts index 8f266eebd..d8a6552a7 100644 --- a/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/rules/rules-screen.component.ts @@ -1,7 +1,7 @@ -import { Component, ElementRef, ViewChild } from '@angular/core'; +import { Component, ElementRef, OnInit, ViewChild } from '@angular/core'; import { PermissionsService } from '@services/permissions.service'; import { RulesControllerService } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { saveAs } from 'file-saver'; import { ComponentHasChanges } from '@guards/can-deactivate.guard'; @@ -17,7 +17,7 @@ import IStandaloneEditorConstructionOptions = monaco.editor.IStandaloneEditorCon templateUrl: './rules-screen.component.html', styleUrls: ['./rules-screen.component.scss'] }) -export class RulesScreenComponent extends ComponentHasChanges { +export class RulesScreenComponent extends ComponentHasChanges implements OnInit { editorOptions: IStandaloneEditorConstructionOptions = { theme: 'vs', language: 'java', @@ -39,13 +39,16 @@ export class RulesScreenComponent extends ComponentHasChanges { readonly permissionsService: PermissionsService, private readonly _rulesControllerService: RulesControllerService, private readonly _appStateService: AppStateService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, protected readonly _translateService: TranslateService, private readonly _activatedRoute: ActivatedRoute ) { super(_translateService); - this._appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId); - this._initialize(); + _appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId); + } + + async ngOnInit() { + await this._initialize(); } get hasChanges(): boolean { @@ -83,27 +86,20 @@ export class RulesScreenComponent extends ComponentHasChanges { async save(): Promise { this.processing = true; - this._rulesControllerService + await this._rulesControllerService .uploadRules({ rules: this._codeEditor.getModel().getValue(), dossierTemplateId: this._appStateService.activeDossierTemplateId }) - .subscribe( - () => { - this._initialize(); - this._notificationService.showToastNotification( - this._translateService.instant('rules-screen.success.generic'), - null, - NotificationType.SUCCESS - ); + .toPromise() + .then( + async () => { + await this._initialize(); + this._toaster.success('rules-screen.success.generic'); }, () => { this.processing = false; - this._notificationService.showToastNotification( - this._translateService.instant('rules-screen.error.generic'), - null, - NotificationType.ERROR - ); + this._toaster.error('rules-screen.error.generic'); } ); } @@ -148,13 +144,16 @@ export class RulesScreenComponent extends ComponentHasChanges { } as IModelDeltaDecoration; } - private _initialize() { - this._rulesControllerService.downloadRules(this._appStateService.activeDossierTemplateId).subscribe( - rules => { - this.currentLines = this.initialLines = rules.rules.split('\n'); - this.revert(); - }, - () => (this.processing = false) - ); + private async _initialize() { + await this._rulesControllerService + .downloadRules(this._appStateService.activeDossierTemplateId) + .toPromise() + .then( + rules => { + this.currentLines = this.initialLines = rules.rules.split('\n'); + this.revert(); + }, + () => (this.processing = false) + ); } } diff --git a/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts index b36206694..33cef2580 100644 --- a/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/smtp-config/smtp-config-screen.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, OnDestroy, OnInit } from '@angular/core'; import { PermissionsService } from '@services/permissions.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AdminDialogService } from '../../services/admin-dialog.service'; @@ -8,15 +8,15 @@ import { SmtpConfigurationControllerService, SMTPConfigurationModel } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '@services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '../../../../services/toaster.service'; +import { AutoUnsubscribeComponent } from '../../../shared/base/auto-unsubscribe.component'; @Component({ selector: 'redaction-smtp-config-screen', templateUrl: './smtp-config-screen.component.html', styleUrls: ['./smtp-config-screen.component.scss'] }) -export class SmtpConfigScreenComponent implements OnInit { +export class SmtpConfigScreenComponent extends AutoUnsubscribeComponent implements OnInit, OnDestroy { viewReady = false; configForm: FormGroup; generalSettings: GeneralConfigurationModel = { @@ -31,11 +31,11 @@ export class SmtpConfigScreenComponent implements OnInit { private readonly _smtpConfigService: SmtpConfigurationControllerService, private readonly _formBuilder: FormBuilder, private readonly _dialogService: AdminDialogService, - private readonly _notificationService: NotificationService, - private readonly _translateService: TranslateService, + private readonly _toaster: Toaster, private readonly _generalSettingsControllerService: GeneralSettingsControllerService ) { - this.configForm = this._formBuilder.group({ + super(); + this.configForm = _formBuilder.group({ host: [undefined, Validators.required], port: [25], from: [undefined, [Validators.required, Validators.email]], @@ -50,7 +50,7 @@ export class SmtpConfigScreenComponent implements OnInit { password: [undefined] }); - this.configForm.controls.auth.valueChanges.subscribe(auth => { + this.addSubscription = this.configForm.controls.auth.valueChanges.subscribe(auth => { if (auth) { this.openAuthConfigDialog(); } @@ -110,17 +110,9 @@ export class SmtpConfigScreenComponent implements OnInit { this.viewReady = false; try { await this._smtpConfigService.testSMTPConfiguration(this.configForm.getRawValue()).toPromise(); - this._notificationService.showToastNotification( - this._translateService.instant('smtp-config-screen.test.success'), - undefined, - NotificationType.SUCCESS - ); + this._toaster.success('smtp-config-screen.test.success'); } catch (e) { - this._notificationService.showToastNotification( - this._translateService.instant('smtp-config-screen.test.error'), - undefined, - NotificationType.ERROR - ); + this._toaster.error('smtp-config-screen.test.error'); } finally { this.viewReady = true; } diff --git a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts index 3bc64a4da..627166a6e 100644 --- a/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts +++ b/apps/red-ui/src/app/modules/admin/screens/watermark/watermark-screen.component.ts @@ -7,8 +7,7 @@ import { HttpClient } from '@angular/common/http'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { debounce } from '@utils/debounce'; import { WatermarkControllerService, WatermarkModelRes } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '@services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '../../../../services/toaster.service'; import { ActivatedRoute } from '@angular/router'; import { BASE_HREF } from '../../../../tokens'; import { stampPDFPage } from '../../../../utils/page-stamper'; @@ -39,15 +38,14 @@ export class WatermarkScreenComponent implements OnInit { readonly permissionsService: PermissionsService, readonly appStateService: AppStateService, @Inject(BASE_HREF) private readonly _baseHref: string, - private readonly _translateService: TranslateService, private readonly _watermarkControllerService: WatermarkControllerService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _http: HttpClient, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _formBuilder: FormBuilder, private readonly _activatedRoute: ActivatedRoute ) { - this.appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId); + appStateService.activateDossierTemplate(_activatedRoute.snapshot.params.dossierTemplateId); this._initForm(); } @@ -81,24 +79,12 @@ export class WatermarkScreenComponent implements OnInit { ? this._watermarkControllerService.saveWatermark(watermark, this.appStateService.activeDossierTemplateId) : this._watermarkControllerService.deleteWatermark(this.appStateService.activeDossierTemplateId); - observable.subscribe( + observable.toPromise().then( () => { this._loadWatermark(); - this._notificationService.showToastNotification( - this._translateService.instant( - watermark.text ? 'watermark-screen.action.change-success' : 'watermark-screen.action.delete-success' - ), - null, - NotificationType.SUCCESS - ); + this._toaster.success(watermark.text ? 'watermark-screen.action.change-success' : 'watermark-screen.action.delete-success'); }, - () => { - this._notificationService.showToastNotification( - this._translateService.instant('watermark-screen.action.error'), - null, - NotificationType.ERROR - ); - } + () => this._toaster.error('watermark-screen.action.error') ); } @@ -188,11 +174,41 @@ export class WatermarkScreenComponent implements OnInit { private _initForm() { this.configForm = this._formBuilder.group({ text: [{ value: null, disabled: !this.permissionsService.isAdmin() }], - hexColor: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required], - opacity: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required], - fontSize: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required], - fontType: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required], - orientation: [{ value: null, disabled: !this.permissionsService.isAdmin() }, Validators.required] + hexColor: [ + { + value: null, + disabled: !this.permissionsService.isAdmin() + }, + Validators.required + ], + opacity: [ + { + value: null, + disabled: !this.permissionsService.isAdmin() + }, + Validators.required + ], + fontSize: [ + { + value: null, + disabled: !this.permissionsService.isAdmin() + }, + Validators.required + ], + fontType: [ + { + value: null, + disabled: !this.permissionsService.isAdmin() + }, + Validators.required + ], + orientation: [ + { + value: null, + disabled: !this.permissionsService.isAdmin() + }, + Validators.required + ] }); } } diff --git a/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts b/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts index 39de289e9..f212e5115 100644 --- a/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/dossier-details/dossier-details.component.ts @@ -7,9 +7,9 @@ import { TranslateChartService } from '@services/translate-chart.service'; import { StatusSorter } from '@utils/sorters/status-sorter'; import { UserService } from '@services/user.service'; import { User } from '@redaction/red-ui-http'; -import { NotificationService } from '@services/notification.service'; -import { FilterService } from '@shared/services/filter.service'; -import { FileStatusWrapper } from '@models/file/file-status.wrapper'; +import { Toaster } from '../../../../services/toaster.service'; +import { FilterService } from '../../../shared/services/filter.service'; +import { FileStatusWrapper } from '../../../../models/file/file-status.wrapper'; import { DossierAttributeWithValue } from '@models/dossier-attributes.model'; @Component({ @@ -34,7 +34,7 @@ export class DossierDetailsComponent implements OnInit { readonly filterService: FilterService, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _userService: UserService, - private readonly _notificationService: NotificationService + private readonly _toaster: Toaster ) {} get memberIds(): string[] { @@ -86,6 +86,6 @@ export class DossierDetailsComponent implements OnInit { const ownerName = this._userService.getNameForId(this.owner.userId); const dossierName = this.appStateService.activeDossier.name; const msg = 'Successfully assigned ' + ownerName + ' to dossier: ' + dossierName; - this._notificationService.showToastNotification(msg); + this._toaster.info(msg); } } diff --git a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts index 7245e5ccc..0dd8eb2bb 100644 --- a/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/page-exclusion/page-exclusion.component.ts @@ -2,9 +2,9 @@ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from import { PermissionsService } from '@services/permissions.service'; import { FormBuilder, FormGroup } from '@angular/forms'; import { PageRange, ReanalysisControllerService } from '@redaction/red-ui-http'; -import { FileDataModel } from '@models/file/file-data.model'; -import { NotificationService, NotificationType } from '@services/notification.service'; -import { LoadingService } from '@services/loading.service'; +import { FileDataModel } from '../../../../models/file/file-data.model'; +import { Toaster } from '../../../../services/toaster.service'; +import { LoadingService } from '../../../../services/loading.service'; import { TranslateService } from '@ngx-translate/core'; @Component({ @@ -23,9 +23,8 @@ export class PageExclusionComponent implements OnChanges { readonly permissionsService: PermissionsService, private readonly _formBuilder: FormBuilder, private readonly _reanalysisControllerService: ReanalysisControllerService, - private readonly _notificationService: NotificationService, - private readonly _loadingService: LoadingService, - private readonly _translateService: TranslateService + private readonly _toaster: Toaster, + private readonly _loadingService: LoadingService ) { this.excludePagesForm = this._formBuilder.group({ value: [''] @@ -80,11 +79,7 @@ export class PageExclusionComponent implements OnChanges { this.excludePagesForm.reset(); this.actionPerformed.emit('exclude-pages'); } catch (e) { - this._notificationService.showToastNotification( - this._translateService.instant('file-preview.tabs.exclude-pages.error'), - null, - NotificationType.ERROR - ); + this._toaster.error('file-preview.tabs.exclude-pages.error'); this._loadingService.stop(); } } diff --git a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts index 85e32b0d4..665fa19c8 100644 --- a/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/team-members-manager/team-members-manager.component.ts @@ -1,8 +1,8 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Dossier, DossierControllerService, StatusControllerService } from '@redaction/red-ui-http'; +import { Dossier } from '@redaction/red-ui-http'; import { AppStateService } from '@state/app-state.service'; import { UserService } from '@services/user.service'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { DossierWrapper } from '@state/model/dossier.wrapper'; @@ -21,10 +21,8 @@ export class TeamMembersManagerComponent implements OnInit { constructor( readonly userService: UserService, - private readonly _dossierControllerService: DossierControllerService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _formBuilder: FormBuilder, - private readonly _statusControllerService: StatusControllerService, private readonly _appStateService: AppStateService ) {} @@ -91,11 +89,7 @@ export class TeamMembersManagerComponent implements OnInit { result = await this._appStateService.createOrUpdateDossier(dw.dossier); this.save.emit(result); } catch (error) { - this._notificationService.showToastNotification( - 'Failed: ' + error.error ? error.error.message : error, - null, - NotificationType.ERROR - ); + this._toaster.error('Failed: ' + error.error ? error.error.message : error); } } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts index 0c7390409..b82a86f1d 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/assign-reviewer-approver-dialog/assign-reviewer-approver-dialog.component.ts @@ -1,9 +1,9 @@ import { Component, Inject } from '@angular/core'; -import { DossierControllerService, StatusControllerService } from '@redaction/red-ui-http'; +import { StatusControllerService } from '@redaction/red-ui-http'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AppStateService } from '@state/app-state.service'; import { UserService } from '@services/user.service'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { DossierWrapper } from '@state/model/dossier.wrapper'; @@ -16,7 +16,6 @@ class DialogData { } @Component({ - selector: 'redaction-dossier-details-dialog', templateUrl: './assign-reviewer-approver-dialog.component.html', styleUrls: ['./assign-reviewer-approver-dialog.component.scss'] }) @@ -26,13 +25,12 @@ export class AssignReviewerApproverDialogComponent { constructor( readonly userService: UserService, - private readonly _dossierControllerService: DossierControllerService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _formBuilder: FormBuilder, private readonly _statusControllerService: StatusControllerService, private readonly _appStateService: AppStateService, - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: DialogData + private readonly _dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly data: DialogData ) { this._loadData(); } @@ -52,10 +50,8 @@ export class AssignReviewerApproverDialogComponent { return true; } - const reviewerId = this.selectedSingleUser; - for (const file of this.data.files) { - if (file.currentReviewer !== reviewerId) { + if (file.currentReviewer !== this.selectedSingleUser) { return true; } } @@ -94,14 +90,10 @@ export class AssignReviewerApproverDialogComponent { file.reviewerName = this.userService.getNameForId(selectedUser); } } catch (error) { - this._notificationService.showToastNotification( - 'Failed: ' + error.error ? error.error.message : error, - null, - NotificationType.ERROR - ); + this._toaster.error('Failed: ' + error.error ? error.error.message : error); } - this.dialogRef.close(); + this._dialogRef.close(); } private _loadData() { diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts index 99c5b12d9..7e08320d3 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/edit-dossier-dialog.component.ts @@ -1,13 +1,10 @@ import { ChangeDetectorRef, Component, Inject, ViewChild } from '@angular/core'; -import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; -import { FormBuilder } from '@angular/forms'; -import { AppStateService } from '@state/app-state.service'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; import { DossierWrapper } from '../../../../state/model/dossier.wrapper'; import { EditDossierGeneralInfoComponent } from './general-info/edit-dossier-general-info.component'; import { EditDossierDownloadPackageComponent } from './download-package/edit-dossier-download-package.component'; import { EditDossierSectionInterface } from './edit-dossier-section.interface'; -import { NotificationService, NotificationType } from '@services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '../../../../services/toaster.service'; import { EditDossierDictionaryComponent } from './dictionary/edit-dossier-dictionary.component'; import { EditDossierTeamMembersComponent } from './team-members/edit-dossier-team-members.component'; import { EditDossierAttributesComponent } from './attributes/edit-dossier-attributes.component'; @@ -38,12 +35,8 @@ export class EditDossierDialogComponent { @ViewChild(EditDossierAttributesComponent) attributesComponent: EditDossierAttributesComponent; constructor( - private readonly _appStateService: AppStateService, - private readonly _formBuilder: FormBuilder, - private readonly _notificationService: NotificationService, - private readonly _translateService: TranslateService, + private readonly _toaster: Toaster, private readonly _changeRef: ChangeDetectorRef, - private readonly _dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) private readonly _data: { dossierWrapper: DossierWrapper; @@ -82,11 +75,7 @@ export class EditDossierDialogComponent { } updatedDossier(updatedDossier: DossierWrapper) { - this._notificationService.showToastNotification( - this._translateService.instant('edit-dossier-dialog.change-successful'), - null, - NotificationType.SUCCESS - ); + this._toaster.success('edit-dossier-dialog.change-successful'); if (updatedDossier) { this.dossierWrapper = updatedDossier; @@ -111,11 +100,7 @@ export class EditDossierDialogComponent { changeTab(key: Section) { if (this.activeComponent.changed) { - this._notificationService.showToastNotification( - this._translateService.instant('edit-dossier-dialog.unsaved-changes'), - null, - NotificationType.ERROR - ); + this._toaster.error('edit-dossier-dialog.unsaved-changes'); return; } this.activeNav = key; diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts index 3fd26d42d..dc52079f7 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/edit-dossier-dialog/general-info/edit-dossier-general-info.component.ts @@ -10,8 +10,7 @@ import { PermissionsService } from '@services/permissions.service'; import { Router } from '@angular/router'; import { MatDialogRef } from '@angular/material/dialog'; import { EditDossierDialogComponent } from '../edit-dossier-dialog.component'; -import { NotificationService, NotificationType } from '../../../../../services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '../../../../../services/toaster.service'; @Component({ selector: 'redaction-edit-dossier-general-info', @@ -34,8 +33,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti private readonly _dialogService: DossiersDialogService, private readonly _router: Router, private readonly _editDossierDialogRef: MatDialogRef, - private readonly _notificationService: NotificationService, - private readonly _translateService: TranslateService + private readonly _toaster: Toaster ) {} get changed() { @@ -113,11 +111,7 @@ export class EditDossierGeneralInfoComponent implements OnInit, EditDossierSecti } private _notifyDossierDeleted() { - this._notificationService.showToastNotification( - this._translateService.instant('edit-dossier-dialog.delete-successful'), - null, - NotificationType.SUCCESS - ); + this._toaster.success('edit-dossier-dialog.delete-successful'); } private _filterInvalidDossierTemplates() { diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts index 8b905a9a1..c3de99226 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/force-redaction-dialog/force-redaction-dialog.component.ts @@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AppStateService } from '@state/app-state.service'; import { MatDialogRef } from '@angular/material/dialog'; import { ForceRedactionRequest, LegalBasisMappingControllerService } from '@redaction/red-ui-http'; -import { NotificationService } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { UserService } from '@services/user.service'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; @@ -29,7 +29,7 @@ export class ForceRedactionDialogComponent implements OnInit { private readonly _appStateService: AppStateService, private readonly _userService: UserService, private readonly _formBuilder: FormBuilder, - private readonly _notificationService: NotificationService, + private readonly _notificationService: Toaster, private readonly _translateService: TranslateService, private readonly _legalBasisMappingControllerService: LegalBasisMappingControllerService, private readonly _manualAnnotationService: ManualAnnotationService, diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts index 85fe5c5e1..b5c5d24df 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts @@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { AppStateService } from '@state/app-state.service'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { AddRedactionRequest, LegalBasisMappingControllerService } from '@redaction/red-ui-http'; -import { NotificationService } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { UserService } from '@services/user.service'; import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; @@ -37,7 +37,7 @@ export class ManualAnnotationDialogComponent implements OnInit { private readonly _appStateService: AppStateService, private readonly _userService: UserService, private readonly _formBuilder: FormBuilder, - private readonly _notificationService: NotificationService, + private readonly _notificationService: Toaster, private readonly _translateService: TranslateService, private readonly _legalBasisMappingControllerService: LegalBasisMappingControllerService, private readonly _manualAnnotationService: ManualAnnotationService, diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.ts index 88c1161d3..33068bc48 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossier-overview-screen/dossier-overview-screen.component.ts @@ -1,6 +1,6 @@ import { ChangeDetectorRef, Component, ElementRef, HostListener, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { NavigationStart, Router } from '@angular/router'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { AppStateService } from '@state/app-state.service'; import { FileDropOverlayService } from '@upload-download/services/file-drop-overlay.service'; import { FileUploadModel } from '@upload-download/model/file-upload.model'; @@ -59,7 +59,7 @@ export class DossierOverviewScreenComponent constructor( readonly permissionsService: PermissionsService, private readonly _userService: UserService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _dialogService: DossiersDialogService, private readonly _fileUploadService: FileUploadService, private readonly _statusOverlayService: StatusOverlayService, @@ -149,19 +149,9 @@ export class DossierOverviewScreenComponent .reanalyzeDossier() .then(() => { this.reloadDossiers(); - this._notificationService.showToastNotification( - this._translateService.instant('dossier-overview.reanalyse-dossier.success'), - null, - NotificationType.SUCCESS - ); + this._toaster.success('dossier-overview.reanalyse-dossier.success'); }) - .catch(() => { - this._notificationService.showToastNotification( - this._translateService.instant('dossier-overview.reanalyse-dossier.error'), - null, - NotificationType.ERROR - ); - }); + .catch(() => this._toaster.error('dossier-overview.reanalyse-dossier.error')); } reloadDossiers() { diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts index 15c433e57..bb0d1f886 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts @@ -12,8 +12,7 @@ import { AnnotationData, FileDataModel } from '@models/file/file-data.model'; import { FileActionService } from '../../services/file-action.service'; import { AnnotationDrawService } from '../../services/annotation-draw.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service'; -import { tap } from 'rxjs/operators'; -import { NotificationService } from '@services/notification.service'; +import { Toaster } from '../../../../services/toaster.service'; import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { PermissionsService } from '@services/permissions.service'; import { Subscription, timer } from 'rxjs'; @@ -37,6 +36,7 @@ import { handleFilterDelta, processFilters } from '@shared/components/filters/po import { LoadingService } from '../../../../services/loading.service'; import { stampPDFPage } from '../../../../utils/page-stamper'; import { TranslateService } from '@ngx-translate/core'; +import { AutoUnsubscribeComponent } from '../../../shared/base/auto-unsubscribe.component'; const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; @@ -45,7 +45,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; templateUrl: './file-preview-screen.component.html', styleUrls: ['./file-preview-screen.component.scss'] }) -export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, OnDetach { +export class FilePreviewScreenComponent extends AutoUnsubscribeComponent implements OnInit, OnDestroy, OnAttach, OnDetach { dialogRef: MatDialogRef; viewMode: ViewMode = 'STANDARD'; fullScreen = false; @@ -59,7 +59,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, secondaryFilters: FilterModel[]; canPerformAnnotationActions: boolean; filesAutoUpdateTimer: Subscription; - fileReanalysedSubscription: Subscription; hideSkipped = false; displayPDFViewer = false; viewDocumentInfo = false; @@ -81,7 +80,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, private readonly _activatedRoute: ActivatedRoute, private readonly _dialogService: DossiersDialogService, private readonly _router: Router, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _annotationProcessingService: AnnotationProcessingService, private readonly _annotationDrawService: AnnotationDrawService, private readonly _fileActionService: FileActionService, @@ -92,6 +91,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, private readonly _loadingService: LoadingService, private readonly _translateService: TranslateService ) { + super(); document.documentElement.addEventListener('fullscreenchange', () => { if (!document.fullscreenElement) { this.fullScreen = false; @@ -221,7 +221,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, ngOnDetach() { this.displayPDFViewer = false; this.viewReady = false; - this._unsubscribeFromFileUpdates(); + super.ngOnDestroy(); } async ngOnAttach(previousRoute: ActivatedRouteSnapshot) { @@ -249,10 +249,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, this.viewReady = true; } - ngOnDestroy(): void { - this._unsubscribeFromFileUpdates(); - } - rebuildFilters(deletePreviousAnnotations: boolean = false) { const startTime = new Date().getTime(); if (deletePreviousAnnotations) { @@ -469,7 +465,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, await this._statusControllerService.setFileReviewer(dossierId, fileId, reviewerId).toPromise(); const msg = `Successfully assigned ${reviewerName} to file: ${filename}`; - this._notificationService.showToastNotification(msg); + this._toaster.info(msg); await this.appStateService.reloadActiveFile(); this._updateCanPerformActions(); this.editingReviewer = false; @@ -488,7 +484,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, } downloadOriginalFile() { - this._fileManagementControllerService + this.addSubscription = this._fileManagementControllerService .downloadOriginalFile(this.dossierId, this.fileId, true, this.fileData.fileStatus.cacheIdentifier, 'response') .subscribe(data => { download(data, this.fileData.fileStatus.filename); @@ -539,10 +535,8 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, } private _subscribeToFileUpdates(): void { - this.filesAutoUpdateTimer = timer(0, 5000) - .pipe(tap(async () => await this.appStateService.reloadActiveFile())) - .subscribe(); - this.fileReanalysedSubscription = this.appStateService.fileReanalysed.subscribe(async (fileStatus: FileStatusWrapper) => { + this.addSubscription = timer(0, 5000).subscribe(async () => await this.appStateService.reloadActiveFile()); + this.addSubscription = this.appStateService.fileReanalysed.subscribe(async (fileStatus: FileStatusWrapper) => { if (fileStatus.fileId === this.fileId) { await this._loadFileData(!this._reloadFileOnReanalysis); this._reloadFileOnReanalysis = false; @@ -553,11 +547,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach, }); } - private _unsubscribeFromFileUpdates(): void { - this.filesAutoUpdateTimer.unsubscribe(); - this.fileReanalysedSubscription.unsubscribe(); - } - private _updateCanPerformActions() { this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions() && diff --git a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts index 055e3670d..102baff26 100644 --- a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts @@ -7,7 +7,7 @@ import { ManualRedactionControllerService } from '@redaction/red-ui-http'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../../../services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { tap } from 'rxjs/operators'; import { UserService } from '@services/user.service'; @@ -38,7 +38,7 @@ export class ManualAnnotationService { private readonly _appStateService: AppStateService, private readonly _userService: UserService, private readonly _translateService: TranslateService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _manualRedactionControllerService: ManualRedactionControllerService, private readonly _dictionaryControllerService: DictionaryControllerService, private readonly _permissionsService: PermissionsService @@ -76,8 +76,12 @@ export class ManualAnnotationService { return obs.pipe( tap( - () => this._notify(this._getMessage(mode)), - error => this._notify(this._getMessage(mode, modifyDictionary, true), NotificationType.ERROR, error) + () => this._toaster.success(this._getMessage(mode), { positionClass: 'toast-file-preview' }), + error => + this._toaster.error(this._getMessage(mode, modifyDictionary, true), { + params: error, + positionClass: 'toast-file-preview' + }) ) ); } @@ -223,13 +227,6 @@ export class ManualAnnotationService { } } - private _notify(key: string, type: NotificationType = NotificationType.SUCCESS, data?: any) { - this._notificationService.showToastNotification(this._translateService.instant(key, data), null, type, { - positionClass: 'toast-file-preview', - actions: [] - }); - } - private _getMessage(mode: Mode, modifyDictionary?: boolean, error: boolean = false) { return ( 'annotation-actions.message.' + diff --git a/apps/red-ui/src/app/modules/shared/base/auto-unsubscribe.component.ts b/apps/red-ui/src/app/modules/shared/base/auto-unsubscribe.component.ts index 680769e31..761726bf7 100644 --- a/apps/red-ui/src/app/modules/shared/base/auto-unsubscribe.component.ts +++ b/apps/red-ui/src/app/modules/shared/base/auto-unsubscribe.component.ts @@ -3,8 +3,6 @@ import { Subscription } from 'rxjs'; /** * Inherit this class when you need to subscribe to observables in your components - * - * @remarks You must explicitly call super.ngOnDestroy in you component to unsubscribe */ @Component({ template: '' }) export abstract class AutoUnsubscribeComponent implements OnDestroy { @@ -21,7 +19,8 @@ export abstract class AutoUnsubscribeComponent implements OnDestroy { /** * This method unsubscribes active subscriptions - * Explicitly call this method in your component's ngOnDestroy + * If you implement OnDestroy in a component that inherits AutoUnsubscribeComponent, + * then you must explicitly call super.ngOnDestroy() */ ngOnDestroy(): void { this._subscriptions.unsubscribe(); diff --git a/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts b/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts index 7ba2a27aa..201189b97 100644 --- a/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts +++ b/apps/red-ui/src/app/modules/shared/base/base-listing.component.ts @@ -5,7 +5,6 @@ import { FilterService } from '../services/filter.service'; import { SearchService } from '../services/search.service'; import { ScreenStateService } from '../services/screen-state.service'; import { Observable } from 'rxjs'; -import { FilterModel } from '../components/filters/popup-filter/model/filter.model'; import { AutoUnsubscribeComponent } from './auto-unsubscribe.component'; @Component({ template: '' }) diff --git a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts index 3233c7e4e..0ec7be010 100644 --- a/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts +++ b/apps/red-ui/src/app/modules/shared/components/buttons/file-download-btn/file-download-btn.component.ts @@ -1,11 +1,11 @@ -import { ChangeDetectionStrategy, Component, Inject, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Input, OnDestroy } from '@angular/core'; import { PermissionsService } from '@services/permissions.service'; import { DossierWrapper } from '@state/model/dossier.wrapper'; import { FileStatusWrapper } from '@models/file/file-status.wrapper'; import { FileDownloadService } from '@upload-download/services/file-download.service'; -import { NotificationService } from '@services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '@services/toaster.service'; import { BASE_HREF } from '../../../../../tokens'; +import { AutoUnsubscribeComponent } from '@shared/base/auto-unsubscribe.component'; export type MenuState = 'OPEN' | 'CLOSED'; @@ -15,7 +15,7 @@ export type MenuState = 'OPEN' | 'CLOSED'; styleUrls: ['./file-download-btn.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class FileDownloadBtnComponent { +export class FileDownloadBtnComponent extends AutoUnsubscribeComponent implements OnDestroy { @Input() dossier: DossierWrapper; @Input() file: FileStatusWrapper | FileStatusWrapper[]; @Input() tooltipPosition: 'above' | 'below' | 'before' | 'after' = 'above'; @@ -27,9 +27,10 @@ export class FileDownloadBtnComponent { @Inject(BASE_HREF) private readonly _baseHref: string, private readonly _permissionsService: PermissionsService, private readonly _fileDownloadService: FileDownloadService, - private readonly _translateService: TranslateService, - private readonly _notificationService: NotificationService - ) {} + private readonly _toaster: Toaster + ) { + super(); + } get canDownloadFiles() { if (!Array.isArray(this.file)) { @@ -41,12 +42,10 @@ export class FileDownloadBtnComponent { downloadFiles($event: MouseEvent) { $event.stopPropagation(); - this._fileDownloadService.downloadFiles(Array.isArray(this.file) ? this.file : [this.file], this.dossier).subscribe(() => { - this._notificationService.showToastNotification( - this._translateService.instant('download-status.queued', { - baseUrl: this._baseHref - }) - ); - }); + this.addSubscription = this._fileDownloadService + .downloadFiles(Array.isArray(this.file) ? this.file : [this.file], this.dossier) + .subscribe(() => { + this._toaster.info('download-status.queued', { params: { baseUrl: this._baseHref } }); + }); } } diff --git a/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts b/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts index 4f6886952..5bce425e3 100644 --- a/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts +++ b/apps/red-ui/src/app/modules/shared/services/dictionary-save.service.ts @@ -1,7 +1,6 @@ import { Injectable } from '@angular/core'; import { Observable, throwError } from 'rxjs'; -import { NotificationService, NotificationType } from '@services/notification.service'; -import { TranslateService } from '@ngx-translate/core'; +import { Toaster } from '../../../services/toaster.service'; import { DictionaryControllerService } from '@redaction/red-ui-http'; import { tap } from 'rxjs/operators'; @@ -11,11 +10,7 @@ const MIN_WORD_LENGTH = 2; providedIn: 'root' }) export class DictionarySaveService { - constructor( - private readonly _notificationService: NotificationService, - private readonly _translateService: TranslateService, - private readonly _dictionaryControllerService: DictionaryControllerService - ) {} + constructor(private readonly _toaster: Toaster, private readonly _dictionaryControllerService: DictionaryControllerService) {} saveEntries( entries: string[], @@ -44,29 +39,13 @@ export class DictionarySaveService { return obs.pipe( tap( () => { - if (showToast) { - this._notificationService.showToastNotification( - this._translateService.instant('dictionary-overview.success.generic'), - null, - NotificationType.SUCCESS - ); - } + if (showToast) this._toaster.success('dictionary-overview.success.generic'); }, - () => { - this._notificationService.showToastNotification( - this._translateService.instant('dictionary-overview.error.generic'), - null, - NotificationType.ERROR - ); - } + () => this._toaster.error('dictionary-overview.error.generic') ) ); } else { - this._notificationService.showToastNotification( - this._translateService.instant('dictionary-overview.error.entries-too-short'), - null, - NotificationType.ERROR - ); + this._toaster.error('dictionary-overview.error.entries-too-short'); return throwError('Entries too short'); } diff --git a/apps/red-ui/src/app/services/error-message.service.ts b/apps/red-ui/src/app/services/error-message.service.ts index d8d311fb1..68fd90874 100644 --- a/apps/red-ui/src/app/services/error-message.service.ts +++ b/apps/red-ui/src/app/services/error-message.service.ts @@ -8,11 +8,11 @@ import { HttpErrorResponse } from '@angular/common/http'; export class ErrorMessageService { constructor(private readonly _translateService: TranslateService) {} - _parseErrorResponse(err: HttpErrorResponse) { + _parseErrorResponse(err: HttpErrorResponse): string { return err?.error?.message?.includes('message') ? ` ${err.error.message.match('"message":"(.*?)\\"')[1]}` : ''; } - getMessage(err: HttpErrorResponse, defaultMessage: string) { - return this._translateService.instant(defaultMessage) + this._parseErrorResponse(err); + getMessage(error: HttpErrorResponse, defaultMessage: string): string { + return this._translateService.instant(defaultMessage) + this._parseErrorResponse(error); } } diff --git a/apps/red-ui/src/app/services/notification.service.ts b/apps/red-ui/src/app/services/notification.service.ts deleted file mode 100644 index 9431503f7..000000000 --- a/apps/red-ui/src/app/services/notification.service.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ActiveToast, ToastrService } from 'ngx-toastr'; -import { IndividualConfig } from 'ngx-toastr/toastr/toastr-config'; -import { NavigationStart, Router } from '@angular/router'; - -export enum NotificationType { - SUCCESS = 'SUCCESS', - WARNING = 'WARNING', - ERROR = 'ERROR', - INFO = 'INFO' -} - -export class ToastAction { - title: string; - action?: () => any; -} - -@Injectable({ - providedIn: 'root' -}) -export class NotificationService { - constructor(private readonly _toastr: ToastrService, private readonly _router: Router) { - _router.events.subscribe(event => { - if (event instanceof NavigationStart) { - this._toastr.clear(); - } - }); - } - - showToastNotification( - message: string, - title?: string, - notificationType = NotificationType.INFO, - options?: Partial & { actions?: ToastAction[] } - ): ActiveToast { - switch (notificationType) { - case NotificationType.ERROR: - return this._toastr.error(message, title, options); - case NotificationType.SUCCESS: - return this._toastr.success(message, title, options); - case NotificationType.WARNING: - return this._toastr.warning(message, title, options); - case NotificationType.INFO: - return this._toastr.info(message, title, options); - } - } -} diff --git a/apps/red-ui/src/app/services/toaster.service.ts b/apps/red-ui/src/app/services/toaster.service.ts new file mode 100644 index 000000000..c24fe23d0 --- /dev/null +++ b/apps/red-ui/src/app/services/toaster.service.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@angular/core'; +import { ActiveToast, ToastrService } from 'ngx-toastr'; +import { IndividualConfig } from 'ngx-toastr/toastr/toastr-config'; +import { NavigationStart, Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import { ErrorMessageService } from '@services/error-message.service'; +import { filter } from 'rxjs/operators'; + +export enum NotificationType { + SUCCESS = 'SUCCESS', + WARNING = 'WARNING', + INFO = 'INFO' +} + +export interface ToasterOptions extends IndividualConfig { + title?: string; + /** + * These params are used as interpolateParams for translate service + */ + params?: object; + actions?: { title?: string; action: () => void }[]; +} + +export interface ErrorToasterOptions extends ToasterOptions { + /** + * Pass an http error that will be processed by error message service and shown in toast + */ + error?: HttpErrorResponse; +} + +@Injectable({ + providedIn: 'root' +}) +export class Toaster { + constructor( + private readonly _toastr: ToastrService, + private readonly _router: Router, + private readonly _translateService: TranslateService, + private readonly _errorMessageService: ErrorMessageService + ) { + _router.events.pipe(filter(event => event instanceof NavigationStart)).subscribe(() => { + _toastr.clear(); + }); + } + + error(message: string, options?: Partial) { + if (options?.error) message = this._errorMessageService.getMessage(options.error, message); + else message = this._translateService.instant(message, options?.params); + + return this._toastr.error(message, options?.title, options); + } + + info(message: string, options?: Partial) { + return this._showToastNotification(message, NotificationType.INFO, options); + } + + success(message: string, options?: Partial) { + return this._showToastNotification(message, NotificationType.SUCCESS, options); + } + + warning(message: string, options?: Partial) { + return this._showToastNotification(message, NotificationType.WARNING, options); + } + + private _showToastNotification( + message: string, + notificationType = NotificationType.INFO, + options?: Partial + ): ActiveToast { + message = this._translateService.instant(message, options?.params); + + switch (notificationType) { + case NotificationType.SUCCESS: + return this._toastr.success(message, options?.title, options); + case NotificationType.WARNING: + return this._toastr.warning(message, options?.title, options); + case NotificationType.INFO: + return this._toastr.info(message, options?.title, options); + } + } +} diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts index 8acaa3a7b..ce9146e7d 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -9,7 +9,7 @@ import { ReanalysisControllerService, StatusControllerService } from '@redaction/red-ui-http'; -import { NotificationService, NotificationType } from '@services/notification.service'; +import { Toaster } from '../services/toaster.service'; import { TranslateService } from '@ngx-translate/core'; import { Event, NavigationEnd, ResolveStart, Router } from '@angular/router'; import { UserService } from '@services/user.service'; @@ -47,7 +47,7 @@ export class AppStateService { private readonly _router: Router, private readonly _userService: UserService, private readonly _dossiersService: DossiersService, - private readonly _notificationService: NotificationService, + private readonly _toaster: Toaster, private readonly _reanalysisControllerService: ReanalysisControllerService, private readonly _translateService: TranslateService, private readonly _dictionaryControllerService: DictionaryControllerService, @@ -239,7 +239,7 @@ export class AppStateService { this.activeDossierTemplateId, this._appState.fileAttributesConfig[this.activeDossierTemplateId] ); - this.activeDossier.files = this.activeDossier.files.map(file => + this.activeDossier.files = this.activeDossier?.files.map(file => file.fileId === activeFileWrapper.fileId ? activeFileWrapper : file ); @@ -330,13 +330,7 @@ export class AppStateService { const index = this.allDossiers.findIndex(p => p.dossierId === dossier.dossierId); this._appState.dossiers.splice(index, 1); }, - () => { - this._notificationService.showToastNotification( - this._translateService.instant('dossiers.delete.delete-failed', dossier), - null, - NotificationType.ERROR - ); - } + () => this._toaster.error('dossiers.delete.delete-failed', { params: dossier }) ); } @@ -353,12 +347,8 @@ export class AppStateService { this._appState.dossiers = [...this._appState.dossiers]; return foundDossier; } catch (error) { - this._notificationService.showToastNotification( - this._translateService.instant( - error.status === 409 ? 'add-dossier-dialog.errors.dossier-already-exists' : 'add-dossier-dialog.errors.generic' - ), - null, - NotificationType.ERROR + this._toaster.error( + error.status === 409 ? 'add-dossier-dialog.errors.dossier-already-exists' : 'add-dossier-dialog.errors.generic' ); } }