diff --git a/apps/red-ui/src/app/common/file-actions/file-actions.component.html b/apps/red-ui/src/app/common/file-actions/file-actions.component.html
index 09531137e..926aec0c3 100644
--- a/apps/red-ui/src/app/common/file-actions/file-actions.component.html
+++ b/apps/red-ui/src/app/common/file-actions/file-actions.component.html
@@ -1,80 +1,96 @@
-
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/apps/red-ui/src/app/common/file-actions/file-actions.component.scss b/apps/red-ui/src/app/common/file-actions/file-actions.component.scss
index e69de29bb..36fe9c897 100644
--- a/apps/red-ui/src/app/common/file-actions/file-actions.component.scss
+++ b/apps/red-ui/src/app/common/file-actions/file-actions.component.scss
@@ -0,0 +1,3 @@
+.file-actions {
+ display: flex;
+}
diff --git a/apps/red-ui/src/app/common/file-actions/file-actions.component.ts b/apps/red-ui/src/app/common/file-actions/file-actions.component.ts
index ff6c849e0..985c79cc7 100644
--- a/apps/red-ui/src/app/common/file-actions/file-actions.component.ts
+++ b/apps/red-ui/src/app/common/file-actions/file-actions.component.ts
@@ -1,27 +1,51 @@
-import { Component, Input, OnInit, Output, EventEmitter } from '@angular/core';
+import { AfterViewInit, Component, EventEmitter, Input, OnInit, Output, ViewChild } from '@angular/core';
import { PermissionsService } from '../service/permissions.service';
import { FileStatusWrapper } from '../../screens/file/model/file-status.wrapper';
import { DialogService } from '../../dialogs/dialog.service';
import { AppStateService } from '../../state/app-state.service';
import { FileActionService } from '../../screens/file/service/file-action.service';
+import { MatTooltip } from '@angular/material/tooltip';
@Component({
selector: 'redaction-file-actions',
templateUrl: './file-actions.component.html',
styleUrls: ['./file-actions.component.scss']
})
-export class FileActionsComponent implements OnInit {
+export class FileActionsComponent implements OnInit, AfterViewInit {
@Input() fileStatus: FileStatusWrapper;
@Output() actionPerformed = new EventEmitter
();
+ @ViewChild('reanalyseTooltip') private _reanalyseTooltip: MatTooltip;
+
+ screen: 'file-preview' | 'project-overview';
constructor(
public readonly permissionsService: PermissionsService,
+ public readonly appStateService: AppStateService,
private readonly _dialogService: DialogService,
- private readonly _appStateService: AppStateService,
private readonly _fileActionService: FileActionService
) {}
- ngOnInit(): void {}
+ ngOnInit(): void {
+ if (!this.fileStatus) {
+ this.fileStatus = this.appStateService.activeFile;
+ this.screen = 'file-preview';
+ this.appStateService.fileChanged.subscribe((fileStatus: FileStatusWrapper) => {
+ if (fileStatus.fileId === this.fileStatus?.fileId) {
+ this.fileStatus = this.appStateService.activeFile;
+ }
+ });
+ } else {
+ this.screen = 'project-overview';
+ }
+ }
+
+ ngAfterViewInit(): void {
+ setTimeout(() => {
+ if (this._reanalyseTooltip) {
+ this._reanalyseTooltip.show();
+ }
+ }, 1000);
+ }
openDeleteFileDialog($event: MouseEvent, fileStatusWrapper: FileStatusWrapper) {
this._dialogService.openDeleteFileDialog($event, fileStatusWrapper.projectId, fileStatusWrapper.fileId, () => {
@@ -31,7 +55,7 @@ export class FileActionsComponent implements OnInit {
downloadFileRedactionReport($event: MouseEvent, file: FileStatusWrapper) {
$event.stopPropagation();
- this._appStateService.downloadFileRedactionReport(file);
+ this.appStateService.downloadFileRedactionReport(file);
}
assignReviewer($event: MouseEvent, file: FileStatusWrapper) {
@@ -67,8 +91,8 @@ export class FileActionsComponent implements OnInit {
});
}
- public reloadProjects(action: string) {
- this._appStateService.getFiles().then(() => {
+ reloadProjects(action: string) {
+ this.appStateService.getFiles().then(() => {
this.actionPerformed.emit(action);
});
}
diff --git a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts
index 7cb7d2d02..8942631b6 100644
--- a/apps/red-ui/src/app/common/filter/utils/filter-utils.ts
+++ b/apps/red-ui/src/app/common/filter/utils/filter-utils.ts
@@ -1,6 +1,6 @@
import { FilterModel } from '../model/filter.model';
import { FileStatusWrapper } from '../../../screens/file/model/file-status.wrapper';
-import { ProjectWrapper } from '../../../state/app-state.service';
+import { ProjectWrapper } from '../../../state/model/project.wrapper';
export const RedactionFilterSorter = {
hint: 1,
diff --git a/apps/red-ui/src/app/common/service/permissions.service.ts b/apps/red-ui/src/app/common/service/permissions.service.ts
index 829dd6207..13208cbbf 100644
--- a/apps/red-ui/src/app/common/service/permissions.service.ts
+++ b/apps/red-ui/src/app/common/service/permissions.service.ts
@@ -19,22 +19,34 @@ export class PermissionsService {
}
isReviewerOrOwner(fileStatus?: FileStatusWrapper, user?: User) {
- return this.isActiveFileDocumentReviewer() || this.isManagerAndOwner();
+ return this.isFileReviewer(fileStatus) || this.isManagerAndOwner();
+ }
+
+ fileRequiresReanalysis(fileStatus?: FileStatusWrapper) {
+ if (!fileStatus) {
+ fileStatus = this._appStateService.activeFile;
+ }
+ return (
+ ((fileStatus.status === 'UNASSIGNED' || fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_APPROVAL') &&
+ (fileStatus.dictionaryVersion !== this._appStateService.dictionaryVersion ||
+ fileStatus.rulesVersion !== this._appStateService.rulesVersion ||
+ fileStatus.hasUnappliedSuggestions)) ||
+ fileStatus.isError
+ );
}
canReanalyseFile(fileStatus?: FileStatusWrapper) {
if (!fileStatus) {
fileStatus = this._appStateService.activeFile;
}
- // can reanalyse file if error, has requests not up to date with dictionary and is owner or reviewer
- return (
- ((!fileStatus.isApproved && this._appStateService.fileNotUpToDateWithDictionary(fileStatus)) || fileStatus.isError || fileStatus.hasRequests) &&
- (this.isManagerAndOwner() || this.isActiveFileDocumentReviewer())
- );
+ return this.fileRequiresReanalysis(fileStatus) && this.isReviewerOrOwner(fileStatus);
}
- isActiveFileDocumentReviewer() {
- return this._appStateService.activeFile?.currentReviewer === this._userService.userId;
+ isFileReviewer(fileStatus?: FileStatusWrapper) {
+ if (!fileStatus) {
+ fileStatus = this._appStateService.activeFile;
+ }
+ return fileStatus.currentReviewer === this._userService.userId;
}
canDeleteFile(fileStatus?: FileStatusWrapper) {
@@ -74,7 +86,7 @@ export class PermissionsService {
user = this._userService.user;
}
if (!project) {
- project = this._appStateService.activeProject;
+ project = this._appStateService.activeProject.project;
}
return user.isManager && project.ownerId === user.id;
}
@@ -84,7 +96,7 @@ export class PermissionsService {
user = this._userService.user;
}
if (!project) {
- project = this._appStateService.activeProject;
+ project = this._appStateService.activeProject.project;
}
return project.memberIds?.includes(user.id);
}
@@ -93,14 +105,14 @@ export class PermissionsService {
if (!fileStatus) {
fileStatus = this._appStateService.activeFile;
}
- return (fileStatus.status === 'UNDER_APPROVAL' || fileStatus.status === 'UNDER_REVIEW') && this._userService.userId === fileStatus.currentReviewer;
+ return (fileStatus.status === 'UNDER_APPROVAL' || fileStatus.status === 'UNDER_REVIEW') && this.isFileReviewer(fileStatus);
}
public canOpenFile(fileStatus: FileStatusWrapper) {
if (!fileStatus) {
fileStatus = this._appStateService.activeFile;
}
- return !fileStatus.isError && !fileStatus.isProcessing;
+ return !fileStatus.isError && !fileStatus.isProcessing && this.isReviewerOrOwner(fileStatus);
}
canShowRedactionReportDownloadBtn(fileStatus?: FileStatusWrapper) {
@@ -114,7 +126,21 @@ export class PermissionsService {
return this.isProjectMember() && !fileStatus.isError && !fileStatus.isApprovedOrUnderApproval;
}
- canUndoApproval(fileStatus: any) {}
+ canUndoApproval(fileStatus: FileStatusWrapper) {
+ if (!fileStatus) {
+ fileStatus = this._appStateService.activeFile;
+ }
+ return fileStatus.status === 'APPROVED' && this.isManagerAndOwner();
+ }
- canUndoUnderApproval(fileStatus: any) {}
+ canUndoUnderApproval(fileStatus: any) {
+ if (!fileStatus) {
+ fileStatus = this._appStateService.activeFile;
+ }
+ return fileStatus.status === 'UNDER_APPROVAL' && this.isManagerAndOwner();
+ }
+
+ canMarkPagesAsViewed() {
+ return this.isReviewerOrOwner();
+ }
}
diff --git a/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts b/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts
index d1b57c1c4..24d2008f4 100644
--- a/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts
+++ b/apps/red-ui/src/app/dialogs/manual-redaction-dialog/manual-annotation-dialog.component.ts
@@ -9,6 +9,7 @@ import { UserService } from '../../user/user.service';
import { ManualRedactionEntryWrapper } from '../../screens/file/model/manual-redaction-entry.wrapper';
import { ManualAnnotationService } from '../../screens/file/service/manual-annotation.service';
import { ManualAnnotationResponse } from '../../screens/file/model/manual-annotation-response';
+import { PermissionsService } from '../../common/service/permissions.service';
@Component({
selector: 'redaction-manual-annotation-dialog',
@@ -30,12 +31,13 @@ export class ManualAnnotationDialogComponent implements OnInit {
private readonly _notificationService: NotificationService,
private readonly _translateService: TranslateService,
private readonly _manualAnnotationService: ManualAnnotationService,
+ private readonly _permissionsService: PermissionsService,
public dialogRef: MatDialogRef,
@Inject(MAT_DIALOG_DATA) public manualRedactionEntryWrapper: ManualRedactionEntryWrapper
) {}
async ngOnInit() {
- this.isDocumentAdmin = this._appStateService.isActiveProjectOwnerAndManager;
+ this.isDocumentAdmin = this._permissionsService.isManagerAndOwner();
const commentField = this.isDocumentAdmin ? [null] : [null, Validators.required];
this.isDictionaryRequest = this.manualRedactionEntryWrapper.type === 'DICTIONARY';
@@ -60,18 +62,14 @@ export class ManualAnnotationDialogComponent implements OnInit {
handleAddRedaction() {
this._enhanceManualRedaction(this.manualRedactionEntryWrapper.manualRedactionEntry);
- this._manualAnnotationService
- .addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry)
- .subscribe(
- (response) => {
- this.dialogRef.close(
- new ManualAnnotationResponse(this.manualRedactionEntryWrapper, response)
- );
- },
- () => {
- this.dialogRef.close();
- }
- );
+ this._manualAnnotationService.addAnnotation(this.manualRedactionEntryWrapper.manualRedactionEntry).subscribe(
+ (response) => {
+ this.dialogRef.close(new ManualAnnotationResponse(this.manualRedactionEntryWrapper, response));
+ },
+ () => {
+ this.dialogRef.close();
+ }
+ );
}
get title() {
@@ -80,8 +78,7 @@ export class ManualAnnotationDialogComponent implements OnInit {
private _enhanceManualRedaction(addRedactionRequest: AddRedactionRequest) {
addRedactionRequest.type = this.redactionForm.get('dictionary').value;
- addRedactionRequest.addToDictionary =
- this.manualRedactionEntryWrapper.type === 'DICTIONARY';
+ addRedactionRequest.addToDictionary = this.manualRedactionEntryWrapper.type === 'DICTIONARY';
addRedactionRequest.reason = this.redactionForm.get('reason').value;
// todo fix this in backend
if (!addRedactionRequest.reason) {
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
index a72e578af..2aad64b0a 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.html
@@ -12,14 +12,14 @@
-
+
{{
appStateService.activeFile.filename
}}
-
+
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss
index 50d6755fc..2058d434a 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.scss
@@ -20,6 +20,7 @@ redaction-pdf-viewer {
display: flex;
justify-content: center;
align-items: center;
+ gap: 4px;
}
.right-fixed-container {
diff --git a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
index 380a988b3..0cce2eb53 100644
--- a/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
+++ b/apps/red-ui/src/app/screens/file/file-preview-screen/file-preview-screen.component.ts
@@ -40,7 +40,6 @@ export class FilePreviewScreenComponent implements OnInit {
@ViewChild(PdfViewerComponent) private _viewerComponent: PdfViewerComponent;
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
@ViewChild('quickNavigation') private _quickNavigationElement: ElementRef;
- @ViewChild('reanalyseTooltip') private _reanalyseTooltip: MatTooltip;
fileData: FileDataModel;
fileId: string;
@@ -98,17 +97,19 @@ export class FilePreviewScreenComponent implements OnInit {
}
get canNotSwitchToRedactedView() {
- return this.appStateService.fileNotUpToDateWithDictionary() || this.fileData?.entriesToAdd?.length > 0;
+ return this.permissionsService.fileRequiresReanalysis();
}
ngOnInit(): void {
- this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions();
- this._loadFileData().subscribe(() => {});
+ this._loadFileData().subscribe(() => {
+ this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions();
+ });
this.appStateService.fileReanalysed.subscribe((fileStatus: FileStatusWrapper) => {
if (fileStatus.fileId === this.fileId) {
this._loadFileData().subscribe(() => {
this.viewReady = true;
this.loadingMessage = null;
+ this.canPerformAnnotationActions = this.permissionsService.canPerformAnnotationActions();
this._cleanupAndRedrawManualAnnotations();
});
}
@@ -353,9 +354,6 @@ export class FilePreviewScreenComponent implements OnInit {
viewerReady($event: WebViewerInstance) {
this.instance = $event;
this.viewReady = true;
- if (this._reanalyseTooltip) {
- this._reanalyseTooltip.show();
- }
this._cleanupAndRedrawManualAnnotations();
}
diff --git a/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts b/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts
index 9789d529d..e99c415bc 100644
--- a/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts
+++ b/apps/red-ui/src/app/screens/file/model/file-status.wrapper.ts
@@ -11,9 +11,8 @@ export class FileStatusWrapper {
return this.fileStatus.added;
}
- // TODO use this for suggestions
- get allManualRedactionsApplied() {
- return this.fileStatus.allManualRedactionsApplied;
+ get hasUnappliedSuggestions() {
+ return !this.fileStatus.allManualRedactionsApplied;
}
get currentReviewer() {
diff --git a/apps/red-ui/src/app/screens/file/page-indicator/page-indicator.component.ts b/apps/red-ui/src/app/screens/file/page-indicator/page-indicator.component.ts
index 4ece4d33c..c34b856fe 100644
--- a/apps/red-ui/src/app/screens/file/page-indicator/page-indicator.component.ts
+++ b/apps/red-ui/src/app/screens/file/page-indicator/page-indicator.component.ts
@@ -1,15 +1,7 @@
-import {
- Component,
- EventEmitter,
- HostListener,
- Input,
- OnChanges,
- OnInit,
- Output,
- SimpleChanges
-} from '@angular/core';
+import { Component, EventEmitter, HostListener, Input, OnChanges, OnInit, Output, SimpleChanges } from '@angular/core';
import { ViewedPages, ViewedPagesControllerService } from '@redaction/red-ui-http';
import { AppStateService } from '../../../state/app-state.service';
+import { PermissionsService } from '../../../common/service/permissions.service';
@Component({
selector: 'redaction-page-indicator',
@@ -28,11 +20,12 @@ export class PageIndicatorComponent implements OnChanges, OnInit {
constructor(
private readonly _viewedPagesControllerService: ViewedPagesControllerService,
- private readonly _appStateService: AppStateService
+ private readonly _appStateService: AppStateService,
+ private readonly _permissionService: PermissionsService
) {}
ngOnInit(): void {
- this.canMarkPagesAsViewed = this._appStateService.canMarkPagesAsViewedForActiveFile;
+ this.canMarkPagesAsViewed = this._permissionService.canMarkPagesAsViewed();
}
ngOnChanges(changes: SimpleChanges): void {
@@ -62,26 +55,16 @@ export class PageIndicatorComponent implements OnChanges, OnInit {
private _markPageRead() {
this._viewedPagesControllerService
- .addPage(
- { page: this.number },
- this._appStateService.activeProjectId,
- this._appStateService.activeFileId
- )
+ .addPage({ page: this.number }, this._appStateService.activeProjectId, this._appStateService.activeFileId)
.subscribe(() => {
this.viewedPages?.pages?.push(this.number);
});
}
private _markPageUnread() {
- this._viewedPagesControllerService
- .removePage(
- this._appStateService.activeProjectId,
- this._appStateService.activeFileId,
- this.number
- )
- .subscribe(() => {
- this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
- });
+ this._viewedPagesControllerService.removePage(this._appStateService.activeProjectId, this._appStateService.activeFileId, this.number).subscribe(() => {
+ this.viewedPages?.pages?.splice(this.viewedPages?.pages?.indexOf(this.number), 1);
+ });
}
// @HostListener('window:keydown', ['$event'])
diff --git a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
index 0f65cf2a5..c0442132b 100644
--- a/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
+++ b/apps/red-ui/src/app/screens/file/pdf-viewer/pdf-viewer.component.ts
@@ -1,16 +1,4 @@
-import {
- AfterViewInit,
- Component,
- ElementRef,
- EventEmitter,
- Input,
- NgZone,
- OnChanges,
- OnInit,
- Output,
- SimpleChanges,
- ViewChild
-} from '@angular/core';
+import { AfterViewInit, Component, ElementRef, EventEmitter, Input, NgZone, OnChanges, OnInit, Output, SimpleChanges, ViewChild } from '@angular/core';
import { AppConfigKey, AppConfigService } from '../../../app-config/app-config.service';
import { ManualRedactionEntry, Rectangle } from '@redaction/red-ui-http';
import WebViewer, { WebViewerInstance } from '@pdftron/webviewer';
@@ -171,36 +159,20 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
type: 'actionButton',
dataElement: 'add-dictionary',
img: '/assets/icons/general/add-dictionary.svg',
- title: this._translateService.instant(
- this._manualAnnotationService.getTitle('DICTIONARY')
- ),
+ title: this._translateService.instant(this._manualAnnotationService.getTitle('DICTIONARY')),
onClick: () => {
const mre = this._getManualRedactionEntry();
- this.manualAnnotationRequested.emit(
- new ManualRedactionEntryWrapper(
- this.instance.docViewer.getSelectedTextQuads(),
- mre,
- 'DICTIONARY'
- )
- );
+ this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'DICTIONARY'));
}
});
this.instance.textPopup.add(
{
type: 'actionButton',
dataElement: 'add-redaction',
img: '/assets/icons/general/add-redaction.svg',
- title: this._translateService.instant(
- this._manualAnnotationService.getTitle('REDACTION')
- ),
+ title: this._translateService.instant(this._manualAnnotationService.getTitle('REDACTION')),
onClick: () => {
const mre = this._getManualRedactionEntry();
- this.manualAnnotationRequested.emit(
- new ManualRedactionEntryWrapper(
- this.instance.docViewer.getSelectedTextQuads(),
- mre,
- 'REDACTION'
- )
- );
+ this.manualAnnotationRequested.emit(new ManualRedactionEntryWrapper(this.instance.docViewer.getSelectedTextQuads(), mre, 'REDACTION'));
}
});
this._handleCustomActions();
@@ -292,14 +264,18 @@ export class PdfViewerComponent implements OnInit, AfterViewInit, OnChanges {
}
private _restoreState(viewerState: ViewerState, instance: WebViewerInstance) {
- if (this._viewerState) {
+ if (viewerState) {
instance.docViewer.setCurrentPage(viewerState.pageNumber);
instance.setLayoutMode(viewerState.layoutMode);
const instanceDisplayMode = instance.docViewer.getDisplayModeManager().getDisplayMode();
instanceDisplayMode.mode = viewerState.displayMode;
instance.docViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
// Synchronize zoom - needs to be done before scrolling
- instance.docViewer.zoomTo(viewerState.zoom);
+ if (viewerState.zoom === 0) {
+ this.instance.setFitMode('FitPage');
+ } else {
+ instance.docViewer.zoomTo(viewerState.zoom);
+ }
const viewerScrollElement = instance.docViewer.getScrollViewElement();
viewerScrollElement.scrollTo(viewerState.scrollLeft, viewerState.scrollTop);
diff --git a/apps/red-ui/src/app/screens/file/service/file-download.service.ts b/apps/red-ui/src/app/screens/file/service/file-download.service.ts
index 69b6b0e6f..d65e149e2 100644
--- a/apps/red-ui/src/app/screens/file/service/file-download.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/file-download.service.ts
@@ -4,11 +4,13 @@ import { map, tap } from 'rxjs/operators';
import {
FileUploadControllerService,
ManualRedactionControllerService,
- RedactionLogControllerService
+ RedactionLogControllerService,
+ ViewedPagesControllerService
} from '@redaction/red-ui-http';
import { FileType } from '../model/file-type';
import { FileDataModel } from '../model/file-data.model';
import { AppStateService } from '../../../state/app-state.service';
+import { PermissionsService } from '../../../common/service/permissions.service';
@Injectable({
providedIn: 'root'
@@ -16,80 +18,69 @@ import { AppStateService } from '../../../state/app-state.service';
export class FileDownloadService {
constructor(
private readonly _appStateService: AppStateService,
+ private readonly _permissionsService: PermissionsService,
private readonly _fileUploadControllerService: FileUploadControllerService,
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
- private readonly _redactionLogControllerService: RedactionLogControllerService
+ private readonly _redactionLogControllerService: RedactionLogControllerService,
+ private readonly _viewedPagesControllerService: ViewedPagesControllerService
) {}
public loadActiveFileManualAnnotations() {
- return this._manualRedactionControllerService.getManualRedaction(
- this._appStateService.activeProjectId,
- this._appStateService.activeFileId
- );
+ return this._manualRedactionControllerService.getManualRedaction(this._appStateService.activeProjectId, this._appStateService.activeFileId);
}
public loadActiveFileData(): Observable {
const annotatedObs = this.loadFile('ANNOTATED', this._appStateService.activeFileId);
const redactedObs = this.loadFile('REDACTED', this._appStateService.activeFileId);
- const reactionLogObs = this._redactionLogControllerService.getRedactionLog(
- this._appStateService.activeFileId
- );
+ const reactionLogObs = this._redactionLogControllerService.getRedactionLog(this._appStateService.activeFileId);
const manualRedactionsObs = this._manualRedactionControllerService.getManualRedaction(
this._appStateService.activeProjectId,
this._appStateService.activeFileId
);
- const viewedPagesObs = this._appStateService.getViewedPagesForActiveFile();
+ const viewedPagesObs = this.getViewedPagesForActiveFile();
- return forkJoin([
- annotatedObs,
- redactedObs,
- reactionLogObs,
- manualRedactionsObs,
- viewedPagesObs
- ]).pipe(map((data) => new FileDataModel(this._appStateService.activeFile, ...data)));
+ return forkJoin([annotatedObs, redactedObs, reactionLogObs, manualRedactionsObs, viewedPagesObs]).pipe(
+ map((data) => new FileDataModel(this._appStateService.activeFile, ...data))
+ );
}
- loadFile(
- fileType: FileType | string,
- fileId: string,
- saveTo: (fileData) => void = () => null,
- fetch: () => any = () => null
- ): Observable {
+ getViewedPagesForActiveFile() {
+ if (this._permissionsService.canMarkPagesAsViewed()) {
+ return this._viewedPagesControllerService.getViewedPages(this._appStateService.activeProjectId, this._appStateService.activeFileId);
+ }
+ return of({ pages: [] });
+ }
+
+ loadFile(fileType: FileType | string, fileId: string, saveTo: (fileData) => void = () => null, fetch: () => any = () => null): Observable {
let fileObs$: Observable;
switch (fileType) {
case FileType.ANNOTATED:
fileObs$ = fetch()
? of(fetch())
- : this._fileUploadControllerService
- .downloadAnnotatedFile(fileId, true, 'body')
- .pipe(
- tap((data) => {
- saveTo(data);
- })
- );
+ : this._fileUploadControllerService.downloadAnnotatedFile(fileId, true, 'body').pipe(
+ tap((data) => {
+ saveTo(data);
+ })
+ );
break;
case FileType.REDACTED:
fileObs$ = fetch()
? of(fetch())
- : this._fileUploadControllerService
- .downloadRedactedFile(fileId, true, 'body')
- .pipe(
- tap((data) => {
- saveTo(data);
- })
- );
+ : this._fileUploadControllerService.downloadRedactedFile(fileId, true, 'body').pipe(
+ tap((data) => {
+ saveTo(data);
+ })
+ );
break;
case FileType.ORIGINAL:
default:
fileObs$ = fetch()
? of(fetch())
- : this._fileUploadControllerService
- .downloadOriginalFile(fileId, true, 'body')
- .pipe(
- tap((data) => {
- saveTo(data);
- })
- );
+ : this._fileUploadControllerService.downloadOriginalFile(fileId, true, 'body').pipe(
+ tap((data) => {
+ saveTo(data);
+ })
+ );
break;
}
return fileObs$;
diff --git a/apps/red-ui/src/app/screens/file/service/manual-annotation.service.ts b/apps/red-ui/src/app/screens/file/service/manual-annotation.service.ts
index 5deeed715..4fc86e54f 100644
--- a/apps/red-ui/src/app/screens/file/service/manual-annotation.service.ts
+++ b/apps/red-ui/src/app/screens/file/service/manual-annotation.service.ts
@@ -1,15 +1,12 @@
import { Injectable } from '@angular/core';
import { AppStateService } from '../../../state/app-state.service';
-import {
- DictionaryControllerService,
- ManualRedactionControllerService,
- ManualRedactionEntry
-} from '@redaction/red-ui-http';
+import { DictionaryControllerService, ManualRedactionControllerService, ManualRedactionEntry } from '@redaction/red-ui-http';
import { AnnotationWrapper } from '../model/annotation.wrapper';
import { NotificationService, NotificationType } from '../../../notification/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { tap } from 'rxjs/operators';
import { UserService } from '../../../user/user.service';
+import { PermissionsService } from '../../../common/service/permissions.service';
@Injectable({
providedIn: 'root'
@@ -21,7 +18,8 @@ export class ManualAnnotationService {
private readonly _translateService: TranslateService,
private readonly _notificationService: NotificationService,
private readonly _manualRedactionControllerService: ManualRedactionControllerService,
- private readonly _dictionaryControllerService: DictionaryControllerService
+ private readonly _dictionaryControllerService: DictionaryControllerService,
+ private readonly _permissionsService: PermissionsService
) {}
// Comments
@@ -49,7 +47,7 @@ export class ManualAnnotationService {
// /manualRedaction/redaction/add
// /manualRedaction/request/add
addAnnotation(manualRedactionEntry: ManualRedactionEntry) {
- if (this._appStateService.isActiveProjectOwnerAndManager) {
+ if (this._permissionsService.isManagerAndOwner()) {
return this._makeRedaction(manualRedactionEntry);
} else {
return this._makeRedactionRequest(manualRedactionEntry);
@@ -81,19 +79,12 @@ export class ManualAnnotationService {
undoRequest(annotationWrapper: AnnotationWrapper) {
return this._manualRedactionControllerService
- .undo(
- this._appStateService.activeProjectId,
- this._appStateService.activeFileId,
- annotationWrapper.id
- )
+ .undo(this._appStateService.activeProjectId, this._appStateService.activeFileId, annotationWrapper.id)
.pipe(
tap(
() => this._notify('manual-annotation.undo-request.success'),
() => {
- this._notify(
- 'manual-annotation.undo-request.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.undo-request.error', NotificationType.ERROR);
}
)
);
@@ -103,39 +94,25 @@ export class ManualAnnotationService {
// /manualRedaction/decline/remove
// /manualRedaction/undo
declineOrRemoveRequest(annotationWrapper: AnnotationWrapper) {
- if (this._appStateService.isActiveProjectOwnerAndManager) {
+ if (this._permissionsService.isManagerAndOwner()) {
return this._manualRedactionControllerService
- .declineRequest(
- this._appStateService.activeProjectId,
- this._appStateService.activeFileId,
- annotationWrapper.id
- )
+ .declineRequest(this._appStateService.activeProjectId, this._appStateService.activeFileId, annotationWrapper.id)
.pipe(
tap(
() => this._notify('manual-annotation.undo-request.success'),
() => {
- this._notify(
- 'manual-annotation.undo-request.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.undo-request.error', NotificationType.ERROR);
}
)
);
} else {
return this._manualRedactionControllerService
- .undo(
- this._appStateService.activeProjectId,
- this._appStateService.activeFileId,
- annotationWrapper.id
- )
+ .undo(this._appStateService.activeProjectId, this._appStateService.activeFileId, annotationWrapper.id)
.pipe(
tap(
() => this._notify('manual-annotation.undo-request.success'),
() => {
- this._notify(
- 'manual-annotation.undo-request.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.undo-request.error', NotificationType.ERROR);
}
)
);
@@ -145,11 +122,8 @@ export class ManualAnnotationService {
// this wraps
// /manualRedaction/redaction/remove/
// /manualRedaction/request/remove/
- removeOrSuggestRemoveAnnotation(
- annotationWrapper: AnnotationWrapper,
- removeFromDictionary: boolean = false
- ) {
- if (this._appStateService.isActiveProjectOwnerAndManager) {
+ removeOrSuggestRemoveAnnotation(annotationWrapper: AnnotationWrapper, removeFromDictionary: boolean = false) {
+ if (this._permissionsService.isManagerAndOwner()) {
return this._manualRedactionControllerService
.removeRedaction(
{
@@ -164,10 +138,7 @@ export class ManualAnnotationService {
tap(
() => this._notify('manual-annotation.remove-redaction-request.success'),
() => {
- this._notify(
- 'manual-annotation.remove-redaction-request.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.remove-redaction-request.error', NotificationType.ERROR);
}
)
);
@@ -186,10 +157,7 @@ export class ManualAnnotationService {
tap(
() => this._notify('manual-annotation.remove-redaction-request.success'),
() => {
- this._notify(
- 'manual-annotation.remove-redaction-request.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.remove-redaction-request.error', NotificationType.ERROR);
}
)
);
@@ -198,19 +166,12 @@ export class ManualAnnotationService {
private _makeRedactionRequest(manualRedactionEntry: ManualRedactionEntry) {
return this._manualRedactionControllerService
- .requestAddRedaction(
- manualRedactionEntry,
- this._appStateService.activeProject.project.projectId,
- this._appStateService.activeFile.fileId
- )
+ .requestAddRedaction(manualRedactionEntry, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId)
.pipe(
tap(
() => this._notify('manual-annotation.redaction-request.success'),
() => {
- this._notify(
- 'manual-annotation.redaction-request.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.redaction-request.error', NotificationType.ERROR);
}
)
);
@@ -218,34 +179,23 @@ export class ManualAnnotationService {
private _makeRedaction(manualRedactionEntry: ManualRedactionEntry) {
return this._manualRedactionControllerService
- .addRedaction(
- manualRedactionEntry,
- this._appStateService.activeProject.project.projectId,
- this._appStateService.activeFile.fileId
- )
+ .addRedaction(manualRedactionEntry, this._appStateService.activeProject.project.projectId, this._appStateService.activeFile.fileId)
.pipe(
tap(
() => this._notify('manual-annotation.redaction-add.success'),
() => {
- this._notify(
- 'manual-annotation.redaction-add.error',
- NotificationType.ERROR
- );
+ this._notify('manual-annotation.redaction-add.error', NotificationType.ERROR);
}
)
);
}
private _notify(key: string, type: NotificationType = NotificationType.SUCCESS) {
- this._notificationService.showToastNotification(
- this._translateService.instant(key),
- null,
- type
- );
+ this._notificationService.showToastNotification(this._translateService.instant(key), null, type);
}
getTitle(type: 'DICTIONARY' | 'REDACTION') {
- if (this._appStateService.isActiveProjectOwnerAndManager) {
+ if (this._permissionsService.isManagerAndOwner()) {
if (type === 'DICTIONARY') {
return 'manual-redaction.dialog.header.dictionary';
} else {
diff --git a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts
index c2e9410ad..af966198f 100644
--- a/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts
+++ b/apps/red-ui/src/app/screens/project-listing-screen/project-listing-screen.component.ts
@@ -55,7 +55,7 @@ export class ProjectListingScreenComponent implements OnInit {
public ngOnInit(): void {
this.appStateService.reset();
this._calculateData();
- this.appStateService.fileStatusChanged.subscribe(() => {
+ this.appStateService.fileChanged.subscribe(() => {
this._calculateData();
});
}
diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts
index 95a3c318a..312fc2e7e 100644
--- a/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts
+++ b/apps/red-ui/src/app/screens/project-overview-screen/project-details/project-details.component.ts
@@ -29,7 +29,7 @@ export class ProjectDetailsComponent implements OnInit {
ngOnInit(): void {
this.calculateChartConfig();
- this.appStateService.fileStatusChanged.subscribe((event) => {
+ this.appStateService.fileChanged.subscribe((event) => {
this.calculateChartConfig();
});
}
diff --git a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html
index 10a22c9eb..3c9f78d16 100644
--- a/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html
+++ b/apps/red-ui/src/app/screens/project-overview-screen/project-overview-screen.component.html
@@ -122,11 +122,7 @@
{{ fileStatus.filename }}
-
+
@@ -163,7 +159,11 @@
>
-
+
{
+ this.appStateService.fileChanged.subscribe(() => {
this.calculateData();
});
}
@@ -71,43 +70,45 @@ export class ProjectOverviewScreenComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this._fileDropOverlayService.initFileDropHandling();
this.calculateData();
- this._displayNewRuleToast();
+ this._displayOutdatedToast();
}
ngOnDestroy(): void {
this._fileDropOverlayService.cleanupFileDropHandling();
}
- private _displayNewRuleToast() {
- // @ts-ignore
- if (!this.appStateService.activeProject.files.filter((file) => this.appStateService.fileNotUpToDateWithDictionary(file)).length) {
- return;
- }
-
- this._notificationService.showToastNotification(
- `${this._translateService.instant('project-overview.new-rule.toast.message-project')} ${this._translateService.instant(
- 'project-overview.new-rule.label'
- )}`,
- null,
- NotificationType.WARNING,
- {
- disableTimeOut: true,
- positionClass: 'toast-top-left',
- actions: [
- {
- title: this._translateService.instant('project-overview.new-rule.toast.actions.reanalyse-all'),
- action: () =>
- this.appStateService
- .reanalyzeProject()
- .toPromise()
- .then(() => this.reloadProjects())
- },
- {
- title: this._translateService.instant('project-overview.new-rule.toast.actions.later')
- }
- ]
+ private _displayOutdatedToast() {
+ if (this.permissionsService.isManagerAndOwner()) {
+ // @ts-ignore
+ if (!this.appStateService.activeProject.files.filter((file) => this.permissionsService.fileRequiresReanalysis(file)).length) {
+ return;
}
- );
+
+ this._notificationService.showToastNotification(
+ `${this._translateService.instant('project-overview.new-rule.toast.message-project')} ${this._translateService.instant(
+ 'project-overview.new-rule.label'
+ )}`,
+ null,
+ NotificationType.WARNING,
+ {
+ disableTimeOut: true,
+ positionClass: 'toast-top-left',
+ actions: [
+ {
+ title: this._translateService.instant('project-overview.new-rule.toast.actions.reanalyse-all'),
+ action: () =>
+ this.appStateService
+ .reanalyzeProject()
+ .toPromise()
+ .then(() => this.reloadProjects())
+ },
+ {
+ title: this._translateService.instant('project-overview.new-rule.toast.actions.later')
+ }
+ ]
+ }
+ );
+ }
}
reloadProjects() {
diff --git a/apps/red-ui/src/app/state/app-state.service.ts b/apps/red-ui/src/app/state/app-state.service.ts
index d4fa48a1d..3d71c83ce 100644
--- a/apps/red-ui/src/app/state/app-state.service.ts
+++ b/apps/red-ui/src/app/state/app-state.service.ts
@@ -39,7 +39,7 @@ export interface AppState {
export class AppStateService {
private _appState: AppState;
private _dictionaryData: { [key: string]: TypeValue } = null;
- public fileStatusChanged = new EventEmitter();
+ public fileChanged = new EventEmitter();
public fileReanalysed = new EventEmitter();
constructor(
@@ -52,8 +52,7 @@ export class AppStateService {
private readonly _translateService: TranslateService,
private readonly _dictionaryControllerService: DictionaryControllerService,
private readonly _statusControllerService: StatusControllerService,
- private readonly _versionsControllerService: VersionsControllerService,
- private readonly _viewedPagesControllerService: ViewedPagesControllerService
+ private readonly _versionsControllerService: VersionsControllerService
) {
this._appState = {
projects: [],
@@ -98,10 +97,6 @@ export class AppStateService {
return this._dictionaryData;
}
- getViewedPagesForActiveFile() {
- return this._viewedPagesControllerService.getViewedPages(this.activeProjectId, this.activeFileId);
- }
-
reanalyzeProject(project?: Project) {
if (!project) {
project = this.activeProject.project;
@@ -206,13 +201,12 @@ export class AppStateService {
for (const oldFile of oldFiles) {
if (oldFile.fileId === file.fileId) {
// emit when analysis count changed
- if (oldFile.lastUpdated !== file.lastUpdated) {
- const fileStatusWrapper = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer));
+ const fileStatusWrapper = new FileStatusWrapper(file, this._userService.getNameForId(file.currentReviewer));
+ if (JSON.stringify(oldFile) !== JSON.stringify(fileStatusWrapper)) {
fileStatusChangedEvent.push(fileStatusWrapper);
-
- if (oldFile.lastProcessed !== file.lastProcessed) {
- fileReanalysedEvent.push(fileStatusWrapper);
- }
+ }
+ if (oldFile.lastProcessed !== file.lastProcessed) {
+ fileReanalysedEvent.push(fileStatusWrapper);
}
found = true;
break;
@@ -230,7 +224,7 @@ export class AppStateService {
this._computeStats();
fileReanalysedEvent.forEach((file) => this.fileReanalysed.emit(file));
- fileStatusChangedEvent.forEach((file) => this.fileStatusChanged.emit(file));
+ fileStatusChangedEvent.forEach((file) => this.fileChanged.emit(file));
return files;
}
@@ -429,16 +423,6 @@ export class AppStateService {
}
}
- fileNotUpToDateWithDictionary(fileStatus?: FileStatusWrapper) {
- if (!fileStatus) {
- fileStatus = this.activeFile;
- }
- return (
- (fileStatus.status === 'UNASSIGNED' || fileStatus.status === 'UNDER_REVIEW' || fileStatus.status === 'UNDER_APPROVAL') &&
- (fileStatus.dictionaryVersion !== this.dictionaryVersion || fileStatus.rulesVersion !== this.rulesVersion)
- );
- }
-
async updateDictionaryVersion() {
const result = await this._versionsControllerService.getVersions().toPromise();
this._appState.dictionaryVersion = result.dictionaryVersion;