From a2a96ad2df62f8120295790634689287b3cc6ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Mon, 13 Dec 2021 21:24:16 +0200 Subject: [PATCH 1/3] Fixed dossier / files changes --- .../dossier-overview-screen.component.ts | 18 +++++++++--------- .../dossiers-listing-screen.component.ts | 8 +------- .../file-actions/file-actions.component.ts | 9 ++++++++- .../entity-services/dossiers.service.ts | 12 ++++++++++-- libs/common-ui | 2 +- 5 files changed, 29 insertions(+), 20 deletions(-) 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 bf8865bdd..7995c6d03 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 @@ -16,8 +16,8 @@ import { FileUploadModel } from '@upload-download/model/file-upload.model'; import { FileUploadService } from '@upload-download/services/file-upload.service'; import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; import * as moment from 'moment'; -import { Observable, timer } from 'rxjs'; -import { filter, skip, switchMap, tap } from 'rxjs/operators'; +import { combineLatest, Observable } from 'rxjs'; +import { skip, switchMap, tap } from 'rxjs/operators'; import { convertFiles, Files, handleFileDrop } from '@utils/index'; import { CircleButtonTypes, @@ -45,7 +45,7 @@ import { LongPressEvent } from '@shared/directives/long-press.directive'; import { UserPreferenceService } from '@services/user-preference.service'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { FilesService } from '@services/entity-services/files.service'; -import { CHANGED_CHECK_INTERVAL } from '@utils/constants'; +import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; @Component({ templateUrl: './dossier-overview-screen.component.html', @@ -81,6 +81,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple readonly permissionsService: PermissionsService, private readonly _loadingService: LoadingService, private readonly _dossiersService: DossiersService, + private readonly _dossierStatsService: DossierStatsService, private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _appConfigService: AppConfigService, private readonly _fileUploadService: FileUploadService, @@ -128,12 +129,11 @@ export class DossierOverviewScreenComponent extends ListingComponent imple this._fileDropOverlayService.initFileDropHandling(this.dossierId); - this.addSubscription = timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL) - .pipe( - switchMap(() => this._filesService.hasChanges$(this.dossierId)), - filter(changed => changed), - switchMap(() => this._reloadFiles()), - ) + this.addSubscription = combineLatest([ + this._dossiersService.getEntityChanged$(this.dossierId), + this._dossierStatsService.watch$(this.dossierId), + ]) + .pipe(switchMap(() => this._reloadFiles())) .subscribe(); this.addSubscription = this.configService.listingMode$.subscribe(() => { diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts index c7ba3f10b..79b8fd977 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts @@ -3,7 +3,6 @@ import { Dossier } from '@red/domain'; import { UserService } from '@services/user.service'; import { PermissionsService } from '@services/permissions.service'; import { TranslateChartService } from '@services/translate-chart.service'; -import { timer } from 'rxjs'; import { Router } from '@angular/router'; import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach, OnDetach, TableComponent } from '@iqser/common-ui'; @@ -12,8 +11,7 @@ import { ConfigService } from '../config.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { FilesService } from '@services/entity-services/files.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; -import { switchMap, tap } from 'rxjs/operators'; -import { CHANGED_CHECK_INTERVAL } from '@utils/constants'; +import { tap } from 'rxjs/operators'; @Component({ templateUrl: './dossiers-listing-screen.component.html', @@ -57,10 +55,6 @@ export class DossiersListingScreenComponent extends ListingComponent im } ngOnInit(): void { - this.addSubscription = timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL) - .pipe(switchMap(() => this._dossiersService.loadAllIfChanged())) - .subscribe(); - this.addSubscription = this._dossiersService.all$.pipe(tap(() => this._computeAllFilters())).subscribe(); } diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts index 150f44c55..95a6427cd 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts @@ -1,5 +1,6 @@ import { ChangeDetectionStrategy, + ChangeDetectorRef, Component, EventEmitter, HostBinding, @@ -96,6 +97,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, private readonly _userPreferenceService: UserPreferenceService, private readonly _reanalysisService: ReanalysisService, private readonly _router: Router, + private readonly _changeRef: ChangeDetectorRef, ) { super(); } @@ -224,7 +226,10 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, } ngOnInit() { - this._dossiersService.getEntityChanged$(this.file.dossierId).pipe(tap(() => this._setup())); + this.addSubscription = this._dossiersService + .getEntityChanged$(this.file.dossierId) + .pipe(tap(() => this._setup())) + .subscribe(); } ngOnChanges() { @@ -353,6 +358,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, this.showReanalyseDossierOverview = this.canReanalyse && this.isDossierOverview && this.analysisForced; this.buttons = this._buttons; + + this._changeRef.markForCheck(); } private async _setFileApproved() { diff --git a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts index 7f4bbdc68..e3543d71b 100644 --- a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts @@ -1,11 +1,12 @@ import { Injectable, Injector } from '@angular/core'; -import { EntitiesService, List, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui'; +import { EntitiesService, List, log, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui'; import { Dossier, IDossier, IDossierRequest } from '@red/domain'; import { catchError, filter, map, mapTo, switchMap, tap } from 'rxjs/operators'; -import { combineLatest, iif, Observable, of, throwError } from 'rxjs'; +import { combineLatest, iif, Observable, of, throwError, timer } from 'rxjs'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; +import { CHANGED_CHECK_INTERVAL } from '@utils/constants'; export interface IDossiersStats { totalPeople: number; @@ -27,6 +28,13 @@ export class DossiersService extends EntitiesService { private readonly _dossierStatsService: DossierStatsService, ) { super(_injector, Dossier, 'dossier'); + + timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL) + .pipe( + switchMap(() => this.loadAllIfChanged()), + log(), + ) + .subscribe(); } loadAll(): Observable { diff --git a/libs/common-ui b/libs/common-ui index 51f3d7502..a1c1be7ed 160000 --- a/libs/common-ui +++ b/libs/common-ui @@ -1 +1 @@ -Subproject commit 51f3d7502b5545663afa2fa0a62abc3578f92905 +Subproject commit a1c1be7edc783bdea42b86c0f8b1f74437065b76 From 72504dfebc8f41b335a1e4f8090a68d99e891f49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Wed, 12 Jan 2022 22:05:59 +0200 Subject: [PATCH 2/3] Use new changes details endpoint --- .../dossier-overview-screen.component.ts | 27 ++++++---------- .../entity-services/dossiers.service.ts | 32 ++++++++++++++++--- 2 files changed, 36 insertions(+), 23 deletions(-) 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 7995c6d03..9aed7b89f 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 @@ -15,9 +15,8 @@ import { FileDropOverlayService } from '@upload-download/services/file-drop-over import { FileUploadModel } from '@upload-download/model/file-upload.model'; import { FileUploadService } from '@upload-download/services/file-upload.service'; import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; -import * as moment from 'moment'; -import { combineLatest, Observable } from 'rxjs'; -import { skip, switchMap, tap } from 'rxjs/operators'; +import { Observable } from 'rxjs'; +import { filter, skip, switchMap, tap } from 'rxjs/operators'; import { convertFiles, Files, handleFileDrop } from '@utils/index'; import { CircleButtonTypes, @@ -129,17 +128,17 @@ export class DossierOverviewScreenComponent extends ListingComponent imple this._fileDropOverlayService.initFileDropHandling(this.dossierId); - this.addSubscription = combineLatest([ - this._dossiersService.getEntityChanged$(this.dossierId), - this._dossierStatsService.watch$(this.dossierId), - ]) - .pipe(switchMap(() => this._reloadFiles())) - .subscribe(); - this.addSubscription = this.configService.listingMode$.subscribe(() => { this._computeAllFilters(); }); + this.addSubscription = this._dossiersService.dossierFileChanges$ + .pipe( + filter(dossierId => dossierId === this.dossierId), + switchMap(dossierId => this._filesService.loadAll(dossierId)), + ) + .subscribe(); + this.addSubscription = this._dossierTemplatesService .getEntityChanged$(this.currentDossier.dossierTemplateId) .pipe( @@ -192,9 +191,6 @@ export class DossierOverviewScreenComponent extends ListingComponent imple (this._fileInput as any).nativeElement.value = null; } - recentlyModifiedChecker = (file: File) => - moment(file.lastUpdated).add(this._appConfigService.values.RECENT_PERIOD_IN_HOURS, 'hours').isAfter(moment()); - private _updateFileAttributes(): void { this._fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig(this.currentDossier.dossierTemplateId)?.fileAttributeConfigs || []; @@ -204,11 +200,6 @@ export class DossierOverviewScreenComponent extends ListingComponent imple this._computeAllFilters(); } - private async _reloadFiles() { - await this._filesService.loadAll(this.dossierId).toPromise(); - this._computeAllFilters(); - } - private _loadEntitiesFromState() { this.currentDossier = this._dossiersService.find(this.dossierId); this._computeAllFilters(); diff --git a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts index e3543d71b..5d14236fe 100644 --- a/apps/red-ui/src/app/services/entity-services/dossiers.service.ts +++ b/apps/red-ui/src/app/services/entity-services/dossiers.service.ts @@ -1,8 +1,8 @@ import { Injectable, Injector } from '@angular/core'; -import { EntitiesService, List, log, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui'; +import { EntitiesService, List, mapEach, QueryParam, RequiredParam, shareLast, Toaster, Validate } from '@iqser/common-ui'; import { Dossier, IDossier, IDossierRequest } from '@red/domain'; import { catchError, filter, map, mapTo, switchMap, tap } from 'rxjs/operators'; -import { combineLatest, iif, Observable, of, throwError, timer } from 'rxjs'; +import { combineLatest, Observable, of, Subject, throwError, timer } from 'rxjs'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; import { DossierStatsService } from '@services/entity-services/dossier-stats.service'; @@ -13,6 +13,16 @@ export interface IDossiersStats { totalAnalyzedPages: number; } +interface ChangesDetails { + dossierChanges: [ + { + dossierChanges: boolean; + dossierId: string; + fileChanges: boolean; + }, + ]; +} + const DOSSIER_EXISTS_MSG = _('add-dossier-dialog.errors.dossier-already-exists'); const GENERIC_MGS = _('add-dossier-dialog.errors.generic'); @@ -21,6 +31,7 @@ const GENERIC_MGS = _('add-dossier-dialog.errors.generic'); }) export class DossiersService extends EntitiesService { readonly generalStats$ = this.all$.pipe(switchMap(entities => this._generalStats$(entities))); + readonly dossierFileChanges$ = new Subject(); constructor( private readonly _toaster: Toaster, @@ -32,7 +43,7 @@ export class DossiersService extends EntitiesService { timer(CHANGED_CHECK_INTERVAL, CHANGED_CHECK_INTERVAL) .pipe( switchMap(() => this.loadAllIfChanged()), - log(), + tap(changes => this._emitFileChanges(changes)), ) .subscribe(); } @@ -47,8 +58,15 @@ export class DossiersService extends EntitiesService { ); } - loadAllIfChanged(): Observable { - return this.hasChanges$().pipe(switchMap(changed => iif(() => changed, this.loadAll()).pipe(mapTo(changed)))); + loadAllIfChanged(): Observable { + return this.hasChangesDetails$().pipe(switchMap(changes => this.loadAll().pipe(mapTo(changes)))); + } + + hasChangesDetails$(): Observable { + const body = { value: this._lastCheckedForChanges.get('root') ?? '0' }; + return this._post(body, `${this._defaultModelPath}/changes/details`).pipe( + filter(changes => changes.dossierChanges.length > 0), + ); } @Validate() @@ -90,6 +108,10 @@ export class DossiersService extends EntitiesService { return super.delete(body, 'deleted-dossiers/hard-delete', body).toPromise(); } + private _emitFileChanges(changes: ChangesDetails): void { + changes.dossierChanges.filter(change => change.fileChanges).forEach(change => this.dossierFileChanges$.next(change.dossierId)); + } + private _computeStats(entities: List): IDossiersStats { let totalAnalyzedPages = 0; const totalPeople = new Set(); From c400e875f98fc031575a9b42f7ffe826dbc89c9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adina=20=C8=9Aeudan?= Date: Thu, 13 Jan 2022 00:21:25 +0200 Subject: [PATCH 3/3] Handle deleted entities --- apps/red-ui/src/app/app.component.html | 1 + .../dossier-overview-screen.component.ts | 27 ++++++++++--- .../dossiers-listing-screen.component.ts | 11 ++--- .../file-preview-screen.component.ts | 40 ++++++++++++++----- .../entity-services/files-map.service.ts | 10 +++++ apps/red-ui/src/assets/i18n/en.json | 14 +++++++ 6 files changed, 79 insertions(+), 24 deletions(-) diff --git a/apps/red-ui/src/app/app.component.html b/apps/red-ui/src/app/app.component.html index ff64625af..20a8fecb2 100644 --- a/apps/red-ui/src/app/app.component.html +++ b/apps/red-ui/src/app/app.component.html @@ -1,3 +1,4 @@ + 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 9aed7b89f..19e8e7e71 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 @@ -15,18 +15,19 @@ import { FileDropOverlayService } from '@upload-download/services/file-drop-over import { FileUploadModel } from '@upload-download/model/file-upload.model'; import { FileUploadService } from '@upload-download/services/file-upload.service'; import { StatusOverlayService } from '@upload-download/services/status-overlay.service'; -import { Observable } from 'rxjs'; +import { Observable, Subscription } from 'rxjs'; import { filter, skip, switchMap, tap } from 'rxjs/operators'; import { convertFiles, Files, handleFileDrop } from '@utils/index'; import { CircleButtonTypes, + CustomError, DefaultListingServices, + ErrorService, ListingComponent, ListingModes, LoadingService, NestedFilter, OnAttach, - OnDetach, TableColumnConfig, TableComponent, WorkflowConfig, @@ -52,7 +53,7 @@ import { DossierStatsService } from '@services/entity-services/dossier-stats.ser providers: [...DefaultListingServices, { provide: ListingComponent, useExisting: forwardRef(() => DossierOverviewScreenComponent) }], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DossierOverviewScreenComponent extends ListingComponent implements OnInit, OnDestroy, OnDetach, OnAttach { +export class DossierOverviewScreenComponent extends ListingComponent implements OnInit, OnDestroy, OnAttach { readonly listingModes = ListingModes; readonly circleButtonTypes = CircleButtonTypes; readonly tableHeaderLabel = _('dossier-overview.table-header.title'); @@ -73,6 +74,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple @ViewChild('fileInput', { static: true }) private readonly _fileInput: ElementRef; @ViewChild(TableComponent) private readonly _tableComponent: TableComponent; private _fileAttributeConfigs: IFileAttributeConfig[]; + private readonly _removableSubscriptions = new Subscription(); constructor( protected readonly _injector: Injector, @@ -92,6 +94,7 @@ export class DossierOverviewScreenComponent extends ListingComponent imple readonly configService: ConfigService, private readonly _userPreferenceService: UserPreferenceService, private readonly _fileMapService: FilesMapService, + private readonly _errorService: ErrorService, activatedRoute: ActivatedRoute, ) { super(_injector); @@ -165,11 +168,10 @@ export class DossierOverviewScreenComponent extends ListingComponent imple ngOnAttach() { this._fileDropOverlayService.initFileDropHandling(this.dossierId); + this._setRemovableSubscriptions(); this._tableComponent?.scrollToLastIndex(); } - ngOnDetach() {} - forceReanalysisAction($event: LongPressEvent) { this.analysisForced = !$event.touchEnd && this._userPreferenceService.areDevFeaturesEnabled; } @@ -191,6 +193,21 @@ export class DossierOverviewScreenComponent extends ListingComponent imple (this._fileInput as any).nativeElement.value = null; } + private _setRemovableSubscriptions(): void { + this._removableSubscriptions.add( + this._dossiersService + .getEntityDeleted$(this.dossierId) + .pipe(tap(() => this._handleDeletedDossier())) + .subscribe(), + ); + } + + private _handleDeletedDossier(): void { + this._errorService.set( + new CustomError(_('error.deleted-entity.dossier.label'), _('error.deleted-entity.dossier.action'), 'iqser:expand'), + ); + } + private _updateFileAttributes(): void { this._fileAttributeConfigs = this._fileAttributesService.getFileAttributeConfig(this.currentDossier.dossierTemplateId)?.fileAttributeConfigs || []; diff --git a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts index 79b8fd977..d70a2ae00 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/dossiers-listing/screen/dossiers-listing-screen.component.ts @@ -1,11 +1,11 @@ -import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, forwardRef, Injector, OnInit, TemplateRef, ViewChild } from '@angular/core'; import { Dossier } from '@red/domain'; import { UserService } from '@services/user.service'; import { PermissionsService } from '@services/permissions.service'; import { TranslateChartService } from '@services/translate-chart.service'; import { Router } from '@angular/router'; import { DossiersDialogService } from '../../../services/dossiers-dialog.service'; -import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach, OnDetach, TableComponent } from '@iqser/common-ui'; +import { DefaultListingServicesTmp, EntitiesService, ListingComponent, OnAttach, TableComponent } from '@iqser/common-ui'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { ConfigService } from '../config.service'; import { DossiersService } from '@services/entity-services/dossiers.service'; @@ -23,7 +23,7 @@ import { tap } from 'rxjs/operators'; ], changeDetection: ChangeDetectionStrategy.OnPush, }) -export class DossiersListingScreenComponent extends ListingComponent implements OnInit, OnDestroy, OnAttach, OnDetach { +export class DossiersListingScreenComponent extends ListingComponent implements OnInit, OnAttach { readonly currentUser = this._userService.currentUser; readonly tableColumnConfigs = this._configService.tableConfig; readonly tableHeaderLabel = _('dossier-listing.table-header.title'); @@ -59,14 +59,9 @@ export class DossiersListingScreenComponent extends ListingComponent im } ngOnAttach(): void { - this.ngOnInit(); this._tableComponent?.scrollToLastIndex(); } - ngOnDetach(): void { - this.ngOnDestroy(); - } - openAddDossierDialog(): void { this._dialogService.openDialog('addDossier', null, null, async (addResponse: { dossier: Dossier; addMembers: boolean }) => { await this._router.navigate([addResponse.dossier.routerLink]); 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 445d22bbd..4ff4c076a 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 @@ -5,7 +5,9 @@ import { PdfViewerComponent } from './components/pdf-viewer/pdf-viewer.component import { AutoUnsubscribe, CircleButtonTypes, + CustomError, Debounce, + ErrorService, FilterService, LoadingService, OnAttach, @@ -43,19 +45,12 @@ import { ViewModeService } from './services/view-mode.service'; import { MultiSelectService } from './services/multi-select.service'; import { DocumentInfoService } from './services/document-info.service'; import { ReanalysisService } from '../../../../services/reanalysis.service'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import Annotation = Core.Annotations.Annotation; import PDFNet = Core.PDFNet; const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f', 'ArrowUp', 'ArrowDown']; -function diff(first: readonly T[], second: readonly T[]): T[] { - // symmetrical difference between two arrays - const a = new Set(first); - const b = new Set(second); - - return [...first.filter(x => !b.has(x)), ...second.filter(x => !a.has(x))]; -} - @Component({ templateUrl: './file-preview-screen.component.html', styleUrls: ['./file-preview-screen.component.scss'], @@ -108,6 +103,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _filesMapService: FilesMapService, private readonly _dossiersService: DossiersService, private readonly _reanalysisService: ReanalysisService, + private readonly _errorService: ErrorService, readonly excludedPagesService: ExcludedPagesService, readonly viewModeService: ViewModeService, readonly multiSelectService: MultiSelectService, @@ -190,7 +186,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni ngOnDetach(): void { this.displayPdfViewer = false; - super.ngOnDestroy(); + super.ngOnDetach(); this._changeDetectorRef.markForCheck(); } @@ -529,10 +525,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } private _subscribeToFileUpdates(): void { - this.addSubscription = timer(0, 5000) + this.addActiveScreenSubscription = timer(0, 5000) .pipe(switchMap(() => this._filesService.reload(this.dossierId, this.fileId))) .subscribe(); - this.addSubscription = this._filesMapService.fileReanalysed$ + this.addActiveScreenSubscription = this._filesMapService.fileReanalysed$ .pipe(filter(file => file.fileId === this.fileId)) .subscribe(async file => { if (file.lastProcessed !== this.fileData?.file.lastProcessed) { @@ -541,6 +537,28 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } this._loadingService.stop(); }); + + this.addActiveScreenSubscription = this._dossiersService + .getEntityDeleted$(this.dossierId) + .pipe(tap(() => this._handleDeletedDossier())) + .subscribe(); + + this.addActiveScreenSubscription = this._filesMapService + .watchDeleted$(this.fileId) + .pipe(tap(() => this._handleDeletedFile())) + .subscribe(); + } + + private _handleDeletedDossier(): void { + this._errorService.set( + new CustomError(_('error.deleted-entity.file-dossier.label'), _('error.deleted-entity.file-dossier.action'), 'iqser:expand'), + ); + } + + private _handleDeletedFile(): void { + this._errorService.set( + new CustomError(_('error.deleted-entity.file.label'), _('error.deleted-entity.file.action'), 'iqser:expand'), + ); } private async _loadFileData(file: File): Promise { diff --git a/apps/red-ui/src/app/services/entity-services/files-map.service.ts b/apps/red-ui/src/app/services/entity-services/files-map.service.ts index b861090da..9d04a9d99 100644 --- a/apps/red-ui/src/app/services/entity-services/files-map.service.ts +++ b/apps/red-ui/src/app/services/entity-services/files-map.service.ts @@ -7,6 +7,7 @@ import { filter, startWith } from 'rxjs/operators'; export class FilesMapService { readonly fileReanalysed$ = new Subject(); private readonly _entityChanged$ = new Subject(); + private readonly _entityDeleted$ = new Subject(); private readonly _map = new Map>(); get$(dossierId: string) { @@ -39,6 +40,7 @@ export class FilesMapService { const reanalysedEntities = []; const changedEntities = []; + const deletedEntities = this.get(key).filter(oldEntity => !entities.find(newEntity => newEntity.id === oldEntity.id)); // Keep old object references for unchanged entities const newEntities = entities.map(newEntity => { @@ -67,6 +69,10 @@ export class FilesMapService { for (const file of changedEntities) { this._entityChanged$.next(file); } + + for (const file of deletedEntities) { + this._entityDeleted$.next(file); + } } replace(entity: File) { @@ -83,4 +89,8 @@ export class FilesMapService { startWith(this.get(key, entityId)), ); } + + watchDeleted$(entityId: string): Observable { + return this._entityDeleted$.pipe(filter(entity => entity.id === entityId)); + } } diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index 510458983..fa8141da2 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -938,6 +938,20 @@ "unsaved-changes": "You have unsaved changes. Save or revert before changing the tab." }, "error": { + "deleted-entity": { + "dossier": { + "action": "Back to overview", + "label": "This dossier has been deleted!" + }, + "file-dossier": { + "action": "Back to overview", + "label": "The dossier of this file has been deleted!" + }, + "file": { + "action": "Back to dossier", + "label": "This file has been deleted!" + } + }, "http": { "generic": "Action failed with code {status}" },