Merge branch 'RED-10034' into 'master'

RED-10034: Removed unnecessary draw call?

Closes RED-10034

See merge request redactmanager/red-ui!593
This commit is contained in:
Timo Bejan 2024-09-28 16:13:35 +02:00
commit fbc7dfce60
12 changed files with 413 additions and 425 deletions

View File

@ -1,161 +1,165 @@
<div
*ngIf="canPerformAnnotationActions && annotationPermissions"
[class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)"
class="annotation-actions"
>
<!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
<ng-container *ngIf="resizing && annotationPermissions.canResizeAnnotation">
<iqser-circle-button
(action)="acceptResize()"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-accept_resize' : 'annotations-accept_resize'"
[class.disabled]="!resized"
[tooltipPosition]="tooltipPosition"
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
@if (canPerformAnnotationActions && annotationPermissions()) {
<div [class.always-visible]="alwaysVisible || (helpModeService.isHelpModeActive$ | async)" class="annotation-actions">
<!-- Resize Mode for annotation -> only resize accept and deny actions are available-->
<ng-container *ngIf="resizing() && annotationPermissions().canResizeAnnotation">
<iqser-circle-button
(action)="acceptResize()"
[buttonId]="
annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-accept_resize' : 'annotations-accept_resize'
"
[class.disabled]="!resized"
[tooltipPosition]="tooltipPosition"
[tooltip]="resized ? ('annotation-actions.resize-accept.label' | translate) : ''"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
<iqser-circle-button
(action)="cancelResize()"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-cancel_resize' : 'annotations-cancel_resize'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize-cancel.label' | translate"
[type]="buttonType"
icon="iqser:close"
></iqser-circle-button>
</ng-container>
<iqser-circle-button
(action)="cancelResize()"
[buttonId]="
annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-cancel_resize' : 'annotations-cancel_resize'
"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize-cancel.label' | translate"
[type]="buttonType"
icon="iqser:close"
></iqser-circle-button>
</ng-container>
<!-- Not resizing - standard actions -->
<ng-container *ngIf="!resizing">
<iqser-circle-button
(action)="resize()"
*ngIf="canResize"
[attr.help-mode-key]="helpModeKey('resize')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-resize' : 'annotations-resize'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize.label' | translate"
[type]="buttonType"
icon="iqser:resize"
></iqser-circle-button>
<!-- Not resizing - standard actions -->
<ng-container *ngIf="!resizing()">
<iqser-circle-button
(action)="resize()"
*ngIf="canResize()"
[attr.help-mode-key]="helpModeKey('resize')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-resize' : 'annotations-resize'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.resize.label' | translate"
[type]="buttonType"
icon="iqser:resize"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.editRedaction(annotations)"
*ngIf="canEdit"
[attr.help-mode-key]="helpModeKey('edit')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-edit' : 'annotations-edit'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
[type]="buttonType"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.editRedaction(annotations())"
*ngIf="canEdit()"
[attr.help-mode-key]="helpModeKey('edit')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-edit' : 'annotations-edit'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.edit-redaction.label' | translate"
[type]="buttonType"
icon="iqser:edit"
></iqser-circle-button>
<iqser-circle-button
(action)="acceptRecommendation()"
*ngIf="canAcceptRecommendation"
[attr.help-mode-key]="helpModeKey('accept')"
[buttonId]="
annotations.length === 1
? 'annotation-' + annotations[0].id + '-accept_recommendation'
: 'annotations-accept_recommendation'
"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.accept-recommendation.label' | translate"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
<iqser-circle-button
(action)="acceptRecommendation()"
*ngIf="canAcceptRecommendation()"
[attr.help-mode-key]="helpModeKey('accept')"
[buttonId]="
annotations().length === 1
? 'annotation-' + annotations()[0].id + '-accept_recommendation'
: 'annotations-accept_recommendation'
"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.accept-recommendation.label' | translate"
[type]="buttonType"
icon="iqser:check"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.convertHighlights(annotations)"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.convert-highlights.label' | translate"
[type]="buttonType"
icon="red:convert"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.convertHighlights(annotations())"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.convert-highlights.label' | translate"
[type]="buttonType"
icon="red:convert"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.removeHighlights(annotations)"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-highlights.label' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.removeHighlights(annotations())"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-highlights.label' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.undoDirectAction(annotations)"
*allow="roles.redactions.deleteManual; if: annotationPermissions.canUndo"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.undo' | translate"
[type]="buttonType"
icon="red:undo"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.undoDirectAction(annotations())"
*allow="roles.redactions.deleteManual; if: annotationPermissions().canUndo"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.undo' | translate"
[type]="buttonType"
icon="red:undo"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationReferencesService.show(annotations[0])"
*ngIf="multiSelectService.inactive() && annotations[0].reference.length"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.see-references.label' | translate"
[type]="buttonType"
icon="red:reference"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationReferencesService.show(annotations()[0])"
*ngIf="multiSelectService.inactive() && annotations()[0].reference.length"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.see-references.label' | translate"
[type]="buttonType"
icon="red:reference"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations)"
*ngIf="canForceRedaction"
[attr.help-mode-key]="isImageHint ? helpModeKey('redact') : helpModeKey('force')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-force_redaction' : 'annotations-force_redaction'"
[icon]="isImageHint ? 'red:pdftron-action-add-redaction' : 'iqser:thumb-up'"
[tooltipPosition]="tooltipPosition"
[tooltip]="
isImageHint
? ('annotation-actions.force-redaction.label-image-hint' | translate)
: ('annotation-actions.force-redaction.label' | translate)
"
[type]="buttonType"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations())"
*ngIf="canForceRedaction()"
[attr.help-mode-key]="isImageHint() ? helpModeKey('redact') : helpModeKey('force')"
[buttonId]="
annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-force_redaction' : 'annotations-force_redaction'
"
[icon]="isImageHint() ? 'red:pdftron-action-add-redaction' : 'iqser:thumb-up'"
[tooltipPosition]="tooltipPosition"
[tooltip]="
isImageHint()
? ('annotation-actions.force-redaction.label-image-hint' | translate)
: ('annotation-actions.force-redaction.label' | translate)
"
[type]="buttonType"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations, true)"
*ngIf="canForceHint"
[attr.help-mode-key]="actionsHelpModeKey"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-force_hint' : 'annotations-force_hint'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.force-hint.label' | translate"
[type]="buttonType"
icon="iqser:thumb-up"
></iqser-circle-button>
<iqser-circle-button
(action)="annotationActionsService.forceAnnotation(annotations(), true)"
*ngIf="canForceHint()"
[attr.help-mode-key]="actionsHelpModeKey"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-force_hint' : 'annotations-force_hint'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.force-hint.label' | translate"
[type]="buttonType"
icon="iqser:thumb-up"
></iqser-circle-button>
<iqser-circle-button
(action)="hideAnnotation()"
*ngIf="isImage && isVisible() && !hideSkipped"
[attr.help-mode-key]="helpModeKey('hide')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-hide' : 'annotations-hide'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.hide' | translate"
[type]="buttonType"
icon="iqser:visibility-off"
></iqser-circle-button>
<iqser-circle-button
(action)="hideAnnotation()"
*ngIf="isImage() && isVisible() && !hideSkipped()"
[attr.help-mode-key]="helpModeKey('hide')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-hide' : 'annotations-hide'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.hide' | translate"
[type]="buttonType"
icon="iqser:visibility-off"
></iqser-circle-button>
<iqser-circle-button
(action)="showAnnotation()"
*ngIf="isImage && !isVisible() && !hideSkipped"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-show' : 'annotations-show'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.show' | translate"
[type]="buttonType"
icon="iqser:visibility"
></iqser-circle-button>
<iqser-circle-button
(action)="showAnnotation()"
*ngIf="isImage() && !isVisible() && !hideSkipped()"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-show' : 'annotations-show'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.show' | translate"
[type]="buttonType"
icon="iqser:visibility"
></iqser-circle-button>
<iqser-circle-button
(action)="removeRedaction()"
*ngIf="canRemoveRedaction"
[attr.help-mode-key]="helpModeKey('remove')"
[buttonId]="annotations.length === 1 ? 'annotation-' + annotations[0].id + '-remove' : 'annotations-remove'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
</ng-container>
</div>
<iqser-circle-button
(action)="removeRedaction()"
*ngIf="canRemoveRedaction()"
[attr.help-mode-key]="helpModeKey('remove')"
[buttonId]="annotations().length === 1 ? 'annotation-' + annotations()[0].id + '-remove' : 'annotations-remove'"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-annotation.remove-redaction' | translate"
[type]="buttonType"
icon="iqser:trash"
></iqser-circle-button>
</ng-container>
</div>
}

View File

@ -1,4 +1,4 @@
import { Component, computed, Input, OnChanges } from '@angular/core';
import { Component, computed, input, Input, untracked } from '@angular/core';
import { CircleButtonComponent, getConfig, HelpModeService, IqserAllowDirective, IqserPermissionsService } from '@iqser/common-ui';
import { AnnotationPermissions } from '@models/file/annotation.permissions';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
@ -28,21 +28,77 @@ export type AnnotationButtonType = keyof typeof AnnotationButtonTypes;
standalone: true,
imports: [CircleButtonComponent, NgIf, TranslateModule, AsyncPipe, IqserAllowDirective],
})
export class AnnotationActionsComponent implements OnChanges {
#annotations: AnnotationWrapper[] = [];
readonly #isDocumine = getConfig().IS_DOCUMINE;
protected _annotationId = '';
export class AnnotationActionsComponent {
@Input() buttonType: AnnotationButtonType = AnnotationButtonTypes.default;
@Input() tooltipPosition: 'before' | 'above' = 'before';
@Input() canPerformAnnotationActions: boolean;
@Input() alwaysVisible: boolean;
@Input() actionsHelpModeKey: string;
readonly roles = Roles;
annotationPermissions: AnnotationPermissions;
isImage = true;
readonly annotations = input.required<AnnotationWrapper[], (AnnotationWrapper | undefined)[]>({
transform: value => value.filter(a => a !== undefined),
});
readonly isVisible = computed(() => {
const hidden = this._annotationManager.hidden();
return this.#annotations.reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
return this.annotations().reduce((acc, annotation) => !hidden.has(annotation.id) && acc, true);
});
readonly somePending = computed(() => {
return this.annotations().some(a => a.pending);
});
readonly sameType = computed(() => {
const annotations = this.annotations();
const type = annotations[0].superType;
return annotations.every(a => a.superType === type);
});
readonly resizing = computed(() => {
return this.annotations().length === 1 && this.annotations()[0].id === this._annotationManager.resizingAnnotationId;
});
readonly viewerAnnotations = computed(() => {
return this._annotationManager.get(this.annotations());
});
readonly annotationPermissions = computed(() =>
AnnotationPermissions.forUser(
this._permissionsService.isApprover(this._state.dossier()),
this.annotations(),
this._state.dictionaries,
this._iqserPermissionsService,
this._state.file().excludedFromAutomaticAnalysis,
),
);
readonly hideSkipped = computed(() => this.skippedService.hideSkipped() && this.annotations().some(a => a.isSkipped));
readonly isImageHint = computed(() => this.annotations().every(a => a.IMAGE_HINT));
readonly isImage = computed(() => this.annotations().reduce((acc, a) => acc && a.isImage, true));
readonly canRemoveRedaction = computed(
() => this.annotationChangesAllowed() && this.annotationPermissions().canRemoveRedaction && this.sameType(),
);
readonly canForceRedaction = computed(() => this.annotationChangesAllowed() && this.annotationPermissions().canForceRedaction);
readonly canForceHint = computed(() => this.annotationChangesAllowed() && this.annotationPermissions().canForceHint);
readonly canAcceptRecommendation = computed(
() => this.annotationChangesAllowed() && this.annotationPermissions().canAcceptRecommendation,
);
readonly #isDocumine = getConfig().IS_DOCUMINE;
readonly annotationChangesAllowed = computed(
() => (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.somePending(),
);
readonly canResize = computed(
() => this.annotationChangesAllowed() && this.annotationPermissions().canResizeAnnotation && this.annotations().length === 1,
);
readonly canEdit = computed(() => {
const canEditRedactions =
this.annotationPermissions().canChangeLegalBasis ||
this.annotationPermissions().canRecategorizeAnnotation ||
this.annotationPermissions().canForceHint ||
this.annotationPermissions().canForceRedaction;
return (
this.annotationChangesAllowed() &&
(this.annotations().length > 1
? this.#isDocumine
? this.annotationPermissions().canEditAnnotations
: this.annotationPermissions().canEditHints ||
this.annotationPermissions().canEditImages ||
this.annotationPermissions().canEditAnnotations
: canEditRedactions)
);
});
constructor(
@ -58,137 +114,54 @@ export class AnnotationActionsComponent implements OnChanges {
readonly annotationReferencesService: AnnotationReferencesService,
) {}
get annotations(): AnnotationWrapper[] {
return this.#annotations;
}
get isImageHint(): boolean {
return this.annotations.every(annotation => annotation.IMAGE_HINT);
}
@Input()
set annotations(annotations: AnnotationWrapper[]) {
this.#annotations = annotations.filter(a => a !== undefined);
this.isImage = this.#annotations?.reduce((accumulator, annotation) => annotation.isImage && accumulator, true);
this._annotationId = this.#annotations[0]?.id;
}
get canEdit(): boolean {
const canEditRedactions =
this.annotationPermissions.canChangeLegalBasis ||
this.annotationPermissions.canRecategorizeAnnotation ||
this.annotationPermissions.canForceHint ||
this.annotationPermissions.canForceRedaction;
return (
this.#annotationChangesAllowed &&
(this.annotations.length > 1
? this.#isDocumine
? this.annotationPermissions.canEditAnnotations
: this.annotationPermissions.canEditHints ||
this.annotationPermissions.canEditImages ||
this.annotationPermissions.canEditAnnotations
: canEditRedactions)
);
}
get canResize(): boolean {
return this.#annotationChangesAllowed && this.annotationPermissions.canResizeAnnotation && this.annotations.length === 1;
}
get canRemoveRedaction(): boolean {
return this.#annotationChangesAllowed && this.annotationPermissions.canRemoveRedaction && this.#sameType;
}
get canForceRedaction() {
return this.#annotationChangesAllowed && this.annotationPermissions.canForceRedaction;
}
get canForceHint() {
return this.#annotationChangesAllowed && this.annotationPermissions.canForceHint;
}
get canAcceptRecommendation() {
return this.#annotationChangesAllowed && this.annotationPermissions.canAcceptRecommendation;
}
get viewerAnnotations() {
return this._annotationManager.get(this.#annotations);
}
get resizing() {
return this.#annotations?.length === 1 && this.#annotations?.[0].id === this._annotationManager.resizingAnnotationId;
}
get resized() {
get resized(): boolean {
return this._annotationManager.annotationHasBeenResized;
}
get hideSkipped() {
return this.skippedService.hideSkipped() && this.annotations.some(a => a.isSkipped);
async removeRedaction(): Promise<void> {
const annotations = untracked(this.annotations);
const permissions = untracked(this.annotationPermissions);
await this.annotationActionsService.removeRedaction(annotations, permissions);
}
get #sameType() {
const type = this.annotations[0].superType;
return this.annotations.every(a => a.superType === type);
}
ngOnChanges(): void {
this.#setPermissions();
}
removeRedaction() {
this.annotationActionsService.removeRedaction(this.annotations, this.annotationPermissions);
}
acceptRecommendation() {
return this.annotationActionsService.convertRecommendationToAnnotation(this.annotations);
async acceptRecommendation(): Promise<void> {
const annotations = untracked(this.annotations);
await this.annotationActionsService.convertRecommendationToAnnotation(annotations);
}
hideAnnotation() {
this._annotationManager.hide(this.viewerAnnotations);
const viewerAnnotations = untracked(this.viewerAnnotations);
this._annotationManager.hide(viewerAnnotations);
this._annotationManager.deselect();
this._annotationManager.addToHidden(this.viewerAnnotations[0].Id);
this._annotationManager.addToHidden(viewerAnnotations[0].Id);
}
showAnnotation() {
this._annotationManager.show(this.viewerAnnotations);
const viewerAnnotations = untracked(this.viewerAnnotations);
this._annotationManager.show(viewerAnnotations);
this._annotationManager.deselect();
this._annotationManager.removeFromHidden(this.viewerAnnotations[0].Id);
this._annotationManager.removeFromHidden(viewerAnnotations[0].Id);
}
resize() {
return this.annotationActionsService.resize(this.#annotations[0]);
const annotations = untracked(this.annotations);
return this.annotationActionsService.resize(annotations[0]);
}
acceptResize() {
if (this.resized) {
return this.annotationActionsService.acceptResize(this.#annotations[0], this.annotationPermissions);
const annotations = untracked(this.annotations);
const permissions = untracked(this.annotationPermissions);
return this.annotationActionsService.acceptResize(annotations[0], permissions);
}
}
cancelResize() {
return this.annotationActionsService.cancelResize(this.#annotations[0]);
const annotations = untracked(this.annotations);
return this.annotationActionsService.cancelResize(annotations[0]);
}
helpModeKey(action: string) {
return this.#isDocumine ? `${action}_annotation` : `${this.actionsHelpModeKey}_${action}`;
}
#setPermissions() {
this.annotationPermissions = AnnotationPermissions.forUser(
this._permissionsService.isApprover(this._state.dossier()),
this.#annotations,
this._state.dictionaries,
this._iqserPermissionsService,
this._state.file().excludedFromAutomaticAnalysis,
);
}
get #annotationChangesAllowed() {
return (!this.#isDocumine || !this._state.file().excludedFromAutomaticAnalysis) && !this.#somePending;
}
get #somePending() {
return this.#annotations.some(a => a.pending);
}
}

View File

@ -1,37 +1,42 @@
<div class="active-bar-marker"></div>
<div [class.removed]="annotation.item.isRemoved" class="annotation">
<div [class.removed]="annotation().item.isRemoved" class="annotation">
<redaction-annotation-card
[annotation]="annotation.item"
[isSelected]="annotation.isSelected"
[matTooltip]="annotation.item.content.translation | translate: annotation.item.content.params | replaceNbsp"
[annotation]="annotation().item"
[isSelected]="annotation().isSelected"
[matTooltip]="annotation().item.content.translation | translate: annotation().item.content.params | replaceNbsp"
matTooltipPosition="above"
></redaction-annotation-card>
<div *ngIf="!annotation.item.isEarmark" class="actions-wrapper">
<div
*ngIf="!annotation.item.pending"
(click)="showComments = !showComments"
[matTooltip]="'comments.comments' | translate: { count: annotation.item.numberOfComments }"
class="comments-counter"
iqserStopPropagation
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation.item.numberOfComments }}
</div>
@if (!annotation().item.isEarmark) {
<div class="actions-wrapper">
@if (!annotation().item.pending) {
<div
(click)="showComments = !showComments"
[matTooltip]="'comments.comments' | translate: { count: annotation().item.numberOfComments }"
class="comments-counter"
iqserStopPropagation
matTooltipPosition="above"
>
<mat-icon svgIcon="red:comment"></mat-icon>
{{ annotation().item.numberOfComments }}
</div>
}
<div *ngIf="multiSelectService.inactive()" class="actions">
<redaction-annotation-actions
[annotations]="[annotation.item]"
[actionsHelpModeKey]="actionsHelpModeKey"
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
></redaction-annotation-actions>
@if (multiSelectService.inactive()) {
<div class="actions">
<redaction-annotation-actions
[actionsHelpModeKey]="actionsHelpModeKey()"
[annotations]="[annotation().item]"
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
></redaction-annotation-actions>
</div>
}
</div>
</div>
}
<ng-container *ngIf="showComments">
<redaction-comments [annotation]="annotation.item"></redaction-comments>
@if (showComments) {
<redaction-comments [annotation]="annotation().item"></redaction-comments>
<div
(click)="showComments = false"
@ -39,7 +44,7 @@
iqserStopPropagation
translate="comments.hide-comments"
></div>
</ng-container>
}
</div>
<redaction-annotation-details [annotation]="annotation"></redaction-annotation-details>
<redaction-annotation-details [annotation]="annotation()"></redaction-annotation-details>

View File

@ -1,4 +1,4 @@
import { Component, HostBinding, inject, Input, OnChanges } from '@angular/core';
import { Component, computed, effect, HostBinding, inject, input } from '@angular/core';
import { getConfig, StopPropagationDirective } from '@iqser/common-ui';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ListItem } from '@models/file/list-item';
@ -6,7 +6,6 @@ import { MultiSelectService } from '../../services/multi-select.service';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { ActionsHelpModeKeys } from '../../utils/constants';
import { AnnotationCardComponent } from '../annotation-card/annotation-card.component';
import { NgIf } from '@angular/common';
import { MatTooltip } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { MatIcon } from '@angular/material/icon';
@ -22,7 +21,6 @@ import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
standalone: true,
imports: [
AnnotationCardComponent,
NgIf,
MatTooltip,
TranslateModule,
StopPropagationDirective,
@ -33,26 +31,28 @@ import { ReplaceNbspPipe } from '@common-ui/pipes/replace-nbsp.pipe';
ReplaceNbspPipe,
],
})
export class AnnotationWrapperComponent implements OnChanges {
readonly #isDocumine = getConfig().IS_DOCUMINE;
export class AnnotationWrapperComponent {
readonly annotation = input.required<ListItem<AnnotationWrapper>>();
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active: boolean;
readonly actionsHelpModeKey = computed(() => this.#getActionsHelpModeKey(this.annotation().item));
showComments = false;
protected readonly pdfProxyService = inject(PdfProxyService);
protected readonly multiSelectService = inject(MultiSelectService);
@Input({ required: true }) annotation!: ListItem<AnnotationWrapper>;
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active = false;
actionsHelpModeKey: string;
showComments = false;
readonly #isDocumine = getConfig().IS_DOCUMINE;
ngOnChanges() {
this.annotationId = this.annotation.item.id;
this.active = this.annotation.isSelected;
this.actionsHelpModeKey = this.#getActionsHelpModeKey();
constructor() {
effect(() => {
this.active = this.annotation().isSelected;
this.annotationId = this.annotation().item.id;
});
}
#getActionsHelpModeKey(): string {
#getActionsHelpModeKey(item: AnnotationWrapper): string {
if (!this.#isDocumine) {
const superType = this.annotation.item.superTypeLabel?.split('.')[1];
const type = this.annotation.item.type;
const superType = item.superTypeLabel?.split('.')[1];
const type = item.type;
if (superType === 'hint' && (type === 'ocr' || type === 'formula' || type === 'image')) {
return ActionsHelpModeKeys[`${superType}-${type}`];
}

View File

@ -1,16 +1,16 @@
<ng-container *ngFor="let annotation of annotations; let idx = index; trackBy: _trackBy">
<div *ngIf="showHighlightGroup(idx) as highlightGroup" class="workload-separator">
@for (annotation of annotations(); track annotation.item.id) {
@if (displayedHighlightGroups()[$index]; as highlightGroup) {
<redaction-highlights-separator [annotation]="annotation.item" [highlightGroup]="highlightGroup"></redaction-highlights-separator>
</div>
}
<redaction-annotation-wrapper
*ngIf="!annotation.item.hiddenInWorkload"
(click)="annotationClicked(annotation.item, $event)"
*ngIf="!annotation.item.hiddenInWorkload"
[annotation]="annotation"
[id]="'annotation-' + annotation.item.id"
[class.documine-wrapper]="isDocumine"
[id]="'annotation-' + annotation.item.id"
></redaction-annotation-wrapper>
</ng-container>
}
<redaction-annotation-references-list
(referenceClicked)="referenceClicked($event)"

View File

@ -1,4 +1,4 @@
import { Component, computed, ElementRef, EventEmitter, Input, Output } from '@angular/core';
import { Component, computed, ElementRef, input, output } from '@angular/core';
import { getConfig, HasScrollbarDirective } from '@iqser/common-ui';
import { FilterService } from '@iqser/common-ui/lib/filtering';
import { IqserEventTarget } from '@iqser/common-ui/lib/utils';
@ -24,13 +24,24 @@ import { AnnotationReferencesListComponent } from '../annotation-references-list
imports: [NgForOf, NgIf, HighlightsSeparatorComponent, AnnotationWrapperComponent, AnnotationReferencesListComponent],
})
export class AnnotationsListComponent extends HasScrollbarDirective {
@Input({ required: true }) annotations: ListItem<AnnotationWrapper>[];
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
readonly #earmarkGroups = computed(() => {
if (this._viewModeService.isEarmarks()) {
return this.#getEarmarksGroups();
readonly annotations = input.required<ListItem<AnnotationWrapper>[]>();
readonly pagesPanelActive = output<boolean>();
readonly displayedHighlightGroups = computed(() => {
const isEarMarks = this._viewModeService.isEarmarks();
let result: Record<number, EarmarkGroup> = {};
if (isEarMarks) {
const annotations = this.annotations();
const earmarkGroups = this.#getEarmarksGroups(annotations);
for (let idx = 0; idx < annotations.length; ++idx) {
const group = earmarkGroups.find(h => h.startIdx === idx);
if (group) {
result[idx] = group;
}
}
}
return [] as EarmarkGroup[];
return result;
});
protected readonly isDocumine = getConfig().IS_DOCUMINE;
@ -77,26 +88,20 @@ export class AnnotationsListComponent extends HasScrollbarDirective {
}
}
showHighlightGroup(idx: number): EarmarkGroup {
return this._viewModeService.isEarmarks() && this.#earmarkGroups().find(h => h.startIdx === idx);
}
protected readonly _trackBy = (index: number, listItem: ListItem<AnnotationWrapper>) => listItem.item.id;
#getEarmarksGroups() {
#getEarmarksGroups(annotations: ListItem<AnnotationWrapper>[]) {
const earmarksGroups: EarmarkGroup[] = [];
if (!this.annotations?.length) {
if (!annotations.length) {
return earmarksGroups;
}
let lastGroup: EarmarkGroup;
for (let idx = 0; idx < this.annotations.length; ++idx) {
if (idx === 0 || this.annotations[idx].item.color !== this.annotations[idx - 1].item.color) {
for (let idx = 0; idx < annotations.length; ++idx) {
if (idx === 0 || annotations[idx].item.color !== annotations[idx - 1].item.color) {
if (lastGroup) {
earmarksGroups.push(lastGroup);
}
lastGroup = { startIdx: idx, length: 1, color: this.annotations[idx].item.color };
lastGroup = { startIdx: idx, length: 1, color: annotations[idx].item.color };
} else {
lastGroup.length += 1;
}

View File

@ -1,5 +1,5 @@
import { ActivatedRouteSnapshot, NavigationExtras, Router, RouterLink } from '@angular/router';
import { ChangeDetectorRef, Component, effect, NgZone, OnDestroy, OnInit, TemplateRef, ViewChild } from '@angular/core';
import { ChangeDetectorRef, Component, effect, NgZone, OnDestroy, OnInit, TemplateRef, untracked, ViewChild } from '@angular/core';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import {
@ -100,13 +100,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
readonly roles = Roles;
readonly fileId = this.state.fileId;
readonly dossierId = this.state.dossierId;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
@ViewChild('annotationFilterTemplate', {
read: TemplateRef,
static: false,
})
private readonly _filterTemplate: TemplateRef<unknown>;
#loadAllAnnotationsEnabled = false;
protected readonly isDocumine = getConfig().IS_DOCUMINE;
constructor(
readonly pdf: PdfViewer,
@ -179,7 +179,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
effect(() => {
this._viewModeService.viewMode();
this.updateViewMode().then();
this.#updateViewMode().then();
});
effect(() => {
@ -207,7 +207,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const earmarks$ = isEarmarksViewMode$.pipe(
tap(() => this._loadingService.start()),
switchMap(() => this._fileDataService.loadEarmarks()),
tap(() => this.updateViewMode().then(() => this._loadingService.stop())),
tap(() => this.#updateViewMode().then(() => this._loadingService.stop())),
);
const currentPageIfEarmarksView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe(
@ -233,7 +233,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return isChangingFromEarmarksViewMode$.pipe(
map(() => this._fileDataService.earmarks().get(this.pdf.currentPage()) ?? []),
map(earmarks => this.deleteAnnotations(earmarks, [])),
map(earmarks => this.#deleteAnnotations(earmarks, [])),
);
}
@ -242,61 +242,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
}
async updateViewMode(): Promise<void> {
this._logger.info(`[PDF] Update ${this._viewModeService.viewMode()} view mode`);
const annotations = this._annotationManager.get(a => bool(a.getCustomData('redact-manager')));
const redactions = annotations.filter(a => bool(a.getCustomData('redaction')));
switch (this._viewModeService.viewMode()) {
case ViewModes.STANDARD: {
const wrappers = this._fileDataService.annotations();
const ocrAnnotationIds = wrappers.filter(a => a.isOCR).map(a => a.id);
const standardEntries = annotations
.filter(a => !bool(a.getCustomData('changeLogRemoved')) && !this._annotationManager.isHidden(a.Id))
.filter(a => !ocrAnnotationIds.includes(a.Id));
const nonStandardEntries = annotations.filter(
a =>
bool(a.getCustomData('changeLogRemoved')) ||
this._annotationManager.isHidden(a.Id) ||
(this._skippedService.hideSkipped() && bool(a.getCustomData('skipped'))),
);
this._readableRedactionsService.setAnnotationsColor(standardEntries, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(standardEntries, true);
this._annotationManager.show(standardEntries);
this._annotationManager.hide(nonStandardEntries);
break;
}
case ViewModes.DELTA: {
const changeLogEntries = annotations.filter(a => bool(a.getCustomData('changeLog')));
const nonChangeLogEntries = annotations.filter(a => !bool(a.getCustomData('changeLog')));
this._readableRedactionsService.setAnnotationsColor(redactions, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(changeLogEntries, true);
this._annotationManager.show(changeLogEntries);
this._annotationManager.hide(nonChangeLogEntries);
break;
}
case ViewModes.REDACTED: {
const nonRedactionEntries = annotations.filter(
a => !bool(a.getCustomData('redaction')) || bool(a.getCustomData('changeLogRemoved')),
);
this._readableRedactionsService.setAnnotationsColor(redactions, 'redactionColor');
this._readableRedactionsService.setAnnotationsOpacity(redactions);
this._annotationManager.show(redactions);
this._annotationManager.hide(nonRedactionEntries);
break;
}
case ViewModes.TEXT_HIGHLIGHTS: {
this._annotationManager.hide(annotations);
}
}
this._logger.info('[PDF] Rebuild filters');
this.#rebuildFilters();
this._logger.info('[PDF] Update done');
}
ngOnDetach() {
this._viewerHeaderService.resetCompareButtons();
this._viewerHeaderService.enableLoadAllAnnotations(); // Reset the button state (since the viewer is reused between files)
@ -413,7 +358,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
const annotations$ = this._fileDataService.annotations$.pipe(
startWith([] as AnnotationWrapper[]),
pairwise(),
tap(annotations => this.deleteAnnotations(...annotations)),
tap(annotations => this.#deleteAnnotations(...annotations)),
tap(() => this.#updateFiltersAfterAnnotationsChanged()),
tap(() => this.#updateViewMode()),
);
const currentPageIfNotHighlightsView$ = combineLatest([this.pdf.currentPage$, this._viewModeService.viewMode$]).pipe(
@ -432,12 +379,72 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return combineLatest([currentPageAnnotations$, this._documentViewer.loaded$]).pipe(
filter(([, loaded]) => loaded),
map(([annotations]) => annotations),
tap(([oldA, newA]) => this.drawChangedAnnotations(oldA, newA)?.then(() => this.updateViewMode())),
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)),
switchMap(async ([oldAnnotations, newAnnotations]) => {
await this.#drawChangedAnnotations(oldAnnotations, newAnnotations);
return newAnnotations;
}),
tap(newAnnotations => this.#highlightSelectedAnnotations(newAnnotations)),
);
}
deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
async #updateViewMode(): Promise<void> {
const viewMode = untracked(this._viewModeService.viewMode);
this._logger.info(`[PDF] Update ${viewMode} view mode`);
const annotations = this._annotationManager.get(a => bool(a.getCustomData('redact-manager')));
const redactions = annotations.filter(a => bool(a.getCustomData('redaction')));
switch (viewMode) {
case ViewModes.STANDARD: {
const wrappers = this._fileDataService.annotations();
// TODO: const wrappers = untracked(this._fileDataService.annotations);
const ocrAnnotationIds = wrappers.filter(a => a.isOCR).map(a => a.id);
const standardEntries = annotations
.filter(a => !bool(a.getCustomData('changeLogRemoved')) && !this._annotationManager.isHidden(a.Id))
.filter(a => !ocrAnnotationIds.includes(a.Id));
const nonStandardEntries = annotations.filter(
a =>
bool(a.getCustomData('changeLogRemoved')) ||
this._annotationManager.isHidden(a.Id) ||
(untracked(this._skippedService.hideSkipped) && bool(a.getCustomData('skipped'))),
);
this._readableRedactionsService.setAnnotationsColor(standardEntries, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(standardEntries, true);
this._annotationManager.show(standardEntries);
this._annotationManager.hide(nonStandardEntries);
break;
}
case ViewModes.DELTA: {
const changeLogEntries = annotations.filter(a => bool(a.getCustomData('changeLog')));
const nonChangeLogEntries = annotations.filter(a => !bool(a.getCustomData('changeLog')));
this._readableRedactionsService.setAnnotationsColor(redactions, 'annotationColor');
this._readableRedactionsService.setAnnotationsOpacity(changeLogEntries, true);
this._annotationManager.show(changeLogEntries);
this._annotationManager.hide(nonChangeLogEntries);
break;
}
case ViewModes.REDACTED: {
const nonRedactionEntries = annotations.filter(
a => !bool(a.getCustomData('redaction')) || bool(a.getCustomData('changeLogRemoved')),
);
this._readableRedactionsService.setAnnotationsColor(redactions, 'redactionColor');
this._readableRedactionsService.setAnnotationsOpacity(redactions);
this._annotationManager.show(redactions);
this._annotationManager.hide(nonRedactionEntries);
break;
}
case ViewModes.TEXT_HIGHLIGHTS: {
this._annotationManager.hide(annotations);
}
}
this._logger.info('[PDF] Rebuild filters');
this.#rebuildFilters();
this._logger.info('[PDF] Update done');
}
#deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
const annotationsToDelete = oldAnnotations.filter(oldAnnotation => {
const newAnnotation = newAnnotations.find(byId(oldAnnotation.id));
return newAnnotation ? hasChanges(oldAnnotation, newAnnotation) : true;
@ -447,11 +454,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._annotationManager.delete(annotationsToDelete);
}
drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
async #drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]): Promise<void> {
const annotationsToDraw = this.#getAnnotationsToDraw(oldAnnotations, newAnnotations);
this._logger.info('[ANNOTATIONS] To draw: ', annotationsToDraw);
this._annotationManager.delete(annotationsToDraw);
return this.#cleanupAndRedrawAnnotations(annotationsToDraw);
await this.#cleanupAndRedrawAnnotations(annotationsToDraw);
}
async #openRedactTextDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
@ -642,7 +649,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
filter(event => event.type === ViewerEvents.LOAD_ALL_ANNOTATIONS),
switchMap(() => {
// TODO: this switchMap is ugly, to be refactored
const annotations = this._fileDataService.annotations();
const annotations = untracked(this._fileDataService.annotations);
const showWarning = !this.userPreferenceService.getBool(PreferencesKeys.loadAllAnnotationsWarning);
const annotationsExceedThreshold = annotations.length >= this.configService.values.ANNOTATIONS_THRESHOLD;
@ -655,7 +662,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
filter(([confirmed]) => confirmed),
map(([, annotations]) => {
this.#loadAllAnnotationsEnabled = true;
this.drawChangedAnnotations([], annotations).then(() => {
this.#drawChangedAnnotations([], annotations).then(() => {
this._toaster.success(_('load-all-annotations-success'));
this._viewerHeaderService.disableLoadAllAnnotations();
});
@ -663,7 +670,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
)
.subscribe();
this.addActiveScreenSubscription = this._readableRedactionsService.active$.pipe(map(() => this.updateViewMode())).subscribe();
this.addActiveScreenSubscription = this._readableRedactionsService.active$
.pipe(switchMap(() => this.#updateViewMode()))
.subscribe();
this.addActiveScreenSubscription = combineLatest([this._viewModeService.viewMode$, this.state.file$, this._documentViewer.loaded$])
.pipe(
@ -702,11 +711,15 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._errorService.set(error);
}
#cleanupAndRedrawAnnotations(newAnnotations: List<AnnotationWrapper>) {
async #cleanupAndRedrawAnnotations(newAnnotations: List<AnnotationWrapper>): Promise<void> {
if (!newAnnotations.length) {
return undefined;
}
await this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId);
}
#updateFiltersAfterAnnotationsChanged(): void {
const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || [];
this.#rebuildFilters();
@ -715,8 +728,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this.#handleDeltaAnnotationFilters(currentFilters);
}, 100);
}
return this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId);
}
#handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {

View File

@ -1,5 +1,4 @@
import { computed, Injectable, Signal, signal } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FileDataService } from './file-data.service';
@ -7,7 +6,6 @@ import { FileDataService } from './file-data.service';
export class AnnotationReferencesService {
readonly references: Signal<AnnotationWrapper[]>;
readonly annotation: Signal<AnnotationWrapper | undefined>;
private readonly _annotation$ = new BehaviorSubject<AnnotationWrapper | undefined>(undefined);
readonly #annotation = signal<AnnotationWrapper | undefined>(undefined);
constructor(private readonly _fileDataService: FileDataService) {

View File

@ -1,4 +1,4 @@
import { computed, effect, inject, Injectable, NgZone } from '@angular/core';
import { computed, effect, inject, Injectable, NgZone, untracked } from '@angular/core';
import { getConfig, IqserPermissionsService } from '@iqser/common-ui';
import { getCurrentUser } from '@iqser/common-ui/lib/users';
import { isJustOne, shareDistinctLast, UI_ROOT_PATH_FN } from '@iqser/common-ui/lib/utils';
@ -49,7 +49,7 @@ export class PdfProxyService {
readonly redactTextRequested$ = new Subject<ManualRedactionEntryWrapper>();
readonly currentUser = getCurrentUser<User>();
readonly pageChanged$ = this._pdf.pageChanged$.pipe(
tap(() => this.#handleExcludedPageActions()),
tap(pageNumber => this.#handleExcludedPageActions(pageNumber)),
tap(() => {
if (this._multiSelectService.inactive()) {
this._annotationManager.deselect();
@ -98,7 +98,7 @@ export class PdfProxyService {
effect(
() => {
const canPerformActions = this.canPerformActions();
this._pdf.isCompareMode();
const isCompareMode = this._pdf.isCompareMode();
this.#configureTextPopup();
@ -109,7 +109,8 @@ export class PdfProxyService {
this.#deactivateMultiSelect();
}
this.#handleExcludedPageActions();
const currentPage = untracked(this._pdf.currentPage);
this.#handleExcludedPageActions(currentPage);
},
{ allowSignalWrites: true },
);
@ -227,8 +228,9 @@ export class PdfProxyService {
this.redactTextRequested$.next({ manualRedactionEntry, type });
}
#handleExcludedPageActions() {
const isCurrentPageExcluded = this._state.file().isPageExcluded(this._pdf.currentPage());
#handleExcludedPageActions(currentPage: number) {
const isCurrentPageExcluded = this._state.file().isPageExcluded(currentPage);
if (!isCurrentPageExcluded) {
return;
}

View File

@ -2,7 +2,7 @@ import { Injectable } from '@angular/core';
import { List } from '@iqser/common-ui/lib/utils';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core } from '@pdftron/webviewer';
import { IRectangle, ISectionRectangle, IPoint, SuperTypes } from '@red/domain';
import { IPoint, IRectangle, ISectionRectangle, SuperTypes } from '@red/domain';
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
import { hexToRgb } from '@utils/functions';
import { BoundingBox, Table } from '../../file-preview/services/tables.service';

View File

@ -19,14 +19,14 @@ const MOVE_OPTION = 'move';
@Injectable()
export class REDAnnotationManager {
resizingAnnotationId?: string = undefined;
annotationHasBeenResized?: boolean = false;
readonly #hidden = signal(new Set<string>());
readonly hidden = this.#hidden.asReadonly();
#manager: AnnotationManager;
readonly #logger = inject(NGXLogger);
readonly #annotationSelected$ = new Subject<[Annotation[], string]>();
readonly annotationSelected$ = this.#annotationSelected$.asObservable();
resizingAnnotationId?: string = undefined;
annotationHasBeenResized?: boolean = false;
readonly hidden = this.#hidden.asReadonly();
get selected() {
return this.#manager.getSelectedAnnotations();
@ -128,21 +128,15 @@ export class REDAnnotationManager {
annotations.forEach(annotation => this.#manager.redrawAnnotation(annotation));
}
add(annotations: Annotation | Annotation[]) {
async add(annotations: Annotation | Annotation[]): Promise<void> {
try {
annotations = asList(annotations);
this.#manager.addAnnotations(annotations, { imported: true });
return this.#manager.drawAnnotationsFromList(annotations);
} catch (e) {
console.log(e);
}
}
showHidden() {
const hidden = this.#getByIds([...this.hidden().values()]);
this.show(hidden);
}
#listenForAnnotationSelected() {
this.#manager.addEventListener('annotationSelected', async (annotations: Annotation[], action: string) => {
this.#logger.info('[PDF] Annotation selected: ', annotations, action);

View File

@ -1,6 +1,6 @@
import { inject, Injectable } from '@angular/core';
import { GenericService, isIqserDevMode, Toaster } from '@iqser/common-ui';
import { EntryStates, IEntityLog, IEntityLogEntry, ISectionGrid } from '@red/domain';
import { GenericService, Toaster } from '@iqser/common-ui';
import { EntryStates, IEntityLog, IEntityLogEntry } from '@red/domain';
import { firstValueFrom, of } from 'rxjs';
import { catchError } from 'rxjs/operators';
@ -20,10 +20,6 @@ export class EntityLogService extends GenericService<unknown> {
return entityLog;
}
getSectionGrid(dossierId: string, fileId: string) {
return this._getOne<ISectionGrid>([dossierId, fileId], 'sectionGrid');
}
#filterInvalidEntries(entityLogEntry: IEntityLogEntry[]) {
return entityLogEntry.filter(entry => {
entry.positions = entry.positions?.filter(p => !!p.rectangle?.length);