RED-6829: remove other observables
This commit is contained in:
parent
58976f24d8
commit
e89d317fa0
@ -1,5 +1,4 @@
|
||||
export interface ListItem<T> {
|
||||
item: T;
|
||||
isSelected: boolean;
|
||||
multiSelectActive: boolean;
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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[] {
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
});
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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[]) {
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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');
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user