RED-6829: remove other observables

This commit is contained in:
Dan Percic 2023-06-11 01:11:22 +03:00
parent 58976f24d8
commit e89d317fa0
28 changed files with 202 additions and 264 deletions

View File

@ -1,5 +1,4 @@
export interface ListItem<T> {
item: T;
isSelected: boolean;
multiSelectActive: boolean;
}

View File

@ -62,7 +62,7 @@
<iqser-circle-button
(action)="annotationActionsService.convertHighlights(annotations)"
*ngIf="(viewModeService.viewMode$ | async) === 'TEXT_HIGHLIGHTS'"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.convert-highlights.label' | translate"
[type]="buttonType"
@ -71,7 +71,7 @@
<iqser-circle-button
(action)="annotationActionsService.removeHighlights(annotations)"
*ngIf="(viewModeService.viewMode$ | async) === 'TEXT_HIGHLIGHTS'"
*ngIf="viewModeService.isEarmarks()"
[tooltipPosition]="tooltipPosition"
[tooltip]="'annotation-actions.remove-highlights.label' | translate"
[type]="buttonType"

View File

@ -1,11 +1,11 @@
import { Component, Input, OnChanges } from '@angular/core';
import { Component, inject, Input, OnChanges } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { annotationChangesTranslations } from '@translations/annotation-changes-translations';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { KeysOf } from '@iqser/common-ui';
import { AnnotationsListingService } from '../../services/annotations-listing.service';
import { ListItem } from '@models/file/list-item';
import { MultiSelectService } from '../../services/multi-select.service';
interface Engine {
readonly icon: string;
@ -48,7 +48,8 @@ export class AnnotationDetailsComponent implements OnChanges {
changesTooltip: string;
noSelection: boolean;
constructor(private readonly _translateService: TranslateService, private readonly _listingService: AnnotationsListingService) {}
private readonly _translateService = inject(TranslateService);
private readonly _multiSelectService = inject(MultiSelectService);
getChangesTooltip(): string | undefined {
const changes = changesProperties.filter(key => this.annotation.item[key]);
@ -65,7 +66,7 @@ export class AnnotationDetailsComponent implements OnChanges {
ngOnChanges() {
this.engines = this.#extractEngines(this.annotation.item).filter(engine => engine.show);
this.changesTooltip = this.getChangesTooltip();
this.noSelection = !this.annotation.isSelected || !this.annotation.multiSelectActive;
this.noSelection = !this.annotation.isSelected || this._multiSelectService.inactive();
}
#extractEngines(annotation: AnnotationWrapper): Engine[] {

View File

@ -20,11 +20,11 @@
{{ annotation.item.comments.length }}
</div>
<div *ngIf="!annotation.multiSelectActive" class="actions">
<div *ngIf="_multiSelectService.inactive()" class="actions">
<redaction-annotation-actions
[annotations]="[annotation.item]"
[attr.help-mode-key]="actionsHelpModeKey"
[canPerformAnnotationActions]="pdfProxyService.canPerformActions()"
[canPerformAnnotationActions]="_pdfProxyService.canPerformActions()"
></redaction-annotation-actions>
</div>
</div>

View File

@ -1,8 +1,9 @@
import { Component, HostBinding, Input, OnChanges } from '@angular/core';
import { Component, HostBinding, inject, Input, OnChanges } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { ActionsHelpModeKeys } from '../../utils/constants';
import { ListItem } from '@models/file/list-item';
import { MultiSelectService } from '../../services/multi-select.service';
@Component({
selector: 'redaction-annotation-wrapper',
@ -15,8 +16,8 @@ export class AnnotationWrapperComponent implements OnChanges {
@HostBinding('attr.annotation-id') annotationId: string;
@HostBinding('class.active') active = false;
actionsHelpModeKey?: string;
constructor(readonly pdfProxyService: PdfProxyService) {}
protected readonly _pdfProxyService = inject(PdfProxyService);
protected readonly _multiSelectService = inject(MultiSelectService);
ngOnChanges() {
this.annotationId = this.annotation.item.id;

View File

@ -1,11 +1,10 @@
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { ChangeDetectorRef, Component, computed, ElementRef, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { FilterService, HasScrollbarDirective, IqserEventTarget } from '@iqser/common-ui';
import { MultiSelectService } from '../../services/multi-select.service';
import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { ViewModeService } from '../../services/view-mode.service';
import { BehaviorSubject } from 'rxjs';
import { EarmarkGroup } from '@red/domain';
import { REDAnnotationManager } from '../../../pdf-viewer/services/annotation-manager.service';
import { AnnotationsListingService } from '../../services/annotations-listing.service';
@ -18,10 +17,14 @@ import { ListItem } from '@models/file/list-item';
})
export class AnnotationsListComponent extends HasScrollbarDirective implements OnChanges {
@Input() annotations: ListItem<AnnotationWrapper>[];
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
readonly earmarkGroups$ = new BehaviorSubject<EarmarkGroup[]>([]);
readonly #earmarkGroups = computed(() => {
if (this._viewModeService.isEarmarks()) {
return this.#getEarmarksGroups();
}
return [] as EarmarkGroup[];
});
constructor(
protected readonly _elementRef: ElementRef,
@ -37,16 +40,6 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
super(_elementRef, _changeDetector);
}
ngOnChanges(): void {
if (this._viewModeService.isEarmarks()) {
this._updateEarmarksGroups();
}
setTimeout(() => {
super.ngOnChanges();
}, 0);
}
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
if (this._userPreferenceService.areDevFeaturesEnabled) {
console.log('Selected Annotation:', annotation);
@ -78,16 +71,18 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
}
showHighlightGroup(idx: number): EarmarkGroup {
return this._viewModeService.isEarmarks() && this.earmarkGroups$.value.find(h => h.startIdx === idx);
return this._viewModeService.isEarmarks() && this.#earmarkGroups().find(h => h.startIdx === idx);
}
protected readonly _trackBy = (index: number, listItem: ListItem<AnnotationWrapper>) => listItem.item.id;
private _updateEarmarksGroups(): void {
if (!this.annotations?.length) {
return;
}
#getEarmarksGroups() {
const earmarksGroups: EarmarkGroup[] = [];
if (!this.annotations?.length) {
return earmarksGroups;
}
let lastGroup: EarmarkGroup;
for (let idx = 0; idx < this.annotations.length; ++idx) {
if (idx === 0 || this.annotations[idx].item.color !== this.annotations[idx - 1].item.color) {
@ -99,7 +94,8 @@ export class AnnotationsListComponent extends HasScrollbarDirective implements O
lastGroup.length += 1;
}
}
earmarksGroups.push(lastGroup);
this.earmarkGroups$.next(earmarksGroups);
return earmarksGroups;
}
}

View File

@ -1,4 +1,8 @@
<div *ngIf="showExcludedPages$ | async; else selectAndFilter" class="right-title heading" translate="file-preview.tabs.exclude-pages.label">
<div
*ngIf="excludedPagesService.shown(); else selectAndFilter"
class="right-title heading"
translate="file-preview.tabs.exclude-pages.label"
>
<div>
<iqser-circle-button
(action)="excludedPagesService.toggle()"
@ -11,7 +15,7 @@
<ng-template #selectAndFilter>
<div class="right-title heading">
{{ title$ | async | translate }}
{{ title() | translate }}
<div>
<div
(click)="multiSelectService.activate()"
@ -32,11 +36,7 @@
</ng-template>
<div class="right-content">
<redaction-readonly-banner
*ngIf="state.isReadonly()"
[file]="file"
[isDossierActive]="state.dossier().isActive"
></redaction-readonly-banner>
<redaction-readonly-banner *ngIf="state.isReadonly()"></redaction-readonly-banner>
<div *ngIf="multiSelectService.active()" class="multi-select">
<div class="selected-wrapper">
@ -76,7 +76,7 @@
>
<div
(click)="scrollQuickNavFirst()"
[class.disabled]="activeViewerPage === 1"
[class.disabled]="pdf.currentPage() === 1"
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
class="jump"
matTooltipPosition="above"
@ -86,14 +86,13 @@
<redaction-pages
(click)="pagesPanelActive = true"
[activePage]="activeViewerPage"
[displayedAnnotations]="displayedAnnotations"
[pages]="displayedPages"
></redaction-pages>
<div
(click)="scrollQuickNavLast()"
[class.disabled]="activeViewerPage === file?.numberOfPages"
[class.disabled]="pdf.currentPage() === state.file()?.numberOfPages"
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
class="jump"
matTooltipPosition="above"
@ -104,15 +103,15 @@
<div class="content">
<div
*ngIf="(isEarmarks$ | async) === false"
[attr.anotation-page-header]="activeViewerPage"
[hidden]="excludedPagesService.shown$ | async"
*ngIf="!viewModeService.isEarmarks()"
[attr.anotation-page-header]="pdf.currentPage()"
[hidden]="excludedPagesService.shown()"
class="workload-separator"
>
<span *ngIf="!!activeViewerPage" class="flex-align-items-center">
<span *ngIf="!!pdf.currentPage()" class="flex-align-items-center">
<iqser-circle-button
(action)="excludedPagesService.toggle()"
*ngIf="currentPageIsExcluded"
*ngIf="currentPageIsExcluded()"
[size]="14"
[tooltip]="'file-preview.excluded-from-redaction' | translate | capitalize"
class="mr-10 primary"
@ -121,7 +120,7 @@
></iqser-circle-button>
<span
[translateParams]="{ page: activeViewerPage, count: activeAnnotations.length }"
[translateParams]="{ page: pdf.currentPage(), count: activeAnnotations.length }"
[translate]="'page'"
class="all-caps-label"
></span>
@ -146,19 +145,19 @@
(keydown)="preventKeyDefault($event)"
(keyup)="preventKeyDefault($event)"
[class.active-panel]="!pagesPanelActive"
[hidden]="excludedPagesService.shown$ | async"
[hidden]="excludedPagesService.shown()"
class="annotations"
id="annotations-list"
tabindex="1"
>
<ng-container *ngIf="activeViewerPage && !displayedAnnotations.get(activeViewerPage)?.length">
<ng-container *ngIf="pdf.currentPage() && !displayedAnnotations.get(pdf.currentPage())?.length">
<iqser-empty-state
[horizontalPadding]="24"
[text]="'file-preview.no-data.title' | translate"
[verticalPadding]="40"
icon="iqser:document"
>
<ng-container *ngIf="currentPageIsExcluded && displayedPages.length">
<ng-container *ngIf="currentPageIsExcluded() && displayedPages.length">
{{ 'file-preview.tabs.annotations.page-is' | translate }}
<a
(click)="excludedPagesService.toggle()"
@ -182,7 +181,7 @@
<div *ngIf="displayedPages.length" class="no-annotations-buttons-container mt-32">
<iqser-icon-button
(action)="jumpToPreviousWithAnnotations()"
[disabled]="activeViewerPage <= displayedPages[0]"
[disabled]="pdf.currentPage() <= displayedPages[0]"
[label]="'file-preview.tabs.annotations.jump-to-previous' | translate"
[type]="iconButtonTypes.dark"
icon="red:nav-prev"
@ -190,7 +189,7 @@
<iqser-icon-button
(action)="jumpToNextWithAnnotations()"
[disabled]="activeViewerPage >= displayedPages[displayedPages.length - 1]"
[disabled]="pdf.currentPage() >= displayedPages[displayedPages.length - 1]"
[label]="'file-preview.tabs.annotations.jump-to-next' | translate"
[type]="iconButtonTypes.dark"
class="mt-8"
@ -201,11 +200,11 @@
<redaction-annotations-list
(pagesPanelActive)="pagesPanelActive = $event"
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
[annotations]="(displayedAnnotations$ | async)?.get(pdf.currentPage())"
></redaction-annotations-list>
</div>
<redaction-page-exclusion *ngIf="showExcludedPages$ | async"></redaction-page-exclusion>
<redaction-page-exclusion *ngIf="excludedPagesService.shown()"></redaction-page-exclusion>
</div>
</div>
</div>
@ -214,7 +213,7 @@
<iqser-circle-button
(action)="skippedService.toggleSkipped()"
*ngIf="filter.id === 'skipped'"
[icon]="(skippedService.hideSkipped$ | async) ? 'red:visibility-off' : 'red:visibility'"
[icon]="skippedService.hideSkipped() ? 'red:visibility-off' : 'red:visibility'"
[type]="circleButtonTypes.dark"
preventDefault
></iqser-circle-button>

View File

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, effect, ElementRef, HostListener, Input, OnDestroy, ViewChild } from '@angular/core';
import { ChangeDetectorRef, Component, computed, effect, ElementRef, HostListener, OnDestroy, ViewChild } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { MatDialog } from '@angular/material/dialog';
@ -12,11 +12,9 @@ import {
IconButtonTypes,
INestedFilter,
IqserEventTarget,
shareLast,
} from '@iqser/common-ui';
import { combineLatest, delay, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { File } from '@red/domain';
import { ExcludedPagesService } from '../../services/excluded-pages.service';
import { MultiSelectService } from '../../services/multi-select.service';
import { DocumentInfoService } from '../../services/document-info.service';
@ -32,7 +30,6 @@ import { REDDocumentViewer } from '../../../pdf-viewer/services/document-viewer.
import { SuggestionsService } from '../../services/suggestions.service';
import { ListItem } from '@models/file/list-item';
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
import { toObservable } from '@angular/core/rxjs-interop';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
@ -43,9 +40,6 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
styleUrls: ['./file-workload.component.scss'],
})
export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy {
@Input() activeViewerPage: number;
@Input({ required: true }) file!: File;
readonly iconButtonTypes = IconButtonTypes;
readonly circleButtonTypes = CircleButtonTypes;
@ -53,9 +47,10 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
displayedPages: number[] = [];
pagesPanelActive = true;
readonly displayedAnnotations$: Observable<Map<number, ListItem<AnnotationWrapper>[]>>;
readonly showExcludedPages$: Observable<boolean>;
readonly title$: Observable<string>;
readonly isEarmarks$: Observable<boolean>;
readonly title = computed(() =>
this.viewModeService.isEarmarks() ? _('file-preview.tabs.highlights.label') : _('file-preview.tabs.annotations.label'),
);
readonly currentPageIsExcluded = computed(() => this.state.file().excludedPages.includes(this.pdf.currentPage()));
@ViewChild('annotationsElement') private readonly _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private readonly _quickNavigationElement: ElementRef;
@ -75,7 +70,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
readonly excludedPagesService: ExcludedPagesService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _annotationProcessingService: AnnotationProcessingService,
private readonly _viewModeService: ViewModeService,
private readonly _suggestionsService: SuggestionsService,
private readonly _pageRotationService: PageRotationService,
) {
@ -99,47 +93,30 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
});
this.displayedAnnotations$ = this._displayedAnnotations$;
this.showExcludedPages$ = this._showExcludedPages$;
this.isEarmarks$ = this._isEarmarks$;
this.title$ = this._title$;
effect(() => {
if (this.multiSelectService.inactive()) {
this.annotationManager.deselect();
}
});
effect(() => {
const __ = this.viewModeService.viewMode();
this._scrollViews();
});
effect(
() => {
if (this.excludedPagesService.shown()) {
this._disableMultiSelectAndDocumentInfo();
}
},
{ allowSignalWrites: true },
);
}
get activeAnnotations(): AnnotationWrapper[] {
return this.displayedAnnotations.get(this.activeViewerPage) || [];
}
get currentPageIsExcluded(): boolean {
return this.file?.excludedPages?.includes(this.activeViewerPage);
}
private get _showExcludedPages$() {
return this.excludedPagesService.shown$.pipe(
tap(show => {
if (show) {
this._disableMultiSelectAndDocumentInfo();
}
}),
shareLast(),
);
}
private get _title$(): Observable<string> {
return this.isEarmarks$.pipe(
map(isHighlights => (isHighlights ? _('file-preview.tabs.highlights.label') : _('file-preview.tabs.annotations.label'))),
);
}
private get _isEarmarks$(): Observable<boolean> {
return this.viewModeService.viewMode$.pipe(
tap(() => this._scrollViews()),
map(() => this.viewModeService.isEarmarks()),
);
return this.displayedAnnotations.get(this.pdf.currentPage()) || [];
}
private get _firstSelectedAnnotation() {
@ -155,7 +132,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
primary$,
secondary$,
this.listingService.selected$,
toObservable(this.multiSelectService.active),
this._pageRotationService.rotations$,
]).pipe(
delay(0),
@ -166,7 +142,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
get #allPages() {
return Array.from({ length: this.file?.numberOfPages }, (_x, i) => i + 1);
return Array.from({ length: this.state.file()?.numberOfPages }, (_x, i) => i + 1);
}
private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') {
@ -232,10 +208,11 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
scrollAnnotations(): void {
if (this._firstSelectedAnnotation?.pageNumber === this.activeViewerPage) {
const currentPage = this.pdf.currentPage();
if (this._firstSelectedAnnotation?.pageNumber === currentPage) {
return;
}
this.scrollAnnotationsToPage(this.activeViewerPage, 'always');
this.scrollAnnotationsToPage(currentPage, 'always');
}
scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed'): void {
@ -257,8 +234,9 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
scrollQuickNavigation(): void {
let quickNavPageIndex = this.displayedPages.findIndex(p => p >= this.activeViewerPage);
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== this.activeViewerPage) {
const currentPage = this.pdf.currentPage();
let quickNavPageIndex = this.displayedPages.findIndex(p => p >= currentPage);
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== currentPage) {
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
}
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
@ -287,8 +265,9 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
navigateAnnotations($event: KeyboardEvent) {
if (!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) {
if (this.displayedPages.indexOf(this.activeViewerPage) !== -1) {
const currentPage = this.pdf.currentPage();
if (!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) {
if (this.displayedPages.indexOf(currentPage) !== -1) {
// Displayed page has annotations
return this.listingService.selectAnnotations(this.activeAnnotations ? this.activeAnnotations[0] : null);
}
@ -362,12 +341,13 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
primary: INestedFilter[],
secondary: INestedFilter[] = [],
): Map<number, AnnotationWrapper[]> {
const onlyPageWithAnnotations = this.viewModeService.onlyPagesWithAnnotations();
if (!primary || primary.length === 0) {
this.displayedPages = this.viewModeService.onlyPagesWithAnnotations ? [] : this.#allPages;
this.displayedPages = onlyPageWithAnnotations ? [] : this.#allPages;
return;
}
if (this._viewModeService.isRedacted()) {
if (this.viewModeService.isRedacted()) {
annotations = annotations.filter(a => !bool(a.isChangeLogRemoved));
annotations = this._suggestionsService.filterWorkloadSuggestionsInPreview(annotations);
}
@ -376,13 +356,13 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
const pagesThatDisplayAnnotations = [...this.displayedAnnotations.keys()];
const enabledFilters = this.filterService.enabledFlatFilters;
if (enabledFilters.some(f => f.id === 'pages-without-annotations')) {
if (enabledFilters.length === 1 && !this.viewModeService.onlyPagesWithAnnotations) {
if (enabledFilters.length === 1 && !onlyPageWithAnnotations) {
this.displayedPages = this.#allPages.filter(page => !pagesThatDisplayAnnotations.includes(page));
} else {
this.displayedPages = [];
}
this.displayedAnnotations.clear();
} else if (enabledFilters.length || this.viewModeService.onlyPagesWithAnnotations) {
} else if (enabledFilters.length || onlyPageWithAnnotations) {
this.displayedPages = pagesThatDisplayAnnotations;
} else {
this.displayedPages = this.#allPages;
@ -393,9 +373,10 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
const currentPage = this.pdf.currentPage();
if (
(!this._firstSelectedAnnotation || this.activeViewerPage !== this._firstSelectedAnnotation.pageNumber) &&
this.displayedPages.indexOf(this.activeViewerPage) >= 0 &&
(!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) &&
this.displayedPages.indexOf(currentPage) >= 0 &&
this.activeAnnotations.length > 0
) {
this.listingService.selectAnnotations(this.activeAnnotations[0]);
@ -403,7 +384,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
}
private _navigatePages($event: KeyboardEvent) {
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
const pageIdx = this.displayedPages.indexOf(this.pdf.currentPage());
if ($event.key !== 'ArrowDown') {
if (pageIdx === -1) {
@ -437,7 +418,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
private _nextPageWithAnnotations() {
let idx = 0;
for (const page of this.displayedPages) {
if (page > this.activeViewerPage && this.displayedAnnotations.get(page)) {
if (page > this.pdf.currentPage() && this.displayedAnnotations.get(page)) {
break;
}
++idx;
@ -449,7 +430,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
let idx = this.displayedPages.length - 1;
const reverseDisplayedPages = [...this.displayedPages].reverse();
for (const page of reverseDisplayedPages) {
if (page < this.activeViewerPage && this.displayedAnnotations.get(page)) {
if (page < this.pdf.currentPage() && this.displayedAnnotations.get(page)) {
break;
}
--idx;
@ -473,7 +454,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
const newValue = annotations.get(key).map(annotation => ({
item: annotation,
isSelected: this.listingService.isSelected(annotation),
multiSelectActive: this.multiSelectService.active(),
}));
listItemsMap.set(key, newValue);
});

View File

@ -2,7 +2,7 @@
(click)="pageSelected.emit(number)"
(dblclick)="toggleReadState()"
*ngIf="componentContext$ | async"
[class.active]="active"
[class.active]="isActive"
[class.read]="read"
[id]="'quick-nav-page-' + number"
class="page-wrapper"
@ -13,7 +13,7 @@
<div *ngIf="activeSelection" class="dot"></div>
<div *ngIf="isRotated" class="rotated">
<div *ngIf="pageRotationService.rotations()[number]" class="rotated">
<mat-icon svgIcon="red:rotation"></mat-icon>
</div>
</div>

View File

@ -1,4 +1,4 @@
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { ViewedPagesService } from '@services/files/viewed-pages.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
@ -8,6 +8,7 @@ import { map, tap } from 'rxjs/operators';
import { AppConfig, ViewedPage } from '@red/domain';
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
import { pairwise } from 'rxjs';
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
interface PageIndicatorContext {
isRotated: boolean;
@ -15,46 +16,42 @@ interface PageIndicatorContext {
}
@Component({
selector: 'redaction-page-indicator [number] [read]',
selector: 'redaction-page-indicator',
templateUrl: './page-indicator.component.html',
styleUrls: ['./page-indicator.component.scss'],
})
export class PageIndicatorComponent extends ContextComponent<PageIndicatorContext> implements OnChanges, OnInit {
@Input() active = false;
@Input({ required: true }) number: number;
@Input() showDottedIcon = false;
@Input() number: number;
@Input() activeSelection = false;
@Input() read = false;
@Output() readonly pageSelected = new EventEmitter<number>();
pageReadTimeout: number = null;
isRotated = false;
readonly #config = getConfig<AppConfig>();
constructor(
private readonly _viewedPagesService: ViewedPagesService,
private readonly _viewedPagesMapService: ViewedPagesMapService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _permissionService: PermissionsService,
private readonly _state: FilePreviewStateService,
private readonly _pdf: PdfViewer,
readonly pageRotationService: PageRotationService,
) {
super();
}
get isActive() {
return this.number === this._pdf.currentPage();
}
ngOnInit() {
const isRotated$ = this.pageRotationService.isRotated$(this.number).pipe(
tap(value => {
this.isRotated = value;
this._changeDetectorRef.detectChanges();
}),
);
const assigneeChanged$ = this._state.file$.pipe(
pairwise(),
map(([prevFile, currFile]) => prevFile.assignee !== currFile.assignee),
tap(assigneeChanged => assigneeChanged && this.handlePageRead()),
);
super._initContext({ isRotated: isRotated$, assigneeChanged: assigneeChanged$ });
super._initContext({ assigneeChanged: assigneeChanged$ });
}
ngOnChanges() {
@ -80,9 +77,9 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
clearTimeout(this.pageReadTimeout);
}
if (this.active && !this.read) {
if (this.isActive && !this.read) {
this.pageReadTimeout = window.setTimeout(async () => {
if (this.active && !this.read) {
if (this.isActive && !this.read) {
await this.#markPageRead();
}
}, this.#config.AUTO_READ_TIME * 1000);

View File

@ -3,7 +3,6 @@
(pageSelected)="pageSelectedByClick($event)"
*ngFor="let pageNumber of pages; trackBy: trackBy"
[activeSelection]="pageHasSelection(pageNumber)"
[active]="pageNumber === activePage"
[number]="pageNumber"
[read]="!!getViewedPage(viewedPages, pageNumber)"
[showDottedIcon]="hasOnlyManualRedactionsAndIsExcluded(pageNumber)"

View File

@ -9,22 +9,21 @@ import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service'
import { ViewedPage } from '@red/domain';
@Component({
selector: 'redaction-pages [pages] [activePage] [displayedAnnotations]',
selector: 'redaction-pages',
templateUrl: './pages.component.html',
styleUrls: ['./pages.component.scss'],
})
export class PagesComponent {
@Input() pages: List<number>;
@Input() activePage: number;
@Input() displayedAnnotations: Map<number, AnnotationWrapper[]>;
readonly #pdf = inject(PdfViewer);
@Input({ required: true }) pages: List<number>;
@Input({ required: true }) displayedAnnotations: Map<number, AnnotationWrapper[]>;
protected readonly _pdf = inject(PdfViewer);
readonly #state = inject(FilePreviewStateService);
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
readonly #multiSelectService = inject(MultiSelectService);
readonly #listingService = inject(AnnotationsListingService);
pageSelectedByClick($event: number): void {
this.#pdf.navigateTo($event);
this._pdf.navigateTo($event);
}
readonly trackBy = (_index: number, item: number) => item;

View File

@ -1,4 +1,4 @@
<div class="justify-center banner read-only flex">
<div *ngIf="_state.file() as file" class="justify-center banner read-only flex">
<div *ngIf="file.isFullProcessing" class="ocr-indicator">
<ng-container *ngIf="file.isOcrProcessing; else defaultProcessing">
<redaction-ocr-progress-bar
@ -16,6 +16,6 @@
<div class="flex-center">
<mat-icon class="primary-white" svgIcon="red:read-only"></mat-icon>
<span [translate]="isDossierActive ? 'readonly' : 'readonly-archived'" class="read-only-text"></span>
<span [translate]="_state.dossier().isActive ? 'readonly' : 'readonly-archived'" class="read-only-text"></span>
</div>
</div>

View File

@ -1,5 +1,5 @@
import { Component, Input } from '@angular/core';
import { File } from '@red/domain';
import { Component, inject } from '@angular/core';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
@Component({
selector: 'redaction-readonly-banner',
@ -7,6 +7,5 @@ import { File } from '@red/domain';
styleUrls: ['./readonly-banner.component.scss'],
})
export class ReadonlyBannerComponent {
@Input() file: File;
@Input() isDossierActive: boolean;
protected readonly _state = inject(FilePreviewStateService);
}

View File

@ -1,12 +1,12 @@
<ng-container *ngIf="state.file$ | async as file">
<ng-container *ngIf="state.file() as file">
<iqser-empty-state
*ngIf="file.excluded && (documentInfoService.hidden$ | async) && excludedPagesService.hidden$ | async"
*ngIf="file.excluded && documentInfoService.hidden() && excludedPagesService.hidden()"
[horizontalPadding]="40"
[text]="'file-preview.tabs.is-excluded' | translate"
icon="red:needs-work"
></iqser-empty-state>
<redaction-document-info *ngIf="documentInfoService.shown$ | async" id="document-info"></redaction-document-info>
<redaction-document-info *ngIf="documentInfoService.shown()" id="document-info"></redaction-document-info>
<redaction-file-workload *ngIf="!file.excluded" [activeViewerPage]="pdf.currentPage$ | async" [file]="file"></redaction-file-workload>
<redaction-file-workload *ngIf="!file.excluded"></redaction-file-workload>
</ng-container>

View File

@ -150,7 +150,7 @@ export class FilePreviewScreenComponent
}
get changed() {
return this._pageRotationService.hasRotations;
return this._pageRotationService.hasRotations();
}
get #textSelected$() {
@ -162,7 +162,7 @@ export class FilePreviewScreenComponent
return textSelected$.pipe(
tap(([selectedText, canPerformActions, file]) => {
const isCurrentPageExcluded = file.isPageExcluded(this.pdf.currentPage);
const isCurrentPageExcluded = file.isPageExcluded(this.pdf.currentPage());
if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
this.pdf.enable(textActions);
@ -192,7 +192,7 @@ export class FilePreviewScreenComponent
);
return currentPageEarmarks$.pipe(
map(earmarks => [earmarks, this._skippedService.hideSkipped, this.state.dossierTemplateId] as const),
map(earmarks => [earmarks, this._skippedService.hideSkipped(), this.state.dossierTemplateId] as const),
tap(args => this._annotationDrawService.draw(...args)),
);
}
@ -204,7 +204,7 @@ export class FilePreviewScreenComponent
);
return isChangingFromEarmarksViewMode$.pipe(
map(() => this._fileDataService.earmarks().get(this.pdf.currentPage) ?? []),
map(() => this._fileDataService.earmarks().get(this.pdf.currentPage()) ?? []),
map(earmarks => this.deleteAnnotations(earmarks, [])),
);
}
@ -505,7 +505,7 @@ export class FilePreviewScreenComponent
}
#getAnnotationsToDraw(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
const currentPage = this.pdf.currentPage;
const currentPage = this.pdf.currentPage();
const currentPageAnnotations = this._annotationManager.get(a => a.getPageNumber() === currentPage);
const existingAnnotations = [];
for (const annotation of currentPageAnnotations) {
@ -578,11 +578,11 @@ export class FilePreviewScreenComponent
});
}
private _setExcludedPageStyles() {
#setExcludedPageStyles() {
const file = this._filesMapService.get(this.dossierId, this.fileId);
setTimeout(() => {
const iframeDoc = this.pdf.instance.UI.iframeWindow.document;
const currentPage = this.pdf.currentPage;
const currentPage = this.pdf.currentPage();
const elementId = `pageWidgetContainer${currentPage}`;
const pageContainer = iframeDoc.getElementById(elementId);
if (pageContainer) {
@ -616,7 +616,7 @@ export class FilePreviewScreenComponent
.subscribe();
this.addActiveScreenSubscription = this._documentViewer.pageComplete$.subscribe(() => {
this._setExcludedPageStyles();
this.#setExcludedPageStyles();
});
this.addActiveScreenSubscription = this._documentViewer.keyUp$.subscribe($event => {
@ -749,7 +749,7 @@ export class FilePreviewScreenComponent
this.#handleDeltaAnnotationFilters(currentFilters);
}
this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped, this.state.dossierTemplateId).then();
this._annotationDrawService.draw(newAnnotations, this._skippedService.hideSkipped(), this.state.dossierTemplateId).then();
}
#handleDeltaAnnotationFilters(currentFilters: NestedFilter[]) {

View File

@ -224,7 +224,7 @@ export class AnnotationActionsService {
async cancelResize(annotationWrapper: AnnotationWrapper) {
this._annotationManager.resizingAnnotationId = undefined;
this._annotationManager.delete(annotationWrapper);
await this._annotationDrawService.draw([annotationWrapper], this._skippedService.hideSkipped, this._state.dossierTemplateId);
await this._annotationDrawService.draw([annotationWrapper], this._skippedService.hideSkipped(), this._state.dossierTemplateId);
this._annotationManager.deselect();
await this._fileDataService.annotationsChanged();
}

View File

@ -51,7 +51,7 @@ export class AnnotationsListingService extends ListingService<AnnotationWrapper>
this._annotationManager.deselect();
}
if (pageNumber === this._pdf.currentPage) {
if (pageNumber === this._pdf.currentPage()) {
return this._annotationManager.jumpAndSelect(annotations);
}

View File

@ -1,7 +1,6 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, merge, Observable } from 'rxjs';
import { shareLast } from '@iqser/common-ui';
import { map, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { computed, effect, Injectable, Signal, signal } from '@angular/core';
import { merge } from 'rxjs';
import { map, startWith, withLatestFrom } from 'rxjs/operators';
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { FilesMapService } from '@services/files/files-map.service';
@ -11,9 +10,9 @@ import { ExcludedPagesService } from './excluded-pages.service';
@Injectable()
export class DocumentInfoService {
readonly shown$: Observable<boolean>;
readonly hidden$: Observable<boolean>;
private readonly _show$ = new BehaviorSubject(false);
readonly shown: Signal<boolean>;
readonly hidden: Signal<boolean>;
readonly #show$ = signal(false);
constructor(
private readonly _dossierTemplatesService: DossierTemplatesService,
@ -22,19 +21,14 @@ export class DocumentInfoService {
private readonly _multiSelectService: MultiSelectService,
private readonly _excludedPagesService: ExcludedPagesService,
) {
this.shown$ = this._show$.asObservable().pipe(
tap(show => {
if (show) {
this._multiSelectService.deactivate();
this._excludedPagesService.hide();
}
}),
shareLast(),
);
this.hidden$ = this.shown$.pipe(
map(value => !value),
shareLast(),
);
this.hidden = computed(() => !this.#show$());
this.shown = this.#show$.asReadonly();
effect(() => {
if (this.shown()) {
this._multiSelectService.deactivate();
this._excludedPagesService.hide();
}
});
}
fileAttributes$(fileId: string, dossierId: string, dossierTemplateId: string) {
@ -50,15 +44,15 @@ export class DocumentInfoService {
}
show() {
this._show$.next(true);
this.#show$.set(true);
}
hide() {
this._show$.next(false);
this.#show$.set(false);
}
toggle() {
this._show$.next(!this._show$.value);
this.#show$.set(!this.#show$());
}
private _toAttributes(attributes: IFileAttributeConfig[], file: File) {

View File

@ -1,28 +1,25 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { shareDistinctLast } from '@iqser/common-ui';
import { map } from 'rxjs/operators';
import { computed, Injectable, Signal, signal } from '@angular/core';
@Injectable()
export class ExcludedPagesService {
readonly shown$: Observable<boolean>;
readonly hidden$: Observable<boolean>;
private readonly _show$ = new BehaviorSubject(false);
readonly shown: Signal<boolean>;
readonly hidden: Signal<boolean>;
readonly #show = signal(false);
constructor() {
this.shown$ = this._show$.asObservable().pipe(shareDistinctLast());
this.hidden$ = this.shown$.pipe(map(value => !value));
this.shown = this.#show.asReadonly();
this.hidden = computed(() => !this.#show());
}
show() {
this._show$.next(true);
this.#show.set(true);
}
hide() {
this._show$.next(false);
this.#show.set(false);
}
toggle() {
this._show$.next(!this._show$.value);
this.#show.set(!this.#show());
}
}

View File

@ -191,7 +191,7 @@ export class PdfProxyService {
}
private _handleCustomActions() {
const isCurrentPageExcluded = this._state.file().isPageExcluded(this._pdf.currentPage);
const isCurrentPageExcluded = this._state.file().isPageExcluded(this._pdf.currentPage());
if (this._viewModeService.isRedacted()) {
this._viewerHeaderService.enable([HeaderElements.TOGGLE_READABLE_REDACTIONS]);
@ -266,7 +266,7 @@ export class PdfProxyService {
// Remove deselected annotations from selected list
nextAnnotations = this._annotationManager.selected.filter(ann => !annotations.some(a => a.Id === ann.Id));
this._pdf.disable(TextPopups.ADD_RECTANGLE);
const currentPage = this._pdf.currentPage;
const currentPage = this._pdf.currentPage();
if (nextAnnotations.some(a => a.getPageNumber() === currentPage)) {
this.#configureAnnotationSpecificActions(nextAnnotations);
} else {

View File

@ -1,36 +1,26 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { skip, tap } from 'rxjs/operators';
import { bool, shareDistinctLast } from '@iqser/common-ui';
import { effect, Injectable, Signal, signal } from '@angular/core';
import { bool } from '@iqser/common-ui';
import { REDAnnotationManager } from '../../pdf-viewer/services/annotation-manager.service';
@Injectable()
export class SkippedService {
readonly hideSkipped$: Observable<boolean>;
readonly #hideSkipped$ = new BehaviorSubject(false);
readonly hideSkipped: Signal<boolean>;
readonly #hideSkipped = signal(false);
constructor(private readonly _annotationManager: REDAnnotationManager) {
this.hideSkipped$ = this.#hideSkipped$.pipe(
tap(hideSkipped => this._handleIgnoreAnnotationsDrawing(hideSkipped)),
shareDistinctLast(),
skip(1),
);
}
this.hideSkipped = this.#hideSkipped.asReadonly();
get hideSkipped(): boolean {
return this.#hideSkipped$.value;
effect(() => {
const ignored = this._annotationManager.get(a => bool(a.getCustomData('skipped')));
if (this.#hideSkipped()) {
this._annotationManager.hide(ignored);
} else {
this._annotationManager.show(ignored);
}
});
}
toggleSkipped(): void {
this.#hideSkipped$.next(!this.hideSkipped);
}
private _handleIgnoreAnnotationsDrawing(hideSkipped: boolean): void {
const ignored = this._annotationManager.get(a => bool(a.getCustomData('skipped')));
if (hideSkipped) {
this._annotationManager.hide(ignored);
} else {
this._annotationManager.show(ignored);
}
this.#hideSkipped.set(!this.#hideSkipped());
}
}

View File

@ -12,16 +12,15 @@ export class ViewModeService {
readonly isStandard = computed(() => this.viewMode() === ViewModes.STANDARD);
readonly isDelta = computed(() => this.viewMode() === ViewModes.DELTA);
readonly #viewMode = signal<ViewMode>(ViewModes.STANDARD);
readonly onlyPagesWithAnnotations = computed(() =>
([ViewModes.DELTA, ViewModes.TEXT_HIGHLIGHTS] as ViewMode[]).includes(this.#viewMode()),
);
constructor() {
this.viewMode$ = toObservable(this.#viewMode);
this.viewMode = this.#viewMode.asReadonly();
}
get onlyPagesWithAnnotations() {
return ([ViewModes.DELTA, ViewModes.TEXT_HIGHLIGHTS] as ViewMode[]).includes(this.#viewMode());
}
is(viewMode: ViewMode) {
return this.viewMode() === viewMode;
}

View File

@ -164,7 +164,7 @@ export class REDDocumentViewer {
pages.forEach(page => this.#document.setRotation(0, Number(page)));
}
rotate(rotation: RotationType, page = this.#pdf.currentPage) {
rotate(rotation: RotationType, page = this.#pdf.currentPage()) {
if (rotation === RotationTypes.LEFT) {
this.#document.rotateCounterClockwise(page);
} else {

View File

@ -1,8 +1,8 @@
import { Injectable, Injector } from '@angular/core';
import { BehaviorSubject, firstValueFrom, Observable, of } from 'rxjs';
import { computed, Injectable, Injector, Signal, signal } from '@angular/core';
import { firstValueFrom, Observable, of } from 'rxjs';
import { RotationType } from '@red/domain';
import { FileManagementService } from '@services/files/file-management.service';
import { distinctUntilChanged, map, tap } from 'rxjs/operators';
import { map, tap } from 'rxjs/operators';
import {
ConfirmationDialogComponent,
ConfirmOption,
@ -18,11 +18,14 @@ import { PdfViewer } from './pdf-viewer.service';
import { FilesMapService } from '@services/files/files-map.service';
import { NGXLogger } from 'ngx-logger';
import { REDDocumentViewer } from './document-viewer.service';
import { toObservable } from '@angular/core/rxjs-interop';
@Injectable()
export class PageRotationService {
readonly rotations$: Observable<Record<number, number>>;
readonly #rotations$ = new BehaviorSubject<Record<number, number>>({});
readonly rotations: Signal<Record<number, number>>;
readonly hasRotations: Signal<boolean>;
readonly #rotations = signal<Record<number, number>>({});
constructor(
private readonly _pdf: PdfViewer,
@ -34,34 +37,21 @@ export class PageRotationService {
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;
this.rotations$ = toObservable(this.#rotations);
this.rotations = this.#rotations.asReadonly();
this.hasRotations = computed(() => Object.values(this.#rotations()).filter(v => !!v).length > 0);
}
get currentPageRotation() {
const savedRotation = this._documentViewer.currentPageRotation;
const currentRotation = this.#rotations[this._pdf.currentPage] ?? 0;
const currentRotation = this.#rotations()[this._pdf.currentPage()] ?? 0;
const rotationsSum = savedRotation + currentRotation;
return Math.abs(rotationsSum < 360 ? rotationsSum : 360 - rotationsSum);
}
get #rotations() {
return this.#rotations$.value;
}
isRotated$(page: number) {
return this.#rotations$.pipe(
map(rotations => !!rotations[page]),
distinctUntilChanged(),
);
}
async applyRotation() {
this._loadingService.start();
const pages = this.#rotations$.value;
const pages = this.#rotations();
const { dossierId, fileId } = this._pdf;
if (!dossierId || !fileId) {
@ -84,26 +74,26 @@ export class PageRotationService {
}
discardRotation() {
this._documentViewer.resetRotation(Object.keys(this.#rotations$.value));
this._documentViewer.resetRotation(Object.keys(this.#rotations()));
this.clearRotations();
}
addRotation(rotation: RotationType): void {
const pageNumber = this._pdf.currentPage;
const pageRotation = this.#rotations$.value[pageNumber];
const pageNumber = this._pdf.currentPage();
const pageRotation = this.#rotations()[pageNumber];
const rotationValue = pageRotation ? (pageRotation + Number(rotation)) % 360 : rotation;
this.#rotations$.next({ ...this.#rotations$.value, [pageNumber]: rotationValue });
this.#rotations.update(value => ({ ...value, [pageNumber]: rotationValue }));
this._documentViewer.rotate(rotation);
}
clearRotations() {
this.#rotations$.next({});
this.#rotations.set({});
}
showConfirmationDialogIfHasRotations() {
return this.hasRotations ? this.#showConfirmationDialog() : of(false);
return this.hasRotations() ? this.#showConfirmationDialog() : of(false);
}
#showConfirmationDialog() {

View File

@ -13,7 +13,7 @@ import { TranslateService } from '@ngx-translate/core';
import { LicenseService } from '@services/license.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { environment } from '@environments/environment';
import { takeUntilDestroyed, toObservable } from '@angular/core/rxjs-interop';
import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
import TextTool = Core.Tools.TextTool;
import Annotation = Core.Annotations.Annotation;
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
@ -26,6 +26,7 @@ export class PdfViewer {
map(params => Number(params.get('page') ?? '1')),
shareDistinctLast(),
);
readonly currentPage = toSignal(this.currentPage$);
documentViewer: DocumentViewer;
@ -81,10 +82,6 @@ export class PdfViewer {
}
}
get currentPage() {
return this.#adjustPage(this.#currentInternalPage);
}
get #totalPages$() {
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged').pipe(startWith(''));
const docLoaded$ = fromEvent(this.documentViewer, 'documentLoaded');

View File

@ -347,7 +347,7 @@ export class ViewerHeaderService {
}
#toggleRotationActionButtons() {
if (this._rotationService.hasRotations) {
if (this._rotationService.hasRotations()) {
this.enable(ROTATION_ACTION_BUTTONS);
} else {
this.disable(ROTATION_ACTION_BUTTONS);

View File

@ -31,6 +31,7 @@ import { ViewerHeaderService } from '../../../pdf-viewer/services/viewer-header.
import { ROTATION_ACTION_BUTTONS } from '../../../pdf-viewer/utils/constants';
import { Roles } from '@users/roles';
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
import { toObservable } from '@angular/core/rxjs-interop';
@Component({
selector: 'redaction-file-actions',
@ -161,7 +162,7 @@ export class FileActionsComponent implements OnChanges {
type: ActionTypes.circleBtn,
action: () => this._documentInfoService.toggle(),
tooltip: _('file-preview.document-info'),
ariaExpanded: this._documentInfoService?.shown$,
ariaExpanded: toObservable(this._documentInfoService.shown, { injector: this._injector }),
icon: 'red:status-info',
show: !!this._documentInfoService,
},
@ -170,7 +171,7 @@ export class FileActionsComponent implements OnChanges {
type: ActionTypes.circleBtn,
action: () => this._excludedPagesService.toggle(),
tooltip: _('file-preview.exclude-pages'),
ariaExpanded: this._excludedPagesService?.shown$,
ariaExpanded: toObservable(this._excludedPagesService.shown, { injector: this._injector }),
showDot: !!this.file.excludedPages?.length,
icon: 'red:exclude-pages',
show: