lint and format
This commit is contained in:
parent
e07599cf27
commit
4ae170a468
@ -63,6 +63,7 @@ import { HumanizePipe } from './utils/humanize.pipe';
|
|||||||
import { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
import { ManualAnnotationDialogComponent } from './dialogs/manual-redaction-dialog/manual-annotation-dialog.component';
|
||||||
import { FileNotAvailableOverlayComponent } from './screens/file/file-not-available-overlay/file-not-available-overlay.component';
|
import { FileNotAvailableOverlayComponent } from './screens/file/file-not-available-overlay/file-not-available-overlay.component';
|
||||||
import { ToastComponent } from './components/toast/toast.component';
|
import { ToastComponent } from './components/toast/toast.component';
|
||||||
|
import { AnnotationFilterComponent } from './screens/file/annotation-filter/annotation-filter.component';
|
||||||
|
|
||||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||||
@ -90,7 +91,8 @@ export function HttpLoaderFactory(httpClient: HttpClient) {
|
|||||||
AuthErrorComponent,
|
AuthErrorComponent,
|
||||||
HumanizePipe,
|
HumanizePipe,
|
||||||
ToastComponent,
|
ToastComponent,
|
||||||
FileNotAvailableOverlayComponent
|
FileNotAvailableOverlayComponent,
|
||||||
|
AnnotationFilterComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@ -0,0 +1,75 @@
|
|||||||
|
<div class="filter-root">
|
||||||
|
<button
|
||||||
|
color="accent"
|
||||||
|
mat-button
|
||||||
|
class="arrow-button"
|
||||||
|
[matMenuTriggerFor]="filterMenu"
|
||||||
|
[ngClass]="{ overlay: hasActiveFilters }"
|
||||||
|
>
|
||||||
|
<span translate="file-preview.filter-menu.label"></span>
|
||||||
|
<mat-icon svgIcon="red:arrow-down"></mat-icon>
|
||||||
|
</button>
|
||||||
|
<div class="dot" *ngIf="hasActiveFilters"></div>
|
||||||
|
<mat-menu #filterMenu="matMenu" xPosition="before" (closed)="applyFilters()">
|
||||||
|
<div class="filter-menu-header">
|
||||||
|
<div
|
||||||
|
class="all-caps-label"
|
||||||
|
translate="file-preview.filter-menu.filter-types.label"
|
||||||
|
></div>
|
||||||
|
<div class="actions">
|
||||||
|
<div
|
||||||
|
class="all-caps-label primary pointer"
|
||||||
|
translate="file-preview.filter-menu.all.label"
|
||||||
|
(click)="activateAllFilters(); $event.stopPropagation()"
|
||||||
|
></div>
|
||||||
|
<div
|
||||||
|
class="all-caps-label primary pointer"
|
||||||
|
translate="file-preview.filter-menu.none.label"
|
||||||
|
(click)="deactivateAllFilters(); $event.stopPropagation()"
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div *ngFor="let filter of filters">
|
||||||
|
<div class="mat-menu-item flex" (click)="toggleFilterExpanded($event, filter)">
|
||||||
|
<div class="arrow-wrapper" *ngIf="filter.filters">
|
||||||
|
<mat-icon *ngIf="filter.expanded" svgIcon="red:arrow-down" color="accent">
|
||||||
|
</mat-icon>
|
||||||
|
<mat-icon *ngIf="!filter.expanded" color="accent" svgIcon="red:arrow-right">
|
||||||
|
</mat-icon>
|
||||||
|
</div>
|
||||||
|
<mat-checkbox
|
||||||
|
[checked]="filter.checked"
|
||||||
|
[indeterminate]="filter.indeterminate"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="filterCheckboxClicked($event, filter)"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
[typeValue]="appStateService.getDictionaryTypeValue(filter.key)"
|
||||||
|
></redaction-annotation-icon>
|
||||||
|
|
||||||
|
{{ 'file-preview.filter-menu.' + filter.key + '.label' | translate }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
<div *ngIf="filter.filters && filter.expanded">
|
||||||
|
<div
|
||||||
|
*ngFor="let subFilter of filter.filters"
|
||||||
|
class="padding-left mat-menu-item"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
>
|
||||||
|
<mat-checkbox
|
||||||
|
[checked]="subFilter.checked"
|
||||||
|
(click)="$event.stopPropagation()"
|
||||||
|
(change)="filterCheckboxClicked($event, subFilter, filter)"
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<redaction-annotation-icon
|
||||||
|
[typeValue]="appStateService.getDictionaryTypeValue(subFilter.key)"
|
||||||
|
></redaction-annotation-icon>
|
||||||
|
{{ appStateService.getDictionaryLabel(subFilter.key) }}
|
||||||
|
</mat-checkbox>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</mat-menu>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,27 @@
|
|||||||
|
@import '../../../../assets/styles/red-variables';
|
||||||
|
|
||||||
|
.filter-root {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.dot {
|
||||||
|
background: $primary;
|
||||||
|
height: 10px;
|
||||||
|
width: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-menu-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 7px 15px 15px;
|
||||||
|
width: 350px;
|
||||||
|
|
||||||
|
.actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,118 @@
|
|||||||
|
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||||
|
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
||||||
|
import { ManualRedactions } from '@redaction/red-ui-http';
|
||||||
|
import { AnnotationFilter } from '../../../utils/types';
|
||||||
|
import { AppStateService } from '../../../state/app-state.service';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-annotation-filter',
|
||||||
|
templateUrl: './annotation-filter.component.html',
|
||||||
|
styleUrls: ['./annotation-filter.component.scss']
|
||||||
|
})
|
||||||
|
export class AnnotationFilterComponent implements OnChanges {
|
||||||
|
@Input() annotations: AnnotationWrapper[];
|
||||||
|
@Input() manualRedactions: ManualRedactions;
|
||||||
|
|
||||||
|
@Output() filtersChanged = new EventEmitter<AnnotationFilter[]>();
|
||||||
|
|
||||||
|
filters: AnnotationFilter[] = [];
|
||||||
|
|
||||||
|
constructor(public readonly appStateService: AppStateService) {}
|
||||||
|
|
||||||
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
if (this.annotations) {
|
||||||
|
this.filters = this._getFilters();
|
||||||
|
this.filtersChanged.emit(this.filters);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filterCheckboxClicked($event: any, filter: AnnotationFilter, parent?: AnnotationFilter) {
|
||||||
|
filter.checked = !filter.checked;
|
||||||
|
if (parent) {
|
||||||
|
this._handleCheckedValue(parent);
|
||||||
|
} else {
|
||||||
|
filter.indeterminate = false;
|
||||||
|
filter.filters.forEach((f) => (f.checked = filter.checked));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
activateAllFilters() {
|
||||||
|
this._setAlLFilters(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
deactivateAllFilters() {
|
||||||
|
this._setAlLFilters(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
get hasActiveFilters(): boolean {
|
||||||
|
for (const filter of this.filters) {
|
||||||
|
if (filter.checked || filter.indeterminate) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyFilters() {
|
||||||
|
this.filtersChanged.emit(this.filters);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleFilterExpanded($event: MouseEvent, filter: AnnotationFilter) {
|
||||||
|
$event.stopPropagation();
|
||||||
|
filter.expanded = !filter.expanded;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setAlLFilters(value: boolean) {
|
||||||
|
this.filters.forEach((f) => {
|
||||||
|
f.checked = value;
|
||||||
|
f.indeterminate = value;
|
||||||
|
f.filters.forEach((ff) => {
|
||||||
|
ff.checked = value;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private _getFilters(): AnnotationFilter[] {
|
||||||
|
const filters: AnnotationFilter[] = [];
|
||||||
|
const availableAnnotationTypes = {};
|
||||||
|
|
||||||
|
this.annotations?.forEach((a) => {
|
||||||
|
if (a.superType === 'hint' || a.superType === 'redaction') {
|
||||||
|
const entry = availableAnnotationTypes[a.superType];
|
||||||
|
if (!entry) {
|
||||||
|
availableAnnotationTypes[a.superType] = new Set<string>([a.dictionary]);
|
||||||
|
} else {
|
||||||
|
entry.add(a.dictionary);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
availableAnnotationTypes[a.superType] = new Set<string>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const key of Object.keys(availableAnnotationTypes)) {
|
||||||
|
const filter: AnnotationFilter = {
|
||||||
|
key: key,
|
||||||
|
filters: Array.from(availableAnnotationTypes[key]).map((dc) => {
|
||||||
|
const defaultFilter = this.appStateService.dictionaryData[dc]?.defaultFilter;
|
||||||
|
return { key: dc, checked: defaultFilter, filters: [] };
|
||||||
|
})
|
||||||
|
};
|
||||||
|
this._handleCheckedValue(filter);
|
||||||
|
if (filter.checked || filter.indeterminate) {
|
||||||
|
filter.expanded = true;
|
||||||
|
}
|
||||||
|
filters.push(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
return filters;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _handleCheckedValue(filter: AnnotationFilter) {
|
||||||
|
filter.checked = filter.filters.reduce((acc, next) => acc && next.checked, true);
|
||||||
|
if (filter.checked) {
|
||||||
|
filter.indeterminate = false;
|
||||||
|
} else {
|
||||||
|
filter.indeterminate = filter.filters.reduce((acc, next) => acc || next.checked, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -87,93 +87,11 @@
|
|||||||
|
|
||||||
<div class="right-fixed-container">
|
<div class="right-fixed-container">
|
||||||
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
|
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||||
<div>
|
<redaction-annotation-filter
|
||||||
<button
|
[annotations]="annotations"
|
||||||
color="accent"
|
[manualRedactions]="fileData?.manualRedactions"
|
||||||
mat-button
|
(filtersChanged)="filtersChanged($event)"
|
||||||
class="arrow-button"
|
></redaction-annotation-filter>
|
||||||
[matMenuTriggerFor]="filterMenu"
|
|
||||||
[ngClass]="{ overlay: hasActiveFilters }"
|
|
||||||
>
|
|
||||||
<span translate="file-preview.filter-menu.label"></span>
|
|
||||||
<mat-icon svgIcon="red:arrow-down"></mat-icon>
|
|
||||||
</button>
|
|
||||||
<div class="dot" *ngIf="hasActiveFilters"></div>
|
|
||||||
<mat-menu #filterMenu="matMenu" xPosition="before" (closed)="applyFilters()">
|
|
||||||
<div class="filter-menu-header">
|
|
||||||
<div
|
|
||||||
class="all-caps-label"
|
|
||||||
translate="file-preview.filter-menu.filter-types.label"
|
|
||||||
></div>
|
|
||||||
<div class="actions">
|
|
||||||
<div
|
|
||||||
class="all-caps-label primary pointer"
|
|
||||||
translate="file-preview.filter-menu.all.label"
|
|
||||||
(click)="setAllFilters(filters, true); $event.stopPropagation()"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="all-caps-label primary pointer"
|
|
||||||
translate="file-preview.filter-menu.none.label"
|
|
||||||
(click)="
|
|
||||||
setAllFilters(filters, false); $event.stopPropagation()
|
|
||||||
"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- <div *ngFor="let key of filterKeys()">-->
|
|
||||||
<!-- <div-->
|
|
||||||
<!-- class="mat-menu-item flex"-->
|
|
||||||
<!-- (click)="setExpanded(key, !expandedFilters[key], $event)"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- <div class="arrow-wrapper" *ngIf="hasSubsections(filters[key])">-->
|
|
||||||
<!-- <mat-icon-->
|
|
||||||
<!-- *ngIf="expandedFilters[key]"-->
|
|
||||||
<!-- svgIcon="red:arrow-down"-->
|
|
||||||
<!-- color="accent"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- </mat-icon>-->
|
|
||||||
<!-- <mat-icon-->
|
|
||||||
<!-- *ngIf="!expandedFilters[key]"-->
|
|
||||||
<!-- color="accent"-->
|
|
||||||
<!-- svgIcon="red:arrow-right"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- </mat-icon>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- <mat-checkbox-->
|
|
||||||
<!-- [checked]="isChecked(key)"-->
|
|
||||||
<!-- [indeterminate]="isIndeterminate(key)"-->
|
|
||||||
<!-- (click)="filterClicked($event,key)"-->
|
|
||||||
<!-- color="primary"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- <redaction-annotation-icon-->
|
|
||||||
<!-- [typeValue]="appStateService.getDictionaryTypeValue(key)"-->
|
|
||||||
<!-- ></redaction-annotation-icon>-->
|
|
||||||
|
|
||||||
<!-- {{ 'file-preview.filter-menu.' + key + '.label' | translate }}-->
|
|
||||||
<!-- </mat-checkbox>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- <div *ngIf="hasSubsections(filters[key]) && expandedFilters[key]">-->
|
|
||||||
<!-- <div-->
|
|
||||||
<!-- *ngFor="let subkey of filterKeys(key)"-->
|
|
||||||
<!-- class="padding-left mat-menu-item"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- <mat-checkbox-->
|
|
||||||
<!-- [checked]="filters[key][subkey]"-->
|
|
||||||
<!-- (click)="filterClicked($event,key,subkey)"-->
|
|
||||||
<!-- color="primary"-->
|
|
||||||
<!-- >-->
|
|
||||||
<!-- <redaction-annotation-icon-->
|
|
||||||
<!-- [typeValue]="-->
|
|
||||||
<!-- appStateService.getDictionaryTypeValue(subkey)-->
|
|
||||||
<!-- "-->
|
|
||||||
<!-- ></redaction-annotation-icon>-->
|
|
||||||
<!-- {{ appStateService.getDictionaryLabel(subkey) }}-->
|
|
||||||
<!-- </mat-checkbox>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
<!-- </div>-->
|
|
||||||
</mat-menu>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="right-content">
|
<div class="right-content">
|
||||||
@ -199,7 +117,7 @@
|
|||||||
<div
|
<div
|
||||||
class="annotations"
|
class="annotations"
|
||||||
[class.activePanel]="!pagesPanelActive"
|
[class.activePanel]="!pagesPanelActive"
|
||||||
#annotations
|
#annotationsElement
|
||||||
tabindex="1"
|
tabindex="1"
|
||||||
(keyup)="$event.preventDefault()"
|
(keyup)="$event.preventDefault()"
|
||||||
(keydown)="$event.preventDefault()"
|
(keydown)="$event.preventDefault()"
|
||||||
|
|||||||
@ -34,20 +34,6 @@ redaction-pdf-viewer {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
padding: 0 24px;
|
padding: 0 24px;
|
||||||
|
|
||||||
> div {
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
.dot {
|
|
||||||
background: $primary;
|
|
||||||
height: 10px;
|
|
||||||
width: 10px;
|
|
||||||
border-radius: 50%;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.close-icon {
|
.close-icon {
|
||||||
height: 14px;
|
height: 14px;
|
||||||
width: 14px;
|
width: 14px;
|
||||||
@ -165,15 +151,3 @@ redaction-pdf-viewer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-menu-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 7px 15px 15px;
|
|
||||||
width: 350px;
|
|
||||||
|
|
||||||
.actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -16,7 +16,6 @@ import { AnnotationUtils } from '../../../utils/annotation-utils';
|
|||||||
import { UserService } from '../../../user/user.service';
|
import { UserService } from '../../../user/user.service';
|
||||||
import { debounce } from '../../../utils/debounce';
|
import { debounce } from '../../../utils/debounce';
|
||||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||||
import { FiltersService } from '../service/filters.service';
|
|
||||||
import { FileDownloadService } from '../service/file-download.service';
|
import { FileDownloadService } from '../service/file-download.service';
|
||||||
import { saveAs } from 'file-saver';
|
import { saveAs } from 'file-saver';
|
||||||
import { FileType } from '../model/file-type';
|
import { FileType } from '../model/file-type';
|
||||||
@ -42,7 +41,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
private _dialogRef: MatDialogRef<any>;
|
private _dialogRef: MatDialogRef<any>;
|
||||||
|
|
||||||
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
||||||
@ViewChild('annotations') private _annotationsElement: ElementRef;
|
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
||||||
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
||||||
|
|
||||||
public fileData: FileDataModel;
|
public fileData: FileDataModel;
|
||||||
@ -50,7 +49,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
public annotations: AnnotationWrapper[] = [];
|
public annotations: AnnotationWrapper[] = [];
|
||||||
public displayedAnnotations: { [key: number]: { annotations: AnnotationWrapper[] } } = {};
|
public displayedAnnotations: { [key: number]: { annotations: AnnotationWrapper[] } } = {};
|
||||||
public selectedAnnotation: AnnotationWrapper;
|
public selectedAnnotation: AnnotationWrapper;
|
||||||
public filters: AnnotationFilter[];
|
|
||||||
public pagesPanelActive = true;
|
public pagesPanelActive = true;
|
||||||
public viewReady = false;
|
public viewReady = false;
|
||||||
|
|
||||||
@ -64,7 +62,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||||
private readonly _fileDownloadService: FileDownloadService,
|
private readonly _fileDownloadService: FileDownloadService,
|
||||||
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
private readonly _reanalysisControllerService: ReanalysisControllerService,
|
||||||
private readonly _filtersService: FiltersService,
|
|
||||||
private ngZone: NgZone
|
private ngZone: NgZone
|
||||||
) {
|
) {
|
||||||
this._activatedRoute.params.subscribe((params) => {
|
this._activatedRoute.params.subscribe((params) => {
|
||||||
@ -78,14 +75,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
return this.userService.user;
|
return this.userService.user;
|
||||||
}
|
}
|
||||||
|
|
||||||
public filterKeys(key?: string) {
|
|
||||||
if (key) {
|
|
||||||
return Object.keys(this.filters[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return Object.keys(this.filters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get redactedView() {
|
public get redactedView() {
|
||||||
return this._activeViewer === 'REDACTED';
|
return this._activeViewer === 'REDACTED';
|
||||||
}
|
}
|
||||||
@ -95,7 +84,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public ngOnInit(): void {
|
public ngOnInit(): void {
|
||||||
this.filters = this._filtersService.getFilters(this.appStateService.dictionaryData);
|
|
||||||
this._loadFileData();
|
this._loadFileData();
|
||||||
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
|
this.appStateService.fileStatusChanged.subscribe((fileStatus) => {
|
||||||
if (fileStatus.fileId === this.fileId) {
|
if (fileStatus.fileId === this.fileId) {
|
||||||
@ -106,19 +94,15 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _loadFileData() {
|
private _loadFileData() {
|
||||||
this._fileDownloadService.loadFileData(this.fileId).subscribe((fileDataModel) => {
|
this._fileDownloadService
|
||||||
this.fileData = fileDataModel;
|
.loadFileData(this.appStateService.activeProjectId, this.fileId)
|
||||||
this.annotations = fileDataModel.redactionLog.redactionLogEntry.map(
|
.subscribe((fileDataModel) => {
|
||||||
(rde) => new AnnotationWrapper(rde, null)
|
this.fileData = fileDataModel;
|
||||||
);
|
this.annotations = fileDataModel.redactionLog.redactionLogEntry.map(
|
||||||
this.filters = this._filtersService.getFilters(
|
(rde) => new AnnotationWrapper(rde, null)
|
||||||
this.appStateService.dictionaryData,
|
);
|
||||||
this.annotations
|
this._changeDetectorRef.detectChanges();
|
||||||
);
|
});
|
||||||
this.applyFilters();
|
|
||||||
this._changeDetectorRef.detectChanges();
|
|
||||||
console.log(this.annotations);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public openFileDetailsDialog($event: MouseEvent) {
|
public openFileDetailsDialog($event: MouseEvent) {
|
||||||
@ -157,13 +141,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
return this.instance;
|
return this.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public applyFilters() {
|
|
||||||
this.displayedAnnotations = AnnotationUtils.parseAnnotations(
|
|
||||||
this.annotations,
|
|
||||||
this.filters
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get displayedPages(): number[] {
|
public get displayedPages(): number[] {
|
||||||
return Object.keys(this.displayedAnnotations).map((key) => Number(key));
|
return Object.keys(this.displayedAnnotations).map((key) => Number(key));
|
||||||
}
|
}
|
||||||
@ -221,7 +198,7 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get activeViewerPage() {
|
get activeViewerPage() {
|
||||||
return this.instance.docViewer.getCurrentPage();
|
return this.instance?.docViewer?.getCurrentPage();
|
||||||
}
|
}
|
||||||
|
|
||||||
@debounce()
|
@debounce()
|
||||||
@ -286,43 +263,6 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public setAllFilters(filter: any, value: boolean, rootKey?: string) {
|
|
||||||
if (rootKey) {
|
|
||||||
this.filters[rootKey] = value;
|
|
||||||
} else {
|
|
||||||
for (const key of Object.keys(filter)) {
|
|
||||||
if (AnnotationUtils.hasSubsections(filter[key])) {
|
|
||||||
this.setAllFilters(filter[key], value);
|
|
||||||
} else {
|
|
||||||
filter[key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public isChecked(key: string): boolean {
|
|
||||||
return AnnotationUtils.isChecked(this.filters[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public isIndeterminate(key: string): boolean {
|
|
||||||
return AnnotationUtils.isIndeterminate(this.filters[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public get hasActiveFilters(): boolean {
|
|
||||||
// return AnnotationUtils.hasActiveFilters(this.filters);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public hasSubsections(filter: AnnotationFilter[]) {
|
|
||||||
// return AnnotationUtils.hasSubsections(filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public setExpanded(key: string, value: boolean, $event: MouseEvent) {
|
|
||||||
$event.stopPropagation();
|
|
||||||
//this.expandedFilters[key] = value;
|
|
||||||
this._changeDetectorRef.detectChanges();
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener('window:keyup', ['$event'])
|
@HostListener('window:keyup', ['$event'])
|
||||||
handleKeyEvent($event: KeyboardEvent) {
|
handleKeyEvent($event: KeyboardEvent) {
|
||||||
const keyArray = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
const keyArray = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||||
@ -491,15 +431,11 @@ export class FilePreviewScreenComponent implements OnInit {
|
|||||||
return new this.activeViewer.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
|
return new this.activeViewer.Annotations.Color(rgbColor.r, rgbColor.g, rgbColor.b);
|
||||||
}
|
}
|
||||||
|
|
||||||
filterClicked($event: MouseEvent, key: string, subkey?: string) {
|
filtersChanged(filters: AnnotationFilter[]) {
|
||||||
$event.preventDefault();
|
this.displayedAnnotations = AnnotationUtils.filterAndGroupAnnotations(
|
||||||
$event.stopPropagation();
|
this.annotations,
|
||||||
if (subkey) {
|
filters
|
||||||
this.filters[key][subkey] = !this.filters[key][subkey];
|
);
|
||||||
} else {
|
this._changeDetectorRef.detectChanges();
|
||||||
//this.setAllFilters(this.filters[key],)
|
|
||||||
this.filters[key] = !this.filters[key];
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,9 +1,10 @@
|
|||||||
import { RedactionLog } from '@redaction/red-ui-http';
|
import { ManualRedactions, RedactionLog } from '@redaction/red-ui-http';
|
||||||
|
|
||||||
export class FileDataModel {
|
export class FileDataModel {
|
||||||
constructor(
|
constructor(
|
||||||
public annotatedFileData: Blob,
|
public annotatedFileData: Blob,
|
||||||
public redactedFileData: Blob,
|
public redactedFileData: Blob,
|
||||||
public redactionLog: RedactionLog
|
public redactionLog: RedactionLog,
|
||||||
|
public manualRedactions: ManualRedactions
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,7 +74,6 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
|
|||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this._restoreViewerState = this._restoreViewerState.bind(this);
|
this._restoreViewerState = this._restoreViewerState.bind(this);
|
||||||
this._manualAnnotationService.loadManualAnnotationsForActiveFile().subscribe(() => {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges(changes: SimpleChanges): void {
|
ngOnChanges(changes: SimpleChanges): void {
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { forkJoin, Observable, of } from 'rxjs';
|
import { forkJoin, Observable, of } from 'rxjs';
|
||||||
import { map, tap } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
import { FileUploadControllerService, RedactionLogControllerService } from '@redaction/red-ui-http';
|
import {
|
||||||
|
FileUploadControllerService,
|
||||||
|
ManualRedactionControllerService,
|
||||||
|
RedactionLogControllerService
|
||||||
|
} from '@redaction/red-ui-http';
|
||||||
import { FileType } from '../model/file-type';
|
import { FileType } from '../model/file-type';
|
||||||
import { FileDataModel } from '../model/file-data.model';
|
import { FileDataModel } from '../model/file-data.model';
|
||||||
|
|
||||||
@ -11,15 +15,20 @@ import { FileDataModel } from '../model/file-data.model';
|
|||||||
export class FileDownloadService {
|
export class FileDownloadService {
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _fileUploadControllerService: FileUploadControllerService,
|
private readonly _fileUploadControllerService: FileUploadControllerService,
|
||||||
|
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
|
||||||
private readonly _redactionLogControllerService: RedactionLogControllerService
|
private readonly _redactionLogControllerService: RedactionLogControllerService
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public loadFileData(fileId: string): Observable<FileDataModel> {
|
public loadFileData(projectId: string, fileId: string): Observable<FileDataModel> {
|
||||||
const annotatedObs = this.loadFile('ANNOTATED', fileId);
|
const annotatedObs = this.loadFile('ANNOTATED', fileId);
|
||||||
const redactedObs = this.loadFile('REDACTED', fileId);
|
const redactedObs = this.loadFile('REDACTED', fileId);
|
||||||
const reactionLogObs = this._redactionLogControllerService.getRedactionLog(fileId);
|
const reactionLogObs = this._redactionLogControllerService.getRedactionLog(fileId);
|
||||||
|
const manualRedactionsObs = this._manualRedactionControllerService.getManualRedaction(
|
||||||
|
projectId,
|
||||||
|
fileId
|
||||||
|
);
|
||||||
|
|
||||||
return forkJoin([annotatedObs, redactedObs, reactionLogObs]).pipe(
|
return forkJoin([annotatedObs, redactedObs, reactionLogObs, manualRedactionsObs]).pipe(
|
||||||
map((data) => new FileDataModel(...data))
|
map((data) => new FileDataModel(...data))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { AnnotationFilter } from '../../../utils/types';
|
|
||||||
import { TypeValue } from '@redaction/red-ui-http';
|
|
||||||
import { AnnotationWrapper } from '../model/annotation.wrapper';
|
|
||||||
|
|
||||||
@Injectable({
|
|
||||||
providedIn: 'root'
|
|
||||||
})
|
|
||||||
export class FiltersService {
|
|
||||||
constructor() {}
|
|
||||||
|
|
||||||
public getFilters(
|
|
||||||
dictionaryData: { [key: string]: TypeValue },
|
|
||||||
annotations?: AnnotationWrapper[]
|
|
||||||
): AnnotationFilter[] {
|
|
||||||
const availableAnnotationTypes: Set<string> = new Set<string>();
|
|
||||||
annotations?.forEach((a) => {
|
|
||||||
availableAnnotationTypes.add(a.superType);
|
|
||||||
availableAnnotationTypes.add(a.dictionary);
|
|
||||||
});
|
|
||||||
const filters: AnnotationFilter[] = [];
|
|
||||||
for (const key of Object.keys(dictionaryData)) {
|
|
||||||
if (availableAnnotationTypes.has(key)) {
|
|
||||||
const typeValue = dictionaryData[key];
|
|
||||||
const filter: AnnotationFilter = this._addOrGetGroup(
|
|
||||||
filters,
|
|
||||||
typeValue.hint ? 'hint' : 'redaction'
|
|
||||||
);
|
|
||||||
filter.filters.push({
|
|
||||||
key: key
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return filters;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _addOrGetGroup(filters: AnnotationFilter[], name: string) {
|
|
||||||
return { key: name, filters: [] };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -16,12 +16,6 @@ import { UserService } from '../../../user/user.service';
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class ManualAnnotationService {
|
export class ManualAnnotationService {
|
||||||
private _manualAnnotationsResponse: ManualRedactions;
|
|
||||||
|
|
||||||
get manualEntries(): ManualRedactionEntry[] {
|
|
||||||
return this._manualAnnotationsResponse ? this._manualAnnotationsResponse.entriesToAdd : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _appStateService: AppStateService,
|
private readonly _appStateService: AppStateService,
|
||||||
private readonly _userService: UserService,
|
private readonly _userService: UserService,
|
||||||
@ -68,7 +62,6 @@ export class ManualAnnotationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public rejectSuggestion(annotationWrapper: AnnotationWrapper) {
|
public rejectSuggestion(annotationWrapper: AnnotationWrapper) {
|
||||||
console.log(annotationWrapper);
|
|
||||||
// if you're the owner, you undo, otherwise you reject
|
// if you're the owner, you undo, otherwise you reject
|
||||||
const observable =
|
const observable =
|
||||||
annotationWrapper.manualRedactionOwner === this._userService.userId
|
annotationWrapper.manualRedactionOwner === this._userService.userId
|
||||||
@ -83,22 +76,16 @@ export class ManualAnnotationService {
|
|||||||
annotationWrapper.uuid
|
annotationWrapper.uuid
|
||||||
);
|
);
|
||||||
|
|
||||||
return observable
|
return observable.pipe(
|
||||||
.pipe(
|
tap(
|
||||||
tap(
|
() => {
|
||||||
() => {
|
this._notify('manual-annotation.reject-request.success');
|
||||||
this._notify('manual-annotation.reject-request.success');
|
},
|
||||||
},
|
() => {
|
||||||
() => {
|
this._notify('manual-annotation.reject-request.error');
|
||||||
this._notify('manual-annotation.reject-request.error');
|
}
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
.pipe(
|
);
|
||||||
mergeMap((result) => {
|
|
||||||
return this.loadManualAnnotationsForActiveFile().pipe(map(() => result));
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public removeRedaction(annotationWrapper: AnnotationWrapper) {}
|
public removeRedaction(annotationWrapper: AnnotationWrapper) {}
|
||||||
@ -140,11 +127,6 @@ export class ManualAnnotationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.pipe(
|
|
||||||
mergeMap((result) => {
|
|
||||||
return this.loadManualAnnotationsForActiveFile().pipe(map(() => result));
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,11 +147,6 @@ export class ManualAnnotationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
|
||||||
.pipe(
|
|
||||||
mergeMap((result) => {
|
|
||||||
return this.loadManualAnnotationsForActiveFile().pipe(map(() => result));
|
|
||||||
})
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -196,17 +173,4 @@ export class ManualAnnotationService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
loadManualAnnotationsForActiveFile() {
|
|
||||||
return this._manualRedactionControllerService
|
|
||||||
.getManualRedaction(
|
|
||||||
this._appStateService.activeProject.project.projectId,
|
|
||||||
this._appStateService.activeFile.fileId
|
|
||||||
)
|
|
||||||
.pipe(
|
|
||||||
tap((response) => {
|
|
||||||
this._manualAnnotationsResponse = response;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,53 +15,36 @@ export class AnnotationUtils {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static hasSubsections(filter: AnnotationFilter | boolean) {
|
public static hasActiveFilters(filters: AnnotationFilter[]): boolean {
|
||||||
return filter instanceof Object;
|
return filters.reduce((acc, next) => acc || next.checked || next.indeterminate, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static checkedSubkeys(filter: AnnotationFilter | boolean) {
|
public static filterAndGroupAnnotations(
|
||||||
return Object.keys(filter).filter((subkey) => this.isChecked(filter[subkey])).length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only some of the sub-items are selected
|
|
||||||
public static isIndeterminate(filter: AnnotationFilter | boolean): boolean {
|
|
||||||
return this.hasSubsections(filter)
|
|
||||||
? AnnotationUtils.checkedSubkeys(filter) > 0 && !this.isChecked(filter)
|
|
||||||
: false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All sub-items are selected
|
|
||||||
public static isChecked(filter: AnnotationFilter | boolean): boolean {
|
|
||||||
return this.hasSubsections(filter)
|
|
||||||
? AnnotationUtils.checkedSubkeys(filter) === Object.keys(filter).length
|
|
||||||
: (filter as boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static hasActiveFilters(filter: AnnotationFilter[]): boolean {
|
|
||||||
const activeFilters = Object.keys(filter).filter((key) => {
|
|
||||||
return this.isChecked(filter[key]) || this.isIndeterminate(filter[key]);
|
|
||||||
});
|
|
||||||
// return activeFilters.length > 0;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static parseAnnotations(
|
|
||||||
annotations: AnnotationWrapper[],
|
annotations: AnnotationWrapper[],
|
||||||
filters: AnnotationFilter[]
|
filters: AnnotationFilter[]
|
||||||
): { [key: number]: { annotations: AnnotationWrapper[] } } {
|
): { [key: number]: { annotations: AnnotationWrapper[] } } {
|
||||||
const obj = {};
|
const obj = {};
|
||||||
|
|
||||||
|
const hasActiveFilters = AnnotationUtils.hasActiveFilters(filters);
|
||||||
|
|
||||||
|
const flatFilters = [];
|
||||||
|
filters.forEach((filter) => {
|
||||||
|
flatFilters.push(filter);
|
||||||
|
flatFilters.push(...filter.filters);
|
||||||
|
});
|
||||||
for (const annotation of annotations) {
|
for (const annotation of annotations) {
|
||||||
const pageNumber = annotation.pageNumber;
|
const pageNumber = annotation.pageNumber;
|
||||||
const type = annotation.superType;
|
const type = annotation.superType;
|
||||||
const dictionary = annotation.dictionary;
|
|
||||||
|
|
||||||
if (this.hasActiveFilters(filters)) {
|
if (hasActiveFilters) {
|
||||||
if (!this.hasSubsections(filters[type]) && !filters[type]) {
|
let found = false;
|
||||||
continue;
|
for (const filter of flatFilters) {
|
||||||
|
if (filter.key === annotation.dictionary && filter.checked) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (!found) {
|
||||||
if (this.hasSubsections(filters[type]) && !filters[type][dictionary]) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
apps/red-ui/src/app/utils/types.d.ts
vendored
1
apps/red-ui/src/app/utils/types.d.ts
vendored
@ -12,5 +12,6 @@ export interface AnnotationFilter {
|
|||||||
key: string;
|
key: string;
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
indeterminate?: boolean;
|
indeterminate?: boolean;
|
||||||
|
expanded?: boolean;
|
||||||
filters?: AnnotationFilter[];
|
filters?: AnnotationFilter[];
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user