add readonly to state service

This commit is contained in:
Dan Percic 2022-02-02 14:27:42 +02:00
parent bf7f3f1923
commit dc4d18433f
8 changed files with 167 additions and 158 deletions

View File

@ -8,7 +8,6 @@ import { UserService } from '@services/user.service';
import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { MultiSelectService } from '../../services/multi-select.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { firstValueFrom } from 'rxjs';
export const AnnotationButtonTypes = {
dark: 'dark',
@ -34,9 +33,9 @@ export class AnnotationActionsComponent implements OnChanges {
constructor(
private readonly _userService: UserService,
readonly multiSelectService: MultiSelectService,
private readonly _state: FilePreviewStateService,
private readonly _permissionsService: PermissionsService,
readonly annotationActionsService: AnnotationActionsService,
private readonly _screenStateService: FilePreviewStateService,
readonly annotationReferencesService: AnnotationReferencesService,
) {}
@ -115,7 +114,7 @@ export class AnnotationActionsComponent implements OnChanges {
}
private async _setPermissions() {
const dossier = await firstValueFrom(this._screenStateService.dossier$);
const dossier = await this._state.dossier;
this.annotationPermissions = AnnotationPermissions.forUser(
this._permissionsService.isApprover(dossier),
this._userService.currentUser,

View File

@ -4,6 +4,8 @@ import { FilterService, HelpModeService, IqserEventTarget } from '@iqser/common-
import { MultiSelectService } from '../../services/multi-select.service';
import { AnnotationReferencesService } from '../../services/annotation-references.service';
import { ViewModeService } from '../../services/view-mode.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { firstValueFrom } from 'rxjs';
@Component({
selector: 'redaction-annotations-list',
@ -16,7 +18,6 @@ export class AnnotationsListComponent implements OnChanges {
@Input() selectedAnnotations: AnnotationWrapper[];
@Input() annotationActionsTemplate: TemplateRef<unknown>;
@Input() activeViewerPage: number;
@Input() canMultiSelect = true;
@Output() readonly pagesPanelActive = new EventEmitter<boolean>();
@Output() readonly selectAnnotations = new EventEmitter<AnnotationWrapper[]>();
@ -28,6 +29,7 @@ export class AnnotationsListComponent implements OnChanges {
readonly helpModeService: HelpModeService,
readonly annotationReferencesService: AnnotationReferencesService,
private readonly _filterService: FilterService,
private readonly _state: FilePreviewStateService,
) {}
ngOnChanges(changes: SimpleChanges): void {
@ -36,7 +38,7 @@ export class AnnotationsListComponent implements OnChanges {
}
}
annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): void {
async annotationClicked(annotation: AnnotationWrapper, $event: MouseEvent): Promise<void> {
if (($event?.target as IqserEventTarget)?.localName === 'input') {
return;
}
@ -50,15 +52,16 @@ export class AnnotationsListComponent implements OnChanges {
if (this.isSelected(annotation.annotationId)) {
this.deselectAnnotations.emit([annotation]);
} else {
if (this.canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this.selectedAnnotations.length > 0) {
const canMultiSelect = await firstValueFrom(this._state.isWritable$);
if (canMultiSelect && ($event?.ctrlKey || $event?.metaKey) && this.selectedAnnotations.length > 0) {
this.multiSelectService.activate();
}
this.selectAnnotations.emit([annotation]);
}
}
referenceClicked(annotation: AnnotationWrapper): void {
this.annotationClicked(annotation, null);
async referenceClicked(annotation: AnnotationWrapper): Promise<void> {
await this.annotationClicked(annotation, null);
if (this._filterService.filtersEnabled('primaryFilters')) {
this._filterService.toggleFilter('primaryFilters', annotation.superType, true);
}

View File

@ -14,7 +14,7 @@
<div>
<div
(click)="multiSelectService.activate()"
*ngIf="!isReadOnly && (multiSelectInactive$ | async)"
*ngIf="(multiSelectInactive$ | async) && state.isWritable$ | async"
class="all-caps-label primary pointer"
iqserHelpMode="bulk-select-annotations"
translate="file-preview.tabs.annotations.select"
@ -31,7 +31,7 @@
</ng-template>
<div class="right-content">
<div *ngIf="isReadOnly" class="justify-center read-only d-flex">
<div *ngIf="state.isReadonly$ | async" class="justify-center read-only d-flex">
<div class="flex-center">
<mat-icon class="primary-white" svgIcon="red:read-only"></mat-icon>
<span class="read-only-text" translate="readonly"></span>
@ -53,7 +53,7 @@
*ngIf="selectedAnnotations?.length > 0"
[alwaysVisible]="true"
[annotations]="selectedAnnotations"
[canPerformAnnotationActions]="!isReadOnly"
[canPerformAnnotationActions]="state.isWritable$ | async"
[viewer]="viewer"
buttonType="primary"
tooltipPosition="above"
@ -67,7 +67,7 @@
></iqser-circle-button>
</div>
<div [class.lower-height]="(multiSelectActive$ | async) || isReadOnly" class="annotations-wrapper">
<div [class.lower-height]="(multiSelectActive$ | async) || (state.isReadonly$ | async)" class="annotations-wrapper">
<div
#quickNavigation
(keydown)="preventKeyDefault($event)"
@ -200,7 +200,6 @@
[activeViewerPage]="activeViewerPage"
[annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="(displayedAnnotations$ | async)?.get(activeViewerPage)"
[canMultiSelect]="!isReadOnly"
[selectedAnnotations]="selectedAnnotations"
iqserHelpMode="workload-annotations-list"
></redaction-annotations-list>

View File

@ -33,6 +33,7 @@ import { ExcludedPagesService } from '../../services/excluded-pages.service';
import { MultiSelectService } from '../../services/multi-select.service';
import { DocumentInfoService } from '../../services/document-info.service';
import { SkippedService } from '../../services/skipped.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
const COMMAND_KEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'Escape'];
const ALL_HOTKEY_ARRAY = ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown'];
@ -75,6 +76,7 @@ export class FileWorkloadComponent {
readonly multiSelectService: MultiSelectService,
readonly documentInfoService: DocumentInfoService,
readonly skippedService: SkippedService,
readonly state: FilePreviewStateService,
private readonly _permissionsService: PermissionsService,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _filterService: FilterService,
@ -95,10 +97,6 @@ export class FileWorkloadComponent {
return this.displayedAnnotations.get(this.activeViewerPage);
}
get isReadOnly(): boolean {
return !this._permissionsService.canPerformAnnotationActions(this.file);
}
get currentPageIsExcluded(): boolean {
return this.file?.excludedPages?.includes(this.activeViewerPage);
}
@ -252,8 +250,8 @@ export class FileWorkloadComponent {
this.selectPage.emit(1);
}
scrollQuickNavLast(): void {
this.selectPage.emit(this.file.numberOfPages);
scrollQuickNavLast(): Promise<void> {
return this.state.file.then(file => this.selectPage.emit(file.numberOfPages));
}
pageSelectedByClick($event: number): void {

View File

@ -1,135 +1,137 @@
<ng-container *ngIf="stateService.dossier$ | async as dossier">
<ng-container *ngIf="stateService.file$ | async as file">
<section [class.fullscreen]="fullScreen">
<div class="page-header">
<div class="flex flex-1">
<redaction-view-switch (switchView)="switchView($event)"></redaction-view-switch>
<section *ngIf="stateService.file$ | async as file" [class.fullscreen]="fullScreen">
<div class="page-header">
<div class="flex flex-1">
<redaction-view-switch (switchView)="switchView($event)"></redaction-view-switch>
</div>
<div class="flex-1 actions-container">
<div
*ngIf="file.isProcessing"
class="spinning-icon mr-16"
matTooltip="{{ 'file-status.processing' | translate }}"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>
<div class="flex-1 actions-container">
<div
*ngIf="file.isProcessing"
class="spinning-icon mr-16"
matTooltip="{{ 'file-status.processing' | translate }}"
matTooltipPosition="above"
>
<mat-icon svgIcon="red:reanalyse"></mat-icon>
</div>
<redaction-user-management></redaction-user-management>
<ng-container *ngIf="permissionsService.isApprover(dossier) && !!file.lastReviewer">
<div class="vertical-line"></div>
<div class="all-caps-label mr-16 ml-8" translate="file-preview.last-reviewer"></div>
<redaction-initials-avatar [user]="file.lastReviewer" [withName]="true"></redaction-initials-avatar>
</ng-container>
<redaction-user-management></redaction-user-management>
<ng-container *ngIf="permissionsService.isApprover(dossier) && !!file.lastReviewer">
<div class="vertical-line"></div>
<div class="all-caps-label mr-16 ml-8" translate="file-preview.last-reviewer"></div>
<redaction-initials-avatar [user]="file.lastReviewer" [withName]="true"></redaction-initials-avatar>
</ng-container>
<redaction-file-actions [file]="file" type="file-preview"></redaction-file-actions>
<div class="vertical-line"></div>
<iqser-circle-button
(action)="toggleFullScreen()"
[icon]="fullScreen ? 'red:exit-fullscreen' : 'red:fullscreen'"
[tooltip]="'file-preview.fullscreen' | translate"
class="ml-2"
tooltipPosition="below"
></iqser-circle-button>
<redaction-file-actions [file]="file" type="file-preview"></redaction-file-actions>
<!-- Dev Mode Features-->
<iqser-circle-button
(action)="downloadOriginalFile(file)"
*ngIf="userPreferenceService.areDevFeaturesEnabled"
[tooltip]="'file-preview.download-original-file' | translate"
[type]="circleButtonTypes.primary"
class="ml-8"
icon="iqser:download"
tooltipPosition="below"
></iqser-circle-button>
<!-- End Dev Mode Features-->
<iqser-circle-button
(action)="toggleFullScreen()"
[icon]="fullScreen ? 'red:exit-fullscreen' : 'red:fullscreen'"
[tooltip]="'file-preview.fullscreen' | translate"
class="ml-2"
tooltipPosition="below"
></iqser-circle-button>
<iqser-circle-button
(action)="closeFullScreen()"
*ngIf="!fullScreen"
[routerLink]="dossier.routerLink"
[tooltip]="'common.close' | translate"
class="ml-8"
icon="iqser:close"
tooltipPosition="below"
></iqser-circle-button>
</div>
<!-- Dev Mode Features-->
<iqser-circle-button
(action)="downloadOriginalFile(file)"
*ngIf="userPreferenceService.areDevFeaturesEnabled"
[tooltip]="'file-preview.download-original-file' | translate"
[type]="circleButtonTypes.primary"
class="ml-8"
icon="iqser:download"
tooltipPosition="below"
></iqser-circle-button>
<!-- End Dev Mode Features-->
<iqser-circle-button
(action)="closeFullScreen()"
*ngIf="!fullScreen"
[routerLink]="dossier.routerLink"
[tooltip]="'common.close' | translate"
class="ml-8"
icon="iqser:close"
tooltipPosition="below"
></iqser-circle-button>
</div>
</div>
<div class="overlay-shadow"></div>
<div class="content-inner">
<div class="content-container">
<redaction-pdf-viewer
(annotationSelected)="handleAnnotationSelected($event)"
(annotationsChanged)="annotationsChangedByReviewAction($event)"
(keyUp)="handleKeyEvent($event); handleArrowEvent($event)"
(manualAnnotationRequested)="openManualAnnotationDialog($event)"
(pageChanged)="viewerPageChanged($event)"
(viewerReady)="viewerReady($event)"
*ngIf="displayPdfViewer"
[annotations]="visibleAnnotations"
[canPerformActions]="canPerformAnnotationActions$ | async"
[class.hidden]="!ready"
[dossier]="dossier"
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
></redaction-pdf-viewer>
</div>
<div class="overlay-shadow"></div>
<div class="right-container">
<iqser-empty-state
*ngIf="file.excluded && (documentInfoService.hidden$ | async) && excludedPagesService.hidden$ | async"
[horizontalPadding]="40"
[text]="'file-preview.tabs.is-excluded' | translate"
icon="red:needs-work"
></iqser-empty-state>
<div class="content-inner">
<div class="content-container">
<redaction-pdf-viewer
(annotationSelected)="handleAnnotationSelected($event)"
(annotationsChanged)="annotationsChangedByReviewAction($event)"
(keyUp)="handleKeyEvent($event); handleArrowEvent($event)"
(manualAnnotationRequested)="openManualAnnotationDialog($event)"
(pageChanged)="viewerPageChanged($event)"
(viewerReady)="viewerReady($event)"
*ngIf="displayPdfViewer"
[annotations]="visibleAnnotations"
[canPerformActions]="canPerformAnnotationActions$ | async"
[class.hidden]="!ready"
[dossier]="dossier"
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
></redaction-pdf-viewer>
</div>
<redaction-document-info *ngIf="documentInfoService.shown$ | async"></redaction-document-info>
<div class="right-container">
<iqser-empty-state
*ngIf="file.excluded && (documentInfoService.hidden$ | async) && excludedPagesService.hidden$ | async"
[horizontalPadding]="40"
[text]="'file-preview.tabs.is-excluded' | translate"
icon="red:needs-work"
></iqser-empty-state>
<redaction-document-info *ngIf="documentInfoService.shown$ | async"></redaction-document-info>
<redaction-file-workload
#fileWorkloadComponent
(annotationsChanged)="annotationsChangedByReviewAction($event)"
(deselectAnnotations)="deselectAnnotations($event)"
(selectAnnotations)="selectAnnotations($event)"
(selectPage)="selectPage($event)"
*ngIf="!file.excluded"
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
[activeViewerPage]="activeViewerPage"
[annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="visibleAnnotations"
[dialogRef]="dialogRef"
[file]="file"
[selectedAnnotations]="selectedAnnotations"
[viewer]="activeViewer"
></redaction-file-workload>
</div>
<redaction-file-workload
#fileWorkloadComponent
(annotationsChanged)="annotationsChangedByReviewAction($event)"
(deselectAnnotations)="deselectAnnotations($event)"
(selectAnnotations)="selectAnnotations($event)"
(selectPage)="selectPage($event)"
*ngIf="!file.excluded"
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
[activeViewerPage]="activeViewerPage"
[annotationActionsTemplate]="annotationActionsTemplate"
[annotations]="visibleAnnotations"
[dialogRef]="dialogRef"
[file]="file"
[selectedAnnotations]="selectedAnnotations"
[viewer]="activeViewer"
></redaction-file-workload>
</div>
</section>
<ng-template #annotationActionsTemplate let-annotation="annotation">
<redaction-annotation-actions
(annotationsChanged)="annotationsChangedByReviewAction($event)"
[annotations]="[annotation]"
[canPerformAnnotationActions]="canPerformAnnotationActions$ | async"
[viewer]="activeViewer"
></redaction-annotation-actions>
</ng-template>
<ng-template #annotationFilterTemplate let-filter="filter">
<redaction-type-filter
*ngIf="filter.topLevelFilter"
[dossierTemplateId]="dossier.dossierTemplateId"
[filter]="filter"
></redaction-type-filter>
<ng-container *ngIf="!filter.topLevelFilter">
<redaction-dictionary-annotation-icon [dictionaryKey]="filter.id" [dossierTemplateId]="dossier.dossierTemplateId">
</redaction-dictionary-annotation-icon>
{{ filter.label | humanize: false }}
</ng-container>
</ng-template>
</ng-container>
</div>
</section>
</ng-container>
<ng-template #annotationActionsTemplate let-annotation="annotation">
<redaction-annotation-actions
(annotationsChanged)="annotationsChangedByReviewAction($event)"
[annotations]="[annotation]"
[canPerformAnnotationActions]="canPerformAnnotationActions$ | async"
[viewer]="activeViewer"
></redaction-annotation-actions>
</ng-template>
<ng-template #annotationFilterTemplate let-filter="filter">
<redaction-type-filter
*ngIf="filter.topLevelFilter"
[dossierTemplateId]="stateService.dossierTemplateId"
[filter]="filter"
></redaction-type-filter>
<ng-container *ngIf="!filter.topLevelFilter">
<redaction-dictionary-annotation-icon
[dictionaryKey]="filter.id"
[dossierTemplateId]="stateService.dossierTemplateId"
></redaction-dictionary-annotation-icon>
{{ filter.label | humanize: false }}
</ng-container>
</ng-template>

View File

@ -5,40 +5,50 @@ import { Dossier, File } from '@red/domain';
import { DossiersService } from '../../../../../services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router';
import { FilesMapService } from '../../../../../services/entity-services/files-map.service';
import { PermissionsService } from '../../../../../services/permissions.service';
import { boolFactory } from '@iqser/common-ui';
@Injectable()
export class FilePreviewStateService {
readonly fileData$: Observable<FileDataModel>;
readonly file$: Observable<File>;
readonly dossier$: Observable<Dossier>;
readonly isReadonly$: Observable<boolean>;
readonly isWritable$: Observable<boolean>;
readonly dossierId: string;
readonly dossierTemplateId: string;
readonly fileId: string;
private readonly _fileData$ = new BehaviorSubject<FileDataModel>(undefined);
readonly #fileData$ = new BehaviorSubject<FileDataModel>(undefined);
constructor(
private readonly _dossiersService: DossiersService,
private readonly _filesMapService: FilesMapService,
dossiersService: DossiersService,
filesMapService: FilesMapService,
permissionsService: PermissionsService,
activatedRoute: ActivatedRoute,
) {
this.dossierId = activatedRoute.snapshot.paramMap.get('dossierId');
this.dossierTemplateId = this._dossiersService.find(this.dossierId).dossierTemplateId;
this.dossier$ = _dossiersService.getEntityChanged$(this.dossierId);
this.dossierTemplateId = dossiersService.find(this.dossierId).dossierTemplateId;
this.dossier$ = dossiersService.getEntityChanged$(this.dossierId);
this.fileId = activatedRoute.snapshot.paramMap.get('fileId');
this.fileData$ = this._fileData$.asObservable();
this.file$ = _filesMapService.watch$(this.dossierId, this.fileId);
this.fileData$ = this.#fileData$.asObservable();
this.file$ = filesMapService.watch$(this.dossierId, this.fileId);
[this.isReadonly$, this.isWritable$] = boolFactory(this.file$, file => !permissionsService.canPerformAnnotationActions(file));
}
get fileData(): FileDataModel {
return this._fileData$.value;
return this.#fileData$.value;
}
set fileData(fileDataModel: FileDataModel) {
this._fileData$.next(fileDataModel);
this.#fileData$.next(fileDataModel);
}
get file(): Promise<File> {
return firstValueFrom(this.file$);
}
get dossier(): Promise<Dossier> {
return firstValueFrom(this.dossier$);
}
}

View File

@ -1,32 +1,30 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, Observable } from 'rxjs';
import { shareDistinctLast } from '@iqser/common-ui';
import { map } from 'rxjs/operators';
import { boolFactory } from '@iqser/common-ui';
@Injectable()
export class MultiSelectService {
readonly active$: Observable<boolean>;
readonly inactive$: Observable<boolean>;
private readonly _active$ = new BehaviorSubject(false);
readonly #active$ = new BehaviorSubject(false);
constructor() {
this.active$ = this._active$.asObservable().pipe(shareDistinctLast());
this.inactive$ = this.active$.pipe(map(value => !value));
[this.active$, this.inactive$] = boolFactory(this.#active$.asObservable());
}
get isActive() {
return this._active$.value;
return this.#active$.value;
}
activate() {
this._active$.next(true);
this.#active$.next(true);
}
deactivate() {
this._active$.next(false);
this.#active$.next(false);
}
toggle() {
this._active$.next(!this._active$.value);
this.#active$.next(!this.#active$.value);
}
}

@ -1 +1 @@
Subproject commit 31b60ff117a8d8ecb9617be3b0c9e1822d437034
Subproject commit b808e4661e56aa93baca96c57f910443437112d4