RED-3623: skip reloading redactions multiple times
This commit is contained in:
parent
7b0d460f06
commit
2b3779a911
@ -1,9 +1,9 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { Observable } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-references-list',
|
||||
@ -16,16 +16,12 @@ export class AnnotationReferencesListComponent {
|
||||
@Output() readonly referenceClicked = new EventEmitter<AnnotationWrapper>();
|
||||
references$ = this._annotationReferences;
|
||||
|
||||
constructor(
|
||||
readonly annotationReferencesService: AnnotationReferencesService,
|
||||
private readonly _filePreviewStateService: FilePreviewStateService,
|
||||
) {}
|
||||
constructor(readonly annotationReferencesService: AnnotationReferencesService, private readonly _fileDataService: FileDataService) {}
|
||||
|
||||
private get _annotationReferences(): Observable<AnnotationWrapper[]> {
|
||||
const combination = combineLatest([this.annotationReferencesService.annotation$, this._filePreviewStateService.fileData$]);
|
||||
return combination.pipe(
|
||||
filter(([annotation]) => !!annotation),
|
||||
map(([{ reference }, fileData]) => fileData.allAnnotations.filter(a => reference.includes(a.annotationId))),
|
||||
return this.annotationReferencesService.annotation$.pipe(
|
||||
filter(annotation => !!annotation),
|
||||
map(({ reference }) => this._fileDataService.allAnnotations.filter(a => reference.includes(a.annotationId))),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -91,7 +91,7 @@
|
||||
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
||||
</div>
|
||||
|
||||
<div *ngIf="state.fileData$ | async as fileData" class="pages" id="pages">
|
||||
<div class="pages" id="pages">
|
||||
<redaction-page-indicator
|
||||
(pageSelected)="pageSelectedByClick($event)"
|
||||
*ngFor="let pageNumber of displayedPages"
|
||||
@ -99,7 +99,7 @@
|
||||
[active]="pageNumber === activeViewerPage"
|
||||
[number]="pageNumber"
|
||||
[showDottedIcon]="hasOnlyManualRedactionsAndIsExcluded(pageNumber)"
|
||||
[viewedPages]="fileData.viewedPages"
|
||||
[viewedPages]="fileDataService.viewedPages"
|
||||
></redaction-page-indicator>
|
||||
</div>
|
||||
|
||||
|
||||
@ -34,6 +34,7 @@ import { SkippedService } from '../../services/skipped.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { ViewModeService } from '../../services/view-mode.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
@ -76,6 +77,7 @@ export class FileWorkloadComponent {
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
readonly excludedPagesService: ExcludedPagesService,
|
||||
readonly fileDataService: FileDataService,
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _annotationProcessingService: AnnotationProcessingService,
|
||||
|
||||
@ -3,7 +3,8 @@ import { ViewMode } from '@red/domain';
|
||||
import { ViewModeService } from '../../services/view-mode.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { FileDataService } from '../../services/file-data.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-view-switch',
|
||||
@ -17,14 +18,13 @@ export class ViewSwitchComponent {
|
||||
readonly canSwitchToDeltaView$: Observable<boolean>;
|
||||
readonly canSwitchToRedactedView$: Observable<boolean>;
|
||||
|
||||
constructor(readonly viewModeService: ViewModeService, private readonly _stateService: FilePreviewStateService) {
|
||||
this.canSwitchToDeltaView$ = _stateService.fileData$.pipe(
|
||||
filter(fileData => !!fileData),
|
||||
switchMap(fileData =>
|
||||
combineLatest([fileData.hasChangeLog$, _stateService.file$]).pipe(
|
||||
map(([hasChangeLog, file]) => hasChangeLog && !file.isApproved),
|
||||
),
|
||||
),
|
||||
constructor(
|
||||
readonly viewModeService: ViewModeService,
|
||||
private readonly _stateService: FilePreviewStateService,
|
||||
private readonly _fileDataService: FileDataService,
|
||||
) {
|
||||
this.canSwitchToDeltaView$ = combineLatest([_fileDataService.hasChangeLog$, _stateService.file$]).pipe(
|
||||
map(([hasChangeLog, file]) => hasChangeLog && !file.isApproved),
|
||||
);
|
||||
|
||||
this.canSwitchToRedactedView$ = _stateService.file$.pipe(map(file => !file.analysisRequired && !file.excluded));
|
||||
|
||||
@ -14,6 +14,7 @@ import { AnnotationProcessingService } from '../dossier/services/annotation-proc
|
||||
import { dossiersServiceProvider } from '@services/entity-services/dossiers.service.provider';
|
||||
import { PageRotationService } from './services/page-rotation.service';
|
||||
import { PdfViewer } from './services/pdf-viewer.service';
|
||||
import { FileDataService } from './services/file-data.service';
|
||||
|
||||
export const filePreviewScreenProviders = [
|
||||
FilterService,
|
||||
@ -31,5 +32,6 @@ export const filePreviewScreenProviders = [
|
||||
PageRotationService,
|
||||
PdfViewer,
|
||||
AnnotationProcessingService,
|
||||
FileDataService,
|
||||
dossiersServiceProvider,
|
||||
];
|
||||
|
||||
@ -42,7 +42,6 @@ import { ReanalysisService } from '@services/reanalysis.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { SkippedService } from './services/skipped.service';
|
||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { filePreviewScreenProviders } from './file-preview-providers';
|
||||
import { ManualAnnotationService } from '@services/manual-annotation.service';
|
||||
import { DossiersService } from '@services/dossiers/dossiers.service';
|
||||
@ -50,6 +49,7 @@ import { PageRotationService } from './services/page-rotation.service';
|
||||
import { ComponentCanDeactivate } from '../../guards/can-deactivate.guard';
|
||||
import { PdfViewer } from './services/pdf-viewer.service';
|
||||
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
|
||||
import { FileDataService } from './services/file-data.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import PDFNet = Core.PDFNet;
|
||||
|
||||
@ -106,6 +106,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
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,
|
||||
@ -128,15 +129,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
get visibleAnnotations(): AnnotationWrapper[] {
|
||||
return this._fileData ? this._fileData.getVisibleAnnotations(this._viewModeService.viewMode) : [];
|
||||
return this._fileDataService.getVisibleAnnotations(this._viewModeService.viewMode);
|
||||
}
|
||||
|
||||
get allAnnotations(): AnnotationWrapper[] {
|
||||
return this._fileData ? this._fileData.allAnnotations : [];
|
||||
}
|
||||
|
||||
private get _fileData(): FileDataModel {
|
||||
return this.stateService.fileData;
|
||||
return this._fileDataService.allAnnotations;
|
||||
}
|
||||
|
||||
private get _canPerformAnnotationActions$() {
|
||||
@ -157,14 +154,14 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
return;
|
||||
}
|
||||
|
||||
const textHighlightAnnotationIds = this._fileData.textHighlightAnnotations.map(a => a.id);
|
||||
const textHighlightAnnotationIds = this._fileDataService.textHighlightAnnotations.map(a => a.id);
|
||||
const textHighlightAnnotations = this._pdf.getAnnotations((a: Core.Annotations.Annotation) =>
|
||||
textHighlightAnnotationIds.includes(a.Id),
|
||||
);
|
||||
|
||||
this._pdf.deleteAnnotations(textHighlightAnnotations);
|
||||
|
||||
const ocrAnnotationIds = this._fileData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
|
||||
const ocrAnnotationIds = this.allAnnotations.filter(a => a.isOCR).map(a => a.id);
|
||||
const annotations = this._pdf.getAnnotations(a => a.getCustomData('redact-manager'));
|
||||
const redactions = annotations.filter(a => a.getCustomData('redaction'));
|
||||
|
||||
@ -201,8 +198,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._loadingService.start();
|
||||
const textHighlights = await firstValueFrom(this._pdfViewerDataService.loadTextHighlightsFor(this.dossierId, this.fileId));
|
||||
this._pdf.hideAnnotations(annotations);
|
||||
this._fileData.textHighlights = textHighlights;
|
||||
await this._annotationDrawService.drawAnnotations(this._fileData.textHighlightAnnotations);
|
||||
this._fileDataService.textHighlights = textHighlights;
|
||||
await this._annotationDrawService.drawAnnotations(this._fileDataService.textHighlightAnnotations);
|
||||
this._loadingService.stop();
|
||||
}
|
||||
}
|
||||
@ -266,7 +263,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._filterService.addFilterGroup({
|
||||
slug: 'secondaryFilters',
|
||||
filterTemplate: this._filterTemplate,
|
||||
filters: processFilters(secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters(this._fileData?.viewedPages)),
|
||||
filters: processFilters(
|
||||
secondaryFilters,
|
||||
AnnotationProcessingService.secondaryAnnotationFilters(this._fileDataService.viewedPages),
|
||||
),
|
||||
});
|
||||
console.log(`[REDACTION] Process time: ${new Date().getTime() - processStartTime} ms`);
|
||||
console.log(`[REDACTION] Filter rebuild time: ${new Date().getTime() - startTime}`);
|
||||
@ -279,7 +279,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
if (this.selectedAnnotations.length > 1) {
|
||||
this.multiSelectService.activate();
|
||||
}
|
||||
this._workloadComponent.scrollToSelectedAnnotation();
|
||||
this._workloadComponent?.scrollToSelectedAnnotation();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
@ -344,7 +344,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
handleArrowEvent($event: KeyboardEvent): void {
|
||||
if (['ArrowUp', 'ArrowDown'].includes($event.key)) {
|
||||
if (this.selectedAnnotations.length === 1) {
|
||||
this._workloadComponent.navigateAnnotations($event);
|
||||
this._workloadComponent?.navigateAnnotations($event);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -585,7 +585,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
return;
|
||||
}
|
||||
|
||||
this.stateService.fileData = await firstValueFrom(this._pdfViewerDataService.loadDataFor(file));
|
||||
await this._fileDataService.load(file);
|
||||
}
|
||||
|
||||
@Debounce(0)
|
||||
@ -595,6 +595,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
private async _reloadAnnotations() {
|
||||
if (!this._fileDataService.shouldUpdateAnnotations) {
|
||||
console.log('skip reloading annotations');
|
||||
return;
|
||||
}
|
||||
this._deleteAnnotations();
|
||||
await this._cleanupAndRedrawAnnotations();
|
||||
await this.updateViewMode();
|
||||
@ -609,7 +613,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
const currentPageAnnotations = this.visibleAnnotations.filter(a => a.pageNumber === page);
|
||||
this._fileData.redactionLog = await firstValueFrom(this._pdfViewerDataService.loadRedactionLogFor(this.dossierId, this.fileId));
|
||||
await this._fileDataService.setRedactionLog(
|
||||
await firstValueFrom(this._pdfViewerDataService.loadRedactionLogFor(this.dossierId, this.fileId)),
|
||||
);
|
||||
|
||||
this._deleteAnnotations(currentPageAnnotations);
|
||||
await this._cleanupAndRedrawAnnotations(annotation => annotation.pageNumber === page);
|
||||
@ -637,7 +643,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this.rebuildFilters();
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
const annotations = this._fileData.allAnnotations;
|
||||
const annotations = this.allAnnotations;
|
||||
const newAnnotations = newAnnotationsFilter ? annotations.filter(newAnnotationsFilter) : annotations;
|
||||
|
||||
if (currentFilters) {
|
||||
|
||||
@ -0,0 +1,259 @@
|
||||
import {
|
||||
ChangeType,
|
||||
File,
|
||||
IRedactionLog,
|
||||
IRedactionLogEntry,
|
||||
IViewedPage,
|
||||
LogEntryStatus,
|
||||
ManualRedactionType,
|
||||
TextHighlightResponse,
|
||||
ViewMode,
|
||||
} from '@red/domain';
|
||||
import { AnnotationWrapper } from '../../../models/file/annotation.wrapper';
|
||||
import * as moment from 'moment';
|
||||
import { BehaviorSubject, firstValueFrom, of } 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, tap } from 'rxjs/operators';
|
||||
import { PermissionsService } from '../../../services/permissions.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { Toaster } from '../../../../../../../libs/common-ui/src';
|
||||
import { RedactionLogService } from '../../dossier/services/redaction-log.service';
|
||||
|
||||
@Injectable()
|
||||
export class FileDataService {
|
||||
static readonly DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||
viewedPages: IViewedPage[] = [];
|
||||
allAnnotations: AnnotationWrapper[] = [];
|
||||
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||
missingTypes = new Set<string>();
|
||||
textHighlightAnnotations: AnnotationWrapper[] = [];
|
||||
shouldUpdateAnnotations = false;
|
||||
#redactionLog: IRedactionLog;
|
||||
#redactionLogHash = '';
|
||||
|
||||
constructor(
|
||||
private readonly _state: FilePreviewStateService,
|
||||
private readonly _viewedPagesService: ViewedPagesService,
|
||||
private readonly _userPreferenceService: UserPreferenceService,
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
private readonly _permissionsService: PermissionsService,
|
||||
private readonly _redactionLogService: RedactionLogService,
|
||||
private readonly _toaster: Toaster,
|
||||
) {}
|
||||
|
||||
set textHighlights(textHighlightResponse: TextHighlightResponse) {
|
||||
const highlights = [];
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
||||
for (const color of Object.keys(textHighlightResponse.redactionPerColor)) {
|
||||
for (const entry of textHighlightResponse.redactionPerColor[color]) {
|
||||
const annotation = AnnotationWrapper.fromHighlight(color, entry);
|
||||
highlights.push(annotation);
|
||||
}
|
||||
}
|
||||
this.textHighlightAnnotations = highlights;
|
||||
}
|
||||
|
||||
async setRedactionLog(redactionLog: IRedactionLog) {
|
||||
this.#redactionLog = redactionLog;
|
||||
const hash = require('object-hash');
|
||||
const newRedactionLogHash = hash(redactionLog.redactionLogEntry ?? []);
|
||||
|
||||
if (newRedactionLogHash === this.#redactionLogHash) {
|
||||
this.shouldUpdateAnnotations = false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.shouldUpdateAnnotations = true;
|
||||
this.#redactionLogHash = newRedactionLogHash;
|
||||
await this.#buildAllAnnotations();
|
||||
}
|
||||
|
||||
async load(file: File) {
|
||||
this.viewedPages = await firstValueFrom(this.getViewedPagesFor(file));
|
||||
await this.setRedactionLog(await firstValueFrom(this.loadRedactionLog()));
|
||||
|
||||
if (this.missingTypes.size > 0) {
|
||||
this._toaster.error(_('error.missing-types'), {
|
||||
disableTimeOut: true,
|
||||
params: { missingTypes: Array.from(this.missingTypes).join(', ') },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getViewedPagesFor(file: File) {
|
||||
if (this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||
return this._viewedPagesService.getViewedPages(file.dossierId, file.fileId);
|
||||
}
|
||||
return of([] as IViewedPage[]);
|
||||
}
|
||||
|
||||
loadRedactionLog() {
|
||||
return 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({})),
|
||||
);
|
||||
}
|
||||
|
||||
getVisibleAnnotations(viewMode: ViewMode) {
|
||||
if (viewMode === 'TEXT_HIGHLIGHTS') {
|
||||
return this.textHighlightAnnotations;
|
||||
}
|
||||
|
||||
return this.allAnnotations.filter(annotation => {
|
||||
if (viewMode === 'STANDARD') {
|
||||
return !annotation.isChangeLogRemoved;
|
||||
} else if (viewMode === 'DELTA') {
|
||||
return annotation.isChangeLogEntry;
|
||||
} else {
|
||||
return annotation.previewAnnotation;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async #buildAllAnnotations() {
|
||||
const file = await this._state.file;
|
||||
const entries: RedactionLogEntry[] = this.#convertData(file);
|
||||
|
||||
const previousAnnotations = [...this.allAnnotations];
|
||||
this.allAnnotations = entries
|
||||
.map(entry => AnnotationWrapper.fromData(entry))
|
||||
.filter(ann => ann.manual || !file.excludedPages.includes(ann.pageNumber));
|
||||
|
||||
if (!this._userPreferenceService.areDevFeaturesEnabled) {
|
||||
this.allAnnotations = this.allAnnotations.filter(annotation => !annotation.isFalsePositive);
|
||||
}
|
||||
|
||||
this._setHiddenPropertyToNewAnnotations(this.allAnnotations, previousAnnotations);
|
||||
}
|
||||
|
||||
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
||||
newAnnotations.forEach(newAnnotation => {
|
||||
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
|
||||
if (oldAnnotation) {
|
||||
newAnnotation.hidden = oldAnnotation.hidden;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
#convertData(file: File): RedactionLogEntry[] {
|
||||
let result: RedactionLogEntry[] = [];
|
||||
|
||||
const reasonAnnotationIds: { [key: string]: RedactionLogEntry[] } = {};
|
||||
const _dictionaryData = this._dictionariesMapService.get(this._state.dossierTemplateId);
|
||||
this.#redactionLog.redactionLogEntry?.forEach(redactionLogEntry => {
|
||||
// copy the redactionLog Entry
|
||||
|
||||
const changeLogValues = this.#getChangeLogValues(redactionLogEntry, file);
|
||||
const dictionaryData = _dictionaryData.find(dict => dict.type === redactionLogEntry.type);
|
||||
if (!dictionaryData) {
|
||||
this.missingTypes.add(redactionLogEntry.type);
|
||||
return;
|
||||
}
|
||||
|
||||
const redactionLogEntryWrapper: RedactionLogEntry = new RedactionLogEntry(
|
||||
redactionLogEntry,
|
||||
changeLogValues.changeLogType,
|
||||
changeLogValues.isChangeLogEntry,
|
||||
changeLogValues.hidden,
|
||||
this.#redactionLog.legalBasis,
|
||||
!!dictionaryData?.hint,
|
||||
);
|
||||
|
||||
if (
|
||||
redactionLogEntry.manualChanges?.find(
|
||||
mc =>
|
||||
mc.manualRedactionType === ManualRedactionType.ADD_TO_DICTIONARY &&
|
||||
(mc.annotationStatus === LogEntryStatus.APPROVED || mc.annotationStatus === LogEntryStatus.REQUESTED),
|
||||
)
|
||||
) {
|
||||
// for dictionary entries -> I.E accepted recommendations or false positives,
|
||||
// check reason
|
||||
if (!reasonAnnotationIds[redactionLogEntry.reason]) {
|
||||
reasonAnnotationIds[redactionLogEntry.reason] = [redactionLogEntryWrapper];
|
||||
} else {
|
||||
reasonAnnotationIds[redactionLogEntry.reason].push(redactionLogEntryWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
result.push(redactionLogEntryWrapper);
|
||||
});
|
||||
|
||||
const reasonKeys = Object.keys(reasonAnnotationIds);
|
||||
result = result.filter(r => {
|
||||
const matched = reasonKeys.indexOf(r.id) >= 0;
|
||||
if (matched) {
|
||||
reasonAnnotationIds[r.id].forEach(value => {
|
||||
value.reason = null;
|
||||
});
|
||||
}
|
||||
return !matched;
|
||||
});
|
||||
|
||||
result = result.filter(r => !r.hidden);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#getChangeLogValues(
|
||||
redactionLogEntry: IRedactionLogEntry,
|
||||
file: File,
|
||||
): {
|
||||
hidden: boolean;
|
||||
changeLogType: ChangeType;
|
||||
isChangeLogEntry: boolean;
|
||||
} {
|
||||
if (file.numberOfAnalyses > 1) {
|
||||
const viableChanges = redactionLogEntry.changes.filter(c => c.analysisNumber > 1);
|
||||
viableChanges.sort((a, b) => moment(a.dateTime).valueOf() - moment(b.dateTime).valueOf());
|
||||
|
||||
const lastChange = viableChanges.length >= 1 ? viableChanges[viableChanges.length - 1] : undefined;
|
||||
const page = redactionLogEntry.positions?.[0].page;
|
||||
|
||||
const viewedPage = this.viewedPages.filter(p => p.page === page).pop();
|
||||
|
||||
// page has been seen -> let's see if it's a change
|
||||
if (viewedPage) {
|
||||
const viewTime = moment(viewedPage.viewedTime).valueOf() - FileDataService.DELTA_VIEW_TIME;
|
||||
// these are all unseen changes
|
||||
const relevantChanges = viableChanges.filter(change => moment(change.dateTime).valueOf() > viewTime);
|
||||
// at least one unseen change
|
||||
if (relevantChanges.length > 0) {
|
||||
// at least 1 relevant change
|
||||
viewedPage.showAsUnseen = moment(viewedPage.viewedTime).valueOf() < moment(lastChange.dateTime).valueOf();
|
||||
this.hasChangeLog$.next(true);
|
||||
return {
|
||||
changeLogType: relevantChanges[relevantChanges.length - 1].type,
|
||||
isChangeLogEntry: true,
|
||||
hidden: false,
|
||||
};
|
||||
} else {
|
||||
// no relevant changes - hide removed anyway
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: lastChange && lastChange.type === 'REMOVED',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Page doesn't have a view-time
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: lastChange && lastChange.type === 'REMOVED',
|
||||
};
|
||||
}
|
||||
} else {
|
||||
return {
|
||||
changeLogType: null,
|
||||
isChangeLogEntry: false,
|
||||
hidden: false,
|
||||
};
|
||||
}
|
||||
// console.log(wrapper.changeLogType, wrapper.hidden, wrapper.isChangeLogEntry, wrapper.value, lastChange);
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,5 @@
|
||||
import { Injectable, Injector } from '@angular/core';
|
||||
import { BehaviorSubject, firstValueFrom, from, Observable, pairwise, switchMap } from 'rxjs';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { firstValueFrom, from, Observable, pairwise, switchMap } from 'rxjs';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
@ -20,14 +19,11 @@ export class FilePreviewStateService {
|
||||
readonly dossier$: Observable<Dossier>;
|
||||
readonly isReadonly$: Observable<boolean>;
|
||||
readonly isWritable$: Observable<boolean>;
|
||||
readonly fileData$: Observable<FileDataModel>;
|
||||
|
||||
readonly dossierId: string;
|
||||
readonly dossierTemplateId: string;
|
||||
readonly fileId: string;
|
||||
|
||||
readonly #fileData$ = new BehaviorSubject<FileDataModel | undefined>(undefined);
|
||||
|
||||
constructor(
|
||||
private readonly _fileManagementService: FileManagementService,
|
||||
private readonly _injector: Injector,
|
||||
@ -43,18 +39,9 @@ export class FilePreviewStateService {
|
||||
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
|
||||
[this.isReadonly$, this.isWritable$] = boolFactory(this.file$, file => !_permissionsService.canPerformAnnotationActions(file));
|
||||
|
||||
this.fileData$ = this.#fileData$.asObservable().pipe(filter(value => !!value));
|
||||
this.blob$ = this.#blob$;
|
||||
}
|
||||
|
||||
get fileData(): FileDataModel {
|
||||
return this.#fileData$.value;
|
||||
}
|
||||
|
||||
set fileData(fileDataModel: FileDataModel) {
|
||||
this.#fileData$.next(fileDataModel);
|
||||
}
|
||||
|
||||
get file(): Promise<File> {
|
||||
return firstValueFrom(this.file$);
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ export class ViewedPagesService extends GenericService<unknown> {
|
||||
getViewedPages(@RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||
return this._getOne<{ pages?: IViewedPage[] }>([dossierId, fileId]).pipe(
|
||||
map(res => res.pages),
|
||||
catchError(() => of([])),
|
||||
catchError(() => of([] as IViewedPage[])),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@
|
||||
"ngx-color-picker": "^12.0.1",
|
||||
"ngx-toastr": "^14.1.3",
|
||||
"ngx-translate-messageformat-compiler": "^5.0.1",
|
||||
"object-hash": "^3.0.0",
|
||||
"papaparse": "^5.3.1",
|
||||
"rxjs": "~7.5.2",
|
||||
"sass": "^1.49.0",
|
||||
|
||||
@ -9183,6 +9183,11 @@ object-copy@^0.1.0:
|
||||
define-property "^0.2.5"
|
||||
kind-of "^3.0.3"
|
||||
|
||||
object-hash@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9"
|
||||
integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
|
||||
|
||||
object-inspect@^1.11.0, object-inspect@^1.9.0:
|
||||
version "1.12.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user