fix RED-3519: show imported redactions after converting highlights

This commit is contained in:
Dan Percic 2022-03-22 18:36:52 +02:00
parent 3a19bf7b07
commit b016254bac
7 changed files with 82 additions and 83 deletions

View File

@ -111,7 +111,7 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
useValue: { useValue: {
level: environment.production ? NgxLoggerLevel.ERROR : NgxLoggerLevel.DEBUG, level: environment.production ? NgxLoggerLevel.ERROR : NgxLoggerLevel.DEBUG,
enableSourceMaps: true, enableSourceMaps: true,
timestampFormat: 'hh:mm:ss SSS', timestampFormat: 'mm:ss:SSS',
disableFileDetails: true, disableFileDetails: true,
features: { features: {
ANNOTATIONS: { ANNOTATIONS: {
@ -119,6 +119,9 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
enabled: true, enabled: true,
level: NgxLoggerLevel.DEBUG, level: NgxLoggerLevel.DEBUG,
}, },
FILTERS: {
enabled: true,
},
}, },
} as ILoggerConfig, } as ILoggerConfig,
}, },

View File

@ -24,7 +24,6 @@ export class AnnotationPermissions {
const summedPermissions: AnnotationPermissions = new AnnotationPermissions(); const summedPermissions: AnnotationPermissions = new AnnotationPermissions();
for (const annotation of annotations) { for (const annotation of annotations) {
console.log(annotation.pending);
const permissions: AnnotationPermissions = new AnnotationPermissions(); const permissions: AnnotationPermissions = new AnnotationPermissions();
permissions.canUndo = (!isApprover && annotation.isSuggestion) || annotation.pending; permissions.canUndo = (!isApprover && annotation.isSuggestion) || annotation.pending;

View File

@ -35,17 +35,17 @@ import { toPosition } from '../../../dossier/utils/pdf-calculation.utils';
import { ViewModeService } from '../../services/view-mode.service'; import { ViewModeService } from '../../services/view-mode.service';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { tap, withLatestFrom } from 'rxjs/operators'; import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { FileManagementService } from '@services/entity-services/file-management.service'; import { FileManagementService } from '@services/entity-services/file-management.service';
import { PageRotationService } from '../../services/page-rotation.service'; import { PageRotationService } from '../../services/page-rotation.service';
import { ALLOWED_KEYBOARD_SHORTCUTS, HeaderElements, TextPopups } from '../../shared/constants'; import { ALLOWED_KEYBOARD_SHORTCUTS, HeaderElements, TextPopups } from '../../shared/constants';
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service'; import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
import { loadCompareDocumentWrapper } from '../../../dossier/utils/compare-mode.utils'; import { loadCompareDocumentWrapper } from '../../../dossier/utils/compare-mode.utils';
import { fromEvent } from 'rxjs'; import { from, fromEvent } from 'rxjs';
import { FileDataService } from '../../services/file-data.service';
import Tools = Core.Tools; import Tools = Core.Tools;
import TextTool = Tools.TextTool; import TextTool = Tools.TextTool;
import Annotation = Core.Annotations.Annotation; import Annotation = Core.Annotations.Annotation;
import { FileDataService } from '../../services/file-data.service';
function getDivider(hiddenOn?: readonly ('desktop' | 'mobile' | 'tablet')[]) { function getDivider(hiddenOn?: readonly ('desktop' | 'mobile' | 'tablet')[]) {
return { return {
@ -117,8 +117,8 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.addActiveScreenSubscription = this.stateService.blob$ this.addActiveScreenSubscription = this.stateService.blob$
.pipe( .pipe(
switchMap(blob => from(this.pdf.lockDocument()).pipe(map(() => blob))),
withLatestFrom(this.stateService.file$), withLatestFrom(this.stateService.file$),
tap(() => (this.pdf.ready = false)),
tap(([blob, file]) => this._loadDocument(blob, file)), tap(([blob, file]) => this._loadDocument(blob, file)),
) )
.subscribe(); .subscribe();
@ -155,7 +155,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
const loadCompareDocument = async () => { const loadCompareDocument = async () => {
this._loadingService.start(); this._loadingService.start();
this.pdf.ready = false;
const mergedDocument = await pdfNet.PDFDoc.create(); const mergedDocument = await pdfNet.PDFDoc.create();
const file = await this.stateService.file; const file = await this.stateService.file;
await loadCompareDocumentWrapper( await loadCompareDocumentWrapper(
@ -690,16 +689,14 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
return entry; return entry;
} }
private async _loadDocument(blob: Blob, file: File) { private _loadDocument(blob: Blob, file: File) {
const document = await this.instance.Core.documentViewer.getDocument()?.getPDFDoc();
await document?.lock();
this.instance.UI.loadDocument(blob, { filename: file?.filename ?? 'document.pdf' }); this.instance.UI.loadDocument(blob, { filename: file?.filename ?? 'document.pdf' });
this._pageRotationService.clearRotationsHideActions(); this._pageRotationService.clearRotationsHideActions();
} }
private _setReadyAndInitialState(): void { private _setReadyAndInitialState() {
this._ngZone.run(() => { this._ngZone.run(() => {
this.pdf.documentLoaded$.next(true); this.pdf.emitDocumentLoaded();
const routePageNumber: number = this._activatedRoute.snapshot.queryParams.page; const routePageNumber: number = this._activatedRoute.snapshot.queryParams.page;
this.pageChanged.emit(routePageNumber || 1); this.pageChanged.emit(routePageNumber || 1);
this._setInitialDisplayMode(); this._setInitialDisplayMode();

View File

@ -9,7 +9,6 @@ import {
ErrorService, ErrorService,
FilterService, FilterService,
LoadingService, LoadingService,
log,
NestedFilter, NestedFilter,
OnAttach, OnAttach,
OnDetach, OnDetach,
@ -143,11 +142,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
async updateViewMode(): Promise<void> { async updateViewMode(): Promise<void> {
if (!this.pdf.ready) { this._logger.debug(`[PDF] Update ${this._viewModeService.viewMode} view mode`);
return;
}
this.pdf.deleteAnnotations(this._fileDataService.textHighlights.map(a => a.id));
const annotations = this.pdf.getAnnotations(a => a.getCustomData('redact-manager')); const annotations = this.pdf.getAnnotations(a => a.getCustomData('redact-manager'));
const redactions = annotations.filter(a => a.getCustomData('redaction')); const redactions = annotations.filter(a => a.getCustomData('redaction'));
@ -233,14 +228,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.displayPdfViewer = true; this.displayPdfViewer = true;
} }
async rebuildFilters(deletePreviousAnnotations = false) { async rebuildFilters() {
const startTime = new Date().getTime(); const startTime = new Date().getTime();
if (deletePreviousAnnotations) {
this.pdf.deleteAnnotations();
console.log(`[REDACTION] Delete previous annotations time: ${new Date().getTime() - startTime} ms`);
}
const processStartTime = new Date().getTime();
const visibleAnnotations = await this._fileDataService.visibleAnnotations; const visibleAnnotations = await this._fileDataService.visibleAnnotations;
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(visibleAnnotations); const annotationFilters = this._annotationProcessingService.getAnnotationFilter(visibleAnnotations);
@ -260,8 +249,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
), ),
}); });
this._logger.debug(`[REDACTION] Process time: ${new Date().getTime() - processStartTime} ms`); this._logger.debug(`[FILTERS] Rebuild time: ${new Date().getTime() - startTime} ms`);
this._logger.debug(`[REDACTION] Filter rebuild time: ${new Date().getTime() - startTime}`);
} }
async handleAnnotationSelected(annotationIds: string[]) { async handleAnnotationSelected(annotationIds: string[]) {
@ -370,8 +358,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
viewerReady() { viewerReady() {
this.ready = true; this.ready = true;
this.pdf.ready = true;
this._setExcludedPageStyles(); this._setExcludedPageStyles();
this.pdf.documentViewer.addEventListener('pageComplete', () => { this.pdf.documentViewer.addEventListener('pageComplete', () => {
@ -426,7 +412,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
let start; let start;
return combineLatest([documentLoaded$, this._fileDataService.annotations$]).pipe( return combineLatest([documentLoaded$, this._fileDataService.annotations$]).pipe(
debounceTime(300), debounceTime(300),
log(),
tap(() => (start = new Date().getTime())), tap(() => (start = new Date().getTime())),
map(([, annotations]) => annotations), map(([, annotations]) => annotations),
startWith({} as Record<string, AnnotationWrapper>), startWith({} as Record<string, AnnotationWrapper>),
@ -451,7 +436,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
drawChangedAnnotations(oldAnnotations: Record<string, AnnotationWrapper>, newAnnotations: Record<string, AnnotationWrapper>) { drawChangedAnnotations(oldAnnotations: Record<string, AnnotationWrapper>, newAnnotations: Record<string, AnnotationWrapper>) {
let annotationsToDraw: readonly AnnotationWrapper[]; let annotationsToDraw: readonly AnnotationWrapper[];
if (this.pdf.hasAnnotations) { const ann = this.pdf.annotationManager.getAnnotationsList().map(a => oldAnnotations[a.Id]);
const hasAnnotations = ann.filter(a => !!a).length > 0;
if (hasAnnotations) {
annotationsToDraw = this.#getAnnotationsToDraw(newAnnotations, oldAnnotations); annotationsToDraw = this.#getAnnotationsToDraw(newAnnotations, oldAnnotations);
} else { } else {
annotationsToDraw = Object.values(newAnnotations); annotationsToDraw = Object.values(newAnnotations);
@ -511,18 +499,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
private async _stampPDF() { private async _stampPDF() {
const pdfDoc = await this.pdf.documentViewer.getDocument().getPDFDoc(); const pdfDoc = await this.pdf.documentViewer.getDocument()?.getPDFDoc();
const file = await this.stateService.file; if (!pdfDoc) {
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
if (!pdfDoc || !this.pdf.ready) {
return; return;
} }
const file = await this.stateService.file;
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
try { try {
await clearStamps(pdfDoc, this.pdf.PDFNet, allPages); await clearStamps(pdfDoc, this.pdf.PDFNet, allPages);
} catch (e) { } catch (e) {
this._logger.debug('Error clearing stamps: ', e); this._logger.error('Error clearing stamps: ', e);
return; return;
} }
@ -616,10 +604,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
} }
private async _cleanupAndRedrawAnnotations(newAnnotations: readonly AnnotationWrapper[]) { private async _cleanupAndRedrawAnnotations(newAnnotations: readonly AnnotationWrapper[]) {
if (!this.pdf.ready) {
return;
}
const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || []; const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || [];
await this.rebuildFilters(); await this.rebuildFilters();

View File

@ -37,10 +37,6 @@ export class AnnotationDrawService {
) {} ) {}
drawAnnotations(annotationWrappers: readonly AnnotationWrapper[]) { drawAnnotations(annotationWrappers: readonly AnnotationWrapper[]) {
if (!this._pdf.instance || !this._pdf.ready) {
return;
}
const licenceKey = environment.licenseKey ? atob(environment.licenseKey) : null; const licenceKey = environment.licenseKey ? atob(environment.licenseKey) : null;
return this._pdf.PDFNet.runWithCleanup(() => this._drawAnnotations(annotationWrappers), licenceKey); return this._pdf.PDFNet.runWithCleanup(() => this._drawAnnotations(annotationWrappers), licenceKey);
} }
@ -89,13 +85,6 @@ export class AnnotationDrawService {
} }
private async _drawAnnotations(annotationWrappers: readonly AnnotationWrapper[]) { private async _drawAnnotations(annotationWrappers: readonly AnnotationWrapper[]) {
const document = await this._pdf.documentViewer.getDocument()?.getPDFDoc();
if (!this._pdf.ready || !document) {
return;
}
await document.lock();
const annotations = annotationWrappers.map(annotation => this._computeAnnotation(annotation)).filter(a => !!a); const annotations = annotationWrappers.map(annotation => this._computeAnnotation(annotation)).filter(a => !!a);
this._pdf.annotationManager.addAnnotations(annotations, { imported: true }); this._pdf.annotationManager.addAnnotations(annotations, { imported: true });
await this._pdf.annotationManager.drawAnnotationsFromList(annotations); await this._pdf.annotationManager.drawAnnotationsFromList(annotations);

View File

@ -21,7 +21,7 @@ import { DictionariesMapService } from '../../../services/entity-services/dictio
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
import { PermissionsService } from '../../../services/permissions.service'; import { PermissionsService } from '../../../services/permissions.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { log, shareDistinctLast, shareLast, Toaster } from '../../../../../../../libs/common-ui/src'; import { shareDistinctLast, shareLast, Toaster } from '../../../../../../../libs/common-ui/src';
import { RedactionLogService } from '../../dossier/services/redaction-log.service'; import { RedactionLogService } from '../../dossier/services/redaction-log.service';
import { TextHighlightService } from '../../dossier/services/text-highlight.service'; import { TextHighlightService } from '../../dossier/services/text-highlight.service';
import { ViewModeService } from './view-mode.service'; import { ViewModeService } from './view-mode.service';
@ -66,7 +66,6 @@ export class FileDataService {
this.annotations$.pipe(map(annotations => this.getVisibleAnnotations(Object.values(annotations), viewMode))), this.annotations$.pipe(map(annotations => this.getVisibleAnnotations(Object.values(annotations), viewMode))),
), ),
), ),
log('Visible annotations: '),
shareDistinctLast(), shareDistinctLast(),
); );
} }

View File

@ -7,20 +7,37 @@ import { Inject, Injectable } from '@angular/core';
import { BASE_HREF } from '../../../tokens'; import { BASE_HREF } from '../../../tokens';
import { environment } from '@environments/environment'; import { environment } from '@environments/environment';
import { DISABLED_HOTKEYS } from '../shared/constants'; import { DISABLED_HOTKEYS } from '../shared/constants';
import { Subject } from 'rxjs'; import { Observable, Subject } from 'rxjs';
import { NGXLogger } from 'ngx-logger';
import { tap } from 'rxjs/operators';
import { shareLast } from '../../../../../../../libs/common-ui/src';
import Annotation = Core.Annotations.Annotation; import Annotation = Core.Annotations.Annotation;
import DocumentViewer = Core.DocumentViewer;
import AnnotationManager = Core.AnnotationManager;
@Injectable() @Injectable()
export class PdfViewer { export class PdfViewer {
ready = false; instance?: WebViewerInstance;
instance: WebViewerInstance;
documentViewer: DocumentViewer;
annotationManager: AnnotationManager;
readonly documentLoaded$ = new Subject();
constructor(@Inject(BASE_HREF) private readonly _baseHref: string, readonly viewModeService: ViewModeService) {} readonly documentLoaded$: Observable<boolean>;
readonly #documentLoaded$ = new Subject<boolean>();
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
private readonly _viewModeService: ViewModeService,
private readonly _logger: NGXLogger,
) {
this.documentLoaded$ = this.#documentLoaded$.asObservable().pipe(
tap(() => this._logger.debug('[PDF] Loaded')),
shareLast(),
);
}
get documentViewer() {
return this.instance?.Core.documentViewer;
}
get annotationManager() {
return this.instance?.Core.annotationManager;
}
get UI() { get UI() {
return this.instance.UI; return this.instance.UI;
@ -39,19 +56,15 @@ export class PdfViewer {
} }
get paginationOffset() { get paginationOffset() {
return this.viewModeService.isCompare ? 2 : 1; return this._viewModeService.isCompare ? 2 : 1;
} }
get currentPage() { get currentPage() {
return this.viewModeService.isCompare ? Math.ceil(this._currentInternalPage / 2) : this._currentInternalPage; return this._viewModeService.isCompare ? Math.ceil(this._currentInternalPage / 2) : this._currentInternalPage;
} }
get totalPages() { get totalPages() {
if (!this.ready) { return this._viewModeService.isCompare ? Math.ceil(this._totalInternalPages / 2) : this._totalInternalPages;
return null;
}
return this.viewModeService.isCompare ? Math.ceil(this._totalInternalPages / 2) : this._totalInternalPages;
} }
private get _currentInternalPage() { private get _currentInternalPage() {
@ -67,6 +80,28 @@ export class PdfViewer {
} }
} }
async lockDocument() {
const document = await this.documentViewer.getDocument()?.getPDFDoc();
if (!document) {
return false;
}
await document.lock();
this._logger.debug('[PDF] Locked');
return true;
}
async unlockDocument() {
const document = await this.documentViewer.getDocument()?.getPDFDoc();
if (!document) {
return false;
}
await document.unlock();
this._logger.debug('[PDF] Unlocked');
return true;
}
hideAnnotations(annotations: Annotation[]): void { hideAnnotations(annotations: Annotation[]): void {
this.annotationManager.hideAnnotations(annotations); this.annotationManager.hideAnnotations(annotations);
} }
@ -88,6 +123,11 @@ export class PdfViewer {
return []; return [];
} }
emitDocumentLoaded() {
this.deleteAnnotations();
this.#documentLoaded$.next(true);
}
async loadViewer(htmlElement: HTMLElement) { async loadViewer(htmlElement: HTMLElement) {
this.instance = await WebViewer( this.instance = await WebViewer(
{ {
@ -100,9 +140,6 @@ export class PdfViewer {
htmlElement, htmlElement,
); );
this.documentViewer = this.instance.Core.documentViewer;
this.annotationManager = this.instance.Core.annotationManager;
return this.instance; return this.instance;
} }
@ -138,11 +175,7 @@ export class PdfViewer {
} }
deselectAllAnnotations() { deselectAllAnnotations() {
if (!this.ready) { this.annotationManager?.deselectAllAnnotations();
return;
}
this.annotationManager.deselectAllAnnotations();
} }
selectAnnotations(annotations: AnnotationWrapper[], multiSelectActive: boolean = false) { selectAnnotations(annotations: AnnotationWrapper[], multiSelectActive: boolean = false) {
@ -167,10 +200,6 @@ export class PdfViewer {
} }
deleteAnnotations(annotationsIds?: readonly string[]) { deleteAnnotations(annotationsIds?: readonly string[]) {
if (!this.ready) {
return;
}
let annotations: Annotation[]; let annotations: Annotation[];
if (!annotationsIds) { if (!annotationsIds) {
annotations = this.getAnnotations(); annotations = this.getAnnotations();
@ -180,7 +209,6 @@ export class PdfViewer {
try { try {
this.annotationManager.deleteAnnotations(annotations, { this.annotationManager.deleteAnnotations(annotations, {
imported: true,
force: true, force: true,
}); });
} catch (error) { } catch (error) {