add exclude pages service

This commit is contained in:
Dan Percic 2021-11-24 22:36:02 +02:00
parent aa02853d8a
commit 9e562b189a
8 changed files with 114 additions and 76 deletions

View File

@ -1,25 +1,11 @@
<div *ngIf="!showExcludedPages" class="right-title heading" translate="file-preview.tabs.annotations.label">
<div>
<div
(click)="multiSelectActive = true"
*ngIf="!multiSelectActive && !isReadOnly"
class="all-caps-label primary pointer"
iqserHelpMode="bulk-select-annotations"
translate="file-preview.tabs.annotations.select"
></div>
<iqser-popup-filter
[actionsTemplate]="annotationFilterActionTemplate"
[primaryFiltersSlug]="'primaryFilters'"
[secondaryFiltersSlug]="'secondaryFilters'"
iqserHelpMode="workload-filter"
></iqser-popup-filter>
</div>
</div>
<div *ngIf="showExcludedPages" class="right-title heading" translate="file-preview.tabs.exclude-pages.label">
<div
*ngIf="excludedPageService.show$ | async; else selectAndFilter"
class="right-title heading"
translate="file-preview.tabs.exclude-pages.label"
>
<div>
<iqser-circle-button
(action)="toggleViewExcludedPages.emit()"
(action)="excludedPageService.toggle()"
[tooltip]="'file-preview.tabs.exclude-pages.close' | translate"
icon="iqser:close"
tooltipPosition="before"
@ -27,6 +13,26 @@
</div>
</div>
<ng-template #selectAndFilter>
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
<div>
<div
(click)="multiSelectActive = true"
*ngIf="!multiSelectActive && !isReadOnly"
class="all-caps-label primary pointer"
iqserHelpMode="bulk-select-annotations"
translate="file-preview.tabs.annotations.select"
></div>
<iqser-popup-filter
[actionsTemplate]="annotationFilterActionTemplate"
[primaryFiltersSlug]="'primaryFilters'"
[secondaryFiltersSlug]="'secondaryFilters'"
iqserHelpMode="workload-filter"
></iqser-popup-filter>
</div>
</div>
</ng-template>
<div class="right-content">
<div *ngIf="isReadOnly" class="justify-center read-only d-flex">
<div class="flex-center">
@ -62,6 +68,7 @@
tooltipPosition="above"
></redaction-annotation-actions>
</div>
<iqser-circle-button
(action)="multiSelectActive = false"
[type]="circleButtonTypes.primary"
@ -87,6 +94,7 @@
>
<mat-icon svgIcon="red:nav-first"></mat-icon>
</div>
<div class="pages" id="pages">
<redaction-page-indicator
(pageSelected)="pageSelectedByClick($event)"
@ -99,6 +107,7 @@
[viewedPages]="viewedPages"
></redaction-page-indicator>
</div>
<div
(click)="scrollQuickNavLast()"
[class.disabled]="activeViewerPage === file?.numberOfPages"
@ -111,11 +120,11 @@
</div>
<div style="overflow: hidden; width: 100%">
<ng-container *ngIf="!showExcludedPages">
<ng-container *ngIf="(excludedPageService.show$ | async) === false">
<div [attr.anotation-page-header]="activeViewerPage" [class.padding-left-0]="currentPageIsExcluded" class="page-separator">
<span *ngIf="!!activeViewerPage" class="flex-align-items-center">
<iqser-circle-button
(action)="toggleViewExcludedPages.emit()"
(action)="excludedPageService.toggle()"
*ngIf="currentPageIsExcluded"
[tooltip]="'file-preview.excluded-from-redaction' | translate | capitalize"
icon="red:exclude-pages"
@ -162,13 +171,14 @@
<ng-container *ngIf="currentPageIsExcluded">
{{ 'file-preview.tabs.annotations.page-is' | translate }}
<a
(click)="toggleViewExcludedPages.emit()"
(click)="excludedPageService.toggle()"
class="with-underline"
translate="file-preview.excluded-from-redaction"
></a
>.
</ng-container>
</iqser-empty-state>
<div *ngIf="displayedPages.length" class="no-annotations-buttons-container mt-32">
<iqser-icon-button
(action)="jumpToPreviousWithAnnotations()"
@ -177,6 +187,7 @@
[type]="iconButtonTypes.dark"
icon="red:nav-prev"
></iqser-icon-button>
<iqser-icon-button
(action)="jumpToNextWithAnnotations()"
[disabled]="activeViewerPage >= displayedPages[displayedPages.length - 1]"
@ -204,11 +215,7 @@
</div>
</ng-container>
<redaction-page-exclusion
(excludePages)="excludePages.emit()"
*ngIf="showExcludedPages"
[file]="file"
></redaction-page-exclusion>
<redaction-page-exclusion *ngIf="excludedPageService.show$ | async" [file]="file"></redaction-page-exclusion>
</div>
</div>
</div>

View File

@ -20,6 +20,7 @@ import { WebViewerInstance } from '@pdftron/webviewer';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { File, IViewedPage } from '@red/domain';
import { ExcludedPagesService } from '../../screens/file-preview-screen/services/excluded-pages.service';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
@ -42,7 +43,6 @@ export class FileWorkloadComponent {
@Input() viewedPages: IViewedPage[];
@Input() @Required() file!: File;
@Input() hideSkipped: boolean;
@Input() showExcludedPages: boolean;
@Input() annotationActionsTemplate: TemplateRef<unknown>;
@Input() viewer: WebViewerInstance;
@Output() readonly shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
@ -53,16 +53,15 @@ export class FileWorkloadComponent {
@Output() readonly selectPage = new EventEmitter<number>();
@Output() readonly toggleSkipped = new EventEmitter<MouseEvent>();
@Output() readonly annotationsChanged = new EventEmitter<AnnotationWrapper>();
@Output() readonly excludePages = new EventEmitter<void>();
displayedPages: number[] = [];
pagesPanelActive = true;
readonly displayedAnnotations$: Observable<Map<number, AnnotationWrapper[]>>;
@Output() @Required() readonly toggleViewExcludedPages = new EventEmitter<void>();
private _annotations$ = new BehaviorSubject<AnnotationWrapper[]>([]);
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
constructor(
readonly excludedPageService: ExcludedPagesService,
private readonly _permissionsService: PermissionsService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _filterService: FilterService,

View File

@ -1,9 +1,10 @@
import { Component, EventEmitter, Input, OnChanges, Output, ViewChild } from '@angular/core';
import { Component, Input, OnChanges, ViewChild } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { InputWithActionComponent, LoadingService, Toaster } from '@iqser/common-ui';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { File, IPageRange } from '@red/domain';
import { ReanalysisService } from '@services/reanalysis.service';
import { ExcludedPagesService } from '../../screens/file-preview-screen/services/excluded-pages.service';
@Component({
selector: 'redaction-page-exclusion',
@ -12,13 +13,13 @@ import { ReanalysisService } from '@services/reanalysis.service';
})
export class PageExclusionComponent implements OnChanges {
@Input() file: File;
@Output() readonly excludePages = new EventEmitter<void>();
excludedPagesRanges: IPageRange[] = [];
@ViewChild(InputWithActionComponent) private readonly _inputComponent: InputWithActionComponent;
constructor(
readonly permissionsService: PermissionsService,
readonly excludedPagesService: ExcludedPagesService,
private readonly _reanalysisService: ReanalysisService,
private readonly _toaster: Toaster,
private readonly _loadingService: LoadingService,
@ -66,7 +67,7 @@ export class PageExclusionComponent implements OnChanges {
)
.toPromise();
this._inputComponent.reset();
this.excludePages.emit();
this.excludedPagesService.toggle();
} catch (e) {
this._toaster.error(_('file-preview.tabs.exclude-pages.error'));
this._loadingService.stop();
@ -85,6 +86,6 @@ export class PageExclusionComponent implements OnChanges {
)
.toPromise();
this._inputComponent.reset();
this.excludePages.emit();
this.excludedPagesService.toggle();
}
}

View File

@ -94,9 +94,7 @@
#fileActions
(ocredFile)="ocredFile()"
(toggleViewDocumentInfo)="toggleViewDocumentInfo()"
(toggleViewExcludedPages)="toggleViewExcludedPages()"
[activeDocumentInfo]="viewDocumentInfo"
[activeExcludePages]="showExcludedPages"
[file]="file"
type="file-preview"
></redaction-file-actions>
@ -157,7 +155,7 @@
<div class="right-container">
<iqser-empty-state
*ngIf="file.excluded && !viewDocumentInfo && !showExcludedPages"
*ngIf="file.excluded && !viewDocumentInfo && (excludedPagesService.show$ | async) === false"
[horizontalPadding]="40"
[text]="'file-preview.tabs.is-excluded' | translate"
icon="red:needs-work"
@ -173,11 +171,9 @@
#fileWorkloadComponent
(annotationsChanged)="annotationsChangedByReviewAction($event)"
(deselectAnnotations)="deselectAnnotations($event)"
(excludePages)="excludePages()"
(selectAnnotations)="selectAnnotations($event)"
(selectPage)="selectPage($event)"
(toggleSkipped)="toggleSkipped($event)"
(toggleViewExcludedPages)="toggleViewExcludedPages()"
*ngIf="!file.excluded"
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
[activeViewerPage]="activeViewerPage"
@ -187,7 +183,6 @@
[file]="file"
[hideSkipped]="hideSkipped"
[selectedAnnotations]="selectedAnnotations"
[showExcludedPages]="showExcludedPages"
[viewedPages]="fileData?.viewedPages"
[viewer]="activeViewer"
></redaction-file-workload>

View File

@ -51,9 +51,10 @@ import { FileActionsComponent } from '../../shared/components/file-actions/file-
import { FilesService } from '@services/entity-services/files.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { filter, switchMapTo, tap } from 'rxjs/operators';
import { filter, switchMap, switchMapTo, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { WatermarkService } from '@shared/services/watermark.service';
import { ExcludedPagesService } from './services/excluded-pages.service';
import Annotation = Core.Annotations.Annotation;
import PDFNet = Core.PDFNet;
@ -62,7 +63,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
@Component({
templateUrl: './file-preview-screen.component.html',
styleUrls: ['./file-preview-screen.component.scss'],
providers: [FilterService],
providers: [FilterService, ExcludedPagesService],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach {
@ -81,11 +82,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
hideSkipped = false;
displayPDFViewer = false;
viewDocumentInfo = false;
showExcludedPages = false;
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
@ViewChild('fileActions') fileActions: FileActionsComponent;
readonly dossierId: string;
readonly dossier$: Observable<Dossier>;
readonly showExcludedPages$: Observable<boolean>;
readonly file$: Observable<File>;
readonly fileId: string;
private _instance: WebViewerInstance;
@ -121,12 +122,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private readonly _translateService: TranslateService,
private readonly _filesMapService: FilesMapService,
private readonly _dossiersService: DossiersService,
readonly excludedPagesService: ExcludedPagesService,
) {
super();
this.dossierId = _activatedRoute.snapshot.paramMap.get('dossierId');
this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId);
this.fileId = _activatedRoute.snapshot.paramMap.get('fileId');
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId).pipe(
tap(async file => {
await this._reloadFile(file);
}),
);
this.showExcludedPages$ = this._showExcludedPages$;
document.documentElement.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
@ -167,6 +174,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return !!this._workloadComponent?.multiSelectActive;
}
private get _showExcludedPages$() {
return this.excludedPagesService.show$.pipe(tap(() => this._disableMultiSelectAndDocumentInfo()));
}
assignTooltip(file: File): string {
return file.isUnderApproval
? this._translateService.instant('dossier-overview.assign-approver')
@ -263,7 +274,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async ngOnInit(): Promise<void> {
this._loadingService.start();
await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId);
await this._loadFileData();
this._updateCanPerformActions();
this._subscribeToFileUpdates();
@ -437,7 +447,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async viewerReady($event: WebViewerInstance) {
this._instance = $event;
await this._stampPDF();
this._cleanupAndRedrawManualAnnotations$();
await this._cleanupAndRedrawManualAnnotations$().toPromise();
this._updateCanPerformActions();
// Go to initial page from query params
@ -462,24 +472,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._reloadFileOnReanalysis = true;
}
async excludePages(): Promise<void> {
this._loadingService.start();
await this._loadFileData(true);
this._cleanupAndRedrawManualAnnotations$();
await this._stampPDF();
this._loadingService.stop();
}
toggleViewExcludedPages(): void {
this.showExcludedPages = !this.showExcludedPages;
this._workloadComponent.multiSelectActive = false;
this.viewDocumentInfo = false;
}
toggleViewDocumentInfo(): void {
this.viewDocumentInfo = !this.viewDocumentInfo;
this._workloadComponent.multiSelectActive = false;
this.showExcludedPages = false;
this.excludedPagesService.hide();
}
async assignToMe(file: File) {
@ -534,6 +530,19 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return false;
}
private async _reloadFile(file: File): Promise<void> {
this._loadingService.start();
await this._loadFileData(file, true);
await this._cleanupAndRedrawManualAnnotations$().toPromise();
await this._stampPDF();
this._loadingService.stop();
}
private _disableMultiSelectAndDocumentInfo(): void {
this._workloadComponent.multiSelectActive = false;
this.viewDocumentInfo = false;
}
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
newAnnotations.map(newAnnotation => {
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
@ -544,6 +553,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
private async _stampPDF() {
if (!this._instance) {
return;
}
const pdfNet = this._instance.Core.PDFNet;
const document = await this._instance.Core.documentViewer.getDocument().getPDFDoc();
const file = this._filesMapService.get(this.dossierId, this.fileId);
@ -603,11 +615,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
});
this.addSubscription = this._filesMapService.fileReanalysed$
.pipe(filter(file => file.fileId === this.fileId))
.subscribe(async () => {
await this._loadFileData(!this._reloadFileOnReanalysis);
.subscribe(async file => {
await this._loadFileData(file, !this._reloadFileOnReanalysis);
this._reloadFileOnReanalysis = false;
this._loadingService.stop();
this._cleanupAndRedrawManualAnnotations$();
await this._cleanupAndRedrawManualAnnotations$().toPromise();
});
}
@ -619,12 +631,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
!this.viewerComponent?.utils.isCompareMode;
}
private async _loadFileData(performUpdate = false): Promise<void> {
const file = this._filesMapService.get(this.dossierId, this.fileId);
private async _loadFileData(file: File, performUpdate = false): Promise<void> {
const fileData = await this._fileDownloadService.loadDataFor(file).toPromise();
if (!file?.isPending && !file?.isError) {
if (performUpdate) {
if (performUpdate && !!this.fileData) {
this.fileData.redactionLog = fileData.redactionLog;
this.fileData.viewedPages = fileData.viewedPages;
this.rebuildFilters(true);
@ -652,7 +663,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private _cleanupAndRedrawManualAnnotations$() {
return this._fileDownloadService.loadRedactionLogFor(this.dossierId, this.fileId).pipe(
tap(redactionLog => (this.fileData.redactionLog = redactionLog)),
switchMapTo(this._redrawAnnotations()),
switchMap(() => this._redrawAnnotations()),
);
}
@ -682,7 +693,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.fileId,
this.dossierId,
this.hideSkipped,
this.viewerComponent.utils.isCompareMode,
!!this.viewerComponent?.utils?.isCompareMode,
);
}

View File

@ -0,0 +1,25 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { shareDistinctLast } from '@iqser/common-ui';
@Injectable()
export class ExcludedPagesService {
readonly show$: Observable<boolean>;
private readonly _show$ = new BehaviorSubject(false);
constructor() {
this.show$ = this._show$.asObservable().pipe(shareDistinctLast());
}
show() {
this._show$.next(true);
}
hide() {
this._show$.next(false);
}
toggle() {
this._show$.next(!this._show$.value);
}
}

View File

@ -71,9 +71,9 @@
></iqser-circle-button>
<iqser-circle-button
(action)="toggleViewExcludedPages.emit()"
*ngIf="showExcludePages"
[attr.aria-expanded]="activeExcludePages"
(action)="excludedPagesService.toggle()"
*ngIf="excludedPagesService && showExcludePages"
[attr.aria-expanded]="excludedPagesService.show$ | async"
[showDot]="!!file.excludedPages?.length"
[tooltip]="'file-preview.exclude-pages' | translate"
icon="red:exclude-pages"

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, OnDestroy, Optional, Output } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { File } from '@red/domain';
import { AppStateService } from '@state/app-state.service';
@ -23,6 +23,7 @@ import { FileManagementService } from '@services/entity-services/file-management
import { FilesService } from '@services/entity-services/files.service';
import { ReanalysisService } from '@services/reanalysis.service';
import { Router } from '@angular/router';
import { ExcludedPagesService } from '../../../screens/file-preview-screen/services/excluded-pages.service';
@Component({
selector: 'redaction-file-actions',
@ -36,7 +37,6 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
@Input() file: File;
@Input() activeDocumentInfo: boolean;
@Input() activeExcludePages: boolean;
@Input() @Required() type: 'file-preview' | 'dossier-overview-list' | 'dossier-overview-workflow';
@Output() readonly ocredFile = new EventEmitter<void>();
@ -65,12 +65,12 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy,
isFilePreview = false;
tooltipPosition: IqserTooltipPosition;
@Output() readonly toggleViewDocumentInfo = new EventEmitter<void>();
@Output() readonly toggleViewExcludedPages = new EventEmitter<void>();
constructor(
readonly permissionsService: PermissionsService,
readonly appStateService: AppStateService,
readonly dossiersService: DossiersService,
@Optional() readonly excludedPagesService: ExcludedPagesService,
private readonly _dialogService: DossiersDialogService,
private readonly _fileAssignService: FileAssignService,
private readonly _loadingService: LoadingService,