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 f27fac5b5..2844c1ca0 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
@@ -81,7 +81,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
hideSkipped = false;
displayPDFViewer = false;
viewDocumentInfo = false;
- excludePages = false;
+ showExcludedPages = false;
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
@ViewChild('fileActions') fileActions: FileActionsComponent;
readonly dossierId: string;
@@ -271,7 +271,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const file = this._filesMapService.get(this.dossierId, this.fileId);
if (file?.analysisRequired) {
- this.fileActions.reanalyseFile();
+ await this.fileActions.reanalyseFile();
}
this.displayPDFViewer = true;
@@ -367,7 +367,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
response.manualRedactionEntryWrapper.rectId,
);
this._instance.Core.annotationManager.deleteAnnotation(annotation);
- await this._filesService.reload(this.dossierId, this.fileId);
+ await this._filesService.reload(this.dossierId, this.fileId).toPromise();
const distinctPages = manualRedactionEntryWrapper.manualRedactionEntry.positions
.map(p => p.page)
.filter((item, pos, self) => self.indexOf(item) === pos);
@@ -459,58 +459,33 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
await this._cleanupAndRedrawManualAnnotationsForEntirePage(annotation?.pageNumber || this.activeViewerPage);
}
- async fileActionPerformed(action: string) {
- this.editingReviewer = false;
+ ocredFile(): void {
+ this._updateCanPerformActions();
+ this._reloadFileOnReanalysis = true;
+ }
- switch (action) {
- case 'enable-analysis':
- case 'disable-analysis':
- this._loadingService.start();
- // the trigger will disable it later
- break;
+ async excludePages(): Promise
{
+ this._loadingService.start();
+ await this._loadFileData(true);
+ this._cleanupAndRedrawManualAnnotations$();
+ await this._stampPDF();
+ this._loadingService.stop();
+ }
- case 'delete':
- return this._router.navigate([this.dossiersService.find(this.dossierId).routerLink]);
+ toggleViewExcludedPages(): void {
+ this.showExcludedPages = !this.showExcludedPages;
+ this._workloadComponent.multiSelectActive = false;
+ this.viewDocumentInfo = false;
+ }
- case 'reanalyse':
- await this._loadFileData(true);
- this._updateCanPerformActions();
- await this._filesService.loadAll(this.dossierId).toPromise();
- return;
-
- case 'exclude-pages':
- await this._filesService.loadAll(this.dossierId).toPromise();
- await this._loadFileData(true);
- this._cleanupAndRedrawManualAnnotations$();
- await this._stampPDF();
- this._loadingService.stop();
- return;
-
- case 'view-document-info':
- this.viewDocumentInfo = !this.viewDocumentInfo;
- return;
-
- case 'view-exclude-pages':
- this.excludePages = !this.excludePages;
- this._workloadComponent.multiSelectActive = false;
- this.viewDocumentInfo = false;
- return;
-
- case 'ocr-file':
- this._updateCanPerformActions();
- this._reloadFileOnReanalysis = true;
- return;
-
- default:
- this._updateCanPerformActions();
- }
+ toggleViewDocumentInfo(): void {
+ this.viewDocumentInfo = !this.viewDocumentInfo;
+ this._workloadComponent.multiSelectActive = false;
+ this.showExcludedPages = false;
}
async assignToMe(file: File) {
- await this._fileAssignService.assignToMe([file], async () => {
- await this._filesService.reload(this.dossierId, this.fileId);
- this._updateCanPerformActions();
- });
+ await this._fileAssignService.assignToMe([file]);
}
async assignReviewer(file: File, user: User | string) {
@@ -518,11 +493,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const reviewerName = this.userService.getNameForId(reviewerId);
const { dossierId, fileId, filename } = file;
+ this._loadingService.start();
await this._filesService.setReviewerFor([fileId], dossierId, reviewerId).toPromise();
+ this._loadingService.stop();
this._toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } });
- await this._filesService.reload(this.dossierId, this.fileId);
- this._updateCanPerformActions();
this.editingReviewer = false;
}
@@ -619,13 +594,15 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.addSubscription = timer(0, 5000)
.pipe(switchMap(() => this._filesService.reload(this.dossierId, this.fileId)))
.subscribe();
+ this.addSubscription = this.file$.subscribe(() => {
+ this._updateCanPerformActions();
+ });
this.addSubscription = this._filesMapService.fileReanalysed$
.pipe(filter(file => file.fileId === this.fileId))
.subscribe(async () => {
await this._loadFileData(!this._reloadFileOnReanalysis);
this._reloadFileOnReanalysis = false;
this._loadingService.stop();
- this._updateCanPerformActions();
this._cleanupAndRedrawManualAnnotations$();
});
}
@@ -679,7 +656,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const currentPageAnnotations = this.annotations.filter(a => a.pageNumber === page);
const currentPageAnnotationIds = currentPageAnnotations.map(a => a.id);
- await this._filesService.reload(this.dossierId, this.fileId);
+ await this._filesService.reload(this.dossierId, this.fileId).toPromise();
this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise();
this.rebuildFilters();
diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html
index 7955ceb8f..3b7168251 100644
--- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html
+++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.html
@@ -62,7 +62,7 @@
>
();
+ @Output() readonly ocredFile = new EventEmitter();
toggleTooltip?: string;
assignTooltip?: string;
@@ -63,6 +64,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
isDossierOverviewWorkflow = false;
isFilePreview = false;
tooltipPosition: IqserTooltipPosition;
+ @Output() readonly toggleViewDocumentInfo = new EventEmitter();
+ @Output() readonly toggleViewExcludedPages = new EventEmitter();
constructor(
readonly permissionsService: PermissionsService,
@@ -77,6 +80,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
private readonly _toaster: Toaster,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _reanalysisService: ReanalysisService,
+ private readonly _router: Router,
) {
super();
}
@@ -93,16 +97,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
this.setup();
}
- toggleViewDocumentInfo() {
- this.actionPerformed.emit('view-document-info');
- }
-
- toggleExcludePages() {
- this.actionPerformed.emit('view-exclude-pages');
- }
-
openDocument() {
- this.actionPerformed.emit('navigate');
+ this._router.navigate([this.file.routerLink]).then();
}
openDeleteFileDialog($event: MouseEvent) {
@@ -121,8 +117,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
.catch(error => {
this._toaster.error(_('error.http.generic'), { params: error });
});
- await this._filesService.loadAll(this.file.dossierId).toPromise();
- this.actionPerformed.emit('delete');
this._loadingService.stop();
},
);
@@ -131,39 +125,24 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
assign($event: MouseEvent) {
const mode = this.file.isUnderApproval ? 'approver' : 'reviewer';
const files = [this.file];
- this._dialogService.openDialog('assignFile', $event, { mode, files }, () => {
- this.actionPerformed.emit('assign-reviewer');
- });
+ this._dialogService.openDialog('assignFile', $event, { mode, files });
}
async assignToMe($event: MouseEvent) {
$event.stopPropagation();
-
- await this._fileAssignService.assignToMe([this.file], () => {
- this.reloadFiles('reanalyse');
- });
+ await this._fileAssignService.assignToMe([this.file]);
}
- reanalyseFile($event?: MouseEvent) {
+ async reanalyseFile($event?: MouseEvent) {
if ($event) {
$event.stopPropagation();
}
- this.addSubscription = this._reanalysisService
- .reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, true)
- .subscribe(() => {
- this.reloadFiles('reanalyse');
- });
+ await this._reanalysisService.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, true).toPromise();
}
async setFileUnderApproval($event: MouseEvent) {
$event.stopPropagation();
- const dossier = this.dossiersService.find(this.file.dossierId);
- if (dossier.approverIds.length > 1) {
- this._fileAssignService.assignApprover($event, this.file, () => this.reloadFiles('assign-reviewer'), true);
- } else {
- await this._filesService.setUnderApprovalFor([this.file.id], dossier.id, dossier.approverIds[0]).toPromise();
- this.reloadFiles('set-under-approval');
- }
+ await this._fileAssignService.assignApprover($event, this.file, true);
}
async setFileApproved($event: MouseEvent) {
@@ -188,27 +167,20 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
async ocrFile($event: MouseEvent) {
$event.stopPropagation();
+ this._loadingService.start();
await this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId).toPromise();
- this.reloadFiles('ocr-file');
+ this.ocredFile.emit();
+ this._loadingService.stop();
}
- setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
- this._fileAssignService.assignReviewer($event, this.file, () => this.reloadFiles('assign-reviewer'), ignoreDialogChanges);
- }
-
- reloadFiles(action: string) {
- this._filesService
- .loadAll(this.file.dossierId)
- .toPromise()
- .then(() => {
- this.actionPerformed.emit(action);
- });
+ async setFileUnderReview($event: MouseEvent, ignoreDialogChanges = false) {
+ await this._fileAssignService.assignReviewer($event, this.file, ignoreDialogChanges);
}
async toggleAnalysis() {
+ this._loadingService.start();
await this._reanalysisService.toggleAnalysis(this.file.dossierId, this.file.fileId, !this.file.excluded).toPromise();
- await this._filesService.loadAll(this.file.dossierId).toPromise();
- this.actionPerformed.emit(this.file?.excluded ? 'enable-analysis' : 'disable-analysis');
+ this._loadingService.stop();
}
forceReanalysisAction($event: LongPressEvent) {
@@ -216,9 +188,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
}
setup() {
- this.isDossierOverview = this.type.startsWith('dossier-overview-list');
this.isDossierOverviewList = this.type === 'dossier-overview-list';
this.isDossierOverviewWorkflow = this.type === 'dossier-overview-workflow';
+ this.isDossierOverview = this.type.startsWith('dossier-overview');
this.isFilePreview = this.type === 'file-preview';
this.tooltipPosition = this.isFilePreview ? 'below' : 'above';
@@ -251,7 +223,8 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
}
private async _setFileApproved() {
+ this._loadingService.start();
await this._filesService.setApprovedFor([this.file.id], this.file.dossierId).toPromise();
- this.reloadFiles('set-approved');
+ this._loadingService.stop();
}
}
diff --git a/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts b/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts
index f3329db62..af171b965 100644
--- a/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts
+++ b/apps/red-ui/src/app/modules/dossier/shared/services/file-assign.service.ts
@@ -4,8 +4,10 @@ import { Dossier, File } from '@red/domain';
import { DossiersDialogService } from '../../services/dossiers-dialog.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FilesService } from '@services/entity-services/files.service';
-import { ConfirmationDialogInput, Toaster } from '@iqser/common-ui';
+import { ConfirmationDialogInput, LoadingService, Toaster } from '@iqser/common-ui';
import { DossiersService } from '@services/entity-services/dossiers.service';
+import { Observable } from 'rxjs';
+import { tap } from 'rxjs/operators';
@Injectable()
export class FileAssignService {
@@ -14,10 +16,11 @@ export class FileAssignService {
private readonly _userService: UserService,
private readonly _filesService: FilesService,
private readonly _dossiersService: DossiersService,
+ private readonly _loadingService: LoadingService,
private readonly _toaster: Toaster,
) {}
- async assignToMe(files: File[], callback?: Function) {
+ async assignToMe(files: File[]) {
return new Promise((resolve, reject) => {
const atLeastOneFileHasReviewer = files.reduce((acc, fs) => acc || !!fs.currentReviewer, false);
if (atLeastOneFileHasReviewer) {
@@ -26,48 +29,43 @@ export class FileAssignService {
question: _('confirmation-dialog.assign-file-to-me.question'),
});
this._dialogService.openDialog('confirm', null, data, () => {
- this._assignReviewerToCurrentUser(files, callback)
+ this._assignReviewerToCurrentUser(files)
+ .toPromise()
.then(() => resolve())
.catch(() => reject());
});
} else {
- this._assignReviewerToCurrentUser(files, callback)
+ this._assignReviewerToCurrentUser(files)
+ .toPromise()
.then(() => resolve())
.catch(() => reject());
}
});
}
- assignReviewer($event: MouseEvent, file: File, callback?: Function, ignoreChanged = false): void {
- this._assignFile('reviewer', $event, file, callback, ignoreChanged);
+ async assignReviewer($event: MouseEvent, file: File, ignoreChanged = false): Promise {
+ await this._assignFile('reviewer', $event, file, ignoreChanged);
}
- assignApprover($event: MouseEvent, file: File, callback?: Function, ignoreChanged = false): void {
- this._assignFile('approver', $event, file, callback, ignoreChanged);
+ async assignApprover($event: MouseEvent, file: File, ignoreChanged = false): Promise {
+ await this._assignFile('approver', $event, file, ignoreChanged);
}
- private _assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file: File, callback?: Function, ignoreChanged = false) {
+ private async _assignFile(mode: 'reviewer' | 'approver', $event: MouseEvent, file: File, ignoreChanged = false): Promise {
const dossier = this._dossiersService.find(file.dossierId);
const userIds = this._getUserIds(mode, dossier);
- if (userIds.length === 1 || userIds.includes(this._userService.currentUser.id)) {
+ if (userIds.length === 1) {
$event?.stopPropagation(); // event$ is null when called from workflow view
const userId = userIds.length === 1 ? userIds[0] : this._userService.currentUser.id;
- this._makeAssignFileRequest(userId, mode, [file]).then(async () => {
- if (callback) {
- await callback();
- }
- });
+ await this._makeAssignFileRequest(userId, mode, [file]);
} else {
const data = { mode, files: [file], ignoreChanged };
- this._dialogService.openDialog('assignFile', $event, data, async () => {
- if (callback) {
- await callback();
- }
- });
+ this._dialogService.openDialog('assignFile', $event, data);
}
}
private async _makeAssignFileRequest(userId: string, mode: 'reviewer' | 'approver', files: File[]) {
+ this._loadingService.start();
try {
if (mode === 'reviewer') {
await this._filesService
@@ -89,22 +87,21 @@ export class FileAssignService {
} catch (error) {
this._toaster.error(_('error.http.generic'), { params: error });
}
+ this._loadingService.stop();
}
private _getUserIds(mode: 'reviewer' | 'approver', dossier: Dossier) {
return mode === 'approver' ? dossier.approverIds : dossier.memberIds;
}
- private async _assignReviewerToCurrentUser(files: File[], callback?: Function) {
- await this._filesService
+ private _assignReviewerToCurrentUser(files: File[]): Observable {
+ this._loadingService.start();
+ return this._filesService
.setReviewerFor(
files.map(f => f.fileId),
files[0].dossierId,
this._userService.currentUser.id,
)
- .toPromise();
- if (callback) {
- await callback();
- }
+ .pipe(tap(() => this._loadingService.stop()));
}
}
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 1402a468a..a8f69b6a4 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
@@ -37,23 +37,36 @@ export class FilesMapService {
return entities.forEach(entity => this._entityChanged$.next(entity));
}
+ const reanalysedEntities = [];
+ const changedEntities = [];
+
// Keep old object references for unchanged entities
const newEntities = entities.map(newEntity => {
const oldEntity = this.get(key, newEntity.id);
if (oldEntity?.lastProcessed !== newEntity.lastProcessed) {
- this.fileReanalysed$.next(newEntity);
+ reanalysedEntities.push(newEntity);
}
if (newEntity.isEqual(oldEntity)) {
return oldEntity;
}
- this._entityChanged$.next(newEntity);
+ changedEntities.push(newEntity);
return newEntity;
});
this._map.get(key).next(newEntities);
+
+ // Emit observables only after entities have been updated
+
+ for (const file of reanalysedEntities) {
+ this.fileReanalysed$.next(file);
+ }
+
+ for (const file of changedEntities) {
+ this._entityChanged$.next(file);
+ }
}
replace(entity: File) {
diff --git a/apps/red-ui/src/app/services/entity-services/files.service.ts b/apps/red-ui/src/app/services/entity-services/files.service.ts
index 24ae31d60..da502bef2 100644
--- a/apps/red-ui/src/app/services/entity-services/files.service.ts
+++ b/apps/red-ui/src/app/services/entity-services/files.service.ts
@@ -7,7 +7,6 @@ import { FilesMapService } from '@services/entity-services/files-map.service';
import { map, mapTo, switchMap, tap } from 'rxjs/operators';
import { DossierStatsService } from '@services/entity-services/dossier-stats.service';
-
@Injectable({
providedIn: 'root',
})
diff --git a/apps/red-ui/src/app/services/reanalysis.service.ts b/apps/red-ui/src/app/services/reanalysis.service.ts
index 1defc5008..fbe5c1b5e 100644
--- a/apps/red-ui/src/app/services/reanalysis.service.ts
+++ b/apps/red-ui/src/app/services/reanalysis.service.ts
@@ -14,22 +14,22 @@ export class ReanalysisService extends GenericService {
@Validate()
excludePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
- return this._post(body, `exclude-pages/${dossierId}/${fileId}`);
+ return this._post(body, `exclude-pages/${dossierId}/${fileId}`).pipe(switchMap(() => this._filesService.reload(dossierId, fileId)));
}
@Validate()
includePages(@RequiredParam() body: IPageExclusionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
- return this._post(body, `include-pages/${dossierId}/${fileId}`);
+ return this._post(body, `include-pages/${dossierId}/${fileId}`).pipe(switchMap(() => this._filesService.reload(dossierId, fileId)));
}
@Validate()
- reanalyzeFilesForDossier(@RequiredParam() body: List, @RequiredParam() dossierId: string, force?: boolean) {
+ reanalyzeFilesForDossier(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, force?: boolean) {
const queryParams: QueryParam[] = [];
if (force) {
queryParams.push({ key: 'force', value: force });
}
- return this._post(body, `reanalyze/${dossierId}/bulk`, queryParams);
+ return this._post(fileIds, `reanalyze/${dossierId}/bulk`, queryParams).pipe(switchMap(() => this._filesService.loadAll(dossierId)));
}
@Validate()