diff --git a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts index 682952d93..5b34c0aa6 100644 --- a/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts +++ b/apps/red-ui/src/app/modules/file-preview/components/file-workload/file-workload.component.ts @@ -32,6 +32,7 @@ import { AnnotationsListingService } from '../../services/annotations-listing.se import { REDDocumentViewer } from '../../../pdf-viewer/services/document-viewer.service'; import { SuggestionsService } from '../../services/suggestions.service'; import { ListItem } from '@models/file/list-item'; +import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -76,6 +77,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy private readonly _annotationProcessingService: AnnotationProcessingService, private readonly _viewModeService: ViewModeService, private readonly _suggestionsService: SuggestionsService, + private readonly _pageRotationService: PageRotationService, ) { super(); @@ -160,6 +162,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy secondary$, this.listingService.selected$, this.multiSelectService.active$, + this._pageRotationService.rotations$, ]).pipe( delay(0), map(([annotations, primary, secondary]) => this._filterAnnotations(annotations, primary, secondary)), diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts index ee9c58efc..b32c3b2a2 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotation-processing.service.ts @@ -10,6 +10,13 @@ import { DefaultColorsService } from '@services/entity-services/default-colors.s import { of } from 'rxjs'; import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service'; import { FileDataService } from './file-data.service'; +import { PageRotationService } from '../../pdf-viewer/services/page-rotation.service'; +import { + sortBottomLeftToTopRight, + sortBottomRightToTopLeft, + sortTopLeftToBottomRight, + sortTopRightToBottomLeft, +} from '../utils/sort-by-page-rotation.utils'; @Injectable() export class AnnotationProcessingService { @@ -18,6 +25,7 @@ export class AnnotationProcessingService { private readonly _fileDataService: FileDataService, private readonly _state: FilePreviewStateService, private readonly _defaultColorsService: DefaultColorsService, + private readonly _pageRotationService: PageRotationService, ) {} get secondaryAnnotationFilters(): INestedFilter[] { @@ -155,7 +163,7 @@ export class AnnotationProcessingService { obj.forEach((values, page) => { if (!values[0].isEarmark) { - obj.set(page, this._sortAnnotations(values)); + obj.set(page, this.#sortAnnotations(values)); } }); @@ -223,18 +231,24 @@ export class AnnotationProcessingService { private _checkByFilterKey = (filter: NestedFilter | IFilter, annotation: AnnotationWrapper) => filter.id === annotation.filterKey; - private _sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] { + #sortAnnotations(annotations: AnnotationWrapper[]): AnnotationWrapper[] { + const pageRotation = this._pageRotationService.currentPageRotation; return annotations.sort((first, second) => { if (first.pageNumber === second.pageNumber) { - if (first.y > second.y) { - return -1; + switch (pageRotation) { + case 0: { + return sortTopLeftToBottomRight(first, second); + } + case 90: { + return sortBottomLeftToTopRight(first, second); + } + case 180: { + return sortBottomRightToTopLeft(first, second); + } + case 270: { + return sortTopRightToBottomLeft(first, second); + } } - - if (first.y < second.y) { - return 1; - } - - return first.x < second.x ? -1 : 1; } return first.pageNumber < second.pageNumber ? -1 : 1; }); diff --git a/apps/red-ui/src/app/modules/file-preview/utils/sort-by-page-rotation.utils.ts b/apps/red-ui/src/app/modules/file-preview/utils/sort-by-page-rotation.utils.ts new file mode 100644 index 000000000..4ccce0755 --- /dev/null +++ b/apps/red-ui/src/app/modules/file-preview/utils/sort-by-page-rotation.utils.ts @@ -0,0 +1,41 @@ +import { AnnotationWrapper } from '@models/file/annotation.wrapper'; + +export const sortTopLeftToBottomRight = (a1: AnnotationWrapper, a2: AnnotationWrapper): number => { + if (a1.y > a2.y) { + return -1; + } + if (a1.y < a2.y) { + return 1; + } + return a1.x < a2.x ? -1 : 1; +}; + +export const sortBottomLeftToTopRight = (a1: AnnotationWrapper, a2: AnnotationWrapper): number => { + if (a1.x < a2.x) { + return -1; + } + if (a1.x > a2.x) { + return 1; + } + return a1.y < a2.y ? -1 : 1; +}; + +export const sortBottomRightToTopLeft = (a1: AnnotationWrapper, a2: AnnotationWrapper): number => { + if (a1.y < a2.y) { + return -1; + } + if (a1.y > a2.y) { + return 1; + } + return a1.x > a2.x ? -1 : 1; +}; + +export const sortTopRightToBottomLeft = (a1: AnnotationWrapper, a2: AnnotationWrapper): number => { + if (a1.x > a2.x) { + return -1; + } + if (a1.x < a2.x) { + return 1; + } + return a1.y > a2.y ? -1 : 1; +}; diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts index ed2ed54dd..596ae33fc 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/document-viewer.service.ts @@ -60,6 +60,10 @@ export class REDDocumentViewer { ); } + get currentPageRotation() { + return this.document?.getPageRotation(this.#document.getCurrentPage()); + } + get #keyUp$() { return fromEvent(this.#document, 'keyUp').pipe( tap(stopAndPreventIfNotAllowed), diff --git a/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts b/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts index e56d9afd1..bd3e17b5d 100644 --- a/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts +++ b/apps/red-ui/src/app/modules/pdf-viewer/services/page-rotation.service.ts @@ -1,5 +1,5 @@ import { Injectable, Injector } from '@angular/core'; -import { BehaviorSubject, firstValueFrom, of } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs'; import { RotationType } from '@red/domain'; import { FileManagementService } from '@services/files/file-management.service'; import { distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators'; @@ -22,6 +22,7 @@ import { REDDocumentViewer } from './document-viewer.service'; @Injectable() export class PageRotationService { readonly #rotations$ = new BehaviorSubject>({}); + readonly rotations$: Observable>; constructor( private readonly _pdf: PdfViewer, @@ -32,12 +33,21 @@ export class PageRotationService { private readonly _filesService: FilesService, private readonly _filesMapService: FilesMapService, private readonly _documentViewer: REDDocumentViewer, - ) {} + ) { + this.rotations$ = this.#rotations$.asObservable(); + } get hasRotations() { return Object.values(this.#rotations$.value).filter(v => !!v).length > 0; } + get currentPageRotation() { + const savedRotation = this._documentViewer.currentPageRotation; + const currentRotation = this.#rotations[this._pdf.currentPage] ?? 0; + const rotationsSum = savedRotation + currentRotation; + return Math.abs(rotationsSum < 360 ? rotationsSum : 360 - rotationsSum); + } + isRotated$(page: number) { return this.#rotations$.pipe( map(rotations => !!rotations[page]), @@ -91,6 +101,10 @@ export class PageRotationService { return this.hasRotations ? this.#showConfirmationDialog() : of(false); } + get #rotations() { + return this.#rotations$.value; + } + #showConfirmationDialog() { const ref = this._injector.get(MatDialog).open(ConfirmationDialogComponent, { ...defaultDialogConfig,