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 e88883107..0d92b4ea4 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 @@ -46,6 +46,7 @@ import { PageExclusionComponent } from '../page-exclusion/page-exclusion.compone import { PagesComponent } from '../pages/pages.component'; import { ReadonlyBannerComponent } from '../readonly-banner/readonly-banner.component'; import { DocumentInfoComponent } from '../document-info/document-info.component'; +import { getLast } from '@utils/functions'; const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape']; const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown']; @@ -322,6 +323,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On this.pdf.navigateTo(this.#nextPageWithAnnotations()); } + @Debounce(15) navigateAnnotations($event: KeyboardEvent) { const currentPage = this.pdf.currentPage(); if (!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) { @@ -339,7 +341,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On const prevPage = this.#prevPageWithAnnotations(); const prevPageAnnotations = this.displayedAnnotations.get(prevPage); - return this.listingService.selectAnnotations(prevPageAnnotations[prevPageAnnotations.length - 1]); + return this.listingService.selectAnnotations(getLast(prevPageAnnotations)); } const page = this._firstSelectedAnnotation.pageNumber; @@ -376,7 +378,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On for (let i = previousPageIdx; i >= 0; i--) { const prevPageAnnotations = this.displayedAnnotations.get(this.displayedPages[i]); if (prevPageAnnotations) { - this.listingService.selectAnnotations(prevPageAnnotations[prevPageAnnotations.length - 1]); + this.listingService.selectAnnotations(getLast(prevPageAnnotations)); break; } } diff --git a/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts b/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts index fee463b7a..596994c1c 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/annotations-listing.service.ts @@ -1,17 +1,16 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper'; -import { Injectable, OnDestroy } from '@angular/core'; +import { effect, Injectable, untracked } from '@angular/core'; import { EntitiesService, ListingService, SearchService } from '@iqser/common-ui'; -import { filter, tap } from 'rxjs/operators'; import { MultiSelectService } from './multi-select.service'; import { PdfViewer } from '../../pdf-viewer/services/pdf-viewer.service'; import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service'; -import { Subscription } from 'rxjs'; import { FilterService } from '@iqser/common-ui/lib/filtering'; import { SortingService } from '@iqser/common-ui/lib/sorting'; +import { toSignal } from '@angular/core/rxjs-interop'; @Injectable() -export class AnnotationsListingService extends ListingService implements OnDestroy { - readonly #subscriptions: Subscription; +export class AnnotationsListingService extends ListingService { + readonly selectedLength = toSignal(this.selectedLength$); constructor( protected readonly _filterService: FilterService, @@ -24,23 +23,22 @@ export class AnnotationsListingService extends ListingService ) { super(_filterService, _searchService, _entitiesService, _sortingService); - this.#subscriptions = this.selectedLength$ - .pipe( - filter(length => length > 1), - tap(() => this._multiSelectService.activate()), - ) - .subscribe(); - } - - ngOnDestroy() { - this.#subscriptions.unsubscribe(); + effect( + () => { + if (this.selectedLength() > 1) { + this._multiSelectService.activate(); + } + }, + { allowSignalWrites: true }, + ); } selectAnnotations(annotations: AnnotationWrapper[] | AnnotationWrapper) { annotations = Array.isArray(annotations) ? annotations : [annotations]; const pageNumber = annotations[annotations.length - 1].pageNumber; - const annotationsToSelect = this._multiSelectService.active() ? [...this.selected, ...annotations] : annotations; + const multiSelectActive = untracked(this._multiSelectService.active); + const annotationsToSelect = multiSelectActive ? [...this.selected, ...annotations] : annotations; this.#selectAnnotations(annotationsToSelect, pageNumber); } @@ -49,16 +47,18 @@ export class AnnotationsListingService extends ListingService return; } - if (this._multiSelectService.inactive()) { + const multiSelectInactive = untracked(this._multiSelectService.inactive); + if (multiSelectInactive) { this._annotationManager.deselect(); } - if (pageNumber === this._pdf.currentPage()) { + const currentPage = untracked(this._pdf.currentPage); + if (pageNumber === currentPage) { return this._annotationManager.jumpAndSelect(annotations); } this._pdf.navigateTo(pageNumber); // wait for page to be loaded and to draw annotations - setTimeout(() => this._annotationManager.jumpAndSelect(annotations), 300); + setTimeout(() => this._annotationManager.jumpAndSelect(annotations), 10); } } diff --git a/apps/red-ui/src/app/modules/file-preview/services/multi-select.service.ts b/apps/red-ui/src/app/modules/file-preview/services/multi-select.service.ts index 650e4cde9..c1217ab55 100644 --- a/apps/red-ui/src/app/modules/file-preview/services/multi-select.service.ts +++ b/apps/red-ui/src/app/modules/file-preview/services/multi-select.service.ts @@ -1,4 +1,4 @@ -import { computed, Injectable, Signal, signal } from '@angular/core'; +import { computed, Injectable, Signal, signal, untracked } from '@angular/core'; import { ViewModeService } from './view-mode.service'; import { FilePreviewStateService } from './file-preview-state.service'; import { ViewMode, ViewModes } from '@red/domain'; @@ -13,13 +13,17 @@ export class MultiSelectService { readonly #active = signal(false); - constructor(protected readonly _viewModeService: ViewModeService, protected readonly _state: FilePreviewStateService) { + constructor( + protected readonly _viewModeService: ViewModeService, + protected readonly _state: FilePreviewStateService, + ) { this.active = this.#active.asReadonly(); this.inactive = computed(() => !this.#active()); } activate() { - if (this.enabled()) { + const enabled = untracked(this.enabled); + if (enabled) { this.#active.set(true); } }