RED-3988: improve annotation manager

This commit is contained in:
Dan Percic 2022-05-20 23:57:03 +03:00
parent 08c9de05de
commit a989708d08
15 changed files with 152 additions and 131 deletions

View File

@ -59,7 +59,7 @@ export class AnnotationActionsComponent implements OnChanges {
} }
get viewerAnnotations() { get viewerAnnotations() {
return this._annotationManager.getAnnotations(this._annotations); return this._annotationManager.get(this._annotations);
} }
get isVisible() { get isVisible() {
@ -94,15 +94,15 @@ export class AnnotationActionsComponent implements OnChanges {
hideAnnotation($event: MouseEvent) { hideAnnotation($event: MouseEvent) {
$event.stopPropagation(); $event.stopPropagation();
this._annotationManager.hideAnnotations(this.viewerAnnotations); this._annotationManager.hide(this.viewerAnnotations);
this._annotationManager.deselectAnnotations(); this._annotationManager.deselect();
this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, true); this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, true);
} }
showAnnotation($event: MouseEvent) { showAnnotation($event: MouseEvent) {
$event.stopPropagation(); $event.stopPropagation();
this._annotationManager.showAnnotations(this.viewerAnnotations); this._annotationManager.show(this.viewerAnnotations);
this._annotationManager.deselectAnnotations(); this._annotationManager.deselect();
this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, false); this._fileDataService.updateHiddenAnnotations(this.viewerAnnotations, false);
} }

View File

@ -70,7 +70,7 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
this.pagesPanelActive.emit(false); this.pagesPanelActive.emit(false);
if (this._listingService.isSelected(annotation)) { if (this._listingService.isSelected(annotation)) {
this._annotationManager.deselectAnnotation(annotation); this._annotationManager.deselect(annotation);
} else { } else {
const canMultiSelect = this._multiSelectService.isEnabled; const canMultiSelect = this._multiSelectService.isEnabled;
if (canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this._listingService.selected.length > 0) { if (canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this._listingService.selected.length > 0) {

View File

@ -48,7 +48,7 @@
<div *ngIf="multiSelectService.active$ | async" class="multi-select"> <div *ngIf="multiSelectService.active$ | async" class="multi-select">
<div class="selected-wrapper"> <div class="selected-wrapper">
<iqser-round-checkbox <iqser-round-checkbox
(click)="annotationManager.deselectAnnotations()" (click)="annotationManager.deselect()"
[indeterminate]="listingService.areSomeSelected$ | async" [indeterminate]="listingService.areSomeSelected$ | async"
type="with-bg" type="with-bg"
></iqser-round-checkbox> ></iqser-round-checkbox>

View File

@ -136,7 +136,7 @@ export class FileWorkloadComponent {
return this.multiSelectService.inactive$.pipe( return this.multiSelectService.inactive$.pipe(
tap(value => { tap(value => {
if (value) { if (value) {
this.annotationManager.deselectAnnotations(); this.annotationManager.deselect();
} }
}), }),
shareDistinctLast(), shareDistinctLast(),
@ -185,7 +185,7 @@ export class FileWorkloadComponent {
} }
deselectAllOnActivePage(): void { deselectAllOnActivePage(): void {
this.annotationManager.deselectAnnotations(this.activeAnnotations); this.annotationManager.deselect(this.activeAnnotations);
} }
@HostListener('window:keyup', ['$event']) @HostListener('window:keyup', ['$event'])

View File

@ -190,13 +190,13 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
if (action === 'deselected') { if (action === 'deselected') {
// Remove deselected annotations from selected list // Remove deselected annotations from selected list
nextAnnotations = this._annotationManager.selectedAnnotations.filter(ann => !annotations.some(a => a.Id === ann.Id)); nextAnnotations = this._annotationManager.selected.filter(ann => !annotations.some(a => a.Id === ann.Id));
} else if (!this._multiSelectService.isEnabled) { } else if (!this._multiSelectService.isEnabled) {
// Only choose the last selected annotation, to bypass viewer multi select // Only choose the last selected annotation, to bypass viewer multi select
nextAnnotations = annotations; nextAnnotations = annotations;
} else { } else {
// Get selected annotations from the manager, no intervention needed // Get selected annotations from the manager, no intervention needed
nextAnnotations = this._annotationManager.selectedAnnotations; nextAnnotations = this._annotationManager.selected;
} }
// this.annotationSelected.emit(nextAnnotations.map(ann => ann.Id)); // this.annotationSelected.emit(nextAnnotations.map(ann => ann.Id));
@ -207,7 +207,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
if (!this._multiSelectService.isEnabled) { if (!this._multiSelectService.isEnabled) {
const notSelected = this._fileDataService.all.filter(wrapper => !nextAnnotations.some(ann => ann.Id === wrapper.id)); const notSelected = this._fileDataService.all.filter(wrapper => !nextAnnotations.some(ann => ann.Id === wrapper.id));
this._annotationManager.deselectAnnotations(notSelected); this._annotationManager.deselect(notSelected);
} }
this.#configureAnnotationSpecificActions(annotations); this.#configureAnnotationSpecificActions(annotations);
@ -286,11 +286,11 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
onClick: () => { onClick: () => {
this._ngZone.run(() => { this._ngZone.run(() => {
if (allAreVisible) { if (allAreVisible) {
this._annotationManager.hideAnnotations(viewerAnnotations); this._annotationManager.hide(viewerAnnotations);
} else { } else {
this._annotationManager.showAnnotations(viewerAnnotations); this._annotationManager.show(viewerAnnotations);
} }
this._annotationManager.deselectAnnotations(); this._annotationManager.deselect();
this._fileDataService.updateHiddenAnnotations(viewerAnnotations, allAreVisible); this._fileDataService.updateHiddenAnnotations(viewerAnnotations, allAreVisible);
}); });
}, },
@ -317,7 +317,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
} }
private _addRectangleManualRedaction() { private _addRectangleManualRedaction() {
const activeAnnotation = this._annotationManager.selectedAnnotations[0]; const activeAnnotation = this._annotationManager.selected[0];
const activePage = activeAnnotation.getPageNumber(); const activePage = activeAnnotation.getPageNumber();
const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation)]; const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation)];
const manualRedactionEntry = this._getManualRedaction({ [activePage]: quads }); const manualRedactionEntry = this._getManualRedaction({ [activePage]: quads });

View File

@ -120,7 +120,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
get changed() { get changed() {
return this._pageRotationService.hasRotations(); return this._pageRotationService.hasRotations;
} }
get scrollableParentView(): ScrollableParentView { get scrollableParentView(): ScrollableParentView {
@ -147,7 +147,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
async updateViewMode(): Promise<void> { async updateViewMode(): Promise<void> {
this._logger.info(`[PDF] Update ${this._viewModeService.viewMode} view mode`); this._logger.info(`[PDF] Update ${this._viewModeService.viewMode} view mode`);
const annotations = this._annotationManager.getAnnotations(a => Boolean(a.getCustomData('redact-manager'))); const annotations = this._annotationManager.get(a => Boolean(a.getCustomData('redact-manager')));
const redactions = annotations.filter(a => a.getCustomData('redaction')); const redactions = annotations.filter(a => a.getCustomData('redaction'));
switch (this._viewModeService.viewMode) { switch (this._viewModeService.viewMode) {
@ -160,8 +160,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
.filter(a => !ocrAnnotationIds.includes(a.Id)); .filter(a => !ocrAnnotationIds.includes(a.Id));
const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true'); const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true');
this._setAnnotationsOpacity(standardEntries, true); this._setAnnotationsOpacity(standardEntries, true);
this._annotationManager.showAnnotations(standardEntries); this._annotationManager.show(standardEntries);
this._annotationManager.hideAnnotations(nonStandardEntries); this._annotationManager.hide(nonStandardEntries);
break; break;
} }
case 'DELTA': { case 'DELTA': {
@ -169,21 +169,21 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const nonChangeLogEntries = annotations.filter(a => a.getCustomData('changeLog') === 'false'); const nonChangeLogEntries = annotations.filter(a => a.getCustomData('changeLog') === 'false');
this._setAnnotationsColor(redactions, 'annotationColor'); this._setAnnotationsColor(redactions, 'annotationColor');
this._setAnnotationsOpacity(changeLogEntries, true); this._setAnnotationsOpacity(changeLogEntries, true);
this._annotationManager.showAnnotations(changeLogEntries); this._annotationManager.show(changeLogEntries);
this._annotationManager.hideAnnotations(nonChangeLogEntries); this._annotationManager.hide(nonChangeLogEntries);
break; break;
} }
case 'REDACTED': { case 'REDACTED': {
const nonRedactionEntries = annotations.filter(a => a.getCustomData('redaction') === 'false'); const nonRedactionEntries = annotations.filter(a => a.getCustomData('redaction') === 'false');
this._setAnnotationsOpacity(redactions); this._setAnnotationsOpacity(redactions);
this._setAnnotationsColor(redactions, 'redactionColor'); this._setAnnotationsColor(redactions, 'redactionColor');
this._annotationManager.showAnnotations(redactions); this._annotationManager.show(redactions);
this._annotationManager.hideAnnotations(nonRedactionEntries); this._annotationManager.hide(nonRedactionEntries);
break; break;
} }
case 'TEXT_HIGHLIGHTS': { case 'TEXT_HIGHLIGHTS': {
this._loadingService.start(); this._loadingService.start();
this._annotationManager.hideAnnotations(annotations); this._annotationManager.hide(annotations);
const highlights = await this._fileDataService.loadTextHighlights(); const highlights = await this._fileDataService.loadTextHighlights();
await this._annotationDrawService.draw(highlights); await this._annotationDrawService.draw(highlights);
this._loadingService.stop(); this._loadingService.stop();
@ -253,9 +253,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
null, null,
{ manualRedactionEntryWrapper, dossierId: this.dossierId, file }, { manualRedactionEntryWrapper, dossierId: this.dossierId, file },
(wrappers: ManualRedactionEntryWrapper[]) => { (wrappers: ManualRedactionEntryWrapper[]) => {
const selectedAnnotations = this._annotationManager.selectedAnnotations; const selectedAnnotations = this._annotationManager.selected;
if (selectedAnnotations.length > 0) { if (selectedAnnotations.length > 0) {
this._annotationManager.deleteAnnotations([selectedAnnotations[0].Id]); this._annotationManager.delete([selectedAnnotations[0].Id]);
} }
const manualRedactionService = this._injector.get(ManualRedactionService); const manualRedactionService = this._injector.get(ManualRedactionService);
const add$ = manualRedactionService.addAnnotation( const add$ = manualRedactionService.addAnnotation(
@ -380,7 +380,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
this._logger.info('[ANNOTATIONS] To delete: ', annotationsToDelete); this._logger.info('[ANNOTATIONS] To delete: ', annotationsToDelete);
this._annotationManager.deleteAnnotations(annotationsToDelete); this._annotationManager.delete(annotationsToDelete);
} }
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) { drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
@ -400,7 +400,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw); this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw);
this._annotationManager.deleteAnnotations(annotationsToDraw); this._annotationManager.delete(annotationsToDraw);
return this._cleanupAndRedrawAnnotations(annotationsToDraw); return this._cleanupAndRedrawAnnotations(annotationsToDraw);
} }
@ -476,7 +476,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
#deactivateMultiSelect() { #deactivateMultiSelect() {
this.multiSelectService.deactivate(); this.multiSelectService.deactivate();
this._annotationManager.deselectAnnotations(); this._annotationManager.deselect();
this.handleAnnotationSelected([]); this.handleAnnotationSelected([]);
} }

View File

@ -397,9 +397,9 @@ export class AnnotationActionsService {
annotationWrapper.resizing = true; annotationWrapper.resizing = true;
const viewerAnnotation = this._annotationManager.getAnnotation(annotationWrapper); const viewerAnnotation = this._annotationManager.get(annotationWrapper);
if (annotationWrapper.rectangle || annotationWrapper.imported || annotationWrapper.isImage) { if (annotationWrapper.rectangle || annotationWrapper.imported || annotationWrapper.isImage) {
this._annotationManager.deleteAnnotation(annotationWrapper); this._annotationManager.delete(annotationWrapper);
const rectangleAnnotation = this.#generateRectangle(annotationWrapper); const rectangleAnnotation = this.#generateRectangle(annotationWrapper);
this._pdf.annotationManager.addAnnotation(rectangleAnnotation, { imported: true }); this._pdf.annotationManager.addAnnotation(rectangleAnnotation, { imported: true });
await this._pdf.annotationManager.drawAnnotationsFromList([rectangleAnnotation]); await this._pdf.annotationManager.drawAnnotationsFromList([rectangleAnnotation]);
@ -437,9 +437,9 @@ export class AnnotationActionsService {
annotationWrapper.resizing = false; annotationWrapper.resizing = false;
this._annotationManager.deleteAnnotation(annotationWrapper); this._annotationManager.delete(annotationWrapper);
await this._annotationDrawService.draw([annotationWrapper]); await this._annotationDrawService.draw([annotationWrapper]);
this._annotationManager.deselectAnnotations(); this._annotationManager.deselect();
await this._fileDataService.annotationsChanged(); await this._fileDataService.annotationsChanged();
} }
@ -529,7 +529,7 @@ export class AnnotationActionsService {
} }
private async _extractTextAndPositions(annotationId: string) { private async _extractTextAndPositions(annotationId: string) {
const viewerAnnotation = this._annotationManager.getAnnotation(annotationId); const viewerAnnotation = this._annotationManager.get(annotationId);
const document = await this._pdf.PDFDoc; const document = await this._pdf.PDFDoc;
const page = await document.getPage(viewerAnnotation.getPageNumber()); const page = await document.getPage(viewerAnnotation.getPageNumber());

View File

@ -28,39 +28,30 @@ export class AnnotationsListingService extends ListingService<AnnotationWrapper>
selectAnnotations(annotations?: AnnotationWrapper[]) { selectAnnotations(annotations?: AnnotationWrapper[]) {
if (!annotations) { if (!annotations) {
return this._annotationManager.deselectAnnotations(); return this._annotationManager.deselect();
} }
const annotationsToSelect = this._multiSelectService.isActive ? [...this.selected, ...annotations] : annotations; const annotationsToSelect = this._multiSelectService.isActive ? [...this.selected, ...annotations] : annotations;
this.#selectAnnotations(annotationsToSelect); this.#selectAnnotations(annotationsToSelect);
} }
#selectAnnotations(annotations: AnnotationWrapper[] = []) { #selectAnnotations(annotations: AnnotationWrapper[]) {
const filteredAnnotationsIds = annotations.filter(a => !!a).map(a => a.id); if (!annotations.length) {
if (!filteredAnnotationsIds.length) {
return; return;
} }
if (!this._multiSelectService.isActive) { if (!this._multiSelectService.isActive) {
this._annotationManager.deselectAnnotations(); this._annotationManager.deselect();
} }
const pageNumber = annotations[0].pageNumber; const pageNumber = annotations[0].pageNumber;
if (pageNumber === this._pdf.currentPage) { if (pageNumber === this._pdf.currentPage) {
return this.#jumpAndSelectAnnotations(filteredAnnotationsIds); return this._annotationManager.jumpAndSelect(annotations);
} }
this._pdf.navigateTo(pageNumber); this._pdf.navigateTo(pageNumber);
// wait for page to be loaded and to draw annotations // wait for page to be loaded and to draw annotations
setTimeout(() => this.#jumpAndSelectAnnotations(filteredAnnotationsIds), 300); setTimeout(() => this._annotationManager.jumpAndSelect(annotations), 300);
}
#jumpAndSelectAnnotations(annotationIds: readonly string[]) {
const annotationsFromViewer = this._annotationManager.getAnnotations(annotationIds);
this._pdf.annotationManager.jumpToAnnotation(annotationsFromViewer[0]);
this._pdf.annotationManager.selectAnnotations(annotationsFromViewer);
} }
} }

View File

@ -28,11 +28,11 @@ export class SkippedService {
} }
private _handleIgnoreAnnotationsDrawing(hideSkipped: boolean): void { private _handleIgnoreAnnotationsDrawing(hideSkipped: boolean): void {
const ignored = this._annotationManager.getAnnotations(a => Boolean(a.getCustomData('skipped'))); const ignored = this._annotationManager.get(a => Boolean(a.getCustomData('skipped')));
if (hideSkipped) { if (hideSkipped) {
this._annotationManager.hideAnnotations(ignored); this._annotationManager.hide(ignored);
} else { } else {
this._annotationManager.showAnnotations(ignored); this._annotationManager.show(ignored);
} }
} }
} }

View File

@ -1,4 +1,14 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnChanges, Optional, ViewChild } from '@angular/core'; import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
HostBinding,
Injector,
Input,
OnChanges,
Optional,
ViewChild,
} from '@angular/core';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { Action, ActionTypes, Dossier, File } from '@red/domain'; import { Action, ActionTypes, Dossier, File } from '@red/domain';
import { DossiersDialogService } from '../../services/dossiers-dialog.service'; import { DossiersDialogService } from '../../services/dossiers-dialog.service';
@ -36,7 +46,7 @@ import { ROTATION_ACTION_BUTTONS } from '../../../shared/components/pdf-viewer/c
}) })
export class FileActionsComponent implements OnChanges { export class FileActionsComponent implements OnChanges {
readonly circleButtonTypes = CircleButtonTypes; readonly circleButtonTypes = CircleButtonTypes;
readonly currentUser = this._userService.currentUser; readonly currentUser;
@Input() file: File; @Input() file: File;
@Input() dossier: Dossier; @Input() dossier: Dossier;
@ -77,24 +87,25 @@ export class FileActionsComponent implements OnChanges {
private readonly _expandableActionsComponent: ExpandableFileActionsComponent; private readonly _expandableActionsComponent: ExpandableFileActionsComponent;
constructor( constructor(
@Optional() private readonly _excludedPagesService: ExcludedPagesService, userService: UserService,
@Optional() private readonly _documentInfoService: DocumentInfoService, private readonly _injector: Injector,
private readonly _pageRotationService: PageRotationService, private readonly _filesService: FilesService,
private readonly _viewerHeaderService: ViewerHeaderService, private readonly _changeRef: ChangeDetectorRef,
private readonly _permissionsService: PermissionsService, private readonly _loadingService: LoadingService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _dialogService: DossiersDialogService, private readonly _dialogService: DossiersDialogService,
private readonly _fileAssignService: FileAssignService, private readonly _fileAssignService: FileAssignService,
private readonly _loadingService: LoadingService,
private readonly _fileManagementService: FileManagementService,
private readonly _filesService: FilesService,
private readonly _userService: UserService,
private readonly _toaster: Toaster,
private readonly _userPreferenceService: UserPreferenceService,
private readonly _reanalysisService: ReanalysisService, private readonly _reanalysisService: ReanalysisService,
private readonly _router: Router, private readonly _permissionsService: PermissionsService,
private readonly _changeRef: ChangeDetectorRef, private readonly _pageRotationService: PageRotationService,
) {} private readonly _viewerHeaderService: ViewerHeaderService,
private readonly _activeDossiersService: ActiveDossiersService,
private readonly _fileManagementService: FileManagementService,
private readonly _userPreferenceService: UserPreferenceService,
@Optional() private readonly _documentInfoService: DocumentInfoService,
@Optional() private readonly _excludedPagesService: ExcludedPagesService,
) {
this.currentUser = userService.currentUser;
}
@HostBinding('class.keep-visible') @HostBinding('class.keep-visible')
get expanded() { get expanded() {
@ -312,9 +323,9 @@ export class FileActionsComponent implements OnChanges {
try { try {
const dossier = this._activeDossiersService.find(this.file.dossierId); const dossier = this._activeDossiersService.find(this.file.dossierId);
await firstValueFrom(this._fileManagementService.delete([this.file], this.file.dossierId)); await firstValueFrom(this._fileManagementService.delete([this.file], this.file.dossierId));
await this._router.navigate([dossier.routerLink]); await this._injector.get(Router).navigate([dossier.routerLink]);
} catch (error) { } catch (error) {
this._toaster.error(_('error.http.generic'), { params: error }); this._injector.get(Toaster).error(_('error.http.generic'), { params: error });
} }
this._loadingService.stop(); this._loadingService.stop();
}, },
@ -363,10 +374,12 @@ export class FileActionsComponent implements OnChanges {
return; return;
} }
} }
if (this._pageRotationService) {
await firstValueFrom(this._pageRotationService.showConfirmationDialogIfHasRotations()); const pageRotationService = this._injector.get(PageRotationService);
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS); await firstValueFrom(pageRotationService.showConfirmationDialogIfHasRotations());
} const viewerHeaderService = this._injector.get(ViewerHeaderService);
viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
this._loadingService.start(); this._loadingService.start();
await firstValueFrom(this._reanalysisService.ocrFiles([this.file], this.file.dossierId)); await firstValueFrom(this._reanalysisService.ocrFiles([this.file], this.file.dossierId));
this._loadingService.stop(); this._loadingService.stop();

View File

@ -2,10 +2,10 @@ import { Injectable } from '@angular/core';
import { Core } from '@pdftron/webviewer'; import { Core } from '@pdftron/webviewer';
import type { List } from '@iqser/common-ui'; import type { List } from '@iqser/common-ui';
import { AnnotationPredicate, DeleteAnnotationsOptions } from '@shared/components/pdf-viewer/types'; import { AnnotationPredicate, DeleteAnnotationsOptions } from '@shared/components/pdf-viewer/types';
import type { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { fromEvent, Observable } from 'rxjs'; import { fromEvent, Observable } from 'rxjs';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { getIds } from '@shared/components/pdf-viewer/functions'; import { getId, isStringOrWrapper } from '@shared/components/pdf-viewer/functions';
import AnnotationManager = Core.AnnotationManager; import AnnotationManager = Core.AnnotationManager;
import Annotation = Core.Annotations.Annotation; import Annotation = Core.Annotations.Annotation;
@ -16,12 +16,12 @@ export class REDAnnotationManager {
annotationSelected$: Observable<[Annotation[], string]>; annotationSelected$: Observable<[Annotation[], string]>;
#manager: AnnotationManager; #manager: AnnotationManager;
get selectedAnnotations() { get selected() {
return this.#manager.getSelectedAnnotations(); return this.#manager.getSelectedAnnotations();
} }
get annotations() { get annotations() {
return this.#getAnnotations(); return this.#get();
} }
get #annotationSelected$() { get #annotationSelected$() {
@ -35,57 +35,56 @@ export class REDAnnotationManager {
this.#autoSelectRectangleAfterCreation(); this.#autoSelectRectangleAfterCreation();
} }
deleteAnnotation(annotation: AnnotationWrapper | string) { delete(annotations?: List | List<AnnotationWrapper> | string | AnnotationWrapper) {
const id = typeof annotation === 'string' ? annotation : annotation.id; const items = isStringOrWrapper(annotations) ? [this.get(annotations)] : this.get(annotations);
return this.deleteAnnotations([id]);
}
deleteAnnotations(annotations: List<AnnotationWrapper>);
deleteAnnotations(annotationsIds?: List);
deleteAnnotations(annotationsIds?: List<string | AnnotationWrapper>) {
const annotations = this.getAnnotations(getIds(annotationsIds));
const options: DeleteAnnotationsOptions = { force: true }; const options: DeleteAnnotationsOptions = { force: true };
this.#manager.deleteAnnotations(annotations, options); this.#manager.deleteAnnotations(items, options);
} }
getAnnotations(annotations: List<AnnotationWrapper | string>): Annotation[]; get(annotation: AnnotationWrapper | string): Annotation;
get(annotations: List | List<AnnotationWrapper>): Annotation[];
getAnnotations(predicate?: (value: Annotation) => boolean): Annotation[]; get(predicate?: (value: Annotation) => boolean): Annotation[];
get(argument?: AnnotationPredicate | List<AnnotationWrapper> | List | AnnotationWrapper | string): Annotation | Annotation[] {
if (isStringOrWrapper(argument)) {
return this.#getById(argument);
}
getAnnotations(argument?: AnnotationPredicate | List<AnnotationWrapper | string>): Annotation[] {
const isList = argument instanceof Array; const isList = argument instanceof Array;
return isList ? this.#getAnnotationsById(getIds(argument)) : this.#getAnnotations(argument); return isList ? this.#getByIds(argument) : this.#get(argument);
} }
getAnnotation(annotation: AnnotationWrapper | string) { deselect(annotation: string | AnnotationWrapper);
const id = typeof annotation === 'string' ? annotation : annotation.id; deselect(annotations?: List | List<AnnotationWrapper>);
return this.#manager.getAnnotationById(id); deselect(argument?: string | AnnotationWrapper | List | List<AnnotationWrapper>) {
} if (!argument) {
deselectAnnotations(annotations?: List<AnnotationWrapper | string>) {
if (!annotations) {
return this.#manager.deselectAllAnnotations(); return this.#manager.deselectAllAnnotations();
} }
const ann = this.#getAnnotationsById(getIds(annotations)); const ann = isStringOrWrapper(argument) ? [this.#getById(argument)] : this.#getByIds(argument);
this.#manager.deselectAnnotations(ann); this.#manager.deselectAnnotations(ann);
} }
deselectAnnotation(annotation: AnnotationWrapper | string) { hide(annotations: Annotation[]): void {
const id = typeof annotation === 'string' ? annotation : annotation.id;
this.deselectAnnotations([id]);
}
hideAnnotations(annotations: Annotation[]): void {
this.#manager.hideAnnotations(annotations); this.#manager.hideAnnotations(annotations);
} }
showAnnotations(annotations: Annotation[]): void { show(annotations: Annotation[]): void {
this.#manager.showAnnotations(annotations); this.#manager.showAnnotations(annotations);
} }
jumpAndSelect(annotations: List | List<AnnotationWrapper>) {
const annotationsFromViewer = this.get(annotations);
this.#manager.jumpToAnnotation(annotationsFromViewer[0]);
this.#manager.selectAnnotations(annotationsFromViewer);
}
#getById(annotation: AnnotationWrapper | string) {
return this.#manager.getAnnotationById(getId(annotation));
}
#autoSelectRectangleAfterCreation() { #autoSelectRectangleAfterCreation() {
this.#manager.addEventListener('annotationChanged', (annotations: Annotation[]) => { this.#manager.addEventListener('annotationChanged', (annotations: Annotation[]) => {
// when a rectangle is drawn, // when a rectangle is drawn,
@ -98,11 +97,11 @@ export class REDAnnotationManager {
}); });
} }
#getAnnotationsById(ids: List) { #getByIds(annotations: List | List<AnnotationWrapper>) {
return ids.map(id => this.#manager.getAnnotationById(id)).filter(a => !!a); return annotations.map((item: string | AnnotationWrapper) => this.#getById(item)).filter(a => !!a);
} }
#getAnnotations(predicate?: AnnotationPredicate) { #get(predicate?: AnnotationPredicate) {
const annotations = this.#manager.getAnnotationsList(); const annotations = this.#manager.getAnnotationsList();
return predicate ? annotations.filter(predicate) : annotations; return predicate ? annotations.filter(predicate) : annotations;
} }

View File

@ -13,10 +13,28 @@ export function stopAndPreventIfNotAllowed($event: KeyboardEvent) {
} }
} }
export function getIds(items?: List<string | AnnotationWrapper>): List | undefined { export function getId(item: string | AnnotationWrapper) {
return items?.map(value => (typeof value === 'string' ? value : value.id)); return typeof item === 'string' ? item : item.annotationId;
} }
export function asList(dataElements: string[] | string): string[] { export function getIds(items?: List | List<AnnotationWrapper>): List | undefined {
return typeof dataElements === 'string' ? [dataElements] : dataElements; return items?.map(getId);
}
export function isStringOrWrapper(value: unknown): value is string | AnnotationWrapper {
return typeof value === 'string' || value instanceof AnnotationWrapper;
}
export function asList(items: string[] | string): string[];
export function asList(items: AnnotationWrapper[] | AnnotationWrapper): AnnotationWrapper[];
export function asList(items: string[] | string | AnnotationWrapper[] | AnnotationWrapper): string[] | AnnotationWrapper[] {
if (typeof items === 'string') {
return [items];
}
if (items instanceof AnnotationWrapper) {
return [items];
}
return items;
} }

View File

@ -1,4 +1,4 @@
import { Injectable } from '@angular/core'; import { Injectable, Injector } from '@angular/core';
import { BehaviorSubject, firstValueFrom, of } from 'rxjs'; import { BehaviorSubject, firstValueFrom, of } from 'rxjs';
import { RotationType, RotationTypes } from '@red/domain'; import { RotationType, RotationTypes } from '@red/domain';
import { FileManagementService } from '../../../../services/files/file-management.service'; import { FileManagementService } from '../../../../services/files/file-management.service';
@ -28,15 +28,19 @@ export class PageRotationService {
constructor( constructor(
private readonly _pdf: PdfViewer, private readonly _pdf: PdfViewer,
private readonly _dialog: MatDialog,
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
private readonly _activatedRoute: ActivatedRoute, private readonly _activatedRoute: ActivatedRoute,
private readonly _logger: NGXLogger, private readonly _logger: NGXLogger,
private readonly _injector: Injector,
private readonly _fileManagementService: FileManagementService, private readonly _fileManagementService: FileManagementService,
private readonly _filesService: FilesService, private readonly _filesService: FilesService,
private readonly _filesMapService: FilesMapService, private readonly _filesMapService: FilesMapService,
) {} ) {}
get hasRotations() {
return Object.values(this.#rotations$.value).filter(v => !!v).length > 0;
}
isRotated$(page: number) { isRotated$(page: number) {
return this.#rotations$.pipe( return this.#rotations$.pipe(
map(rotations => !!rotations[page]), map(rotations => !!rotations[page]),
@ -44,10 +48,6 @@ export class PageRotationService {
); );
} }
hasRotations() {
return Object.values(this.#rotations$.value).filter(v => !!v).length > 0;
}
applyRotation() { applyRotation() {
this._loadingService.start(); this._loadingService.start();
const pages = this.#rotations$.value; const pages = this.#rotations$.value;
@ -104,11 +104,11 @@ export class PageRotationService {
} }
showConfirmationDialogIfHasRotations() { showConfirmationDialogIfHasRotations() {
return this.hasRotations() ? this.#showConfirmationDialog() : of(ConfirmOptions.DISCARD_CHANGES); return this.hasRotations ? this.#showConfirmationDialog() : of(false);
} }
#showConfirmationDialog() { #showConfirmationDialog() {
const ref = this._dialog.open(ConfirmationDialogComponent, { const ref = this._injector.get(MatDialog).open(ConfirmationDialogComponent, {
...defaultDialogConfig, ...defaultDialogConfig,
data: new ConfirmationDialogInput({ data: new ConfirmationDialogInput({
title: _('page-rotation.confirmation-dialog.title'), title: _('page-rotation.confirmation-dialog.title'),
@ -118,8 +118,8 @@ export class PageRotationService {
}), }),
}); });
return ref const closed$ = ref.afterClosed().pipe(map((option: ConfirmOptions) => option === ConfirmOptions.CONFIRM));
.afterClosed()
.pipe(tap((option: ConfirmOptions) => (option === ConfirmOptions.CONFIRM ? this.applyRotation() : this.discardRotation()))); return closed$.pipe(tap(apply => (apply ? this.applyRotation() : this.discardRotation())));
} }
} }

View File

@ -141,7 +141,7 @@ export class PdfViewer {
get #pageChanged$() { get #pageChanged$() {
const page$ = fromEvent<number>(this.documentViewer, 'pageNumberUpdated'); const page$ = fromEvent<number>(this.documentViewer, 'pageNumberUpdated');
return page$.pipe(tap(() => this._annotationManager.deselectAnnotations())); return page$.pipe(tap(() => this._annotationManager.deselect()));
} }
navigateTo(page: string | number) { navigateTo(page: string | number) {

View File

@ -170,7 +170,7 @@ export class ViewerHeaderService {
} }
#toggleRotationActionButtons() { #toggleRotationActionButtons() {
if (this._rotationService.hasRotations()) { if (this._rotationService.hasRotations) {
this.enable(ROTATION_ACTION_BUTTONS); this.enable(ROTATION_ACTION_BUTTONS);
} else { } else {
this.disable(ROTATION_ACTION_BUTTONS); this.disable(ROTATION_ACTION_BUTTONS);