Change Log
This commit is contained in:
parent
48c11aa441
commit
270b6c1da9
@ -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
|
||||
>
|
||||
|
||||
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
|
||||
@ -29,4 +29,7 @@ export interface RedactionLogEntryWrapper {
|
||||
|
||||
userId?: string;
|
||||
comments?: Comment[];
|
||||
|
||||
isChangeLogEntry?: boolean;
|
||||
changeLogType?: 'ADDED' | 'REMOVED';
|
||||
}
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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));
|
||||
|
||||
|
||||
@ -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);
|
||||
})
|
||||
|
||||
@ -48,6 +48,5 @@ export function translateQuads(page: number, rotation: number, quads: any) {
|
||||
default:
|
||||
result = quads;
|
||||
}
|
||||
console.log(quads, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -18,3 +18,11 @@
|
||||
border-right: 1px solid $white;
|
||||
}
|
||||
}
|
||||
|
||||
.mat-button-toggle-disabled {
|
||||
.mat-button-toggle-button {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
outline: none;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user