-
+ >
@@ -193,3 +194,12 @@
+
+
+
+
+ {{ filter.label ? (filter.label | translate) : (filter.key | humanize) }}
+
+
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 9c3c33a2b..b5f33ec8c 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
@@ -8,11 +8,10 @@ import {
ViewChild
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
-import { ManualRedactionEntry, ReanalysisControllerService } from '@redaction/red-ui-http';
+import { ReanalysisControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '../../../state/app-state.service';
import { WebViewerInstance } from '@pdftron/webviewer';
import { PdfViewerComponent } from '../pdf-viewer/pdf-viewer.component';
-import { AnnotationUtils } from '../../../utils/annotation-utils';
import { UserService } from '../../../user/user.service';
import { debounce } from '../../../utils/debounce';
import scrollIntoView from 'scroll-into-view-if-needed';
@@ -22,14 +21,14 @@ import { FileType } from '../model/file-type';
import { DialogService } from '../../../dialogs/dialog.service';
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import { ManualRedactionEntryWrapper } from '../model/manual-redaction-entry.wrapper';
-import { hexToRgb } from '../../../utils/functions';
import { AnnotationWrapper } from '../model/annotation.wrapper';
import { ManualAnnotationService } from '../service/manual-annotation.service';
import { ManualAnnotationResponse } from '../model/manual-annotation-response';
import { FileDataModel } from '../model/file-data.model';
-import { AnnotationFilter } from '../../../utils/types';
import { FileActionService } from '../service/file-action.service';
import { AnnotationDrawService } from '../service/annotation-draw.service';
+import { AnnotationProcessingService } from '../service/annotation-processing.service';
+import { FilterModel } from '../../../common/filter/model/filter.model';
@Component({
selector: 'redaction-file-preview-screen',
@@ -53,6 +52,7 @@ export class FilePreviewScreenComponent implements OnInit {
public selectedAnnotation: AnnotationWrapper;
public pagesPanelActive = true;
public viewReady = false;
+ filters: FilterModel[];
constructor(
public readonly appStateService: AppStateService,
@@ -61,6 +61,7 @@ export class FilePreviewScreenComponent implements OnInit {
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: DialogService,
private readonly _router: Router,
+ private readonly _annotationProcessingService: AnnotationProcessingService,
private readonly _annotationDrawService: AnnotationDrawService,
private readonly _fileActionService: FileActionService,
private readonly _manualAnnotationService: ManualAnnotationService,
@@ -111,6 +112,9 @@ export class FilePreviewScreenComponent implements OnInit {
this.annotations.push(...manualRedactionAnnotations);
this.annotations.push(...redactionLogAnnotations);
+ this.filters = this._annotationProcessingService.getAnnotationFilter(
+ this.annotations
+ );
this._changeDetectorRef.detectChanges();
});
}
@@ -402,8 +406,8 @@ export class FilePreviewScreenComponent implements OnInit {
);
}
- filtersChanged(filters: AnnotationFilter[]) {
- this.displayedAnnotations = AnnotationUtils.filterAndGroupAnnotations(
+ filtersChanged(filters: FilterModel[]) {
+ this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(
this.annotations,
filters
);
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 7d39b9533..ea2329877 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
@@ -18,15 +18,12 @@ import {
ManualRedactions,
Rectangle
} from '@redaction/red-ui-http';
-import WebViewer, { Annotations, WebViewerInstance } from '@pdftron/webviewer';
+import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
import { TranslateService } from '@ngx-translate/core';
import { FileDownloadService } from '../service/file-download.service';
-import { Subject } from 'rxjs';
-import { throttleTime } from 'rxjs/operators';
import { ManualRedactionEntryWrapper } from '../model/manual-redaction-entry.wrapper';
import { AppStateService } from '../../../state/app-state.service';
import { AnnotationWrapper } from '../model/annotation.wrapper';
-import { AnnotationUtils } from '../../../utils/annotation-utils';
import { ManualAnnotationService } from '../service/manual-annotation.service';
export interface ViewerState {
diff --git a/apps/red-ui/src/app/screens/file/service/annotation-processing.service.ts b/apps/red-ui/src/app/screens/file/service/annotation-processing.service.ts
new file mode 100644
index 000000000..c01e6d459
--- /dev/null
+++ b/apps/red-ui/src/app/screens/file/service/annotation-processing.service.ts
@@ -0,0 +1,119 @@
+import { Injectable } from '@angular/core';
+import { AppStateService } from '../../../state/app-state.service';
+import { AnnotationWrapper } from '../model/annotation.wrapper';
+import { FilterModel } from '../../../common/filter/model/filter.model';
+import { handleCheckedValue } from '../../../common/filter/utils/filter-utils';
+
+@Injectable({
+ providedIn: 'root'
+})
+export class AnnotationProcessingService {
+ constructor(private readonly _appStateService: AppStateService) {}
+
+ getAnnotationFilter(annotations: AnnotationWrapper[]): FilterModel[] {
+ const filters: FilterModel[] = [];
+ const availableAnnotationTypes = {};
+
+ annotations?.forEach((a) => {
+ if (a.superType === 'hint' || a.superType === 'redaction') {
+ const entry = availableAnnotationTypes[a.superType];
+ if (!entry) {
+ availableAnnotationTypes[a.superType] = new Set([a.dictionary]);
+ } else {
+ entry.add(a.dictionary);
+ }
+ } else {
+ availableAnnotationTypes[a.superType] = new Set();
+ }
+ });
+
+ for (const key of Object.keys(availableAnnotationTypes)) {
+ const filter: FilterModel = {
+ key: key,
+ label: 'annotation-filter.super-type.' + key,
+ filters: Array.from(availableAnnotationTypes[key]).map((dc: string) => {
+ const defaultFilter = this._appStateService.dictionaryData[dc]?.defaultFilter;
+ return { key: dc, checked: defaultFilter, filters: [] };
+ })
+ };
+ handleCheckedValue(filter);
+ if (filter.checked || filter.indeterminate) {
+ filter.expanded = true;
+ }
+ filters.push(filter);
+ }
+
+ return filters;
+ }
+
+ filterAndGroupAnnotations(
+ annotations: AnnotationWrapper[],
+ filters: FilterModel[]
+ ): { [key: number]: { annotations: AnnotationWrapper[] } } {
+ const obj = {};
+
+ const hasActiveFilters = this._hasActiveFilters(filters);
+
+ const flatFilters = [];
+ filters.forEach((filter) => {
+ flatFilters.push(filter);
+ flatFilters.push(...filter.filters);
+ });
+ for (const annotation of annotations) {
+ const pageNumber = annotation.pageNumber;
+ const type = annotation.superType;
+
+ if (hasActiveFilters) {
+ let found = false;
+ for (const filter of flatFilters) {
+ if (
+ filter.checked &&
+ (filter.key === annotation.dictionary ||
+ filter.key === annotation.superType)
+ ) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ continue;
+ }
+ }
+
+ if (!obj[pageNumber]) {
+ obj[pageNumber] = {
+ annotations: [],
+ hint: 0,
+ redaction: 0,
+ request: 0,
+ ignore: 0
+ };
+ }
+ obj[pageNumber].annotations.push(annotation);
+ obj[pageNumber][type]++;
+ }
+
+ Object.keys(obj).map((page) => {
+ obj[page].annotations = this._sortAnnotations(obj[page].annotations);
+ });
+
+ return obj;
+ }
+
+ private _sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {
+ return annotations.sort((ann1, ann2) => {
+ if (ann1.pageNumber === ann2.pageNumber) {
+ if (ann1.y === ann2.y) {
+ return ann1.x < ann2.x ? 1 : -1;
+ } else {
+ return ann1.y < ann2.y ? 1 : -1;
+ }
+ }
+ return ann1.pageNumber < ann2.pageNumber ? -1 : 1;
+ });
+ }
+
+ private _hasActiveFilters(filters: FilterModel[]): boolean {
+ return filters.reduce((acc, next) => acc || next.checked || next.indeterminate, false);
+ }
+}
diff --git a/apps/red-ui/src/app/utils/annotation-utils.ts b/apps/red-ui/src/app/utils/annotation-utils.ts
deleted file mode 100644
index 49b7ea6a3..000000000
--- a/apps/red-ui/src/app/utils/annotation-utils.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import { AnnotationWrapper } from '../screens/file/model/annotation.wrapper';
-import { AnnotationFilter } from './types';
-import { ManualRedactionEntry } from '@redaction/red-ui-http';
-import { WebViewerInstance } from '@pdftron/webviewer';
-import { hexToRgb } from './functions';
-
-export class AnnotationUtils {
- public static sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] {
- return annotations.sort((ann1, ann2) => {
- if (ann1.pageNumber === ann2.pageNumber) {
- if (ann1.y === ann2.y) {
- return ann1.x < ann2.x ? 1 : -1;
- } else {
- return ann1.y < ann2.y ? 1 : -1;
- }
- }
- return ann1.pageNumber < ann2.pageNumber ? -1 : 1;
- });
- }
-
- public static hasActiveFilters(filters: AnnotationFilter[]): boolean {
- return filters.reduce((acc, next) => acc || next.checked || next.indeterminate, false);
- }
-
- public static filterAndGroupAnnotations(
- annotations: AnnotationWrapper[],
- filters: AnnotationFilter[]
- ): { [key: number]: { annotations: AnnotationWrapper[] } } {
- const obj = {};
-
- const hasActiveFilters = AnnotationUtils.hasActiveFilters(filters);
-
- const flatFilters = [];
- filters.forEach((filter) => {
- flatFilters.push(filter);
- flatFilters.push(...filter.filters);
- });
- for (const annotation of annotations) {
- const pageNumber = annotation.pageNumber;
- const type = annotation.superType;
-
- if (hasActiveFilters) {
- let found = false;
- for (const filter of flatFilters) {
- if (
- filter.checked &&
- (filter.key === annotation.dictionary ||
- filter.key === annotation.superType)
- ) {
- found = true;
- break;
- }
- }
- if (!found) {
- continue;
- }
- }
-
- if (!obj[pageNumber]) {
- obj[pageNumber] = {
- annotations: [],
- hint: 0,
- redaction: 0,
- request: 0,
- ignore: 0
- };
- }
- obj[pageNumber].annotations.push(annotation);
- obj[pageNumber][type]++;
- }
-
- Object.keys(obj).map((page) => {
- obj[page].annotations = this.sortAnnotations(obj[page].annotations);
- });
-
- return obj;
- }
-}
diff --git a/apps/red-ui/src/app/utils/types.d.ts b/apps/red-ui/src/app/utils/types.d.ts
index 569d5b93d..06777b119 100644
--- a/apps/red-ui/src/app/utils/types.d.ts
+++ b/apps/red-ui/src/app/utils/types.d.ts
@@ -7,11 +7,3 @@ export class SortingOption {
order: string;
column: string;
}
-
-export interface AnnotationFilter {
- key: string;
- checked?: boolean;
- indeterminate?: boolean;
- expanded?: boolean;
- filters?: AnnotationFilter[];
-}
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index 4d4d40b24..5a309ac5e 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -483,18 +483,6 @@
},
"filter-types": {
"label": "Filter types"
- },
- "redaction": {
- "label": "Redaction"
- },
- "hint": {
- "label": "Hint"
- },
- "request": {
- "label": "Redaction Request"
- },
- "ignore": {
- "label": "Ignored redaction"
}
},
"tabs": {
@@ -590,5 +578,14 @@
"suggestion": "Suggestion for redaction",
"dictionary": "Dictionary",
"content": "Content",
- "page": "Page"
+ "page": "Page",
+ "filter": {},
+ "annotation-filter": {
+ "super-type": {
+ "redaction": "Redaction",
+ "hint": "Hint",
+ "request": "Redaction Request",
+ "ignore": "Ignored redaction"
+ }
+ }
}