From 92fda2695da0b8cad88291994639325607b8e4ec Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Thu, 25 Nov 2021 16:09:24 +0200 Subject: [PATCH] use service for multiselect --- .../annotations-list.component.html | 6 +- .../annotations-list.component.ts | 18 ++---- .../file-workload.component.html | 21 +++---- .../file-workload/file-workload.component.ts | 60 +++++++++++-------- .../pdf-viewer/pdf-viewer.component.ts | 9 +-- .../user-management.component.ts | 2 + .../file-preview-screen.component.html | 7 +-- .../file-preview-screen.component.ts | 37 +++++------- .../services/excluded-pages.service.ts | 7 ++- .../services/multi-select.service.ts | 32 ++++++++++ .../file-actions/file-actions.component.html | 2 +- .../modules/dossier/utils/pdf-viewer.utils.ts | 16 +---- 12 files changed, 120 insertions(+), 97 deletions(-) create mode 100644 apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/multi-select.service.ts diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotations-list/annotations-list.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotations-list/annotations-list.component.html index 9527825e8..4f8ce523b 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotations-list/annotations-list.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/annotations-list/annotations-list.component.html @@ -4,7 +4,7 @@ [attr.annotation-id]="annotation.id" [attr.annotation-page]="activeViewerPage" [class.active]="isSelected(annotation.annotationId)" - [class.multi-select-active]="multiSelectActive" + [class.multi-select-active]="multiSelectService.active$ | async" class="annotation-wrapper" >
@@ -30,7 +30,7 @@
@@ -47,7 +47,7 @@ {{ annotation.comments.length }} -
+
; - @Input() multiSelectActive = false; @Input() activeViewerPage: number; @Input() canMultiSelect = true; - @Output() readonly multiSelectActiveChange = new EventEmitter(); @Output() readonly pagesPanelActive = new EventEmitter(); - @Output() readonly selectAnnotations = new EventEmitter< - AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean } - >(); + @Output() readonly selectAnnotations = new EventEmitter(); @Output() readonly deselectAnnotations = new EventEmitter(); + constructor(readonly multiSelectService: MultiSelectService) {} + annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void { - console.log(annotation); if (($event.target as IqserEventTarget).localName === 'input') { return; } @@ -35,13 +33,9 @@ export class AnnotationsListComponent { this.deselectAnnotations.emit([annotation]); } else { if (this.canMultiSelect && ($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) { - this.multiSelectActive = true; - this.multiSelectActiveChange.emit(true); + this.multiSelectService.activate(); } - this.selectAnnotations.emit({ - annotations: [annotation], - multiSelect: this.multiSelectActive, - }); + this.selectAnnotations.emit([annotation]); } } 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 16338b043..f0ecf0c69 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 @@ -1,5 +1,5 @@
@@ -17,8 +17,8 @@
-
+
+ {{ selectedAnnotations?.length || 0 }} selected +
-
+
- +
-
+
- +
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 4cbf24a2e..507f275d1 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 @@ -14,13 +14,23 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationProcessingService } from '../../../../services/annotation-processing.service'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import scrollIntoView from 'scroll-into-view-if-needed'; -import { CircleButtonTypes, Debounce, FilterService, IconButtonTypes, INestedFilter, IqserEventTarget, Required } from '@iqser/common-ui'; +import { + CircleButtonTypes, + Debounce, + FilterService, + IconButtonTypes, + INestedFilter, + IqserEventTarget, + Required, + shareDistinctLast, +} from '@iqser/common-ui'; import { PermissionsService } from '@services/permissions.service'; import { WebViewerInstance } from '@pdftron/webviewer'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, tap } from 'rxjs/operators'; import { File, IViewedPage } from '@red/domain'; import { ExcludedPagesService } from '../../services/excluded-pages.service'; +import { MultiSelectService } from '../../services/multi-select.service'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -46,9 +56,7 @@ export class FileWorkloadComponent { @Input() annotationActionsTemplate: TemplateRef; @Input() viewer: WebViewerInstance; @Output() readonly shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter(); - @Output() readonly selectAnnotations = new EventEmitter< - AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean } - >(); + @Output() readonly selectAnnotations = new EventEmitter(); @Output() readonly deselectAnnotations = new EventEmitter(); @Output() readonly selectPage = new EventEmitter(); @Output() readonly toggleSkipped = new EventEmitter(); @@ -56,18 +64,23 @@ export class FileWorkloadComponent { displayedPages: number[] = []; pagesPanelActive = true; readonly displayedAnnotations$: Observable>; + readonly multiSelectActive$: Observable; + readonly multiSelectInactive$: Observable; private _annotations$ = new BehaviorSubject([]); @ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef; @ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef; constructor( readonly excludedPageService: ExcludedPagesService, + readonly multiSelectService: MultiSelectService, private readonly _permissionsService: PermissionsService, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _filterService: FilterService, private readonly _annotationProcessingService: AnnotationProcessingService, ) { this.displayedAnnotations$ = this._displayedAnnotations$; + this.multiSelectActive$ = this._multiSelectActive$; + this.multiSelectInactive$ = this._multiSelectInactive$; } @Input() @@ -75,22 +88,6 @@ export class FileWorkloadComponent { this._annotations$.next(value); } - private _multiSelectActive = false; - - get multiSelectActive(): boolean { - return this._multiSelectActive; - } - - set multiSelectActive(value: boolean) { - this._multiSelectActive = value; - if (!value) { - this.selectAnnotations.emit(); - } else { - this.shouldDeselectAnnotationsOnPageChange = false; - this.shouldDeselectAnnotationsOnPageChangeChange.emit(false); - } - } - get activeAnnotations(): AnnotationWrapper[] | undefined { return this.displayedAnnotations.get(this.activeViewerPage); } @@ -103,6 +100,21 @@ export class FileWorkloadComponent { return this.file?.excludedPages?.includes(this.activeViewerPage); } + private get _multiSelectInactive$() { + return this.multiSelectService.inactive$.pipe( + tap(() => this.selectAnnotations.emit()), + shareDistinctLast(), + ); + } + + private get _multiSelectActive$() { + const disableDeselectOnPageChange = () => { + this.shouldDeselectAnnotationsOnPageChange = false; + this.shouldDeselectAnnotationsOnPageChangeChange.emit(false); + }; + return this.multiSelectService.active$.pipe(tap(disableDeselectOnPageChange), shareDistinctLast()); + } + private get _firstSelectedAnnotation() { return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null; } @@ -133,7 +145,7 @@ export class FileWorkloadComponent { } pageHasSelection(page: number) { - return this.multiSelectActive && !!this.selectedAnnotations?.find(a => a.pageNumber === page); + return this.multiSelectService.isActive && !!this.selectedAnnotations?.find(a => a.pageNumber === page); } selectAllOnActivePage() { @@ -164,7 +176,7 @@ export class FileWorkloadComponent { // if we activated annotationsPanel - // select first annotation from this page in case there is no // selected annotation on this page and not in multi select mode - if (!this.pagesPanelActive && !this.multiSelectActive) { + if (!this.pagesPanelActive && !this.multiSelectService.isActive) { this._selectFirstAnnotationOnCurrentPageIfNecessary(); } return; @@ -173,7 +185,7 @@ export class FileWorkloadComponent { if (!this.pagesPanelActive) { // Disable annotation navigation in multi select mode // => TODO: maybe implement selection on enter? - if (!this.multiSelectActive) { + if (!this.multiSelectService.isActive) { this._navigateAnnotations($event); } } else { 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 7b6cd87ff..146b0eb3c 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 @@ -36,6 +36,7 @@ import { ActivatedRoute } from '@angular/router'; import { toPosition } from '../../../../utils/pdf-calculation.utils'; import { DossiersService } from '@services/entity-services/dossiers.service'; import { ViewModeService } from '../../services/view-mode.service'; +import { MultiSelectService } from '../../services/multi-select.service'; import Tools = Core.Tools; import TextTool = Tools.TextTool; import Annotation = Core.Annotations.Annotation; @@ -65,7 +66,6 @@ export class PdfViewerComponent implements OnInit, OnChanges { @Input() canPerformActions = false; @Input() annotations: AnnotationWrapper[]; @Input() shouldDeselectAnnotationsOnPageChange = true; - @Input() multiSelectActive: boolean; @Output() readonly fileReady = new EventEmitter(); @Output() readonly annotationSelected = new EventEmitter(); @Output() readonly manualAnnotationRequested = new EventEmitter(); @@ -96,6 +96,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { private readonly _loadingService: LoadingService, private readonly _dossiersService: DossiersService, readonly viewModeService: ViewModeService, + readonly multiSelectService: MultiSelectService, ) {} private get _dossier(): Dossier { @@ -119,10 +120,6 @@ export class PdfViewerComponent implements OnInit, OnChanges { if (changes.canPerformActions) { this._handleCustomActions(); } - - if (changes.multiSelectActive) { - this.utils.multiSelectActive = this.multiSelectActive; - } } uploadFile(files: any) { @@ -228,7 +225,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { this.documentViewer = this.instance.Core.documentViewer; this.annotationManager = this.instance.Core.annotationManager; - this.utils = new PdfViewerUtils(this.instance, this.viewModeService, this.multiSelectActive); + this.utils = new PdfViewerUtils(this.instance, this.viewModeService); this._setSelectionMode(); this._disableElements(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts index 564aa9300..575d93250 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/user-management/user-management.component.ts @@ -86,6 +86,7 @@ export class UserManagementComponent implements OnChanges { const { dossierId, fileId, filename } = file; this.loadingService.start(); + if (!assigneeId) { await this.filesService.setUnassigned([fileId], dossierId).toPromise(); } else if (file.isUnderReview) { @@ -93,6 +94,7 @@ export class UserManagementComponent implements OnChanges { } else if (file.isUnderApproval) { await this.filesService.setUnderApprovalFor([fileId], dossierId, assigneeId).toPromise(); } + this.loadingService.stop(); this.toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } }); diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html index a9dcd1937..bdcaf2315 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html @@ -73,23 +73,22 @@ *ngIf="displayPDFViewer" [annotations]="annotations" [canPerformActions]="canPerformAnnotationActions$ | async" - [fileData]="displayData" + [fileData]="fileData.fileData" [file]="file" - [multiSelectActive]="multiSelectActive" [shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange" >
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 2177ccd43..052ddc0fb 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 @@ -23,6 +23,7 @@ import { OnDetach, processFilters, shareDistinctLast, + shareLast, } from '@iqser/common-ui'; import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; @@ -52,6 +53,7 @@ import { FilesMapService } from '@services/entity-services/files-map.service'; import { WatermarkService } from '@shared/services/watermark.service'; import { ExcludedPagesService } from './services/excluded-pages.service'; import { ViewModeService } from './services/view-mode.service'; +import { MultiSelectService } from './services/multi-select.service'; import Annotation = Core.Annotations.Annotation; import PDFNet = Core.PDFNet; @@ -60,7 +62,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f']; @Component({ templateUrl: './file-preview-screen.component.html', styleUrls: ['./file-preview-screen.component.scss'], - providers: [FilterService, ExcludedPagesService, ViewModeService], + providers: [FilterService, ExcludedPagesService, ViewModeService, MultiSelectService], changeDetection: ChangeDetectionStrategy.OnPush, }) export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach { @@ -116,6 +118,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _dossiersService: DossiersService, readonly excludedPagesService: ExcludedPagesService, readonly viewModeService: ViewModeService, + readonly multiSelectService: MultiSelectService, ) { super(); this.dossierId = _activatedRoute.snapshot.paramMap.get('dossierId'); @@ -125,6 +128,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni tap(async file => { await this._reloadFile(file); }), + shareLast(), ); this.showExcludedPages$ = this._showExcludedPages$; this.canPerformAnnotationActions$ = this._canPerformAnnotationActions$; @@ -152,16 +156,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni return this.viewModeService.isStandard ? currentPage : currentPage % 2 === 0 ? currentPage / 2 : (currentPage + 1) / 2; } - get displayData(): Blob { - return this.fileData?.fileData; - } - - get multiSelectActive(): boolean { - return !!this._workloadComponent?.multiSelectActive; - } - private get _showExcludedPages$() { - return this.excludedPagesService.show$.pipe(tap(() => this._disableMultiSelectAndDocumentInfo())); + return this.excludedPagesService.shown$.pipe(tap(() => this._disableMultiSelectAndDocumentInfo())); } private get _canPerformAnnotationActions$() { @@ -288,17 +284,17 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni .map(id => this.annotations.find(annotationWrapper => annotationWrapper.id === id)) .filter(ann => ann !== undefined); if (this.selectedAnnotations.length > 1) { - this._workloadComponent.multiSelectActive = true; + this.multiSelectService.activate(); } this._workloadComponent.scrollToSelectedAnnotation(); this._changeDetectorRef.markForCheck(); } - selectAnnotations(annotations?: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) { + selectAnnotations(annotations?: AnnotationWrapper[]) { if (annotations) { - this.viewerComponent.utils.selectAnnotations(annotations); + this.viewerComponent?.utils.selectAnnotations(annotations, this.multiSelectService.isActive); } else { - this.viewerComponent.utils.deselectAllAnnotations(); + this.viewerComponent?.utils.deselectAllAnnotations(); } } @@ -379,7 +375,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } this._scrollViews(); - if (!this._workloadComponent.multiSelectActive) { + if (!this.multiSelectService.isActive) { this.shouldDeselectAnnotationsOnPageChange = true; } @@ -420,8 +416,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } toggleViewDocumentInfo(): void { - this.viewDocumentInfo = !this.viewDocumentInfo; - this._workloadComponent.multiSelectActive = false; + this.viewDocumentInfo = true; + this.multiSelectService.deactivate(); this.excludedPagesService.hide(); } @@ -455,15 +451,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } private async _reloadFile(file: File): Promise { - 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.multiSelectService.deactivate(); this.viewDocumentInfo = false; } @@ -551,12 +545,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni if (performUpdate && !!this.fileData) { this.fileData.redactionLog = fileData.redactionLog; this.fileData.viewedPages = fileData.viewedPages; - this.rebuildFilters(true); } else { this.fileData = fileData; - this.rebuildFilters(); } + this.rebuildFilters(); return; } diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/excluded-pages.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/excluded-pages.service.ts index 8285eeefc..72089416c 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/excluded-pages.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/excluded-pages.service.ts @@ -1,14 +1,17 @@ import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { shareDistinctLast } from '@iqser/common-ui'; +import { map } from 'rxjs/operators'; @Injectable() export class ExcludedPagesService { - readonly show$: Observable; + readonly shown$: Observable; + readonly hidden$: Observable; private readonly _show$ = new BehaviorSubject(false); constructor() { - this.show$ = this._show$.asObservable().pipe(shareDistinctLast()); + this.shown$ = this._show$.asObservable().pipe(shareDistinctLast()); + this.hidden$ = this.shown$.pipe(map(value => !value)); } show() { diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/multi-select.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/multi-select.service.ts new file mode 100644 index 000000000..60e990157 --- /dev/null +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/multi-select.service.ts @@ -0,0 +1,32 @@ +import { Injectable } from '@angular/core'; +import { BehaviorSubject, Observable } from 'rxjs'; +import { shareDistinctLast } from '@iqser/common-ui'; +import { map } from 'rxjs/operators'; + +@Injectable() +export class MultiSelectService { + readonly active$: Observable; + readonly inactive$: Observable; + private readonly _active$ = new BehaviorSubject(false); + + constructor() { + this.active$ = this._active$.asObservable().pipe(shareDistinctLast()); + this.inactive$ = this.active$.pipe(map(value => !value)); + } + + get isActive() { + return this._active$.value; + } + + activate() { + this._active$.next(true); + } + + deactivate() { + this._active$.next(false); + } + + toggle() { + this._active$.next(!this._active$.value); + } +} 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 f477b0970..766eac5a7 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 @@ -73,7 +73,7 @@