diff --git a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html index 661024ca0..f2b1822f0 100644 --- a/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/annotation-actions/annotation-actions.component.html @@ -32,7 +32,7 @@ > (); annotationPermissions: AnnotationPermissions; constructor( readonly annotationActionsService: AnnotationActionsService, private readonly _permissionsService: PermissionsService, + private readonly _dossiersService: DossiersService, private readonly _userService: UserService, ) {} @@ -67,23 +69,21 @@ export class AnnotationActionsComponent implements OnChanges { return this.annotations?.length === 1 && this.annotations?.[0].resizing; } + private get _dossier(): Dossier { + return this._dossiersService.find(this.file.dossierId); + } + ngOnChanges(): void { this._setPermissions(); } suggestRemoveAnnotations($event, removeFromDict: boolean) { $event.stopPropagation(); - this.annotationActionsService.suggestRemoveAnnotation( - $event, - this.annotations, - this.dossier, - removeFromDict, - this.annotationsChanged, - ); + this.annotationActionsService.suggestRemoveAnnotation($event, this.annotations, this.file, removeFromDict, this.annotationsChanged); } markAsFalsePositive($event) { - this.annotationActionsService.markAsFalsePositive($event, this.annotations, this.dossier, this.annotationsChanged); + this.annotationActionsService.markAsFalsePositive($event, this.annotations, this.file, this.annotationsChanged); } hideAnnotation($event: MouseEvent) { @@ -105,7 +105,7 @@ export class AnnotationActionsComponent implements OnChanges { } acceptResize($event: MouseEvent) { - this.annotationActionsService.acceptResize($event, this.viewer, this.dossier, this.annotations[0], this.annotationsChanged); + this.annotationActionsService.acceptResize($event, this.viewer, this.file, this.annotations[0], this.annotationsChanged); } cancelResize($event: MouseEvent) { @@ -114,7 +114,7 @@ export class AnnotationActionsComponent implements OnChanges { private _setPermissions() { this.annotationPermissions = AnnotationPermissions.forUser( - this._permissionsService.isApprover(this.dossier), + this._permissionsService.isApprover(this._dossier), this._userService.currentUser, this.annotations, ); diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html index c6463bd9e..6fdbcb1ad 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.html @@ -56,7 +56,7 @@ [alwaysVisible]="true" [annotations]="selectedAnnotations" [canPerformAnnotationActions]="!isReadOnly" - [dossier]="dossier" + [file]="file" [viewer]="viewer" buttonType="primary" tooltipPosition="above" diff --git a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts index 1741d5210..23ca8210f 100644 --- a/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/file-workload/file-workload.component.ts @@ -19,7 +19,7 @@ import { PermissionsService } from '@services/permissions.service'; import { WebViewerInstance } from '@pdftron/webviewer'; import { BehaviorSubject, combineLatest, Observable } from 'rxjs'; import { map } from 'rxjs/operators'; -import { Dossier, File, IViewedPage } from '@red/domain'; +import { File, IViewedPage } from '@red/domain'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -41,7 +41,6 @@ export class FileWorkloadComponent { @Input() dialogRef: MatDialogRef; @Input() viewedPages: IViewedPage[]; @Input() @Required() file!: File; - @Input() @Required() dossier!: Dossier; @Input() hideSkipped: boolean; @Input() showExcludedPages: boolean; @Input() annotationActionsTemplate: TemplateRef; diff --git a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts index 2b98ab406..d835e71af 100644 --- a/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/modules/dossier/components/pdf-viewer/pdf-viewer.component.ts @@ -466,12 +466,7 @@ export class PdfViewerComponent implements OnInit, OnChanges { } this.instance.UI.annotationPopup.add( - this._annotationActionsService.getViewerAvailableActions( - this.instance, - this._dossier, - annotationWrappers, - this.annotationsChanged, - ), + this._annotationActionsService.getViewerAvailableActions(this.instance, this.file, annotationWrappers, this.annotationsChanged), ); } diff --git a/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts index bafec82ce..55ce35c2b 100644 --- a/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/dossier/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts @@ -7,7 +7,8 @@ import { ManualAnnotationService } from '../../services/manual-annotation.servic 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, IAddRedactionRequest } from '@red/domain'; +import { Dictionary, Dossier, File, IAddRedactionRequest } from '@red/domain'; +import { DossiersService } from '@services/entity-services/dossiers.service'; export interface LegalBasisOption { label?: string; @@ -30,16 +31,20 @@ export class ManualAnnotationDialogComponent implements OnInit { redactionDictionaries: Dictionary[] = []; legalOptions: LegalBasisOption[] = []; + private readonly _dossier: Dossier; + constructor( private readonly _appStateService: AppStateService, private readonly _formBuilder: FormBuilder, private readonly _justificationsService: JustificationsService, private readonly _manualAnnotationService: ManualAnnotationService, private readonly _permissionsService: PermissionsService, + private readonly _dossiersService: DossiersService, public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; dossier: Dossier }, + @Inject(MAT_DIALOG_DATA) public data: { manualRedactionEntryWrapper: ManualRedactionEntryWrapper; file: File }, ) { - this.isDocumentAdmin = this._permissionsService.isApprover(this.data.dossier); + this._dossier = this._dossiersService.find(this.data.file.dossierId); + this.isDocumentAdmin = this._permissionsService.isApprover(this._dossier); this.isFalsePositiveRequest = this.data.manualRedactionEntryWrapper.type === 'FALSE_POSITIVE'; this.isDictionaryRequest = this.data.manualRedactionEntryWrapper.type === 'DICTIONARY' || this.isFalsePositiveRequest; @@ -49,32 +54,8 @@ export class ManualAnnotationDialogComponent implements OnInit { this.redactionDictionaries = this._redactionDictionaries; } - private _getForm(): FormGroup { - return this._formBuilder.group({ - reason: this.isDictionaryRequest ? [null] : [null, Validators.required], - dictionary: this.isDictionaryRequest - ? [this.isFalsePositiveRequest ? 'false_positive' : null, Validators.required] - : ['manual', Validators.required], - comment: this.isDocumentAdmin ? [null] : [null, Validators.required], - }); - } - - private get _redactionDictionaries(): Dictionary[] { - const redactionDictionaries: Dictionary[] = []; - - for (const key of Object.keys(this._appStateService.dictionaryData[this.data.dossier.dossierTemplateId])) { - const dictionaryData = this._appStateService.getDictionary(key, this.data.dossier.dossierTemplateId); - if (!dictionaryData.virtual && dictionaryData.addToDictionaryAction) { - redactionDictionaries.push(dictionaryData); - } - } - redactionDictionaries.sort((a, b) => a.label.localeCompare(b.label)); - - return redactionDictionaries; - } - get title() { - return this._manualAnnotationService.getTitle(this.data.manualRedactionEntryWrapper.type, this.data.dossier); + return this._manualAnnotationService.getTitle(this.data.manualRedactionEntryWrapper.type, this._dossier); } get displayedDictionaryLabel() { @@ -85,8 +66,23 @@ export class ManualAnnotationDialogComponent implements OnInit { return null; } + private get _redactionDictionaries(): Dictionary[] { + const redactionDictionaries: Dictionary[] = []; + const dossier = this._dossier; + + for (const key of Object.keys(this._appStateService.dictionaryData[dossier.dossierTemplateId])) { + const dictionaryData = this._appStateService.getDictionary(key, dossier.dossierTemplateId); + if (!dictionaryData.virtual && dictionaryData.addToDictionaryAction) { + redactionDictionaries.push(dictionaryData); + } + } + redactionDictionaries.sort((a, b) => a.label.localeCompare(b.label)); + + return redactionDictionaries; + } + async ngOnInit() { - const data = await this._justificationsService.getForDossierTemplate(this.data.dossier.dossierTemplateId).toPromise(); + const data = await this._justificationsService.getForDossierTemplate(this._dossier.dossierTemplateId).toPromise(); this.legalOptions = data.map(lbm => ({ legalBasis: lbm.reason, @@ -99,12 +95,10 @@ export class ManualAnnotationDialogComponent implements OnInit { handleAddRedaction() { this._enhanceManualRedaction(this.data.manualRedactionEntryWrapper.manualRedactionEntry); - this._manualAnnotationService - .addAnnotation(this.data.manualRedactionEntryWrapper.manualRedactionEntry, this.data.dossier) - .subscribe( - response => this.dialogRef.close(new ManualAnnotationResponse(this.data.manualRedactionEntryWrapper, response)), - () => this.dialogRef.close(), - ); + this._manualAnnotationService.addAnnotation(this.data.manualRedactionEntryWrapper.manualRedactionEntry, this.data.file).subscribe( + response => this.dialogRef.close(new ManualAnnotationResponse(this.data.manualRedactionEntryWrapper, response)), + () => this.dialogRef.close(), + ); } format(value: string) { @@ -115,6 +109,16 @@ export class ManualAnnotationDialogComponent implements OnInit { ); } + private _getForm(): FormGroup { + return this._formBuilder.group({ + reason: this.isDictionaryRequest ? [null] : [null, Validators.required], + dictionary: this.isDictionaryRequest + ? [this.isFalsePositiveRequest ? 'false_positive' : null, Validators.required] + : ['manual', Validators.required], + comment: this.isDocumentAdmin ? [null] : [null, Validators.required], + }); + } + private _enhanceManualRedaction(addRedactionRequest: IAddRedactionRequest) { const legalOption: LegalBasisOption = this.redactionForm.get('reason').value; addRedactionRequest.type = this.redactionForm.get('dictionary').value; diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html index 99bb129ec..fbeef0359 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.html @@ -1,222 +1,223 @@ -
-
+ - - - + + + - - - - - - {{ filter.id | humanize: false }} - - + + + + + + {{ filter.id | humanize: false }} + + +
diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts index ead9075ae..3f03c5458 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview-screen.component.ts @@ -51,7 +51,7 @@ import { FileActionsComponent } from '../../shared/components/file-actions/file- 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 { filter, switchMap, switchMapTo, tap } from 'rxjs/operators'; +import { filter, switchMapTo, tap } from 'rxjs/operators'; import { FilesMapService } from '@services/entity-services/files-map.service'; import { WatermarkService } from '@shared/services/watermark.service'; import Annotation = Core.Annotations.Annotation; @@ -99,11 +99,10 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _filterTemplate: TemplateRef; constructor( - readonly appStateService: AppStateService, - readonly dossiersService: DossiersService, readonly permissionsService: PermissionsService, readonly userPreferenceService: UserPreferenceService, - readonly userService: UserService, + private readonly _appStateService: AppStateService, + private readonly _userService: UserService, private readonly _watermarkService: WatermarkService, private readonly _changeDetectorRef: ChangeDetectorRef, private readonly _activatedRoute: ActivatedRoute, @@ -122,7 +121,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _translateService: TranslateService, private readonly _filesMapService: FilesMapService, private readonly _dossiersService: DossiersService, - private readonly _userPreferenceService: UserPreferenceService, ) { super(); this.dossierId = _activatedRoute.snapshot.paramMap.get('dossierId'); @@ -255,7 +253,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni async ngOnAttach(previousRoute: ActivatedRouteSnapshot): Promise { const file = this._filesMapService.get(this.dossierId, this.fileId); if (!file.canBeOpened) { - return this._router.navigate([this.dossiersService.find(this.dossierId)?.routerLink]); + return this._router.navigate([this._dossiersService.find(this.dossierId)?.routerLink]); } await this.ngOnInit(); @@ -264,7 +262,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni async ngOnInit(): Promise { this._loadingService.start(); - await this._userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId); + await this.userPreferenceService.saveLastOpenedFileForDossier(this.dossierId, this.fileId); await this._loadFileData(); this._updateCanPerformActions(); this._subscribeToFileUpdates(); @@ -294,8 +292,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni const processStartTime = new Date().getTime(); const dossier = this._dossiersService.find(this.dossierId); const newAnnotationsData = this.fileData.getAnnotations( - this.appStateService.dictionaryData[dossier.dossierTemplateId], - this.userService.currentUser, + this._appStateService.dictionaryData[dossier.dossierTemplateId], + this._userService.currentUser, this.viewMode, this.userPreferenceService.areDevFeaturesEnabled, ); @@ -356,11 +354,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni openManualAnnotationDialog(manualRedactionEntryWrapper: ManualRedactionEntryWrapper) { this._ngZone.run(() => { - const dossier = this._dossiersService.find(this.dossierId); + const file = this._filesMapService.get(this.dossierId, this.fileId); this.dialogRef = this._dialogService.openDialog( 'manualAnnotation', null, - { manualRedactionEntryWrapper, dossier }, + { manualRedactionEntryWrapper, file }, async (response: ManualAnnotationResponse) => { if (response?.annotationId) { const annotation = this._instance.Core.annotationManager.getAnnotationById( @@ -490,7 +488,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni async assignReviewer(file: File, user: User | string) { const reviewerId = typeof user === 'string' ? user : user?.id; - const reviewerName = this.userService.getNameForId(reviewerId); + const reviewerName = this._userService.getNameForId(reviewerId); const { dossierId, fileId, filename } = file; this._loadingService.start(); @@ -547,7 +545,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni await clearStamps(document, pdfNet, allPages); if (this.viewMode === 'REDACTED') { - const dossier = this.dossiersService.find(this.dossierId); + const dossier = this._dossiersService.find(this.dossierId); if (dossier.watermarkPreviewEnabled) { await this._stampPreview(document, dossier.dossierTemplateId); } @@ -633,7 +631,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } if (file.isError) { - await this._router.navigate([this.dossiersService.find(this.dossierId).routerLink]); + await this._router.navigate([this._dossiersService.find(this.dossierId).routerLink]); } } diff --git a/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts index ba57ebe6f..816c7f418 100644 --- a/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/annotation-actions.service.ts @@ -10,10 +10,11 @@ import { DossiersDialogService } from './dossiers-dialog.service'; import { BASE_HREF } from '../../../tokens'; import { UserService } from '@services/user.service'; import { Core, WebViewerInstance } from '@pdftron/webviewer'; -import { Dossier, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain'; +import { Dossier, File, IAddRedactionRequest, ILegalBasisChangeRequest, IRectangle, IResizeRequest } from '@red/domain'; import { toPosition } from '../utils/pdf-calculation.utils'; import { AnnotationDrawService } from './annotation-draw.service'; import { translateQuads } from '../../../utils'; +import { DossiersService } from '@services/entity-services/dossiers.service'; import Annotation = Core.Annotations.Annotation; @Injectable() @@ -27,18 +28,19 @@ export class AnnotationActionsService { private readonly _translateService: TranslateService, private readonly _dialogService: DossiersDialogService, private readonly _annotationDrawService: AnnotationDrawService, + private readonly _dossiersService: DossiersService, ) {} acceptSuggestion( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { $event?.stopPropagation(); annotations.forEach(annotation => { this._processObsAndEmit( - this._manualAnnotationService.approve(annotation.id, dossier, annotation.isModifyDictionary), + this._manualAnnotationService.approve(annotation.id, file, annotation.isModifyDictionary), annotation, annotationsChanged, ); @@ -48,26 +50,18 @@ export class AnnotationActionsService { rejectSuggestion( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { $event?.stopPropagation(); annotations.forEach(annotation => { - this._processObsAndEmit( - this._manualAnnotationService.declineOrRemoveRequest(annotation, dossier), - annotation, - annotationsChanged, - ); + this._processObsAndEmit(this._manualAnnotationService.declineOrRemoveRequest(annotation, file), annotation, annotationsChanged); }); } - forceRedaction( - $event: MouseEvent, - annotations: AnnotationWrapper[], - dossier: Dossier, - annotationsChanged: EventEmitter, - ) { - this._dialogService.openDialog('forceRedaction', $event, { dossier }, (request: ILegalBasisChangeRequest) => { + forceRedaction($event: MouseEvent, annotations: AnnotationWrapper[], file: File, annotationsChanged: EventEmitter) { + const data = { dossier: this._dossier(file) }; + this._dialogService.openDialog('forceRedaction', $event, data, (request: ILegalBasisChangeRequest) => { annotations.forEach(annotation => { this._processObsAndEmit( this._manualAnnotationService.force( @@ -75,7 +69,7 @@ export class AnnotationActionsService { ...request, annotationId: annotation.id, }, - dossier, + file, ), annotation, annotationsChanged, @@ -87,17 +81,17 @@ export class AnnotationActionsService { changeLegalBasis( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { this._dialogService.openDialog( 'changeLegalBasis', $event, - { annotations, dossier }, + { annotations, dossier: this._dossier(file) }, (data: { comment: string; legalBasis: string }) => { annotations.forEach(annotation => { this._processObsAndEmit( - this._manualAnnotationService.changeLegalBasis(annotation.annotationId, dossier, data.legalBasis, data.comment), + this._manualAnnotationService.changeLegalBasis(annotation.annotationId, file, data.legalBasis, data.comment), annotation, annotationsChanged, ); @@ -109,20 +103,15 @@ export class AnnotationActionsService { suggestRemoveAnnotation( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, removeFromDictionary: boolean, annotationsChanged: EventEmitter, ) { - const data = { annotationsToRemove: annotations, removeFromDictionary, dossier }; + const data = { annotationsToRemove: annotations, removeFromDictionary, dossier: this._dossier(file) }; this._dialogService.openDialog('removeAnnotations', $event, data, (result: { comment: string }) => { annotations.forEach(annotation => { this._processObsAndEmit( - this._manualAnnotationService.removeOrSuggestRemoveAnnotation( - annotation, - dossier, - removeFromDictionary, - result.comment, - ), + this._manualAnnotationService.removeOrSuggestRemoveAnnotation(annotation, file, removeFromDictionary, result.comment), annotation, annotationsChanged, ); @@ -133,24 +122,25 @@ export class AnnotationActionsService { markAsFalsePositive( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { annotations.forEach(annotation => { - this._markAsFalsePositive($event, annotation, dossier, this._getFalsePositiveText(annotation), annotationsChanged); + this._markAsFalsePositive($event, annotation, file, this._getFalsePositiveText(annotation), annotationsChanged); }); } recategorizeImages( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { - this._dialogService.openDialog('recategorizeImage', $event, { annotations, dossier }, (data: { type: string; comment: string }) => { + const data = { annotations, dossier: this._dossier(file) }; + this._dialogService.openDialog('recategorizeImage', $event, data, (res: { type: string; comment: string }) => { annotations.forEach(annotation => { this._processObsAndEmit( - this._manualAnnotationService.recategorizeImg(annotation.annotationId, dossier, data.type, data.comment), + this._manualAnnotationService.recategorizeImg(annotation.annotationId, file, res.type, res.comment), annotation, annotationsChanged, ); @@ -161,36 +151,37 @@ export class AnnotationActionsService { undoDirectAction( $event: MouseEvent, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { $event?.stopPropagation(); annotations.forEach(annotation => { - this._processObsAndEmit(this._manualAnnotationService.undoRequest(annotation, dossier), annotation, annotationsChanged); + this._processObsAndEmit(this._manualAnnotationService.undoRequest(annotation, file), annotation, annotationsChanged); }); } convertRecommendationToAnnotation( $event: any, annotations: AnnotationWrapper[], - dossier: Dossier, + file: File, annotationsChanged: EventEmitter, ) { $event?.stopPropagation(); annotations.forEach(annotation => { - this._processObsAndEmit(this._manualAnnotationService.addRecommendation(annotation, dossier), annotation, annotationsChanged); + this._processObsAndEmit(this._manualAnnotationService.addRecommendation(annotation, file), annotation, annotationsChanged); }); } getViewerAvailableActions( viewer: WebViewerInstance, - dossier: Dossier, + file: File, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter, ): Record[] { const availableActions = []; + const dossier = this._dossier(file); const annotationPermissions = annotations.map(annotation => ({ annotation, @@ -213,7 +204,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.resize-accept.label'), onClick: () => { this._ngZone.run(() => { - this.acceptResize(null, viewer, dossier, firstAnnotation, annotationsChanged); + this.acceptResize(null, viewer, file, firstAnnotation, annotationsChanged); }); }, }); @@ -250,7 +241,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.edit-reason.label'), onClick: () => { this._ngZone.run(() => { - this.changeLegalBasis(null, annotations, dossier, annotationsChanged); + this.changeLegalBasis(null, annotations, file, annotationsChanged); }); }, }); @@ -264,7 +255,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.recategorize-image'), onClick: () => { this._ngZone.run(() => { - this.recategorizeImages(null, annotations, dossier, annotationsChanged); + this.recategorizeImages(null, annotations, file, annotationsChanged); }); }, }); @@ -281,7 +272,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.remove-annotation.remove-from-dict'), onClick: () => { this._ngZone.run(() => { - this.suggestRemoveAnnotation(null, annotations, dossier, true, annotationsChanged); + this.suggestRemoveAnnotation(null, annotations, file, true, annotationsChanged); }); }, }); @@ -295,7 +286,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.accept-recommendation.label'), onClick: () => { this._ngZone.run(() => { - this.convertRecommendationToAnnotation(null, annotations, dossier, annotationsChanged); + this.convertRecommendationToAnnotation(null, annotations, file, annotationsChanged); }); }, }); @@ -309,7 +300,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.accept-suggestion.label'), onClick: () => { this._ngZone.run(() => { - this.acceptSuggestion(null, annotations, dossier, annotationsChanged); + this.acceptSuggestion(null, annotations, file, annotationsChanged); }); }, }); @@ -323,7 +314,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.undo'), onClick: () => { this._ngZone.run(() => { - this.undoDirectAction(null, annotations, dossier, annotationsChanged); + this.undoDirectAction(null, annotations, file, annotationsChanged); }); }, }); @@ -337,7 +328,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'), onClick: () => { this._ngZone.run(() => { - this.markAsFalsePositive(null, annotations, dossier, annotationsChanged); + this.markAsFalsePositive(null, annotations, file, annotationsChanged); }); }, }); @@ -351,7 +342,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.force-redaction.label'), onClick: () => { this._ngZone.run(() => { - this.forceRedaction(null, annotations, dossier, annotationsChanged); + this.forceRedaction(null, annotations, file, annotationsChanged); }); }, }); @@ -365,7 +356,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.reject-suggestion'), onClick: () => { this._ngZone.run(() => { - this.rejectSuggestion(null, annotations, dossier, annotationsChanged); + this.rejectSuggestion(null, annotations, file, annotationsChanged); }); }, }); @@ -382,7 +373,7 @@ export class AnnotationActionsService { title: this._translateService.instant('annotation-actions.remove-annotation.only-here'), onClick: () => { this._ngZone.run(() => { - this.suggestRemoveAnnotation(null, annotations, dossier, false, annotationsChanged); + this.suggestRemoveAnnotation(null, annotations, file, false, annotationsChanged); }); }, }); @@ -416,11 +407,12 @@ export class AnnotationActionsService { acceptResize( $event: MouseEvent, viewer: WebViewerInstance, - dossier: Dossier, + file: File, annotationWrapper: AnnotationWrapper, annotationsChanged: EventEmitter, ) { - this._dialogService.openDialog('resizeAnnotation', $event, { dossier }, async (result: { comment: string }) => { + const data = { dossier: this._dossier(file) }; + this._dialogService.openDialog('resizeAnnotation', $event, data, async (result: { comment: string }) => { const textAndPositions = await this._extractTextAndPositions(viewer, annotationWrapper.id); const text = annotationWrapper.value === 'Rectangle' ? 'Rectangle' : annotationWrapper.isImage ? 'Image' : textAndPositions.text; @@ -433,7 +425,7 @@ export class AnnotationActionsService { }; this._processObsAndEmit( - this._manualAnnotationService.resizeOrSuggestToResize(annotationWrapper, dossier, resizeRequest), + this._manualAnnotationService.resizeOrSuggestToResize(annotationWrapper, file, resizeRequest), annotationWrapper, annotationsChanged, ); @@ -458,6 +450,10 @@ export class AnnotationActionsService { annotationsChanged.emit(annotationWrapper); } + private _dossier(file: File): Dossier { + return this._dossiersService.find(file.dossierId); + } + private _processObsAndEmit(obs: Observable, annotation: AnnotationWrapper, annotationsChanged: EventEmitter) { obs.subscribe( () => { @@ -488,7 +484,7 @@ export class AnnotationActionsService { private _markAsFalsePositive( $event: MouseEvent, annotation: AnnotationWrapper, - dossier: Dossier, + file: File, text: string, annotationsChanged: EventEmitter, ) { @@ -502,7 +498,7 @@ export class AnnotationActionsService { falsePositiveRequest.addToDictionary = true; falsePositiveRequest.comment = { text: 'False Positive' }; - this._processObsAndEmit(this._manualAnnotationService.addAnnotation(falsePositiveRequest, dossier), annotation, annotationsChanged); + this._processObsAndEmit(this._manualAnnotationService.addAnnotation(falsePositiveRequest, file), annotation, annotationsChanged); } private _convertPath(path: string): string { diff --git a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts index f1a81dc1f..aa9c72a1f 100644 --- a/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts +++ b/apps/red-ui/src/app/modules/dossier/services/manual-annotation.service.ts @@ -2,6 +2,7 @@ import { Injectable, Injector } from '@angular/core'; import { AppStateService } from '@state/app-state.service'; import { Dossier, + File, IAddRedactionRequest, IApproveRequest, IImageRecategorizationRequest, @@ -18,6 +19,7 @@ import { AnnotationActionMode } from '../models/annotation-action-mode.model'; import { annotationActionsTranslations } from '../translations/annotation-actions-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; +import { DossiersService } from '@services/entity-services/dossiers.service'; @Injectable() export class ManualAnnotationService extends GenericService { @@ -29,6 +31,7 @@ export class ManualAnnotationService extends GenericService private readonly _appStateService: AppStateService, private readonly _toaster: Toaster, private readonly _permissionsService: PermissionsService, + private readonly _dossiersService: DossiersService, protected readonly _injector: Injector, ) { super(_injector, 'manualRedaction'); @@ -51,10 +54,10 @@ export class ManualAnnotationService extends GenericService }; } - _makeRequest(mode: AnnotationActionMode, dossier: Dossier, body: any, secondParam: any = null, modifyDictionary = false) { + _makeRequest(mode: AnnotationActionMode, file: File, body: any, secondParam: any = null, modifyDictionary = false) { const obs = !secondParam - ? this[this.CONFIG[mode]](body, dossier.id, this._appStateService.activeFileId) - : this[this.CONFIG[mode]](body, secondParam, dossier.id, this._appStateService.activeFileId); + ? this[this.CONFIG[mode]](body, file.dossierId, file.id) + : this[this.CONFIG[mode]](body, secondParam, file.dossierId, file.id); return obs.pipe( tap( @@ -64,7 +67,7 @@ export class ManualAnnotationService extends GenericService this._toaster.error(this._getMessage(mode, modifyDictionary, true, isConflict), { error, params: { - dictionaryName: this._appStateService.getDictionary(body.type, dossier.dossierTemplateId).label, + dictionaryName: this._appStateService.getDictionary(body.type, this._dossier(file).dossierTemplateId).label, content: body.value, }, positionClass: 'toast-file-preview', @@ -86,7 +89,7 @@ export class ManualAnnotationService extends GenericService return super.delete({}, url); } - addRecommendation(annotation: AnnotationWrapper, dossier: Dossier) { + addRecommendation(annotation: AnnotationWrapper, file: File) { const manualRedactionEntry: IAddRedactionRequest = {}; manualRedactionEntry.addToDictionary = true; // set the ID as reason, so we can hide the suggestion @@ -95,78 +98,85 @@ export class ManualAnnotationService extends GenericService manualRedactionEntry.positions = annotation.positions; manualRedactionEntry.type = annotation.recommendationType; manualRedactionEntry.comment = { text: 'Accepted Recommendation' }; - return this.addAnnotation(manualRedactionEntry, dossier); + return this.addAnnotation(manualRedactionEntry, file); + } + + // /manualRedaction/request/legalBasis + changeLegalBasis(annotationId: string, file: File, legalBasis: string, comment?: string) { + const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) + ? 'change-legal-basis' + : 'request-change-legal-basis'; + return this._makeRequest(mode, file, { annotationId, legalBasis, comment }); } // this wraps // /manualRedaction/redaction/legalBasisChange - // /manualRedaction/request/legalBasis - changeLegalBasis(annotationId: string, dossier: Dossier, legalBasis: string, comment?: string) { - const mode: AnnotationActionMode = this._permissionsService.isApprover(dossier) - ? 'change-legal-basis' - : 'request-change-legal-basis'; - return this._makeRequest(mode, dossier, { annotationId, legalBasis, comment }); + + // /manualRedaction/request/recategorize + recategorizeImg(annotationId: string, file: File, type: string, comment: string) { + const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) + ? 'recategorize-image' + : 'request-image-recategorization'; + return this._makeRequest(mode, file, { annotationId, type, comment }); } // this wraps // /manualRedaction/redaction/recategorize - // /manualRedaction/request/recategorize - recategorizeImg(annotationId: string, dossier: Dossier, type: string, comment: string) { - const mode: AnnotationActionMode = this._permissionsService.isApprover(dossier) - ? 'recategorize-image' - : 'request-image-recategorization'; - return this._makeRequest(mode, dossier, { annotationId, type, comment }); + + // /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); } // this wraps // /manualRedaction/redaction/add - // /manualRedaction/request/add - addAnnotation(manualRedactionEntry: IAddRedactionRequest, dossier: Dossier) { - const mode: AnnotationActionMode = this._permissionsService.isApprover(dossier) ? 'add' : 'suggest'; - return this._makeRequest(mode, dossier, manualRedactionEntry, null, manualRedactionEntry.addToDictionary); + + // /manualRedaction/request/force + force(request: ILegalBasisChangeRequest, file: File) { + const mode: AnnotationActionMode = this._permissionsService.isApprover(this._dossier(file)) + ? 'force-redaction' + : 'request-force-redaction'; + return this._makeRequest(mode, file, request); } // this wraps // /manualRedaction/redaction/force - // /manualRedaction/request/force - force(request: ILegalBasisChangeRequest, dossier: Dossier) { - const mode: AnnotationActionMode = this._permissionsService.isApprover(dossier) ? 'force-redaction' : 'request-force-redaction'; - return this._makeRequest(mode, dossier, request); + + // /manualRedaction/approve + approve(annotationId: string, file: File, addToDictionary: boolean = false) { + // for only here - approve the request + return this._makeRequest('approve', file, { addOrRemoveFromDictionary: addToDictionary }, annotationId, addToDictionary); } // this wraps - // /manualRedaction/approve - approve(annotationId: string, dossier: Dossier, addToDictionary: boolean = false) { - // for only here - approve the request - return this._makeRequest('approve', dossier, { addOrRemoveFromDictionary: addToDictionary }, annotationId, addToDictionary); + + undoRequest(annotationWrapper: AnnotationWrapper, file: File) { + return this._makeRequest('undo', file, annotationWrapper.id, null, annotationWrapper.isModifyDictionary); } - undoRequest(annotationWrapper: AnnotationWrapper, dossier: Dossier) { - return this._makeRequest('undo', dossier, 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); } // this wraps // /manualRedaction/decline/remove - // /manualRedaction/undo - declineOrRemoveRequest(annotationWrapper: AnnotationWrapper, dossier: Dossier) { - const mode: AnnotationActionMode = this._permissionsService.isApprover(dossier) ? 'decline' : 'undo'; - return this._makeRequest(mode, dossier, 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); } // this wraps // /manualRedaction/redaction/resize/ - // /manualRedaction/request/resize/ - resizeOrSuggestToResize(annotationWrapper: AnnotationWrapper, dossier: Dossier, resizeRequest: IResizeRequest) { - const mode: AnnotationActionMode = this._permissionsService.isApprover(dossier) ? 'resize' : 'request-resize'; - return this._makeRequest(mode, dossier, resizeRequest); - } - // this wraps - // /manualRedaction/redaction/remove/ // /manualRedaction/request/remove/ removeOrSuggestRemoveAnnotation( annotationWrapper: AnnotationWrapper, - dossier: Dossier, + file: File, removeFromDictionary: boolean = false, comment: string, ) { @@ -174,7 +184,7 @@ export class ManualAnnotationService extends GenericService body: any, removeDict = false; - if (this._permissionsService.isApprover(dossier)) { + if (this._permissionsService.isApprover(this._dossier(file))) { // if it was something manual simply decline the existing request if (annotationWrapper.type === 'manual') { mode = 'undo'; @@ -198,9 +208,12 @@ export class ManualAnnotationService extends GenericService removeDict = removeFromDictionary; } - return this._makeRequest(mode, dossier, body, null, removeDict); + return this._makeRequest(mode, file, body, null, removeDict); } + // this wraps + // /manualRedaction/redaction/remove/ + getTitle(type: 'DICTIONARY' | 'REDACTION' | 'FALSE_POSITIVE', dossier: Dossier) { if (this._permissionsService.isApprover(dossier)) { switch (type) { @@ -338,6 +351,10 @@ export class ManualAnnotationService extends GenericService return this._post(body, url); } + private _dossier(file: File): Dossier { + return this._dossiersService.find(file.dossierId); + } + private _getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) { const type = modifyDictionary ? 'dictionary' : 'manual-redaction'; const resultType = error ? (isConflict ? 'conflictError' : 'error') : 'success'; diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts index 5274db6f2..1e16193d8 100644 --- a/apps/red-ui/src/app/state/app-state.service.ts +++ b/apps/red-ui/src/app/state/app-state.service.ts @@ -1,16 +1,14 @@ import { Injectable } from '@angular/core'; import { Dictionary, DossierTemplate, IColors } from '@red/domain'; -import { ActivationEnd, Router } from '@angular/router'; +import { Router } from '@angular/router'; import { forkJoin, Observable, of } from 'rxjs'; import { catchError, map, tap } from 'rxjs/operators'; -import { currentComponentRoute, FALLBACK_COLOR, hexToRgb } from '@utils/functions'; -import { DossiersService } from '@services/entity-services/dossiers.service'; +import { FALLBACK_COLOR, hexToRgb } from '@utils/functions'; import { DictionaryService } from '@shared/services/dictionary.service'; import { DossierTemplatesService } from '@services/entity-services/dossier-templates.service'; import { FileAttributesService } from '@services/entity-services/file-attributes.service'; export interface AppState { - activeFileId?: string; activeDictionaryType?: string; } @@ -19,31 +17,13 @@ export interface AppState { }) export class AppStateService { private _appState: AppState = {}; - private _activeDossierId: string; // TODO: Temporary constructor( private readonly _router: Router, - private readonly _dossiersService: DossiersService, private readonly _dictionaryService: DictionaryService, private readonly _dossierTemplatesService: DossierTemplatesService, private readonly _fileAttributesService: FileAttributesService, - ) { - _router.events.pipe(currentComponentRoute).subscribe((event: ActivationEnd) => { - const fileId = event.snapshot.paramMap.get('fileId'); - const sameIdAsCurrentActive = fileId === this.activeFileId; - - if (sameIdAsCurrentActive) { - return; - } - - if (fileId === null || fileId === undefined) { - return (this._appState.activeFileId = undefined); - } - - this._activeDossierId = event.snapshot.paramMap.get('dossierId'); - return this._activateFile(this._activeDossierId, fileId); - }); - } + ) {} private _dictionaryData?: { [key: string]: { [key: string]: Dictionary } }; @@ -66,10 +46,6 @@ export class AppStateService { : undefined; } - get activeFileId(): string | undefined { - return this._appState.activeFileId; - } - getDictionaryColor(type: string, dossierTemplateId: string) { return !this._dictionaryData ? '#cccccc' @@ -99,7 +75,6 @@ export class AppStateService { } reset() { - this._appState.activeFileId = null; this._appState.activeDictionaryType = null; } @@ -160,16 +135,6 @@ export class AppStateService { ); } - private _activateFile(dossierId: string, fileId: string) { - if (this._activeDossierId === dossierId && this.activeFileId === fileId) { - return; - } - - if (this._dossiersService.find(this._activeDossierId)) { - this._appState.activeFileId = fileId; - } - } - private _getDictionaryDataForDossierTemplate$(dossierTemplateId: string): Observable<{ [key: string]: any }> { const dictionaryData: { [key: string]: any } = {};