Merge remote-tracking branch 'origin/master' into updates

This commit is contained in:
Dan Percic 2021-05-17 13:38:27 +03:00
commit 4223423e10
14 changed files with 259 additions and 75 deletions

View File

@ -49,15 +49,15 @@
tabindex="0"
>
<div
(click)="quickScrollFirstEnabled && scrollQuickNavFirst()"
[class.disabled]="!quickScrollFirstEnabled"
(click)="scrollQuickNavFirst()"
[class.disabled]="activeViewerPage === 1"
[matTooltip]="'file-preview.quick-nav.jump-first' | translate"
class="jump"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:nav-first"></mat-icon>
</div>
<div (scroll)="computeQuickNavButtonsState()" class="pages" id="pages">
<div class="pages" id="pages">
<redaction-page-indicator
(pageSelected)="pageSelectedByClick($event)"
*ngFor="let pageNumber of displayedPages"
@ -70,7 +70,7 @@
</div>
<div
(click)="scrollQuickNavLast()"
[class.disabled]="!quickScrollLastEnabled"
[class.disabled]="activeViewerPage === fileData?.fileStatus?.numberOfPages"
[matTooltip]="'file-preview.quick-nav.jump-last' | translate"
class="jump"
matTooltipPosition="above"

View File

@ -83,6 +83,7 @@
&.disabled {
cursor: default;
pointer-events: none;
mat-icon {
opacity: 0.3;

View File

@ -44,8 +44,6 @@ export class FileWorkloadComponent {
@Output() selectPage = new EventEmitter<number>();
@Output() toggleSkipped = new EventEmitter<any>();
@Output() annotationsChanged = new EventEmitter<AnnotationWrapper>();
quickScrollFirstEnabled = false;
quickScrollLastEnabled = false;
displayedPages: number[] = [];
pagesPanelActive = true;
@ViewChild('annotationsElement') private _annotationsElement: ElementRef;
@ -129,20 +127,9 @@ export class FileWorkloadComponent {
filters.secondary
);
this.displayedPages = Object.keys(this.displayedAnnotations).map((key) => Number(key));
this.computeQuickNavButtonsState();
this._changeDetectorRef.markForCheck();
}
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);
}
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent) {
this.pagesPanelActive = false;
if (this.annotationIsSelected(annotation)) {
@ -234,15 +221,11 @@ export class FileWorkloadComponent {
}
scrollQuickNavFirst() {
if (this.displayedPages.length > 0) {
this._scrollQuickNavigationToPage(this.displayedPages[0]);
}
this.selectPage.emit(1);
}
scrollQuickNavLast() {
if (this.displayedPages.length > 0) {
this._scrollQuickNavigationToPage(this.displayedPages[this.displayedPages.length - 1]);
}
this.selectPage.emit(this.fileData.fileStatus.numberOfPages);
}
pageSelectedByClick($event: number) {

View File

@ -35,7 +35,7 @@
<ul *ngIf="!data.removeFromDictionary">
<li *ngFor="let annotation of data.annotationsToRemove">
{{ annotation.value }}
{{ printable(annotation) }}
</li>
</ul>
</div>

View File

@ -2,6 +2,7 @@ import { Component, Inject } from '@angular/core';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { TranslateService } from '@ngx-translate/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { humanize } from '../../../../utils/functions';
export interface RemoveAnnotationsDialogInput {
annotationsToRemove: AnnotationWrapper[];
@ -27,4 +28,14 @@ export class RemoveAnnotationsDialogComponent {
confirm() {
this.dialogRef.close(true);
}
printable(annotation: AnnotationWrapper) {
if (annotation.isImage) {
return this._translateService.instant('remove-annotations-dialog.image-type', {
typeLabel: humanize(annotation.dictionary)
});
} else {
return annotation.value;
}
}
}

View File

@ -37,6 +37,7 @@ 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';
import { DossierDictionaryDialogComponent } from './dialogs/dossier-dictionary-dialog/dossier-dictionary-dialog.component';
import { UserPreferenceControllerService } from '@redaction/red-ui-http';
const screens = [
ProjectListingScreenComponent,
@ -83,7 +84,8 @@ const services = [
ManualAnnotationService,
PdfViewerDataService,
AnnotationDrawService,
AnnotationProcessingService
AnnotationProcessingService,
UserPreferenceControllerService
];
@NgModule({

View File

@ -30,7 +30,11 @@ import { handleFilterDelta, processFilters } from '@shared/components/filter/uti
import { UserPreferenceService } from '@services/user-preference.service';
import { UserService } from '@services/user.service';
import { FormBuilder, FormGroup } from '@angular/forms';
import { FileManagementControllerService, StatusControllerService } from '@redaction/red-ui-http';
import {
FileManagementControllerService,
StatusControllerService,
UserPreferenceControllerService
} from '@redaction/red-ui-http';
import { PdfViewerDataService } from '../../services/pdf-viewer-data.service';
import { download } from '@utils/file-download-utils';
import { ViewMode } from '@models/file/view-mode';
@ -79,6 +83,7 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
readonly permissionsService: PermissionsService,
readonly userPreferenceService: UserPreferenceService,
readonly userService: UserService,
private readonly _userPreferenceControllerService: UserPreferenceControllerService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _activatedRoute: ActivatedRoute,
private readonly _dialogService: ProjectsDialogService,
@ -96,6 +101,16 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
this.reviewerForm = this._formBuilder.group({
reviewer: [this.appStateService.activeFile.currentReviewer]
});
this._loadFileData().subscribe(() => {
this._updateCanPerformActions();
});
document.documentElement.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
this.fullScreen = false;
}
});
}
get singleUsersSelectOptions() {
@ -208,23 +223,18 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
}
ngOnAttach(previousRoute: ActivatedRouteSnapshot) {
this.displayPDFViewer = true;
this.ngOnInit();
this._lastPage = previousRoute.queryParams.page;
this._subscribeToFileUpdates();
}
ngOnInit(): void {
this.displayPDFViewer = true;
document.documentElement.addEventListener('fullscreenchange', () => {
if (!document.fullscreenElement) {
this.fullScreen = false;
}
});
this._loadFileData().subscribe(() => {
this._updateCanPerformActions();
});
const key = 'Project-Recent-' + this.projectId;
this._userPreferenceControllerService
.savePreferences([this.fileId], key)
.toPromise()
.then();
this._subscribeToFileUpdates();
}
@ -348,6 +358,10 @@ export class FilePreviewScreenComponent implements OnInit, OnDestroy, OnAttach,
@HostListener('window:keyup', ['$event'])
handleKeyEvent($event: KeyboardEvent) {
if (this._router.url.indexOf('/file/') < 0) {
return;
}
if (
!ALL_HOTKEY_ARRAY.includes($event.key) ||
this.dialogRef?.getState() === MatDialogState.OPEN

View File

@ -200,6 +200,7 @@
"
[class.disabled]="fileStatus.isExcluded"
[class.pointer]="permissionsService.canOpenFile(fileStatus)"
[class.last-opened]="isLastOpenedFile(fileStatus)"
[routerLink]="fileLink(fileStatus)"
class="table-item"
>

View File

@ -122,3 +122,23 @@ cdk-virtual-scroll-viewport {
.primary-attribute {
padding-top: 6px;
}
.last-opened {
> .selection-column {
padding-left: 6px !important;
border-left: 4px solid $red-1;
}
> div {
animation: red-fading-background 3s 1;
}
}
@keyframes red-fading-background {
0% {
background-color: rgba($red-1, 0.1);
}
100% {
background-color: inherit;
}
}

View File

@ -18,7 +18,7 @@ import {
} from '@shared/components/filter/utils/filter-utils';
import { PermissionsService } from '@services/permissions.service';
import { UserService } from '@services/user.service';
import { FileStatus } from '@redaction/red-ui-http';
import { FileStatus, UserPreferenceControllerService } from '@redaction/red-ui-http';
import { Subscription, timer } from 'rxjs';
import { filter, tap } from 'rxjs/operators';
import { RedactionFilterSorter } from '@utils/sorters/redaction-filter-sorter';
@ -57,6 +57,7 @@ export class ProjectOverviewScreenComponent
private _routerEventsScrollPositionSub: Subscription;
private _fileChangedSub: Subscription;
private _lastScrollPosition: number;
private _lastOpenedFileId = '';
@ViewChild(CdkVirtualScrollViewport) private _scrollBar: CdkVirtualScrollViewport;
@ -75,6 +76,7 @@ export class ProjectOverviewScreenComponent
private readonly _translateService: TranslateService,
private readonly _fileDropOverlayService: FileDropOverlayService,
private readonly _appStateService: AppStateService,
private readonly _userPreferenceControllerService: UserPreferenceControllerService,
protected readonly _injector: Injector
) {
super(_injector);
@ -85,6 +87,10 @@ export class ProjectOverviewScreenComponent
return this._appStateService.activeProject;
}
isLastOpenedFile(fileStatus: FileStatusWrapper): boolean {
return this._lastOpenedFileId === fileStatus.fileId;
}
protected get _filterComponents(): FilterComponent[] {
return [
this._statusFilterComponent,
@ -112,6 +118,12 @@ export class ProjectOverviewScreenComponent
}
ngOnInit(): void {
this._userPreferenceControllerService.getAllUserAttributes().subscribe((attributes) => {
if (attributes === null || attributes === undefined) return;
const key = 'Project-Recent-' + this.activeProject.projectId;
this._lastOpenedFileId = attributes[key][0];
});
this._fileDropOverlayService.initFileDropHandling();
this.calculateData();

View File

@ -1,10 +1,11 @@
import { Component, Input } from '@angular/core';
import { Component, Inject, Input } from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { ProjectWrapper } from '@state/model/project.wrapper';
import { FileStatusWrapper } from '@models/file/file-status.wrapper';
import { FileDownloadService } from '@upload-download/services/file-download.service';
import { NotificationService } from '@services/notification.service';
import { TranslateService } from '@ngx-translate/core';
import { BASE_HREF } from '../../../../../tokens';
export type MenuState = 'OPEN' | 'CLOSED';
@ -21,6 +22,7 @@ export class FileDownloadBtnComponent {
@Input() tooltipClass: string;
constructor(
@Inject(BASE_HREF) private readonly _baseHref: string,
private readonly _permissionsService: PermissionsService,
private readonly _fileDownloadService: FileDownloadService,
private readonly _translateService: TranslateService,
@ -47,7 +49,9 @@ export class FileDownloadBtnComponent {
.downloadFiles(Array.isArray(this.file) ? this.file : [this.file], this.project)
.subscribe(() => {
this._notificationService.showToastNotification(
this._translateService.instant('download-status.queued')
this._translateService.instant('download-status.queued', {
baseUrl: this._baseHref
})
);
});
}

View File

@ -275,7 +275,6 @@
"description": "Beschreibung"
},
"header": "Dossierübersicht",
"no-project": "Angefordertes Dossier: {{projectId}} existiert nicht! <a href='/main/projects'>Zurück zur Dossierliste.</a>",
"bulk": {
"delete": "Dokumente löschen",
"assign": "Prüfer zuweisen",

View File

@ -22,7 +22,7 @@
}
},
"download-status": {
"queued": "Your download has been queued, you can see all your requested downloads here: <a href='/main/downloads'>My Downloads <a/>.",
"queued": "Your download has been queued, you can see all your requested downloads here: <a href='{{baseUrl}}/main/downloads'>My Downloads <a/>.",
"error": {
"generic": "Download failed"
},
@ -231,7 +231,8 @@
"report": {
"action": "Download redaction report"
},
"assign": "Assign Reviewer",
"assign-reviewer": "Assign Reviewer",
"assign-approver": "Assign Approver",
"assign-me": "Assign To Me",
"table-header": {
"title": "{{length}} documents",
@ -281,7 +282,6 @@
"description": "Description"
},
"header": "Dossier Overview",
"no-project": "Requested dossier: {{projectId}} does not exist! <a href='/main/projects'>Back to Dossier Listing. <a/>",
"bulk": {
"delete": "Delete Documents",
"assign": "Assign Reviewer",
@ -349,8 +349,8 @@
"download-original-file": "Download Original File",
"exit-fullscreen": "Exit Full Screen (F)",
"quick-nav": {
"jump-first": "Jump to first annotation",
"jump-last": "Jump to last annotation"
"jump-first": "Jump to first page",
"jump-last": "Jump to last page"
}
},
"annotation-actions": {
@ -376,6 +376,10 @@
"success": "Redaction suggestion approved!",
"error": "Failed to approved redaction: {{error}}"
},
"request-remove": {
"success": "Requested to remove redaction!",
"error": "Failed to request removal of redaction: {{error}}"
},
"assign-reviewer": "Assign Reviewer",
"assign-approver": "Assign Approver",
"assign-me": "Assign To Me",
@ -431,35 +435,167 @@
"accept-recommendation": {
"label": "Accept Recommendation"
},
"assign-reviewer-owner": {
"dialog": {
"single-user": "Reviewer",
"title": "Manage File Reviewer",
"save": "Save",
"cancel": "Cancel"
}
"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",
"suggest-only-here": "Suggest to remove only here",
"remove-from-dict": "Remove from dictionary",
"only-here": "Remove only here",
"false-positive": "False Positive"
},
"assign-approver-owner": {
"dialog": {
"single-user": "Approver",
"title": "Manage File Approver",
"save": "Save",
"cancel": "Cancel"
}
},
"assign-project-owner": {
"dialog": {
"single-user": "Owner",
"multi-user": "Review Team",
"title": "Manage Dossier Team",
"approvers": "Approvers",
"reviewers": "Reviewers",
"save": "Save Changes",
"cancel": "Cancel",
"search": "Search...",
"no-approvers": "No approvers yet.\nSelect from the list below.",
"no-reviewers": "No reviewers yet.\nSelect from the list below.",
"make-approver": "Make Approver"
"remove": "Remove",
"undo": "Undo",
"reject": "Reject",
"hide": "Hide",
"show": "Show"
},
"initials-avatar": {
"unassigned": "Unassigned",
"you": "You"
},
"assign-reviewer-owner": {
"dialog": {
"single-user": "Reviewer",
"title": "Manage File Reviewer",
"save": "Save",
"cancel": "Cancel"
}
},
"assign-approver-owner": {
"dialog": {
"single-user": "Approver",
"title": "Manage File Approver",
"save": "Save",
"cancel": "Cancel"
}
},
"assign-project-owner": {
"dialog": {
"single-user": "Owner",
"multi-user": "Review Team",
"title": "Manage Dossier Team",
"approvers": "Approvers",
"reviewers": "Reviewers",
"save": "Save Changes",
"cancel": "Cancel",
"search": "Search...",
"no-approvers": "No approvers yet.\nSelect from the list below.",
"no-reviewers": "No reviewers yet.\nSelect from the list below.",
"make-approver": "Make Approver"
}
},
"project-member-guard": {
"access-denied": "You are not allowed to access that page."
},
"comments": {
"comment": "{{count}} comment",
"comments": "{{count}} comments",
"add-comment": "Add a comment",
"hide-comments": "Hide",
"cancel": "Cancel"
},
"UNPROCESSED": "Unprocessed",
"REPROCESS": "Processing",
"FULLREPROCESS": "Processing",
"PROCESSING": "Processing",
"OCR_PROCESSING": "OCR Processing",
"ERROR": "Re-processing required",
"UNASSIGNED": "Unassigned",
"UNDER_REVIEW": "Under Review",
"UNDER_APPROVAL": "Under Approval",
"APPROVED": "Approved",
"EXCLUDED": "Excluded",
"by": "by",
"efsa": "EFSA Approval",
"finished": "Finished",
"submitted": "Submitted",
"active": "Active",
"archived": "Archived",
"hint": "Hint",
"skipped": "Skipped",
"redaction": "Redaction",
"comment": "Comment",
"pending-analysis": "Pending Re-Analysis",
"suggestion": "Suggestion for redaction",
"dictionary": "Dictionary",
"type": "Type",
"content": "Reason",
"page": "Page",
"annotation": "Annotation",
"annotations": "Annotations",
"filter": {
"hint": "Hints only",
"redaction": "Redacted",
"suggestion": "Suggested Redaction",
"analysis": "Analysis required",
"none": "No Annotations",
"updated": "Updated",
"image": "Images"
},
"filter-menu": {
"label": "Filter",
"filter-types": "Filter types",
"filter-options": "Filter options",
"with-comments": "Show only annotations with comments"
},
"sorting": {
"recent": "Recent",
"oldest": "Oldest",
"alphabetically": "Alphabetically",
"number-of-pages": "Number of pages",
"number-of-analyses": "Number of analyses",
"custom": "Custom"
},
"readonly-pill": "Read-only",
"group": {
"redactions": "Redaction Dictionaries",
"hints": "Hint Dictionaries"
},
"annotation-type": {
"recommendation": "Recommendation",
"remove-only-here": "Pending removal ( only here )",
"add-dictionary": "Pending add to dictionary",
"remove-dictionary": "Pending remove from dictionary",
"suggestion-add-dictionary": "Suggested dictionary add",
"suggestion-force-redaction": "Suggestion force redaction",
"suggestion-remove-dictionary": "Suggested dictionary removal",
"suggestion-add": "Suggested redaction",
"suggestion-remove": "Suggested redaction removal",
"skipped": "Skipped",
"pending-analysis": "Pending Re-Analysis",
"hint": "Hint",
"redaction": "Redaction",
"manual-redaction": "Manual Redaction",
"declined-suggestion": "Declined Suggestion"
},
"manual-annotation": {
"dialog": {
"header": {
"dictionary": "Add to dictionary",
"redaction": "Redaction",
"force": "Force Redaction",
"request-dictionary": "Request add to dictionary",
"request-redaction": "Request Redaction",
"false-positive": "Set false positive",
"request-false-positive": "Request false positive"
},
"add-redaction": {
"success": "Redaction suggestion added!",
"failed": "Failed to add redaction: {{message}}"
},
"actions": {
"save": "Save"
},
"content": {
"text": "Selected text:",
"rectangle": "Custom Rectangle",
"dictionary": "Dictionary",
"reason": "Reason",
"reason-placeholder": "Select a reason ...",
"legalBasis": "Legal Basis",
"comment": "Comment"
}
},
"approve-request": {
@ -492,6 +628,7 @@
"title": "Remove Redaction",
"question": "Following redactions will be removed only here:"
},
"image-type": "Image: {{typeLabel}}",
"dictionary": "Dictionary",
"value": "Value",
"confirm": "Yes, proceed and remove!",

View File

@ -1,6 +1,6 @@
{
"name": "redaction",
"version": "2.38.0",
"version": "2.40.0",
"private": true,
"license": "MIT",
"scripts": {