diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html index 8a063381a..7111f4d66 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.html @@ -19,6 +19,7 @@ iqserHelpMode="bulk-select-annotations" translate="file-preview.tabs.annotations.select" > + @@ -118,99 +119,103 @@
- -
- - +
+ + - - {{ activeViewerPage }} - - {{ activeAnnotations?.length || 0 }} - - + + {{ activeViewerPage }} - + {{ activeAnnotations?.length || 0 }} + + -
-
-
+
+
+
+
+
+ +
+ + + + {{ 'file-preview.tabs.annotations.page-is' | translate }} + . + + + +
+ + +
-
+ -
- - - - {{ 'file-preview.tabs.annotations.page-is' | translate }} - . - - - -
- - - -
-
- - -
- + +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.scss b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.scss index 64f58538a..78609b428 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.scss +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.scss @@ -151,3 +151,7 @@ ::ng-deep .page-separator iqser-circle-button mat-icon { color: var(--iqser-primary); } + +[hidden] { + display: none !important; +} diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts index 999da819d..0607b4055 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/file-workload/file-workload.component.ts @@ -21,7 +21,6 @@ import { IconButtonTypes, INestedFilter, IqserEventTarget, - Required, shareDistinctLast, shareLast, } from '@iqser/common-ui'; @@ -38,7 +37,7 @@ const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'E const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @Component({ - selector: 'redaction-file-workload', + selector: 'redaction-file-workload [file]', templateUrl: './file-workload.component.html', styleUrls: ['./file-workload.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, @@ -53,7 +52,7 @@ export class FileWorkloadComponent { @Input() shouldDeselectAnnotationsOnPageChange: boolean; @Input() dialogRef: MatDialogRef; @Input() viewedPages: IViewedPage[]; - @Input() @Required() file!: File; + @Input() file!: File; @Input() hideSkipped: boolean; @Input() annotationActionsTemplate: TemplateRef; @Input() viewer: WebViewerInstance; @@ -161,7 +160,7 @@ export class FileWorkloadComponent { } } - hasOnlyManualRedactionsAndNotExcluded(pageNumber: number): boolean { + hasOnlyManualRedactionsAndIsExcluded(pageNumber: number): boolean { const hasOnlyManualRedactions = this.displayedAnnotations.get(pageNumber).every(annotation => annotation.manual); return hasOnlyManualRedactions && this.file.excludedPages.includes(pageNumber); } diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts index 58dc2bc0c..ba2eb3b72 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-exclusion/page-exclusion.component.ts @@ -67,7 +67,6 @@ export class PageExclusionComponent implements OnChanges { ) .toPromise(); this._inputComponent.reset(); - this.excludedPagesService.toggle(); } catch (e) { this._toaster.error(_('file-preview.tabs.exclude-pages.error')); } @@ -86,7 +85,6 @@ export class PageExclusionComponent implements OnChanges { ) .toPromise(); this._inputComponent.reset(); - this.excludedPagesService.toggle(); this._loadingService.stop(); } } diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts index 2ae2b8f06..dc417f73d 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/page-indicator/page-indicator.component.ts @@ -14,12 +14,8 @@ import { FilesMapService } from '@services/entity-services/files-map.service'; changeDetection: ChangeDetectionStrategy.OnPush, }) export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy, OnChanges { - @Input() - file: File; - - @Input() - active = false; - + @Input() file: File; + @Input() active = false; @Input() showDottedIcon = false; @Input() number: number; @Input() viewedPages: IViewedPage[]; @@ -84,23 +80,6 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy } } - // @HostListener('window:keydown', ['$event']) - // handleKeyDown(event: KeyboardEvent) { - // if (this.canMarkPagesAsViewed) { - // if (this.active && event.key === ' ') { - // if ( - // document.activeElement && - // document.activeElement.className.indexOf('activePanel') >= 0 - // ) { - // this.toggleReadState(); - // event.stopPropagation(); - // event.preventDefault(); - // return false; - // } - // } - // } - // } - private async _markPageRead() { await this._viewedPagesService.addPage({ page: this.number }, this.file.dossierId, this.file.fileId).toPromise(); if (this.activePage) { diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts index daa248d77..059d44dc7 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts @@ -284,7 +284,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { this._selectedText = selectedText; const textActions = [dataElements.ADD_DICTIONARY, dataElements.ADD_FALSE_POSITIVE]; - if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded) { + if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded(this.file)) { this.instance.UI.enableElements(textActions); } else { this.instance.UI.disableElements(textActions); @@ -588,7 +588,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { ANNOTATION_POPUP, ]; - if (this.canPerformActions && !this.utils.isCurrentPageExcluded) { + if (this.canPerformActions && !this.utils.isCurrentPageExcluded(this.file)) { try { this.instance.UI.enableTools(['AnnotationCreateRectangle']); } catch (e) { @@ -605,7 +605,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { let elementsToDisable = [...elements, ADD_RECTANGLE]; - if (this.utils.isCurrentPageExcluded) { + if (this.utils.isCurrentPageExcluded(this.file)) { const allowedActionsWhenPageExcluded: string[] = [ANNOTATION_POPUP, ADD_RECTANGLE, ADD_REDACTION, SHAPE_TOOL_GROUP_BUTTON]; elementsToDisable = elementsToDisable.filter(element => !allowedActionsWhenPageExcluded.includes(element)); } else { 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 ec3849327..16976197e 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 @@ -330,7 +330,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni .map(p => p.page) .filter((item, pos, self) => self.indexOf(item) === pos); for (const page of distinctPages) { - await this._cleanupAndRedrawManualAnnotationsForEntirePage(page); + await this._reloadAnnotationsForPage(page); } } }, @@ -398,7 +398,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni this._instance = $event; this.ready = true; await this._stampPDF(); - await this._cleanupAndRedrawManualAnnotations(); + await this._reloadAnnotations(); this._setExcludedPageStyles(); this._instance.Core.documentViewer.addEventListener('pageComplete', () => { @@ -419,7 +419,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } async annotationsChangedByReviewAction(annotation: AnnotationWrapper) { - await this._cleanupAndRedrawManualAnnotationsForEntirePage(annotation?.pageNumber || this.activeViewerPage); + await this._reloadAnnotationsForPage(annotation?.pageNumber || this.activeViewerPage); } ocredFile(): void { @@ -548,27 +548,32 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni await this._loadFileData(file, !this._reloadFileOnReanalysis); this._reloadFileOnReanalysis = false; this._loadingService.stop(); - await this._cleanupAndRedrawManualAnnotations(); + await this._reloadAnnotations(); }); } - private async _loadFileData(file: File, performUpdate = false): Promise { + private async _loadFileData(file: File, performUpdate = false): Promise { + if (!file || file.isError) { + return this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]); + } + const fileData = await this._fileDownloadService.loadDataFor(file).toPromise(); - if (!file?.isPending && !file?.isError) { + if (!file.isPending) { + let excludedOrIncludedPages = new Set(); + let currentPageAnnotations: AnnotationWrapper[] = []; + if (performUpdate && !!this.fileData) { this.fileData.redactionLog = fileData.redactionLog; this.fileData.viewedPages = fileData.viewedPages; + excludedOrIncludedPages = new Set([...this.fileData.file.excludedPages, ...file.excludedPages]); + currentPageAnnotations = this.annotations.filter(a => excludedOrIncludedPages.has(a.pageNumber)); + this.fileData.file = file; } else { this.fileData = fileData; } - this.rebuildFilters(); - return; - } - - if (file.isError) { - await this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]); + return this._cleanupAndRedrawAnnotations(currentPageAnnotations, a => excludedOrIncludedPages.has(a.pageNumber)); } } @@ -578,36 +583,32 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni this._workloadComponent?.scrollAnnotations(); } - private async _cleanupAndRedrawManualAnnotations() { - const currentAnnotations = this.annotations; + private async _reloadAnnotations() { this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise(); - this.rebuildFilters(); - - if (this.viewModeService.viewMode === 'STANDARD') { - currentAnnotations.forEach(annotation => { - this._findAndDeleteAnnotation(annotation.id); - }); - const newAnnotations = this.annotations; - this._handleDeltaAnnotationFilters(currentAnnotations, newAnnotations); - await this._redrawAnnotations(newAnnotations); - } + await this._cleanupAndRedrawAnnotations(this.annotations); } - private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) { + private async _reloadAnnotationsForPage(page: number) { const currentPageAnnotations = this.annotations.filter(a => a.pageNumber === page); - const currentPageAnnotationIds = currentPageAnnotations.map(a => a.id); await this._filesService.reload(this.dossierId, this.fileId).toPromise(); this.fileData.redactionLog = await this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise(); + await this._cleanupAndRedrawAnnotations(currentPageAnnotations, annotation => annotation.pageNumber === page); + } + + private async _cleanupAndRedrawAnnotations( + annotationsToDelete: AnnotationWrapper[], + newAnnotationsFilter?: (annotation: AnnotationWrapper) => boolean, + ) { this.rebuildFilters(); if (this.viewModeService.viewMode === 'STANDARD') { - currentPageAnnotationIds.forEach(id => { - this._findAndDeleteAnnotation(id); + annotationsToDelete?.forEach(annotation => { + this._findAndDeleteAnnotation(annotation.id); }); - const newPageAnnotations = this.annotations.filter(item => item.pageNumber === page); - this._handleDeltaAnnotationFilters(currentPageAnnotations, newPageAnnotations); + const newPageAnnotations = newAnnotationsFilter ? this.annotations.filter(newAnnotationsFilter) : this.annotations; + this._handleDeltaAnnotationFilters(annotationsToDelete ?? [], newPageAnnotations); await this._redrawAnnotations(newPageAnnotations); } } diff --git a/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts b/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts index 42bcbf6d6..5b29b9c07 100644 --- a/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts +++ b/apps/red-ui/src/app/modules/dossier/utils/pdf-viewer.utils.ts @@ -2,6 +2,7 @@ import { translateQuads } from '@utils/pdf-coordinates'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { Core, WebViewerInstance } from '@pdftron/webviewer'; import { ViewModeService } from '../screens/file-preview-screen/services/view-mode.service'; +import { File } from '@red/domain'; import Annotation = Core.Annotations.Annotation; const DISABLED_HOTKEYS = [ @@ -39,7 +40,6 @@ const DISABLED_HOTKEYS = [ export class PdfViewerUtils { ready = false; - excludedPages: number[] = []; constructor(readonly instance: WebViewerInstance, readonly viewModeService: ViewModeService) {} @@ -47,11 +47,6 @@ export class PdfViewerUtils { return this.viewModeService.isCompare ? 2 : 1; } - get isCurrentPageExcluded() { - const currentPage = this.currentPage; - return !!this.excludedPages?.includes(currentPage); - } - get currentPage() { try { return this.viewModeService.isCompare ? Math.ceil(this._currentInternalPage / 2) : this._currentInternalPage; @@ -88,6 +83,11 @@ export class PdfViewerUtils { return this.instance?.Core.documentViewer?.getPageCount(); } + isCurrentPageExcluded(file: File) { + const currentPage = this.currentPage; + return !!file?.excludedPages?.includes(currentPage); + } + navigateToPage(pageNumber: string | number) { const parsedNumber = typeof pageNumber === 'string' ? parseInt(pageNumber, 10) : pageNumber; this._navigateToPage(this.paginationOffset === 2 ? parsedNumber * this.paginationOffset - 1 : parsedNumber); 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 485854529..258c93870 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 @@ -42,9 +42,9 @@ export class FilesService extends EntitiesService { } @Validate() - setUnderApprovalFor(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, asigneeId: string) { + setUnderApprovalFor(@RequiredParam() fileIds: List, @RequiredParam() dossierId: string, assigneeId: string) { const url = `${this._defaultModelPath}/under-approval/${dossierId}/bulk`; - return this._post(fileIds, url, [{ key: 'assigneeId', value: asigneeId }]).pipe(switchMap(() => this.loadAll(dossierId))); + return this._post(fileIds, url, [{ key: 'assigneeId', value: assigneeId }]).pipe(switchMap(() => this.loadAll(dossierId))); } /**