draw only changed annotations
This commit is contained in:
parent
7399f44881
commit
be8316f609
@ -1,6 +1,8 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { GenericService, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';
|
||||
import { IRedactionLog, ISectionGrid } from '@red/domain';
|
||||
import { catchError, tap } from 'rxjs/operators';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
@ -17,7 +19,11 @@ export class RedactionLogService extends GenericService<unknown> {
|
||||
queryParams.push({ key: 'withManualRedactions', value: withManualRedactions });
|
||||
}
|
||||
|
||||
return this._getOne<IRedactionLog>([dossierId, fileId], 'redactionLog', queryParams);
|
||||
const redactionLog$ = this._getOne<IRedactionLog>([dossierId, fileId], 'redactionLog', queryParams);
|
||||
return redactionLog$.pipe(
|
||||
tap(redactionLog => redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page)),
|
||||
catchError(() => of({} as IRedactionLog)),
|
||||
);
|
||||
}
|
||||
|
||||
@Validate()
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div *ngIf="references$ | async as references" class="content-container">
|
||||
<div *ngIf="annotationReferencesService.references$ | async as references" class="content-container">
|
||||
<div class="dialog references-dialog">
|
||||
<div class="references-header flex">
|
||||
<div class="small-label uppercase">
|
||||
|
||||
@ -1,10 +1,6 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||
import { Observable, switchMap } from 'rxjs';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
import { filterEach } from '../../../../../../../../libs/common-ui/src';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-references-list',
|
||||
@ -15,16 +11,8 @@ import { filterEach } from '../../../../../../../../libs/common-ui/src';
|
||||
export class AnnotationReferencesListComponent {
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Output() readonly referenceClicked = new EventEmitter<AnnotationWrapper>();
|
||||
references$ = this._annotationReferences;
|
||||
|
||||
constructor(readonly annotationReferencesService: AnnotationReferencesService, private readonly _fileDataService: FileDataService) {}
|
||||
|
||||
private get _annotationReferences(): Observable<AnnotationWrapper[]> {
|
||||
return this.annotationReferencesService.annotation$.pipe(
|
||||
filter(annotation => !!annotation),
|
||||
switchMap(({ reference }) => this._fileDataService.annotations$.pipe(filterEach(a => reference.includes(a.annotationId)))),
|
||||
);
|
||||
}
|
||||
constructor(readonly annotationReferencesService: AnnotationReferencesService) {}
|
||||
|
||||
isSelected(annotationId: string): boolean {
|
||||
return this.selectedAnnotations.some(a => a.annotationId === annotationId);
|
||||
|
||||
@ -90,7 +90,7 @@
|
||||
(selectAnnotations)="selectAnnotations($event)"
|
||||
(selectPage)="selectPage($event)"
|
||||
*ngIf="!file.excluded"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[activeViewerPage]="pdf.currentPage"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[dialogRef]="dialogRef"
|
||||
[file]="file"
|
||||
|
||||
@ -64,11 +64,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
fullScreen = false;
|
||||
selectedAnnotations: AnnotationWrapper[] = [];
|
||||
displayPdfViewer = false;
|
||||
activeViewerPage: number = null;
|
||||
readonly canPerformAnnotationActions$: Observable<boolean>;
|
||||
readonly fileId = this.stateService.fileId;
|
||||
readonly dossierId = this.stateService.dossierId;
|
||||
readonly file$ = this.stateService.file$.pipe(tap(file => this._loadFileData(file)));
|
||||
readonly file$ = this.stateService.file$.pipe(tap(() => this._fileDataService.loadAnnotations()));
|
||||
ready = false;
|
||||
private _lastPage: string;
|
||||
|
||||
@ -80,38 +79,37 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _filterTemplate: TemplateRef<unknown>;
|
||||
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
private readonly _watermarkService: WatermarkService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
readonly pdf: PdfViewer,
|
||||
private readonly _router: Router,
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _ngZone: NgZone,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _filesService: FilesService,
|
||||
private readonly _errorService: ErrorService,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
private readonly _filterService: FilterService,
|
||||
private readonly _translateService: TranslateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _skippedService: SkippedService,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _pageRotationService: PageRotationService,
|
||||
private readonly _skippedService: SkippedService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
private readonly _pdf: PdfViewer,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
readonly excludedPagesService: ExcludedPagesService,
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
readonly excludedPagesService: ExcludedPagesService,
|
||||
private readonly _watermarkService: WatermarkService,
|
||||
private readonly _translateService: TranslateService,
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
private readonly _pageRotationService: PageRotationService,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||
) {
|
||||
super();
|
||||
this.bla();
|
||||
this.canPerformAnnotationActions$ = this._canPerformAnnotationActions$;
|
||||
|
||||
document.documentElement.addEventListener('fullscreenchange', () => {
|
||||
@ -142,13 +140,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
async updateViewMode(): Promise<void> {
|
||||
if (!this._pdf.ready) {
|
||||
if (!this.pdf.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._pdf.deleteAnnotations(this._fileDataService.textHighlights.map(a => a.id));
|
||||
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'));
|
||||
|
||||
switch (this._viewModeService.viewMode) {
|
||||
@ -162,8 +160,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
.filter(a => !ocrAnnotationIds.includes(a.Id));
|
||||
const nonStandardEntries = annotations.filter(a => a.getCustomData('changeLogRemoved') === 'true');
|
||||
this._setAnnotationsOpacity(standardEntries, true);
|
||||
this._pdf.showAnnotations(standardEntries);
|
||||
this._pdf.hideAnnotations(nonStandardEntries);
|
||||
this.pdf.showAnnotations(standardEntries);
|
||||
this.pdf.hideAnnotations(nonStandardEntries);
|
||||
break;
|
||||
}
|
||||
case 'DELTA': {
|
||||
@ -171,21 +169,21 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
const nonChangeLogEntries = annotations.filter(a => a.getCustomData('changeLog') === 'false');
|
||||
this._setAnnotationsColor(redactions, 'annotationColor');
|
||||
this._setAnnotationsOpacity(changeLogEntries, true);
|
||||
this._pdf.showAnnotations(changeLogEntries);
|
||||
this._pdf.hideAnnotations(nonChangeLogEntries);
|
||||
this.pdf.showAnnotations(changeLogEntries);
|
||||
this.pdf.hideAnnotations(nonChangeLogEntries);
|
||||
break;
|
||||
}
|
||||
case 'REDACTED': {
|
||||
const nonRedactionEntries = annotations.filter(a => a.getCustomData('redaction') === 'false');
|
||||
this._setAnnotationsOpacity(redactions);
|
||||
this._setAnnotationsColor(redactions, 'redactionColor');
|
||||
this._pdf.showAnnotations(redactions);
|
||||
this._pdf.hideAnnotations(nonRedactionEntries);
|
||||
this.pdf.showAnnotations(redactions);
|
||||
this.pdf.hideAnnotations(nonRedactionEntries);
|
||||
break;
|
||||
}
|
||||
case 'TEXT_HIGHLIGHTS': {
|
||||
this._loadingService.start();
|
||||
this._pdf.hideAnnotations(annotations);
|
||||
this.pdf.hideAnnotations(annotations);
|
||||
const highlights = await this._fileDataService.loadTextHighlights();
|
||||
await this._annotationDrawService.drawAnnotations(highlights);
|
||||
this._loadingService.stop();
|
||||
@ -234,7 +232,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
async rebuildFilters(deletePreviousAnnotations = false) {
|
||||
const startTime = new Date().getTime();
|
||||
if (deletePreviousAnnotations) {
|
||||
this._pdf.deleteAnnotations();
|
||||
this.pdf.deleteAnnotations();
|
||||
|
||||
console.log(`[REDACTION] Delete previous annotations time: ${new Date().getTime() - startTime} ms`);
|
||||
}
|
||||
@ -277,14 +275,14 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
selectAnnotations(annotations?: AnnotationWrapper[]) {
|
||||
if (annotations) {
|
||||
const annotationsToSelect = this.multiSelectService.isActive ? [...this.selectedAnnotations, ...annotations] : annotations;
|
||||
this._pdf.selectAnnotations(annotationsToSelect, this.multiSelectService.isActive);
|
||||
this.pdf.selectAnnotations(annotationsToSelect, this.multiSelectService.isActive);
|
||||
} else {
|
||||
this._pdf.deselectAllAnnotations();
|
||||
this.pdf.deselectAllAnnotations();
|
||||
}
|
||||
}
|
||||
|
||||
selectPage(pageNumber: number) {
|
||||
this._pdf.navigateToPage(pageNumber);
|
||||
this.pdf.navigateToPage(pageNumber);
|
||||
this._workloadComponent?.scrollAnnotationsToPage(pageNumber, 'always');
|
||||
this._lastPage = pageNumber.toString();
|
||||
}
|
||||
@ -298,7 +296,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
async ({ manualRedactionEntry }: ManualRedactionEntryWrapper) => {
|
||||
const addAnnotation$ = this._manualAnnotationService.addAnnotation(manualRedactionEntry, this.dossierId, this.fileId);
|
||||
await firstValueFrom(addAnnotation$.pipe(catchError(() => of(undefined))));
|
||||
await this._fileDataService.load(await this.stateService.file);
|
||||
await this._fileDataService.loadAnnotations();
|
||||
},
|
||||
);
|
||||
});
|
||||
@ -363,17 +361,16 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
};
|
||||
await this._router.navigate([], extras);
|
||||
|
||||
this.activeViewerPage = this._pdf.currentPage;
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
viewerReady() {
|
||||
this.ready = true;
|
||||
this._pdf.ready = true;
|
||||
this.pdf.ready = true;
|
||||
|
||||
this._setExcludedPageStyles();
|
||||
|
||||
this._pdf.documentViewer.addEventListener('pageComplete', () => {
|
||||
this.pdf.documentViewer.addEventListener('pageComplete', () => {
|
||||
this._setExcludedPageStyles();
|
||||
});
|
||||
|
||||
@ -382,7 +379,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
if (pageNumber) {
|
||||
setTimeout(() => {
|
||||
this.selectPage(parseInt(pageNumber, 10));
|
||||
this.activeViewerPage = this._pdf.currentPage;
|
||||
this._scrollViews();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
this._loadingService.stop();
|
||||
@ -421,41 +417,37 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
download(await firstValueFrom(originalFile), file.filename);
|
||||
}
|
||||
|
||||
bla() {
|
||||
const documentLoaded$ = this._pdf.documentLoaded$.pipe(tap(() => this.viewerReady()));
|
||||
loadAnnotations() {
|
||||
const documentLoaded$ = this.pdf.documentLoaded$.pipe(tap(() => this.viewerReady()));
|
||||
let start;
|
||||
combineLatest([documentLoaded$, this._fileDataService.annotations$])
|
||||
.pipe(
|
||||
withLatestFrom(this.stateService.file$),
|
||||
filter(([, file]) => !file.isProcessing),
|
||||
tap(() => (start = new Date().getTime())),
|
||||
map(([[, annotations]]) => annotations),
|
||||
startWith([] as AnnotationWrapper[]),
|
||||
pairwise(),
|
||||
tap(annotations => this.deleteAnnotations(...annotations)),
|
||||
switchMap(annotations => this.drawChangedAnnotations(...annotations)),
|
||||
tap(() => console.log(`%c [ANNOTATIONS] Processing time: ${new Date().getTime() - start}`, 'color: aqua')),
|
||||
tap(() => this.updateViewMode()),
|
||||
)
|
||||
.subscribe();
|
||||
return combineLatest([documentLoaded$, this._fileDataService.annotations$]).pipe(
|
||||
withLatestFrom(this.stateService.file$),
|
||||
filter(([, file]) => !file.isProcessing),
|
||||
tap(() => (start = new Date().getTime())),
|
||||
map(([[, annotations]]) => annotations),
|
||||
startWith({} as Record<string, AnnotationWrapper>),
|
||||
pairwise(),
|
||||
tap(annotations => this.deleteAnnotations(...annotations)),
|
||||
switchMap(annotations => this.drawChangedAnnotations(...annotations)),
|
||||
tap(() => console.log(`%c [ANNOTATIONS] Processing time: ${new Date().getTime() - start}`, 'color: aqua')),
|
||||
tap(() => this.updateViewMode()),
|
||||
);
|
||||
}
|
||||
|
||||
deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||
const annotationsToDelete = oldAnnotations.filter(
|
||||
oldAnnotation => !newAnnotations.some(newAnnotation => newAnnotation.id === oldAnnotation.id),
|
||||
);
|
||||
deleteAnnotations(oldAnnotations: Record<string, AnnotationWrapper>, newAnnotations: Record<string, AnnotationWrapper>) {
|
||||
const annotationsToDelete = Object.values(oldAnnotations).filter(oldAnnotation => !newAnnotations[oldAnnotation.id]);
|
||||
|
||||
if (annotationsToDelete.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('%c [ANNOTATIONS] To delete: ', 'color: aqua', annotationsToDelete);
|
||||
this._pdf.deleteAnnotations(annotationsToDelete.map(annotation => annotation.id));
|
||||
this.pdf.deleteAnnotations(annotationsToDelete.map(annotation => annotation.id));
|
||||
}
|
||||
|
||||
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||
const annotationsToDraw = newAnnotations.filter(newAnnotation => {
|
||||
const oldAnnotation = oldAnnotations.find(annotation => annotation.id === newAnnotation.id);
|
||||
drawChangedAnnotations(oldAnnotations: Record<string, AnnotationWrapper>, newAnnotations: Record<string, AnnotationWrapper>) {
|
||||
const annotationsToDraw = Object.values(newAnnotations).filter(newAnnotation => {
|
||||
const oldAnnotation = oldAnnotations[newAnnotation.id];
|
||||
if (!oldAnnotation) {
|
||||
return true;
|
||||
}
|
||||
@ -480,23 +472,23 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
console.log('%c [ANNOTATIONS] To draw: ', 'color: aqua', annotationsToDraw);
|
||||
const annotationsToDrawIds = annotationsToDraw.map(a => a.annotationId);
|
||||
this._pdf.deleteAnnotations(annotationsToDrawIds);
|
||||
return this._cleanupAndRedrawAnnotations(annotation => annotationsToDrawIds.includes(annotation.annotationId));
|
||||
this.pdf.deleteAnnotations(annotationsToDrawIds);
|
||||
return this._cleanupAndRedrawAnnotations(annotationsToDraw);
|
||||
}
|
||||
|
||||
async #deactivateMultiSelect() {
|
||||
this.multiSelectService.deactivate();
|
||||
this._pdf.deselectAllAnnotations();
|
||||
this.pdf.deselectAllAnnotations();
|
||||
await this.handleAnnotationSelected([]);
|
||||
}
|
||||
|
||||
private _setExcludedPageStyles() {
|
||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||
setTimeout(() => {
|
||||
const iframeDoc = this._pdf.UI.iframeWindow.document;
|
||||
const pageContainer = iframeDoc.getElementById(`pageWidgetContainer${this.activeViewerPage}`);
|
||||
const iframeDoc = this.pdf.UI.iframeWindow.document;
|
||||
const pageContainer = iframeDoc.getElementById(`pageWidgetContainer${this.pdf.currentPage}`);
|
||||
if (pageContainer) {
|
||||
if (file.excludedPages.includes(this.activeViewerPage)) {
|
||||
if (file.excludedPages.includes(this.pdf.currentPage)) {
|
||||
pageContainer.classList.add('excluded-page');
|
||||
} else {
|
||||
pageContainer.classList.remove('excluded-page');
|
||||
@ -506,16 +498,16 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
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;
|
||||
const allPages = [...Array(file.numberOfPages).keys()].map(page => page + 1);
|
||||
|
||||
if (!pdfDoc || !this._pdf.ready) {
|
||||
if (!pdfDoc || !this.pdf.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await clearStamps(pdfDoc, this._pdf.PDFNet, allPages);
|
||||
await clearStamps(pdfDoc, this.pdf.PDFNet, allPages);
|
||||
} catch (e) {
|
||||
console.log('Error clearing stamps: ', e);
|
||||
return;
|
||||
@ -529,8 +521,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
} else {
|
||||
await this._stampExcludedPages(pdfDoc, file.excludedPages);
|
||||
}
|
||||
this._pdf.documentViewer.refreshAll();
|
||||
this._pdf.documentViewer.updateView([this.activeViewerPage], this.activeViewerPage);
|
||||
|
||||
this.pdf.documentViewer.refreshAll();
|
||||
this.pdf.documentViewer.updateView([this.pdf.currentPage], this.pdf.currentPage);
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
@ -538,7 +531,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
const watermark = await firstValueFrom(this._watermarkService.getWatermark(dossierTemplateId));
|
||||
await stampPDFPage(
|
||||
document,
|
||||
this._pdf.PDFNet,
|
||||
this.pdf.PDFNet,
|
||||
watermark.text,
|
||||
watermark.fontSize,
|
||||
watermark.fontType,
|
||||
@ -553,7 +546,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
if (excludedPages && excludedPages.length > 0) {
|
||||
await stampPDFPage(
|
||||
document,
|
||||
this._pdf.PDFNet,
|
||||
this.pdf.PDFNet,
|
||||
this._translateService.instant('file-preview.excluded-from-redaction') as string,
|
||||
17,
|
||||
'courier',
|
||||
@ -566,6 +559,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private _subscribeToFileUpdates(): void {
|
||||
this.addActiveScreenSubscription = this.loadAnnotations().subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = timer(0, 5000)
|
||||
.pipe(
|
||||
switchMap(() => this.stateService.file$),
|
||||
@ -600,27 +595,14 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
);
|
||||
}
|
||||
|
||||
private async _loadFileData(file: File): Promise<void | boolean> {
|
||||
if (!file || file.isError) {
|
||||
const dossier = await this.stateService.dossier;
|
||||
return this._router.navigate([dossier.routerLink]);
|
||||
}
|
||||
|
||||
if (file.isUnprocessed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this._fileDataService.load(file);
|
||||
}
|
||||
|
||||
@Debounce(0)
|
||||
private _scrollViews() {
|
||||
this._workloadComponent?.scrollQuickNavigation();
|
||||
this._workloadComponent?.scrollAnnotations();
|
||||
}
|
||||
|
||||
private async _cleanupAndRedrawAnnotations(newAnnotationsFilter?: (annotation: AnnotationWrapper) => boolean) {
|
||||
if (!this._pdf.ready) {
|
||||
private async _cleanupAndRedrawAnnotations(newAnnotations: AnnotationWrapper[]) {
|
||||
if (!this.pdf.ready) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -628,8 +610,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
await this.rebuildFilters();
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
const annotations = await this._fileDataService.annotations;
|
||||
const newAnnotations = newAnnotationsFilter ? annotations.filter(newAnnotationsFilter) : annotations;
|
||||
|
||||
if (currentFilters) {
|
||||
const visibleAnnotations = await this._fileDataService.visibleAnnotations;
|
||||
@ -670,11 +650,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private _handleIgnoreAnnotationsDrawing(hideSkipped: boolean): void {
|
||||
const ignored = this._pdf.getAnnotations(a => a.getCustomData('skipped'));
|
||||
const ignored = this.pdf.getAnnotations(a => a.getCustomData('skipped'));
|
||||
if (hideSkipped) {
|
||||
this._pdf.hideAnnotations(ignored);
|
||||
this.pdf.hideAnnotations(ignored);
|
||||
} else {
|
||||
this._pdf.showAnnotations(ignored);
|
||||
this.pdf.showAnnotations(ignored);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,15 +1,27 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { shareDistinctLast } from '@iqser/common-ui';
|
||||
import { BehaviorSubject, Observable, switchMap } from 'rxjs';
|
||||
import { filterEach, shareDistinctLast } from '@iqser/common-ui';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { FileDataService } from './file-data.service';
|
||||
|
||||
@Injectable()
|
||||
export class AnnotationReferencesService {
|
||||
readonly annotation$: Observable<AnnotationWrapper>;
|
||||
readonly references$: Observable<AnnotationWrapper[]>;
|
||||
private readonly _annotation$ = new BehaviorSubject<AnnotationWrapper | undefined>(undefined);
|
||||
|
||||
constructor() {
|
||||
constructor(private readonly _fileDataService: FileDataService) {
|
||||
this.annotation$ = this._annotation$.asObservable().pipe(shareDistinctLast());
|
||||
this.references$ = this.#references$;
|
||||
}
|
||||
|
||||
get #references$(): Observable<AnnotationWrapper[]> {
|
||||
const annotations$ = this._fileDataService.annotations$.pipe(map(dict => Object.values(dict)));
|
||||
return this.annotation$.pipe(
|
||||
filter(annotation => !!annotation),
|
||||
switchMap(({ reference }) => annotations$.pipe(filterEach(a => reference.includes(a.annotationId)))),
|
||||
);
|
||||
}
|
||||
|
||||
show(annotation: AnnotationWrapper) {
|
||||
|
||||
@ -12,21 +12,22 @@ import {
|
||||
} from '@red/domain';
|
||||
import { AnnotationWrapper } from '../../../models/file/annotation.wrapper';
|
||||
import * as moment from 'moment';
|
||||
import { BehaviorSubject, firstValueFrom, iif, Observable, of } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, iif, Observable, Subject } from 'rxjs';
|
||||
import { RedactionLogEntry } from '../../../models/file/redaction-log.entry';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FilePreviewStateService } from './file-preview-state.service';
|
||||
import { ViewedPagesService } from '../../../services/entity-services/viewed-pages.service';
|
||||
import { UserPreferenceService } from '../../../services/user-preference.service';
|
||||
import { DictionariesMapService } from '../../../services/entity-services/dictionaries-map.service';
|
||||
import { catchError, map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import { PermissionsService } from '../../../services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { shareLast, Toaster } from '../../../../../../../libs/common-ui/src';
|
||||
import { log, shareDistinctLast, shareLast, Toaster } from '../../../../../../../libs/common-ui/src';
|
||||
import { RedactionLogService } from '../../dossier/services/redaction-log.service';
|
||||
import { TextHighlightService } from '../../dossier/services/text-highlight.service';
|
||||
import { ViewModeService } from './view-mode.service';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import { Router } from '@angular/router';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||
@ -34,14 +35,14 @@ const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||
@Injectable()
|
||||
export class FileDataService {
|
||||
viewedPages: IViewedPage[] = [];
|
||||
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||
missingTypes = new Set<string>();
|
||||
shouldUpdateAnnotations = false;
|
||||
readonly annotations$: Observable<AnnotationWrapper[]>;
|
||||
|
||||
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||
readonly annotations$: Observable<Record<string, AnnotationWrapper>>;
|
||||
readonly visibleAnnotations$: Observable<AnnotationWrapper[]>;
|
||||
readonly hiddenAnnotations = new Set<string>();
|
||||
|
||||
readonly #redactionLog$ = new BehaviorSubject<IRedactionLog>({});
|
||||
readonly #redactionLog$ = new Subject<IRedactionLog>();
|
||||
readonly #textHighlights$ = new BehaviorSubject<AnnotationWrapper[]>([]);
|
||||
|
||||
constructor(
|
||||
@ -54,6 +55,7 @@ export class FileDataService {
|
||||
private readonly _redactionLogService: RedactionLogService,
|
||||
private readonly _textHighlightsService: TextHighlightService,
|
||||
private readonly _toaster: Toaster,
|
||||
private readonly _router: Router,
|
||||
) {
|
||||
this.annotations$ = this.#annotations$;
|
||||
this.visibleAnnotations$ = this._viewModeService.viewMode$.pipe(
|
||||
@ -61,10 +63,11 @@ export class FileDataService {
|
||||
iif(
|
||||
() => viewMode === ViewModes.TEXT_HIGHLIGHTS,
|
||||
this.#textHighlights$,
|
||||
this.annotations$.pipe(map(annotations => this.getVisibleAnnotations(annotations, viewMode))),
|
||||
this.annotations$.pipe(map(annotations => this.getVisibleAnnotations(Object.values(annotations), viewMode))),
|
||||
),
|
||||
),
|
||||
shareLast(),
|
||||
log('Visible annotations: '),
|
||||
shareDistinctLast(),
|
||||
);
|
||||
}
|
||||
|
||||
@ -73,7 +76,7 @@ export class FileDataService {
|
||||
}
|
||||
|
||||
get annotations() {
|
||||
return firstValueFrom(this.annotations$);
|
||||
return firstValueFrom(this.annotations$.pipe(map(dict => Object.values(dict))));
|
||||
}
|
||||
|
||||
get textHighlights() {
|
||||
@ -88,12 +91,24 @@ export class FileDataService {
|
||||
map(annotations =>
|
||||
this._userPreferenceService.areDevFeaturesEnabled ? annotations : annotations.filter(a => !a.isFalsePositive),
|
||||
),
|
||||
map(annotations => Object.assign({} as Record<string, AnnotationWrapper>, ...annotations.map(a => ({ [a.id]: a })))),
|
||||
shareLast(),
|
||||
);
|
||||
}
|
||||
|
||||
async load(file: File) {
|
||||
this.viewedPages = await firstValueFrom(this.getViewedPagesFor(file));
|
||||
async loadAnnotations() {
|
||||
const file = await this._state.file;
|
||||
|
||||
if (!file || file.isError) {
|
||||
const dossier = await this._state.dossier;
|
||||
return this._router.navigate([dossier.routerLink]);
|
||||
}
|
||||
|
||||
if (file.isUnprocessed) {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.loadViewedPages(file);
|
||||
await this.loadRedactionLog();
|
||||
}
|
||||
|
||||
@ -108,29 +123,25 @@ export class FileDataService {
|
||||
}
|
||||
|
||||
async loadTextHighlights() {
|
||||
const { dossierId, fileId } = this._state;
|
||||
const redactionPerColor = await firstValueFrom(this._textHighlightsService.getTextHighlights(dossierId, fileId));
|
||||
const textHighlights = this.#buildTextHighlights(redactionPerColor);
|
||||
const redactionPerColor = this._textHighlightsService.getTextHighlights(this._state.dossierId, this._state.fileId);
|
||||
const textHighlights = this.#buildTextHighlights(await firstValueFrom(redactionPerColor));
|
||||
this.#textHighlights$.next(textHighlights);
|
||||
|
||||
return textHighlights;
|
||||
}
|
||||
|
||||
getViewedPagesFor(file: File) {
|
||||
if (this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||
return this._viewedPagesService.getViewedPages(file.dossierId, file.fileId);
|
||||
async loadViewedPages(file: File) {
|
||||
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||
this.viewedPages = [];
|
||||
return;
|
||||
}
|
||||
return of([] as IViewedPage[]);
|
||||
|
||||
this.viewedPages = await firstValueFrom(this._viewedPagesService.getViewedPages(file.dossierId, file.fileId));
|
||||
}
|
||||
|
||||
loadRedactionLog() {
|
||||
const redactionLog$ = this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId).pipe(
|
||||
tap(redactionLog => redactionLog.redactionLogEntry.sort((a, b) => a.positions[0].page - b.positions[0].page)),
|
||||
catchError(() => of({})),
|
||||
tap(redactionLog => this.#redactionLog$.next(redactionLog)),
|
||||
);
|
||||
|
||||
return firstValueFrom(redactionLog$);
|
||||
const redactionLog$ = this._redactionLogService.getRedactionLog(this._state.dossierId, this._state.fileId);
|
||||
return firstValueFrom(redactionLog$.pipe(tap(redactionLog => this.#redactionLog$.next(redactionLog))));
|
||||
}
|
||||
|
||||
getVisibleAnnotations(annotations: AnnotationWrapper[], viewMode: ViewMode) {
|
||||
@ -179,12 +190,12 @@ export class FileDataService {
|
||||
#convertData(redactionLog: IRedactionLog, file: File): RedactionLogEntry[] {
|
||||
let result: RedactionLogEntry[] = [];
|
||||
const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
|
||||
const _dictionaryData = this._dictionariesMapService.get(this._state.dossierTemplateId);
|
||||
const dictionaries = this._dictionariesMapService.get(this._state.dossierTemplateId);
|
||||
|
||||
redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
|
||||
const changeLogValues = this.#getChangeLogValues(redactionLogEntry, file);
|
||||
const dictionaryData = _dictionaryData.find(dict => dict.type === redactionLogEntry.type);
|
||||
if (!dictionaryData) {
|
||||
const dictionary = dictionaries.find(dict => dict.type === redactionLogEntry.type);
|
||||
if (!dictionary) {
|
||||
this.missingTypes.add(redactionLogEntry.type);
|
||||
return;
|
||||
}
|
||||
@ -195,7 +206,7 @@ export class FileDataService {
|
||||
changeLogValues.isChangeLogEntry,
|
||||
changeLogValues.hidden,
|
||||
redactionLog.legalBasis,
|
||||
!!dictionaryData?.hint,
|
||||
!!dictionary?.hint,
|
||||
);
|
||||
|
||||
if (
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user