From 5a314127b1c0549119a5b9a05168d4ec398b24af Mon Sep 17 00:00:00 2001 From: Timo Bejan Date: Fri, 23 Oct 2020 19:09:33 +0300 Subject: [PATCH] viewer reuse --- apps/red-ui/src/app/dialogs/dialog.service.ts | 6 +- .../file-preview-screen.component.html | 20 +-- .../file-preview-screen.component.scss | 5 +- .../file-preview-screen.component.ts | 72 +++++---- .../file/pdf-viewer/pdf-viewer.component.html | 2 +- .../file/pdf-viewer/pdf-viewer.component.ts | 150 +++++++++++++----- .../file/service/viewer-sync.service.ts | 93 ----------- apps/red-ui/src/app/utils/annotation-utils.ts | 9 +- 8 files changed, 173 insertions(+), 184 deletions(-) delete mode 100644 apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts diff --git a/apps/red-ui/src/app/dialogs/dialog.service.ts b/apps/red-ui/src/app/dialogs/dialog.service.ts index 1cb177e16..14629083d 100644 --- a/apps/red-ui/src/app/dialogs/dialog.service.ts +++ b/apps/red-ui/src/app/dialogs/dialog.service.ts @@ -64,11 +64,15 @@ export class DialogService { }); } - public openManualRedactionDialog($event: ManualRedactionEntry) { + public openManualRedactionDialog($event: ManualRedactionEntry, cb?: Function) { this._dialog.open(ManualRedactionDialogComponent, { ...dialogConfig, autoFocus: true, data: $event + }).afterClosed().subscribe(result => { + if (cb) { + cb(result); + } }); } diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html index 6d6558445..432656490 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html @@ -1,4 +1,4 @@ -
+
- + diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss index 01f94cbe5..5c054bb30 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss @@ -6,10 +6,7 @@ redaction-pdf-viewer { width: calc(100vw - #{$right-container-width}); height: calc(100vh - 110px); top: 110px; - - &.visible { - z-index: 2; - } + z-index: 2; } .actions-container { diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts index 773965d5b..bdbc7b6cd 100644 --- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts @@ -2,8 +2,7 @@ import {ChangeDetectorRef, Component, ElementRef, HostListener, NgZone, OnInit, import {ActivatedRoute, Router} from '@angular/router'; import {ManualRedactionEntry, ReanalysisControllerService} from '@redaction/red-ui-http'; import {AppStateService} from '../../../state/app-state.service'; -import {ViewerSyncService} from '../service/viewer-sync.service'; -import {Annotations} from '@pdftron/webviewer'; +import {Annotations, WebViewerInstance} from '@pdftron/webviewer'; import {PdfViewerComponent} from '../pdf-viewer/pdf-viewer.component'; import {AnnotationUtils} from '../../../utils/annotation-utils'; import {UserService} from '../../../user/user.service'; @@ -22,7 +21,7 @@ import {DialogService} from '../../../dialogs/dialog.service'; styleUrls: ['./file-preview-screen.component.scss'] }) export class FilePreviewScreenComponent implements OnInit { - private _readyViewers: string[] = []; + private projectId: string; private _activeViewer: 'ANNOTATED' | 'REDACTED' = 'ANNOTATED'; @@ -30,18 +29,20 @@ export class FilePreviewScreenComponent implements OnInit { @ViewChild('annotations') private _annotationsElement: ElementRef; @ViewChild('quickNavigation') private _quickNavigationElement: ElementRef; + public annotatedFileData: Blob; + public redactedFileData: Blob; public fileId: string; public annotations: Annotations.Annotation[] = []; public displayedAnnotations: { [key: number]: { annotations: Annotations.Annotation[] } } = {}; public selectedAnnotation: Annotations.Annotation; public filters: AnnotationFilters; public expandedFilters: AnnotationFilters = {hint: false}; + private instance: WebViewerInstance; constructor( public readonly appStateService: AppStateService, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _activatedRoute: ActivatedRoute, - private readonly _viewerSyncService: ViewerSyncService, private readonly _dialogService: DialogService, private readonly _router: Router, private readonly _userService: UserService, @@ -75,13 +76,23 @@ export class FilePreviewScreenComponent implements OnInit { public set redactedView(value: boolean) { this._activeViewer = value ? 'REDACTED' : 'ANNOTATED'; - this._activateViewer(this._activeViewer); } public ngOnInit(): void { // PDFTRON cache fix localStorage.clear(); - this._viewerSyncService.activateViewer('ANNOTATED'); + this._reloadFiles(); + } + + private _reloadFiles() { + this._fileDownloadService.loadFile('ANNOTATED', this.fileId, (data) => { + this.annotatedFileData = data; + }).subscribe(() => { + }); + this._fileDownloadService.loadFile('REDACTED', this.fileId, (data) => { + this.redactedFileData = data; + }).subscribe(() => { + }); } public openFileDetailsDialog($event: MouseEvent) { @@ -106,33 +117,15 @@ export class FilePreviewScreenComponent implements OnInit { this._dialogService.openAssignFileOwnerDialog($event, file); } - public fileReady(viewer: string) { - this._readyViewers.push(viewer); - this._changeDetectorRef.detectChanges(); - } - - public get viewReady() { - return this._readyViewers.length >= 2; - } public get activeViewer() { - return this._viewerSyncService.activeViewer; - } - - private _activateViewer(value: string) { - this._viewerSyncService.activateViewer(value); + return this.instance; } public applyFilters() { this.displayedAnnotations = AnnotationUtils.parseAnnotations(this.annotations, this.filters); } - public handleAnnotationsAdded(annotations: Annotations.Annotation[]) { - AnnotationUtils.addAnnotations(this.annotations, annotations); - this.applyFilters(); - this._changeDetectorRef.detectChanges(); - } - public get displayedPages(): number[] { return Object.keys(this.displayedAnnotations).map(key => Number(key)); } @@ -162,13 +155,19 @@ export class FilePreviewScreenComponent implements OnInit { public openManualRedactionDialog($event: ManualRedactionEntry) { this.ngZone.run(() => { - this._dialogService.openManualRedactionDialog($event); + this._dialogService.openManualRedactionDialog($event, () => { + setTimeout(() => { + console.log('reload files after 5 seconds'); + this._reloadFiles(); + }, 5000) + }); }); + } get activeViewerPage() { - return this._viewerSyncService.activeViewerPage; + return this.instance.docViewer.getCurrentPage(); } @debounce() @@ -304,5 +303,24 @@ export class FilePreviewScreenComponent implements OnInit { viewerPageChanged($event: number) { this._scrollViews(); this._changeDetectorRef.detectChanges(); + `` + } + + viewerReady($event: WebViewerInstance) { + this.instance = $event; + } + + handleAnnotationsAdded(annotations: Annotations.Annotation[]) { + + // handle comments + annotations.forEach(a => { + a['comments'] = a['Mi'] ? a['Mi'].map(m => { + return {value: m.eC} + }) : []; + }) + + AnnotationUtils.addAnnotations(this.annotations, annotations); + this.applyFilters(); + this._changeDetectorRef.detectChanges(); } } diff --git a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.html b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.html index d9f531bb5..a1c96bfbe 100644 --- a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.html +++ b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.html @@ -1,3 +1,3 @@
-
+
diff --git a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts index 799acf2fc..1a7072a27 100644 --- a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts @@ -4,30 +4,43 @@ import { ElementRef, EventEmitter, Input, - OnDestroy, + OnChanges, OnInit, Output, + SimpleChanges, ViewChild } from '@angular/core'; -import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service'; -import { FileStatus, ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http'; -import WebViewer, { Annotations, WebViewerInstance } from '@pdftron/webviewer'; -import { TranslateService } from '@ngx-translate/core'; -import { ViewerSyncService } from '../service/viewer-sync.service'; -import { FileDownloadService } from '../service/file-download.service'; -import { FileType } from '../model/file-type'; +import {AppConfigKey, AppConfigService} from '../../../app-config/app-config.service'; +import {FileStatus, ManualRedactionEntry, Rectangle} from '@redaction/red-ui-http'; +import WebViewer, {Annotations, WebViewerInstance} from '@pdftron/webviewer'; +import {TranslateService} from '@ngx-translate/core'; +import {FileDownloadService} from '../service/file-download.service'; +export interface ViewerState { + + displayMode?: any; + layoutMode?: any; + pageNumber?: any; + scrollTop?: any; + scrollLeft?: any; + zoom?: any; + leftPanelState?: any; + +} @Component({ selector: 'redaction-pdf-viewer', templateUrl: './pdf-viewer.component.html', styleUrls: ['./pdf-viewer.component.scss'] }) -export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { +export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges { - @Input() fileId: string; - @Input() fileType: FileType; + private _viewerState: ViewerState = null; // no initial state + + @Input() fileData: Blob; @Input() fileStatus: FileStatus; + + @Output() fileReady = new EventEmitter(); @Output() annotationsAdded = new EventEmitter(); @Output() annotationSelected = new EventEmitter(); @@ -35,32 +48,30 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { @Output() pageChanged = new EventEmitter(); @Output() keyUp = new EventEmitter(); - @ViewChild('viewer', { static: true }) viewer: ElementRef; - wvInstance: WebViewerInstance; + @Output() viewerReady = new EventEmitter(); - _fileData: Blob; + @Input() flag: boolean = false; - constructor(private readonly _viewerSyncService: ViewerSyncService, - private readonly _translateService: TranslateService, + @ViewChild('viewer', {static: true}) viewer: ElementRef; + instance: WebViewerInstance; + + constructor(private readonly _translateService: TranslateService, private readonly _fileDownloadService: FileDownloadService, private readonly _appConfigService: AppConfigService) { } ngOnInit() { - this.wvDocumentLoadedHandler = this.wvDocumentLoadedHandler.bind(this); + this._restoreViewerState = this._restoreViewerState.bind(this); } - wvDocumentLoadedHandler(): void { - this.wvInstance.setFitMode('FitPage'); - this.fileReady.emit(); + ngOnChanges(changes: SimpleChanges): void { + if (changes.fileData && !changes.fileData.firstChange) { + this._changeDocument(); + } } ngAfterViewInit(): void { - this._fileDownloadService.loadFile(this.fileType, this.fileId, (data) => { - this._fileData = data; - }, () => this._fileData).subscribe(() => { - this._loadViewer(this._fileData); - }); + this._loadViewer(this.fileData); } private _loadViewer(pdfBlob: any) { @@ -70,8 +81,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { isReadOnly: true, path: '/assets/wv-resources' }, this.viewer.nativeElement).then(instance => { - this.wvInstance = instance; - this._viewerSyncService.registerViewer(this.fileType, this.wvInstance); + this.instance = instance; this._disableElements(); this._configureTextPopup(); this._configureHeader(); @@ -93,7 +103,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { this.pageChanged.emit(p); }); - instance.docViewer.on('documentLoaded', this.wvDocumentLoadedHandler); + instance.docViewer.on('documentLoaded', this._restoreViewerState); instance.docViewer.on('keyDown', ($event) => { if ($event.key.startsWith('Arrow')) { @@ -107,13 +117,15 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { } }) - instance.loadDocument(pdfBlob, { filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf' }); + instance.loadDocument(pdfBlob, {filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'}); + + this.viewerReady.emit(instance); }); } private _disableElements() { - this.wvInstance.disableElements([ + this.instance.disableElements([ 'textHighlightToolButton', 'textUnderlineToolButton', 'textSquigglyToolButton', @@ -130,14 +142,14 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { } private _configureTextPopup() { - this.wvInstance.textPopup.add({ + this.instance.textPopup.add({ type: 'actionButton', img: '/assets/icons/general/add-redaction.svg', title: this._translateService.instant('pdf-viewer.text-popup.actions.suggestion-redaction.label'), onClick: () => { - const selectedQuads = this.wvInstance.docViewer.getSelectedTextQuads(); - const text = this.wvInstance.docViewer.getSelectedText(); - const entry: ManualRedactionEntry = { positions: [] }; + const selectedQuads = this.instance.docViewer.getSelectedTextQuads(); + const text = this.instance.docViewer.getSelectedText(); + const entry: ManualRedactionEntry = {positions: []}; for (const key of Object.keys(selectedQuads)) { for (const quad of selectedQuads[key]) { entry.positions.push(this.toPosition(parseInt(key, 10), quad)); @@ -151,7 +163,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { private toPosition(page: number, selectedQuad: any): Rectangle { - const pageHeight = this.wvInstance.docViewer.getPageHeight(page); + const pageHeight = this.instance.docViewer.getPageHeight(page); const height = selectedQuad.y2 - selectedQuad.y4; return { page: page, @@ -165,24 +177,76 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy { } private _configureHeader() { - this.wvInstance.setToolbarGroup('toolbarGroup-View'); + this.instance.setToolbarGroup('toolbarGroup-View'); } - ngOnDestroy(): void { - this._viewerSyncService.deregisterInstance(this.fileType); - } public selectAnnotation(annotation: Annotations.Annotation) { - this.wvInstance.annotManager.deselectAllAnnotations(); - this.wvInstance.annotManager.selectAnnotation(annotation); - this.wvInstance.docViewer.displayPageLocation( + this.instance.annotManager.deselectAllAnnotations(); + this.instance.annotManager.selectAnnotation(annotation); + this.instance.docViewer.displayPageLocation( annotation.getPageNumber(), 0, annotation.getY() - 100); } public navigateToPage(pageNumber: number) { - this.wvInstance.docViewer.displayPageLocation(pageNumber, 0, 0); + this.instance.docViewer.displayPageLocation(pageNumber, 0, 0); } + + + private _changeDocument() { + // sync layout and display mode + + + const instance = this.instance; + const docViewer = this.instance.docViewer; + + const lastScrolledViewerScrollElement = docViewer.getScrollViewElement(); + + const viewerState: ViewerState = { + displayMode: docViewer.getDisplayModeManager().getDisplayMode().mode, + layoutMode: instance.getLayoutMode(), + pageNumber: instance.docViewer.getCurrentPage(), + scrollLeft: lastScrolledViewerScrollElement.scrollLeft, + scrollTop: lastScrolledViewerScrollElement.scrollTop, + zoom: docViewer.getZoom(), + leftPanelState: instance.isElementOpen('leftPanel') + } + + this.instance.loadDocument(this.fileData, {filename: this.fileStatus ? this.fileStatus.filename : 'document.pdf'}); + + this._viewerState = viewerState; + + } + + private _restoreViewerState() { + this._restoreState(this._viewerState, this.instance); + } + + private _restoreState(viewerState: ViewerState, instance: WebViewerInstance) { + if (this._viewerState) { + instance.docViewer.setCurrentPage(viewerState.pageNumber); + instance.setLayoutMode(viewerState.layoutMode); + const instanceDisplayMode = instance.docViewer.getDisplayModeManager().getDisplayMode(); + instanceDisplayMode.mode = viewerState.displayMode; + instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); + // Synchronize zoom - needs to be done before scrolling + instance.docViewer.zoomTo(viewerState.zoom); + const viewerScrollElement = instance.docViewer.getScrollViewElement(); + viewerScrollElement.scrollTo(viewerState.scrollLeft, viewerState.scrollTop); + + if (viewerState.leftPanelState) { + instance.openElements(['leftPanel']); + } else { + instance.closeElements(['leftPanel']); + } + } else { + // viewer init + this.instance.setFitMode('FitPage'); + } + } + + } diff --git a/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts b/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts deleted file mode 100644 index 2fb7d9569..000000000 --- a/apps/red-ui/src/app/screens/file/service/viewer-sync.service.ts +++ /dev/null @@ -1,93 +0,0 @@ -import {Injectable} from '@angular/core'; -import {WebViewerInstance} from '@pdftron/webviewer'; - -@Injectable({ - providedIn: 'root' -}) -export class ViewerSyncService { - - private _activeViewer: string; - - private _viewers: { [key: string]: WebViewerInstance } = {}; - - constructor() { - } - - get activeViewerObject(): WebViewerInstance{ - if (this._activeViewer) { - const activeViewer = this._viewers[this._activeViewer]; - return activeViewer; - } - return undefined; - } - - get activeViewerPage() { - if (this._activeViewer) { - const lastActiveViewer = this._viewers[this._activeViewer]; - return lastActiveViewer.docViewer.getCurrentPage(); - } - return 1; - } - - syncViewers() { - localStorage.clear(); - if (this._activeViewer) { - const lastActiveViewer = this._viewers[this._activeViewer]; - if (lastActiveViewer) { - - const lastDisplayMode = lastActiveViewer.docViewer.getDisplayModeManager().getDisplayMode().mode; - const lastLayoutMode = lastActiveViewer.getLayoutMode(); - const lastPageNumber = lastActiveViewer.docViewer.getCurrentPage(); - - const lastScrolledViewerScrollElement = lastActiveViewer.docViewer.getScrollViewElement(); - const lastViewerScrollTop = lastScrolledViewerScrollElement.scrollTop; - const lastViewerScrollLeft = lastScrolledViewerScrollElement.scrollLeft; - const lastViewerZoom = lastActiveViewer.docViewer.getZoom(); - const lastViewerLeftPaneState = lastActiveViewer.isElementOpen('leftPanel'); - - for (const key of Object.keys(this._viewers)) { - if (key !== this._activeViewer) { - const instance = this._viewers[key]; - - // sync layout and display mode - instance.docViewer.setCurrentPage(lastPageNumber); - instance.setLayoutMode(lastLayoutMode); - const displayMode = instance.docViewer.getDisplayModeManager().getDisplayMode(); - displayMode.mode = lastDisplayMode; - instance.docViewer.getDisplayModeManager().setDisplayMode(displayMode); - // Synchronize zoom - needs to be done before scrolling - instance.docViewer.zoomTo(lastViewerZoom); - - // Synchronize scroll - const viewerScrollElement = instance.docViewer.getScrollViewElement(); - viewerScrollElement.scrollTo(lastViewerScrollLeft, lastViewerScrollTop); - - // Synchronize left panel - if (lastViewerLeftPaneState) { - instance.openElements(['leftPanel']); - } else { - instance.closeElements(['leftPanel']); - } - } - } - } - } - } - - deregisterInstance(key: string) { - delete this._viewers[key]; - } - - registerViewer(key: string, instance: WebViewerInstance) { - this._viewers[key] = instance; - } - - activateViewer(key: string) { - this.syncViewers(); - this._activeViewer = key; - } - - get activeViewer() { - return this._activeViewer; - } -} diff --git a/apps/red-ui/src/app/utils/annotation-utils.ts b/apps/red-ui/src/app/utils/annotation-utils.ts index cdfc3fa2e..1da9cf0c9 100644 --- a/apps/red-ui/src/app/utils/annotation-utils.ts +++ b/apps/red-ui/src/app/utils/annotation-utils.ts @@ -1,5 +1,5 @@ -import { Annotations } from '@pdftron/webviewer'; -import { AnnotationFilters } from './types'; +import {Annotations} from '@pdftron/webviewer'; +import {AnnotationFilters} from './types'; export class AnnotationUtils { public static sortAnnotations(annotations: Annotations.Annotation[]): Annotations.Annotation[] { @@ -87,7 +87,10 @@ export class AnnotationUtils { public static addAnnotations(initialAnnotations: Annotations.Annotation[], addedAnnotations: Annotations.Annotation[]) { for (const annotation of addedAnnotations) { if (annotation.Id.indexOf(':') > 0) { - initialAnnotations.push(annotation); + const found = initialAnnotations.find(a => a.Id === annotation.Id); + if(!found) { + initialAnnotations.push(annotation); + } } } }