RED-3721: New highlights endpoints

This commit is contained in:
Adina Țeudan 2022-04-07 14:41:52 +03:00
parent 6a7e438c63
commit 3bb3e93906
11 changed files with 55 additions and 55 deletions

View File

@ -1,6 +1,6 @@
import { annotationTypesTranslations } from '../../translations/annotation-types-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { IComment, IManualChange, ImportedRedaction, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain';
import { Highlight, IComment, IManualChange, IPoint, IRectangle, LogEntryStatus, ManualRedactionType } from '@red/domain';
import { RedactionLogEntry } from '@models/file/redaction-log.entry';
import {
FalsePositiveSuperTypes,
@ -228,17 +228,17 @@ export class AnnotationWrapper implements IListable, Record<string, unknown> {
);
}
static fromHighlight(color: string, entry: ImportedRedaction) {
static fromHighlight(highlight: Highlight) {
const annotationWrapper = new AnnotationWrapper();
annotationWrapper.annotationId = entry.id;
annotationWrapper.pageNumber = entry.positions[0].page;
annotationWrapper.annotationId = highlight.id;
annotationWrapper.pageNumber = highlight.positions[0].page;
annotationWrapper.superType = SuperTypes.TextHighlight;
annotationWrapper.typeValue = SuperTypes.TextHighlight;
annotationWrapper.value = 'Imported';
annotationWrapper.color = color;
annotationWrapper.positions = entry.positions;
annotationWrapper.firstTopLeftPoint = entry.positions[0]?.topLeft;
annotationWrapper.color = highlight.hexColor;
annotationWrapper.positions = highlight.positions;
annotationWrapper.firstTopLeftPoint = highlight.positions[0]?.topLeft;
annotationWrapper.typeLabel = annotationTypesTranslations[annotationWrapper.superType];
return annotationWrapper;

View File

@ -1,40 +1,41 @@
import { Injectable, Injector } from '@angular/core';
import { GenericService, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { ImportedRedaction, TextHighlightOperation, TextHighlightRequest, TextHighlightResponse } from '@red/domain';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { Highlight, TextHighlightOperation, TextHighlightResponse } from '@red/domain';
import { catchError, map, tap } from 'rxjs/operators';
import { of } from 'rxjs';
import { Observable, of } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@Injectable({
providedIn: 'root',
})
export class TextHighlightService extends GenericService<TextHighlightResponse> {
constructor(protected readonly _injector: Injector, private readonly _toaster: Toaster) {
super(_injector, 'texthighlights-conversion');
super(_injector, '');
}
@Validate()
getTextHighlights(@RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
const request: TextHighlightRequest = {
dossierId,
fileId,
operation: TextHighlightOperation.INFO,
};
return this._post(request).pipe(
map(response => response.redactionPerColor),
catchError(() => of({} as Record<string, ImportedRedaction[]>)),
getTextHighlights(@RequiredParam() dossierId: string, @RequiredParam() fileId: string): Observable<AnnotationWrapper[]> {
return this._http.get<{ highlights: Highlight[] }>(`/${this.#getPath(dossierId, fileId)}`).pipe(
map(response => response.highlights),
map(highlights => highlights.map(highlight => AnnotationWrapper.fromHighlight(highlight))),
catchError(() => of([])),
);
}
@Validate()
performHighlightsAction(
@RequiredParam() ids: string[],
@RequiredParam() dossierId: string,
@RequiredParam() fileId: string,
@RequiredParam() colors: string[],
@RequiredParam() operation: TextHighlightOperation,
) {
const request: TextHighlightRequest = { dossierId, fileId, colors, operation };
return this._post(request).pipe(tap(() => this._toaster.success(_('highlight-action-dialog.success'), { params: { operation } })));
return this._post({ ids }, `${this.#getPath(dossierId, fileId)}/${operation}`).pipe(
tap(() => this._toaster.success(_('highlight-action-dialog.success'), { params: { operation } })),
);
}
#getPath(dossierId: string, fileId: string): string {
return `dossiers/${dossierId}/files/${fileId}/highlights`;
}
}

View File

@ -4,6 +4,7 @@ import { TextHighlightOperation, TextHighlightsGroup } from '@red/domain';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
import { FileDataService } from '../../services/file-data.service';
@Component({
selector: 'redaction-highlights-separator [highlightGroup] [annotation]',
@ -18,7 +19,11 @@ export class HighlightsSeparatorComponent {
readonly circleButtonTypes = CircleButtonTypes;
readonly isWritable$ = this._state.isWritable$;
constructor(private readonly _dialogService: FilePreviewDialogService, private readonly _state: FilePreviewStateService) {}
constructor(
private readonly _dialogService: FilePreviewDialogService,
private readonly _state: FilePreviewStateService,
private readonly _fileDataService: FileDataService,
) {}
convertHighlights(highlightGroup: TextHighlightsGroup): void {
const data = this._getActionData(highlightGroup, TextHighlightOperation.CONVERT);
@ -31,11 +36,13 @@ export class HighlightsSeparatorComponent {
}
private _getActionData(highlightGroup: TextHighlightsGroup, operation: TextHighlightOperation) {
const highlights = this._fileDataService.all.filter(a => a.color === highlightGroup.color);
return {
dossierId: this._state.dossierId,
fileId: this._state.fileId,
color: highlightGroup.color,
operation,
highlights,
};
}
}

View File

@ -6,12 +6,14 @@ import { BaseDialogComponent, LoadingService } from '@iqser/common-ui';
import { TextHighlightService } from '../../../dossier/services/text-highlight.service';
import { firstValueFrom } from 'rxjs';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
export interface HighlightActionData {
readonly operation: TextHighlightOperation;
readonly color: string;
readonly dossierId: string;
readonly fileId: string;
readonly highlights: AnnotationWrapper[];
}
@Component({
@ -55,8 +57,9 @@ export class HighlightActionDialogComponent extends BaseDialogComponent {
async save(): Promise<void> {
this._loadingService.start();
const { dossierId, fileId, color, operation } = this.data;
await firstValueFrom(this._textHighlightService.performHighlightsAction(dossierId, fileId, [color], operation));
const { dossierId, fileId, operation, highlights } = this.data;
const ids = highlights.map(h => h.id);
await firstValueFrom(this._textHighlightService.performHighlightsAction(ids, dossierId, fileId, operation));
this._loadingService.stop();
this._dialogRef.close(true);
}

View File

@ -1,7 +1,6 @@
import {
ChangeType,
File,
ImportedRedaction,
IRedactionLog,
IRedactionLogEntry,
IViewedPage,
@ -100,12 +99,11 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
await this.loadRedactionLog();
}
async loadTextHighlights() {
const redactionPerColor = this._textHighlightsService.getTextHighlights(this._state.dossierId, this._state.fileId);
const textHighlights = this.#buildTextHighlights(await firstValueFrom(redactionPerColor));
this.#textHighlights$.next(textHighlights);
async loadTextHighlights(): Promise<AnnotationWrapper[]> {
const highlights = await firstValueFrom(this._textHighlightsService.getTextHighlights(this._state.dossierId, this._state.fileId));
this.#textHighlights$.next(highlights);
return textHighlights;
return highlights;
}
loadRedactionLog() {
@ -159,15 +157,6 @@ export class FileDataService extends EntitiesService<AnnotationWrapper> {
});
}
#buildTextHighlights(redactionPerColor: Record<string, ImportedRedaction[]>): AnnotationWrapper[] {
if (!redactionPerColor) {
return [];
}
const highlights = Object.entries(redactionPerColor);
return highlights.flatMap(([color, entries]) => entries.map(entry => AnnotationWrapper.fromHighlight(color, entry)));
}
#buildAnnotations(redactionLog: IRedactionLog, file: File) {
const entries: RedactionLogEntry[] = this.#convertData(redactionLog, file);
const annotations = entries.map(entry => AnnotationWrapper.fromData(entry));

View File

@ -1436,7 +1436,7 @@
"save": "Remove Highlights",
"title": "Remove highlights"
},
"success": "{operation, select, CONVERT{Converting} REMOVE{Removing} other{}} highlights in progress..."
"success": "{operation, select, convert{Converting} delete{Removing} other{}} highlights in progress..."
},
"highlights": "{color} - {length} {length, plural, one{highlight} other{highlights}}",
"image-category": {

View File

@ -0,0 +1,7 @@
import { IRectangle } from '../geometry';
export interface Highlight {
readonly id: string;
readonly positions: IRectangle[];
readonly hexColor: string;
}

View File

@ -1,6 +0,0 @@
import { IRectangle } from '../geometry';
export interface ImportedRedaction {
id: string;
positions: IRectangle[];
}

View File

@ -1,4 +1,4 @@
export * from './imported-redaction';
export * from './highlight';
export * from './text-highlight-operation';
export * from './text-highlight.response';
export * from './text-highlight.request';

View File

@ -1,5 +1,4 @@
export enum TextHighlightOperation {
REMOVE = 'REMOVE',
CONVERT = 'CONVERT',
INFO = 'INFO',
REMOVE = 'delete',
CONVERT = 'convert',
}

View File

@ -1,9 +1,9 @@
import { ImportedRedaction } from './imported-redaction';
import { Highlight } from './highlight';
import { TextHighlightOperation } from './text-highlight-operation';
export interface TextHighlightResponse {
dossierId?: string;
fileId?: string;
operation?: TextHighlightOperation;
redactionPerColor?: Record<string, ImportedRedaction[]>;
redactionPerColor?: Record<string, Highlight[]>;
}