RED-10034: File workload improvementes - WIP

This commit is contained in:
Adina Țeudan 2024-09-30 18:30:30 +03:00
parent 8e9f6e19bf
commit 671c44438d
2 changed files with 72 additions and 59 deletions

View File

@ -32,11 +32,11 @@
</ng-template> </ng-template>
</ng-container> </ng-container>
@if (displayedAnnotations$ | async; as annotations) { @if (filteredAnnotations$ | async; as annotations) {
<div class="right-content"> <div class="right-content">
<ng-container *ngIf="!isDocumine"> <ng-container *ngIf="!isDocumine">
<redaction-readonly-banner <redaction-readonly-banner
*ngIf="showAnalysisDisabledBanner; else readOnlyBanner" *ngIf="showAnalysisDisabledBanner(); else readOnlyBanner"
[customTranslation]="translations.analysisDisabled" [customTranslation]="translations.analysisDisabled"
></redaction-readonly-banner> ></redaction-readonly-banner>
<ng-template #readOnlyBanner> <ng-template #readOnlyBanner>
@ -112,7 +112,7 @@
></iqser-circle-button> ></iqser-circle-button>
<span <span
[translateParams]="{ page: pdf.currentPage(), count: activeAnnotations.length }" [translateParams]="{ page: pdf.currentPage(), count: activeAnnotations().length }"
[translate]="'page'" [translate]="'page'"
class="all-caps-label" class="all-caps-label"
></span> ></span>
@ -200,7 +200,7 @@
<redaction-annotations-list <redaction-annotations-list
(pagesPanelActive)="pagesPanelActive = $event" (pagesPanelActive)="pagesPanelActive = $event"
[annotations]="annotations.get(pdf.currentPage())" [annotations]="annotationsList$ | async"
></redaction-annotations-list> ></redaction-annotations-list>
</div> </div>
} }
@ -224,7 +224,7 @@
</ng-template> </ng-template>
<ng-template #documineHeader> <ng-template #documineHeader>
<span [translate]="'annotations'" [attr.help-mode-key]="'annotations_list'"></span> <span [attr.help-mode-key]="'annotations_list'" [translate]="'annotations'"></span>
<ng-container *ngTemplateOutlet="annotationsFilter"></ng-container> <ng-container *ngTemplateOutlet="annotationsFilter"></ng-container>
</ng-template> </ng-template>

View File

@ -9,6 +9,7 @@ import {
OnDestroy, OnDestroy,
OnInit, OnInit,
TemplateRef, TemplateRef,
untracked,
viewChild, viewChild,
} from '@angular/core'; } from '@angular/core';
import { MatDialog } from '@angular/material/dialog'; import { MatDialog } from '@angular/material/dialog';
@ -89,23 +90,30 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
], ],
}) })
export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, OnDestroy { export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, OnDestroy {
private readonly _annotationsElement = viewChild<ElementRef>('annotationsElement');
private readonly _quickNavigationElement = viewChild<ElementRef>('quickNavigation');
readonly multiSelectTemplate = viewChild<TemplateRef<any>>('multiSelect'); readonly multiSelectTemplate = viewChild<TemplateRef<any>>('multiSelect');
readonly #isIqserDevMode = this._userPreferenceService.isIqserDevMode; readonly annotationsList$: Observable<ListItem<AnnotationWrapper>[]>;
readonly allPages = computed(() => Array.from({ length: this.state.file()?.numberOfPages }, (_x, i) => i + 1));
protected readonly iconButtonTypes = IconButtonTypes; protected readonly iconButtonTypes = IconButtonTypes;
protected readonly circleButtonTypes = CircleButtonTypes; protected readonly circleButtonTypes = CircleButtonTypes;
protected readonly displayedAnnotations$: Observable<Map<number, ListItem<AnnotationWrapper>[]>>; protected readonly filteredAnnotations$: Observable<Map<number, ListItem<AnnotationWrapper>[]>>;
protected readonly title = computed(() => protected readonly title = computed(() =>
this.viewModeService.isEarmarks() ? _('file-preview.tabs.highlights.label') : _('file-preview.tabs.annotations.label'), this.viewModeService.isEarmarks() ? _('file-preview.tabs.highlights.label') : _('file-preview.tabs.annotations.label'),
); );
protected readonly currentPageIsExcluded = computed(() => this.state.file().excludedPages.includes(this.pdf.currentPage())); protected readonly currentPageIsExcluded = computed(() => this.state.file().excludedPages.includes(this.pdf.currentPage()));
protected readonly translations = workloadTranslations; protected readonly translations = workloadTranslations;
protected readonly isDocumine = getConfig().IS_DOCUMINE; protected readonly isDocumine = getConfig().IS_DOCUMINE;
readonly showAnalysisDisabledBanner = computed(() => {
const file = this.state.file();
return this.isDocumine && file.excludedFromAutomaticAnalysis && file.workflowStatus !== WorkflowFileStatuses.APPROVED;
});
protected displayedAnnotations = new Map<number, AnnotationWrapper[]>(); protected displayedAnnotations = new Map<number, AnnotationWrapper[]>();
readonly activeAnnotations = computed(() => this.displayedAnnotations.get(this.pdf.currentPage()) || []);
protected displayedPages: number[] = []; protected displayedPages: number[] = [];
protected pagesPanelActive = true; protected pagesPanelActive = true;
protected enabledFilters = []; protected enabledFilters = [];
private readonly _annotationsElement = viewChild<ElementRef>('annotationsElement');
private readonly _quickNavigationElement = viewChild<ElementRef>('quickNavigation');
readonly #isIqserDevMode = this._userPreferenceService.isIqserDevMode;
#displayedPagesChanged = false; #displayedPagesChanged = false;
constructor( constructor(
@ -129,10 +137,9 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
) { ) {
super(); super();
// TODO: ngOnDetach is not called here, so we need to unsubscribe manually effect(() => {
this.addActiveScreenSubscription = this.pdf.currentPage$.subscribe(pageNumber => {
this._scrollViews(); this._scrollViews();
this.scrollAnnotationsToPage(pageNumber, 'always'); this.scrollAnnotationsToPage(this.pdf.currentPage(), 'always');
}); });
this.addActiveScreenSubscription = this.listingService.selected$.subscribe(annotationIds => { this.addActiveScreenSubscription = this.listingService.selected$.subscribe(annotationIds => {
@ -146,7 +153,11 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this.handleKeyEvent($event); this.handleKeyEvent($event);
}); });
this.displayedAnnotations$ = this._displayedAnnotations$; this.filteredAnnotations$ = this._filteredAnnotations$;
this.annotationsList$ = combineLatest([this.filteredAnnotations$, this.pdf.currentPage$]).pipe(
map(([annotations, page]) => annotations.get(page)),
);
effect(() => { effect(() => {
if (this.multiSelectService.inactive()) { if (this.multiSelectService.inactive()) {
@ -169,20 +180,11 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
); );
} }
get activeAnnotations(): AnnotationWrapper[] {
return this.displayedAnnotations.get(this.pdf.currentPage()) || [];
}
get showAnalysisDisabledBanner() {
const file = this.state.file();
return this.isDocumine && file.excludedFromAutomaticAnalysis && file.workflowStatus !== WorkflowFileStatuses.APPROVED;
}
private get _firstSelectedAnnotation() { private get _firstSelectedAnnotation() {
return this.listingService.selected.length ? this.listingService.selected[0] : null; return this.listingService.selected.length ? this.listingService.selected[0] : null;
} }
private get _displayedAnnotations$(): Observable<Map<number, ListItem<AnnotationWrapper>[]>> { private get _filteredAnnotations$(): Observable<Map<number, ListItem<AnnotationWrapper>[]>> {
const primary$ = this.filterService.getFilterModels$('primaryFilters'); const primary$ = this.filterService.getFilterModels$('primaryFilters');
const secondary$ = this.filterService.getFilterModels$('secondaryFilters'); const secondary$ = this.filterService.getFilterModels$('secondaryFilters');
@ -205,10 +207,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
); );
} }
get #allPages() {
return Array.from({ length: this.state.file()?.numberOfPages }, (_x, i) => i + 1);
}
private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') { private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') {
if (elements.length > 0) { if (elements.length > 0) {
scrollIntoView(elements[0], { scrollIntoView(elements[0], {
@ -222,12 +220,13 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
ngOnInit(): void { ngOnInit(): void {
setTimeout(() => { setTimeout(() => {
const showExcludePages = getLocalStorageDataByFileId(this.state.file()?.id, 'show-exclude-pages') ?? false; const file = untracked(this.state.file);
const showExcludePages = getLocalStorageDataByFileId(file?.id, 'show-exclude-pages') ?? false;
if (showExcludePages) { if (showExcludePages) {
this.excludedPagesService.show(); this.excludedPagesService.show();
} }
const showDocumentInfo = getLocalStorageDataByFileId(this.state.file()?.id, 'show-document-info') ?? false; const showDocumentInfo = getLocalStorageDataByFileId(file?.id, 'show-document-info') ?? false;
if (showDocumentInfo) { if (showDocumentInfo) {
this.documentInfoService.show(); this.documentInfoService.show();
} }
@ -235,16 +234,20 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
selectAllOnActivePage() { selectAllOnActivePage() {
this.listingService.selectAnnotations(this.activeAnnotations); const activeAnnotations = untracked(this.activeAnnotations);
this.listingService.selectAnnotations(activeAnnotations);
} }
deselectAllOnActivePage(): void { deselectAllOnActivePage(): void {
this.listingService.deselect(this.activeAnnotations); const activeAnnotations = untracked(this.activeAnnotations);
this.annotationManager.deselect(this.activeAnnotations); this.listingService.deselect(activeAnnotations);
this.annotationManager.deselect(activeAnnotations);
} }
@HostListener('window:keyup', ['$event']) @HostListener('window:keyup', ['$event'])
handleKeyEvent($event: KeyboardEvent): void { handleKeyEvent($event: KeyboardEvent): void {
const multiSelectServiceInactive = untracked(this.multiSelectService.inactive);
if ( if (
!ALL_HOTKEY_ARRAY.includes($event.key) || !ALL_HOTKEY_ARRAY.includes($event.key) ||
this._dialog.openDialogs.length || this._dialog.openDialogs.length ||
@ -264,7 +267,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
// if we activated annotationsPanel - // if we activated annotationsPanel -
// select first annotation from this page in case there is no // select first annotation from this page in case there is no
// selected annotation on this page and not in multi select mode // selected annotation on this page and not in multi select mode
if (!this.pagesPanelActive && this.multiSelectService.inactive()) { if (!this.pagesPanelActive && multiSelectServiceInactive) {
this._documentViewer.clearSelection(); this._documentViewer.clearSelection();
this.#selectFirstAnnotationOnCurrentPageIfNecessary(); this.#selectFirstAnnotationOnCurrentPageIfNecessary();
} }
@ -275,7 +278,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
if (!this.pagesPanelActive) { if (!this.pagesPanelActive) {
// Disable annotation navigation in multi select mode // Disable annotation navigation in multi select mode
// => TODO: maybe implement selection on enter? // => TODO: maybe implement selection on enter?
if (this.multiSelectService.inactive()) { if (multiSelectServiceInactive) {
this.navigateAnnotations($event); this.navigateAnnotations($event);
} }
} else { } else {
@ -286,7 +289,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
scrollAnnotations(): void { scrollAnnotations(): void {
const currentPage = this.pdf.currentPage(); const currentPage = untracked(this.pdf.currentPage);
if (this._firstSelectedAnnotation?.pageNumber === currentPage) { if (this._firstSelectedAnnotation?.pageNumber === currentPage) {
return; return;
} }
@ -294,27 +297,27 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed'): void { scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed'): void {
if (this._annotationsElement()) { const annotationsElement = untracked(this._annotationsElement);
const elements: HTMLElement[] = this._annotationsElement().nativeElement.querySelectorAll( if (annotationsElement) {
`div[anotation-page-header="${page}"]`, const elements: HTMLElement[] = annotationsElement.nativeElement.querySelectorAll(`div[anotation-page-header="${page}"]`);
);
FileWorkloadComponent._scrollToFirstElement(elements, mode); FileWorkloadComponent._scrollToFirstElement(elements, mode);
} }
} }
@Debounce() @Debounce()
scrollToSelectedAnnotation(): void { scrollToSelectedAnnotation(): void {
if (this.listingService.selected.length === 0 || !this._annotationsElement()) { const annotationsElement = untracked(this._annotationsElement);
if (this.listingService.selected.length === 0 || annotationsElement) {
return; return;
} }
const elements: HTMLElement[] = this._annotationsElement().nativeElement.querySelectorAll( const elements: HTMLElement[] = annotationsElement.nativeElement.querySelectorAll(
`[annotation-id="${this._firstSelectedAnnotation?.id}"]`, `[annotation-id="${this._firstSelectedAnnotation?.id}"]`,
); );
FileWorkloadComponent._scrollToFirstElement(elements); FileWorkloadComponent._scrollToFirstElement(elements);
} }
scrollQuickNavigation(): void { scrollQuickNavigation(): void {
const currentPage = this.pdf.currentPage(); const currentPage = untracked(this.pdf.currentPage);
let quickNavPageIndex = this.displayedPages.findIndex(p => p >= currentPage); let quickNavPageIndex = this.displayedPages.findIndex(p => p >= currentPage);
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== currentPage) { if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== currentPage) {
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1); quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
@ -327,7 +330,8 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
scrollQuickNavLast() { scrollQuickNavLast() {
this.pdf.navigateTo(this.state.file().numberOfPages); const file = untracked(this.state.file);
this.pdf.navigateTo(file.numberOfPages);
} }
preventKeyDefault($event: KeyboardEvent): void { preventKeyDefault($event: KeyboardEvent): void {
@ -345,11 +349,12 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
navigateAnnotations($event: KeyboardEvent) { navigateAnnotations($event: KeyboardEvent) {
const currentPage = this.pdf.currentPage(); const currentPage = untracked(this.pdf.currentPage);
const activeAnnotations = untracked(this.activeAnnotations);
if (!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) { if (!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) {
if (this.displayedPages.indexOf(currentPage) !== -1) { if (this.displayedPages.indexOf(currentPage) !== -1) {
// Displayed page has annotations // Displayed page has annotations
return this.listingService.selectAnnotations(this.activeAnnotations ? this.activeAnnotations[0] : null); return this.listingService.selectAnnotations(activeAnnotations ? activeAnnotations[0] : null);
} }
// Displayed page doesn't have annotations // Displayed page doesn't have annotations
if ($event.key === 'ArrowDown') { if ($event.key === 'ArrowDown') {
@ -422,14 +427,16 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
secondary: INestedFilter[] = [], secondary: INestedFilter[] = [],
componentReferenceIds: string[], componentReferenceIds: string[],
): Map<number, AnnotationWrapper[]> { ): Map<number, AnnotationWrapper[]> {
const onlyPageWithAnnotations = this.viewModeService.onlyPagesWithAnnotations(); const onlyPageWithAnnotations = untracked(this.viewModeService.onlyPagesWithAnnotations);
const isRedacted = untracked(this.viewModeService.isRedacted);
const allPages = untracked(this.allPages);
if (!primary || primary.length === 0) { if (!primary || primary.length === 0) {
const pages = onlyPageWithAnnotations ? [] : this.#allPages; const pages = onlyPageWithAnnotations ? [] : allPages;
this.#setDisplayedPages(pages); this.#setDisplayedPages(pages);
return; return;
} }
if (this.viewModeService.isRedacted()) { if (isRedacted) {
annotations = annotations.filter(a => !a.isRemoved); annotations = annotations.filter(a => !a.isRemoved);
} }
@ -447,7 +454,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
this.enabledFilters = this.filterService.enabledFlatFilters; this.enabledFilters = this.filterService.enabledFlatFilters;
if (this.enabledFilters.some(f => f.id === 'pages-without-annotations')) { if (this.enabledFilters.some(f => f.id === 'pages-without-annotations')) {
if (this.enabledFilters.length === 1 && !onlyPageWithAnnotations) { if (this.enabledFilters.length === 1 && !onlyPageWithAnnotations) {
const pages = this.#allPages.filter(page => !pagesThatDisplayAnnotations.includes(page)); const pages = allPages.filter(page => !pagesThatDisplayAnnotations.includes(page));
this.#setDisplayedPages(pages); this.#setDisplayedPages(pages);
} else { } else {
this.#setDisplayedPages([]); this.#setDisplayedPages([]);
@ -456,7 +463,7 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} else if (this.enabledFilters.length || onlyPageWithAnnotations || componentReferenceIds) { } else if (this.enabledFilters.length || onlyPageWithAnnotations || componentReferenceIds) {
this.#setDisplayedPages(pagesThatDisplayAnnotations); this.#setDisplayedPages(pagesThatDisplayAnnotations);
} else { } else {
this.#setDisplayedPages(this.#allPages); this.#setDisplayedPages(allPages);
} }
this.displayedPages.sort((a, b) => a - b); this.displayedPages.sort((a, b) => a - b);
@ -464,18 +471,20 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
#selectFirstAnnotationOnCurrentPageIfNecessary() { #selectFirstAnnotationOnCurrentPageIfNecessary() {
const currentPage = this.pdf.currentPage(); const currentPage = untracked(this.pdf.currentPage);
const activeAnnotations = untracked(this.activeAnnotations);
if ( if (
(!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) && (!this._firstSelectedAnnotation || currentPage !== this._firstSelectedAnnotation.pageNumber) &&
this.displayedPages.indexOf(currentPage) >= 0 && this.displayedPages.indexOf(currentPage) >= 0 &&
this.activeAnnotations.length > 0 activeAnnotations.length > 0
) { ) {
this.listingService.selectAnnotations(this.activeAnnotations[0]); this.listingService.selectAnnotations(activeAnnotations[0]);
} }
} }
#navigatePages($event: KeyboardEvent) { #navigatePages($event: KeyboardEvent) {
const pageIdx = this.displayedPages.indexOf(this.pdf.currentPage()); const currentPage = untracked(this.pdf.currentPage);
const pageIdx = this.displayedPages.indexOf(currentPage);
if ($event.key !== 'ArrowDown') { if ($event.key !== 'ArrowDown') {
if (pageIdx === -1) { if (pageIdx === -1) {
@ -507,9 +516,10 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
#nextPageWithAnnotations() { #nextPageWithAnnotations() {
const currentPage = untracked(this.pdf.currentPage);
let idx = 0; let idx = 0;
for (const page of this.displayedPages) { for (const page of this.displayedPages) {
if (page > this.pdf.currentPage() && this.displayedAnnotations.get(page)) { if (page > currentPage && this.displayedAnnotations.get(page)) {
break; break;
} }
++idx; ++idx;
@ -518,10 +528,11 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
#prevPageWithAnnotations() { #prevPageWithAnnotations() {
const currentPage = untracked(this.pdf.currentPage);
let idx = this.displayedPages.length - 1; let idx = this.displayedPages.length - 1;
const reverseDisplayedPages = [...this.displayedPages].reverse(); const reverseDisplayedPages = [...this.displayedPages].reverse();
for (const page of reverseDisplayedPages) { for (const page of reverseDisplayedPages) {
if (page < this.pdf.currentPage() && this.displayedAnnotations.get(page)) { if (page < currentPage && this.displayedAnnotations.get(page)) {
break; break;
} }
--idx; --idx;
@ -530,8 +541,9 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
#scrollQuickNavigationToPage(page: number) { #scrollQuickNavigationToPage(page: number) {
if (this._quickNavigationElement()) { const quickNavigationElement = untracked(this._quickNavigationElement);
const elements: HTMLElement[] = this._quickNavigationElement().nativeElement.querySelectorAll(`#quick-nav-page-${page}`); if (quickNavigationElement) {
const elements: HTMLElement[] = quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`);
FileWorkloadComponent._scrollToFirstElement(elements); FileWorkloadComponent._scrollToFirstElement(elements);
} }
} }
@ -552,7 +564,8 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnInit, On
} }
#scrollToFirstAnnotationPage(annotations: Map<number, ListItem<AnnotationWrapper>[]>) { #scrollToFirstAnnotationPage(annotations: Map<number, ListItem<AnnotationWrapper>[]>) {
if (this.isDocumine && annotations.size && this.#displayedPagesChanged && !this.displayedPages.includes(this.pdf.currentPage())) { const currentPage = untracked(this.pdf.currentPage);
if (this.isDocumine && annotations.size && this.#displayedPagesChanged && !this.displayedPages.includes(currentPage)) {
const page = annotations.keys().next().value; const page = annotations.keys().next().value;
this.pdf.navigateTo(page); this.pdf.navigateTo(page);
} }