viewer reuse

This commit is contained in:
Timo Bejan 2020-10-23 19:09:33 +03:00
parent aad3846a8f
commit 5a314127b1
8 changed files with 173 additions and 184 deletions

View File

@ -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);
}
});
}

View File

@ -1,4 +1,4 @@
<section [class.hidden]="!viewReady">
<section>
<div class="page-header">
<div class="flex-1">
<mat-slide-toggle color="primary"
@ -55,19 +55,15 @@
<div class="flex red-content-inner">
<div class="left-container">
<redaction-pdf-viewer [class.visible]="activeViewer === 'ANNOTATED'" [fileId]="fileId" fileType="ANNOTATED"
<redaction-pdf-viewer *ngIf="annotatedFileData && redactedFileData"
[fileData]="redactedView ? redactedFileData : annotatedFileData "
[fileStatus]="appStateService.activeFile"
(fileReady)="fileReady('ANNOTATED')"
(keyUp)="handleKeyEvent($event)"
(pageChanged)="viewerPageChanged($event)"
(manualAnnotationRequested)="openManualRedactionDialog($event)"
(annotationSelected)="handleAnnotationSelected($event)"
(annotationsAdded)="handleAnnotationsAdded($event)"></redaction-pdf-viewer>
<redaction-pdf-viewer [class.visible]="activeViewer === 'REDACTED'" [fileId]="fileId" fileType="REDACTED"
(keyUp)="handleKeyEvent($event)"
(pageChanged)="viewerPageChanged($event)"
(manualAnnotationRequested)="openManualRedactionDialog($event)"
(fileReady)="fileReady('REDACTED')"></redaction-pdf-viewer>
(annotationsAdded)="handleAnnotationsAdded($event)"
(viewerReady)="viewerReady($event)"></redaction-pdf-viewer>
</div>
<div class="right-fixed-container">
@ -159,8 +155,8 @@
<div><strong><span translate="dictionary"></span>: </strong>{{getDictionary(annotation)}}</div>
<div *ngIf="annotation.getContents()"><strong><span translate="content"></span>:
</strong>{{annotation.getContents()}}</div>
<div *ngIf="annotation.Mi[0]?.eC">
<strong><span translate="comment"></span>:</strong> {{ annotation.Mi[0].eC }}
<div *ngFor="let comment of annotation['comments']">
<strong><span translate="comment"></span>:</strong> {{ comment.value }}
</div>
</div>
@ -184,4 +180,4 @@
</div>
</section>
<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>
<!--<redaction-full-page-loading-indicator [displayed]="!viewReady"></redaction-full-page-loading-indicator>-->

View File

@ -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 {

View File

@ -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();
}
}

View File

@ -1,3 +1,3 @@
<div class="page">
<div #viewer [id]="fileId+fileType" class="viewer"></div>
<div #viewer [id]="fileStatus.fileId" class="viewer"></div>
</div>

View File

@ -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<Annotations.Annotation[]>();
@Output() annotationSelected = new EventEmitter<Annotations.Annotation>();
@ -35,32 +48,30 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnDestroy {
@Output() pageChanged = new EventEmitter<number>();
@Output() keyUp = new EventEmitter<KeyboardEvent>();
@ViewChild('viewer', { static: true }) viewer: ElementRef;
wvInstance: WebViewerInstance;
@Output() viewerReady = new EventEmitter<WebViewerInstance>();
_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(<any>{
this.instance.textPopup.add(<any>{
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');
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}
}