Change Log

This commit is contained in:
Timo 2021-02-07 21:13:19 +02:00
parent 48c11aa441
commit 270b6c1da9
10 changed files with 131 additions and 34 deletions

View File

@ -4,7 +4,7 @@
<div class="red-input-group slider-row">
<mat-button-toggle-group [value]="viewMode" (change)="switchView($event)" appearance="legacy">
<mat-button-toggle [value]="'STANDARD'"> {{ 'file-preview.standard' | translate }}</mat-button-toggle>
<mat-button-toggle [value]="'DELTA'" [disabled]="canNotSwitchToRedactedView"> {{ 'file-preview.delta' | translate }}</mat-button-toggle>
<mat-button-toggle [value]="'DELTA'" [disabled]="canNotSwitchToDeltaView"> {{ 'file-preview.delta' | translate }}</mat-button-toggle>
<mat-button-toggle [value]="'REDACTED'" [disabled]="canNotSwitchToRedactedView">
{{ 'file-preview.redacted' | translate }}</mat-button-toggle
>

View File

@ -32,7 +32,8 @@ import { download } from '../../../utils/file-download-utils';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { ViewMode } from '../model/view-mode';
const KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'f', 'F', 'Escape'];
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape', 'F', 'f'];
@Component({
selector: 'redaction-file-preview-screen',
@ -101,19 +102,41 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
updateViewMode() {
const allAnnotations = this._instance.annotManager.getAnnotationsList();
const redactions = allAnnotations.filter((a) => a.getCustomData('redaction'));
if (this.viewMode === 'STANDARD') {
redactions.forEach((redaction) => {
redaction['StrokeColor'] = redaction.getCustomData('annotationColor');
});
this._instance.annotManager.showAnnotations(allAnnotations);
} else {
const other = allAnnotations.filter((a) => !a.getCustomData('redaction'));
redactions.forEach((redaction) => {
redaction['StrokeColor'] = redaction.getCustomData('redactionColor');
});
this._instance.annotManager.hideAnnotations(other);
switch (this.viewMode) {
case 'STANDARD':
const standardEntries = allAnnotations.filter((a) => !a.getCustomData('changeLogRemoved'));
const nonStandardEntries = allAnnotations.filter((a) => a.getCustomData('changeLogRemoved'));
redactions.forEach((redaction) => {
redaction['StrokeColor'] = redaction.getCustomData('annotationColor');
});
this._instance.annotManager.showAnnotations(standardEntries);
this._instance.annotManager.hideAnnotations(nonStandardEntries);
break;
case 'DELTA':
const changeLogEntries = allAnnotations.filter((a) => a.getCustomData('changeLog'));
const nonChangeLogEntries = allAnnotations.filter((a) => !a.getCustomData('changeLog'));
redactions.forEach((redaction) => {
redaction['StrokeColor'] = redaction.getCustomData('annotationColor');
});
this._instance.annotManager.showAnnotations(changeLogEntries);
this._instance.annotManager.hideAnnotations(nonChangeLogEntries);
break;
case 'REDACTED':
const redactionEntries = allAnnotations.filter((a) => a.getCustomData('redaction'));
const nonRedactionEntries = allAnnotations.filter((a) => !a.getCustomData('redaction'));
redactions.forEach((redaction) => {
redaction['StrokeColor'] = redaction.getCustomData('redactionColor');
});
this._instance.annotManager.showAnnotations(redactionEntries);
this._instance.annotManager.hideAnnotations(nonRedactionEntries);
break;
}
this._rebuildFilters();
this._updateCanPerformActions();
}
@ -137,6 +160,10 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
return this.permissionsService.fileRequiresReanalysis();
}
get canNotSwitchToDeltaView() {
return this.fileData?.redactionChangeLog?.redactionLogEntry?.length === 0;
}
ngOnInit(): void {
document.documentElement.addEventListener('fullscreenchange', (event) => {
if (!document.fullscreenElement) {
@ -202,6 +229,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
this.annotations = this.fileData.getAnnotations(
this.appStateService.dictionaryData[this.appStateService.activeProject.ruleSetId],
this.permissionsService.currentUser,
this.viewMode,
this.userPreferenceService.areDevFeaturesEnabled
);
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
@ -299,7 +327,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
@HostListener('window:keyup', ['$event'])
handleKeyEvent($event: KeyboardEvent) {
if (!KEY_ARRAY.includes($event.key) || this._dialogRef?.getState() === MatDialogState.OPEN) {
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this._dialogRef?.getState() === MatDialogState.OPEN) {
return;
}
@ -309,6 +337,10 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
}
if (['f', 'F'].includes($event.key)) {
// if you type in an input, don't toggle full-screen
if ($event.target instanceof HTMLInputElement) {
return;
}
this.toggleFullScreen();
return;
}
@ -485,7 +517,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
}
preventKeyDefault($event: KeyboardEvent) {
if (KEY_ARRAY.includes($event.key)) {
if (COMMAND_KEY_ARRAY.includes($event.key)) {
$event.preventDefault();
}
}
@ -494,7 +526,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
this.fileData.manualRedactions = manualRedactions;
this._rebuildFilters();
this._annotationDrawService.drawAnnotations(this._instance, this.annotations, this.viewMode);
this._annotationDrawService.drawAnnotations(this._instance, this.annotations);
});
}

View File

@ -37,6 +37,9 @@ export class AnnotationWrapper {
textAfter?: string;
textBefore?: string;
isChangeLogEntry?: boolean;
changeLogType?: 'ADDED' | 'REMOVED';
constructor() {}
get isUndoableSuperType() {
@ -50,6 +53,10 @@ export class AnnotationWrapper {
);
}
get isChangeLogRemoved() {
return this.changeLogType === 'REMOVED';
}
get descriptor() {
return this.isModifyDictionary ? 'dictionary' : 'type';
}
@ -150,6 +157,8 @@ export class AnnotationWrapper {
const annotationWrapper = new AnnotationWrapper();
annotationWrapper.annotationId = redactionLogEntry.id;
annotationWrapper.isChangeLogEntry = redactionLogEntry.isChangeLogEntry;
annotationWrapper.changeLogType = redactionLogEntry.changeLogType;
annotationWrapper.redaction = redactionLogEntry.redacted;
annotationWrapper.hint = redactionLogEntry.hint;
annotationWrapper.dictionary = redactionLogEntry.type;

View File

@ -1,14 +1,25 @@
import { IdRemoval, ManualRedactionEntry, ManualRedactions, RedactionLog, RedactionLogEntry, TypeValue, ViewedPages } from '@redaction/red-ui-http';
import {
IdRemoval,
ManualRedactionEntry,
ManualRedactions,
RedactionChangeLog,
RedactionLog,
RedactionLogEntry,
TypeValue,
ViewedPages
} from '@redaction/red-ui-http';
import { FileStatusWrapper } from './file-status.wrapper';
import { UserWrapper } from '../../../user/user.service';
import { AnnotationWrapper } from './annotation.wrapper';
import { RedactionLogEntryWrapper } from './redaction-log-entry.wrapper';
import { ViewMode } from './view-mode';
export class FileDataModel {
constructor(
public fileStatus: FileStatusWrapper,
public fileData: Blob,
public redactionLog: RedactionLog,
public redactionChangeLog: RedactionChangeLog,
public manualRedactions: ManualRedactions,
public viewedPages?: ViewedPages
) {}
@ -17,17 +28,32 @@ export class FileDataModel {
return this.redactionLog.redactionLogEntry;
}
getAnnotations(dictionaryData: { [p: string]: TypeValue }, currentUser: UserWrapper, areDevFeaturesEnabled: boolean): AnnotationWrapper[] {
getAnnotations(
dictionaryData: { [p: string]: TypeValue },
currentUser: UserWrapper,
viewMode: ViewMode,
areDevFeaturesEnabled: boolean
): AnnotationWrapper[] {
const entries: RedactionLogEntryWrapper[] = this._convertData(dictionaryData);
let annotations = entries.map((entry) => AnnotationWrapper.fromData(entry));
// filter based on dev-mode
annotations = annotations.filter((annotation) => {
if (!areDevFeaturesEnabled) {
return !annotation.isIgnored && !annotation.isFalsePositive;
if (viewMode === 'STANDARD') {
if (annotation.isChangeLogRemoved) {
return false;
} else {
if (!areDevFeaturesEnabled) {
return !annotation.isIgnored && !annotation.isFalsePositive;
} else {
return true;
}
}
} else if (viewMode === 'DELTA') {
return annotation.isChangeLogEntry;
} else {
return true;
return annotation.isRedacted;
}
});
@ -37,16 +63,34 @@ export class FileDataModel {
private _convertData(dictionaryData: { [p: string]: TypeValue }): RedactionLogEntryWrapper[] {
let result: RedactionLogEntryWrapper[] = [];
this.redactionChangeLog.redactionLogEntry.forEach((changeLogEntry) => {
if (changeLogEntry.changeType === 'REMOVED') {
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
Object.assign(redactionLogEntryWrapper, changeLogEntry);
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
redactionLogEntryWrapper.isChangeLogEntry = true;
redactionLogEntryWrapper.changeLogType = changeLogEntry.changeType;
redactionLogEntryWrapper.id = 'changed-log-removed-' + redactionLogEntryWrapper.id;
result.push(redactionLogEntryWrapper);
}
});
this.redactionLog.redactionLogEntry.forEach((redactionLogEntry) => {
// false positive entries from the redaction-log need to be skipped
if (redactionLogEntry.type === 'false_positive') {
return;
}
const existingChangeLogEntry = this.redactionChangeLog.redactionLogEntry.find((rle) => rle.id === redactionLogEntry.id);
// copy the redactionLog Entry
const redactionLogEntryWrapper: RedactionLogEntryWrapper = { actionPendingReanalysis: false };
Object.assign(redactionLogEntryWrapper, redactionLogEntry);
redactionLogEntryWrapper.comments = this.manualRedactions.comments[redactionLogEntryWrapper.id];
redactionLogEntryWrapper.isChangeLogEntry = !!existingChangeLogEntry;
redactionLogEntryWrapper.changeLogType = 'ADDED';
result.push(redactionLogEntryWrapper);
});

View File

@ -29,4 +29,7 @@ export interface RedactionLogEntryWrapper {
userId?: string;
comments?: Comment[];
isChangeLogEntry?: boolean;
changeLogType?: 'ADDED' | 'REMOVED';
}

View File

@ -437,7 +437,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this.instance.hotkeys.off('A');
this.instance.hotkeys.off('C');
this.instance.hotkeys.off('E');
this.instance.hotkeys.off('F');
// this.instance.hotkeys.off('F');
this.instance.hotkeys.off('I');
this.instance.hotkeys.off('L');
this.instance.hotkeys.off('N');

View File

@ -5,7 +5,6 @@ import { hexToRgb } from '../../../utils/functions';
import { AppStateService } from '../../../state/app-state.service';
import { AnnotationWrapper } from '../model/annotation.wrapper';
import { UserPreferenceService } from '../../../common/service/user-preference.service';
import { ViewMode } from '../model/view-mode';
@Injectable({
providedIn: 'root'
@ -17,17 +16,10 @@ export class AnnotationDrawService {
private readonly _userPreferenceService: UserPreferenceService
) {}
public drawAnnotations(activeViewer: WebViewerInstance, annotationWrappers: AnnotationWrapper[], viewMode: ViewMode = 'STANDARD') {
public drawAnnotations(activeViewer: WebViewerInstance, annotationWrappers: AnnotationWrapper[]) {
const annotations = [];
annotationWrappers.forEach((annotation) => {
if (viewMode === 'REDACTED') {
if (annotation.isRedacted) {
const pdfViewerAnnotation = this.computeAnnotation(activeViewer, annotation);
annotations.push(pdfViewerAnnotation);
}
} else {
annotations.push(this.computeAnnotation(activeViewer, annotation));
}
annotations.push(this.computeAnnotation(activeViewer, annotation));
});
const annotationManager = activeViewer.annotManager;
@ -89,7 +81,13 @@ export class AnnotationDrawService {
highlight.Quads = this._rectanglesToQuads(annotationWrapper.positions, activeViewer, pageNumber);
highlight.Id = annotationWrapper.id;
highlight.ReadOnly = true;
// change log entries are drawn lighter
highlight.Opacity = annotationWrapper.isChangeLogRemoved ? 0.2 : 1;
highlight.Hidden = annotationWrapper.isChangeLogRemoved;
highlight.setCustomData('redaction', annotationWrapper.isRedacted);
highlight.setCustomData('changeLog', annotationWrapper.isChangeLogEntry);
highlight.setCustomData('changeLogRemoved', annotationWrapper.isChangeLogRemoved);
highlight.setCustomData('redactionColor', this.getColor(activeViewer, 'ignore', 'ignore'));
highlight.setCustomData('annotationColor', this.getColor(activeViewer, annotationWrapper.superType, annotationWrapper.dictionary));

View File

@ -33,13 +33,17 @@ export class PdfViewerDataService {
loadActiveFileData(): Observable<FileDataModel> {
const fileObs = this.downloadOriginalFile(this._appStateService.activeFile);
const reactionLogObs = this._redactionLogControllerService.getRedactionLog(this._appStateService.activeProjectId, this._appStateService.activeFileId);
const redactionChangeLogObs = this._redactionLogControllerService.getRedactionChangeLog(
this._appStateService.activeProjectId,
this._appStateService.activeFileId
);
const manualRedactionsObs = this._manualRedactionControllerService.getManualRedaction(
this._appStateService.activeProjectId,
this._appStateService.activeFileId
);
const viewedPagesObs = this.getViewedPagesForActiveFile();
return forkJoin([fileObs, reactionLogObs, manualRedactionsObs, viewedPagesObs]).pipe(
return forkJoin([fileObs, reactionLogObs, redactionChangeLogObs, manualRedactionsObs, viewedPagesObs]).pipe(
map((data) => {
return new FileDataModel(this._appStateService.activeFile, ...data);
})

View File

@ -48,6 +48,5 @@ export function translateQuads(page: number, rotation: number, quads: any) {
default:
result = quads;
}
console.log(quads, result);
return result;
}

View File

@ -18,3 +18,11 @@
border-right: 1px solid $white;
}
}
.mat-button-toggle-disabled {
.mat-button-toggle-button {
cursor: not-allowed !important;
}
outline: none;
}