From 9ae510bf5ac1642778ec2b930b5409832e3b3501 Mon Sep 17 00:00:00 2001 From: Dan Percic Date: Tue, 15 Mar 2022 16:38:45 +0200 Subject: [PATCH] wip --- .../pdf-viewer/pdf-viewer.component.ts | 704 ------------------ .../file-preview.module.ts | 73 -- .../pdf-viewer/pdf-viewer.component.ts | 6 +- .../manual-annotation-dialog.component.ts | 2 +- .../file-preview/file-preview.module.ts | 6 +- .../services/manual-redaction.service.ts | 114 +-- .../annotation-actions-translations.ts | 97 ++- libs/red-domain/src/lib/annotations/types.ts | 16 + 8 files changed, 190 insertions(+), 828 deletions(-) delete mode 100644 apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts delete mode 100644 apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts rename apps/red-ui/src/app/modules/{dossier/screens/file-preview-screen => file-preview}/services/manual-redaction.service.ts (77%) diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts deleted file mode 100644 index ae87c066b..000000000 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/components/pdf-viewer/pdf-viewer.component.ts +++ /dev/null @@ -1,704 +0,0 @@ -import { - Component, - ElementRef, - EventEmitter, - Inject, - Input, - NgZone, - OnChanges, - OnInit, - Output, - SimpleChanges, - ViewChild, -} from '@angular/core'; -import { Dossier, File, IHeaderElement, IManualRedactionEntry, RotationTypes } from '@red/domain'; -import { Core, WebViewerInstance } from '@pdftron/webviewer'; -import { TranslateService } from '@ngx-translate/core'; -import { - ManualRedactionEntryType, - ManualRedactionEntryTypes, - ManualRedactionEntryWrapper, -} from '@models/file/manual-redaction-entry.wrapper'; -import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { ManualRedactionService } from '../../services/manual-redaction.service'; -import { environment } from '@environments/environment'; -import { AnnotationDrawService } from '../../services/annotation-draw.service'; -import { AnnotationActionsService } from '../../services/annotation-actions.service'; -import { UserPreferenceService } from '@services/user-preference.service'; -import { BASE_HREF } from '../../../../../../tokens'; -import { ConfigService } from '@services/config.service'; -import { AutoUnsubscribe, ConfirmationDialogInput, LoadingService } from '@iqser/common-ui'; -import { DossiersDialogService } from '../../../../services/dossiers-dialog.service'; -import { loadCompareDocumentWrapper } from '../../../../utils/compare-mode.utils'; -import { PdfViewer } from '../../services/pdf-viewer.service'; -import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -import { ActivatedRoute } from '@angular/router'; -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 { tap, withLatestFrom } from 'rxjs/operators'; -import { PageRotationService } from '../../services/page-rotation.service'; -import { ALLOWED_KEYBOARD_SHORTCUTS, HeaderElements, TextPopups } from '../../shared/constants'; -import Tools = Core.Tools; -import TextTool = Tools.TextTool; -import Annotation = Core.Annotations.Annotation; - -function getDivider(hiddenOn?: readonly ('desktop' | 'mobile' | 'tablet')[]) { - return { - type: 'divider', - hidden: hiddenOn, - }; -} - -@Component({ - selector: 'redaction-pdf-viewer', - templateUrl: './pdf-viewer.component.html', - styleUrls: ['./pdf-viewer.component.scss'], -}) -export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnChanges { - @Input() dossier: Dossier; - @Input() canPerformActions = false; - @Input() annotations: AnnotationWrapper[]; - @Output() readonly fileReady = new EventEmitter(); - @Output() readonly annotationSelected = new EventEmitter(); - @Output() readonly manualAnnotationRequested = new EventEmitter(); - @Output() readonly pageChanged = new EventEmitter(); - @Output() readonly keyUp = new EventEmitter(); - @Output() readonly viewerReady = new EventEmitter(); - @Output() readonly annotationsChanged = new EventEmitter(); - @ViewChild('viewer', { static: true }) viewer: ElementRef; - @ViewChild('compareFileInput', { static: true }) compareFileInput: ElementRef; - instance: WebViewerInstance; - documentViewer: Core.DocumentViewer; - annotationManager: Core.AnnotationManager; - private _selectedText = ''; - - constructor( - @Inject(BASE_HREF) private readonly _baseHref: string, - private readonly _translateService: TranslateService, - private readonly _manualRedactionService: ManualRedactionService, - private readonly _dialogService: DossiersDialogService, - private readonly _ngZone: NgZone, - private readonly _activatedRoute: ActivatedRoute, - private readonly _userPreferenceService: UserPreferenceService, - private readonly _annotationDrawService: AnnotationDrawService, - private readonly _annotationActionsService: AnnotationActionsService, - private readonly _configService: ConfigService, - private readonly _loadingService: LoadingService, - private readonly _pageRotationService: PageRotationService, - readonly stateService: FilePreviewStateService, - readonly viewModeService: ViewModeService, - readonly multiSelectService: MultiSelectService, - readonly pdf: PdfViewer, - ) { - super(); - } - - private get _toggleTooltipsBtnTitle(): string { - return this._translateService.instant(_('pdf-viewer.toggle-tooltips'), { - active: this._userPreferenceService.getFilePreviewTooltipsPreference(), - }); - } - - private get _toggleTooltipsIcon(): string { - return this._convertPath( - this._userPreferenceService.getFilePreviewTooltipsPreference() - ? '/assets/icons/general/pdftron-action-enable-tooltips.svg' - : '/assets/icons/general/pdftron-action-disable-tooltips.svg', - ); - } - - async ngOnInit() { - this._setReadyAndInitialState = this._setReadyAndInitialState.bind(this); - await this._loadViewer(); - - this.addActiveScreenSubscription = this.stateService.blob$ - .pipe( - withLatestFrom(this.stateService.file$), - tap(() => (this.pdf.ready = false)), - tap(([blob, file]) => this._loadDocument(blob, file)), - ) - .subscribe(); - } - - async ngOnChanges(changes: SimpleChanges): Promise { - if (!this.instance) { - return; - } - - if (changes.canPerformActions) { - await this._handleCustomActions(); - } - } - - uploadFile(files: FileList) { - const fileToCompare = files[0]; - this.compareFileInput.nativeElement.value = null; - - if (!fileToCompare) { - console.error('No file to compare!'); - return; - } - - const fileReader = new FileReader(); - fileReader.onload = async () => { - const pdfNet = this.instance.Core.PDFNet; - - await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null); - - const compareDocument = await pdfNet.PDFDoc.createFromBuffer(fileReader.result as ArrayBuffer); - const blob = await this.stateService.blob; - const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer()); - - const loadCompareDocument = async () => { - this._loadingService.start(); - this.pdf.ready = false; - const mergedDocument = await pdfNet.PDFDoc.create(); - const file = await this.stateService.file; - await loadCompareDocumentWrapper( - currentDocument, - compareDocument, - mergedDocument, - this.instance, - file, - () => { - this.viewModeService.compareMode = true; - }, - () => { - this.pdf.navigateToPage(1); - }, - this.instance.Core.PDFNet, - ); - this._loadingService.stop(); - }; - - const currentDocumentPageCount = await currentDocument.getPageCount(); - const compareDocumentPageCount = await compareDocument.getPageCount(); - - if (currentDocumentPageCount !== compareDocumentPageCount) { - this._dialogService.openDialog( - 'confirm', - null, - new ConfirmationDialogInput({ - title: _('confirmation-dialog.compare-file.title'), - question: _('confirmation-dialog.compare-file.question'), - translateParams: { - fileName: fileToCompare.name, - currentDocumentPageCount, - compareDocumentPageCount, - }, - }), - loadCompareDocument, - ); - } else { - await loadCompareDocument(); - } - }; - - fileReader.readAsArrayBuffer(fileToCompare); - } - - async closeCompareMode() { - this.viewModeService.compareMode = false; - const pdfNet = this.instance.Core.PDFNet; - await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null); - const blob = await this.stateService.blob; - const currentDocument = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer()); - - const filename = (await this.stateService.file).filename ?? 'document.pdf'; - this.instance.UI.loadDocument(currentDocument, { filename }); - - this.instance.UI.disableElements([HeaderElements.CLOSE_COMPARE_BUTTON]); - this.instance.UI.enableElements([HeaderElements.COMPARE_BUTTON]); - this.pdf.navigateToPage(1); - } - - private async _loadViewer() { - this.instance = await this.pdf.loadViewer(this.viewer.nativeElement as HTMLElement); - this.documentViewer = this.pdf.documentViewer; - this.annotationManager = this.pdf.annotationManager; - - this._setSelectionMode(); - this._configureElements(); - this.pdf.disableHotkeys(); - await this._configureTextPopup(); - - this.annotationManager.addEventListener('annotationSelected', (annotations: Annotation[], action) => { - const nextAnnotations = this.multiSelectService.isEnabled ? this.annotationManager.getSelectedAnnotations() : annotations; - this.annotationSelected.emit(nextAnnotations.map(ann => ann.Id)); - if (action === 'deselected') { - this._toggleRectangleAnnotationAction(true); - } else { - if (!this.multiSelectService.isEnabled) { - this.pdf.deselectAnnotations(this.annotations.filter(wrapper => !nextAnnotations.find(ann => ann.Id === wrapper.id))); - } - this._configureAnnotationSpecificActions(annotations); - this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly); - } - }); - - this.annotationManager.addEventListener('annotationChanged', (annotations: Annotation[]) => { - // when a rectangle is drawn, - // it returns one annotation with tool name 'AnnotationCreateRectangle; - // this will auto select rectangle after drawing - if (annotations.length === 1 && annotations[0].ToolName === 'AnnotationCreateRectangle') { - this.annotationManager.selectAnnotations(annotations); - annotations[0].disableRotationControl(); - } - }); - - this.documentViewer.addEventListener('pageNumberUpdated', (pageNumber: number) => { - this.pdf.deselectAllAnnotations(); - this._ngZone.run(() => this.pageChanged.emit(pageNumber)); - return this._handleCustomActions(); - }); - - this.documentViewer.addEventListener('documentLoaded', this._setReadyAndInitialState); - - this.documentViewer.addEventListener('keyUp', ($event: KeyboardEvent) => { - // arrows and full-screen - if (($event.target as HTMLElement)?.tagName?.toLowerCase() !== 'input') { - if ($event.key.startsWith('Arrow') || $event.key === 'f') { - this._ngZone.run(() => this.keyUp.emit($event)); - $event.preventDefault(); - $event.stopPropagation(); - } - } - - if (!ALLOWED_KEYBOARD_SHORTCUTS.includes($event.key)) { - $event.preventDefault(); - $event.stopPropagation(); - } - }); - - this.documentViewer.addEventListener('textSelected', async (quads, selectedText, pageNumber: number) => { - this._selectedText = selectedText; - const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]; - - const file = await this.stateService.file; - - if (this.viewModeService.isCompare && pageNumber % 2 === 0) { - this.instance.UI.disableElements(['textPopup']); - } else { - this.instance.UI.enableElements(['textPopup']); - } - - if (selectedText.length > 2 && this.canPerformActions && !this.pdf.isCurrentPageExcluded(file)) { - this.instance.UI.enableElements(textActions); - } else { - this.instance.UI.disableElements(textActions); - } - }); - - this.instance.UI.iframeWindow.addEventListener('visibilityChanged', (event: any) => { - if (event.detail.element === 'searchPanel') { - const inputElement = this.instance.UI.iframeWindow.document.getElementById('SearchPanel__input') as HTMLInputElement; - setTimeout(() => { - inputElement.value = ''; - }, 0); - if (!event.detail.isVisible) { - this.documentViewer.clearSearchResults(); - } - } - }); - } - - private _setInitialDisplayMode() { - this.instance.UI.setFitMode('FitPage'); - const instanceDisplayMode = this.documentViewer.getDisplayModeManager().getDisplayMode(); - instanceDisplayMode.mode = this.viewModeService.isCompare ? 'Facing' : 'Single'; - this.documentViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); - } - - private _convertPath(path: string): string { - return this._baseHref + path; - } - - private _setSelectionMode(): void { - const textTool = this.instance.Core.Tools.TextTool as unknown as TextTool; - textTool.SELECTION_MODE = this._configService.values.SELECTION_MODE; - } - - private _toggleRectangleAnnotationAction(readonly = false) { - if (!readonly) { - this.instance.UI.enableElements([TextPopups.ADD_RECTANGLE]); - } else { - this.instance.UI.disableElements([TextPopups.ADD_RECTANGLE]); - } - } - - private _configureElements() { - this.instance.UI.disableElements([ - 'pageNavOverlay', - 'menuButton', - 'selectToolButton', - 'textHighlightToolButton', - 'textUnderlineToolButton', - 'textSquigglyToolButton', - 'textStrikeoutToolButton', - 'viewControlsButton', - 'contextMenuPopup', - 'linkButton', - 'toggleNotesButton', - 'notesPanel', - 'thumbnailControl', - 'documentControl', - 'ribbons', - 'toolsHeader', - 'rotateClockwiseButton', - 'rotateCounterClockwiseButton', - 'annotationStyleEditButton', - 'annotationGroupButton', - ]); - - const applyRotation: IHeaderElement = { - type: 'customElement', - dataElement: HeaderElements.APPLY_ROTATION, - render: () => { - const paragraph = document.createElement('p'); - paragraph.innerText = this._translateService.instant('page-rotation.apply'); - paragraph.style.cssText = ` - font-size: 11px; - font-weight: 600; - color: #DD4D50; - cursor: pointer; - margin: 0 12px; - `; - paragraph.addEventListener('click', async () => { - await this._pageRotationService.applyRotation(); - }); - return paragraph; - }, - }; - const discardRotation: IHeaderElement = { - type: 'customElement', - dataElement: HeaderElements.DISCARD_ROTATION, - render: () => { - const paragraph = document.createElement('p'); - paragraph.innerText = this._translateService.instant('page-rotation.discard'); - paragraph.style.cssText = ` - font-size: 11px; - font-weight: 600; - color: #283241; - cursor: pointer; - opacity: 0.7; - `; - paragraph.addEventListener('click', () => { - this._pageRotationService.discardRotation(); - }); - return paragraph; - }, - }; - - const divider = getDivider(); - const headerItems: IHeaderElement[] = [ - divider, - { - type: 'actionButton', - element: 'compare', - dataElement: HeaderElements.COMPARE_BUTTON, - img: this._convertPath('/assets/icons/general/pdftron-action-compare.svg'), - title: 'Compare', - onClick: () => { - this.compareFileInput.nativeElement.click(); - }, - }, - { - type: 'actionButton', - element: 'closeCompare', - dataElement: HeaderElements.CLOSE_COMPARE_BUTTON, - img: this._convertPath('/assets/icons/general/pdftron-action-close-compare.svg'), - title: 'Leave Compare Mode', - onClick: async () => { - await this.closeCompareMode(); - }, - }, - divider, - { - type: 'actionButton', - element: 'tooltips', - dataElement: HeaderElements.TOGGLE_TOOLTIPS, - img: this._toggleTooltipsIcon, - title: this._toggleTooltipsBtnTitle, - onClick: async () => { - await this._userPreferenceService.toggleFilePreviewTooltipsPreference(); - this._updateTooltipsVisibility(); - this.instance.UI.updateElement(HeaderElements.TOGGLE_TOOLTIPS, { - title: this._toggleTooltipsBtnTitle, - img: this._toggleTooltipsIcon, - }); - }, - }, - divider, - { - type: 'toolGroupButton', - toolGroup: 'rectangleTools', - dataElement: HeaderElements.SHAPE_TOOL_GROUP_BUTTON, - img: this._convertPath('/assets/icons/general/rectangle.svg'), - title: 'annotation.rectangle', - }, - divider, - { - type: 'actionButton', - element: 'tooltips', - dataElement: HeaderElements.ROTATE_LEFT_BUTTON, - img: this._convertPath('/assets/icons/general/rotate-left.svg'), - onClick: () => this._pageRotationService.addRotation(RotationTypes.LEFT), - }, - { - type: 'actionButton', - element: 'tooltips', - dataElement: HeaderElements.ROTATE_RIGHT_BUTTON, - img: this._convertPath('/assets/icons/general/rotate-right.svg'), - onClick: () => this._pageRotationService.addRotation(RotationTypes.RIGHT), - }, - applyRotation, - discardRotation, - divider, - ]; - - this.instance.UI.setHeaderItems(header => { - header.getItems().splice(8, 0, ...headerItems); - }); - - this.instance.UI.disableElements([ - HeaderElements.CLOSE_COMPARE_BUTTON, - HeaderElements.APPLY_ROTATION, - HeaderElements.DISCARD_ROTATION, - ]); - - const dossierTemplateId = this.dossier.dossierTemplateId; - - this.documentViewer.getTool('AnnotationCreateRectangle').setStyles({ - StrokeThickness: 2, - StrokeColor: this._annotationDrawService.getAndConvertColor(dossierTemplateId, 'manual'), - FillColor: this._annotationDrawService.getAndConvertColor(dossierTemplateId, 'manual'), - Opacity: 0.6, - }); - } - - private _configureAnnotationSpecificActions(viewerAnnotations: Annotation[]) { - if (!this.canPerformActions) { - return; - } - - const annotationWrappers = viewerAnnotations.map(va => this.annotations.find(a => a.id === va.Id)).filter(va => !!va); - this.instance.UI.annotationPopup.update([]); - - if (annotationWrappers.length === 0) { - this._configureRectangleAnnotationPopup(viewerAnnotations[0]); - return; - } - - // Add hide action as last item - const allAnnotationsHaveImageAction = annotationWrappers.reduce((acc, next) => acc && next.isImage, true); - if (allAnnotationsHaveImageAction) { - const allAreVisible = viewerAnnotations.reduce((acc, next) => next.isVisible() && acc, true); - - this.instance.UI.annotationPopup.add([ - { - type: 'actionButton', - img: allAreVisible - ? this._convertPath('/assets/icons/general/visibility-off.svg') - : this._convertPath('/assets/icons/general/visibility.svg'), - title: this._translateService.instant(`annotation-actions.${allAreVisible ? 'hide' : 'show'}`), - onClick: () => { - this._ngZone.run(() => { - if (allAreVisible) { - this.annotationManager.hideAnnotations(viewerAnnotations); - } else { - this.annotationManager.showAnnotations(viewerAnnotations); - } - this.annotationManager.deselectAllAnnotations(); - this._annotationActionsService.updateHiddenAnnotation(this.annotations, viewerAnnotations, allAreVisible); - }); - }, - }, - ]); - } - - const actions = this._annotationActionsService.getViewerAvailableActions(this.dossier, annotationWrappers, this.annotationsChanged); - this.instance.UI.annotationPopup.add(actions); - } - - private _configureRectangleAnnotationPopup(annotation: Annotation) { - if (!this.viewModeService.isCompare || annotation.getPageNumber() % 2 === 1) { - this.instance.UI.annotationPopup.add([ - { - type: 'actionButton', - dataElement: TextPopups.ADD_RECTANGLE, - img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'), - title: this.#getTitle(ManualRedactionEntryTypes.REDACTION), - onClick: () => this._addRectangleManualRedaction(), - }, - ]); - } - } - - private _addRectangleManualRedaction() { - const activeAnnotation = this.annotationManager.getSelectedAnnotations()[0]; - const activePage = activeAnnotation.getPageNumber(); - const quads = [this._annotationDrawService.annotationToQuads(activeAnnotation)]; - const manualRedaction = this._getManualRedaction({ [activePage]: quads }); - this._cleanUpSelectionAndButtonState(); - - this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(quads, manualRedaction, 'REDACTION', activeAnnotation.Id)); - } - - private _cleanUpSelectionAndButtonState() { - const rectangleElements = [HeaderElements.SHAPE_TOOL_GROUP_BUTTON]; - this.pdf.deselectAllAnnotations(); - this.instance.UI.disableElements(rectangleElements); - this.instance.UI.enableElements(rectangleElements); - } - - private _configureTextPopup() { - const searchButton = { - type: 'actionButton', - img: this._convertPath('/assets/icons/general/pdftron-action-search.svg'), - title: this._translateService.instant('pdf-viewer.text-popup.actions.search'), - onClick: () => { - const text = this.documentViewer.getSelectedText(); - const searchOptions = { - caseSensitive: true, // match case - wholeWord: true, // match whole words only - wildcard: false, // allow using '*' as a wildcard value - regex: false, // string is treated as a regular expression - searchUp: false, // search from the end of the document upwards - ambientString: true, // return ambient string as part of the result - }; - this.instance.UI.openElements(['searchPanel']); - setTimeout(() => this.instance.UI.searchTextFull(text, searchOptions), 250); - }, - }; - const popups: IHeaderElement[] = [searchButton]; - - // Adding directly to the false-positive dict is only available in dev-mode - if (this._userPreferenceService.areDevFeaturesEnabled) { - popups.push({ - type: 'actionButton', - dataElement: TextPopups.ADD_FALSE_POSITIVE, - img: this._convertPath('/assets/icons/general/pdftron-action-false-positive.svg'), - title: this.#getTitle(ManualRedactionEntryTypes.FALSE_POSITIVE), - onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.FALSE_POSITIVE), - }); - } - - popups.push({ - type: 'actionButton', - dataElement: TextPopups.ADD_REDACTION, - img: this._convertPath('/assets/icons/general/pdftron-action-add-redaction.svg'), - title: this.#getTitle(ManualRedactionEntryTypes.REDACTION), - onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.REDACTION), - }); - popups.push({ - type: 'actionButton', - dataElement: TextPopups.ADD_DICTIONARY, - img: this._convertPath('/assets/icons/general/pdftron-action-add-dict.svg'), - title: this.#getTitle(ManualRedactionEntryTypes.DICTIONARY), - onClick: () => this._addManualRedactionOfType(ManualRedactionEntryTypes.DICTIONARY), - }); - - this.instance.UI.textPopup.add(popups); - - return this._handleCustomActions(); - } - - #getTitle(type: ManualRedactionEntryType) { - return this._translateService.instant(this._manualRedactionService.getTitle(type, this.dossier)); - } - - private _addManualRedactionOfType(type: ManualRedactionEntryType) { - const selectedQuads: Readonly> = this.documentViewer.getSelectedTextQuads(); - const text = this.documentViewer.getSelectedText(); - const manualRedaction = this._getManualRedaction(selectedQuads, text, true); - this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(selectedQuads, manualRedaction, type)); - } - - private async _handleCustomActions() { - this.instance.UI.setToolMode('AnnotationEdit'); - const elementsToToggle = [ - TextPopups.ADD_REDACTION, - TextPopups.ADD_RECTANGLE, - TextPopups.ADD_FALSE_POSITIVE, - HeaderElements.SHAPE_TOOL_GROUP_BUTTON, - HeaderElements.ANNOTATION_POPUP, - ]; - - const isCurrentPageExcluded = this.pdf.isCurrentPageExcluded(await this.stateService.file); - - if (this.canPerformActions && !isCurrentPageExcluded) { - try { - this.instance.UI.enableTools(['AnnotationCreateRectangle']); - } catch (e) { - // happens - } - this.instance.UI.enableElements(elementsToToggle); - - if (this._selectedText.length > 2) { - this.instance.UI.enableElements([TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]); - } - - return; - } - - let elementsToDisable = [...elementsToToggle, TextPopups.ADD_RECTANGLE]; - - if (isCurrentPageExcluded) { - const allowedActionsWhenPageExcluded: string[] = [ - HeaderElements.ANNOTATION_POPUP, - TextPopups.ADD_RECTANGLE, - TextPopups.ADD_REDACTION, - HeaderElements.SHAPE_TOOL_GROUP_BUTTON, - ]; - elementsToDisable = elementsToDisable.filter(element => !allowedActionsWhenPageExcluded.includes(element)); - } else { - this.instance.UI.disableTools(['AnnotationCreateRectangle']); - } - - this.instance.UI.disableElements(elementsToDisable); - } - - private _getManualRedaction( - quads: Readonly>, - text?: string, - convertQuads = false, - ): IManualRedactionEntry { - const entry: IManualRedactionEntry = { positions: [] }; - - for (const key of Object.keys(quads)) { - for (const quad of quads[key]) { - const page = parseInt(key, 10); - const pageHeight = this.documentViewer.getPageHeight(page); - entry.positions.push(toPosition(page, pageHeight, convertQuads ? this.pdf.translateQuad(page, quad) : quad)); - } - } - - entry.value = text; - entry.rectangle = !text; - return entry; - } - - private async _loadDocument(blob: Blob, file: File) { - const document = await this.instance.Core.documentViewer.getDocument()?.getPDFDoc(); - await document?.lock(); - this.instance.UI.loadDocument(blob, { filename: file?.filename ?? 'document.pdf' }); - this._pageRotationService.clearRotationsHideActions(); - } - - private _setReadyAndInitialState(): void { - this._ngZone.run(() => { - this.viewerReady.emit(this.instance); - const routePageNumber: number = this._activatedRoute.snapshot.queryParams.page; - this.pageChanged.emit(routePageNumber || 1); - this._setInitialDisplayMode(); - this._updateTooltipsVisibility(); - }); - } - - private _updateTooltipsVisibility(): void { - const current = this._userPreferenceService.getFilePreviewTooltipsPreference(); - this.instance.UI.setAnnotationContentOverlayHandler(() => (current ? undefined : false)); - } -} diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts deleted file mode 100644 index 57e7f1772..000000000 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/file-preview.module.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { IqserIconsModule } from '@iqser/common-ui'; -import { TranslateModule } from '@ngx-translate/core'; -import { RouterModule, Routes } from '@angular/router'; -import { SharedModule } from '@shared/shared.module'; -import { SharedDossiersModule } from '../../shared/shared-dossiers.module'; -import { FilePreviewScreenComponent } from './file-preview-screen.component'; -import { FileWorkloadComponent } from './components/file-workload/file-workload.component'; -import { AnnotationDetailsComponent } from './components/annotation-details/annotation-details.component'; -import { AnnotationsListComponent } from './components/annotations-list/annotations-list.component'; -import { PageIndicatorComponent } from './components/page-indicator/page-indicator.component'; -import { PageExclusionComponent } from './components/page-exclusion/page-exclusion.component'; -import { PdfViewerComponent } from './components/pdf-viewer/pdf-viewer.component'; -import { AnnotationActionsComponent } from './components/annotation-actions/annotation-actions.component'; -import { CommentsComponent } from './components/comments/comments.component'; -import { DocumentInfoComponent } from './components/document-info/document-info.component'; -import { TypeAnnotationIconComponent } from './components/type-annotation-icon/type-annotation-icon.component'; -import { OverlayModule } from '@angular/cdk/overlay'; -import { ViewSwitchComponent } from './components/view-switch/view-switch.component'; -import { UserManagementComponent } from './components/user-management/user-management.component'; -import { AnnotationReferencesListComponent } from './components/annotation-references-list/annotation-references-list.component'; -import { AcceptRecommendationDialogComponent } from './dialogs/accept-recommendation-dialog/accept-recommendation-dialog.component'; -import { AnnotationCardComponent } from './components/annotation-card/annotation-card.component'; -import { AnnotationReferencesPageIndicatorComponent } from './components/annotation-references-page-indicator/annotation-references-page-indicator.component'; -import { HighlightsSeparatorComponent } from './components/highlights-separator/highlights-separator.component'; -import { PendingChangesGuard } from '../../../../guards/can-deactivate.guard'; -import { ManualRedactionService } from './services/manual-redaction.service'; - -const routes: Routes = [ - { - path: '', - component: FilePreviewScreenComponent, - pathMatch: 'full', - data: { reuse: true }, - canDeactivate: [PendingChangesGuard], - }, -]; - -const components = [ - FileWorkloadComponent, - AnnotationDetailsComponent, - AnnotationsListComponent, - PageIndicatorComponent, - PageExclusionComponent, - PdfViewerComponent, - AnnotationActionsComponent, - CommentsComponent, - DocumentInfoComponent, - TypeAnnotationIconComponent, - ViewSwitchComponent, - UserManagementComponent, - AcceptRecommendationDialogComponent, - AnnotationReferencesListComponent, - AnnotationCardComponent, - AnnotationReferencesPageIndicatorComponent, - HighlightsSeparatorComponent, -]; - -@NgModule({ - declarations: [FilePreviewScreenComponent, ...components], - imports: [ - RouterModule.forChild(routes), - CommonModule, - SharedModule, - SharedDossiersModule, - IqserIconsModule, - TranslateModule, - OverlayModule, - ], - providers: [ManualRedactionService], -}) -export class FilePreviewModule {} diff --git a/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts index 388bc218f..3e6a774f7 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/pdf-viewer/pdf-viewer.component.ts @@ -20,7 +20,6 @@ import { ManualRedactionEntryWrapper, } from '@models/file/manual-redaction-entry.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { ManualAnnotationService } from '@services/manual-annotation.service'; import { environment } from '@environments/environment'; import { AnnotationDrawService } from '../../services/annotation-draw.service'; import { AnnotationActionsService } from '../../services/annotation-actions.service'; @@ -44,6 +43,7 @@ import { from, fromEvent } from 'rxjs'; import { FileDataService } from '../../services/file-data.service'; import { ViewerHeaderConfigService } from '../../services/viewer-header-config.service'; import { TooltipsService } from '../../services/tooltips.service'; +import { ManualRedactionService } from '../../services/manual-redaction.service'; import Tools = Core.Tools; import TextTool = Tools.TextTool; import Annotation = Core.Annotations.Annotation; @@ -71,7 +71,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha constructor( @Inject(BASE_HREF) private readonly _baseHref: string, private readonly _translateService: TranslateService, - private readonly _manualAnnotationService: ManualAnnotationService, + private readonly _manualRedactionService: ManualRedactionService, private readonly _dialogService: FilePreviewDialogService, private readonly _ngZone: NgZone, private readonly _activatedRoute: ActivatedRoute, @@ -481,7 +481,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha } #getTitle(type: ManualRedactionEntryType) { - return this._translateService.instant(this._manualAnnotationService.getTitle(type, this.dossier)); + return this._translateService.instant(this._manualRedactionService.getTitle(type, this.dossier)); } private _addManualRedactionOfType(type: ManualRedactionEntryType) { diff --git a/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts index 0adc208b1..2acec7c55 100644 --- a/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts @@ -2,13 +2,13 @@ import { Component, Inject, Injector, OnInit } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper'; -import { ManualRedactionService } from '../../screens/file-preview-screen/services/manual-redaction.service'; import { JustificationsService } from '@services/entity-services/justifications.service'; import { Dictionary, Dossier, IAddRedactionRequest } from '@red/domain'; import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; import { DictionaryService } from '@services/entity-services/dictionary.service'; import { BaseDialogComponent, CircleButtonTypes } from '@iqser/common-ui'; import { firstValueFrom } from 'rxjs'; +import { ManualRedactionService } from '../../services/manual-redaction.service'; export interface LegalBasisOption { label?: string; diff --git a/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts b/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts index 2a923dab7..9cd2521c8 100644 --- a/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts +++ b/apps/red-ui/src/app/modules/file-preview/file-preview.module.ts @@ -35,6 +35,7 @@ import { HighlightActionDialogComponent } from './dialogs/highlight-action-dialo import { FilePreviewDialogService } from './services/file-preview-dialog.service'; import { ColorPickerModule } from 'ngx-color-picker'; import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component'; +import { ManualRedactionService } from './services/manual-redaction.service'; const routes: Routes = [ { @@ -75,10 +76,11 @@ const components = [ AnnotationCardComponent, AnnotationReferencesPageIndicatorComponent, HighlightsSeparatorComponent, + FilePreviewScreenComponent, ]; @NgModule({ - declarations: [FilePreviewScreenComponent, ...components, ...dialogs], + declarations: [...components, ...dialogs], imports: [ RouterModule.forChild(routes), CommonModule, @@ -89,6 +91,6 @@ const components = [ OverlayModule, ColorPickerModule, ], - providers: [FilePreviewDialogService], + providers: [FilePreviewDialogService, ManualRedactionService], }) export class FilePreviewModule {} diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/manual-redaction.service.ts b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts similarity index 77% rename from apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/manual-redaction.service.ts rename to apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts index c30e4066d..ea253885f 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/manual-redaction.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/manual-redaction.service.ts @@ -1,6 +1,7 @@ import { Injectable, Injector } from '@angular/core'; import type { AnnotationActionMode, + DictionaryActions, Dossier, IAddRedactionRequest, IApproveRequest, @@ -9,18 +10,23 @@ import type { IManualAddResponse, IRemoveRedactionRequest, IResizeRequest, + ManualRedactionActions, } from '@red/domain'; -import { type AnnotationWrapper } from '@models/file/annotation.wrapper'; +import { type AnnotationWrapper } from '../../../models/file/annotation.wrapper'; import { GenericService, RequiredParam, Toaster, Validate } from '@iqser/common-ui'; import { map, tap } from 'rxjs/operators'; -import { PermissionsService } from '@services/permissions.service'; -import { annotationActionsTranslations } from '../../../../../translations/annotation-actions-translations'; +import { PermissionsService } from '../../../services/permissions.service'; +import { + annotationActionsTranslations, + dictionaryActionsTranslations, + manualRedactionActionsTranslations, +} from '../../../translations/annotation-actions-translations'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { HttpErrorResponse, HttpStatusCode } from '@angular/common/http'; -import { ActiveDossiersService } from '@services/dossiers/active-dossiers.service'; +import { ActiveDossiersService } from '../../../services/dossiers/active-dossiers.service'; import { type Observable } from 'rxjs'; -import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; -import { type ManualRedactionEntryType } from '@models/file/manual-redaction-entry.wrapper'; +import { DictionariesMapService } from '../../../services/entity-services/dictionaries-map.service'; +import { type ManualRedactionEntryType } from '../../../models/file/manual-redaction-entry.wrapper'; function getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, error = false, isConflict = false) { const type = modifyDictionary ? 'dictionary' : 'manual-redaction'; @@ -28,8 +34,22 @@ function getMessage(mode: AnnotationActionMode, modifyDictionary?: boolean, erro return annotationActionsTranslations[type][mode][resultType]; } +function getResponseType(error: boolean, isConflict: boolean) { + return error ? (isConflict ? 'conflictError' : 'error') : 'success'; +} + +function getDictionaryMessage(action: DictionaryActions, error = false, isConflict = false) { + return dictionaryActionsTranslations[action][getResponseType(error, isConflict)]; +} + +function getManualRedactionMessage(action: ManualRedactionActions, error = false, isConflict = false) { + return manualRedactionActionsTranslations[action][getResponseType(error, isConflict)]; +} + @Injectable() export class ManualRedactionService extends GenericService { + readonly request = `${this._defaultModelPath}/request`; + readonly redaction = `${this._defaultModelPath}/redaction`; CONFIG: { [key in AnnotationActionMode]: string; }; @@ -153,6 +173,20 @@ export class ManualRedactionService extends GenericService { return this._makeRequest(mode, dossierId, fileId, request); } + _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, dossierId, fileId, request); + } + + _requestForce(request: ILegalBasisChangeRequest, dossierId: string, fileId: string) { + const mode: AnnotationActionMode = this._permissionsService.isApprover(this.#dossier(dossierId)) + ? 'force-redaction' + : 'request-force-redaction'; + return this._makeRequest(mode, dossierId, fileId, request); + } + approve(annotationId: string, dossierId: string, fileId: string, addToDictionary: boolean = false) { // for only here - approve the request return this._makeRequest( @@ -236,7 +270,7 @@ export class ManualRedactionService extends GenericService { @Validate() addRedaction(@RequiredParam() body: IAddRedactionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/redaction/add/${dossierId}/${fileId}`; + const url = `${this.redaction}/add/${dossierId}/${fileId}`; return this._post(body, url); } @@ -246,7 +280,7 @@ export class ManualRedactionService extends GenericService { @RequiredParam() dossierId: string, @RequiredParam() fileId: string, ) { - const url = `${this._defaultModelPath}/redaction/recategorize/${dossierId}/${fileId}`; + const url = `${this.redaction}/recategorize/${dossierId}/${fileId}`; return this._post(body, url); } @@ -256,13 +290,13 @@ export class ManualRedactionService extends GenericService { @RequiredParam() dossierId: string, @RequiredParam() fileId: string, ) { - const url = `${this._defaultModelPath}/request/recategorize/${dossierId}/${fileId}`; + const url = `${this.request}/recategorize/${dossierId}/${fileId}`; return this._post(body, url); } @Validate() legalBasisChange(@RequiredParam() body: ILegalBasisChangeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/redaction/legalBasisChange/${dossierId}/${fileId}`; + const url = `${this.redaction}/legalBasisChange/${dossierId}/${fileId}`; return this._post(body, url); } @@ -272,7 +306,7 @@ export class ManualRedactionService extends GenericService { @RequiredParam() dossierId: string, @RequiredParam() fileId: string, ) { - const url = `${this._defaultModelPath}/request/legalBasis/${dossierId}/${fileId}`; + const url = `${this.request}/legalBasis/${dossierId}/${fileId}`; return this._post(body, url); } @@ -282,7 +316,7 @@ export class ManualRedactionService extends GenericService { @RequiredParam() dossierId: string, @RequiredParam() fileId: string, ) { - const url = `${this._defaultModelPath}/request/remove/${dossierId}/${fileId}`; + const url = `${this.request}/remove/${dossierId}/${fileId}`; return this._post(body, url); } @@ -311,20 +345,17 @@ export class ManualRedactionService extends GenericService { @Validate() removeRedaction(@RequiredParam() body: IRemoveRedactionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/redaction/remove/${dossierId}/${fileId}`; - return this._post(body, url); + return this._post(body, `${this.redaction}/remove/${dossierId}/${fileId}`); } @Validate() requestAddRedaction(@RequiredParam() body: IAddRedactionRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/request/add/${dossierId}/${fileId}`; - return this._post(body, url); + return this._post(body, `${this.request}/add/${dossierId}/${fileId}`); } @Validate() forceRedaction(@RequiredParam() body: ILegalBasisChangeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/redaction/force/${dossierId}/${fileId}`; - return this._post(body, url); + return this._post(body, `${this.redaction}/force/${dossierId}/${fileId}`); } @Validate() @@ -333,41 +364,38 @@ export class ManualRedactionService extends GenericService { @RequiredParam() dossierId: string, @RequiredParam() fileId: string, ) { - const url = `${this._defaultModelPath}/request/force/${dossierId}/${fileId}`; - return this._post(body, url); + return this._post(body, `${this.request}/force/${dossierId}/${fileId}`); } @Validate() resize(@RequiredParam() body: IResizeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/redaction/resize/${dossierId}/${fileId}`; - return this._post(body, url); + return this._post(body, `${this.redaction}/resize/${dossierId}/${fileId}`); } @Validate() requestResize(@RequiredParam() body: IResizeRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) { - const url = `${this._defaultModelPath}/request/resize/${dossierId}/${fileId}`; - return this._post(body, url); + return this._post(body, `${this.request}/resize/${dossierId}/${fileId}`); } - // #showToast(body, dossierId: string) { - // return tap({ - // next: () => this._toaster.success(getMessage(mode, modifyDictionary), { positionClass: 'toast-file-preview' }), - // error: (error: HttpErrorResponse) => { - // const isConflict = error.status === HttpStatusCode.Conflict; - // this._toaster.error(getMessage(mode, modifyDictionary, true, isConflict), { - // error, - // params: { - // dictionaryName: this._dictionariesMapService.getDictionary( - // body.type as string, - // this.#dossier(dossierId).dossierTemplateId, - // ).label, - // content: body.value, - // }, - // positionClass: 'toast-file-preview', - // }); - // }, - // }); - // } + #showToast(mode: AnnotationActionMode, body, dossierId: string, modifyDictionary = false) { + return tap({ + next: () => this._toaster.success(getMessage(mode, modifyDictionary), { positionClass: 'toast-file-preview' }), + error: (error: HttpErrorResponse) => { + const isConflict = error.status === HttpStatusCode.Conflict; + this._toaster.error(getMessage(mode, modifyDictionary, true, isConflict), { + error, + params: { + dictionaryName: this._dictionariesMapService.getDictionary( + body.type as string, + this.#dossier(dossierId).dossierTemplateId, + ).label, + content: body.value, + }, + positionClass: 'toast-file-preview', + }); + }, + }); + } #dossier(dossierId: string): Dossier { return this._activeDossiersService.find(dossierId); diff --git a/apps/red-ui/src/app/translations/annotation-actions-translations.ts b/apps/red-ui/src/app/translations/annotation-actions-translations.ts index 168f860e0..b8287e31b 100644 --- a/apps/red-ui/src/app/translations/annotation-actions-translations.ts +++ b/apps/red-ui/src/app/translations/annotation-actions-translations.ts @@ -1,7 +1,13 @@ -import { AnnotationActionMode } from '@red/domain'; +import { AnnotationActionMode, DictionaryActions, ManualRedactionActions } from '@red/domain'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; -type ActionType = { [key in AnnotationActionMode]?: { error: string; success: string; conflictError?: string } }; +interface AnnotationActionResponses { + error: string; + success: string; + conflictError?: string; +} + +type ActionType = { [key in AnnotationActionMode]?: AnnotationActionResponses }; export const annotationActionsTranslations: { dictionary: ActionType; @@ -93,3 +99,90 @@ export const annotationActionsTranslations: { }, }, }; + +export const dictionaryActionsTranslations: Record = { + add: { + error: _('annotation-actions.message.dictionary.add.error'), + conflictError: _('annotation-actions.message.dictionary.add.conflict-error'), + success: _('annotation-actions.message.dictionary.add.success'), + }, + approve: { + error: _('annotation-actions.message.dictionary.approve.error'), + success: _('annotation-actions.message.dictionary.approve.success'), + }, + decline: { + error: _('annotation-actions.message.dictionary.decline.error'), + success: _('annotation-actions.message.dictionary.decline.success'), + }, + remove: { + error: _('annotation-actions.message.dictionary.remove.error'), + success: _('annotation-actions.message.dictionary.remove.success'), + }, + 'request-remove': { + error: _('annotation-actions.message.dictionary.request-remove.error'), + success: _('annotation-actions.message.dictionary.request-remove.success'), + }, + suggest: { + error: _('annotation-actions.message.dictionary.suggest.error'), + success: _('annotation-actions.message.dictionary.suggest.success'), + }, + undo: { + error: _('annotation-actions.message.dictionary.undo.error'), + success: _('annotation-actions.message.dictionary.undo.success'), + }, +} as const; + +export const manualRedactionActionsTranslations: Record = { + add: { + error: _('annotation-actions.message.manual-redaction.add.error'), + success: _('annotation-actions.message.manual-redaction.add.success'), + }, + approve: { + error: _('annotation-actions.message.manual-redaction.approve.error'), + success: _('annotation-actions.message.manual-redaction.approve.success'), + }, + 'change-legal-basis': { + error: _('annotation-actions.message.manual-redaction.change-legal-basis.error'), + success: _('annotation-actions.message.manual-redaction.change-legal-basis.success'), + }, + decline: { + error: _('annotation-actions.message.manual-redaction.decline.error'), + success: _('annotation-actions.message.manual-redaction.decline.success'), + }, + 'force-redaction': { + error: _('annotation-actions.message.manual-redaction.force-redaction.error'), + success: _('annotation-actions.message.manual-redaction.force-redaction.success'), + }, + 'recategorize-image': { + error: _('annotation-actions.message.manual-redaction.recategorize-image.error'), + success: _('annotation-actions.message.manual-redaction.recategorize-image.success'), + }, + 'request-change-legal-basis': { + error: _('annotation-actions.message.manual-redaction.request-change-legal-basis.error'), + success: _('annotation-actions.message.manual-redaction.request-change-legal-basis.success'), + }, + 'request-force-redaction': { + error: _('annotation-actions.message.manual-redaction.request-force-redaction.error'), + success: _('annotation-actions.message.manual-redaction.request-force-redaction.success'), + }, + 'request-image-recategorization': { + error: _('annotation-actions.message.manual-redaction.request-image-recategorization.error'), + success: _('annotation-actions.message.manual-redaction.request-image-recategorization.success'), + }, + suggest: { + error: _('annotation-actions.message.manual-redaction.suggest.error'), + success: _('annotation-actions.message.manual-redaction.suggest.success'), + }, + undo: { + error: _('annotation-actions.message.manual-redaction.undo.error'), + success: _('annotation-actions.message.manual-redaction.undo.success'), + }, + remove: { + error: _('annotation-actions.message.manual-redaction.remove.error'), + success: _('annotation-actions.message.manual-redaction.remove.success'), + }, + 'request-remove': { + error: _('annotation-actions.message.manual-redaction.request-remove.error'), + success: _('annotation-actions.message.manual-redaction.request-remove.success'), + }, +} as const; diff --git a/libs/red-domain/src/lib/annotations/types.ts b/libs/red-domain/src/lib/annotations/types.ts index 7c16837f6..d629a24fa 100644 --- a/libs/red-domain/src/lib/annotations/types.ts +++ b/libs/red-domain/src/lib/annotations/types.ts @@ -16,3 +16,19 @@ export type AnnotationActionMode = | 'request-force-redaction' | 'resize' | 'request-resize'; + +export type DictionaryActions = 'add' | 'approve' | 'remove' | 'decline' | 'request-remove' | 'suggest' | 'undo'; +export type ManualRedactionActions = + | 'add' + | 'approve' + | 'remove' + | 'change-legal-basis' + | 'decline' + | 'request-remove' + | 'request-change-legal-basis' + | 'recategorize-image' + | 'request-image-recategorization' + | 'suggest' + | 'undo' + | 'force-redaction' + | 'request-force-redaction';