-
- {{ 'file-preview.no-annotations-for-page' | translate }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ activeViewerPage }} - {{ displayedAnnotations[activeViewerPage]?.annotations?.length || 0 }}
+
+
+
-
-
-
-
-
-
-
-
{{ annotation.typeLabel | translate }}
+
+ {{ 'file-preview.no-annotations-for-page' | translate }}
+
+
+
+
+
+
+
+
+
+
+ {{ annotation.typeLabel | translate }}
+
+
+ {{ annotation.descriptor | translate }}: {{ annotation.dictionary | humanize: false }}
+
+
+ : {{ annotation.content }}
+
+ {{ annotation.id }}
-
- {{ annotation.descriptor | translate }}: {{ annotation.dictionary | humanize: false }}
-
-
-
: {{ annotation.content }}
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.scss b/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.scss
index 1ead7e879..91d22cdea 100644
--- a/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.scss
+++ b/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.scss
@@ -2,11 +2,51 @@
@import '../../../../../assets/styles/red-mixins';
.right-content {
+ flex-direction: column;
+
.no-annotations {
padding: 24px;
text-align: center;
}
+ .multi-select {
+ min-height: 40px;
+ background: $primary;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 8px 0 16px;
+ color: $white;
+
+ .selected-wrapper {
+ display: flex;
+ align-items: center;
+
+ .select-oval,
+ .selection-icon {
+ margin-right: 8px;
+ color: inherit;
+ }
+
+ .all-caps-label {
+ opacity: 1;
+ }
+
+ redaction-annotation-remove-actions {
+ margin-left: 16px;
+ }
+ }
+ }
+
+ .annotations-wrapper {
+ display: flex;
+ height: 100%;
+
+ &.multi-select-active {
+ height: calc(100% - 40px);
+ }
+ }
+
.quick-navigation,
.annotations {
overflow-y: scroll;
@@ -64,7 +104,16 @@
padding: 0 10px;
display: flex;
align-items: center;
+ justify-content: space-between;
background-color: $grey-6;
+
+ > div {
+ display: flex;
+
+ > div:not(:last-child) {
+ margin-right: 8px;
+ }
+ }
}
.annotations {
@@ -76,13 +125,23 @@
display: flex;
border-bottom: 1px solid $separator;
- .active-marker {
+ .active-bar-marker {
min-width: 4px;
min-height: 100%;
}
+ .active-icon-marker-container {
+ min-width: 20px;
+
+ .active-icon-marker {
+ color: $primary;
+ width: 20px;
+ height: 20px;
+ }
+ }
+
&.active {
- .active-marker {
+ &:not(.multi-select-active) .active-bar-marker {
background-color: $primary;
}
}
diff --git a/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts b/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts
index a56d1d7f9..53af97aa6 100644
--- a/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts
+++ b/apps/red-ui/src/app/modules/projects/components/file-workload/file-workload.component.ts
@@ -6,6 +6,8 @@ import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import scrollIntoView from 'scroll-into-view-if-needed';
import { debounce } from '../../../../utils/debounce';
import { FileDataModel } from '../../../../models/file/file-data.model';
+import { AnnotationPermissions } from '../../../../models/file/annotation.permissions';
+import { PermissionsService } from '../../../../services/permissions.service';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
@@ -22,21 +24,23 @@ export class FileWorkloadComponent {
@Input()
set annotations(value: AnnotationWrapper[]) {
this._annotations = value;
- // this.computeQuickNavButtonsState();
}
@Input() selectedAnnotations: AnnotationWrapper[];
@Input() activeViewerPage: number;
@Input() shouldDeselectAnnotationsOnPageChange: boolean;
+ @Output() shouldDeselectAnnotationsOnPageChangeChange = new EventEmitter
();
@Input() dialogRef: MatDialogRef;
@Input() annotationFilters: FilterModel[];
@Input() fileData: FileDataModel;
@Input() hideSkipped: boolean;
@Input() annotationActionsTemplate: TemplateRef;
- @Output() selectAnnotation = new EventEmitter();
+ @Output() selectAnnotations = new EventEmitter();
+ @Output() deselectAnnotations = new EventEmitter();
@Output() selectPage = new EventEmitter();
@Output() toggleSkipped = new EventEmitter();
+ @Output() annotationsChanged = new EventEmitter();
public quickScrollFirstEnabled = false;
public quickScrollLastEnabled = false;
@@ -46,7 +50,27 @@ export class FileWorkloadComponent {
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
- constructor(private _changeDetectorRef: ChangeDetectorRef, private _annotationProcessingService: AnnotationProcessingService) {}
+ private _multiSelectActive = false;
+
+ public get multiSelectActive(): boolean {
+ return this._multiSelectActive;
+ }
+
+ public set multiSelectActive(value: boolean) {
+ this._multiSelectActive = value;
+ if (!value) {
+ this.selectAnnotations.emit();
+ } else {
+ this.shouldDeselectAnnotationsOnPageChange = false;
+ this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
+ }
+ }
+
+ constructor(
+ private _changeDetectorRef: ChangeDetectorRef,
+ private _annotationProcessingService: AnnotationProcessingService,
+ private readonly _permissionsService: PermissionsService
+ ) {}
private get firstSelectedAnnotation() {
return this.selectedAnnotations?.length ? this.selectedAnnotations[0] : null;
@@ -71,6 +95,20 @@ export class FileWorkloadComponent {
console.log(annotation);
}
+ public pageHasSelection(page: number) {
+ return this.multiSelectActive && !!this.selectedAnnotations?.find((a) => a.pageNumber === page);
+ }
+
+ public selectAllOnActivePage() {
+ this.selectAnnotations.emit(this.displayedAnnotations[this.activeViewerPage].annotations);
+ this._changeDetectorRef.detectChanges();
+ }
+
+ public deselectAllOnActivePage() {
+ this.deselectAnnotations.emit(this.displayedAnnotations[this.activeViewerPage].annotations);
+ this._changeDetectorRef.detectChanges();
+ }
+
@debounce(0)
public filtersChanged(filters: FilterModel[]) {
this.displayedAnnotations = this._annotationProcessingService.filterAndGroupAnnotations(this._annotations, filters);
@@ -88,9 +126,16 @@ export class FileWorkloadComponent {
}, 0);
}
- public annotationClicked(annotation: AnnotationWrapper) {
+ public annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent) {
this.pagesPanelActive = false;
- this.selectAnnotation.emit(annotation);
+ if (this.annotationIsSelected(annotation)) {
+ this.deselectAnnotations.emit([annotation]);
+ } else {
+ if (($event.ctrlKey || $event.metaKey) && this.selectedAnnotations.length > 0) {
+ this.multiSelectActive = true;
+ }
+ this.selectAnnotations.emit({ annotations: [annotation], multiSelect: true });
+ }
}
@HostListener('window:keyup', ['$event'])
@@ -107,15 +152,18 @@ export class FileWorkloadComponent {
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) {
+ // selected annotation on this page and not in multi select mode
+ if (!this.pagesPanelActive && !this.multiSelectActive) {
this._selectFirstAnnotationOnCurrentPageIfNecessary();
}
return;
}
if (!this.pagesPanelActive) {
- this._navigateAnnotations($event);
+ // Disable annotation navigation in multi select mode => TODO: maybe implement selection on enter?
+ if (!this.multiSelectActive) {
+ this._navigateAnnotations($event);
+ }
} else {
this._navigatePages($event);
}
@@ -180,7 +228,7 @@ export class FileWorkloadComponent {
(!this.firstSelectedAnnotation || this.activeViewerPage !== this.firstSelectedAnnotation.pageNumber) &&
this.displayedPages.indexOf(this.activeViewerPage) >= 0
) {
- this.selectAnnotation.emit(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
+ this.selectAnnotations.emit([this.displayedAnnotations[this.activeViewerPage].annotations[0]]);
}
}
@@ -189,18 +237,20 @@ export class FileWorkloadComponent {
const pageIdx = this.displayedPages.indexOf(this.activeViewerPage);
if (pageIdx !== -1) {
// Displayed page has annotations
- this.selectAnnotation.emit(this.displayedAnnotations[this.activeViewerPage].annotations[0]);
+ this.selectAnnotations.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]);
+ this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
+ this.selectAnnotations.emit([this.displayedAnnotations[nextPage].annotations[0]]);
} else {
const prevPage = this._prevPageWithAnnotations();
this.shouldDeselectAnnotationsOnPageChange = false;
+ this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
const prevPageAnnotations = this.displayedAnnotations[prevPage].annotations;
- this.selectAnnotation.emit(prevPageAnnotations[prevPageAnnotations.length - 1]);
+ this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
}
}
} else {
@@ -212,22 +262,24 @@ export class FileWorkloadComponent {
if ($event.key === 'ArrowDown') {
if (idx + 1 !== annotationsOnPage.length) {
// If not last item in page
- this.selectAnnotation.emit(annotationsOnPage[idx + 1]);
+ this.selectAnnotations.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]);
+ this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
+ this.selectAnnotations.emit([nextPageAnnotations[0]]);
}
} else {
if (idx !== 0) {
// If not first item in page
- this.selectAnnotation.emit(annotationsOnPage[idx - 1]);
+ this.selectAnnotations.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]);
+ this.shouldDeselectAnnotationsOnPageChangeChange.emit(false);
+ this.selectAnnotations.emit([prevPageAnnotations[prevPageAnnotations.length - 1]]);
}
}
}
diff --git a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.html b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.html
index d0955c95d..d912f51bd 100644
--- a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.html
+++ b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.html
@@ -10,4 +10,5 @@
{{ number }}
+
diff --git a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.scss b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.scss
index 3bbdd9b5f..dd386e81d 100644
--- a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.scss
+++ b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.scss
@@ -41,4 +41,14 @@
color: $grey-1;
}
}
+
+ .dot {
+ background: $primary;
+ height: 8px;
+ width: 8px;
+ border-radius: 50%;
+ position: absolute;
+ top: 9px;
+ right: 10px;
+ }
}
diff --git a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts
index c2f0ce4a0..5e7bb8cf0 100644
--- a/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts
+++ b/apps/red-ui/src/app/modules/projects/components/page-indicator/page-indicator.component.ts
@@ -14,6 +14,7 @@ export class PageIndicatorComponent implements OnChanges, OnInit, OnDestroy {
@Input() active: boolean;
@Input() number: number;
@Input() viewedPages: ViewedPages;
+ @Input() activeSelection = false;
@Output() pageSelected = new EventEmitter();
diff --git a/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts
index 701917417..f576348bf 100644
--- a/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts
+++ b/apps/red-ui/src/app/modules/projects/components/pdf-viewer/pdf-viewer.component.ts
@@ -28,6 +28,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
@Input() canPerformActions = false;
@Input() annotations: AnnotationWrapper[];
@Input() shouldDeselectAnnotationsOnPageChange = true;
+ @Input() multiSelectActive: boolean;
@Output() fileReady = new EventEmitter();
@Output() annotationSelected = new EventEmitter();
@@ -83,13 +84,13 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
this._configureTextPopup();
instance.annotManager.on('annotationSelected', (annotations, action) => {
+ this.annotationSelected.emit(instance.annotManager.getSelectedAnnotations().map((ann) => ann.Id));
if (action === 'deselected') {
- this.annotationSelected.emit([]);
this._toggleRectangleAnnotationAction(true);
} else {
this._configureAnnotationSpecificActions(annotations);
this._toggleRectangleAnnotationAction(annotations.length === 1 && annotations[0].ReadOnly);
- this.annotationSelected.emit(annotations.map((a) => a.Id));
+ // this.annotationSelected.emit(annotations.map((a) => a.Id));
}
});
@@ -103,7 +104,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
instance.docViewer.on('pageNumberUpdated', (pageNumber) => {
if (this.shouldDeselectAnnotationsOnPageChange) {
- this.instance.annotManager.deselectAllAnnotations();
+ this.deselectAllAnnotations();
}
this._ngZone.run(() => this.pageChanged.emit(pageNumber));
});
@@ -253,7 +254,7 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
quadsObject[activePage] = [quad];
const mre = this._getManualRedactionEntry(quadsObject, 'Rectangle');
// cleanup selection and button state
- this.instance.annotManager.deselectAllAnnotations();
+ this.deselectAllAnnotations();
this.instance.disableElements(['shapeToolGroupButton']);
this.instance.enableElements(['shapeToolGroupButton']);
// dispatch event
@@ -381,12 +382,33 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
};
}
- public selectAnnotation(annotation: AnnotationWrapper) {
+ public deselectAllAnnotations() {
this.instance.annotManager.deselectAllAnnotations();
- const annotationFromViewer = this.instance.annotManager.getAnnotationById(annotation.id);
- this.instance.annotManager.selectAnnotation(annotationFromViewer);
- this.navigateToPage(annotation.pageNumber);
- this.instance.annotManager.jumpToAnnotation(annotationFromViewer);
+ }
+
+ public selectAnnotations($event: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
+ let annotations: AnnotationWrapper[];
+ let multiSelect: boolean;
+ if ($event instanceof Array) {
+ annotations = $event;
+ multiSelect = false;
+ } else {
+ annotations = $event.annotations;
+ multiSelect = $event.multiSelect;
+ }
+
+ if (!this.multiSelectActive && !multiSelect) {
+ this.deselectAllAnnotations();
+ }
+
+ const annotationsFromViewer = annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id));
+ this.instance.annotManager.selectAnnotations(annotationsFromViewer);
+ this.navigateToPage(annotations[0].pageNumber);
+ this.instance.annotManager.jumpToAnnotation(annotationsFromViewer[0]);
+ }
+
+ public deselectAnnotations(annotations: AnnotationWrapper[]) {
+ this.instance.annotManager.deselectAnnotations(annotations.map((ann) => this.instance.annotManager.getAnnotationById(ann.id)));
}
public navigateToPage(pageNumber: number) {
diff --git a/apps/red-ui/src/app/modules/projects/projects.module.ts b/apps/red-ui/src/app/modules/projects/projects.module.ts
index 46f6b10d9..81ebdb3a4 100644
--- a/apps/red-ui/src/app/modules/projects/projects.module.ts
+++ b/apps/red-ui/src/app/modules/projects/projects.module.ts
@@ -34,6 +34,7 @@ import { PdfViewerDataService } from './services/pdf-viewer-data.service';
import { ManualAnnotationService } from './services/manual-annotation.service';
import { AnnotationDrawService } from './services/annotation-draw.service';
import { AnnotationProcessingService } from './services/annotation-processing.service';
+import { AnnotationRemoveActionsComponent } from './components/annotation-remove-actions/annotation-remove-actions.component';
const screens = [ProjectListingScreenComponent, ProjectOverviewScreenComponent, FilePreviewScreenComponent];
@@ -62,6 +63,7 @@ const components = [
ProjectListingActionsComponent,
DocumentInfoComponent,
FileWorkloadComponent,
+ AnnotationRemoveActionsComponent,
...screens,
...dialogs
diff --git a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.html
index 48d5737a4..2c957ea18 100644
--- a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.html
+++ b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.html
@@ -205,6 +205,7 @@
[fileStatus]="appStateService.activeFile"
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
[annotations]="annotations"
+ [multiSelectActive]="!!fileWorkloadComponent?.multiSelectActive"
>
@@ -229,15 +230,17 @@
[annotations]="annotations"
[selectedAnnotations]="selectedAnnotations"
[activeViewerPage]="activeViewerPage"
- [shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
+ [(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
[dialogRef]="dialogRef"
[annotationFilters]="annotationFilters"
[fileData]="fileData"
[hideSkipped]="hideSkipped"
[annotationActionsTemplate]="annotationActionsTemplate"
- (selectAnnotation)="selectAnnotation($event)"
+ (selectAnnotations)="selectAnnotations($event)"
+ (deselectAnnotations)="deselectAnnotations($event)"
(selectPage)="selectPage($event)"
(toggleSkipped)="toggleSkipped($event)"
+ (annotationsChanged)="annotationsChangedByReviewAction($event)"
>
diff --git a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.scss b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.scss
index cf0fe3019..99f4f8411 100644
--- a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.scss
+++ b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.scss
@@ -38,6 +38,15 @@
align-items: center;
justify-content: space-between;
padding: 0 24px;
+
+ > div {
+ display: flex;
+ align-items: center;
+
+ > *:not(:last-child) {
+ margin-right: 16px;
+ }
+ }
}
::ng-deep .right-content {
@@ -45,139 +54,8 @@
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;
- // }
- // }
- // }
- //}
}
-//.no-annotations {
-// padding: 24px;
-// text-align: center;
-//}
-
.assign-actions-wrapper {
display: flex;
margin-left: 8px;
diff --git a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts
index c2ed3bdb7..ada33a26a 100644
--- a/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/modules/projects/screens/file-preview-screen/file-preview-screen.component.ts
@@ -65,6 +65,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
@ViewChild('fileWorkloadComponent') private _workloadComponent: FileWorkloadComponent;
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
+ @ViewChild(FileWorkloadComponent) public fileWorkloadComponent: FileWorkloadComponent;
constructor(
public readonly appStateService: AppStateService,
@@ -111,11 +112,11 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
}
get canSwitchToRedactedView() {
- return !this.permissionsService.fileRequiresReanalysis() && !this.fileData.fileStatus.isExcluded;
+ return this.fileData && !this.permissionsService.fileRequiresReanalysis() && !this.fileData.fileStatus.isExcluded;
}
get canSwitchToDeltaView() {
- return this.fileData?.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.fileStatus.isExcluded;
+ return this.fileData && this.fileData.redactionChangeLog?.redactionLogEntry?.length > 0 && !this.fileData.fileStatus.isExcluded;
}
get displayData() {
@@ -233,13 +234,26 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
}
handleAnnotationSelected(annotationIds: string[]) {
- this.selectedAnnotations = annotationIds.map((annotationId) => this.annotations.find((annotationWrapper) => annotationWrapper.id === annotationId));
+ this.selectedAnnotations = annotationIds
+ .map((annotationId) => this.annotations.find((annotationWrapper) => annotationWrapper.id === annotationId))
+ .filter((ann) => ann !== undefined);
+ if (this.selectedAnnotations.length > 1) {
+ this._workloadComponent.multiSelectActive = true;
+ }
this._workloadComponent.scrollToSelectedAnnotation();
this._changeDetectorRef.detectChanges();
}
- selectAnnotation(annotation: AnnotationWrapper) {
- this._viewerComponent.selectAnnotation(annotation);
+ selectAnnotations(annotations?: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
+ if (!!annotations) {
+ this._viewerComponent.selectAnnotations(annotations);
+ } else {
+ this._viewerComponent.deselectAllAnnotations();
+ }
+ }
+
+ deselectAnnotations(annotations: AnnotationWrapper[]) {
+ this._viewerComponent.deselectAnnotations(annotations);
}
selectPage(pageNumber: number) {
@@ -298,7 +312,9 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
viewerPageChanged($event: any) {
if (typeof $event === 'number') {
this._scrollViews();
- this.shouldDeselectAnnotationsOnPageChange = true;
+ if (!this.fileWorkloadComponent.multiSelectActive) {
+ this.shouldDeselectAnnotationsOnPageChange = true;
+ }
// Add current page in URL query params
this._router.navigate([], {
@@ -321,7 +337,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
setTimeout(() => {
this.selectPage(parseInt(pageNumber, 10));
this._scrollViews();
- }, 500);
+ }, 600);
}
}
@@ -381,7 +397,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy {
/* Close fullscreen */
closeFullScreen() {
- if (document.exitFullscreen) {
+ if (!!document.fullscreenElement && document.exitFullscreen) {
document.exitFullscreen();
}
}
diff --git a/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts b/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts
index 58d95d3ec..954108a56 100644
--- a/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts
+++ b/apps/red-ui/src/app/modules/projects/services/annotation-actions.service.ts
@@ -65,7 +65,10 @@ export class AnnotationActionsService {
public markAsFalsePositive($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter
) {
annotations.forEach((annotation) => {
- this._markAsFalsePositive($event, annotation, this._getFalsePositiveText(annotation), annotationsChanged);
+ const permissions = AnnotationPermissions.forUser(this._permissionsService.isManagerAndOwner(), this._permissionsService.currentUser, annotation);
+ const value = permissions.canMarkTextOnlyAsFalsePositive ? annotation.value : this._getFalsePositiveText(annotation);
+
+ this._markAsFalsePositive($event, annotation, value, annotationsChanged);
});
}
@@ -85,12 +88,6 @@ export class AnnotationActionsService {
});
}
- public markTextOnlyAsFalsePositive($event: MouseEvent, annotations: AnnotationWrapper[], annotationsChanged: EventEmitter) {
- annotations.forEach((annotation) => {
- this._markAsFalsePositive($event, annotation, annotation.value, annotationsChanged);
- });
- }
-
private _processObsAndEmit(obs: Observable, annotation: AnnotationWrapper, annotationsChanged: EventEmitter) {
obs.subscribe(
() => {
@@ -216,21 +213,10 @@ export class AnnotationActionsService {
});
}
- const canMarkTextOnlyAsFalsePositive = annotationPermissions.reduce((acc, next) => acc && next.permissions.canMarkTextOnlyAsFalsePositive, true);
- if (canMarkTextOnlyAsFalsePositive) {
- availableActions.push({
- type: 'actionButton',
- img: '/assets/icons/general/thumb-down.svg',
- title: this._translateService.instant('annotation-actions.remove-annotation.false-positive'),
- onClick: () => {
- this._ngZone.run(() => {
- this.markTextOnlyAsFalsePositive(null, annotations, annotationsChanged);
- });
- }
- });
- }
-
- const canMarkAsFalsePositive = annotationPermissions.reduce((acc, next) => acc && next.permissions.canMarkAsFalsePositive, true);
+ const canMarkAsFalsePositive = annotationPermissions.reduce(
+ (acc, next) => acc && (next.permissions.canMarkAsFalsePositive || next.permissions.canMarkTextOnlyAsFalsePositive),
+ true
+ );
if (canMarkAsFalsePositive) {
availableActions.push({
type: 'actionButton',
diff --git a/apps/red-ui/src/assets/i18n/en.json b/apps/red-ui/src/assets/i18n/en.json
index fb7999a45..608cecfcd 100644
--- a/apps/red-ui/src/assets/i18n/en.json
+++ b/apps/red-ui/src/assets/i18n/en.json
@@ -422,6 +422,7 @@
"label": "Accept Recommendation"
},
"suggest-remove-annotation": "Remove or Suggest to remove this entry",
+ "suggest-remove-annotations": "Remove or Suggest to remove selected entries",
"reject-suggestion": "Reject Suggestion",
"remove-annotation": {
"suggest-remove-from-dict": "Suggest to remove from dictionary",
diff --git a/apps/red-ui/src/assets/styles/red-button.scss b/apps/red-ui/src/assets/styles/red-button.scss
index d63f68b46..bae06f57f 100644
--- a/apps/red-ui/src/assets/styles/red-button.scss
+++ b/apps/red-ui/src/assets/styles/red-button.scss
@@ -102,6 +102,10 @@ redaction-icon-button {
&[aria-expanded='true'] {
button {
background: rgba($primary, 0.1);
+
+ &.primary {
+ background: $red-2;
+ }
}
}
}
diff --git a/apps/red-ui/src/assets/styles/red-components.scss b/apps/red-ui/src/assets/styles/red-components.scss
index dc71cde87..6e9a3f051 100644
--- a/apps/red-ui/src/assets/styles/red-components.scss
+++ b/apps/red-ui/src/assets/styles/red-components.scss
@@ -194,6 +194,10 @@
&.always-visible {
opacity: 1;
}
+
+ &.primary-bg {
+ background-color: transparent;
+ }
}
.selection-icon {