Pull request #345: Remove file from FileDataModel
Merge in RED/ui from refactor-fp/state to master * commit '6c29461f18fa7d2e63b2abbc536c98b13b2289f7': cleanup fix lost async pipe fix excluded pages remove file from action requests remove file from fileData renaming and load file data flow fix missing fileData fix compile errors WIP Remove some inputs from components
This commit is contained in:
commit
d2e9fa12b3
@ -19,7 +19,6 @@ export class FileDataModel {
|
||||
allAnnotations: AnnotationWrapper[];
|
||||
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||
readonly blob$ = new BehaviorSubject<Blob>(undefined);
|
||||
readonly file$ = new BehaviorSubject<File>(undefined);
|
||||
|
||||
constructor(
|
||||
private readonly _file: File,
|
||||
@ -29,19 +28,10 @@ export class FileDataModel {
|
||||
private _dictionaryData?: { [p: string]: Dictionary },
|
||||
private _areDevFeaturesEnabled?: boolean,
|
||||
) {
|
||||
this.file$.next(_file);
|
||||
this.blob$.next(_blob);
|
||||
this._buildAllAnnotations();
|
||||
}
|
||||
|
||||
get file(): File {
|
||||
return this.file$.value;
|
||||
}
|
||||
|
||||
set file(file: File) {
|
||||
this.file$.next(file);
|
||||
}
|
||||
|
||||
get redactionLog(): IRedactionLog {
|
||||
return this._redactionLog;
|
||||
}
|
||||
|
||||
@ -69,7 +69,7 @@
|
||||
|
||||
<div [class.required]="commentIsMandatory" class="iqser-input-group w-300">
|
||||
<label translate="manual-annotation.dialog.content.comment"></label>
|
||||
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
|
||||
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -80,5 +80,5 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<iqser-circle-button class="dialog-close" icon="iqser:close" (action)="close()"></iqser-circle-button>
|
||||
<iqser-circle-button (action)="close()" class="dialog-close" icon="iqser:close"></iqser-circle-button>
|
||||
</section>
|
||||
|
||||
@ -4,10 +4,9 @@ import { AppStateService } from '@state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
|
||||
import { ManualAnnotationService } from '../../services/manual-annotation.service';
|
||||
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { JustificationsService } from '@services/entity-services/justifications.service';
|
||||
import { Dictionary, Dossier, File, IAddRedactionRequest, IManualAddResponse } from '@red/domain';
|
||||
import { Dictionary, Dossier, IAddRedactionRequest } from '@red/domain';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
@ -20,7 +19,6 @@ export interface LegalBasisOption {
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-manual-annotation-dialog',
|
||||
templateUrl: './manual-annotation-dialog.component.html',
|
||||
styleUrls: ['./manual-annotation-dialog.component.scss'],
|
||||
})
|
||||
@ -44,10 +42,10 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
private readonly _dictionaryService: DictionaryService,
|
||||
protected readonly _injector: Injector,
|
||||
protected readonly _dialogRef: MatDialogRef<ManualAnnotationDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; file: File },
|
||||
@Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; dossierId: string },
|
||||
) {
|
||||
super(_injector, _dialogRef);
|
||||
this._dossier = this._dossiersService.find(this.data.file.dossierId);
|
||||
this._dossier = this._dossiersService.find(this.data.dossierId);
|
||||
this.isDocumentAdmin = this._permissionsService.isApprover(this._dossier);
|
||||
|
||||
this.isFalsePositiveRequest = this.data.manualRedactionEntryWrapper.type === 'FALSE_POSITIVE';
|
||||
@ -73,6 +71,10 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
return this.form.invalid;
|
||||
}
|
||||
|
||||
get commentIsMandatory() {
|
||||
return !this.isDocumentAdmin && !this.isDictionaryRequest;
|
||||
}
|
||||
|
||||
async ngOnInit() {
|
||||
super.ngOnInit();
|
||||
this.possibleDictionaries = await this._appStateService.getDictionariesOptions(
|
||||
@ -91,11 +93,7 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
|
||||
save() {
|
||||
this._enhanceManualRedaction(this.data.manualRedactionEntryWrapper.manualRedactionEntry);
|
||||
this._manualAnnotationService.addAnnotation(this.data.manualRedactionEntryWrapper.manualRedactionEntry, this.data.file).subscribe(
|
||||
(response: IManualAddResponse) =>
|
||||
this._dialogRef.close(new ManualAnnotationResponse(this.data.manualRedactionEntryWrapper, response)),
|
||||
() => this._dialogRef.close(),
|
||||
);
|
||||
this._dialogRef.close(this.data.manualRedactionEntryWrapper);
|
||||
}
|
||||
|
||||
format(value: string) {
|
||||
@ -118,10 +116,6 @@ export class ManualAnnotationDialogComponent extends BaseDialogComponent impleme
|
||||
});
|
||||
}
|
||||
|
||||
get commentIsMandatory() {
|
||||
return !this.isDocumentAdmin && !this.isDictionaryRequest;
|
||||
}
|
||||
|
||||
private _enhanceManualRedaction(addRedactionRequest: IAddRedactionRequest) {
|
||||
const legalOption: LegalBasisOption = this.form.get('reason').value;
|
||||
addRedactionRequest.type = this.form.get('dictionary').value;
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.changeLegalBasis($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.changeLegalBasis($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canChangeLegalBasis"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.edit-reason.label' | translate"
|
||||
@ -52,7 +52,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.acceptSuggestion($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.acceptSuggestion($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canAcceptSuggestion"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.accept-suggestion.label' | translate"
|
||||
@ -61,7 +61,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.undoDirectAction($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.undoDirectAction($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canUndo"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.undo' | translate"
|
||||
@ -70,7 +70,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.rejectSuggestion($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.rejectSuggestion($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canRejectSuggestion"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.reject-suggestion' | translate"
|
||||
@ -79,7 +79,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.recategorizeImages($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.recategorizeImages($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canRecategorizeImage"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.recategorize-image' | translate"
|
||||
@ -97,7 +97,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.forceAnnotation($event, annotations, file, annotationsChanged)"
|
||||
(action)="annotationActionsService.forceAnnotation($event, annotations, annotationsChanged)"
|
||||
*ngIf="annotationPermissions.canForceRedaction"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.force-redaction.label' | translate"
|
||||
@ -106,7 +106,7 @@
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="annotationActionsService.forceAnnotation($event, annotations, file, annotationsChanged, true)"
|
||||
(action)="annotationActionsService.forceAnnotation($event, annotations, annotationsChanged, true)"
|
||||
*ngIf="annotationPermissions.canForceHint"
|
||||
[tooltipPosition]="tooltipPosition"
|
||||
[tooltip]="'annotation-actions.force-hint.label' | translate"
|
||||
|
||||
@ -87,21 +87,15 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
|
||||
removeOrSuggestRemoveAnnotation($event: MouseEvent, removeFromDict: boolean) {
|
||||
$event.stopPropagation();
|
||||
this.annotationActionsService.removeOrSuggestRemoveAnnotation(
|
||||
$event,
|
||||
this.annotations,
|
||||
this.file,
|
||||
removeFromDict,
|
||||
this.annotationsChanged,
|
||||
);
|
||||
this.annotationActionsService.removeOrSuggestRemoveAnnotation($event, this.annotations, removeFromDict, this.annotationsChanged);
|
||||
}
|
||||
|
||||
markAsFalsePositive($event: MouseEvent) {
|
||||
this.annotationActionsService.markAsFalsePositive($event, this.annotations, this.file, this.annotationsChanged);
|
||||
this.annotationActionsService.markAsFalsePositive($event, this.annotations, this.annotationsChanged);
|
||||
}
|
||||
|
||||
acceptRecommendation($event: MouseEvent) {
|
||||
this.annotationActionsService.convertRecommendationToAnnotation($event, this.annotations, this.file, this.annotationsChanged);
|
||||
this.annotationActionsService.convertRecommendationToAnnotation($event, this.annotations, this.annotationsChanged);
|
||||
}
|
||||
|
||||
hideAnnotation($event: MouseEvent) {
|
||||
@ -123,7 +117,7 @@ export class AnnotationActionsComponent implements OnChanges {
|
||||
}
|
||||
|
||||
acceptResize($event: MouseEvent) {
|
||||
this.annotationActionsService.acceptResize($event, this.viewer, this.file, this.annotations[0], this.annotationsChanged);
|
||||
this.annotationActionsService.acceptResize($event, this.viewer, this.annotations[0], this.annotationsChanged);
|
||||
}
|
||||
|
||||
cancelResize($event: MouseEvent) {
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
<div class="content-container">
|
||||
<div *ngIf="references$ | async as references" class="content-container">
|
||||
<div class="dialog references-dialog">
|
||||
<div class="references-header flex">
|
||||
<div class="small-label">
|
||||
{{ annotationReferences.length }}
|
||||
{{ (annotationReferences.length === 1 ? 'references.singular' : 'references.plural') | translate }}
|
||||
{{ references.length }}
|
||||
{{ (references.length === 1 ? 'references.singular' : 'references.plural') | translate }}
|
||||
</div>
|
||||
<iqser-circle-button (action)="annotationReferencesService.hide()" icon="iqser:close"></iqser-circle-button>
|
||||
</div>
|
||||
<div class="annotations-container flex">
|
||||
<div *ngIf="annotationReferencesService.annotation$ | async as annotation" class="annotations-container flex">
|
||||
<div [class.active]="isSelected(annotation.id)" class="annotation-container">
|
||||
<div class="annotation-card-container flex">
|
||||
<redaction-annotation-card [annotation]="annotation" [file]="file"></redaction-annotation-card>
|
||||
@ -18,7 +18,7 @@
|
||||
</div>
|
||||
<div
|
||||
(click)="referenceClicked.emit(reference)"
|
||||
*ngFor="let reference of annotationReferences"
|
||||
*ngFor="let reference of references"
|
||||
[class.active]="isSelected(reference.id)"
|
||||
class="annotation-container"
|
||||
>
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
|
||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||
import { File } from '@red/domain';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotation-references-list',
|
||||
@ -10,18 +12,23 @@ import { FileDataModel } from '@models/file/file-data.model';
|
||||
styleUrls: ['./annotation-references-list.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AnnotationReferencesListComponent implements OnChanges {
|
||||
@Input() annotation: AnnotationWrapper;
|
||||
export class AnnotationReferencesListComponent {
|
||||
@Input() file: File;
|
||||
@Input() fileData: FileDataModel;
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Output() readonly referenceClicked = new EventEmitter<AnnotationWrapper>();
|
||||
annotationReferences: AnnotationWrapper[];
|
||||
references$ = this._annotationReferences;
|
||||
|
||||
constructor(readonly annotationReferencesService: AnnotationReferencesService) {}
|
||||
constructor(
|
||||
readonly annotationReferencesService: AnnotationReferencesService,
|
||||
private readonly _filePreviewStateService: FilePreviewStateService,
|
||||
) {}
|
||||
|
||||
ngOnChanges(): void {
|
||||
this.annotationReferences = this.fileData.allAnnotations.filter(a => this.annotation.reference.includes(a.annotationId));
|
||||
private get _annotationReferences(): Observable<AnnotationWrapper[]> {
|
||||
const combination = combineLatest([this.annotationReferencesService.annotation$, this._filePreviewStateService.fileData$]);
|
||||
return combination.pipe(
|
||||
filter(([annotation]) => !!annotation),
|
||||
map(([{ reference }, fileData]) => fileData.allAnnotations.filter(a => reference.includes(a.annotationId))),
|
||||
);
|
||||
}
|
||||
|
||||
isSelected(annotationId: string): boolean {
|
||||
|
||||
@ -44,11 +44,9 @@
|
||||
<redaction-annotation-details [annotation]="annotation" [isSelected]="isSelected(annotation.id)"></redaction-annotation-details>
|
||||
</div>
|
||||
|
||||
<ng-container *ngIf="annotationReferencesService.annotation$ | async as annotation">
|
||||
<ng-container *ngIf="annotationReferencesService.annotation$ | async">
|
||||
<redaction-annotation-references-list
|
||||
(referenceClicked)="referenceClicked($event)"
|
||||
[annotation]="annotation"
|
||||
[fileData]="fileData"
|
||||
[file]="file"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
></redaction-annotation-references-list>
|
||||
|
||||
@ -4,7 +4,6 @@ import { FilterService, HelpModeService, IqserEventTarget } from '@iqser/common-
|
||||
import { File } from '@red/domain';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { AnnotationReferencesService } from '../../services/annotation-references.service';
|
||||
import { FileDataModel } from '../../../../../../models/file/file-data.model';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-annotations-list',
|
||||
@ -14,7 +13,6 @@ import { FileDataModel } from '../../../../../../models/file/file-data.model';
|
||||
})
|
||||
export class AnnotationsListComponent implements OnChanges {
|
||||
@Input() file: File;
|
||||
@Input() fileData: FileDataModel;
|
||||
@Input() annotations: AnnotationWrapper[];
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Input() annotationActionsTemplate: TemplateRef<unknown>;
|
||||
|
||||
@ -1,54 +1,58 @@
|
||||
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
|
||||
<div>
|
||||
<iqser-circle-button
|
||||
(action)="edit()"
|
||||
*ngIf="permissionsService.canEditFileAttributes(dossier, file)"
|
||||
[tooltip]="'file-preview.tabs.document-info.edit' | translate"
|
||||
icon="iqser:edit"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
<ng-container *ngIf="stateService.file$ | async as file">
|
||||
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
|
||||
<div>
|
||||
<iqser-circle-button
|
||||
(action)="edit(file)"
|
||||
*ngIf="permissionsService.canEditFileAttributes(file)"
|
||||
[tooltip]="'file-preview.tabs.document-info.edit' | translate"
|
||||
icon="iqser:edit"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="documentInfoService.hide()"
|
||||
[tooltip]="'file-preview.tabs.document-info.close' | translate"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-content" iqserHasScrollbar>
|
||||
<div class="section">
|
||||
<div *ngFor="let attr of fileAttributes$ | async" class="attribute">
|
||||
<div class="small-label">{{ attr.label }}:</div>
|
||||
<div>{{ attr.value || '-' }}</div>
|
||||
<iqser-circle-button
|
||||
(action)="documentInfoService.hide()"
|
||||
[tooltip]="'file-preview.tabs.document-info.close' | translate"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:folder"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.dossier' | translate: { dossierName: dossier.dossierName } }}</span>
|
||||
<div class="right-content" iqserHasScrollbar>
|
||||
<div class="section">
|
||||
<div *ngFor="let attr of fileAttributes$ | async" class="attribute">
|
||||
<div class="small-label">{{ attr.label }}:</div>
|
||||
<div>{{ attr.value || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="iqser:document"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate: { pages: file.numberOfPages } }}</span>
|
||||
</div>
|
||||
<div *ngIf="stateService.dossier$ | async as dossier" class="section small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:folder"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.dossier' | translate: { dossierName: dossier.dossierName } }}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.created-on' | translate: { date: file.added | date: 'mediumDate' } }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="iqser:document"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate: { pages: file.numberOfPages } }}</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="dossier.dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.due' | translate: { date: dossier.dueDate | date: 'mediumDate' } }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
<span>{{
|
||||
'file-preview.tabs.document-info.details.created-on' | translate: { date: file.added | date: 'mediumDate' }
|
||||
}}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
{{ dossierTemplateName }}
|
||||
<div *ngIf="dossier.dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.due' | translate: { date: dossier.dueDate | date: 'mediumDate' } }}</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
{{ dossierTemplateName$ | async }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@ -1,44 +1,40 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
|
||||
import { AutoUnsubscribe } from '@iqser/common-ui';
|
||||
import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service';
|
||||
import { DocumentInfoService } from '../../services/document-info.service';
|
||||
import { Observable } from 'rxjs';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, Observable, switchMap } from 'rxjs';
|
||||
import { PermissionsService } from '../../../../../../services/permissions.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { File } from '@red/domain';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-document-info [file] [dossier]',
|
||||
selector: 'redaction-document-info',
|
||||
templateUrl: './document-info.component.html',
|
||||
styleUrls: ['./document-info.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class DocumentInfoComponent extends AutoUnsubscribe implements OnInit {
|
||||
@Input() file: File;
|
||||
@Input() dossier: Dossier;
|
||||
|
||||
fileAttributes$: Observable<{ label: string; value: string }[]>;
|
||||
dossierTemplateName: string;
|
||||
export class DocumentInfoComponent {
|
||||
readonly fileAttributes$: Observable<{ label: string; value: string }[]>;
|
||||
readonly dossierTemplateName$: Observable<string>;
|
||||
|
||||
constructor(
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
private readonly _dialogService: DossiersDialogService,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.dossierTemplateName = this._dossierTemplatesService.find(this.dossier.dossierTemplateId).name;
|
||||
this.fileAttributes$ = this.documentInfoService.fileAttributes$(
|
||||
this.file.fileId,
|
||||
this.file.dossierId,
|
||||
this.dossier.dossierTemplateId,
|
||||
this.fileAttributes$ = combineLatest([this.stateService.file$, this.stateService.dossier$]).pipe(
|
||||
switchMap(([file, dossier]) => this.documentInfoService.fileAttributes$(file.fileId, dossier.id, dossier.dossierTemplateId)),
|
||||
);
|
||||
this.dossierTemplateName$ = this.stateService.dossier$.pipe(
|
||||
switchMap(dossier => this._dossierTemplatesService.getEntityChanged$(dossier.dossierTemplateId)),
|
||||
map(dossierTemplate => dossierTemplate.name),
|
||||
);
|
||||
}
|
||||
|
||||
edit() {
|
||||
this._dialogService.openDialog('documentInfo', null, this.file);
|
||||
edit(file: File) {
|
||||
this._dialogService.openDialog('documentInfo', null, file);
|
||||
}
|
||||
}
|
||||
|
||||
@ -203,7 +203,6 @@
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
|
||||
[canMultiSelect]="!isReadOnly"
|
||||
[fileData]="fileData"
|
||||
[file]="file"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
iqserHelpMode="workload-annotations-list"
|
||||
|
||||
@ -33,7 +33,6 @@ import { ExcludedPagesService } from '../../services/excluded-pages.service';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { DocumentInfoService } from '../../services/document-info.service';
|
||||
import { SkippedService } from '../../services/skipped.service';
|
||||
import { FileDataModel } from '../../../../../../models/file/file-data.model';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
@ -56,7 +55,6 @@ export class FileWorkloadComponent {
|
||||
@Input() file!: File;
|
||||
@Input() annotationActionsTemplate: TemplateRef<unknown>;
|
||||
@Input() viewer: WebViewerInstance;
|
||||
@Input() fileData: FileDataModel;
|
||||
@Output() readonly shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter<boolean>();
|
||||
@Output() readonly selectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
||||
@Output() readonly deselectAnnotations = new EventEmitter<AnnotationWrapper[]>();
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<div class="page">
|
||||
<div #viewer [id]="file.fileId" class="viewer"></div>
|
||||
<div #viewer [id]="(stateService.file$ | async).fileId" class="viewer"></div>
|
||||
</div>
|
||||
|
||||
<input #compareFileInput (change)="uploadFile($event.target['files'])" class="file-upload-input" type="file" />
|
||||
|
||||
@ -37,7 +37,7 @@ import { toPosition } from '../../../../utils/pdf-calculation.utils';
|
||||
import { ViewModeService } from '../../services/view-mode.service';
|
||||
import { MultiSelectService } from '../../services/multi-select.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { filter, switchMap, tap } from 'rxjs/operators';
|
||||
import { filter, switchMap, tap, withLatestFrom } from 'rxjs/operators';
|
||||
import Tools = Core.Tools;
|
||||
import TextTool = Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
@ -64,7 +64,6 @@ const dataElements = {
|
||||
styleUrls: ['./pdf-viewer.component.scss'],
|
||||
})
|
||||
export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnChanges {
|
||||
@Input() file: File;
|
||||
@Input() dossier: Dossier;
|
||||
@Input() canPerformActions = false;
|
||||
@Input() annotations: AnnotationWrapper[];
|
||||
@ -96,7 +95,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
private readonly _annotationActionsService: AnnotationActionsService,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _stateService: FilePreviewStateService,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
readonly viewModeService: ViewModeService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
) {
|
||||
@ -121,24 +120,25 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
this._setReadyAndInitialState = this._setReadyAndInitialState.bind(this);
|
||||
await this._loadViewer();
|
||||
|
||||
this.addActiveScreenSubscription = this._stateService.fileData$
|
||||
this.addActiveScreenSubscription = this.stateService.fileData$
|
||||
.pipe(
|
||||
filter(fileData => !!fileData),
|
||||
switchMap(fileData => fileData.blob$),
|
||||
// Skip document reload if file content hasn't changed
|
||||
shareDistinctLast(),
|
||||
tap(() => this._loadDocument()),
|
||||
withLatestFrom(this.stateService.file$),
|
||||
tap(([blob, file]) => this._loadDocument(blob, file)),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
async ngOnChanges(changes: SimpleChanges): Promise<void> {
|
||||
if (!this.instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (changes.canPerformActions) {
|
||||
this._handleCustomActions();
|
||||
await this._handleCustomActions();
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,18 +158,19 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||
|
||||
const compareDocument = await pdfNet.PDFDoc.createFromBuffer(fileReader.result as ArrayBuffer);
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this._stateService.fileData.blob$.value.arrayBuffer());
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.stateService.fileData.blob$.value.arrayBuffer());
|
||||
|
||||
const loadCompareDocument = async () => {
|
||||
this._loadingService.start();
|
||||
this.utils.ready = false;
|
||||
const mergedDocument = await pdfNet.PDFDoc.create();
|
||||
const file = await this.stateService.file;
|
||||
await loadCompareDocumentWrapper(
|
||||
currentDocument,
|
||||
compareDocument,
|
||||
mergedDocument,
|
||||
this.instance,
|
||||
this.file,
|
||||
file,
|
||||
() => {
|
||||
this.viewModeService.compareMode = true;
|
||||
},
|
||||
@ -211,10 +212,11 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
this.viewModeService.compareMode = false;
|
||||
const pdfNet = this.instance.Core.PDFNet;
|
||||
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this._stateService.fileData.blob$.value.arrayBuffer());
|
||||
this.instance.UI.loadDocument(currentDocument, {
|
||||
filename: this.file ? this.file.filename : 'document.pdf',
|
||||
});
|
||||
const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await this.stateService.fileData.blob$.value.arrayBuffer());
|
||||
|
||||
const filename = (await this.stateService.file).filename ?? 'document.pdf';
|
||||
this.instance.UI.loadDocument(currentDocument, { filename });
|
||||
|
||||
this.instance.UI.disableElements([dataElements.CLOSE_COMPARE_BUTTON]);
|
||||
this.instance.UI.enableElements([dataElements.COMPARE_BUTTON]);
|
||||
this.utils.navigateToPage(1);
|
||||
@ -239,7 +241,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
this._setSelectionMode();
|
||||
this._configureElements();
|
||||
this.utils.disableHotkeys();
|
||||
this._configureTextPopup();
|
||||
await this._configureTextPopup();
|
||||
|
||||
this.annotationManager.addEventListener('annotationSelected', (annotations: Annotation[], action) => {
|
||||
this.annotationSelected.emit(this.annotationManager.getSelectedAnnotations().map(ann => ann.Id));
|
||||
@ -266,7 +268,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
this.utils.deselectAllAnnotations();
|
||||
}
|
||||
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
|
||||
this._handleCustomActions();
|
||||
return this._handleCustomActions();
|
||||
});
|
||||
|
||||
this.documentViewer.addEventListener('documentLoaded', this._setReadyAndInitialState);
|
||||
@ -289,11 +291,12 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
}
|
||||
});
|
||||
|
||||
this.documentViewer.addEventListener('textSelected', (quads, selectedText) => {
|
||||
this.documentViewer.addEventListener('textSelected', async (quads, selectedText) => {
|
||||
this._selectedText = selectedText;
|
||||
const textActions = [dataElements.ADD_DICTIONARY, dataElements.ADD_FALSE_POSITIVE];
|
||||
|
||||
if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded(this.file)) {
|
||||
const file = await this.stateService.file;
|
||||
if (selectedText.length > 2 && this.canPerformActions && !this.utils.isCurrentPageExcluded(file)) {
|
||||
this.instance.UI.enableElements(textActions);
|
||||
} else {
|
||||
this.instance.UI.disableElements(textActions);
|
||||
@ -480,9 +483,13 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
]);
|
||||
}
|
||||
|
||||
this.instance.UI.annotationPopup.add(
|
||||
this._annotationActionsService.getViewerAvailableActions(this.instance, this.file, annotationWrappers, this.annotationsChanged),
|
||||
const actions = this._annotationActionsService.getViewerAvailableActions(
|
||||
this.instance,
|
||||
this.dossier,
|
||||
annotationWrappers,
|
||||
this.annotationsChanged,
|
||||
);
|
||||
this.instance.UI.annotationPopup.add(actions);
|
||||
}
|
||||
|
||||
private _configureRectangleAnnotationPopup() {
|
||||
@ -572,7 +579,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
},
|
||||
]);
|
||||
|
||||
this._handleCustomActions();
|
||||
return this._handleCustomActions();
|
||||
}
|
||||
|
||||
private _addManualRedactionOfType(type: ManualRedactionEntryType) {
|
||||
@ -582,7 +589,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(selectedQuads, manualRedaction, type));
|
||||
}
|
||||
|
||||
private _handleCustomActions() {
|
||||
private async _handleCustomActions() {
|
||||
this.instance.UI.setToolMode('AnnotationEdit');
|
||||
const { ANNOTATION_POPUP, ADD_RECTANGLE, ADD_REDACTION, SHAPE_TOOL_GROUP_BUTTON } = dataElements;
|
||||
const elements = [
|
||||
@ -594,7 +601,9 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
ANNOTATION_POPUP,
|
||||
];
|
||||
|
||||
if (this.canPerformActions && !this.utils.isCurrentPageExcluded(this.file)) {
|
||||
const isCurrentPageExcluded = this.utils.isCurrentPageExcluded(await this.stateService.file);
|
||||
|
||||
if (this.canPerformActions && !isCurrentPageExcluded) {
|
||||
try {
|
||||
this.instance.UI.enableTools(['AnnotationCreateRectangle']);
|
||||
} catch (e) {
|
||||
@ -611,7 +620,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
|
||||
let elementsToDisable = [...elements, ADD_RECTANGLE];
|
||||
|
||||
if (this.utils.isCurrentPageExcluded(this.file)) {
|
||||
if (isCurrentPageExcluded) {
|
||||
const allowedActionsWhenPageExcluded: string[] = [ANNOTATION_POPUP, ADD_RECTANGLE, ADD_REDACTION, SHAPE_TOOL_GROUP_BUTTON];
|
||||
elementsToDisable = elementsToDisable.filter(element => !allowedActionsWhenPageExcluded.includes(element));
|
||||
} else {
|
||||
@ -641,10 +650,8 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
return entry;
|
||||
}
|
||||
|
||||
private _loadDocument() {
|
||||
this.instance.UI.loadDocument(this._stateService.fileData.blob$.value, {
|
||||
filename: this.file ? this.file.filename : 'document.pdf',
|
||||
});
|
||||
private _loadDocument(blob: Blob, file: File) {
|
||||
this.instance.UI.loadDocument(blob, { filename: file?.filename ?? 'document.pdf' });
|
||||
}
|
||||
|
||||
private _setReadyAndInitialState(): void {
|
||||
|
||||
@ -1,47 +1,49 @@
|
||||
<iqser-status-bar [configs]="statusBarConfigs" [small]="true"></iqser-status-bar>
|
||||
<iqser-status-bar [configs]="statusBarConfig$ | async" [small]="true"></iqser-status-bar>
|
||||
|
||||
<div class="all-caps-label mr-16 ml-8">
|
||||
{{ translations[file.workflowStatus] | translate }}
|
||||
<span *ngIf="file.isUnderReview || file.isUnderApproval">{{ 'by' | translate }}:</span>
|
||||
</div>
|
||||
<ng-container *ngIf="stateService.file$ | async as file">
|
||||
<div class="all-caps-label mr-16 ml-8">
|
||||
{{ translations[file.workflowStatus] | translate }}
|
||||
<span *ngIf="file.isUnderReview || file.isUnderApproval">{{ 'by' | translate }}:</span>
|
||||
</div>
|
||||
|
||||
<redaction-initials-avatar
|
||||
*ngIf="!editingReviewer"
|
||||
[user]="file.assignee"
|
||||
[withName]="!!file.assignee"
|
||||
tooltipPosition="below"
|
||||
></redaction-initials-avatar>
|
||||
|
||||
<div
|
||||
(click)="editingReviewer = true"
|
||||
*ngIf="!editingReviewer && canAssignReviewer"
|
||||
class="assign-reviewer pointer"
|
||||
translate="file-preview.assign-reviewer"
|
||||
></div>
|
||||
|
||||
<redaction-assign-user-dropdown
|
||||
(cancel)="editingReviewer = false"
|
||||
(save)="assignReviewer(file, $event)"
|
||||
*ngIf="editingReviewer"
|
||||
[options]="usersOptions"
|
||||
[value]="file.assignee"
|
||||
></redaction-assign-user-dropdown>
|
||||
|
||||
<div *ngIf="!editingReviewer && canAssign" class="assign-actions-wrapper">
|
||||
<iqser-circle-button
|
||||
(action)="editingReviewer = true"
|
||||
*ngIf="canAssignOrUnassign && !!file.assignee"
|
||||
[tooltip]="assignTooltip"
|
||||
icon="iqser:edit"
|
||||
<redaction-initials-avatar
|
||||
*ngIf="(editingReviewer$ | async) === false"
|
||||
[user]="file.assignee"
|
||||
[withName]="!!file.assignee"
|
||||
tooltipPosition="below"
|
||||
iqserHelpMode="assign-reviewer"
|
||||
></iqser-circle-button>
|
||||
></redaction-initials-avatar>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="fileAssignService.assignToMe([file])"
|
||||
*ngIf="canAssignToSelf"
|
||||
[tooltip]="'file-preview.assign-me' | translate"
|
||||
icon="red:assign-me"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
<div
|
||||
(click)="editingReviewer$.next(true)"
|
||||
*ngIf="(editingReviewer$ | async) === false && (canAssignReviewer$ | async)"
|
||||
class="assign-reviewer pointer"
|
||||
translate="file-preview.assign-reviewer"
|
||||
></div>
|
||||
|
||||
<redaction-assign-user-dropdown
|
||||
(cancel)="editingReviewer$.next(false)"
|
||||
(save)="assignReviewer(file, $event)"
|
||||
*ngIf="editingReviewer$ | async"
|
||||
[options]="usersOptions$ | async"
|
||||
[value]="file.assignee"
|
||||
></redaction-assign-user-dropdown>
|
||||
|
||||
<div *ngIf="(editingReviewer$ | async) === false && canAssign$ | async" class="assign-actions-wrapper">
|
||||
<iqser-circle-button
|
||||
(action)="editingReviewer$.next(true)"
|
||||
*ngIf="(canAssignOrUnassign$ | async) && !!file.assignee"
|
||||
[tooltip]="assignTooltip$ | async"
|
||||
icon="iqser:edit"
|
||||
iqserHelpMode="assign-reviewer"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="fileAssignService.assignToMe([file])"
|
||||
*ngIf="canAssignToSelf$ | async"
|
||||
[tooltip]="'file-preview.assign-me' | translate"
|
||||
icon="red:assign-me"
|
||||
tooltipPosition="below"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
import { Dossier, File, StatusBarConfigs, User } from '@red/domain';
|
||||
import { List, LoadingService, Toaster } from '@iqser/common-ui';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
@ -8,7 +8,10 @@ import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
import { BehaviorSubject, combineLatest, firstValueFrom, Observable, switchMap } from 'rxjs';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { distinctUntilChanged, map } from 'rxjs/operators';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-user-management',
|
||||
@ -16,22 +19,19 @@ import { firstValueFrom } from 'rxjs';
|
||||
styleUrls: ['./user-management.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class UserManagementComponent implements OnChanges {
|
||||
export class UserManagementComponent {
|
||||
readonly translations = workflowFileStatusTranslations;
|
||||
|
||||
@Input() file: File;
|
||||
@Input() dossier: Dossier;
|
||||
|
||||
editingReviewer = false;
|
||||
statusBarConfigs: StatusBarConfigs;
|
||||
canAssignToSelf = false;
|
||||
canAssignUser = false;
|
||||
canUnassignUser = false;
|
||||
canAssign = false;
|
||||
canAssignOrUnassign = false;
|
||||
canAssignReviewer = false;
|
||||
assignTooltip: string;
|
||||
usersOptions: List;
|
||||
readonly statusBarConfig$: Observable<StatusBarConfigs>;
|
||||
readonly assignTooltip$: Observable<string>;
|
||||
readonly canAssignReviewer$: Observable<boolean>;
|
||||
readonly canAssignToSelf$: Observable<boolean>;
|
||||
readonly editingReviewer$ = new BehaviorSubject<boolean>(false);
|
||||
readonly canAssignOrUnassign$: Observable<boolean>;
|
||||
readonly canAssign$: Observable<boolean>;
|
||||
readonly usersOptions$: Observable<List>;
|
||||
private readonly _dossier$: Observable<Dossier>;
|
||||
private readonly _canAssignUser$: Observable<boolean>;
|
||||
private readonly _canUnassignUser$: Observable<boolean>;
|
||||
|
||||
constructor(
|
||||
readonly fileAssignService: FileAssignService,
|
||||
@ -41,44 +41,55 @@ export class UserManagementComponent implements OnChanges {
|
||||
readonly toaster: Toaster,
|
||||
readonly loadingService: LoadingService,
|
||||
readonly translateService: TranslateService,
|
||||
) {}
|
||||
readonly stateService: FilePreviewStateService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
) {
|
||||
this._dossier$ = this.stateService.file$.pipe(switchMap(file => this._dossiersService.getEntityChanged$(file.dossierId)));
|
||||
this.statusBarConfig$ = this.stateService.file$.pipe(map(file => [{ length: 1, color: file.workflowStatus }]));
|
||||
this.assignTooltip$ = this.stateService.file$.pipe(
|
||||
map(file =>
|
||||
file.isUnderApproval
|
||||
? this.translateService.instant(_('dossier-overview.assign-approver'))
|
||||
: file.assignee
|
||||
? this.translateService.instant(_('file-preview.change-reviewer'))
|
||||
: this.translateService.instant(_('file-preview.assign-reviewer')),
|
||||
),
|
||||
);
|
||||
|
||||
private get _statusBarConfig(): StatusBarConfigs {
|
||||
return [{ length: 1, color: this.file.workflowStatus }];
|
||||
}
|
||||
this.canAssignToSelf$ = this.stateService.file$.pipe(
|
||||
map(file => this.permissionsService.canAssignToSelf(file)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
this._canAssignUser$ = this.stateService.file$.pipe(
|
||||
map(file => this.permissionsService.canAssignUser(file)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
this._canUnassignUser$ = this.stateService.file$.pipe(
|
||||
map(file => this.permissionsService.canUnassignUser(file)),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
private get _assignOrChangeReviewerTooltip(): string {
|
||||
return this.file.assignee
|
||||
? this.translateService.instant(_('file-preview.change-reviewer'))
|
||||
: this.translateService.instant(_('file-preview.assign-reviewer'));
|
||||
}
|
||||
this.canAssignOrUnassign$ = combineLatest([this._canAssignUser$, this._canUnassignUser$]).pipe(
|
||||
map(([canAssignUser, canUnassignUser]) => canAssignUser || canUnassignUser),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
private get _assignTooltip(): string {
|
||||
return this.file.isUnderApproval
|
||||
? this.translateService.instant(_('dossier-overview.assign-approver'))
|
||||
: this._assignOrChangeReviewerTooltip;
|
||||
}
|
||||
this.canAssign$ = combineLatest([this.canAssignToSelf$, this.canAssignOrUnassign$]).pipe(
|
||||
map(([canAssignToSelf, canAssignOrUnassign]) => canAssignToSelf || canAssignOrUnassign),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
private get _canAssignReviewer(): boolean {
|
||||
return !this.file.assignee && this.canAssignUser && this.dossier.hasReviewers;
|
||||
}
|
||||
this.canAssignReviewer$ = combineLatest([this.stateService.file$, this._canAssignUser$, this._dossier$]).pipe(
|
||||
map(([file, canAssignUser, dossier]) => !file.assignee && canAssignUser && dossier.hasReviewers),
|
||||
distinctUntilChanged(),
|
||||
);
|
||||
|
||||
private get _usersOptions(): List {
|
||||
const unassignUser = this.canUnassignUser ? [undefined] : [];
|
||||
return this.file.isUnderApproval ? [...this.dossier.approverIds, ...unassignUser] : [...this.dossier.memberIds, ...unassignUser];
|
||||
}
|
||||
|
||||
ngOnChanges() {
|
||||
this.canAssignToSelf = this.permissionsService.canAssignToSelf(this.file);
|
||||
this.canAssignUser = this.permissionsService.canAssignUser(this.file);
|
||||
this.canUnassignUser = this.permissionsService.canUnassignUser(this.file);
|
||||
this.canAssignOrUnassign = this.canAssignUser || this.canUnassignUser;
|
||||
this.canAssign = this.canAssignToSelf || this.canAssignOrUnassign;
|
||||
|
||||
this.statusBarConfigs = this._statusBarConfig;
|
||||
this.canAssignReviewer = this._canAssignReviewer;
|
||||
this.assignTooltip = this._assignTooltip;
|
||||
this.usersOptions = this._usersOptions;
|
||||
this.usersOptions$ = combineLatest([this._canUnassignUser$, this.stateService.file$, this._dossier$]).pipe(
|
||||
map(([canUnassignUser, file, dossier]) => {
|
||||
const unassignUser = canUnassignUser ? [undefined] : [];
|
||||
return file.isUnderApproval ? [...dossier.approverIds, ...unassignUser] : [...dossier.memberIds, ...unassignUser];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async assignReviewer(file: File, user: User | string) {
|
||||
@ -99,6 +110,6 @@ export class UserManagementComponent implements OnChanges {
|
||||
this.loadingService.stop();
|
||||
|
||||
this.toaster.info(_('assignment.reviewer'), { params: { reviewerName, filename } });
|
||||
this.editingReviewer = false;
|
||||
this.editingReviewer$.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,9 +21,9 @@
|
||||
</button>
|
||||
|
||||
<button
|
||||
(click)="canSwitchToRedactedView && switchView.emit('REDACTED')"
|
||||
(click)="switchView.emit('REDACTED')"
|
||||
[class.active]="viewModeService.isRedacted"
|
||||
[disabled]="!canSwitchToRedactedView"
|
||||
[disabled]="(canSwitchToRedactedView$ | async) === false"
|
||||
[matTooltip]="'file-preview.redacted-tooltip' | translate"
|
||||
class="red-tab"
|
||||
iqserHelpMode="preview-view"
|
||||
|
||||
@ -1,38 +1,32 @@
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
|
||||
import { File, ViewMode } from '@red/domain';
|
||||
import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core';
|
||||
import { ViewMode } from '@red/domain';
|
||||
import { ViewModeService } from '../../services/view-mode.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { combineLatest, Observable } from 'rxjs';
|
||||
import { filter, map, switchMap } from 'rxjs/operators';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-view-switch [file]',
|
||||
selector: 'redaction-view-switch',
|
||||
templateUrl: './view-switch.component.html',
|
||||
styleUrls: ['./view-switch.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class ViewSwitchComponent implements OnChanges {
|
||||
export class ViewSwitchComponent {
|
||||
@Output() readonly switchView = new EventEmitter<ViewMode>();
|
||||
@Input() file: File;
|
||||
|
||||
readonly canSwitchToDeltaView$: Observable<boolean>;
|
||||
canSwitchToRedactedView = false;
|
||||
readonly canSwitchToRedactedView$: Observable<boolean>;
|
||||
|
||||
constructor(readonly viewModeService: ViewModeService, private readonly _stateService: FilePreviewStateService) {
|
||||
this.canSwitchToDeltaView$ = this._stateService.fileData$.pipe(
|
||||
this.canSwitchToDeltaView$ = _stateService.fileData$.pipe(
|
||||
filter(fileData => !!fileData),
|
||||
switchMap(fileData =>
|
||||
combineLatest([fileData.hasChangeLog$, fileData.file$]).pipe(
|
||||
combineLatest([fileData.hasChangeLog$, _stateService.file$]).pipe(
|
||||
map(([hasChangeLog, file]) => hasChangeLog && !file.isApproved),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
if (changes.file) {
|
||||
const file = changes?.file.currentValue as File;
|
||||
this.canSwitchToRedactedView = !file.analysisRequired && !file.excluded;
|
||||
}
|
||||
this.canSwitchToRedactedView$ = _stateService.file$.pipe(map(file => !file.analysisRequired && !file.excluded));
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
<div [class.required]="!isDocumentAdmin" class="iqser-input-group w-300">
|
||||
<label translate="manual-annotation.dialog.content.comment"></label>
|
||||
<textarea formControlName="comment" name="comment" redactionHasScrollbar rows="4" type="text"></textarea>
|
||||
<textarea formControlName="comment" iqserHasScrollbar name="comment" rows="4" type="text"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@ import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { Dictionary, Dossier, File } from '@red/domain';
|
||||
import { Dictionary, Dossier } from '@red/domain';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { BaseDialogComponent } from '@iqser/common-ui';
|
||||
import { DictionaryService } from '@shared/services/dictionary.service';
|
||||
@ -12,7 +12,7 @@ import { AnnotationWrapper } from '../../../../../../models/file/annotation.wrap
|
||||
|
||||
export interface AcceptRecommendationData {
|
||||
readonly annotations: AnnotationWrapper[];
|
||||
readonly file: File;
|
||||
readonly dossierId: string;
|
||||
}
|
||||
|
||||
export interface AcceptRecommendationReturnType {
|
||||
@ -42,7 +42,7 @@ export class AcceptRecommendationDialogComponent extends BaseDialogComponent imp
|
||||
@Inject(MAT_DIALOG_DATA) readonly data: AcceptRecommendationData,
|
||||
) {
|
||||
super(_injector, _dialogRef);
|
||||
this._dossier = this._dossiersService.find(this.data.file.dossierId);
|
||||
this._dossier = this._dossiersService.find(this.data.dossierId);
|
||||
this.isDocumentAdmin = this._permissionsService.isApprover(this._dossier);
|
||||
this.form = this._getForm();
|
||||
this.initialFormValue = this.form.getRawValue();
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { ExcludedPagesService } from './services/excluded-pages.service';
|
||||
import { ViewModeService } from './services/view-mode.service';
|
||||
import { MultiSelectService } from './services/multi-select.service';
|
||||
import { DocumentInfoService } from './services/document-info.service';
|
||||
import { CommentingService } from './services/commenting.service';
|
||||
import { SkippedService } from './services/skipped.service';
|
||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||
import { AnnotationActionsService } from './services/annotation-actions.service';
|
||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
|
||||
import { AnnotationReferencesService } from './services/annotation-references.service';
|
||||
import { FilterService } from '../../../../../../../../libs/common-ui/src';
|
||||
|
||||
export const filePreviewScreenProviders = [
|
||||
FilterService,
|
||||
ExcludedPagesService,
|
||||
ViewModeService,
|
||||
MultiSelectService,
|
||||
DocumentInfoService,
|
||||
CommentingService,
|
||||
SkippedService,
|
||||
AnnotationDrawService,
|
||||
AnnotationActionsService,
|
||||
FilePreviewStateService,
|
||||
PdfViewerDataService,
|
||||
AnnotationReferencesService,
|
||||
];
|
||||
@ -1,9 +1,9 @@
|
||||
<ng-container *ngIf="dossier$ | async as dossier">
|
||||
<ng-container *ngIf="fileData?.file$ | async as file">
|
||||
<ng-container *ngIf="stateService.dossier$ | async as dossier">
|
||||
<ng-container *ngIf="stateService.file$ | async as file">
|
||||
<section [class.fullscreen]="fullScreen">
|
||||
<div class="page-header">
|
||||
<div class="flex flex-1">
|
||||
<redaction-view-switch (switchView)="switchView($event)" [file]="file"></redaction-view-switch>
|
||||
<redaction-view-switch (switchView)="switchView($event)"></redaction-view-switch>
|
||||
</div>
|
||||
|
||||
<div class="flex-1 actions-container">
|
||||
@ -16,7 +16,7 @@
|
||||
<mat-icon svgIcon="red:reanalyse"></mat-icon>
|
||||
</div>
|
||||
|
||||
<redaction-user-management [dossier]="dossier" [file]="file"></redaction-user-management>
|
||||
<redaction-user-management></redaction-user-management>
|
||||
|
||||
<ng-container *ngIf="permissionsService.isApprover(dossier) && !!file.lastReviewer">
|
||||
<div class="vertical-line"></div>
|
||||
@ -76,7 +76,6 @@
|
||||
[canPerformActions]="canPerformAnnotationActions$ | async"
|
||||
[class.hidden]="!ready"
|
||||
[dossier]="dossier"
|
||||
[file]="file"
|
||||
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
|
||||
></redaction-pdf-viewer>
|
||||
</div>
|
||||
@ -89,11 +88,7 @@
|
||||
icon="red:needs-work"
|
||||
></iqser-empty-state>
|
||||
|
||||
<redaction-document-info
|
||||
*ngIf="documentInfoService.shown$ | async"
|
||||
[dossier]="dossier"
|
||||
[file]="file"
|
||||
></redaction-document-info>
|
||||
<redaction-document-info *ngIf="documentInfoService.shown$ | async"></redaction-document-info>
|
||||
|
||||
<redaction-file-workload
|
||||
#fileWorkloadComponent
|
||||
@ -107,7 +102,6 @@
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
[annotations]="visibleAnnotations"
|
||||
[dialogRef]="dialogRef"
|
||||
[fileData]="fileData"
|
||||
[file]="file"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[viewer]="activeViewer"
|
||||
|
||||
@ -22,9 +22,9 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||
import { ManualAnnotationResponse } from '@models/file/manual-annotation-response';
|
||||
import { AnnotationDrawService } from './services/annotation-draw.service';
|
||||
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
|
||||
import { Dossier, File, ViewMode } from '@red/domain';
|
||||
import { File, ViewMode } from '@red/domain';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { combineLatest, Observable, timer } from 'rxjs';
|
||||
import { combineLatest, firstValueFrom, Observable, of, timer } from 'rxjs';
|
||||
import { UserPreferenceService } from '@services/user-preference.service';
|
||||
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
|
||||
import { download } from '@utils/file-download-utils';
|
||||
@ -36,7 +36,7 @@ import { handleFilterDelta } from '@utils/filter-utils';
|
||||
import { FilesService } from '@services/entity-services/files.service';
|
||||
import { DossiersService } from '@services/entity-services/dossiers.service';
|
||||
import { FileManagementService } from '@services/entity-services/file-management.service';
|
||||
import { map, switchMap, tap } from 'rxjs/operators';
|
||||
import { catchError, filter, map, switchMap, tap } from 'rxjs/operators';
|
||||
import { FilesMapService } from '@services/entity-services/files-map.service';
|
||||
import { WatermarkService } from '@shared/services/watermark.service';
|
||||
import { ExcludedPagesService } from './services/excluded-pages.service';
|
||||
@ -45,12 +45,11 @@ import { MultiSelectService } from './services/multi-select.service';
|
||||
import { DocumentInfoService } from './services/document-info.service';
|
||||
import { ReanalysisService } from '../../../../services/reanalysis.service';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
import { CommentingService } from './services/commenting.service';
|
||||
import { SkippedService } from './services/skipped.service';
|
||||
import { AnnotationActionsService } from './services/annotation-actions.service';
|
||||
import { FilePreviewStateService } from './services/file-preview-state.service';
|
||||
import { FileDataModel } from '../../../../models/file/file-data.model';
|
||||
import { AnnotationReferencesService } from './services/annotation-references.service';
|
||||
import { filePreviewScreenProviders } from './file-preview-providers';
|
||||
import { ManualAnnotationService } from '../../services/manual-annotation.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
import PDFNet = Core.PDFNet;
|
||||
|
||||
@ -59,20 +58,7 @@ const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f', 'ArrowUp', 'ArrowDown'];
|
||||
@Component({
|
||||
templateUrl: './file-preview-screen.component.html',
|
||||
styleUrls: ['./file-preview-screen.component.scss'],
|
||||
providers: [
|
||||
FilterService,
|
||||
ExcludedPagesService,
|
||||
ViewModeService,
|
||||
MultiSelectService,
|
||||
DocumentInfoService,
|
||||
CommentingService,
|
||||
SkippedService,
|
||||
AnnotationDrawService,
|
||||
AnnotationActionsService,
|
||||
FilePreviewStateService,
|
||||
PdfViewerDataService,
|
||||
AnnotationReferencesService,
|
||||
],
|
||||
providers: filePreviewScreenProviders,
|
||||
})
|
||||
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach {
|
||||
readonly circleButtonTypes = CircleButtonTypes;
|
||||
@ -84,10 +70,9 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
displayPdfViewer = false;
|
||||
activeViewerPage: number = null;
|
||||
@ViewChild(PdfViewerComponent) readonly viewerComponent: PdfViewerComponent;
|
||||
readonly dossierId: string;
|
||||
readonly canPerformAnnotationActions$: Observable<boolean>;
|
||||
readonly dossier$: Observable<Dossier>;
|
||||
readonly fileId: string;
|
||||
readonly fileId = this.stateService.fileId;
|
||||
readonly dossierId = this.stateService.dossierId;
|
||||
ready = false;
|
||||
private _instance: WebViewerInstance;
|
||||
private _lastPage: string;
|
||||
@ -102,7 +87,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
constructor(
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly userPreferenceService: UserPreferenceService,
|
||||
private readonly _stateService: FilePreviewStateService,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
private readonly _watermarkService: WatermarkService,
|
||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||
private readonly _activatedRoute: ActivatedRoute,
|
||||
@ -122,15 +107,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
private readonly _reanalysisService: ReanalysisService,
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _skippedService: SkippedService,
|
||||
private readonly _manualAnnotationService: ManualAnnotationService,
|
||||
readonly excludedPagesService: ExcludedPagesService,
|
||||
readonly viewModeService: ViewModeService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
) {
|
||||
super();
|
||||
this.dossierId = _activatedRoute.snapshot.paramMap.get('dossierId');
|
||||
this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId);
|
||||
this.fileId = _activatedRoute.snapshot.paramMap.get('fileId');
|
||||
this.canPerformAnnotationActions$ = this._canPerformAnnotationActions$;
|
||||
|
||||
document.documentElement.addEventListener('fullscreenchange', () => {
|
||||
@ -141,34 +124,30 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
get visibleAnnotations(): AnnotationWrapper[] {
|
||||
return this.fileData ? this.fileData.getVisibleAnnotations(this.viewModeService.viewMode) : [];
|
||||
return this._fileData ? this._fileData.getVisibleAnnotations(this.viewModeService.viewMode) : [];
|
||||
}
|
||||
|
||||
get allAnnotations(): AnnotationWrapper[] {
|
||||
return this.fileData ? this.fileData.allAnnotations : [];
|
||||
return this._fileData ? this._fileData.allAnnotations : [];
|
||||
}
|
||||
|
||||
get activeViewer(): WebViewerInstance {
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
get fileData(): FileDataModel {
|
||||
return this._stateService.fileData;
|
||||
private get _fileData(): FileDataModel {
|
||||
return this.stateService.fileData;
|
||||
}
|
||||
|
||||
private get _canPerformAnnotationActions$() {
|
||||
return combineLatest([
|
||||
this._stateService.fileData$.pipe(switchMap(fileData => fileData.file$)),
|
||||
this.viewModeService.viewMode$,
|
||||
this.viewModeService.compareMode$,
|
||||
]).pipe(
|
||||
return combineLatest([this.stateService.file$, this.viewModeService.viewMode$, this.viewModeService.compareMode$]).pipe(
|
||||
map(([file, viewMode]) => this.permissionsService.canPerformAnnotationActions(file) && viewMode === 'STANDARD'),
|
||||
shareDistinctLast(),
|
||||
);
|
||||
}
|
||||
|
||||
async updateViewMode(): Promise<void> {
|
||||
const ocrAnnotationIds = this.fileData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
|
||||
const ocrAnnotationIds = this._fileData.allAnnotations.filter(a => a.isOCR).map(a => a.id);
|
||||
const annotations = this._getAnnotations(a => a.getCustomData('redact-manager'));
|
||||
const redactions = annotations.filter(a => a.getCustomData('redaction'));
|
||||
|
||||
@ -211,7 +190,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
|
||||
async ngOnAttach(previousRoute: ActivatedRouteSnapshot): Promise<boolean> {
|
||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||
const file = await this.stateService.file;
|
||||
if (!file.canBeOpened) {
|
||||
return this._router.navigate([this._dossiersService.find(this.dossierId)?.routerLink]);
|
||||
}
|
||||
@ -228,9 +207,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId);
|
||||
this._subscribeToFileUpdates();
|
||||
|
||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||
const file = await this.stateService.file;
|
||||
if (file?.analysisRequired) {
|
||||
await this._reanalysisService.reanalyzeFilesForDossier([this.fileId], this.dossierId, true).toPromise();
|
||||
const reanalyzeFiles = this._reanalysisService.reanalyzeFilesForDossier([this.fileId], this.dossierId, true);
|
||||
await firstValueFrom(reanalyzeFiles);
|
||||
}
|
||||
|
||||
this.displayPdfViewer = true;
|
||||
@ -263,7 +243,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._filterService.addFilterGroup({
|
||||
slug: 'secondaryFilters',
|
||||
filterTemplate: this._filterTemplate,
|
||||
filters: processFilters(secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters(this.fileData?.viewedPages)),
|
||||
filters: processFilters(secondaryFilters, AnnotationProcessingService.secondaryAnnotationFilters(this._fileData?.viewedPages)),
|
||||
});
|
||||
console.log(`[REDACTION] Process time: ${new Date().getTime() - processStartTime} ms`);
|
||||
console.log(`[REDACTION] Filter rebuild time: ${new Date().getTime() - startTime}`);
|
||||
@ -301,18 +281,22 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
|
||||
openManualAnnotationDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) {
|
||||
this._ngZone.run(() => {
|
||||
const file = this._filesMapService.get(this.dossierId, this.fileId);
|
||||
this.dialogRef = this._dialogService.openDialog(
|
||||
'manualAnnotation',
|
||||
null,
|
||||
{ manualRedactionEntryWrapper, file },
|
||||
async (response: ManualAnnotationResponse) => {
|
||||
{ manualRedactionEntryWrapper, dossierId: this.dossierId },
|
||||
async (entryWrapper: ManualRedactionEntryWrapper) => {
|
||||
const addAnnotation$ = this._manualAnnotationService
|
||||
.addAnnotation(entryWrapper.manualRedactionEntry, this.dossierId, this.fileId)
|
||||
.pipe(catchError(() => of(undefined)));
|
||||
const addAnnotationResponse = await firstValueFrom(addAnnotation$);
|
||||
const response = new ManualAnnotationResponse(entryWrapper, addAnnotationResponse);
|
||||
|
||||
if (response?.annotationId) {
|
||||
const annotation = this._instance.Core.annotationManager.getAnnotationById(
|
||||
response.manualRedactionEntryWrapper.rectId,
|
||||
);
|
||||
this._instance.Core.annotationManager.deleteAnnotation(annotation);
|
||||
// await this._filesService.reload(this.dossierId, this.fileId).toPromise();
|
||||
const distinctPages = manualRedactionEntryWrapper.manualRedactionEntry.positions
|
||||
.map(p => p.page)
|
||||
.filter((item, pos, self) => self.indexOf(item) === pos);
|
||||
@ -470,19 +454,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}, 100);
|
||||
}
|
||||
|
||||
private async _reloadFile(file: File): Promise<void> {
|
||||
const previousFile = this.fileData?.file;
|
||||
await this._loadFileData(file);
|
||||
|
||||
// file already loaded at least once
|
||||
if (previousFile) {
|
||||
// If it has been OCRd, we need to wait for it to load into the viewer
|
||||
if (previousFile.lastOCRTime !== this.fileData?.file?.lastOCRTime) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async _stampPDF() {
|
||||
if (!this._instance?.Core.documentViewer.getDocument()) {
|
||||
return;
|
||||
@ -538,23 +509,19 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
}
|
||||
}
|
||||
|
||||
private async _fileUpdated(file: File): Promise<File> {
|
||||
if (!this.fileData || file.lastProcessed === this.fileData.file.lastProcessed) {
|
||||
await this._reloadFile(file);
|
||||
} else {
|
||||
// File reanalysed
|
||||
const previousAnnotations = this.visibleAnnotations;
|
||||
await this._loadFileData(file);
|
||||
await this._reloadAnnotations(previousAnnotations);
|
||||
}
|
||||
|
||||
return file;
|
||||
private async _fileUpdated(file: File): Promise<void> {
|
||||
await this._loadFileData(file);
|
||||
await this._reloadAnnotations();
|
||||
await this._stampPDF();
|
||||
}
|
||||
|
||||
private _subscribeToFileUpdates(): void {
|
||||
this.addActiveScreenSubscription = this._filesMapService
|
||||
.watch$(this.dossierId, this.fileId)
|
||||
.pipe(switchMap(file => this._fileUpdated(file)))
|
||||
.pipe(
|
||||
filter(f => !!f),
|
||||
switchMap(file => this._fileUpdated(file)),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = timer(0, 5000)
|
||||
@ -593,13 +560,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
return this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]);
|
||||
}
|
||||
|
||||
const fileData = await this._pdfViewerDataService.loadDataFor(file).toPromise();
|
||||
|
||||
if (file.isPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._stateService.fileData = fileData;
|
||||
this.stateService.fileData = await firstValueFrom(this._pdfViewerDataService.loadDataFor(file));
|
||||
}
|
||||
|
||||
@Debounce(0)
|
||||
@ -608,25 +573,24 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
this._workloadComponent?.scrollAnnotations();
|
||||
}
|
||||
|
||||
private async _reloadAnnotations(previousAnnotations?: AnnotationWrapper[]) {
|
||||
private async _reloadAnnotations() {
|
||||
this._deleteAnnotations();
|
||||
await this._cleanupAndRedrawAnnotations(previousAnnotations);
|
||||
await this._cleanupAndRedrawAnnotations();
|
||||
}
|
||||
|
||||
private async _reloadAnnotationsForPage(page: number) {
|
||||
this.fileData.file = await this._filesService.reload(this.dossierId, this.fileId).toPromise();
|
||||
|
||||
const file = await firstValueFrom(this._filesService.reload(this.dossierId, this.fileId));
|
||||
// if this action triggered a re-processing,
|
||||
// we don't want to redraw for this page since they will get redrawn as soon as processing ends;
|
||||
if (this.fileData.file.isProcessing) {
|
||||
if (file.isProcessing) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPageAnnotations = this.visibleAnnotations.filter(a => a.pageNumber === page);
|
||||
this.fileData.redactionLog = await this._pdfViewerDataService.loadRedactionLogFor(this.dossierId, this.fileId).toPromise();
|
||||
this._fileData.redactionLog = await firstValueFrom(this._pdfViewerDataService.loadRedactionLogFor(this.dossierId, this.fileId));
|
||||
|
||||
this._deleteAnnotations(currentPageAnnotations);
|
||||
await this._cleanupAndRedrawAnnotations(currentPageAnnotations, annotation => annotation.pageNumber === page);
|
||||
await this._cleanupAndRedrawAnnotations(annotation => annotation.pageNumber === page);
|
||||
}
|
||||
|
||||
private _deleteAnnotations(annotationsToDelete?: AnnotationWrapper[]) {
|
||||
@ -645,10 +609,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
});
|
||||
}
|
||||
|
||||
private async _cleanupAndRedrawAnnotations(
|
||||
currentAnnotations?: AnnotationWrapper[],
|
||||
newAnnotationsFilter?: (annotation: AnnotationWrapper) => boolean,
|
||||
) {
|
||||
private async _cleanupAndRedrawAnnotations(newAnnotationsFilter?: (annotation: AnnotationWrapper) => boolean) {
|
||||
if (!this._instance?.Core.documentViewer.getDocument()) {
|
||||
return;
|
||||
}
|
||||
@ -656,17 +617,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
||||
const currentFilters = this._filterService.getGroup('primaryFilters')?.filters || [];
|
||||
this.rebuildFilters();
|
||||
|
||||
if (this.viewModeService.viewMode === 'STANDARD') {
|
||||
const startTime = new Date().getTime();
|
||||
const newAnnotations = newAnnotationsFilter ? this.visibleAnnotations.filter(newAnnotationsFilter) : this.visibleAnnotations;
|
||||
if (currentFilters) {
|
||||
this._handleDeltaAnnotationFilters(currentFilters, this.visibleAnnotations);
|
||||
}
|
||||
await this._redrawAnnotations(newAnnotations);
|
||||
console.log(
|
||||
`[REDACTION] Annotations redraw time: ${new Date().getTime() - startTime} ms for ${newAnnotations.length} annotations`,
|
||||
);
|
||||
if (!this.viewModeService.isStandard) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = new Date().getTime();
|
||||
const newAnnotations = newAnnotationsFilter ? this.visibleAnnotations.filter(newAnnotationsFilter) : this.visibleAnnotations;
|
||||
|
||||
if (currentFilters) {
|
||||
this._handleDeltaAnnotationFilters(currentFilters, this.visibleAnnotations);
|
||||
}
|
||||
await this._redrawAnnotations(newAnnotations);
|
||||
console.log(`[REDACTION] Annotations redraw time: ${new Date().getTime() - startTime} ms for ${newAnnotations.length} annotations`);
|
||||
}
|
||||
|
||||
private _redrawAnnotations(annotations = this.allAnnotations) {
|
||||
|
||||
@ -10,7 +10,7 @@ import { DossiersDialogService } from '../../../services/dossiers-dialog.service
|
||||
import { BASE_HREF } from '../../../../../tokens';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { Core, WebViewerInstance } from '@pdftron/webviewer';
|
||||
import { Dossier, File, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain';
|
||||
import { Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain';
|
||||
import { toPosition } from '../../../utils/pdf-calculation.utils';
|
||||
import { AnnotationDrawService } from './annotation-draw.service';
|
||||
import { translateQuads } from '@utils/pdf-coordinates';
|
||||
@ -23,6 +23,7 @@ import {
|
||||
import { defaultDialogConfig } from '@iqser/common-ui';
|
||||
import { filter } from 'rxjs/operators';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { FilePreviewStateService } from './file-preview-state.service';
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
|
||||
@Injectable()
|
||||
@ -38,44 +39,45 @@ export class AnnotationActionsService {
|
||||
private readonly _dialog: MatDialog,
|
||||
private readonly _annotationDrawService: AnnotationDrawService,
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _screenStateService: FilePreviewStateService,
|
||||
) {}
|
||||
|
||||
acceptSuggestion(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
private get _dossier(): Dossier {
|
||||
return this._dossiersService.find(this._screenStateService.dossierId);
|
||||
}
|
||||
|
||||
acceptSuggestion($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
$event?.stopPropagation();
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.approve(annotation.id, file, annotation.isModifyDictionary),
|
||||
this._manualAnnotationService.approve(annotation.id, dossierId, fileId, annotation.isModifyDictionary),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
rejectSuggestion(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
rejectSuggestion($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
$event?.stopPropagation();
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(this._manualAnnotationService.declineOrRemoveRequest(annotation, file), annotation, annotationsChanged);
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.declineOrRemoveRequest(annotation, dossierId, fileId),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
forceAnnotation(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
hint: boolean = false,
|
||||
) {
|
||||
const data = { dossier: this._dossier(file), hint };
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
const data = { dossier: this._dossier, hint };
|
||||
this._dialogService.openDialog('forceAnnotation', $event, data, (request: ILegalBasisChangeRequest) => {
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
@ -84,7 +86,8 @@ export class AnnotationActionsService {
|
||||
...request,
|
||||
annotationId: annotation.id,
|
||||
},
|
||||
file,
|
||||
dossierId,
|
||||
fileId,
|
||||
),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
@ -93,22 +96,19 @@ export class AnnotationActionsService {
|
||||
});
|
||||
}
|
||||
|
||||
changeLegalBasis(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
changeLegalBasis($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
this._dialogService.openDialog(
|
||||
'changeLegalBasis',
|
||||
$event,
|
||||
{ annotations, dossier: this._dossier(file) },
|
||||
{ annotations, dossier: this._dossier },
|
||||
(data: { comment: string; legalBasis: string; section: string; value: string }) => {
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.changeLegalBasis(
|
||||
annotation.annotationId,
|
||||
file,
|
||||
dossierId,
|
||||
fileId,
|
||||
data.section,
|
||||
data.value,
|
||||
data.legalBasis,
|
||||
@ -125,20 +125,26 @@ export class AnnotationActionsService {
|
||||
removeOrSuggestRemoveAnnotation(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
removeFromDictionary: boolean,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
const data = {
|
||||
annotationsToRemove: annotations,
|
||||
removeFromDictionary,
|
||||
dossier: this._dossier(file),
|
||||
dossier: this._dossier,
|
||||
hint: annotations[0].hintDictionary,
|
||||
};
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
this._dialogService.openDialog('removeAnnotations', $event, data, (result: { comment: string }) => {
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.removeOrSuggestRemoveAnnotation(annotation, file, removeFromDictionary, result.comment),
|
||||
this._manualAnnotationService.removeOrSuggestRemoveAnnotation(
|
||||
annotation,
|
||||
dossierId,
|
||||
fileId,
|
||||
removeFromDictionary,
|
||||
result.comment,
|
||||
),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
@ -146,28 +152,19 @@ export class AnnotationActionsService {
|
||||
});
|
||||
}
|
||||
|
||||
markAsFalsePositive(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
markAsFalsePositive($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
annotations.forEach(annotation => {
|
||||
this._markAsFalsePositive($event, annotation, file, this._getFalsePositiveText(annotation), annotationsChanged);
|
||||
this._markAsFalsePositive($event, annotation, this._getFalsePositiveText(annotation), annotationsChanged);
|
||||
});
|
||||
}
|
||||
|
||||
recategorizeImages(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
const data = { annotations, dossier: this._dossier(file) };
|
||||
recategorizeImages($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
const data = { annotations, dossier: this._dossier };
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
this._dialogService.openDialog('recategorizeImage', $event, data, (res: { type: string; comment: string }) => {
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.recategorizeImg(annotation.annotationId, file, res.type, res.comment),
|
||||
this._manualAnnotationService.recategorizeImg(annotation.annotationId, dossierId, fileId, res.type, res.comment),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
@ -175,37 +172,37 @@ export class AnnotationActionsService {
|
||||
});
|
||||
}
|
||||
|
||||
undoDirectAction(
|
||||
$event: MouseEvent,
|
||||
annotations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
undoDirectAction($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter<AnnotationWrapper>) {
|
||||
$event?.stopPropagation();
|
||||
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(this._manualAnnotationService.undoRequest(annotation, file), annotation, annotationsChanged);
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.undoRequest(annotation, dossierId, fileId),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
convertRecommendationToAnnotation(
|
||||
$event: any,
|
||||
recommendations: AnnotationWrapper[],
|
||||
file: File,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
$event?.stopPropagation();
|
||||
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
const dialogRef = this._dialog.open<AcceptRecommendationDialogComponent, AcceptRecommendationData, AcceptRecommendationReturnType>(
|
||||
AcceptRecommendationDialogComponent,
|
||||
{ ...defaultDialogConfig, autoFocus: true, data: { annotations: recommendations, file } },
|
||||
{ ...defaultDialogConfig, autoFocus: true, data: { annotations: recommendations, dossierId } },
|
||||
);
|
||||
const dialogClosed = dialogRef.afterClosed().pipe(filter(value => !!value && !!value.annotations));
|
||||
dialogClosed.subscribe(({ annotations, comment: commentText }) => {
|
||||
const comment = commentText ? { text: commentText } : undefined;
|
||||
annotations.forEach(annotation => {
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.addRecommendation(annotation, file, comment),
|
||||
this._manualAnnotationService.addRecommendation(annotation, dossierId, fileId, comment),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
@ -215,13 +212,11 @@ export class AnnotationActionsService {
|
||||
|
||||
getViewerAvailableActions(
|
||||
viewer: WebViewerInstance,
|
||||
file: File,
|
||||
dossier: Dossier,
|
||||
annotations: AnnotationWrapper[],
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
): Record<string, unknown>[] {
|
||||
const availableActions = [];
|
||||
const dossier = this._dossier(file);
|
||||
|
||||
const annotationPermissions = annotations.map(annotation => ({
|
||||
annotation,
|
||||
permissions: AnnotationPermissions.forUser(
|
||||
@ -241,21 +236,19 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
||||
title: this._translateService.instant('annotation-actions.resize-accept.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.acceptResize(null, viewer, file, firstAnnotation, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.acceptResize(null, viewer, firstAnnotation, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
availableActions.push({
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/close.svg'),
|
||||
title: this._translateService.instant('annotation-actions.resize-cancel.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.cancelResize(null, viewer, firstAnnotation, annotationsChanged);
|
||||
});
|
||||
},
|
||||
}),
|
||||
});
|
||||
return availableActions;
|
||||
}
|
||||
@ -264,11 +257,7 @@ export class AnnotationActionsService {
|
||||
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, viewer, annotations[0]);
|
||||
});
|
||||
},
|
||||
onClick: () => this._ngZone.run(() => this.resize(null, viewer, annotations[0])),
|
||||
});
|
||||
}
|
||||
|
||||
@ -278,11 +267,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/edit.svg'),
|
||||
title: this._translateService.instant('annotation-actions.edit-reason.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.changeLegalBasis(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.changeLegalBasis(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -292,11 +280,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/thumb-down.svg'),
|
||||
title: this._translateService.instant('annotation-actions.recategorize-image'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.recategorizeImages(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.recategorizeImages(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -309,11 +296,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/remove-from-dict.svg'),
|
||||
title: this._translateService.instant('annotation-actions.remove-annotation.remove-from-dict'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.removeOrSuggestRemoveAnnotation(null, annotations, file, true, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.removeOrSuggestRemoveAnnotation(null, annotations, true, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -323,11 +309,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
||||
title: this._translateService.instant('annotation-actions.accept-recommendation.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.convertRecommendationToAnnotation(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.convertRecommendationToAnnotation(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -337,11 +322,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/check.svg'),
|
||||
title: this._translateService.instant('annotation-actions.accept-suggestion.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.acceptSuggestion(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.acceptSuggestion(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -351,11 +335,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/undo.svg'),
|
||||
title: this._translateService.instant('annotation-actions.undo'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.undoDirectAction(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.undoDirectAction(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -365,11 +348,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/thumb-down.svg'),
|
||||
title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.markAsFalsePositive(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.markAsFalsePositive(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -379,11 +361,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/thumb-up.svg'),
|
||||
title: this._translateService.instant('annotation-actions.force-redaction.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.forceAnnotation(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.forceAnnotation(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -393,11 +374,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/thumb-up.svg'),
|
||||
title: this._translateService.instant('annotation-actions.force-hint.label'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.forceAnnotation(null, annotations, file, annotationsChanged, true);
|
||||
});
|
||||
},
|
||||
this.forceAnnotation(null, annotations, annotationsChanged, true);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -407,11 +387,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/close.svg'),
|
||||
title: this._translateService.instant('annotation-actions.reject-suggestion'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.rejectSuggestion(null, annotations, file, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.rejectSuggestion(null, annotations, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -424,11 +403,10 @@ export class AnnotationActionsService {
|
||||
type: 'actionButton',
|
||||
img: this._convertPath('/assets/icons/general/trash.svg'),
|
||||
title: this._translateService.instant('annotation-actions.remove-annotation.only-here'),
|
||||
onClick: () => {
|
||||
onClick: () =>
|
||||
this._ngZone.run(() => {
|
||||
this.removeOrSuggestRemoveAnnotation(null, annotations, file, false, annotationsChanged);
|
||||
});
|
||||
},
|
||||
this.removeOrSuggestRemoveAnnotation(null, annotations, false, annotationsChanged);
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
@ -460,11 +438,11 @@ export class AnnotationActionsService {
|
||||
acceptResize(
|
||||
$event: MouseEvent,
|
||||
viewer: WebViewerInstance,
|
||||
file: File,
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
annotationsChanged?: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
const data = { dossier: this._dossier(file) };
|
||||
const data = { dossier: this._dossier };
|
||||
const fileId = this._screenStateService.fileId;
|
||||
this._dialogService.openDialog('resizeAnnotation', $event, data, async (result: { comment: string }) => {
|
||||
const textAndPositions = await this._extractTextAndPositions(viewer, annotationWrapper.id);
|
||||
const text =
|
||||
@ -478,7 +456,7 @@ export class AnnotationActionsService {
|
||||
};
|
||||
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.resizeOrSuggestToResize(annotationWrapper, file, resizeRequest),
|
||||
this._manualAnnotationService.resizeOrSuggestToResize(annotationWrapper, data.dossier.dossierId, fileId, resizeRequest),
|
||||
annotationWrapper,
|
||||
annotationsChanged,
|
||||
);
|
||||
@ -503,10 +481,6 @@ export class AnnotationActionsService {
|
||||
annotationsChanged.emit(annotationWrapper);
|
||||
}
|
||||
|
||||
private _dossier(file: File): Dossier {
|
||||
return this._dossiersService.find(file.dossierId);
|
||||
}
|
||||
|
||||
private _processObsAndEmit(
|
||||
obs: Observable<unknown>,
|
||||
annotation: AnnotationWrapper,
|
||||
@ -541,7 +515,6 @@ export class AnnotationActionsService {
|
||||
private _markAsFalsePositive(
|
||||
$event: MouseEvent,
|
||||
annotation: AnnotationWrapper,
|
||||
file: File,
|
||||
text: string,
|
||||
annotationsChanged: EventEmitter<AnnotationWrapper>,
|
||||
) {
|
||||
@ -554,8 +527,13 @@ export class AnnotationActionsService {
|
||||
falsePositiveRequest.positions = annotation.positions;
|
||||
falsePositiveRequest.addToDictionary = true;
|
||||
falsePositiveRequest.comment = { text: 'False Positive' };
|
||||
const { dossierId, fileId } = this._screenStateService;
|
||||
|
||||
this._processObsAndEmit(this._manualAnnotationService.addAnnotation(falsePositiveRequest, file), annotation, annotationsChanged);
|
||||
this._processObsAndEmit(
|
||||
this._manualAnnotationService.addAnnotation(falsePositiveRequest, dossierId, fileId),
|
||||
annotation,
|
||||
annotationsChanged,
|
||||
);
|
||||
}
|
||||
|
||||
private _convertPath(path: string): string {
|
||||
|
||||
@ -1,14 +1,31 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { Dossier, File } from '@red/domain';
|
||||
import { DossiersService } from '../../../../../services/entity-services/dossiers.service';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { FilesMapService } from '../../../../../services/entity-services/files-map.service';
|
||||
|
||||
@Injectable()
|
||||
export class FilePreviewStateService {
|
||||
readonly fileData$: Observable<FileDataModel>;
|
||||
readonly file$: Observable<File>;
|
||||
readonly dossier$: Observable<Dossier>;
|
||||
readonly dossierId: string;
|
||||
readonly fileId: string;
|
||||
private readonly _fileData$ = new BehaviorSubject<FileDataModel>(undefined);
|
||||
|
||||
constructor() {
|
||||
constructor(
|
||||
private readonly _dossiersService: DossiersService,
|
||||
private readonly _filesMapService: FilesMapService,
|
||||
activatedRoute: ActivatedRoute,
|
||||
) {
|
||||
this.dossierId = activatedRoute.snapshot.paramMap.get('dossierId');
|
||||
this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId);
|
||||
this.fileId = activatedRoute.snapshot.paramMap.get('fileId');
|
||||
|
||||
this.fileData$ = this._fileData$.asObservable();
|
||||
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
|
||||
}
|
||||
|
||||
get fileData(): FileDataModel {
|
||||
@ -18,4 +35,8 @@ export class FilePreviewStateService {
|
||||
set fileData(fileDataModel: FileDataModel) {
|
||||
this._fileData$.next(fileDataModel);
|
||||
}
|
||||
|
||||
get file(): Promise<File> {
|
||||
return firstValueFrom(this.file$);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import { Injectable, Injector } from '@angular/core';
|
||||
import { AppStateService } from '@state/app-state.service';
|
||||
import {
|
||||
Dossier,
|
||||
File,
|
||||
IAddRedactionRequest,
|
||||
IApproveRequest,
|
||||
IImageRecategorizationRequest,
|
||||
@ -57,14 +56,15 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
|
||||
_makeRequest(
|
||||
mode: AnnotationActionMode,
|
||||
file: File,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
body: any,
|
||||
secondParam: any = null,
|
||||
modifyDictionary = false,
|
||||
): Observable<unknown> {
|
||||
const obs = !secondParam
|
||||
? this[this.CONFIG[mode]](body, file.dossierId, file.id)
|
||||
: this[this.CONFIG[mode]](body, secondParam, file.dossierId, file.id);
|
||||
? this[this.CONFIG[mode]](body, dossierId, fileId)
|
||||
: this[this.CONFIG[mode]](body, secondParam, dossierId, fileId);
|
||||
|
||||
return obs.pipe(
|
||||
tap({
|
||||
@ -74,7 +74,8 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
this._toaster.error(this._getMessage(mode, modifyDictionary, true, isConflict), {
|
||||
error,
|
||||
params: {
|
||||
dictionaryName: this._appStateService.getDictionary(body.type, this._dossier(file).dossierTemplateId).label,
|
||||
dictionaryName: this._appStateService.getDictionary(body.type, this._dossier(dossierId).dossierTemplateId)
|
||||
.label,
|
||||
content: body.value,
|
||||
},
|
||||
positionClass: 'toast-file-preview',
|
||||
@ -96,7 +97,7 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
return super.delete({}, url);
|
||||
}
|
||||
|
||||
addRecommendation(annotation: AnnotationWrapper, file: File, comment = { text: 'Accepted Recommendation' }) {
|
||||
addRecommendation(annotation: AnnotationWrapper, dossierId: string, fileId: string, comment = { text: 'Accepted Recommendation' }) {
|
||||
const manualRedactionEntry: IAddRedactionRequest = {};
|
||||
manualRedactionEntry.addToDictionary = true;
|
||||
// set the ID as reason, so we can hide the suggestion
|
||||
@ -105,76 +106,89 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
manualRedactionEntry.positions = annotation.positions;
|
||||
manualRedactionEntry.type = annotation.recommendationType;
|
||||
manualRedactionEntry.comment = comment;
|
||||
return this.addAnnotation(manualRedactionEntry, file);
|
||||
return this.addAnnotation(manualRedactionEntry, dossierId, fileId);
|
||||
}
|
||||
|
||||
// /manualRedaction/request/legalBasis
|
||||
changeLegalBasis(annotationId: string, file: File, section: string, value: string, legalBasis: string, comment?: string) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file))
|
||||
changeLegalBasis(
|
||||
annotationId: string,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
section: string,
|
||||
value: string,
|
||||
legalBasis: string,
|
||||
comment?: string,
|
||||
) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(dossierId))
|
||||
? 'change-legal-basis'
|
||||
: 'request-change-legal-basis';
|
||||
return this._makeRequest(mode, file, { annotationId, legalBasis, comment, section, value });
|
||||
return this._makeRequest(mode, dossierId, fileId, { annotationId, legalBasis, comment, section, value });
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/legalBasisChange
|
||||
|
||||
// /manualRedaction/request/recategorize
|
||||
recategorizeImg(annotationId: string, file: File, type: string, comment: string) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file))
|
||||
recategorizeImg(annotationId: string, dossierId: string, fileId: string, type: string, comment: string) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(dossierId))
|
||||
? 'recategorize-image'
|
||||
: 'request-image-recategorization';
|
||||
return this._makeRequest(mode, file, { annotationId, type, comment });
|
||||
return this._makeRequest(mode, dossierId, fileId, { annotationId, type, comment });
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/recategorize
|
||||
|
||||
// /manualRedaction/request/add
|
||||
addAnnotation(manualRedactionEntry: IAddRedactionRequest, file: File) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) ? 'add' : 'suggest';
|
||||
return this._makeRequest(mode, file, manualRedactionEntry, null, manualRedactionEntry.addToDictionary);
|
||||
addAnnotation(manualRedactionEntry: IAddRedactionRequest, dossierId: string, fileId: string) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(dossierId)) ? 'add' : 'suggest';
|
||||
return this._makeRequest(mode, dossierId, fileId, manualRedactionEntry, null, manualRedactionEntry.addToDictionary);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/add
|
||||
|
||||
// /manualRedaction/request/force
|
||||
force(request: ILegalBasisChangeRequest, file: File) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file))
|
||||
force(request: ILegalBasisChangeRequest, dossierId: string, fileId: string) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(dossierId))
|
||||
? 'force-redaction'
|
||||
: 'request-force-redaction';
|
||||
return this._makeRequest(mode, file, request);
|
||||
return this._makeRequest(mode, dossierId, fileId, request);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/redaction/force
|
||||
|
||||
// /manualRedaction/approve
|
||||
approve(annotationId: string, file: File, addToDictionary: boolean = false) {
|
||||
approve(annotationId: string, dossierId: string, fileId: string, addToDictionary: boolean = false) {
|
||||
// for only here - approve the request
|
||||
return this._makeRequest('approve', file, { addOrRemoveFromDictionary: addToDictionary }, annotationId, addToDictionary);
|
||||
return this._makeRequest(
|
||||
'approve',
|
||||
dossierId,
|
||||
fileId,
|
||||
{ addOrRemoveFromDictionary: addToDictionary },
|
||||
annotationId,
|
||||
addToDictionary,
|
||||
);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
|
||||
undoRequest(annotationWrapper: AnnotationWrapper, file: File) {
|
||||
return this._makeRequest('undo', file, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
|
||||
}
|
||||
|
||||
// /manualRedaction/undo
|
||||
declineOrRemoveRequest(annotationWrapper: AnnotationWrapper, file: File) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) ? 'decline' : 'undo';
|
||||
return this._makeRequest(mode, file, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
|
||||
undoRequest(annotationWrapper: AnnotationWrapper, dossierId: string, fileId: string) {
|
||||
return this._makeRequest('undo', dossierId, fileId, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
// /manualRedaction/decline/remove
|
||||
declineOrRemoveRequest(annotationWrapper: AnnotationWrapper, dossierId: string, fileId: string) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(dossierId)) ? 'decline' : 'undo';
|
||||
return this._makeRequest(mode, dossierId, fileId, annotationWrapper.id, null, annotationWrapper.isModifyDictionary);
|
||||
}
|
||||
|
||||
// /manualRedaction/request/resize/
|
||||
resizeOrSuggestToResize(annotationWrapper: AnnotationWrapper, file: File, resizeRequest: IResizeRequest) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) ? 'resize' : 'request-resize';
|
||||
return this._makeRequest(mode, file, resizeRequest);
|
||||
resizeOrSuggestToResize(annotationWrapper: AnnotationWrapper, dossierId: string, fileId: string, resizeRequest: IResizeRequest) {
|
||||
const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(dossierId)) ? 'resize' : 'request-resize';
|
||||
return this._makeRequest(mode, dossierId, fileId, resizeRequest);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
@ -183,7 +197,8 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
// /manualRedaction/request/remove/
|
||||
removeOrSuggestRemoveAnnotation(
|
||||
annotationWrapper: AnnotationWrapper,
|
||||
file: File,
|
||||
dossierId: string,
|
||||
fileId: string,
|
||||
removeFromDictionary: boolean = false,
|
||||
comment: string,
|
||||
) {
|
||||
@ -191,7 +206,7 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
body: any,
|
||||
removeDict = false;
|
||||
|
||||
if (this._permissionsService.isApprover(this._dossier(file))) {
|
||||
if (this._permissionsService.isApprover(this._dossier(dossierId))) {
|
||||
// if it was something manual simply decline the existing request
|
||||
mode = 'remove';
|
||||
body = {
|
||||
@ -210,7 +225,7 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
removeDict = removeFromDictionary;
|
||||
}
|
||||
|
||||
return this._makeRequest(mode, file, body, null, removeDict);
|
||||
return this._makeRequest(mode, dossierId, fileId, body, null, removeDict);
|
||||
}
|
||||
|
||||
// this wraps
|
||||
@ -353,8 +368,8 @@ export class ManualAnnotationService extends GenericService<IManualAddResponse>
|
||||
return this._post(body, url);
|
||||
}
|
||||
|
||||
private _dossier(file: File): Dossier {
|
||||
return this._dossiersService.find(file.dossierId);
|
||||
private _dossier(dossierId: string): Dossier {
|
||||
return this._dossiersService.find(dossierId);
|
||||
}
|
||||
|
||||
private _getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { forkJoin, Observable, of } from 'rxjs';
|
||||
import { catchError, map, tap } from 'rxjs/operators';
|
||||
import { forkJoin, Observable, of, switchMap } from 'rxjs';
|
||||
import { catchError, map, take, tap } from 'rxjs/operators';
|
||||
import { FileDataModel } from '@models/file/file-data.model';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { File, IRedactionLog, IViewedPage } from '@red/domain';
|
||||
@ -32,19 +32,23 @@ export class PdfViewerDataService {
|
||||
);
|
||||
}
|
||||
|
||||
loadDataFor(file: File): Observable<FileDataModel> {
|
||||
const fileData = this._stateService.fileData;
|
||||
const blob$ = fileData?.file.cacheIdentifier === file.cacheIdentifier ? of(fileData.blob$.value) : this.downloadOriginalFile(file);
|
||||
const redactionLog$ = this.loadRedactionLogFor(file.dossierId, file.fileId);
|
||||
const viewedPages$ = this.getViewedPagesFor(file);
|
||||
loadDataFor(newFile: File): Observable<FileDataModel> {
|
||||
const oldBlob$ = this._stateService.fileData?.blob$;
|
||||
const blob$ = this._stateService.file$.pipe(
|
||||
map(file => file.cacheIdentifier === newFile.cacheIdentifier && oldBlob$),
|
||||
switchMap(isSame => (isSame ? oldBlob$ : this.downloadOriginalFile(newFile))),
|
||||
take(1),
|
||||
);
|
||||
const redactionLog$ = this.loadRedactionLogFor(newFile.dossierId, newFile.fileId);
|
||||
const viewedPages$ = this.getViewedPagesFor(newFile);
|
||||
|
||||
const dossier = this._dossiersService.find(file.dossierId);
|
||||
const dossier = this._dossiersService.find(newFile.dossierId);
|
||||
|
||||
return forkJoin([blob$, redactionLog$, viewedPages$]).pipe(
|
||||
map(
|
||||
(data: [blob: Blob, redactionLog: IRedactionLog, viewedPages: IViewedPage[]]) =>
|
||||
new FileDataModel(
|
||||
file,
|
||||
newFile,
|
||||
...data,
|
||||
this._appStateService.dictionaryData[dossier.dossierTemplateId],
|
||||
this._userPreferenceService.areDevFeaturesEnabled,
|
||||
@ -60,7 +64,7 @@ export class PdfViewerDataService {
|
||||
return of([]);
|
||||
}
|
||||
|
||||
downloadOriginalFile(file: File): Observable<any> {
|
||||
downloadOriginalFile(file: File): Observable<Blob> {
|
||||
return this._fileManagementService.downloadOriginalFile(file.dossierId, file.fileId, 'body', true, file.cacheIdentifier);
|
||||
}
|
||||
}
|
||||
|
||||
@ -138,7 +138,7 @@ export class PdfViewerUtils {
|
||||
this._annotationManager.deselectAnnotations(ann);
|
||||
}
|
||||
|
||||
private _navigateToPage(pageNumber) {
|
||||
private _navigateToPage(pageNumber: number) {
|
||||
if (this._currentInternalPage !== pageNumber) {
|
||||
this._documentViewer.displayPageLocation(pageNumber, 0, 0);
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { File } from '@red/domain';
|
||||
import { filter, startWith } from 'rxjs/operators';
|
||||
import { shareLast } from '@iqser/common-ui';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class FilesMapService {
|
||||
@ -37,7 +38,7 @@ export class FilesMapService {
|
||||
return entities.forEach(entity => this._entityChanged$.next(entity));
|
||||
}
|
||||
|
||||
const changedEntities = [];
|
||||
const changedEntities: File[] = [];
|
||||
const deletedEntities = this.get(key).filter(oldEntity => !entities.find(newEntity => newEntity.id === oldEntity.id));
|
||||
|
||||
// Keep old object references for unchanged entities
|
||||
@ -77,6 +78,7 @@ export class FilesMapService {
|
||||
return this._entityChanged$.pipe(
|
||||
filter(entity => entity.id === entityId),
|
||||
startWith(this.get(key, entityId)),
|
||||
shareLast(),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -18,7 +18,8 @@ export class PermissionsService {
|
||||
return this.isApprover(dossier);
|
||||
}
|
||||
|
||||
canEditFileAttributes(dossier: Dossier, file: File): boolean {
|
||||
canEditFileAttributes(file: File): boolean {
|
||||
const dossier = this._getDossier(file);
|
||||
return ((file.isUnderReview || file.isNew) && this.isDossierMember(dossier)) || (file.isUnderApproval && this.isApprover(dossier));
|
||||
}
|
||||
|
||||
|
||||
@ -23,8 +23,6 @@ export function handleFilterDelta(oldFilters: INestedFilter[], newFilters: INest
|
||||
}
|
||||
}
|
||||
|
||||
console.log(newFiltersDelta);
|
||||
|
||||
for (const key of Object.keys(newFiltersDelta)) {
|
||||
const foundFilter = allFilters.find(f => f.id === key);
|
||||
if (foundFilter) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user