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 index 6c88b3c2c..7b28c7eed 100644 --- 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 @@ -358,7 +358,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha dataElement: HeaderElements.APPLY_ROTATION, render: () => { const paragraph = document.createElement('p'); - paragraph.innerText = 'APPLY'; + paragraph.innerText = this._translateService.instant('page-rotation.apply'); paragraph.style.cssText = ` font-size: 11px; font-weight: 600; @@ -377,7 +377,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha dataElement: HeaderElements.DISCARD_ROTATION, render: () => { const paragraph = document.createElement('p'); - paragraph.innerText = 'DISCARD'; + paragraph.innerText = this._translateService.instant('page-rotation.discard'); paragraph.style.cssText = ` font-size: 11px; font-weight: 600; 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 98be732c8..0f14c28c2 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 @@ -50,6 +50,7 @@ import { FilePreviewStateService } from './services/file-preview-state.service'; import { FileDataModel } from '../../../../models/file/file-data.model'; import { filePreviewScreenProviders } from './file-preview-providers'; import { ManualAnnotationService } from '../../services/manual-annotation.service'; +import { PageRotationService } from './services/page-rotation.service'; import Annotation = Core.Annotations.Annotation; import PDFNet = Core.PDFNet; @@ -106,6 +107,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni private readonly _dossiersService: DossiersService, private readonly _reanalysisService: ReanalysisService, private readonly _errorService: ErrorService, + private readonly _pageRotationService: PageRotationService, private readonly _skippedService: SkippedService, private readonly _manualAnnotationService: ManualAnnotationService, readonly excludedPagesService: ExcludedPagesService, @@ -215,6 +217,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } ngOnDetach(): void { + this._pageRotationService.clearRotations(); this.displayPdfViewer = false; super.ngOnDetach(); this._changeDetectorRef.markForCheck(); @@ -387,11 +390,12 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni } } - viewerPageChanged($event: any) { + async viewerPageChanged($event: any) { if (typeof $event !== 'number') { return; } + await firstValueFrom(this._pageRotationService.showConfirmationDialogIfHasRotations()); this._scrollViews(); this.multiSelectService.deactivate(); @@ -401,7 +405,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni queryParamsHandling: 'merge', replaceUrl: true, }; - this._router.navigate([], extras).then(); + await this._router.navigate([], extras); this._setActiveViewerPage(); this._changeDetectorRef.markForCheck(); diff --git a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/page-rotation.service.ts b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/page-rotation.service.ts index d0a72f8c1..dec43e358 100644 --- a/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/page-rotation.service.ts +++ b/apps/red-ui/src/app/modules/dossier/screens/file-preview-screen/services/page-rotation.service.ts @@ -1,70 +1,78 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject, firstValueFrom } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, of } from 'rxjs'; import { PermissionsService } from '@services/permissions.service'; import { RotationType, RotationTypes } from '@red/domain'; import { FileManagementService } from '@services/entity-services/file-management.service'; import { FilePreviewStateService } from './file-preview-state.service'; -import { distinctUntilChanged, map } from 'rxjs/operators'; +import { distinctUntilChanged, map, tap } from 'rxjs/operators'; import { PdfViewer } from './pdf-viewer.service'; import { HeaderElements } from '../shared/constants'; +import { + ConfirmationDialogComponent, + ConfirmationDialogInput, + ConfirmOptions, + defaultDialogConfig, + LoadingService, +} from '@iqser/common-ui'; +import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; +import { MatDialog } from '@angular/material/dialog'; -const confirmationButtons = [HeaderElements.APPLY_ROTATION, HeaderElements.DISCARD_ROTATION]; +const actionButtons = [HeaderElements.APPLY_ROTATION, HeaderElements.DISCARD_ROTATION]; const oneRotationDegree = 90; @Injectable() export class PageRotationService { - readonly rotations$ = new BehaviorSubject>({}); + readonly #rotations$ = new BehaviorSubject>({}); constructor( + private readonly _pdf: PdfViewer, + private readonly _dialog: MatDialog, + private readonly _loadingService: LoadingService, + private readonly _screenState: FilePreviewStateService, private readonly _permissionsService: PermissionsService, private readonly _fileManagementService: FileManagementService, - private readonly _screenState: FilePreviewStateService, - private readonly _pdf: PdfViewer, ) {} - get canRotate() { - return this._screenState.file.then(file => this._permissionsService.isFileAssignee(file)); - } - isRotated(page: number) { - return this.rotations$.pipe( + return this.#rotations$.pipe( map(rotations => !!rotations[page]), distinctUntilChanged(), ); } hasRotations() { - return Object.values(this.rotations$.value).filter(v => !!v).length > 0; + return Object.values(this.#rotations$.value).filter(v => !!v).length > 0; } applyRotation() { - const pages = this.rotations$.value; + this._loadingService.start(); + const pages = this.#rotations$.value; const { dossierId, fileId } = this._screenState; const request = this._fileManagementService.rotatePage({ pages }, dossierId, fileId); - this.clearRotations(); + this.clearRotationsHideActions(); - return firstValueFrom(request); + return firstValueFrom(request.pipe(tap(() => this._loadingService.stop()))); } discardRotation() { - const rotations = this.rotations$.value; + const rotations = this.#rotations$.value; for (const page of Object.keys(rotations)) { const times = rotations[page] / oneRotationDegree; for (let i = 1; i <= times; i++) { - this._pdf.documentViewer.rotateCounterClockwise(Number(page)); + this._pdf?.documentViewer?.rotateCounterClockwise(Number(page)); } } - this.clearRotations(); + this.clearRotationsHideActions(); } addRotation(rotation: RotationType): void { const pageNumber = this._pdf.currentPage; - const pageRotation = this.rotations$.value[pageNumber]; + const pageRotation = this.#rotations$.value[pageNumber]; const rotationValue = pageRotation ? (pageRotation + Number(rotation)) % 360 : rotation; - this.rotations$.next({ ...this.rotations$.value, [pageNumber]: rotationValue }); + this.#rotations$.next({ ...this.#rotations$.value, [pageNumber]: rotationValue }); if (rotation === RotationTypes.LEFT) { this._pdf.documentViewer.rotateCounterClockwise(pageNumber); @@ -73,22 +81,46 @@ export class PageRotationService { } if (this.hasRotations()) { - this.#showConfirmationButtons(); + this.#showActionButtons(); } else { - this.#hideConfirmationButtons(); + this.#hideActionButtons(); } } clearRotations() { - this.rotations$.next({}); - this.#hideConfirmationButtons(); + this.#rotations$.next({}); } - #showConfirmationButtons() { - this._pdf.UI.enableElements(confirmationButtons); + clearRotationsHideActions() { + this.clearRotations(); + this.#hideActionButtons(); } - #hideConfirmationButtons() { - this._pdf.UI.disableElements(confirmationButtons); + showConfirmationDialog() { + const ref = this._dialog.open(ConfirmationDialogComponent, { + ...defaultDialogConfig, + data: new ConfirmationDialogInput({ + title: _('page-rotation.confirmation-dialog.title'), + question: _('page-rotation.confirmation-dialog.question'), + confirmationText: _('page-rotation.apply'), + discardChangesText: _('page-rotation.discard'), + }), + }); + + return ref + .afterClosed() + .pipe(tap((option: ConfirmOptions) => (option === ConfirmOptions.CONFIRM ? this.applyRotation() : this.discardRotation()))); + } + + showConfirmationDialogIfHasRotations() { + return this.hasRotations() ? this.showConfirmationDialog() : of(ConfirmOptions.DISCARD_CHANGES); + } + + #showActionButtons() { + this._pdf.UI.enableElements(actionButtons); + } + + #hideActionButtons() { + this._pdf.UI.disableElements(actionButtons); } } diff --git a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts index 227422d94..e32acc196 100644 --- a/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts +++ b/apps/red-ui/src/app/modules/dossier/shared/components/file-actions/file-actions.component.ts @@ -39,6 +39,7 @@ import { DocumentInfoService } from '../../../screens/file-preview-screen/servic import { ExpandableFileActionsComponent } from '@shared/components/expandable-file-actions/expandable-file-actions.component'; import { firstValueFrom } from 'rxjs'; import { RedactionImportService } from '../../services/redaction-import.service'; +import { PageRotationService } from '../../../screens/file-preview-screen/services/page-rotation.service'; @Component({ selector: 'redaction-file-actions [file] [type]', @@ -92,6 +93,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, constructor( @Optional() private readonly _excludedPagesService: ExcludedPagesService, @Optional() private readonly _documentInfoService: DocumentInfoService, + @Optional() private readonly _pageRotationService: PageRotationService, private readonly _permissionsService: PermissionsService, private readonly _dossiersService: DossiersService, private readonly _dialogService: DossiersDialogService, @@ -200,7 +202,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, }, { type: ActionTypes.circleBtn, - action: $event => this.toggleAutomaticAnalysis($event), + action: $event => this._toggleAutomaticAnalysis($event), tooltip: _('dossier-overview.stop-auto-analysis'), icon: 'red:disable-analysis', show: this.canDisableAutoAnalysis, @@ -215,7 +217,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, }, { type: ActionTypes.circleBtn, - action: $event => this.toggleAutomaticAnalysis($event), + action: $event => this._toggleAutomaticAnalysis($event), tooltip: _('dossier-overview.start-auto-analysis'), buttonType: this.isFilePreview ? CircleButtonTypes.warn : CircleButtonTypes.default, icon: 'red:enable-analysis', @@ -363,7 +365,7 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, await firstValueFrom(this._reanalysisService.reanalyzeFilesForDossier([this.file.fileId], this.file.dossierId, params)); } - private async toggleAutomaticAnalysis($event: MouseEvent) { + private async _toggleAutomaticAnalysis($event: MouseEvent) { $event.stopPropagation(); this._loadingService.start(); await firstValueFrom(this._reanalysisService.toggleAutomaticAnalysis(this.file.dossierId, [this.file])); @@ -377,6 +379,9 @@ export class FileActionsComponent extends AutoUnsubscribe implements OnDestroy, private async _ocrFile($event: MouseEvent) { $event.stopPropagation(); + if (this._pageRotationService) { + await firstValueFrom(this._pageRotationService.showConfirmationDialogIfHasRotations()); + } this._loadingService.start(); await firstValueFrom(this._reanalysisService.ocrFiles([this.file.fileId], this.file.dossierId)); this._loadingService.stop(); diff --git a/apps/red-ui/src/assets/i18n/de.json b/apps/red-ui/src/assets/i18n/de.json index 579a0d666..e2ffa3b79 100644 --- a/apps/red-ui/src/assets/i18n/de.json +++ b/apps/red-ui/src/assets/i18n/de.json @@ -733,7 +733,6 @@ "documents-status": "", "dossier-status": "", "name": "Name", - "last-modified": "", "needs-work": "Arbeitsvorrat", "owner": "Besitzer" }, @@ -754,7 +753,6 @@ "delete": { "action": "Datei löschen" }, - "stop-auto-analysis": "", "dossier-details": { "attributes": { "expand": "{count} {count, plural, one{benutzerdefiniertes Attribut} other{benutzerdefinierte Attribute}}", @@ -778,7 +776,6 @@ }, "download-file": "Herunterladen", "download-file-disabled": "Nur genehmigte Dateien können heruntergeladen werden", - "start-auto-analysis": "", "file-listing": { "file-entry": { "file-error": "Reanalyse erforderlich", @@ -824,6 +821,8 @@ "reanalyse": { "action": "Datei analysieren" }, + "start-auto-analysis": "", + "stop-auto-analysis": "", "table-col-names": { "added-on": "Hinzugefügt", "assigned-to": "Zugewiesen an", @@ -1573,6 +1572,14 @@ "title": "Das Dokument existiert bereits!" }, "page": "Seite", + "page-rotation": { + "apply": "", + "confirmation-dialog": { + "question": "", + "title": "" + }, + "discard": "" + }, "pagination": { "next": "Nächste", "previous": "Vorherige" diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json index b6be5b8c1..4351b2c12 100644 --- a/apps/red-ui/src/assets/i18n/en.json +++ b/apps/red-ui/src/assets/i18n/en.json @@ -733,7 +733,6 @@ "documents-status": "Documents Status", "dossier-status": "Dossier Status", "name": "Name", - "last-modified": "Last modified", "needs-work": "Workload", "owner": "Owner" }, @@ -754,7 +753,6 @@ "delete": { "action": "Delete File" }, - "stop-auto-analysis": "Stop auto-analysis", "dossier-details": { "attributes": { "expand": "{count} custom {count, plural, one{attribute} other{attributes}}", @@ -778,7 +776,6 @@ }, "download-file": "Download", "download-file-disabled": "You need to be approver in the dossier and the {count, plural, one{file needs} other{files need}} to be approved in order to download.", - "start-auto-analysis": "Start auto-analysis", "file-listing": { "file-entry": { "file-error": "Re-processing required", @@ -824,6 +821,8 @@ "reanalyse": { "action": "Analyze File" }, + "start-auto-analysis": "Start auto-analysis", + "stop-auto-analysis": "Stop auto-analysis", "table-col-names": { "added-on": "Added", "assigned-to": "Assigned to", @@ -1573,6 +1572,14 @@ "title": "Document already exists!" }, "page": "Page", + "page-rotation": { + "apply": "APPLY", + "confirmation-dialog": { + "question": "You have unapplied page rotations. Choose how to proceed:", + "title": "Pending page rotations" + }, + "discard": "DISCARD" + }, "pagination": { "next": "Next", "previous": "Prev"