RED-6144: some refactorings

This commit is contained in:
Dan Percic 2023-02-11 23:34:18 +02:00
parent f9db9885da
commit fa0ae7e333
25 changed files with 100 additions and 177 deletions

View File

@ -1,7 +1,7 @@
<div class="details"> <div class="details">
<redaction-annotation-icon <redaction-annotation-icon
[color]="annotation.color" [color]="annotation.color"
[label]="annotation.isEarmark ? '' : (annotationTypesTranslations[annotation.superType] | translate)[0].toUpperCase()" [label]="annotation.isEarmark ? '' : ((annotationTypesTranslations[annotation.superType] | translate)[0] | uppercase)"
[type]="annotation.iconShape" [type]="annotation.iconShape"
class="mt-6 mr-10" class="mt-6 mr-10"
></redaction-annotation-icon> ></redaction-annotation-icon>

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
import { annotationTypesTranslations } from '@translations/annotation-types-translations'; import { annotationTypesTranslations } from '@translations/annotation-types-translations';
@ -8,7 +8,6 @@ import { ROLES } from '@users/roles';
selector: 'redaction-annotation-card', selector: 'redaction-annotation-card',
templateUrl: './annotation-card.component.html', templateUrl: './annotation-card.component.html',
styleUrls: ['./annotation-card.component.scss'], styleUrls: ['./annotation-card.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AnnotationCardComponent { export class AnnotationCardComponent {
readonly roles = ROLES; readonly roles = ROLES;

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnChanges, OnDestroy } from '@angular/core'; import { ChangeDetectorRef, Component, HostBinding, Input, OnChanges, OnDestroy } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { BehaviorSubject, filter, Subscription } from 'rxjs'; import { BehaviorSubject, filter, Subscription } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators'; import { switchMap, tap } from 'rxjs/operators';
@ -7,7 +7,6 @@ import { AnnotationsListingService } from '../../services/annotations-listing.se
@Component({ @Component({
selector: 'redaction-annotation-reference [annotation]', selector: 'redaction-annotation-reference [annotation]',
templateUrl: './annotation-reference.component.html', templateUrl: './annotation-reference.component.html',
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AnnotationReferenceComponent implements OnChanges, OnDestroy { export class AnnotationReferenceComponent implements OnChanges, OnDestroy {
@Input() annotation: AnnotationWrapper; @Input() annotation: AnnotationWrapper;
@ -16,16 +15,15 @@ export class AnnotationReferenceComponent implements OnChanges, OnDestroy {
readonly #subscription: Subscription; readonly #subscription: Subscription;
constructor(private readonly _listingService: AnnotationsListingService, private readonly _changeRef: ChangeDetectorRef) { constructor(private readonly _listingService: AnnotationsListingService, private readonly _changeRef: ChangeDetectorRef) {
this.#subscription = this.#annotationChanged$ const annotationsChanged$ = this.#annotationChanged$.pipe(
.pipe( filter(annotation => !!annotation),
filter(annotation => !!annotation), switchMap(annotation => this._listingService.isSelected$(annotation)),
switchMap(annotation => this._listingService.isSelected$(annotation)), tap((isSelected: boolean) => {
tap((isSelected: boolean) => { this.isSelected = isSelected;
this.isSelected = isSelected; this._changeRef.markForCheck();
this._changeRef.markForCheck(); }),
}), );
) this.#subscription = annotationsChanged$.subscribe();
.subscribe();
} }
ngOnChanges(): void { ngOnChanges(): void {

View File

@ -2,10 +2,11 @@
<div class="dialog references-dialog"> <div class="dialog references-dialog">
<div class="references-header flex"> <div class="references-header flex">
<div class="small-label uppercase"> <div class="small-label uppercase">
{{ 'references' | translate: { count: references.length } }} {{ 'references' | translate : { count: references.length } }}
</div> </div>
<iqser-circle-button (action)="annotationReferencesService.hide()" icon="iqser:close"></iqser-circle-button> <iqser-circle-button (action)="annotationReferencesService.hide()" icon="iqser:close"></iqser-circle-button>
</div> </div>
<div *ngIf="annotationReferencesService.annotation$ | async as annotation" class="annotations-container flex"> <div *ngIf="annotationReferencesService.annotation$ | async as annotation" class="annotations-container flex">
<div [class.active]="isSelected$ | async" class="annotation-container"> <div [class.active]="isSelected$ | async" class="annotation-container">
<div class="annotation-card-container flex"> <div class="annotation-card-container flex">

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; import { Component, EventEmitter, Output } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { AnnotationReferencesService } from '../../services/annotation-references.service'; import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { Observable, switchMap } from 'rxjs'; import { Observable, switchMap } from 'rxjs';
@ -9,7 +9,6 @@ import { filter } from 'rxjs/operators';
selector: 'redaction-annotation-references-list', selector: 'redaction-annotation-references-list',
templateUrl: './annotation-references-list.component.html', templateUrl: './annotation-references-list.component.html',
styleUrls: ['./annotation-references-list.component.scss'], styleUrls: ['./annotation-references-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AnnotationReferencesListComponent { export class AnnotationReferencesListComponent {
@Output() readonly referenceClicked = new EventEmitter<AnnotationWrapper>(); @Output() readonly referenceClicked = new EventEmitter<AnnotationWrapper>();

View File

@ -1,10 +1,9 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
@Component({ @Component({
selector: 'redaction-annotation-references-page-indicator', selector: 'redaction-annotation-references-page-indicator',
templateUrl: './annotation-references-page-indicator.component.html', templateUrl: './annotation-references-page-indicator.component.html',
styleUrls: ['./annotation-references-page-indicator.component.scss'], styleUrls: ['./annotation-references-page-indicator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AnnotationReferencesPageIndicatorComponent { export class AnnotationReferencesPageIndicatorComponent {
@Input() number: number; @Input() number: number;

View File

@ -20,10 +20,13 @@
</div> </div>
<div *ngIf="multiSelectService.inactive$ | async" class="actions"> <div *ngIf="multiSelectService.inactive$ | async" class="actions">
<ng-container <redaction-annotation-actions
[ngTemplateOutletContext]="{ annotation: annotation }" [annotations]="[annotation]"
[ngTemplateOutlet]="annotationActionsTemplate" [canPerformAnnotationActions]="pdfProxyService.canPerformAnnotationActions$ | async"
></ng-container> [iqserHelpMode]="getActionsHelpModeKey(annotation)"
[overlappingElements]="['USER_MENU', 'WORKLOAD_FILTER', 'DOCUMENT_INFO']"
[scrollableParentView]="scrollableParentView"
></redaction-annotation-actions>
</div> </div>
</div> </div>

View File

@ -1,46 +1,50 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnChanges, TemplateRef } from '@angular/core'; import { Component, HostBinding, Input, OnChanges } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs'; import { BehaviorSubject, Observable } from 'rxjs';
import { switchMap, tap } from 'rxjs/operators'; import { distinctUntilChanged, switchMap, tap } from 'rxjs/operators';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
import { AnnotationsListingService } from '../../services/annotations-listing.service'; import { AnnotationsListingService } from '../../services/annotations-listing.service';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { ScrollableParentViews } from '@iqser/common-ui';
import { ActionsHelpModeKeys } from '../../utils/constants';
@Component({ @Component({
selector: 'redaction-annotation-wrapper [annotation] [annotationActionsTemplate]', selector: 'redaction-annotation-wrapper [annotation]',
templateUrl: './annotation-wrapper.component.html', templateUrl: './annotation-wrapper.component.html',
styleUrls: ['./annotation-wrapper.component.scss'], styleUrls: ['./annotation-wrapper.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AnnotationWrapperComponent implements OnChanges { export class AnnotationWrapperComponent implements OnChanges {
@Input() annotation!: AnnotationWrapper; @Input() annotation!: AnnotationWrapper;
@Input() annotationActionsTemplate: TemplateRef<unknown>;
readonly isSelected$!: Observable<boolean>; readonly isSelected$!: Observable<boolean>;
@HostBinding('attr.annotation-id') annotationId: string; @HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('attr.annotation-page') annotationPage: number; @HostBinding('class.active') active = false;
@HostBinding('class.active') active: boolean; readonly scrollableParentView = ScrollableParentViews.ANNOTATIONS_LIST;
private readonly _annotationChanged$ = new BehaviorSubject<AnnotationWrapper>(undefined); readonly #annotationChanged$ = new BehaviorSubject<AnnotationWrapper>(undefined);
constructor( constructor(
private readonly _changeRef: ChangeDetectorRef,
readonly listingService: AnnotationsListingService, readonly listingService: AnnotationsListingService,
readonly multiSelectService: MultiSelectService, readonly multiSelectService: MultiSelectService,
readonly pdfProxyService: PdfProxyService,
) { ) {
this.isSelected$ = this._annotationChanged$.pipe( this.isSelected$ = this.#annotationChanged$.pipe(
tap(annotation => {
this.annotationId = annotation.id;
this.annotationPage = annotation.pageNumber;
this._changeRef.markForCheck();
}),
switchMap(entity => this.listingService.isSelected$(entity)), switchMap(entity => this.listingService.isSelected$(entity)),
tap(isSelected => { distinctUntilChanged(),
this.active = isSelected; tap(isSelected => (this.active = isSelected)),
this._changeRef.markForCheck();
}),
); );
} }
ngOnChanges(): void { ngOnChanges() {
this._annotationChanged$.next(this.annotation); this.#annotationChanged$.next(this.annotation);
this.annotationId = this.annotation.id;
}
getActionsHelpModeKey(annotation: AnnotationWrapper): string {
const type = annotation?.typeLabel?.split('.')[1];
const typeValue = annotation?.typeValue;
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
return ActionsHelpModeKeys[`${type}-${typeValue}`];
}
return ActionsHelpModeKeys[type];
} }
} }

View File

@ -3,13 +3,10 @@
<redaction-highlights-separator [annotation]="annotation" [highlightGroup]="highlightGroup"></redaction-highlights-separator> <redaction-highlights-separator [annotation]="annotation" [highlightGroup]="highlightGroup"></redaction-highlights-separator>
</div> </div>
<redaction-annotation-wrapper <redaction-annotation-wrapper (click)="annotationClicked(annotation, $event)" [annotation]="annotation"></redaction-annotation-wrapper>
(click)="annotationClicked(annotation, $event)"
[annotationActionsTemplate]="annotationActionsTemplate"
[annotation]="annotation"
></redaction-annotation-wrapper>
</ng-container> </ng-container>
<ng-container *ngIf="annotationReferencesService.annotation$ | async"> <redaction-annotation-references-list
<redaction-annotation-references-list (referenceClicked)="referenceClicked($event)"></redaction-annotation-references-list> (referenceClicked)="referenceClicked($event)"
</ng-container> *ngIf="annotationReferencesService.annotation$ | async"
></redaction-annotation-references-list>

View File

@ -1,14 +1,4 @@
import { import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnChanges, Output } from '@angular/core';
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
EventEmitter,
Input,
OnChanges,
Output,
TemplateRef,
} from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FilterService, HasScrollbarDirective, IqserEventTarget } from '@iqser/common-ui'; import { FilterService, HasScrollbarDirective, IqserEventTarget } from '@iqser/common-ui';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
@ -24,11 +14,9 @@ import { AnnotationsListingService } from '../../services/annotations-listing.se
selector: 'redaction-annotations-list', selector: 'redaction-annotations-list',
templateUrl: './annotations-list.component.html', templateUrl: './annotations-list.component.html',
styleUrls: ['./annotations-list.component.scss'], styleUrls: ['./annotations-list.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class AnnotationsListComponent extends HasScrollbarDirective implements OnChanges { export class AnnotationsListComponent extends HasScrollbarDirective implements OnChanges {
@Input() annotations: AnnotationWrapper[]; @Input() annotations: AnnotationWrapper[];
@Input() annotationActionsTemplate: TemplateRef<unknown>;
@Output() readonly pagesPanelActive = new EventEmitter<boolean>(); @Output() readonly pagesPanelActive = new EventEmitter<boolean>();

View File

@ -1,9 +1,9 @@
<ng-container *ngIf="componentContext$ | async as ctx"> <ng-container *ngIf="componentContext$ | async as ctx">
<div *ngFor="let comment of annotation.comments; trackBy: trackBy" class="comment"> <div *ngFor="let comment of annotation.comments; trackBy: trackBy" class="comment">
<div class="comment-details-wrapper"> <div class="comment-details-wrapper">
<div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date: 'exactDate'" class="small-label"> <div [matTooltipPosition]="'above'" [matTooltip]="comment.date | date : 'exactDate'" class="small-label">
<strong> {{ comment.user | name }} </strong> <strong> {{ comment.user | name }} </strong>
{{ comment.date | date: 'sophisticatedDate' }} {{ comment.date | date : 'sophisticatedDate' }}
</div> </div>
<div class="comment-actions"> <div class="comment-actions">

View File

@ -1,10 +1,9 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, HostBinding, Input, OnInit, ViewChild } from '@angular/core'; import { ChangeDetectorRef, Component, HostBinding, Input, OnInit, ViewChild } from '@angular/core';
import { Dossier, File, IComment } from '@red/domain'; import type { Dossier, File, IComment, User } from '@red/domain';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { UserService } from '@users/user.service';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { InputWithActionComponent, LoadingService, trackByFactory, ContextComponent } from '@iqser/common-ui'; import { ContextComponent, getCurrentUser, InputWithActionComponent, LoadingService, trackByFactory } from '@iqser/common-ui';
import { firstValueFrom, Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { CommentingService } from '../../services/commenting.service'; import { CommentingService } from '../../services/commenting.service';
import { tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service';
@ -20,20 +19,17 @@ interface CommentsContext {
selector: 'redaction-comments', selector: 'redaction-comments',
templateUrl: './comments.component.html', templateUrl: './comments.component.html',
styleUrls: ['./comments.component.scss'], styleUrls: ['./comments.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class CommentsComponent extends ContextComponent<CommentsContext> implements OnInit { export class CommentsComponent extends ContextComponent<CommentsContext> implements OnInit {
@Input() annotation: AnnotationWrapper; @Input() annotation: AnnotationWrapper;
readonly trackBy = trackByFactory(); readonly trackBy = trackByFactory();
readonly file$: Observable<File>; readonly currentUser = getCurrentUser<User>();
readonly dossier$: Observable<Dossier>;
hiddenComments$: Observable<boolean>; hiddenComments$: Observable<boolean>;
@HostBinding('class.hidden') _hidden = true; @HostBinding('class.hidden') _hidden = true;
@ViewChild(InputWithActionComponent) private readonly _input: InputWithActionComponent; @ViewChild(InputWithActionComponent) private readonly _input: InputWithActionComponent;
constructor( constructor(
readonly permissionsService: PermissionsService, readonly permissionsService: PermissionsService,
private readonly _userService: UserService,
private readonly _manualRedactionService: ManualRedactionService, private readonly _manualRedactionService: ManualRedactionService,
private readonly _commentingService: CommentingService, private readonly _commentingService: CommentingService,
private readonly _loadingService: LoadingService, private readonly _loadingService: LoadingService,
@ -41,8 +37,6 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
private readonly _stateService: FilePreviewStateService, private readonly _stateService: FilePreviewStateService,
) { ) {
super(); super();
this.file$ = _stateService.file$;
this.dossier$ = _stateService.dossier$;
} }
ngOnInit() { ngOnInit() {
@ -53,8 +47,8 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
); );
super._initContext({ super._initContext({
file: this.file$, file: this._stateService.file$,
dossier: this.dossier$, dossier: this._stateService.dossier$,
hiddenComments: this.hiddenComments$, hiddenComments: this.hiddenComments$,
}); });
} }
@ -65,12 +59,12 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
} }
this._loadingService.start(); this._loadingService.start();
const { dossierId, fileId } = this._stateService; const { dossierId, fileId } = this._stateService;
const commentId = await firstValueFrom(this._manualRedactionService.addComment(value, this.annotation.id, dossierId, fileId)); const commentId = await this._manualRedactionService.addComment(value, this.annotation.id, dossierId, fileId);
this.annotation.comments.push({ this.annotation.comments.push({
text: value, text: value,
id: commentId, id: commentId,
annotationId: this.annotation.id, annotationId: this.annotation.id,
user: this._userService.currentUser.id, user: this.currentUser.id,
}); });
this._input.reset(); this._input.reset();
this._changeRef.markForCheck(); this._changeRef.markForCheck();
@ -86,7 +80,7 @@ export class CommentsComponent extends ContextComponent<CommentsContext> impleme
$event.stopPropagation(); $event.stopPropagation();
this._loadingService.start(); this._loadingService.start();
const { dossierId, fileId } = this._stateService; const { dossierId, fileId } = this._stateService;
await firstValueFrom(this._manualRedactionService.deleteComment(comment.id, this.annotation.id, dossierId, fileId)); await this._manualRedactionService.deleteComment(comment.id, this.annotation.id, dossierId, fileId);
this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1); this.annotation.comments.splice(this.annotation.comments.indexOf(comment), 1);
this._changeRef.markForCheck(); this._changeRef.markForCheck();
this._loadingService.stop(); this._loadingService.stop();

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { Component } from '@angular/core';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service'; import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { DocumentInfoService } from '../../services/document-info.service'; import { DocumentInfoService } from '../../services/document-info.service';
import { combineLatest, Observable, switchMap } from 'rxjs'; import { combineLatest, Observable, switchMap } from 'rxjs';
@ -19,7 +19,6 @@ interface FileAttribute {
selector: 'redaction-document-info', selector: 'redaction-document-info',
templateUrl: './document-info.component.html', templateUrl: './document-info.component.html',
styleUrls: ['./document-info.component.scss'], styleUrls: ['./document-info.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class DocumentInfoComponent { export class DocumentInfoComponent {
readonly fileAttributes$: Observable<FileAttribute[]>; readonly fileAttributes$: Observable<FileAttribute[]>;

View File

@ -203,7 +203,6 @@
<redaction-annotations-list <redaction-annotations-list
(pagesPanelActive)="pagesPanelActive = $event" (pagesPanelActive)="pagesPanelActive = $event"
[annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)" [annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
></redaction-annotations-list> ></redaction-annotations-list>
</div> </div>

View File

@ -1,17 +1,7 @@
import { import { ChangeDetectorRef, Component, ElementRef, HostListener, Input, OnDestroy, ViewChild } from '@angular/core';
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ElementRef,
HostListener,
Input,
OnDestroy,
TemplateRef,
ViewChild,
} from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { AnnotationProcessingService } from '../../services/annotation-processing.service'; import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { MatDialogRef, MatDialogState } from '@angular/material/dialog'; import { MatDialogState } from '@angular/material/dialog';
import scrollIntoView from 'scroll-into-view-if-needed'; import scrollIntoView from 'scroll-into-view-if-needed';
import { import {
AutoUnsubscribe, AutoUnsubscribe,
@ -25,7 +15,7 @@ import {
shareDistinctLast, shareDistinctLast,
shareLast, shareLast,
} from '@iqser/common-ui'; } from '@iqser/common-ui';
import { combineLatest, firstValueFrom, Observable } from 'rxjs'; import { combineLatest, delay, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators'; import { map, tap } from 'rxjs/operators';
import { File } from '@red/domain'; import { File } from '@red/domain';
import { ExcludedPagesService } from '../../services/excluded-pages.service'; import { ExcludedPagesService } from '../../services/excluded-pages.service';
@ -49,7 +39,6 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
selector: 'redaction-file-workload [file]', selector: 'redaction-file-workload [file]',
templateUrl: './file-workload.component.html', templateUrl: './file-workload.component.html',
styleUrls: ['./file-workload.component.scss'], styleUrls: ['./file-workload.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy { export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy {
readonly iconButtonTypes = IconButtonTypes; readonly iconButtonTypes = IconButtonTypes;
@ -57,9 +46,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
displayedAnnotations = new Map<number, AnnotationWrapper[]>(); displayedAnnotations = new Map<number, AnnotationWrapper[]>();
@Input() activeViewerPage: number; @Input() activeViewerPage: number;
@Input() dialogRef: MatDialogRef<unknown>;
@Input() file!: File; @Input() file!: File;
@Input() annotationActionsTemplate: TemplateRef<unknown>;
displayedPages: number[] = []; displayedPages: number[] = [];
pagesPanelActive = true; pagesPanelActive = true;
readonly displayedAnnotations$: Observable<Map<number, AnnotationWrapper[]>>; readonly displayedAnnotations$: Observable<Map<number, AnnotationWrapper[]>>;
@ -166,6 +153,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
const secondary$ = this.filterService.getFilterModels$('secondaryFilters'); const secondary$ = this.filterService.getFilterModels$('secondaryFilters');
return combineLatest([this.fileDataService.all$, primary$, secondary$]).pipe( return combineLatest([this.fileDataService.all$, primary$, secondary$]).pipe(
delay(0),
map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary)), map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary)),
); );
} }
@ -198,7 +186,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
handleKeyEvent($event: KeyboardEvent): void { handleKeyEvent($event: KeyboardEvent): void {
if ( if (
!ALL_HOTKEY_ARRAY.includes($event.key) || !ALL_HOTKEY_ARRAY.includes($event.key) ||
this.dialogRef?.getState() === MatDialogState.OPEN || this.state.dialogRef?.getState() === MatDialogState.OPEN ||
($event.target as IqserEventTarget).localName === 'input' ($event.target as IqserEventTarget).localName === 'input'
) { ) {
return; return;
@ -273,8 +261,8 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
this.pdf.navigateTo(1); this.pdf.navigateTo(1);
} }
scrollQuickNavLast(): Promise<void> { scrollQuickNavLast() {
return firstValueFrom(this.state.file$).then(file => this.pdf.navigateTo(file.numberOfPages)); this.pdf.navigateTo(this.state.file.numberOfPages);
} }
preventKeyDefault($event: KeyboardEvent): void { preventKeyDefault($event: KeyboardEvent): void {

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { CircleButtonTypes } from '@iqser/common-ui'; import { CircleButtonTypes } from '@iqser/common-ui';
import { EarmarkGroup, EarmarkOperation } from '@red/domain'; import { EarmarkGroup, EarmarkOperation } from '@red/domain';
import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service';
@ -12,7 +12,6 @@ import { ROLES } from '@users/roles';
selector: 'redaction-highlights-separator [highlightGroup] [annotation]', selector: 'redaction-highlights-separator [highlightGroup] [annotation]',
templateUrl: './highlights-separator.component.html', templateUrl: './highlights-separator.component.html',
styleUrls: ['./highlights-separator.component.scss'], styleUrls: ['./highlights-separator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class HighlightsSeparatorComponent { export class HighlightsSeparatorComponent {
@Input() highlightGroup: EarmarkGroup; @Input() highlightGroup: EarmarkGroup;

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component, inject, Input } from '@angular/core'; import { Component, inject, Input } from '@angular/core';
import { List } from '@iqser/common-ui'; import { List } from '@iqser/common-ui';
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service'; import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
@ -12,13 +12,11 @@ import { ViewedPage } from '@red/domain';
selector: 'redaction-pages [pages] [activePage] [displayedAnnotations]', selector: 'redaction-pages [pages] [activePage] [displayedAnnotations]',
templateUrl: './pages.component.html', templateUrl: './pages.component.html',
styleUrls: ['./pages.component.scss'], styleUrls: ['./pages.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class PagesComponent { export class PagesComponent {
@Input() pages: List<number>; @Input() pages: List<number>;
@Input() activePage: number; @Input() activePage: number;
@Input() displayedAnnotations: Map<number, AnnotationWrapper[]>; @Input() displayedAnnotations: Map<number, AnnotationWrapper[]>;
readonly #pdf = inject(PdfViewer); readonly #pdf = inject(PdfViewer);
readonly #state = inject(FilePreviewStateService); readonly #state = inject(FilePreviewStateService);
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId); readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);

View File

@ -8,21 +8,5 @@
<redaction-document-info *ngIf="documentInfoService.shown$ | async" id="document-info"></redaction-document-info> <redaction-document-info *ngIf="documentInfoService.shown$ | async" id="document-info"></redaction-document-info>
<redaction-file-workload <redaction-file-workload *ngIf="!file.excluded" [activeViewerPage]="pdf.currentPage$ | async" [file]="file"></redaction-file-workload>
*ngIf="!file.excluded"
[activeViewerPage]="pdf.currentPage$ | async"
[annotationActionsTemplate]="annotationActionsTemplate"
[dialogRef]="state.dialogRef"
[file]="file"
></redaction-file-workload>
<ng-template #annotationActionsTemplate let-annotation="annotation">
<redaction-annotation-actions
[annotations]="[annotation]"
[canPerformAnnotationActions]="pdfProxyService.canPerformAnnotationActions$ | async"
[iqserHelpMode]="getActionsHelpModeKey(annotation)"
[overlappingElements]="['USER_MENU', 'WORKLOAD_FILTER', 'DOCUMENT_INFO']"
[scrollableParentView]="scrollableParentView"
></redaction-annotation-actions>
</ng-template>
</ng-container> </ng-container>

View File

@ -1,38 +1,19 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { Component } from '@angular/core';
import { DocumentInfoService } from '../../services/document-info.service'; import { DocumentInfoService } from '../../services/document-info.service';
import { ExcludedPagesService } from '../../services/excluded-pages.service'; import { ExcludedPagesService } from '../../services/excluded-pages.service';
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service'; import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ActionsHelpModeKeys } from '../../utils/constants';
import { ScrollableParentView, ScrollableParentViews } from '@iqser/common-ui';
@Component({ @Component({
selector: 'redaction-file-preview-right-container', selector: 'redaction-file-preview-right-container',
templateUrl: './file-preview-right-container.component.html', templateUrl: './file-preview-right-container.component.html',
styleUrls: ['./file-preview-right-container.component.scss'], styleUrls: ['./file-preview-right-container.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class FilePreviewRightContainerComponent { export class FilePreviewRightContainerComponent {
constructor( constructor(
readonly pdf: PdfViewer, readonly pdf: PdfViewer,
readonly pdfProxyService: PdfProxyService,
readonly state: FilePreviewStateService, readonly state: FilePreviewStateService,
readonly documentInfoService: DocumentInfoService, readonly documentInfoService: DocumentInfoService,
readonly excludedPagesService: ExcludedPagesService, readonly excludedPagesService: ExcludedPagesService,
) {} ) {}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.ANNOTATIONS_LIST;
}
getActionsHelpModeKey(annotation: AnnotationWrapper): string {
const type = annotation?.typeLabel?.split('.')[1];
const typeValue = annotation?.typeValue;
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
return ActionsHelpModeKeys[`${type}-${typeValue}`];
}
return ActionsHelpModeKeys[type];
}
} }

View File

@ -7,7 +7,7 @@
</div> </div>
<iqser-initials-avatar <iqser-initials-avatar
*ngIf="(editingReviewer$ | async) === false" *ngIf="!editingReviewer"
[id]="'assignee'" [id]="'assignee'"
[tooltipPosition]="'below'" [tooltipPosition]="'below'"
[user]="file.assignee" [user]="file.assignee"
@ -15,24 +15,24 @@
></iqser-initials-avatar> ></iqser-initials-avatar>
<div <div
(click)="editingReviewer$.next(true)" (click)="editingReviewer = true"
*ngIf="(editingReviewer$ | async) === false && (canAssignReviewer$ | async)" *ngIf="!editingReviewer && (canAssignReviewer$ | async)"
[id]="'assign-reviewer'" [id]="'assign-reviewer'"
[translate]="'file-preview.assign-reviewer'" [translate]="'file-preview.assign-reviewer'"
class="assign-reviewer pointer" class="assign-reviewer pointer"
></div> ></div>
<redaction-assign-user-dropdown <redaction-assign-user-dropdown
(cancel)="editingReviewer$.next(false)" (cancel)="editingReviewer = false"
(save)="assignReviewer(file, $event)" (save)="assignReviewer(file, $event)"
*ngIf="editingReviewer$ | async" *ngIf="editingReviewer"
[options]="usersOptions$ | async" [options]="usersOptions$ | async"
[value]="file.assignee" [value]="file.assignee"
></redaction-assign-user-dropdown> ></redaction-assign-user-dropdown>
<div *ngIf="(editingReviewer$ | async) === false && canAssign$ | async" class="assign-actions-wrapper"> <div *ngIf="!editingReviewer && canAssign$ | async" class="assign-actions-wrapper">
<iqser-circle-button <iqser-circle-button
(action)="editingReviewer$.next(true)" (action)="editingReviewer = true"
*ngIf="(canAssignOrUnassign$ | async) && !!file.assignee" *ngIf="(canAssignOrUnassign$ | async) && !!file.assignee"
[buttonId]="'change-assignee'" [buttonId]="'change-assignee'"
[icon]="'iqser:edit'" [icon]="'iqser:edit'"

View File

@ -1,4 +1,4 @@
import { ChangeDetectionStrategy, Component } from '@angular/core'; import { Component } from '@angular/core';
import { Dossier, File, StatusBarConfigs, User } from '@red/domain'; import { Dossier, File, StatusBarConfigs, User } from '@red/domain';
import { List, LoadingService, Toaster } from '@iqser/common-ui'; import { List, LoadingService, Toaster } from '@iqser/common-ui';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
@ -7,7 +7,7 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { UserService } from '@users/user.service'; import { UserService } from '@users/user.service';
import { FilesService } from '@services/files/files.service'; import { FilesService } from '@services/files/files.service';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { BehaviorSubject, combineLatest, combineLatestWith, firstValueFrom, Observable, switchMap } from 'rxjs'; import { combineLatest, combineLatestWith, firstValueFrom, Observable, switchMap } from 'rxjs';
import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { distinctUntilChanged, map } from 'rxjs/operators'; import { distinctUntilChanged, map } from 'rxjs/operators';
import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service';
@ -17,7 +17,6 @@ import { FileAssignService } from '../../../shared-dossiers/services/file-assign
selector: 'redaction-user-management', selector: 'redaction-user-management',
templateUrl: './user-management.component.html', templateUrl: './user-management.component.html',
styleUrls: ['./user-management.component.scss'], styleUrls: ['./user-management.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class UserManagementComponent { export class UserManagementComponent {
readonly translations = workflowFileStatusTranslations; readonly translations = workflowFileStatusTranslations;
@ -25,7 +24,7 @@ export class UserManagementComponent {
readonly assignTooltip$: Observable<string>; readonly assignTooltip$: Observable<string>;
readonly canAssignReviewer$: Observable<boolean>; readonly canAssignReviewer$: Observable<boolean>;
readonly canAssignToSelf$: Observable<boolean>; readonly canAssignToSelf$: Observable<boolean>;
readonly editingReviewer$ = new BehaviorSubject<boolean>(false); editingReviewer = false;
readonly canAssignOrUnassign$: Observable<boolean>; readonly canAssignOrUnassign$: Observable<boolean>;
readonly canAssign$: Observable<boolean>; readonly canAssign$: Observable<boolean>;
readonly usersOptions$: Observable<List>; readonly usersOptions$: Observable<List>;
@ -113,6 +112,6 @@ export class UserManagementComponent {
this.loadingService.stop(); this.loadingService.stop();
this.toaster.success(_('assignment.reviewer'), { params: { reviewerName, filename } }); this.toaster.success(_('assignment.reviewer'), { params: { reviewerName, filename } });
this.editingReviewer$.next(false); this.editingReviewer = false;
} }
} }

View File

@ -1,6 +1,5 @@
import { import {
AfterViewInit, AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef, ChangeDetectorRef,
Component, Component,
ElementRef, ElementRef,
@ -78,7 +77,6 @@ const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
templateUrl: './file-preview-screen.component.html', templateUrl: './file-preview-screen.component.html',
styleUrls: ['./file-preview-screen.component.scss'], styleUrls: ['./file-preview-screen.component.scss'],
providers: filePreviewScreenProviders, providers: filePreviewScreenProviders,
changeDetection: ChangeDetectionStrategy.OnPush,
}) })
export class FilePreviewScreenComponent export class FilePreviewScreenComponent
extends AutoUnsubscribe extends AutoUnsubscribe
@ -300,7 +298,7 @@ export class FilePreviewScreenComponent
await firstValueFrom(reanalyzeFiles); await firstValueFrom(reanalyzeFiles);
} }
this.pdfProxyService.loadViewer(); this.pdfProxyService.configureElements();
} }
ngAfterViewInit() { ngAfterViewInit() {

View File

@ -5,7 +5,6 @@ import {
IRedactionLog, IRedactionLog,
IRedactionLogEntry, IRedactionLogEntry,
LogEntryStatuses, LogEntryStatuses,
ManualRedactionType,
ViewedPage, ViewedPage,
ViewMode, ViewMode,
ViewModes, ViewModes,

View File

@ -12,7 +12,7 @@ import type {
} from '@red/domain'; } from '@red/domain';
import { type AnnotationWrapper } from '@models/file/annotation.wrapper'; import { type AnnotationWrapper } from '@models/file/annotation.wrapper';
import { GenericService, IqserPermissionsService, List, RequiredParam, Toaster, Validate } from '@iqser/common-ui'; import { GenericService, IqserPermissionsService, List, RequiredParam, Toaster, Validate } from '@iqser/common-ui';
import { map, tap } from 'rxjs/operators'; import { tap } from 'rxjs/operators';
import { PermissionsService } from '@services/permissions.service'; import { PermissionsService } from '@services/permissions.service';
import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations'; import { dictionaryActionsTranslations, manualRedactionActionsTranslations } from '@translations/annotation-actions-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
@ -21,7 +21,7 @@ import { ActiveDossiersService } from '@services/dossiers/active-dossiers.servic
import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper'; import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { ROLES } from '@users/roles'; import { ROLES } from '@users/roles';
import { of } from 'rxjs'; import { firstValueFrom, of } from 'rxjs';
function getResponseType(error: boolean, isConflict: boolean) { function getResponseType(error: boolean, isConflict: boolean) {
const isConflictError = isConflict ? 'conflictError' : 'error'; const isConflictError = isConflict ? 'conflictError' : 'error';
@ -54,15 +54,16 @@ export class ManualRedactionService extends GenericService<IManualAddResponse> {
} }
@Validate() @Validate()
addComment(@RequiredParam() comment: string, @RequiredParam() annotationId: string, dossierId: string, fileId: string) { async addComment(@RequiredParam() comment: string, @RequiredParam() annotationId: string, dossierId: string, fileId: string) {
const url = `${this._defaultModelPath}/comment/add/${dossierId}/${fileId}/${annotationId}`; const url = `${this._defaultModelPath}/comment/add/${dossierId}/${fileId}/${annotationId}`;
return this._post<{ commentId: string }>({ text: comment }, url).pipe(map(res => res.commentId)); const request = await firstValueFrom(this._post<{ commentId: string }>({ text: comment }, url));
return request.commentId;
} }
@Validate() @Validate()
deleteComment(@RequiredParam() commentId: string, @RequiredParam() annotationId: string, dossierId: string, fileId: string) { deleteComment(@RequiredParam() commentId: string, @RequiredParam() annotationId: string, dossierId: string, fileId: string) {
const url = `${this._defaultModelPath}/comment/undo/${dossierId}/${fileId}/${annotationId}/${commentId}`; const url = `${this._defaultModelPath}/comment/undo/${dossierId}/${fileId}/${annotationId}/${commentId}`;
return super.delete({}, url); return firstValueFrom(super.delete({}, url));
} }
addRecommendation(annotations: AnnotationWrapper[], dossierId: string, fileId: string, comment = { text: 'Accepted Recommendation' }) { addRecommendation(annotations: AnnotationWrapper[], dossierId: string, fileId: string, comment = { text: 'Accepted Recommendation' }) {

View File

@ -112,8 +112,10 @@ export class PdfProxyService {
this._changeDetectorRef.markForCheck(); this._changeDetectorRef.markForCheck();
} }
loadViewer() { configureElements() {
this._configureElements(); const hexColor = this._dictionariesMapService.get(this._state.dossierTemplateId, 'manual').hexColor;
const color = this._annotationDrawService.convertColor(hexColor);
this._documentViewer.setRectangleToolStyles(color);
} }
#deactivateMultiSelect() { #deactivateMultiSelect() {
@ -185,12 +187,6 @@ export class PdfProxyService {
this._annotationsActionsService.cancelResize(null, wrapper).then(); this._annotationsActionsService.cancelResize(null, wrapper).then();
} }
private _configureElements() {
const hexColor = this._dictionariesMapService.get(this._state.dossierTemplateId, 'manual').hexColor;
const color = this._annotationDrawService.convertColor(hexColor);
this._documentViewer.setRectangleToolStyles(color);
}
#configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) { #configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) {
if (!this.canPerformActions) { if (!this.canPerformActions) {
return this._pdf.resetAnnotationActions(); return this._pdf.resetAnnotationActions();