RED-3988: move compare input to pdf viewer
This commit is contained in:
parent
a989708d08
commit
c5d82a097f
@ -1,5 +1,3 @@
|
||||
<input #compareFileInput (change)="uploadFile($event.target['files'])" accept="application/pdf" class="file-upload-input" type="file" />
|
||||
|
||||
<div *ngIf="pdf.loaded$ | async" class="pagination noselect">
|
||||
<div (click)="pdf.navigatePreviousPage()">
|
||||
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
|
||||
|
||||
@ -1,16 +1,4 @@
|
||||
import {
|
||||
Component,
|
||||
ElementRef,
|
||||
EventEmitter,
|
||||
Inject,
|
||||
Input,
|
||||
NgZone,
|
||||
OnChanges,
|
||||
OnInit,
|
||||
Output,
|
||||
SimpleChanges,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { Component, EventEmitter, Inject, Input, NgZone, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
|
||||
import { Dossier, IHeaderElement, IManualRedactionEntry } from '@red/domain';
|
||||
import { Core, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
@ -20,21 +8,17 @@ import {
|
||||
ManualRedactionEntryWrapper,
|
||||
} from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { environment } from '@environments/environment';
|
||||
import { AnnotationDrawService } from '../../services/annotation-draw.service';
|
||||
import { AnnotationActionsService } from '../../services/annotation-actions.service';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens';
|
||||
import { AutoUnsubscribe, ConfirmationDialogInput, ErrorService, LoadingService, log } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { AutoUnsubscribe, ErrorService, log } from '@iqser/common-ui';
|
||||
import { toPosition } from '../../utils/pdf-calculation.utils';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { PageRotationService } from '../../../shared/components/pdf-viewer/page-rotation.service';
|
||||
import { HeaderElements, TextPopups } from '../../utils/constants';
|
||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
||||
import { loadCompareDocumentWrapper } from '../../utils/compare-mode.utils';
|
||||
import { from } from 'rxjs';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
import { ViewerHeaderService } from '../../../shared/components/pdf-viewer/viewer-header.service';
|
||||
@ -57,7 +41,6 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
@Output() readonly manualAnnotationRequested = new EventEmitter<ManualRedactionEntryWrapper>();
|
||||
@Output() readonly pageChanged = this.pdf.pageChanged$.pipe(tap(() => this._handleCustomActions()));
|
||||
@Output() readonly keyUp = this.pdf.keyUp$;
|
||||
@ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef;
|
||||
instance: WebViewerInstance;
|
||||
private _selectedText = '';
|
||||
readonly #visibilityOffIcon = this._convertPath('/assets/icons/general/visibility-off.svg');
|
||||
@ -71,12 +54,10 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _manualRedactionService: ManualRedactionService,
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _pageRotationService: PageRotationService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
||||
@ -117,74 +98,6 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
}
|
||||
}
|
||||
|
||||
uploadFile(files: FileList) {
|
||||
const fileToCompare = files[0];
|
||||
this.compareFileInput.nativeElement.value = null;
|
||||
|
||||
if (!fileToCompare) {
|
||||
console.error('No file to compare!');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = async () => {
|
||||
const pdfNet = this.instance.Core.PDFNet;
|
||||
|
||||
await pdfNet.initialize(environment.licenseKey ? window.atob(environment.licenseKey) : null);
|
||||
|
||||
const compareDocument = await pdfNet.PDFDoc.createFromBuffer(fileReader.result as ArrayBuffer);
|
||||
const blob = await this._state.blob;
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||
|
||||
const loadCompareDocument = async () => {
|
||||
this._loadingService.start();
|
||||
const mergedDocument = await pdfNet.PDFDoc.create();
|
||||
const file = this._state.file;
|
||||
await loadCompareDocumentWrapper(
|
||||
currentDocument,
|
||||
compareDocument,
|
||||
mergedDocument,
|
||||
this.instance,
|
||||
file,
|
||||
() => {
|
||||
this.pdf.openCompareMode();
|
||||
},
|
||||
() => {
|
||||
this.pdf.navigateTo(1);
|
||||
},
|
||||
this.instance.Core.PDFNet,
|
||||
);
|
||||
this._viewerHeaderService.disable([HeaderElements.COMPARE_BUTTON]);
|
||||
this._viewerHeaderService.enable([HeaderElements.CLOSE_COMPARE_BUTTON]);
|
||||
this._loadingService.stop();
|
||||
};
|
||||
|
||||
const currentDocumentPageCount = await currentDocument.getPageCount();
|
||||
const compareDocumentPageCount = await compareDocument.getPageCount();
|
||||
|
||||
if (currentDocumentPageCount !== compareDocumentPageCount) {
|
||||
this._dialogService.openDialog(
|
||||
'confirm',
|
||||
null,
|
||||
new ConfirmationDialogInput({
|
||||
title: _('confirmation-dialog.compare-file.title'),
|
||||
question: _('confirmation-dialog.compare-file.question'),
|
||||
translateParams: {
|
||||
fileName: fileToCompare.name,
|
||||
currentDocumentPageCount,
|
||||
compareDocumentPageCount,
|
||||
},
|
||||
}),
|
||||
loadCompareDocument,
|
||||
);
|
||||
} else {
|
||||
await loadCompareDocument();
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsArrayBuffer(fileToCompare);
|
||||
}
|
||||
|
||||
#processSelectedAnnotations(annotations: Annotation[], action) {
|
||||
let nextAnnotations: Annotation[];
|
||||
|
||||
@ -199,9 +112,8 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
nextAnnotations = this._annotationManager.selected;
|
||||
}
|
||||
|
||||
// this.annotationSelected.emit(nextAnnotations.map(ann => ann.Id));
|
||||
if (action === 'deselected') {
|
||||
this._toggleRectangleAnnotationAction(true);
|
||||
this.pdf.disable(TextPopups.ADD_RECTANGLE);
|
||||
return nextAnnotations.map(ann => ann.Id);
|
||||
}
|
||||
|
||||
@ -241,8 +153,8 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
});
|
||||
}
|
||||
|
||||
private _toggleRectangleAnnotationAction(readonly = false) {
|
||||
if (!readonly) {
|
||||
private _toggleRectangleAnnotationAction(disable = false) {
|
||||
if (!disable) {
|
||||
this.pdf.enable(TextPopups.ADD_RECTANGLE);
|
||||
} else {
|
||||
this.pdf.disable(TextPopups.ADD_RECTANGLE);
|
||||
@ -250,7 +162,7 @@ export class PdfPaginatorComponent extends AutoUnsubscribe implements OnInit, On
|
||||
}
|
||||
|
||||
private _configureElements() {
|
||||
this._viewerHeaderService.initialize(this.compareFileInput);
|
||||
// this._viewerHeaderService.initialize(this.compareFileInput);
|
||||
|
||||
const dossierTemplateId = this.dossier.dossierTemplateId;
|
||||
const color = this._annotationDrawService.getAndConvertColor(dossierTemplateId, 'manual');
|
||||
|
||||
@ -201,7 +201,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
async ngOnAttach(previousRoute: ActivatedRouteSnapshot): Promise<void> {
|
||||
async ngOnAttach(previousRoute: ActivatedRouteSnapshot) {
|
||||
if (!this.state.file.canBeOpened) {
|
||||
return this._navigateToDossier();
|
||||
}
|
||||
@ -355,9 +355,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
filter(s => s),
|
||||
tap(() => this.viewerReady()),
|
||||
);
|
||||
|
||||
const currentPageAnnotations$ = combineLatest([this.pdf.currentPage$, this._fileDataService.annotations$]).pipe(
|
||||
map(([page, annotations]) => annotations.filter(annotation => annotation.pageNumber === page)),
|
||||
);
|
||||
|
||||
let start;
|
||||
return combineLatest([currentPageAnnotations$, documentLoaded$]).pipe(
|
||||
tap(() => (start = new Date().getTime())),
|
||||
@ -386,7 +388,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||
let annotationsToDraw: readonly AnnotationWrapper[];
|
||||
const annotations = this._annotationManager.annotations;
|
||||
const ann = annotations.map(a => oldAnnotations.find(oldAnnotation => oldAnnotation.id === a.Id));
|
||||
const ann = annotations.map(a => oldAnnotations.some(oldAnnotation => oldAnnotation.id === a.Id));
|
||||
const hasAnnotations = ann.filter(a => !!a).length > 0;
|
||||
|
||||
if (hasAnnotations) {
|
||||
@ -540,7 +542,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
_('error.deleted-entity.file.action'),
|
||||
'iqser:expand',
|
||||
null,
|
||||
this._navigateToDossier.bind(this),
|
||||
() => this._navigateToDossier(),
|
||||
);
|
||||
this._errorService.set(error);
|
||||
}
|
||||
@ -600,6 +602,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private _navigateToDossier() {
|
||||
this._router.navigate([this._dossiersService.find(this.dossierId)?.routerLink]);
|
||||
return this._router.navigate([this._dossiersService.find(this.dossierId)?.routerLink]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,18 +15,18 @@ export class MultiSelectService {
|
||||
readonly inactive$: Observable<boolean>;
|
||||
|
||||
readonly #active$ = new BehaviorSubject(false);
|
||||
readonly #enabled$ = new BehaviorSubject(true);
|
||||
#enabled = true;
|
||||
|
||||
constructor(viewModeService: ViewModeService, state: FilePreviewStateService) {
|
||||
[this.active$, this.inactive$] = boolFactory(this.#active$.asObservable());
|
||||
this.enabled$ = combineLatest([viewModeService.viewMode$, state.isWritable$]).pipe(
|
||||
map(([viewMode, isWritable]) => isWritable && ENABLED_MULTISELECT.includes(viewMode)),
|
||||
tap(enabled => this.#enabled$.next(enabled)),
|
||||
tap(enabled => (this.#enabled = enabled)),
|
||||
);
|
||||
}
|
||||
|
||||
get isEnabled() {
|
||||
return this.#enabled$.value;
|
||||
return this.#enabled;
|
||||
}
|
||||
|
||||
get isActive() {
|
||||
|
||||
@ -1,12 +1,35 @@
|
||||
import { Component, ElementRef, ViewChild } from '@angular/core';
|
||||
import { PdfViewer } from './pdf-viewer.service';
|
||||
import { REDAnnotationManager } from '@shared/components/pdf-viewer/annotation-manager.service';
|
||||
import { ViewerHeaderService } from '@shared/components/pdf-viewer/viewer-header.service';
|
||||
import { environment } from '@environments/environment';
|
||||
import { loadCompareDocumentWrapper } from '../../../file-preview/utils/compare-mode.utils';
|
||||
import { HeaderElements } from '../../../file-preview/utils/constants';
|
||||
import { ConfirmationDialogInput, LoadingService } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FilesMapService } from '@services/files/files-map.service';
|
||||
import { SharedDialogService } from '@shared/services/dialog.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-pdf-viewer',
|
||||
template: ' <div #viewer></div>',
|
||||
template: `
|
||||
<div #viewer></div>
|
||||
|
||||
<input
|
||||
#compareFileInput
|
||||
(change)="uploadFile($event.target['files'])"
|
||||
accept="application/pdf"
|
||||
class="file-upload-input"
|
||||
type="file"
|
||||
/>
|
||||
`,
|
||||
styles: [
|
||||
`
|
||||
.file-upload-input {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
div {
|
||||
width: calc(100% - 350px);
|
||||
height: calc(100% - 111px);
|
||||
@ -18,9 +41,19 @@ import { REDAnnotationManager } from '@shared/components/pdf-viewer/annotation-m
|
||||
],
|
||||
})
|
||||
export class PdfViewerComponent {
|
||||
@ViewChild('compareFileInput', { static: true }) private readonly _compareFileInput: ElementRef;
|
||||
|
||||
#viewer: ElementRef;
|
||||
|
||||
constructor(private readonly _pdf: PdfViewer, private readonly _annotationManager: REDAnnotationManager) {}
|
||||
constructor(
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _dialogService: SharedDialogService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _annotationManager: REDAnnotationManager,
|
||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
||||
) {}
|
||||
|
||||
@ViewChild('viewer', { static: true })
|
||||
set viewer(value: ElementRef) {
|
||||
@ -30,6 +63,75 @@ export class PdfViewerComponent {
|
||||
|
||||
this.#viewer = value;
|
||||
const pdfInit = this._pdf.init(value.nativeElement as HTMLElement);
|
||||
pdfInit.then(instance => this._annotationManager.init(instance.Core.annotationManager));
|
||||
pdfInit.then(instance => {
|
||||
this._annotationManager.init(instance.Core.annotationManager);
|
||||
console.log(this._compareFileInput);
|
||||
this._viewerHeaderService.initialize(this._compareFileInput);
|
||||
});
|
||||
}
|
||||
|
||||
uploadFile(files: FileList) {
|
||||
const fileToCompare = files[0];
|
||||
this._compareFileInput.nativeElement.value = null;
|
||||
|
||||
if (!fileToCompare) {
|
||||
console.error('No file to compare!');
|
||||
return;
|
||||
}
|
||||
|
||||
const fileReader = new FileReader();
|
||||
fileReader.onload = async () => {
|
||||
const pdfNet = this._pdf.PDFNet;
|
||||
|
||||
await pdfNet.initialize(environment.licenseKey ? window.atob(environment.licenseKey) : null);
|
||||
|
||||
const compareDocument = await pdfNet.PDFDoc.createFromBuffer(fileReader.result as ArrayBuffer);
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this._pdf.blob.arrayBuffer());
|
||||
|
||||
const loadCompareDocument = async () => {
|
||||
this._loadingService.start();
|
||||
const mergedDocument = await pdfNet.PDFDoc.create();
|
||||
const dossierId = this._activatedRoute.snapshot.paramMap.get('dossierId');
|
||||
const fileId = this._activatedRoute.snapshot.paramMap.get('fileId');
|
||||
const file = this._filesMapService.get(dossierId, fileId);
|
||||
await loadCompareDocumentWrapper(
|
||||
currentDocument,
|
||||
compareDocument,
|
||||
mergedDocument,
|
||||
this._pdf.instance,
|
||||
file,
|
||||
() => this._pdf.openCompareMode(),
|
||||
() => this._pdf.navigateTo(1),
|
||||
this._pdf.PDFNet,
|
||||
);
|
||||
this._viewerHeaderService.disable([HeaderElements.COMPARE_BUTTON]);
|
||||
this._viewerHeaderService.enable([HeaderElements.CLOSE_COMPARE_BUTTON]);
|
||||
this._loadingService.stop();
|
||||
};
|
||||
|
||||
const currentDocumentPageCount = await currentDocument.getPageCount();
|
||||
const compareDocumentPageCount = await compareDocument.getPageCount();
|
||||
|
||||
if (currentDocumentPageCount !== compareDocumentPageCount) {
|
||||
this._dialogService.openDialog(
|
||||
'confirm',
|
||||
null,
|
||||
new ConfirmationDialogInput({
|
||||
title: _('confirmation-dialog.compare-file.title'),
|
||||
question: _('confirmation-dialog.compare-file.question'),
|
||||
translateParams: {
|
||||
fileName: fileToCompare.name,
|
||||
currentDocumentPageCount,
|
||||
compareDocumentPageCount,
|
||||
},
|
||||
}),
|
||||
loadCompareDocument,
|
||||
);
|
||||
} else {
|
||||
await loadCompareDocument();
|
||||
}
|
||||
};
|
||||
|
||||
fileReader.readAsArrayBuffer(fileToCompare);
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +44,7 @@ export class PdfViewer {
|
||||
|
||||
#instance: WebViewerInstance;
|
||||
readonly #compareMode$ = new BehaviorSubject(false);
|
||||
#currentBlob: Blob;
|
||||
|
||||
constructor(
|
||||
private readonly _logger: NGXLogger,
|
||||
@ -65,6 +66,10 @@ export class PdfViewer {
|
||||
return this.documentViewer.getDocument();
|
||||
}
|
||||
|
||||
get blob() {
|
||||
return this.#currentBlob;
|
||||
}
|
||||
|
||||
get PDFNet(): typeof Core.PDFNet {
|
||||
return this.#instance.Core.PDFNet;
|
||||
}
|
||||
@ -247,6 +252,7 @@ export class PdfViewer {
|
||||
await pdfNet.initialize(environment.licenseKey ? window.atob(environment.licenseKey) : null);
|
||||
const document = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||
await document.flattenAnnotations(false);
|
||||
this.#currentBlob = blob;
|
||||
|
||||
this.#instance.UI.loadDocument(document, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
||||
}
|
||||
|
||||
@ -200,11 +200,9 @@ export class ViewerHeaderService {
|
||||
dataElement: HeaderElements.COMPARE_BUTTON,
|
||||
img: this._convertPath('/assets/icons/general/pdftron-action-compare.svg'),
|
||||
title: 'Compare',
|
||||
onClick: async () => {
|
||||
onClick: () => {
|
||||
compareFileInput.nativeElement.click();
|
||||
const data = await this._pdf.documentViewer.getDocument().getFileData();
|
||||
const arr = new Uint8Array(data);
|
||||
this.#docBeforeCompare = new Blob([arr], { type: 'application/pdf' });
|
||||
this.#docBeforeCompare = this._pdf.blob;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,13 +1,17 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { AddDossierDialogComponent } from '../dialogs/add-dossier-dialog/add-dossier-dialog.component';
|
||||
import { DialogConfig, DialogService } from '@iqser/common-ui';
|
||||
import { ConfirmationDialogComponent, DialogConfig, DialogService } from '@iqser/common-ui';
|
||||
|
||||
type DialogType = 'addDossier';
|
||||
type DialogType = 'addDossier' | 'confirm';
|
||||
|
||||
@Injectable()
|
||||
export class SharedDialogService extends DialogService<DialogType> {
|
||||
protected readonly _config: DialogConfig<DialogType> = {
|
||||
confirm: {
|
||||
component: ConfirmationDialogComponent,
|
||||
dialogConfig: { disableClose: false },
|
||||
},
|
||||
addDossier: {
|
||||
component: AddDossierDialogComponent,
|
||||
dialogConfig: { width: '900px', autoFocus: true },
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user