diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
index 1cf1d6382..79d076308 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
@@ -4,7 +4,7 @@
{{ 'file-preview.standard' | translate }}
- {{ 'file-preview.delta' | translate }}
+ {{ 'file-preview.delta' | translate }}
{{ 'file-preview.redacted' | translate }}
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
index 6304b06e5..3831f07da 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
@@ -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);
});
}
diff --git a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts
index b3881bc5c..f07f4d1a8 100644
--- a/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts
+++ b/apps/red-ui/src/app/screens/file/model/annotation.wrapper.ts
@@ -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;
diff --git a/apps/red-ui/src/app/screens/file/model/file-data.model.ts b/apps/red-ui/src/app/screens/file/model/file-data.model.ts
index e359cc1dd..24f8f88a7 100644
--- a/apps/red-ui/src/app/screens/file/model/file-data.model.ts
+++ b/apps/red-ui/src/app/screens/file/model/file-data.model.ts
@@ -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);
});
diff --git a/apps/red-ui/src/app/screens/file/model/redaction-log-entry.wrapper.ts b/apps/red-ui/src/app/screens/file/model/redaction-log-entry.wrapper.ts
index 767fbcb59..6ceb8b46a 100644
--- a/apps/red-ui/src/app/screens/file/model/redaction-log-entry.wrapper.ts
+++ b/apps/red-ui/src/app/screens/file/model/redaction-log-entry.wrapper.ts
@@ -29,4 +29,7 @@ export interface RedactionLogEntryWrapper {
userId?: string;
comments?: Comment[];
+
+ isChangeLogEntry?: boolean;
+ changeLogType?: 'ADDED' | 'REMOVED';
}
diff --git a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
index 3d157d589..24c3b0a67 100644
--- a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
+++ b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
@@ -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');
diff --git a/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts b/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts
index 47e209b23..464442597 100644
--- a/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/annotation-draw.service.ts
@@ -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));
diff --git a/apps/red-ui/src/app/screens/file/service/pdf-viewer-data.service.ts b/apps/red-ui/src/app/screens/file/service/pdf-viewer-data.service.ts
index b96cfd4d1..a97e95c0e 100644
--- a/apps/red-ui/src/app/screens/file/service/pdf-viewer-data.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/pdf-viewer-data.service.ts
@@ -33,13 +33,17 @@ export class PdfViewerDataService {
loadActiveFileData(): Observable {
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);
})
diff --git a/apps/red-ui/src/app/utils/pdf-coordinates.ts b/apps/red-ui/src/app/utils/pdf-coordinates.ts
index e4274eb82..a5c16b1b4 100644
--- a/apps/red-ui/src/app/utils/pdf-coordinates.ts
+++ b/apps/red-ui/src/app/utils/pdf-coordinates.ts
@@ -48,6 +48,5 @@ export function translateQuads(page: number, rotation: number, quads: any) {
default:
result = quads;
}
- console.log(quads, result);
return result;
}
diff --git a/apps/red-ui/src/assets/styles/red-toggle-button.scss b/apps/red-ui/src/assets/styles/red-toggle-button.scss
index a76d3e506..07c95ccad 100644
--- a/apps/red-ui/src/assets/styles/red-toggle-button.scss
+++ b/apps/red-ui/src/assets/styles/red-toggle-button.scss
@@ -18,3 +18,11 @@
border-right: 1px solid $white;
}
}
+
+.mat-button-toggle-disabled {
+ .mat-button-toggle-button {
+ cursor: not-allowed !important;
+ }
+
+ outline: none;
+}