File workload separate component
This commit is contained in:
parent
f268726402
commit
5636656f76
@ -120,6 +120,7 @@ import { AddEditFileAttributeDialogComponent } from './dialogs/add-edit-file-att
|
||||
import { ConfirmDeleteFileAttributeDialogComponent } from './dialogs/confirm-delete-file-attribute-dialog/confirm-delete-file-attribute-dialog.component';
|
||||
import { DocumentInfoDialogComponent } from './dialogs/document-info-dialog/document-info-dialog.component';
|
||||
import { DocumentInfoComponent } from './components/document-info/document-info.component';
|
||||
import { FileWorkloadComponent } from './components/file-workload/file-workload.component';
|
||||
|
||||
export function HttpLoaderFactory(httpClient: HttpClient) {
|
||||
return new TranslateHttpLoader(httpClient, '/assets/i18n/', '.json');
|
||||
@ -226,7 +227,8 @@ const matImports = [
|
||||
AddEditFileAttributeDialogComponent,
|
||||
ConfirmDeleteFileAttributeDialogComponent,
|
||||
DocumentInfoDialogComponent,
|
||||
DocumentInfoComponent
|
||||
DocumentInfoComponent,
|
||||
FileWorkloadComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@ -15,7 +15,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="content" redactionHasScrollbar>
|
||||
<div class="right-content" redactionHasScrollbar>
|
||||
<div class="section">
|
||||
1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4
|
||||
</div>
|
||||
|
||||
@ -11,8 +11,9 @@
|
||||
@include inset-shadow;
|
||||
}
|
||||
|
||||
.content {
|
||||
max-height: calc(100% - 71px);
|
||||
.right-content {
|
||||
flex-direction: column;
|
||||
|
||||
@include scroll-bar;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
@ -0,0 +1,133 @@
|
||||
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div>
|
||||
<redaction-filter
|
||||
(filtersChanged)="filtersChanged($event)"
|
||||
[chevron]="true"
|
||||
[filterTemplate]="annotationFilterTemplate"
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
[filters]="annotationFilters"
|
||||
></redaction-filter>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-content">
|
||||
<div
|
||||
#quickNavigation
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="pagesPanelActive"
|
||||
class="quick-navigation"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollFirstEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
||||
</div>
|
||||
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
|
||||
<redaction-page-indicator
|
||||
(pageSelected)="pageSelectedByClick($event)"
|
||||
*ngFor="let pageNumber of displayedPages"
|
||||
[active]="pageNumber === activeViewerPage"
|
||||
[number]="pageNumber"
|
||||
[viewedPages]="fileData.viewedPages"
|
||||
>
|
||||
</redaction-page-indicator>
|
||||
</div>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollLastEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="scrollQuickNavLast()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-last"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="overflow: hidden; width: 100%;">
|
||||
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
||||
><span translate="page"></span> {{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
#annotationsElement
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="!pagesPanelActive"
|
||||
redactionHasScrollbar
|
||||
class="annotations"
|
||||
tabindex="1"
|
||||
>
|
||||
<div *ngIf="!displayedAnnotations[activeViewerPage]" class="heading-l no-annotations">
|
||||
{{ 'file-preview.no-annotations-for-page' | translate }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
(click)="annotationClicked(annotation)"
|
||||
class="annotation-wrapper"
|
||||
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
|
||||
attr.annotation-id="{{ annotation.id }}"
|
||||
attr.annotation-page="{{ activeViewerPage }}"
|
||||
[class.active]="annotationIsSelected(annotation)"
|
||||
>
|
||||
<div class="active-marker"></div>
|
||||
<div class="annotation" [class.removed]="annotation.isChangeLogRemoved">
|
||||
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
|
||||
<div class="details">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation.dictionary && annotation.dictionary !== 'manual'">
|
||||
<strong
|
||||
><span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.dictionary | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.content && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
|
||||
</div>
|
||||
</div>
|
||||
<ng-container [ngTemplateOutlet]="annotationActionsTemplate" [ngTemplateOutletContext]="{ annotation: annotation }"> </ng-container>
|
||||
|
||||
<!-- <redaction-annotation-actions-->
|
||||
<!-- (annotationsChanged)="annotationsChangedByReviewAction($event)"-->
|
||||
<!-- [annotation]="annotation"-->
|
||||
<!-- [canPerformAnnotationActions]="canPerformAnnotationActions"-->
|
||||
<!-- [viewer]="activeViewer"-->
|
||||
<!-- ></redaction-annotation-actions>-->
|
||||
</div>
|
||||
</redaction-hidden-action>
|
||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ng-template #annotationFilterTemplate let-filter="filter">
|
||||
<redaction-type-filter *ngIf="filter.topLevelFilter" [filter]="filter"></redaction-type-filter>
|
||||
<ng-container *ngIf="!filter.topLevelFilter">
|
||||
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.key"></redaction-dictionary-annotation-icon>
|
||||
{{ filter.key | humanize: false }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #annotationFilterActionTemplate let-filter="filter">
|
||||
<ng-container *ngIf="filter.key === 'skipped'">
|
||||
<redaction-circle-button
|
||||
[icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'"
|
||||
(action)="toggleSkipped.emit($event)"
|
||||
class="skipped-toggle-button"
|
||||
>
|
||||
</redaction-circle-button>
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
@ -0,0 +1,134 @@
|
||||
@import '../../../assets/styles/red-variables';
|
||||
@import '../../../assets/styles/red-mixins';
|
||||
|
||||
.right-content {
|
||||
.no-annotations {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.quick-navigation,
|
||||
.annotations {
|
||||
overflow-y: scroll;
|
||||
outline: none;
|
||||
|
||||
&.active-panel {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-navigation {
|
||||
border-right: 1px solid $separator;
|
||||
min-width: 61px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.jump {
|
||||
min-height: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.25s;
|
||||
|
||||
&:not(.disabled):hover {
|
||||
background-color: $grey-6;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
|
||||
mat-icon {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pages {
|
||||
@include no-scroll-bar();
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.page-separator {
|
||||
border-bottom: 1px solid $separator;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: $grey-6;
|
||||
}
|
||||
|
||||
.annotations {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: calc(100% - 32px);
|
||||
|
||||
.annotation-wrapper {
|
||||
display: flex;
|
||||
border-bottom: 1px solid $separator;
|
||||
|
||||
.active-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.active-marker {
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 10px 21px 10px 6px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.removed {
|
||||
text-decoration: line-through;
|
||||
color: $grey-7;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f9fafb;
|
||||
|
||||
::ng-deep .annotation-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
@include scroll-bar;
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover {
|
||||
.annotation {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,296 @@
|
||||
import { ChangeDetectorRef, Component, ElementRef, EventEmitter, HostListener, Input, Output, TemplateRef, ViewChild } from '@angular/core';
|
||||
import { FilterModel } from '../filter/model/filter.model';
|
||||
import { AnnotationWrapper } from '../../screens/file/model/annotation.wrapper';
|
||||
import { AnnotationProcessingService } from '../../screens/file/service/annotation-processing.service';
|
||||
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
|
||||
import scrollIntoView from 'scroll-into-view-if-needed';
|
||||
import { debounce } from '../../utils/debounce';
|
||||
import { FileDataModel } from '../../screens/file/model/file-data.model';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-workload',
|
||||
templateUrl: './file-workload.component.html',
|
||||
styleUrls: ['./file-workload.component.scss']
|
||||
})
|
||||
export class FileWorkloadComponent {
|
||||
public displayedAnnotations: { [key: number]: { annotations: AnnotationWrapper[] } } = {};
|
||||
private _annotations: AnnotationWrapper[];
|
||||
|
||||
@Input()
|
||||
set annotations(value: AnnotationWrapper[]) {
|
||||
this._annotations = value;
|
||||
// this.computeQuickNavButtonsState();
|
||||
}
|
||||
|
||||
@Input() selectedAnnotations: AnnotationWrapper[];
|
||||
@Input() activeViewerPage: number;
|
||||
@Input() shouldDeselectAnnotationsOnPageChange: boolean;
|
||||
@Input() dialogRef: MatDialogRef<any>;
|
||||
@Input() annotationFilters: FilterModel[];
|
||||
@Input() fileData: FileDataModel;
|
||||
@Input() hideSkipped: boolean;
|
||||
@Input() annotationActionsTemplate: TemplateRef<any>;
|
||||
|
||||
@Output() selectAnnotation = new EventEmitter<AnnotationWrapper>();
|
||||
@Output() selectPage = new EventEmitter<number>();
|
||||
@Output() toggleSkipped = new EventEmitter<any>();
|
||||
|
||||
public quickScrollFirstEnabled = false;
|
||||
public quickScrollLastEnabled = false;
|
||||
public displayedPages: number[] = [];
|
||||
public pagesPanelActive = true;
|
||||
|
||||
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
||||
|
||||
constructor(private _changeDetectorRef: ChangeDetectorRef, private _annotationProcessingService: AnnotationProcessingService) {}
|
||||
|
||||
private get firstSelectedAnnotation() {
|
||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
||||
}
|
||||
|
||||
private static _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') {
|
||||
if (elements.length > 0) {
|
||||
scrollIntoView(elements[0], {
|
||||
behavior: 'smooth',
|
||||
scrollMode: mode,
|
||||
block: 'start',
|
||||
inline: 'start'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public annotationIsSelected(annotation: AnnotationWrapper) {
|
||||
return this.selectedAnnotations?.find((a) => a.id === annotation.id);
|
||||
}
|
||||
|
||||
public logAnnotation(annotation: AnnotationWrapper) {
|
||||
console.log(annotation);
|
||||
}
|
||||
|
||||
@debounce(0)
|
||||
public filtersChanged(filters: FilterModel[]) {
|
||||
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(this._annotations, filters);
|
||||
this.displayedPages = Object.keys(this.displayedAnnotations).map((key) => Number(key));
|
||||
this.computeQuickNavButtonsState();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
public computeQuickNavButtonsState() {
|
||||
setTimeout(() => {
|
||||
const element: HTMLElement = this._quickNavigationElement.nativeElement.querySelector(`#pages`);
|
||||
const { scrollTop, scrollHeight, clientHeight } = element;
|
||||
this.quickScrollFirstEnabled = scrollTop !== 0;
|
||||
this.quickScrollLastEnabled = scrollHeight !== scrollTop + clientHeight;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
public annotationClicked(annotation: AnnotationWrapper) {
|
||||
this.pagesPanelActive = false;
|
||||
this.selectAnnotation.emit(annotation);
|
||||
}
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent) {
|
||||
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this.dialogRef?.getState() === MatDialogState.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event.key === 'ArrowLeft') {
|
||||
this.pagesPanelActive = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event.key === 'ArrowRight') {
|
||||
this.pagesPanelActive = false;
|
||||
// if we activated annotationsPanel - select first annotation from this page in case there is no
|
||||
// selected annotation on this page
|
||||
if (!this.pagesPanelActive) {
|
||||
this._selectFirstAnnotationOnCurrentPageIfNecessary();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pagesPanelActive) {
|
||||
this._navigateAnnotations($event);
|
||||
} else {
|
||||
this._navigatePages($event);
|
||||
}
|
||||
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
public scrollAnnotations() {
|
||||
if (this.firstSelectedAnnotation?.pageNumber === this.activeViewerPage) {
|
||||
return;
|
||||
}
|
||||
this.scrollAnnotationsToPage(this.activeViewerPage, 'always');
|
||||
}
|
||||
|
||||
public scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed') {
|
||||
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[anotation-page-header="${page}"]`);
|
||||
FileWorkloadComponent._scrollToFirstElement(elements, mode);
|
||||
}
|
||||
|
||||
@debounce()
|
||||
public scrollToSelectedAnnotation() {
|
||||
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
|
||||
return;
|
||||
}
|
||||
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[annotation-id="${this.firstSelectedAnnotation.id}"].active`);
|
||||
FileWorkloadComponent._scrollToFirstElement(elements);
|
||||
}
|
||||
|
||||
public scrollQuickNavigation() {
|
||||
let quickNavPageIndex = this.displayedPages.findIndex((p) => p >= this.activeViewerPage);
|
||||
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== this.activeViewerPage) {
|
||||
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
|
||||
}
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
|
||||
}
|
||||
|
||||
public scrollQuickNavFirst() {
|
||||
if (this.displayedPages.length > 0) {
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public scrollQuickNavLast() {
|
||||
if (this.displayedPages.length > 0) {
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[this.displayedPages.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
public pageSelectedByClick($event: number) {
|
||||
this.pagesPanelActive = true;
|
||||
this.selectPage.emit($event);
|
||||
}
|
||||
|
||||
public preventKeyDefault($event: KeyboardEvent) {
|
||||
if (COMMAND_KEY_ARRAY.includes($event.key)) {
|
||||
$event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
||||
if (
|
||||
(!this.firstSelectedAnnotation || this.activeViewerPage !== this.firstSelectedAnnotation.pageNumber) &&
|
||||
this.displayedPages.indexOf(this.activeViewerPage) >= 0
|
||||
) {
|
||||
this.selectAnnotation.emit(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private _navigateAnnotations($event: KeyboardEvent) {
|
||||
if (!this.firstSelectedAnnotation || this.activeViewerPage !== this.firstSelectedAnnotation.pageNumber) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
if (pageIdx !== -1) {
|
||||
// Displayed page has annotations
|
||||
this.selectAnnotation.emit(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
} else {
|
||||
// Displayed page doesn't have annotations
|
||||
if ($event.key === 'ArrowDown') {
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation.emit(this.displayedAnnotations[nextPage].annotations[0]);
|
||||
} else {
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
||||
this.selectAnnotation.emit(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const page = this.firstSelectedAnnotation.pageNumber;
|
||||
const pageIdx = this.displayedPages.indexOf(page);
|
||||
const annotationsOnPage = this.displayedAnnotations[page].annotations;
|
||||
const idx = annotationsOnPage.findIndex((a) => a.id === this.firstSelectedAnnotation.id);
|
||||
|
||||
if ($event.key === 'ArrowDown') {
|
||||
if (idx + 1 !== annotationsOnPage.length) {
|
||||
// If not last item in page
|
||||
this.selectAnnotation.emit(annotationsOnPage[idx + 1]);
|
||||
} else if (pageIdx + 1 < this.displayedPages.length) {
|
||||
// If not last page
|
||||
const nextPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx + 1]].annotations;
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation.emit(nextPageAnnotations[0]);
|
||||
}
|
||||
} else {
|
||||
if (idx !== 0) {
|
||||
// If not first item in page
|
||||
this.selectAnnotation.emit(annotationsOnPage[idx - 1]);
|
||||
} else if (pageIdx) {
|
||||
// If not first page
|
||||
const prevPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx - 1]].annotations;
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation.emit(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _navigatePages($event: KeyboardEvent) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
|
||||
if ($event.key === 'ArrowDown') {
|
||||
if (pageIdx !== -1) {
|
||||
// If active page has annotations
|
||||
if (pageIdx !== this.displayedPages.length - 1) {
|
||||
this.selectPage.emit(this.displayedPages[pageIdx + 1]);
|
||||
}
|
||||
} else {
|
||||
// If active page doesn't have annotations
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
if (nextPage) {
|
||||
this.selectPage.emit(nextPage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pageIdx !== -1) {
|
||||
// If active page has annotations
|
||||
if (pageIdx !== 0) {
|
||||
this.selectPage.emit(this.displayedPages[pageIdx - 1]);
|
||||
}
|
||||
} else {
|
||||
// If active page doesn't have annotations
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
if (prevPage) {
|
||||
this.selectPage.emit(prevPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _nextPageWithAnnotations() {
|
||||
let idx = 0;
|
||||
for (const page of this.displayedPages) {
|
||||
if (page > this.activeViewerPage) {
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return idx < this.displayedPages.length ? this.displayedPages[idx] : null;
|
||||
}
|
||||
|
||||
private _prevPageWithAnnotations() {
|
||||
let idx = this.displayedPages.length - 1;
|
||||
for (const page of this.displayedPages.reverse()) {
|
||||
if (page < this.activeViewerPage) {
|
||||
this.selectPage.emit(this.displayedPages[idx]);
|
||||
this.scrollAnnotations();
|
||||
break;
|
||||
}
|
||||
--idx;
|
||||
}
|
||||
return idx >= 0 ? this.displayedPages[idx] : null;
|
||||
}
|
||||
|
||||
private _scrollQuickNavigationToPage(page: number) {
|
||||
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`);
|
||||
FileWorkloadComponent._scrollToFirstElement(elements);
|
||||
}
|
||||
}
|
||||
@ -207,118 +207,21 @@
|
||||
<redaction-document-info *ngIf="viewReady && viewDocumentInfo" [file]="fileData.fileStatus" (closeDocumentInfoView)="viewDocumentInfo = false">
|
||||
</redaction-document-info>
|
||||
|
||||
<div class="right-title heading" translate="file-preview.tabs.annotations.label">
|
||||
<div>
|
||||
<redaction-filter
|
||||
(filtersChanged)="filtersChanged($event)"
|
||||
[chevron]="true"
|
||||
[filterTemplate]="annotationFilterTemplate"
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
[filters]="annotationFilters"
|
||||
></redaction-filter>
|
||||
</div>
|
||||
</div>
|
||||
<div class="right-content">
|
||||
<div
|
||||
#quickNavigation
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="pagesPanelActive"
|
||||
class="quick-navigation"
|
||||
tabindex="0"
|
||||
>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollFirstEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
||||
</div>
|
||||
<div class="pages" (scroll)="computeQuickNavButtonsState()" id="pages">
|
||||
<redaction-page-indicator
|
||||
(pageSelected)="pageSelectedByClick($event)"
|
||||
*ngFor="let pageNumber of displayedPages"
|
||||
[active]="pageNumber === activeViewerPage"
|
||||
[number]="pageNumber"
|
||||
[viewedPages]="fileData.viewedPages"
|
||||
>
|
||||
</redaction-page-indicator>
|
||||
</div>
|
||||
<div
|
||||
class="jump"
|
||||
[class.disabled]="!quickScrollLastEnabled"
|
||||
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
|
||||
matTooltipPosition="above"
|
||||
(click)="scrollQuickNavLast()"
|
||||
>
|
||||
<mat-icon svgIcon="red:nav-last"></mat-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="overflow: hidden; width: 100%;">
|
||||
<div attr.anotation-page-header="{{ activeViewerPage }}" class="page-separator">
|
||||
<span *ngIf="!!activeViewerPage" class="all-caps-label"
|
||||
><span translate="page"></span> {{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
|
||||
<span [translate]="displayedAnnotations[activeViewerPage]?.annotations?.length === 1 ? 'annotation' : 'annotations'"></span
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
#annotationsElement
|
||||
(keydown)="preventKeyDefault($event)"
|
||||
(keyup)="preventKeyDefault($event)"
|
||||
[class.active-panel]="!pagesPanelActive"
|
||||
redactionHasScrollbar
|
||||
class="annotations"
|
||||
tabindex="1"
|
||||
>
|
||||
<div *ngIf="!displayedAnnotations[activeViewerPage]" class="heading-l no-annotations">
|
||||
{{ 'file-preview.no-annotations-for-page' | translate }}
|
||||
</div>
|
||||
|
||||
<div
|
||||
(click)="annotationClicked(annotation)"
|
||||
class="annotation-wrapper"
|
||||
*ngFor="let annotation of displayedAnnotations[activeViewerPage]?.annotations"
|
||||
attr.annotation-id="{{ annotation.id }}"
|
||||
attr.annotation-page="{{ activeViewerPage }}"
|
||||
[class.active]="annotationIsSelected(annotation)"
|
||||
>
|
||||
<div class="active-marker"></div>
|
||||
<div class="annotation" [class.removed]="annotation.isChangeLogRemoved">
|
||||
<redaction-hidden-action (action)="logAnnotation(annotation)" [requiredClicks]="2">
|
||||
<div class="details">
|
||||
<redaction-type-annotation-icon [annotation]="annotation"></redaction-type-annotation-icon>
|
||||
<div class="flex-1">
|
||||
<div>
|
||||
<strong>{{ annotation.typeLabel | translate }}</strong>
|
||||
</div>
|
||||
<div *ngIf="annotation.dictionary && annotation.dictionary !== 'manual'">
|
||||
<strong
|
||||
><span>{{ annotation.descriptor | translate }}</span
|
||||
>: </strong
|
||||
>{{ annotation.dictionary | humanize: false }}
|
||||
</div>
|
||||
<div *ngIf="annotation.content && !annotation.isHint">
|
||||
<strong><span translate="content"></span>: </strong>{{ annotation.content }}
|
||||
</div>
|
||||
</div>
|
||||
<redaction-annotation-actions
|
||||
(annotationsChanged)="annotationsChangedByReviewAction($event)"
|
||||
[annotation]="annotation"
|
||||
[canPerformAnnotationActions]="canPerformAnnotationActions"
|
||||
[viewer]="activeViewer"
|
||||
></redaction-annotation-actions>
|
||||
</div>
|
||||
</redaction-hidden-action>
|
||||
<redaction-comments [annotation]="annotation"></redaction-comments>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<redaction-file-workload
|
||||
#fileWorkloadComponent
|
||||
[annotations]="annotations"
|
||||
[selectedAnnotations]="selectedAnnotations"
|
||||
[activeViewerPage]="activeViewerPage"
|
||||
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
|
||||
[dialogRef]="dialogRef"
|
||||
[annotationFilters]="annotationFilters"
|
||||
[fileData]="fileData"
|
||||
[hideSkipped]="hideSkipped"
|
||||
[annotationActionsTemplate]="annotationActionsTemplate"
|
||||
(selectAnnotation)="selectAnnotation($event)"
|
||||
(selectPage)="selectPage($event)"
|
||||
(toggleSkipped)="toggleSkipped($event)"
|
||||
></redaction-file-workload>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -330,17 +233,11 @@
|
||||
</div>
|
||||
</redaction-full-page-loading-indicator>
|
||||
|
||||
<ng-template #annotationFilterTemplate let-filter="filter">
|
||||
<redaction-type-filter *ngIf="filter.topLevelFilter" [filter]="filter"></redaction-type-filter>
|
||||
<ng-container *ngIf="!filter.topLevelFilter">
|
||||
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.key"></redaction-dictionary-annotation-icon>
|
||||
{{ filter.key | humanize: false }}
|
||||
</ng-container>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #annotationFilterActionTemplate let-filter="filter">
|
||||
<ng-container *ngIf="filter.key === 'skipped'">
|
||||
<redaction-circle-button [icon]="hideSkipped ? 'red:visibility-off' : 'red:visibility'" (action)="toggleSkipped($event)" class="skipped-toggle-button">
|
||||
</redaction-circle-button>
|
||||
</ng-container>
|
||||
<ng-template #annotationActionsTemplate let-annotation="annotation">
|
||||
<redaction-annotation-actions
|
||||
(annotationsChanged)="annotationsChangedByReviewAction($event)"
|
||||
[annotation]="annotation"
|
||||
[canPerformAnnotationActions]="canPerformAnnotationActions"
|
||||
[viewer]="activeViewer"
|
||||
></redaction-annotation-actions>
|
||||
</ng-template>
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
min-width: 350px;
|
||||
position: relative;
|
||||
|
||||
::ng-deep.right-title {
|
||||
::ng-deep .right-title {
|
||||
height: 70px;
|
||||
display: flex;
|
||||
border-bottom: 1px solid $separator;
|
||||
@ -40,142 +40,143 @@
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
.right-content {
|
||||
::ng-deep .right-content {
|
||||
height: calc(100% - 72px);
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
|
||||
.quick-navigation,
|
||||
.annotations {
|
||||
overflow-y: scroll;
|
||||
outline: none;
|
||||
|
||||
&.active-panel {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
}
|
||||
|
||||
.quick-navigation {
|
||||
border-right: 1px solid $separator;
|
||||
min-width: 61px;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.jump {
|
||||
min-height: 32px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.25s;
|
||||
|
||||
&:not(.disabled):hover {
|
||||
background-color: $grey-6;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: default;
|
||||
|
||||
mat-icon {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pages {
|
||||
@include no-scroll-bar();
|
||||
overflow: auto;
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.page-separator {
|
||||
border-bottom: 1px solid $separator;
|
||||
height: 32px;
|
||||
box-sizing: border-box;
|
||||
padding: 0 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background-color: $grey-6;
|
||||
}
|
||||
|
||||
.annotations {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
height: calc(100% - 32px);
|
||||
|
||||
.annotation-wrapper {
|
||||
display: flex;
|
||||
border-bottom: 1px solid $separator;
|
||||
|
||||
.active-marker {
|
||||
min-width: 4px;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.active-marker {
|
||||
background-color: $primary;
|
||||
}
|
||||
}
|
||||
|
||||
.annotation {
|
||||
padding: 10px 21px 10px 6px;
|
||||
font-size: 11px;
|
||||
line-height: 14px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.removed {
|
||||
text-decoration: line-through;
|
||||
color: $grey-7;
|
||||
}
|
||||
|
||||
.details {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
redaction-type-annotation-icon {
|
||||
margin-top: 6px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: #f9fafb;
|
||||
|
||||
::ng-deep .annotation-actions {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
overflow-y: auto;
|
||||
@include scroll-bar;
|
||||
}
|
||||
|
||||
&.has-scrollbar:hover {
|
||||
.annotation {
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//
|
||||
// .quick-navigation,
|
||||
// .annotations {
|
||||
// overflow-y: scroll;
|
||||
// outline: none;
|
||||
//
|
||||
// &.active-panel {
|
||||
// background-color: #fafafa;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .quick-navigation {
|
||||
// border-right: 1px solid $separator;
|
||||
// min-width: 61px;
|
||||
// overflow: hidden;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
//
|
||||
// .jump {
|
||||
// min-height: 32px;
|
||||
// display: flex;
|
||||
// justify-content: center;
|
||||
// align-items: center;
|
||||
// cursor: pointer;
|
||||
// transition: background-color 0.25s;
|
||||
//
|
||||
// &:not(.disabled):hover {
|
||||
// background-color: $grey-6;
|
||||
// }
|
||||
//
|
||||
// mat-icon {
|
||||
// width: 16px;
|
||||
// height: 16px;
|
||||
// }
|
||||
//
|
||||
// &.disabled {
|
||||
// cursor: default;
|
||||
//
|
||||
// mat-icon {
|
||||
// opacity: 0.3;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .pages {
|
||||
// @include no-scroll-bar();
|
||||
// overflow: auto;
|
||||
// flex: 1;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .page-separator {
|
||||
// border-bottom: 1px solid $separator;
|
||||
// height: 32px;
|
||||
// box-sizing: border-box;
|
||||
// padding: 0 10px;
|
||||
// display: flex;
|
||||
// align-items: center;
|
||||
// background-color: $grey-6;
|
||||
// }
|
||||
//
|
||||
// .annotations {
|
||||
// overflow: hidden;
|
||||
// width: 100%;
|
||||
// height: calc(100% - 32px);
|
||||
//
|
||||
// .annotation-wrapper {
|
||||
// display: flex;
|
||||
// border-bottom: 1px solid $separator;
|
||||
//
|
||||
// .active-marker {
|
||||
// min-width: 4px;
|
||||
// min-height: 100%;
|
||||
// }
|
||||
//
|
||||
// &.active {
|
||||
// .active-marker {
|
||||
// background-color: $primary;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// .annotation {
|
||||
// padding: 10px 21px 10px 6px;
|
||||
// font-size: 11px;
|
||||
// line-height: 14px;
|
||||
// cursor: pointer;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
//
|
||||
// &.removed {
|
||||
// text-decoration: line-through;
|
||||
// color: $grey-7;
|
||||
// }
|
||||
//
|
||||
// .details {
|
||||
// display: flex;
|
||||
// position: relative;
|
||||
// }
|
||||
//
|
||||
// redaction-type-annotation-icon {
|
||||
// margin-top: 6px;
|
||||
// margin-right: 10px;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:hover {
|
||||
// background-color: #f9fafb;
|
||||
//
|
||||
// ::ng-deep .annotation-actions {
|
||||
// display: flex;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// &:hover {
|
||||
// overflow-y: auto;
|
||||
// @include scroll-bar;
|
||||
// }
|
||||
//
|
||||
// &.has-scrollbar:hover {
|
||||
// .annotation {
|
||||
// padding-right: 10px;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
}
|
||||
|
||||
.no-annotations {
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
//.no-annotations {
|
||||
// padding: 24px;
|
||||
// text-align: center;
|
||||
//}
|
||||
|
||||
.assign-actions-wrapper {
|
||||
display: flex;
|
||||
|
||||
@ -30,9 +30,9 @@ import { FileManagementControllerService, StatusControllerService } from '@redac
|
||||
import { PdfViewerDataService } from '../service/pdf-viewer-data.service';
|
||||
import { download } from '../../../utils/file-download-utils';
|
||||
import { ViewMode } from '../model/view-mode';
|
||||
import { FileWorkloadComponent } from '../../../components/file-workload/file-workload.component';
|
||||
|
||||
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
|
||||
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape', 'F', 'f'];
|
||||
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-file-preview-screen',
|
||||
@ -40,6 +40,33 @@ const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Es
|
||||
styleUrls: ['./file-preview-screen.component.scss']
|
||||
})
|
||||
export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
public dialogRef: MatDialogRef<any>;
|
||||
public viewMode: ViewMode = 'STANDARD';
|
||||
public fullScreen = false;
|
||||
public editingReviewer = false;
|
||||
public reviewerForm: FormGroup;
|
||||
public shouldDeselectAnnotationsOnPageChange = true;
|
||||
public analysisProgressInSeconds = 0;
|
||||
public analysisProgress: number;
|
||||
public analysisInterval: number;
|
||||
fileData: FileDataModel;
|
||||
fileId: string;
|
||||
annotationData: AnnotationData;
|
||||
selectedAnnotations: AnnotationWrapper[];
|
||||
viewReady = false;
|
||||
annotationFilters: FilterModel[];
|
||||
loadingMessage: string;
|
||||
canPerformAnnotationActions: boolean;
|
||||
filesAutoUpdateTimer: Subscription;
|
||||
fileReanalysedSubscription: Subscription;
|
||||
hideSkipped = false;
|
||||
public viewDocumentInfo = false;
|
||||
private projectId: string;
|
||||
private _instance: WebViewerInstance;
|
||||
|
||||
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
|
||||
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
||||
|
||||
constructor(
|
||||
public readonly appStateService: AppStateService,
|
||||
public readonly permissionsService: PermissionsService,
|
||||
@ -72,7 +99,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
get annotations() {
|
||||
get annotations(): AnnotationWrapper[] {
|
||||
return this.annotationData ? this.annotationData.visibleAnnotations : [];
|
||||
}
|
||||
|
||||
@ -92,62 +119,16 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
return this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0;
|
||||
}
|
||||
|
||||
get ignoreColor() {
|
||||
return this.appStateService.getDictionaryColor('skipped');
|
||||
}
|
||||
|
||||
get displayData() {
|
||||
return this.fileData?.fileData;
|
||||
}
|
||||
|
||||
private projectId: string;
|
||||
private _instance: WebViewerInstance;
|
||||
private _dialogRef: MatDialogRef<any>;
|
||||
|
||||
public viewMode: ViewMode = 'STANDARD';
|
||||
public fullScreen = false;
|
||||
public editingReviewer = false;
|
||||
public reviewerForm: FormGroup;
|
||||
public shouldDeselectAnnotationsOnPageChange = true;
|
||||
|
||||
public analysisProgressInSeconds = 0;
|
||||
public analysisProgress: number;
|
||||
public analysisInterval: number;
|
||||
|
||||
public quickScrollFirstEnabled = false;
|
||||
public quickScrollLastEnabled = false;
|
||||
|
||||
public displayedPages: number[] = [];
|
||||
|
||||
get indeterminateMode() {
|
||||
return (
|
||||
this.analysisProgress > 100 || this.appStateService.activeFile.analysisDuration < 3 * 1000 // it takes longer than usual - switch to indeterminate
|
||||
); // on less than 3 seconds show indeterminate
|
||||
}
|
||||
|
||||
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
|
||||
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
|
||||
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
|
||||
|
||||
fileData: FileDataModel;
|
||||
fileId: string;
|
||||
annotationData: AnnotationData;
|
||||
|
||||
displayedAnnotations: { [key: number]: { annotations: AnnotationWrapper[] } } = {};
|
||||
selectedAnnotations: AnnotationWrapper[];
|
||||
pagesPanelActive = true;
|
||||
viewReady = false;
|
||||
annotationFilters: FilterModel[];
|
||||
|
||||
loadingMessage: string;
|
||||
canPerformAnnotationActions: boolean;
|
||||
filesAutoUpdateTimer: Subscription;
|
||||
fileReanalysedSubscription: Subscription;
|
||||
|
||||
hideSkipped = false;
|
||||
|
||||
public viewDocumentInfo = false;
|
||||
|
||||
updateViewMode() {
|
||||
const allAnnotations = this._instance.annotManager.getAnnotationsList();
|
||||
|
||||
@ -183,15 +164,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
break;
|
||||
}
|
||||
|
||||
this._rebuildFilters();
|
||||
this.rebuildFilters();
|
||||
|
||||
this._updateCanPerformActions();
|
||||
}
|
||||
|
||||
private _updateCanPerformActions() {
|
||||
this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions() && this.viewMode === 'STANDARD';
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
document.documentElement.addEventListener('fullscreenchange', (event) => {
|
||||
if (!document.fullscreenElement) {
|
||||
@ -227,32 +204,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
this.fileReanalysedSubscription.unsubscribe();
|
||||
}
|
||||
|
||||
private _loadFileData(performUpdate: boolean = false) {
|
||||
return this._fileDownloadService.loadActiveFileData().pipe(
|
||||
tap((fileDataModel) => {
|
||||
if (fileDataModel.fileStatus.isWorkable) {
|
||||
if (performUpdate) {
|
||||
this.fileData.redactionLog = fileDataModel.redactionLog;
|
||||
this.fileData.redactionChangeLog = fileDataModel.redactionChangeLog;
|
||||
this.fileData.fileStatus = fileDataModel.fileStatus;
|
||||
this.fileData.manualRedactions = fileDataModel.manualRedactions;
|
||||
this._rebuildFilters(true);
|
||||
} else {
|
||||
this.fileData = fileDataModel;
|
||||
this._rebuildFilters();
|
||||
}
|
||||
} else {
|
||||
if (fileDataModel.fileStatus.isError) {
|
||||
this._router.navigate(['/ui/projects/' + this.appStateService.activeProjectId]);
|
||||
} else {
|
||||
this.loadingMessage = 'file-preview.reanalyse-file';
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private _rebuildFilters(deletePreviousAnnotations: boolean = false) {
|
||||
public rebuildFilters(deletePreviousAnnotations: boolean = false) {
|
||||
const startTime = new Date().getTime();
|
||||
if (deletePreviousAnnotations) {
|
||||
this.activeViewer.annotManager.deleteAnnotations(this.activeViewer.annotManager.getAnnotationsList(), {
|
||||
@ -270,7 +222,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
);
|
||||
const annotationFilters = this._annotationProcessingService.getAnnotationFilter(this.annotations);
|
||||
this.annotationFilters = processFilters(this.annotationFilters, annotationFilters);
|
||||
this.filtersChanged(this.annotationFilters);
|
||||
this._workloadComponent.filtersChanged(this.annotationFilters);
|
||||
console.log('[REDACTION] Process time: ' + (new Date().getTime() - processStartTime) + 'ms');
|
||||
console.log(
|
||||
'[REDACTION] Annotation Redraw and filter rebuild time: ' +
|
||||
@ -283,27 +235,22 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
|
||||
handleAnnotationSelected(annotationIds: string[]) {
|
||||
this.selectedAnnotations = annotationIds.map((annotationId) => this.annotations.find((annotationWrapper) => annotationWrapper.id === annotationId));
|
||||
this.scrollToSelectedAnnotation();
|
||||
this._workloadComponent.scrollToSelectedAnnotation();
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
annotationClicked(annotation: AnnotationWrapper) {
|
||||
this.pagesPanelActive = false;
|
||||
this.selectAnnotation(annotation);
|
||||
}
|
||||
|
||||
selectAnnotation(annotation: AnnotationWrapper) {
|
||||
this._viewerComponent.selectAnnotation(annotation);
|
||||
}
|
||||
|
||||
selectPage(pageNumber: number) {
|
||||
this._viewerComponent.navigateToPage(pageNumber);
|
||||
this._scrollAnnotationsToPage(pageNumber, 'always');
|
||||
this._workloadComponent.scrollAnnotationsToPage(pageNumber, 'always');
|
||||
}
|
||||
|
||||
openManualAnnotationDialog($event: ManualRedactionEntryWrapper) {
|
||||
this.ngZone.run(() => {
|
||||
this._dialogRef = this._dialogService.openManualAnnotationDialog($event, async (response: ManualAnnotationResponse) => {
|
||||
this.dialogRef = this._dialogService.openManualAnnotationDialog($event, async (response: ManualAnnotationResponse) => {
|
||||
if (response?.annotationId) {
|
||||
const annotation = this.activeViewer.annotManager.getAnnotationById(response.manualRedactionEntryWrapper.rectId);
|
||||
this.activeViewer.annotManager.deleteAnnotation(annotation);
|
||||
@ -317,81 +264,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
annotationIsSelected(annotation: AnnotationWrapper) {
|
||||
return this.selectedAnnotations?.find((a) => a.id === annotation.id);
|
||||
}
|
||||
|
||||
private get firstSelectedAnnotation() {
|
||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
|
||||
}
|
||||
|
||||
private get lastSelectedAnnotation() {
|
||||
return this.selectedAnnotations?.length ? this.selectedAnnotations[this.selectedAnnotations.length - 1] : null;
|
||||
}
|
||||
|
||||
@debounce()
|
||||
private _scrollViews() {
|
||||
this._scrollQuickNavigation();
|
||||
this._scrollAnnotations();
|
||||
}
|
||||
|
||||
@debounce()
|
||||
private scrollToSelectedAnnotation() {
|
||||
if (!this.selectedAnnotations || this.selectedAnnotations.length === 0) {
|
||||
return;
|
||||
}
|
||||
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[annotation-id="${this.firstSelectedAnnotation.id}"].active`);
|
||||
this._scrollToFirstElement(elements);
|
||||
}
|
||||
|
||||
private _scrollQuickNavigationToPage(page: number) {
|
||||
const elements: any[] = this._quickNavigationElement.nativeElement.querySelectorAll(`#quick-nav-page-${page}`);
|
||||
this._scrollToFirstElement(elements);
|
||||
}
|
||||
|
||||
private _scrollQuickNavigation() {
|
||||
let quickNavPageIndex = this.displayedPages.findIndex((p) => p >= this.activeViewerPage);
|
||||
if (quickNavPageIndex === -1 || this.displayedPages[quickNavPageIndex] !== this.activeViewerPage) {
|
||||
quickNavPageIndex = Math.max(0, quickNavPageIndex - 1);
|
||||
}
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[quickNavPageIndex]);
|
||||
}
|
||||
|
||||
public scrollQuickNavFirst() {
|
||||
if (this.displayedPages.length > 0) {
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public scrollQuickNavLast() {
|
||||
if (this.displayedPages.length > 0) {
|
||||
this._scrollQuickNavigationToPage(this.displayedPages[this.displayedPages.length - 1]);
|
||||
}
|
||||
}
|
||||
|
||||
private _scrollAnnotations() {
|
||||
if (this.firstSelectedAnnotation?.pageNumber === this.activeViewerPage) {
|
||||
return;
|
||||
}
|
||||
this._scrollAnnotationsToPage(this.activeViewerPage, 'always');
|
||||
}
|
||||
|
||||
private _scrollAnnotationsToPage(page: number, mode: 'always' | 'if-needed' = 'if-needed') {
|
||||
const elements: any[] = this._annotationsElement.nativeElement.querySelectorAll(`div[anotation-page-header="${page}"]`);
|
||||
this._scrollToFirstElement(elements, mode);
|
||||
}
|
||||
|
||||
private _scrollToFirstElement(elements: HTMLElement[], mode: 'always' | 'if-needed' = 'if-needed') {
|
||||
if (elements.length > 0) {
|
||||
scrollIntoView(elements[0], {
|
||||
behavior: 'smooth',
|
||||
scrollMode: mode,
|
||||
block: 'start',
|
||||
inline: 'start'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public toggleFullScreen() {
|
||||
this.fullScreen = !this.fullScreen;
|
||||
if (this.fullScreen) {
|
||||
@ -403,7 +275,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
|
||||
@HostListener('window:keyup', ['$event'])
|
||||
handleKeyEvent($event: KeyboardEvent) {
|
||||
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this._dialogRef?.getState() === MatDialogState.OPEN) {
|
||||
if (!ALL_HOTKEY_ARRAY.includes($event.key) || this.dialogRef?.getState() === MatDialogState.OPEN) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -421,145 +293,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($event.key === 'ArrowLeft') {
|
||||
this.pagesPanelActive = true;
|
||||
}
|
||||
if ($event.key === 'ArrowRight') {
|
||||
this.pagesPanelActive = false;
|
||||
// if we activated annotationsPanel - select first annotation from this page in case there is no
|
||||
// selected annotation on this page
|
||||
if (!this.pagesPanelActive) {
|
||||
this._selectFirstAnnotationOnCurrentPageIfNecessary();
|
||||
}
|
||||
}
|
||||
|
||||
if ($event.key === 'ArrowLeft' || $event.key === 'ArrowRight') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.pagesPanelActive) {
|
||||
this._navigateAnnotations($event);
|
||||
} else {
|
||||
this._navigatePages($event);
|
||||
}
|
||||
|
||||
this._changeDetectorRef.detectChanges();
|
||||
}
|
||||
|
||||
private _selectFirstAnnotationOnCurrentPageIfNecessary() {
|
||||
if (
|
||||
(!this.firstSelectedAnnotation || this.activeViewerPage !== this.firstSelectedAnnotation.pageNumber) &&
|
||||
this.displayedPages.indexOf(this.activeViewerPage) >= 0
|
||||
) {
|
||||
this.selectAnnotation(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
}
|
||||
}
|
||||
|
||||
private _navigateAnnotations($event: KeyboardEvent) {
|
||||
if (!this.firstSelectedAnnotation || this.activeViewerPage !== this.firstSelectedAnnotation.pageNumber) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
if (pageIdx !== -1) {
|
||||
// Displayed page has annotations
|
||||
this.selectAnnotation(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
|
||||
} else {
|
||||
// Displayed page doesn't have annotations
|
||||
if ($event.key === 'ArrowDown') {
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation(this.displayedAnnotations[nextPage].annotations[0]);
|
||||
} else {
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
|
||||
this.selectAnnotation(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const page = this.firstSelectedAnnotation.pageNumber;
|
||||
const pageIdx = this.displayedPages.indexOf(page);
|
||||
const annotationsOnPage = this.displayedAnnotations[page].annotations;
|
||||
const idx = annotationsOnPage.findIndex((a) => a.id === this.firstSelectedAnnotation.id);
|
||||
|
||||
if ($event.key === 'ArrowDown') {
|
||||
if (idx + 1 !== annotationsOnPage.length) {
|
||||
// If not last item in page
|
||||
this.selectAnnotation(annotationsOnPage[idx + 1]);
|
||||
} else if (pageIdx + 1 < this.displayedPages.length) {
|
||||
// If not last page
|
||||
const nextPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx + 1]].annotations;
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation(nextPageAnnotations[0]);
|
||||
}
|
||||
} else {
|
||||
if (idx !== 0) {
|
||||
// If not first item in page
|
||||
this.selectAnnotation(annotationsOnPage[idx - 1]);
|
||||
} else if (pageIdx) {
|
||||
// If not first page
|
||||
const prevPageAnnotations = this.displayedAnnotations[this.displayedPages[pageIdx - 1]].annotations;
|
||||
this.shouldDeselectAnnotationsOnPageChange = false;
|
||||
this.selectAnnotation(prevPageAnnotations[prevPageAnnotations.length - 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _navigatePages($event: KeyboardEvent) {
|
||||
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
|
||||
|
||||
if ($event.key === 'ArrowDown') {
|
||||
if (pageIdx !== -1) {
|
||||
// If active page has annotations
|
||||
if (pageIdx !== this.displayedPages.length - 1) {
|
||||
this.selectPage(this.displayedPages[pageIdx + 1]);
|
||||
}
|
||||
} else {
|
||||
// If active page doesn't have annotations
|
||||
const nextPage = this._nextPageWithAnnotations();
|
||||
if (nextPage) {
|
||||
this.selectPage(nextPage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (pageIdx !== -1) {
|
||||
// If active page has annotations
|
||||
if (pageIdx !== 0) {
|
||||
this.selectPage(this.displayedPages[pageIdx - 1]);
|
||||
}
|
||||
} else {
|
||||
// If active page doesn't have annotations
|
||||
const prevPage = this._prevPageWithAnnotations();
|
||||
if (prevPage) {
|
||||
this.selectPage(prevPage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _nextPageWithAnnotations() {
|
||||
let idx = 0;
|
||||
for (const page of this.displayedPages) {
|
||||
if (page > this.activeViewerPage) {
|
||||
break;
|
||||
}
|
||||
++idx;
|
||||
}
|
||||
return idx < this.displayedPages.length ? this.displayedPages[idx] : null;
|
||||
}
|
||||
|
||||
private _prevPageWithAnnotations() {
|
||||
let idx = this.displayedPages.length - 1;
|
||||
for (const page of this.displayedPages.reverse()) {
|
||||
if (page < this.activeViewerPage) {
|
||||
this.selectPage(this.displayedPages[idx]);
|
||||
this._scrollAnnotations();
|
||||
break;
|
||||
}
|
||||
--idx;
|
||||
}
|
||||
return idx >= 0 ? this.displayedPages[idx] : null;
|
||||
}
|
||||
|
||||
viewerPageChanged($event: any) {
|
||||
if (typeof $event === 'number') {
|
||||
this._scrollViews();
|
||||
@ -590,74 +326,10 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
filtersChanged(filters: FilterModel[]) {
|
||||
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(this.annotations, filters);
|
||||
this.displayedPages = Object.keys(this.displayedAnnotations).map((key) => Number(key));
|
||||
this.computeQuickNavButtonsState();
|
||||
this._changeDetectorRef.markForCheck();
|
||||
}
|
||||
|
||||
preventKeyDefault($event: KeyboardEvent) {
|
||||
if (COMMAND_KEY_ARRAY.includes($event.key)) {
|
||||
$event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
public computeQuickNavButtonsState() {
|
||||
const element: HTMLElement = this._quickNavigationElement.nativeElement.querySelector(`#pages`);
|
||||
const { scrollTop, scrollHeight, clientHeight } = element;
|
||||
this.quickScrollFirstEnabled = scrollTop !== 0;
|
||||
this.quickScrollLastEnabled = scrollHeight !== scrollTop + clientHeight;
|
||||
}
|
||||
|
||||
private _cleanupAndRedrawManualAnnotations() {
|
||||
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
|
||||
this.fileData.manualRedactions = manualRedactions;
|
||||
this._rebuildFilters();
|
||||
this._annotationDrawService.drawAnnotations(this._instance, this.annotationData.allAnnotations, this.hideSkipped);
|
||||
});
|
||||
}
|
||||
|
||||
private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) {
|
||||
const currentPageAnnotations = this.annotations.filter((a) => a.pageNumber === page);
|
||||
const currentPageAnnotationIds = currentPageAnnotations.map((a) => a.id);
|
||||
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
||||
|
||||
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
|
||||
this.fileData.manualRedactions = manualRedactions;
|
||||
this._rebuildFilters();
|
||||
if (this.viewMode === 'STANDARD') {
|
||||
currentPageAnnotationIds.forEach((id) => {
|
||||
this._findAndDeleteAnnotation(id);
|
||||
});
|
||||
const newPageAnnotations = this.annotations.filter((item) => item.pageNumber === page);
|
||||
this._handleDeltaAnnotationFilters(currentPageAnnotations, newPageAnnotations);
|
||||
this._annotationDrawService.drawAnnotations(this._instance, newPageAnnotations, this.hideSkipped);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _handleDeltaAnnotationFilters(currentPageAnnotations: AnnotationWrapper[], newPageAnnotations: AnnotationWrapper[]) {
|
||||
const hasAnyFilterSet = this.annotationFilters.find((f) => f.checked || f.indeterminate);
|
||||
if (hasAnyFilterSet) {
|
||||
const oldPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(currentPageAnnotations);
|
||||
const newPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(newPageAnnotations);
|
||||
handleFilterDelta(oldPageSpecificFilters, newPageSpecificFilters, this.annotationFilters);
|
||||
this.filtersChanged(this.annotationFilters);
|
||||
}
|
||||
}
|
||||
|
||||
async annotationsChangedByReviewAction(annotation: AnnotationWrapper) {
|
||||
await this._cleanupAndRedrawManualAnnotationsForEntirePage(annotation.pageNumber);
|
||||
}
|
||||
|
||||
private _findAndDeleteAnnotation(id: string) {
|
||||
const viewerAnnotation = this.activeViewer.annotManager.getAnnotationById(id);
|
||||
if (viewerAnnotation) {
|
||||
this.activeViewer.annotManager.deleteAnnotation(viewerAnnotation, { imported: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
async fileActionPerformed(action: string) {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
@ -682,31 +354,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private _startAnalysisTimer() {
|
||||
this._stopAnalysisTimer();
|
||||
|
||||
if (this.appStateService.activeFile.analysisDuration > 0) {
|
||||
this.analysisProgress = 0;
|
||||
this.analysisProgressInSeconds = 0;
|
||||
|
||||
this.analysisInterval = setInterval(() => {
|
||||
this.analysisProgressInSeconds += 1;
|
||||
this.analysisProgress = (this.analysisProgressInSeconds * 100) / (this.appStateService.activeFile.analysisDuration / 1000);
|
||||
}, 1000);
|
||||
} else {
|
||||
this.analysisInterval = 0;
|
||||
this.analysisProgress = 0;
|
||||
this.analysisProgressInSeconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private _stopAnalysisTimer() {
|
||||
if (this.analysisInterval) {
|
||||
clearInterval(this.analysisInterval);
|
||||
this.analysisInterval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public async assignToMe() {
|
||||
await this._fileActionService.assignToMe(this.fileData.fileStatus, async () => {
|
||||
await this.appStateService.reloadActiveFile();
|
||||
@ -732,21 +379,6 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
this.reviewerForm.setValue({ reviewer: this.appStateService.activeFile.currentReviewer });
|
||||
}
|
||||
|
||||
pageSelectedByClick($event: number) {
|
||||
this.pagesPanelActive = true;
|
||||
this.selectPage($event);
|
||||
}
|
||||
|
||||
/* Get the documentElement (<html>) to display the page in fullscreen */
|
||||
|
||||
/* View in fullscreen */
|
||||
private _openFullScreen() {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement.requestFullscreen) {
|
||||
documentElement.requestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
/* Close fullscreen */
|
||||
closeFullScreen() {
|
||||
if (document.exitFullscreen) {
|
||||
@ -787,8 +419,118 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
|
||||
window.open(`/html-debug/${this.projectId}/${this.fileId}`, '_blank');
|
||||
}
|
||||
|
||||
logAnnotation(annotation: AnnotationWrapper) {
|
||||
console.log(annotation);
|
||||
private _updateCanPerformActions() {
|
||||
this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions() && this.viewMode === 'STANDARD';
|
||||
}
|
||||
|
||||
private _loadFileData(performUpdate: boolean = false) {
|
||||
return this._fileDownloadService.loadActiveFileData().pipe(
|
||||
tap((fileDataModel) => {
|
||||
if (fileDataModel.fileStatus.isWorkable) {
|
||||
if (performUpdate) {
|
||||
this.fileData.redactionLog = fileDataModel.redactionLog;
|
||||
this.fileData.redactionChangeLog = fileDataModel.redactionChangeLog;
|
||||
this.fileData.fileStatus = fileDataModel.fileStatus;
|
||||
this.fileData.manualRedactions = fileDataModel.manualRedactions;
|
||||
this.rebuildFilters(true);
|
||||
} else {
|
||||
this.fileData = fileDataModel;
|
||||
this.rebuildFilters();
|
||||
}
|
||||
} else {
|
||||
if (fileDataModel.fileStatus.isError) {
|
||||
this._router.navigate(['/ui/projects/' + this.appStateService.activeProjectId]);
|
||||
} else {
|
||||
this.loadingMessage = 'file-preview.reanalyse-file';
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
@debounce()
|
||||
private _scrollViews() {
|
||||
this._workloadComponent.scrollQuickNavigation();
|
||||
this._workloadComponent.scrollAnnotations();
|
||||
}
|
||||
|
||||
/* Get the documentElement (<html>) to display the page in fullscreen */
|
||||
|
||||
private _cleanupAndRedrawManualAnnotations() {
|
||||
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
|
||||
this.fileData.manualRedactions = manualRedactions;
|
||||
this.rebuildFilters();
|
||||
this._annotationDrawService.drawAnnotations(this._instance, this.annotationData.allAnnotations, this.hideSkipped);
|
||||
});
|
||||
}
|
||||
|
||||
private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) {
|
||||
const currentPageAnnotations = this.annotations.filter((a) => a.pageNumber === page);
|
||||
const currentPageAnnotationIds = currentPageAnnotations.map((a) => a.id);
|
||||
this.fileData.fileStatus = await this.appStateService.reloadActiveFile();
|
||||
|
||||
this._fileDownloadService.loadActiveFileManualAnnotations().subscribe((manualRedactions) => {
|
||||
this.fileData.manualRedactions = manualRedactions;
|
||||
this.rebuildFilters();
|
||||
if (this.viewMode === 'STANDARD') {
|
||||
currentPageAnnotationIds.forEach((id) => {
|
||||
this._findAndDeleteAnnotation(id);
|
||||
});
|
||||
const newPageAnnotations = this.annotations.filter((item) => item.pageNumber === page);
|
||||
this._handleDeltaAnnotationFilters(currentPageAnnotations, newPageAnnotations);
|
||||
this._annotationDrawService.drawAnnotations(this._instance, newPageAnnotations, this.hideSkipped);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private _handleDeltaAnnotationFilters(currentPageAnnotations: AnnotationWrapper[], newPageAnnotations: AnnotationWrapper[]) {
|
||||
const hasAnyFilterSet = this.annotationFilters.find((f) => f.checked || f.indeterminate);
|
||||
if (hasAnyFilterSet) {
|
||||
const oldPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(currentPageAnnotations);
|
||||
const newPageSpecificFilters = this._annotationProcessingService.getAnnotationFilter(newPageAnnotations);
|
||||
handleFilterDelta(oldPageSpecificFilters, newPageSpecificFilters, this.annotationFilters);
|
||||
this._workloadComponent.filtersChanged(this.annotationFilters);
|
||||
}
|
||||
}
|
||||
|
||||
private _findAndDeleteAnnotation(id: string) {
|
||||
const viewerAnnotation = this.activeViewer.annotManager.getAnnotationById(id);
|
||||
if (viewerAnnotation) {
|
||||
this.activeViewer.annotManager.deleteAnnotation(viewerAnnotation, { imported: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
private _startAnalysisTimer() {
|
||||
this._stopAnalysisTimer();
|
||||
|
||||
if (this.appStateService.activeFile.analysisDuration > 0) {
|
||||
this.analysisProgress = 0;
|
||||
this.analysisProgressInSeconds = 0;
|
||||
|
||||
this.analysisInterval = setInterval(() => {
|
||||
this.analysisProgressInSeconds += 1;
|
||||
this.analysisProgress = (this.analysisProgressInSeconds * 100) / (this.appStateService.activeFile.analysisDuration / 1000);
|
||||
}, 1000);
|
||||
} else {
|
||||
this.analysisInterval = 0;
|
||||
this.analysisProgress = 0;
|
||||
this.analysisProgressInSeconds = 0;
|
||||
}
|
||||
}
|
||||
|
||||
private _stopAnalysisTimer() {
|
||||
if (this.analysisInterval) {
|
||||
clearInterval(this.analysisInterval);
|
||||
this.analysisInterval = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* View in fullscreen */
|
||||
private _openFullScreen() {
|
||||
const documentElement = document.documentElement;
|
||||
if (documentElement.requestFullscreen) {
|
||||
documentElement.requestFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
private _handleIgnoreAnnotationsDrawing() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user