RED-3791: use existing convert recommendation to annotation action
This commit is contained in:
parent
f4b9595725
commit
b7d926046d
@ -14,6 +14,7 @@ import { FileDataService } from './services/file-data.service';
|
|||||||
import { AnnotationsListingService } from './services/annotations-listing.service';
|
import { AnnotationsListingService } from './services/annotations-listing.service';
|
||||||
import { StampService } from './services/stamp.service';
|
import { StampService } from './services/stamp.service';
|
||||||
import { PdfProxyService } from './services/pdf-proxy.service';
|
import { PdfProxyService } from './services/pdf-proxy.service';
|
||||||
|
import { PdfAnnotationActionsService } from './services/pdf-annotation-actions.service';
|
||||||
|
|
||||||
export const filePreviewScreenProviders = [
|
export const filePreviewScreenProviders = [
|
||||||
FilterService,
|
FilterService,
|
||||||
@ -24,6 +25,7 @@ export const filePreviewScreenProviders = [
|
|||||||
CommentingService,
|
CommentingService,
|
||||||
SkippedService,
|
SkippedService,
|
||||||
AnnotationActionsService,
|
AnnotationActionsService,
|
||||||
|
PdfAnnotationActionsService,
|
||||||
FilePreviewStateService,
|
FilePreviewStateService,
|
||||||
AnnotationReferencesService,
|
AnnotationReferencesService,
|
||||||
AnnotationProcessingService,
|
AnnotationProcessingService,
|
||||||
|
|||||||
@ -1,19 +1,13 @@
|
|||||||
import { Inject, Injectable, NgZone } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
|
||||||
import { ManualRedactionService } from './manual-redaction.service';
|
import { ManualRedactionService } from './manual-redaction.service';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { TranslateService } from '@ngx-translate/core';
|
|
||||||
import { getFirstRelevantTextPart } from '../../../utils';
|
import { getFirstRelevantTextPart } from '../../../utils';
|
||||||
import { AnnotationPermissions } from '@models/file/annotation.permissions';
|
|
||||||
import { BASE_HREF } from '../../../tokens';
|
|
||||||
import { UserService } from '@services/user.service';
|
|
||||||
import { Core } from '@pdftron/webviewer';
|
import { Core } from '@pdftron/webviewer';
|
||||||
import {
|
import {
|
||||||
DictionaryEntryTypes,
|
DictionaryEntryTypes,
|
||||||
Dossier,
|
Dossier,
|
||||||
IAddRedactionRequest,
|
IAddRedactionRequest,
|
||||||
IHeaderElement,
|
|
||||||
ILegalBasisChangeRequest,
|
ILegalBasisChangeRequest,
|
||||||
IRecategorizationRequest,
|
IRecategorizationRequest,
|
||||||
IRectangle,
|
IRectangle,
|
||||||
@ -33,23 +27,16 @@ import { filter } from 'rxjs/operators';
|
|||||||
import { MatDialog } from '@angular/material/dialog';
|
import { MatDialog } from '@angular/material/dialog';
|
||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { FilePreviewDialogService } from './file-preview-dialog.service';
|
import { FilePreviewDialogService } from './file-preview-dialog.service';
|
||||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
|
||||||
import { FileDataService } from './file-data.service';
|
import { FileDataService } from './file-data.service';
|
||||||
import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
|
import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service';
|
||||||
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
|
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
|
||||||
import { SkippedService } from './skipped.service';
|
import { SkippedService } from './skipped.service';
|
||||||
import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.service';
|
import { REDDocumentViewer } from '../../pdf-viewer/services/document-viewer.service';
|
||||||
import { AnnotationsListingService } from './annotations-listing.service';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnnotationActionsService {
|
export class AnnotationActionsService {
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(BASE_HREF) private readonly _baseHref: string,
|
|
||||||
private readonly _ngZone: NgZone,
|
|
||||||
private readonly _userService: UserService,
|
|
||||||
private readonly _permissionsService: PermissionsService,
|
|
||||||
private readonly _manualRedactionService: ManualRedactionService,
|
private readonly _manualRedactionService: ManualRedactionService,
|
||||||
private readonly _translateService: TranslateService,
|
|
||||||
private readonly _dialogService: FilePreviewDialogService,
|
private readonly _dialogService: FilePreviewDialogService,
|
||||||
private readonly _dialog: MatDialog,
|
private readonly _dialog: MatDialog,
|
||||||
private readonly _pdf: PdfViewer,
|
private readonly _pdf: PdfViewer,
|
||||||
@ -57,11 +44,9 @@ export class AnnotationActionsService {
|
|||||||
private readonly _annotationManager: REDAnnotationManager,
|
private readonly _annotationManager: REDAnnotationManager,
|
||||||
private readonly _annotationDrawService: AnnotationDrawService,
|
private readonly _annotationDrawService: AnnotationDrawService,
|
||||||
private readonly _activeDossiersService: ActiveDossiersService,
|
private readonly _activeDossiersService: ActiveDossiersService,
|
||||||
private readonly _dictionariesMapService: DictionariesMapService,
|
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _fileDataService: FileDataService,
|
private readonly _fileDataService: FileDataService,
|
||||||
private readonly _skippedService: SkippedService,
|
private readonly _skippedService: SkippedService,
|
||||||
private readonly _listingService: AnnotationsListingService,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
private get _dossier(): Dossier {
|
private get _dossier(): Dossier {
|
||||||
@ -71,29 +56,25 @@ export class AnnotationActionsService {
|
|||||||
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[]) {
|
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[]) {
|
||||||
$event?.stopPropagation();
|
$event?.stopPropagation();
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
this._processObsAndEmit(
|
const ids = annotations.map(a => a.id);
|
||||||
this._manualRedactionService.approve(
|
const request = this._manualRedactionService.approve(ids, dossierId, fileId);
|
||||||
annotations.map(a => a.id),
|
this.#processObsAndEmit(request);
|
||||||
dossierId,
|
|
||||||
fileId,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
removeHighlights(highlights: AnnotationWrapper[]): void {
|
removeHighlights(highlights: AnnotationWrapper[]): void {
|
||||||
const data = this._getHighlightOperationData(TextHighlightOperation.REMOVE, highlights);
|
const data = this.#getHighlightOperationData(TextHighlightOperation.REMOVE, highlights);
|
||||||
this._dialogService.openDialog('highlightAction', null, data);
|
this._dialogService.openDialog('highlightAction', null, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
convertHighlights(highlights: AnnotationWrapper[]): void {
|
convertHighlights(highlights: AnnotationWrapper[]): void {
|
||||||
const data = this._getHighlightOperationData(TextHighlightOperation.CONVERT, highlights);
|
const data = this.#getHighlightOperationData(TextHighlightOperation.CONVERT, highlights);
|
||||||
this._dialogService.openDialog('highlightAction', null, data);
|
this._dialogService.openDialog('highlightAction', null, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
rejectSuggestion($event: MouseEvent, annotations: AnnotationWrapper[]) {
|
rejectSuggestion($event: MouseEvent, annotations: AnnotationWrapper[]) {
|
||||||
$event?.stopPropagation();
|
$event?.stopPropagation();
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
this._processObsAndEmit(
|
this.#processObsAndEmit(
|
||||||
this._manualRedactionService.declineOrRemove(
|
this._manualRedactionService.declineOrRemove(
|
||||||
annotations.map(a => a.id),
|
annotations.map(a => a.id),
|
||||||
dossierId,
|
dossierId,
|
||||||
@ -107,7 +88,7 @@ export class AnnotationActionsService {
|
|||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
const data = { dossier: this._dossier, annotations, hint };
|
const data = { dossier: this._dossier, annotations, hint };
|
||||||
this._dialogService.openDialog('forceAnnotation', $event, data, (request: ILegalBasisChangeRequest) => {
|
this._dialogService.openDialog('forceAnnotation', $event, data, (request: ILegalBasisChangeRequest) => {
|
||||||
this._processObsAndEmit(
|
this.#processObsAndEmit(
|
||||||
this._manualRedactionService.bulkForce(
|
this._manualRedactionService.bulkForce(
|
||||||
annotations.map(a => ({ ...request, annotationId: a.id })),
|
annotations.map(a => ({ ...request, annotationId: a.id })),
|
||||||
dossierId,
|
dossierId,
|
||||||
@ -132,7 +113,7 @@ export class AnnotationActionsService {
|
|||||||
value: data.value,
|
value: data.value,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this._processObsAndEmit(this._manualRedactionService.changeLegalBasis(body, dossierId, fileId));
|
this.#processObsAndEmit(this._manualRedactionService.changeLegalBasis(body, dossierId, fileId));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -151,7 +132,7 @@ export class AnnotationActionsService {
|
|||||||
removeFromDictionary,
|
removeFromDictionary,
|
||||||
comment: result.comment,
|
comment: result.comment,
|
||||||
}));
|
}));
|
||||||
this._processObsAndEmit(this._manualRedactionService.removeOrSuggestRemove(body, dossierId, fileId, removeFromDictionary));
|
this.#processObsAndEmit(this._manualRedactionService.removeOrSuggestRemove(body, dossierId, fileId, removeFromDictionary));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,7 +145,7 @@ export class AnnotationActionsService {
|
|||||||
type,
|
type,
|
||||||
comment,
|
comment,
|
||||||
}));
|
}));
|
||||||
this._processObsAndEmit(this._manualRedactionService.recategorizeImage(body, dossierId, fileId));
|
this.#processObsAndEmit(this._manualRedactionService.recategorizeImage(body, dossierId, fileId));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +154,7 @@ export class AnnotationActionsService {
|
|||||||
|
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
const modifyDictionary = annotations[0].isModifyDictionary;
|
const modifyDictionary = annotations[0].isModifyDictionary;
|
||||||
this._processObsAndEmit(
|
this.#processObsAndEmit(
|
||||||
this._manualRedactionService.undoRequest(
|
this._manualRedactionService.undoRequest(
|
||||||
annotations.map(a => a.id),
|
annotations.map(a => a.id),
|
||||||
dossierId,
|
dossierId,
|
||||||
@ -194,205 +175,10 @@ export class AnnotationActionsService {
|
|||||||
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations));
|
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations));
|
||||||
dialogClosed.subscribe(({ annotations, comment: commentText }) => {
|
dialogClosed.subscribe(({ annotations, comment: commentText }) => {
|
||||||
const comment = commentText ? { text: commentText } : undefined;
|
const comment = commentText ? { text: commentText } : undefined;
|
||||||
this._processObsAndEmit(this._manualRedactionService.addRecommendation(annotations, dossierId, fileId, comment));
|
this.#processObsAndEmit(this._manualRedactionService.addRecommendation(annotations, dossierId, fileId, comment));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getViewerAvailableActions(annotations: AnnotationWrapper[]): IHeaderElement[] {
|
|
||||||
const dossier = this._state.dossier;
|
|
||||||
|
|
||||||
const availableActions: IHeaderElement[] = [];
|
|
||||||
const annotationPermissions = annotations.map(annotation => ({
|
|
||||||
annotation,
|
|
||||||
permissions: AnnotationPermissions.forUser(
|
|
||||||
this._permissionsService.isApprover(dossier),
|
|
||||||
this._userService.currentUser,
|
|
||||||
annotation,
|
|
||||||
this._dictionariesMapService.get(dossier.dossierTemplateId),
|
|
||||||
),
|
|
||||||
}));
|
|
||||||
|
|
||||||
// you can only resize one annotation at a time
|
|
||||||
const canResize = annotationPermissions.length === 1 && annotationPermissions[0].permissions.canResizeAnnotation;
|
|
||||||
if (canResize) {
|
|
||||||
const firstAnnotation = annotations[0];
|
|
||||||
// if we already entered resize-mode previously
|
|
||||||
if (firstAnnotation.resizing) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.resize-accept.label'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(async () => {
|
|
||||||
await this.acceptResize(null, firstAnnotation);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/close.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.resize-cancel.label'),
|
|
||||||
onClick: () => this._ngZone.run(() => this.cancelResize(null, firstAnnotation)),
|
|
||||||
});
|
|
||||||
return availableActions;
|
|
||||||
}
|
|
||||||
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/resize.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.resize.label'),
|
|
||||||
onClick: () => this._ngZone.run(() => this.resize(null, annotations[0])),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canChangeLegalBasis = annotationPermissions.reduce((acc, next) => acc && next.permissions.canChangeLegalBasis, true);
|
|
||||||
if (canChangeLegalBasis) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/edit.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.edit-reason.label'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.changeLegalBasis(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canRecategorizeImage = annotationPermissions.reduce((acc, next) => acc && next.permissions.canRecategorizeImage, true);
|
|
||||||
if (canRecategorizeImage) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/thumb-down.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.recategorize-image'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.recategorizeImages(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canRemoveOrSuggestToRemoveFromDictionary = annotationPermissions.reduce(
|
|
||||||
(acc, next) => acc && next.permissions.canRemoveOrSuggestToRemoveFromDictionary,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
if (canRemoveOrSuggestToRemoveFromDictionary) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/remove-from-dict.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.remove-annotation.remove-from-dict'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.removeOrSuggestRemoveAnnotation(null, annotations, true);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canAcceptRecommendation = annotationPermissions.reduce((acc, next) => acc && next.permissions.canAcceptRecommendation, true);
|
|
||||||
if (canAcceptRecommendation) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.accept-recommendation.label'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.convertRecommendationToAnnotation(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canAcceptSuggestion = annotationPermissions.reduce((acc, next) => acc && next.permissions.canAcceptSuggestion, true);
|
|
||||||
if (canAcceptSuggestion) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.accept-suggestion.label'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.acceptSuggestion(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canUndo = annotationPermissions.reduce((acc, next) => acc && next.permissions.canUndo, true);
|
|
||||||
if (canUndo) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/undo.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.undo'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.undoDirectAction(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canMarkAsFalsePositive = annotationPermissions.reduce((acc, next) => acc && next.permissions.canMarkAsFalsePositive, true);
|
|
||||||
if (canMarkAsFalsePositive) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/thumb-down.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.markAsFalsePositive(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canForceRedaction = annotationPermissions.reduce((acc, next) => acc && next.permissions.canForceRedaction, true);
|
|
||||||
if (canForceRedaction) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/thumb-up.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.force-redaction.label'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.forceAnnotation(null, annotations);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canForceHint = annotationPermissions.reduce((acc, next) => acc && next.permissions.canForceHint, true);
|
|
||||||
if (canForceHint) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/thumb-up.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.force-hint.label'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.forceAnnotation(null, annotations, true);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canRejectSuggestion = annotationPermissions.reduce((acc, next) => acc && next.permissions.canRejectSuggestion, true);
|
|
||||||
if (canRejectSuggestion) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/close.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.reject-suggestion'),
|
|
||||||
onClick: () => this._ngZone.run(() => this.rejectSuggestion(null, annotations)),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const canRemoveOrSuggestToRemoveOnlyHere = annotationPermissions.reduce(
|
|
||||||
(acc, next) => acc && next.permissions.canRemoveOrSuggestToRemoveOnlyHere,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
if (canRemoveOrSuggestToRemoveOnlyHere) {
|
|
||||||
availableActions.push({
|
|
||||||
type: 'actionButton',
|
|
||||||
img: this._convertPath('/assets/icons/general/trash.svg'),
|
|
||||||
title: this._translateService.instant('annotation-actions.remove-annotation.only-here'),
|
|
||||||
onClick: () =>
|
|
||||||
this._ngZone.run(() => {
|
|
||||||
this.removeOrSuggestRemoveAnnotation(null, annotations, false);
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return availableActions;
|
|
||||||
}
|
|
||||||
|
|
||||||
async resize($event: MouseEvent, annotationWrapper: AnnotationWrapper) {
|
async resize($event: MouseEvent, annotationWrapper: AnnotationWrapper) {
|
||||||
$event?.stopPropagation();
|
$event?.stopPropagation();
|
||||||
|
|
||||||
@ -415,10 +201,19 @@ export class AnnotationActionsService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async acceptResize($event: MouseEvent, annotation: AnnotationWrapper): Promise<void> {
|
async acceptResize($event: MouseEvent, annotation: AnnotationWrapper): Promise<void> {
|
||||||
const fileId = this._state.fileId;
|
const textAndPositions = await this.#extractTextAndPositions(annotation.id);
|
||||||
const textAndPositions = await this._extractTextAndPositions(annotation.id);
|
if (annotation.isRecommendation) {
|
||||||
|
const recommendation = {
|
||||||
|
...annotation,
|
||||||
|
value: textAndPositions.text,
|
||||||
|
positions: textAndPositions.positions,
|
||||||
|
} as AnnotationWrapper;
|
||||||
|
return this.convertRecommendationToAnnotation($event, [recommendation]);
|
||||||
|
}
|
||||||
|
|
||||||
const text = annotation.rectangle ? annotation.value : annotation.isImage ? 'Image' : textAndPositions.text;
|
const text = annotation.rectangle ? annotation.value : annotation.isImage ? 'Image' : textAndPositions.text;
|
||||||
const data = { annotation, text };
|
const data = { annotation, text };
|
||||||
|
|
||||||
this._dialogService.openDialog('resizeAnnotation', $event, data, (result: { comment: string; updateDictionary: boolean }) => {
|
this._dialogService.openDialog('resizeAnnotation', $event, data, (result: { comment: string; updateDictionary: boolean }) => {
|
||||||
const resizeRequest: IResizeRequest = {
|
const resizeRequest: IResizeRequest = {
|
||||||
annotationId: annotation.id,
|
annotationId: annotation.id,
|
||||||
@ -428,14 +223,9 @@ export class AnnotationActionsService {
|
|||||||
updateDictionary: result.updateDictionary,
|
updateDictionary: result.updateDictionary,
|
||||||
};
|
};
|
||||||
|
|
||||||
let obs;
|
const { fileId, dossierId } = this._state;
|
||||||
if (annotation.isRecommendation) {
|
const request = this._manualRedactionService.resizeOrSuggestResize([resizeRequest], dossierId, fileId);
|
||||||
obs = this.#convertRecommendationToRedaction(resizeRequest, annotation, fileId);
|
this.#processObsAndEmit(request);
|
||||||
} else {
|
|
||||||
obs = this._manualRedactionService.resizeOrSuggestResize([resizeRequest], this._dossier.id, fileId);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._processObsAndEmit(obs);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,17 +256,7 @@ export class AnnotationActionsService {
|
|||||||
}));
|
}));
|
||||||
const { dossierId, fileId } = this._state;
|
const { dossierId, fileId } = this._state;
|
||||||
|
|
||||||
this._processObsAndEmit(this._manualRedactionService.addAnnotation(requests, dossierId, fileId));
|
this.#processObsAndEmit(this._manualRedactionService.addAnnotation(requests, dossierId, fileId));
|
||||||
}
|
|
||||||
|
|
||||||
#convertRecommendationToRedaction(resizeRequest: IResizeRequest, annotation: AnnotationWrapper, fileId: string) {
|
|
||||||
const addRequest: IAddRedactionRequest = {
|
|
||||||
...resizeRequest,
|
|
||||||
comment: resizeRequest.comment ? { text: resizeRequest.comment } : null,
|
|
||||||
reason: 'Dictionary Request',
|
|
||||||
type: annotation.type,
|
|
||||||
};
|
|
||||||
return this._manualRedactionService.add([addRequest], this._dossier.id, fileId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#generateRectangle(annotationWrapper: AnnotationWrapper) {
|
#generateRectangle(annotationWrapper: AnnotationWrapper) {
|
||||||
@ -504,7 +284,7 @@ export class AnnotationActionsService {
|
|||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getHighlightOperationData(operation: TextHighlightOperation, highlights: AnnotationWrapper[]) {
|
#getHighlightOperationData(operation: TextHighlightOperation, highlights: AnnotationWrapper[]) {
|
||||||
return {
|
return {
|
||||||
dossierId: this._state.dossierId,
|
dossierId: this._state.dossierId,
|
||||||
fileId: this._state.fileId,
|
fileId: this._state.fileId,
|
||||||
@ -516,7 +296,7 @@ export class AnnotationActionsService {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private _processObsAndEmit(obs: Observable<unknown>) {
|
#processObsAndEmit(obs: Observable<unknown>) {
|
||||||
obs.subscribe({
|
obs.subscribe({
|
||||||
next: () => this._fileDataService.annotationsChanged(),
|
next: () => this._fileDataService.annotationsChanged(),
|
||||||
error: () => this._fileDataService.annotationsChanged(),
|
error: () => this._fileDataService.annotationsChanged(),
|
||||||
@ -546,11 +326,7 @@ export class AnnotationActionsService {
|
|||||||
return annotation.value;
|
return annotation.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _convertPath(path: string): string {
|
async #extractTextAndPositions(annotationId: string) {
|
||||||
return this._baseHref + path;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _extractTextAndPositions(annotationId: string) {
|
|
||||||
const viewerAnnotation = this._annotationManager.get(annotationId);
|
const viewerAnnotation = this._annotationManager.get(annotationId);
|
||||||
|
|
||||||
const document = await this._documentViewer.PDFDoc;
|
const document = await this._documentViewer.PDFDoc;
|
||||||
|
|||||||
@ -68,7 +68,7 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
|
|||||||
addToDictionary: true,
|
addToDictionary: true,
|
||||||
sourceId: annotation.annotationId,
|
sourceId: annotation.annotationId,
|
||||||
value: annotation.value,
|
value: annotation.value,
|
||||||
reason: annotation.legalBasis,
|
reason: annotation.legalBasis ?? 'Dictionary Request',
|
||||||
positions: annotation.positions,
|
positions: annotation.positions,
|
||||||
type: annotation.recommendationType,
|
type: annotation.recommendationType,
|
||||||
comment,
|
comment,
|
||||||
|
|||||||
@ -0,0 +1,168 @@
|
|||||||
|
import { inject, Injectable, NgZone } from '@angular/core';
|
||||||
|
import { AnnotationWrapper } from '../../../models/file/annotation.wrapper';
|
||||||
|
import { AnnotationPermissions } from '../../../models/file/annotation.permissions';
|
||||||
|
import { PermissionsService } from '../../../services/permissions.service';
|
||||||
|
import { UserService } from '../../../services/user.service';
|
||||||
|
import { DictionariesMapService } from '../../../services/entity-services/dictionaries-map.service';
|
||||||
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { AnnotationActionsService } from './annotation-actions.service';
|
||||||
|
import { BASE_HREF_FN } from '../../../tokens';
|
||||||
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
|
import { IHeaderElement } from '@red/domain';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PdfAnnotationActionsService {
|
||||||
|
readonly #permissionsService = inject(PermissionsService);
|
||||||
|
readonly #currentUser = inject(UserService).currentUser;
|
||||||
|
readonly #dictionariesMapService = inject(DictionariesMapService);
|
||||||
|
readonly #state = inject(FilePreviewStateService);
|
||||||
|
readonly #translateService = inject(TranslateService);
|
||||||
|
readonly #ngZone = inject(NgZone);
|
||||||
|
readonly #convertPath = inject(BASE_HREF_FN);
|
||||||
|
readonly #annotationActionsService = inject(AnnotationActionsService);
|
||||||
|
|
||||||
|
get(annotations: AnnotationWrapper[]): IHeaderElement[] {
|
||||||
|
const availableActions: IHeaderElement[] = [];
|
||||||
|
const permissions = this.#getAnnotationsPermissions(annotations);
|
||||||
|
|
||||||
|
// you can only resize one annotation at a time
|
||||||
|
if (permissions.canResize) {
|
||||||
|
const firstAnnotation = annotations[0];
|
||||||
|
// if we already entered resize-mode previously
|
||||||
|
if (firstAnnotation.resizing) {
|
||||||
|
const acceptResizeButton = this.#getButton('check', _('annotation-actions.resize-accept.label'), () =>
|
||||||
|
this.#annotationActionsService.acceptResize(null, firstAnnotation),
|
||||||
|
);
|
||||||
|
const cancelResizeButton = this.#getButton('close', _('annotation-actions.resize-cancel.label'), () =>
|
||||||
|
this.#annotationActionsService.cancelResize(null, firstAnnotation),
|
||||||
|
);
|
||||||
|
|
||||||
|
return [acceptResizeButton, cancelResizeButton];
|
||||||
|
}
|
||||||
|
|
||||||
|
const resizeButton = this.#getButton('resize', _('annotation-actions.resize.label'), () =>
|
||||||
|
this.#annotationActionsService.resize(null, firstAnnotation),
|
||||||
|
);
|
||||||
|
|
||||||
|
availableActions.push(resizeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canChangeLegalBasis) {
|
||||||
|
const editButton = this.#getButton('edit', _('annotation-actions.edit-reason.label'), () =>
|
||||||
|
this.#annotationActionsService.changeLegalBasis(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(editButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canRecategorizeImage) {
|
||||||
|
const recategorizeButton = this.#getButton('thumb-down', _('annotation-actions.recategorize-image'), () =>
|
||||||
|
this.#annotationActionsService.recategorizeImages(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(recategorizeButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canRemoveOrSuggestToRemoveFromDictionary) {
|
||||||
|
const removeFromDictButton = this.#getButton(
|
||||||
|
'remove-from-dict',
|
||||||
|
_('annotation-actions.remove-annotation.remove-from-dict'),
|
||||||
|
() => this.#annotationActionsService.removeOrSuggestRemoveAnnotation(null, annotations, true),
|
||||||
|
);
|
||||||
|
availableActions.push(removeFromDictButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canAcceptRecommendation) {
|
||||||
|
const acceptRecommendationButton = this.#getButton('check', _('annotation-actions.accept-recommendation.label'), () =>
|
||||||
|
this.#annotationActionsService.convertRecommendationToAnnotation(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(acceptRecommendationButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canAcceptSuggestion) {
|
||||||
|
const acceptSuggestionButton = this.#getButton('check', _('annotation-actions.accept-suggestion.label'), () =>
|
||||||
|
this.#annotationActionsService.acceptSuggestion(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(acceptSuggestionButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canUndo) {
|
||||||
|
const undoButton = this.#getButton('undo', _('annotation-actions.undo'), () =>
|
||||||
|
this.#annotationActionsService.undoDirectAction(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(undoButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canMarkAsFalsePositive) {
|
||||||
|
const markAsFalsePositiveButton = this.#getButton('thumb-down', _('annotation-actions.remove-annotation.false-positive'), () =>
|
||||||
|
this.#annotationActionsService.markAsFalsePositive(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(markAsFalsePositiveButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canForceRedaction) {
|
||||||
|
const forceRedactionButton = this.#getButton('thumb-up', _('annotation-actions.force-redaction.label'), () =>
|
||||||
|
this.#annotationActionsService.forceAnnotation(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(forceRedactionButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canForceHint) {
|
||||||
|
const forceHintButton = this.#getButton('thumb-up', _('annotation-actions.force-hint.label'), () =>
|
||||||
|
this.#annotationActionsService.forceAnnotation(null, annotations, true),
|
||||||
|
);
|
||||||
|
availableActions.push(forceHintButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canRejectSuggestion) {
|
||||||
|
const rejectSuggestionButton = this.#getButton('close', _('annotation-actions.reject-suggestion'), () =>
|
||||||
|
this.#annotationActionsService.rejectSuggestion(null, annotations),
|
||||||
|
);
|
||||||
|
availableActions.push(rejectSuggestionButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (permissions.canRemoveOrSuggestToRemoveOnlyHere) {
|
||||||
|
const removeOrSuggestToRemoveOnlyHereButton = this.#getButton(
|
||||||
|
'trash',
|
||||||
|
_('annotation-actions.remove-annotation.only-here'),
|
||||||
|
() => this.#annotationActionsService.removeOrSuggestRemoveAnnotation(null, annotations, false),
|
||||||
|
);
|
||||||
|
availableActions.push(removeOrSuggestToRemoveOnlyHereButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableActions;
|
||||||
|
}
|
||||||
|
|
||||||
|
#getButton(icon: string, title: string, action: () => void | Promise<void>): IHeaderElement {
|
||||||
|
return {
|
||||||
|
type: 'actionButton',
|
||||||
|
img: this.#convertPath(`/assets/icons/general/${icon}.svg`),
|
||||||
|
title: this.#translateService.instant(title),
|
||||||
|
onClick: () => this.#ngZone.run(async () => action()),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#getAnnotationsPermissions(annotations: AnnotationWrapper[]) {
|
||||||
|
const dossier = this.#state.dossier;
|
||||||
|
const isApprover = this.#permissionsService.isApprover(dossier);
|
||||||
|
const dictionaries = this.#dictionariesMapService.get(dossier.dossierTemplateId);
|
||||||
|
|
||||||
|
const permissions = annotations.map(a => AnnotationPermissions.forUser(isApprover, this.#currentUser, a, dictionaries));
|
||||||
|
return {
|
||||||
|
canResize: permissions.length === 1 && permissions[0].canResizeAnnotation,
|
||||||
|
canChangeLegalBasis: permissions.reduce((acc, next) => acc && next.canChangeLegalBasis, true),
|
||||||
|
canRecategorizeImage: permissions.reduce((acc, next) => acc && next.canRecategorizeImage, true),
|
||||||
|
canRemoveOrSuggestToRemoveFromDictionary: permissions.reduce(
|
||||||
|
(acc, next) => acc && next.canRemoveOrSuggestToRemoveFromDictionary,
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
canAcceptRecommendation: permissions.reduce((acc, next) => acc && next.canAcceptRecommendation, true),
|
||||||
|
canAcceptSuggestion: permissions.reduce((acc, next) => acc && next.canAcceptSuggestion, true),
|
||||||
|
canUndo: permissions.reduce((acc, next) => acc && next.canUndo, true),
|
||||||
|
canMarkAsFalsePositive: permissions.reduce((acc, next) => acc && next.canMarkAsFalsePositive, true),
|
||||||
|
canForceRedaction: permissions.reduce((acc, next) => acc && next.canForceRedaction, true),
|
||||||
|
canForceHint: permissions.reduce((acc, next) => acc && next.canForceHint, true),
|
||||||
|
canRejectSuggestion: permissions.reduce((acc, next) => acc && next.canRejectSuggestion, true),
|
||||||
|
canRemoveOrSuggestToRemoveOnlyHere: permissions.reduce((acc, next) => acc && next.canRemoveOrSuggestToRemoveOnlyHere, true),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,6 @@ import {
|
|||||||
ManualRedactionEntryWrapper,
|
ManualRedactionEntryWrapper,
|
||||||
} from '../../../models/file/manual-redaction-entry.wrapper';
|
} from '../../../models/file/manual-redaction-entry.wrapper';
|
||||||
import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service';
|
import { AnnotationDrawService } from '../../pdf-viewer/services/annotation-draw.service';
|
||||||
import { AnnotationActionsService } from './annotation-actions.service';
|
|
||||||
import { UserPreferenceService } from '../../../services/user-preference.service';
|
import { UserPreferenceService } from '../../../services/user-preference.service';
|
||||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../tokens';
|
import { BASE_HREF_FN, BaseHrefFn } from '../../../tokens';
|
||||||
import { shareDistinctLast } from '@iqser/common-ui';
|
import { shareDistinctLast } from '@iqser/common-ui';
|
||||||
@ -28,6 +27,7 @@ import { combineLatest, Observable, Subject } from 'rxjs';
|
|||||||
import { ViewModeService } from './view-mode.service';
|
import { ViewModeService } from './view-mode.service';
|
||||||
import { PermissionsService } from '../../../services/permissions.service';
|
import { PermissionsService } from '../../../services/permissions.service';
|
||||||
import { AnnotationsListingService } from './annotations-listing.service';
|
import { AnnotationsListingService } from './annotations-listing.service';
|
||||||
|
import { PdfAnnotationActionsService } from './pdf-annotation-actions.service';
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
import Quad = Core.Math.Quad;
|
import Quad = Core.Math.Quad;
|
||||||
|
|
||||||
@ -57,7 +57,7 @@ export class PdfProxyService {
|
|||||||
private readonly _ngZone: NgZone,
|
private readonly _ngZone: NgZone,
|
||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _annotationDrawService: AnnotationDrawService,
|
private readonly _annotationDrawService: AnnotationDrawService,
|
||||||
private readonly _annotationActionsService: AnnotationActionsService,
|
private readonly _pdfAnnotationActionsService: PdfAnnotationActionsService,
|
||||||
private readonly _fileDataService: FileDataService,
|
private readonly _fileDataService: FileDataService,
|
||||||
private readonly _viewerHeaderService: ViewerHeaderService,
|
private readonly _viewerHeaderService: ViewerHeaderService,
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
@ -191,7 +191,7 @@ export class PdfProxyService {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actions = this._annotationActionsService.getViewerAvailableActions(annotationWrappers);
|
const actions = this._pdfAnnotationActionsService.get(annotationWrappers);
|
||||||
this.instance.UI.annotationPopup.add(actions);
|
this.instance.UI.annotationPopup.add(actions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user