lint and format

This commit is contained in:
Timo Bejan 2020-10-30 20:12:38 +02:00
parent e07599cf27
commit 4ae170a468
14 changed files with 289 additions and 323 deletions

View File

@ -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,

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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()"

View File

@ -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;
}
}

View File

@ -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;
} }
} }

View File

@ -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
) {} ) {}
} }

View File

@ -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 {

View File

@ -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))
); );
} }

View File

@ -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: [] };
}
}

View File

@ -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;
})
);
}
} }

View File

@ -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;
} }
} }

View File

@ -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[];
} }