use custom share operators, update data streams

This commit is contained in:
Dan Percic 2021-11-06 15:56:12 +02:00
parent 1e14469941
commit e15884f037
10 changed files with 97 additions and 102 deletions

View File

@ -8,8 +8,9 @@ import { FileDownloadService } from '@upload-download/services/file-download.ser
import { TranslateService } from '@ngx-translate/core';
import { SpotlightSearchAction } from '@components/spotlight-search/spotlight-search-action';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { distinctUntilChanged, filter, map, startWith } from 'rxjs/operators';
import { filter, map, startWith } from 'rxjs/operators';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { shareDistinctLast } from '@iqser/common-ui';
interface MenuItem {
readonly name: string;
@ -73,7 +74,7 @@ export class BaseScreenComponent {
filter(isNavigationStart),
map((event: NavigationStart) => event.url),
startWith(this._router.url),
distinctUntilChanged(),
shareDistinctLast(),
);
readonly isDossiersView$ = this._navigationStart$.pipe(map(isDossiersView));
readonly isSearchScreen$ = this._navigationStart$.pipe(map(isSearchScreen));

View File

@ -105,11 +105,11 @@
<div style="overflow: hidden; width: 100%">
<ng-container *ngIf="!excludePages">
<div [attr.anotation-page-header]="activeViewerPage" [class.padding-left-0]="isExcluded" class="page-separator">
<div [attr.anotation-page-header]="activeViewerPage" [class.padding-left-0]="currentPageIsExcluded" class="page-separator">
<span *ngIf="!!activeViewerPage" class="flex-align-items-center">
<iqser-circle-button
(action)="viewExcludePages()"
*ngIf="isExcluded"
*ngIf="currentPageIsExcluded"
[tooltip]="'file-preview.excluded-from-redaction' | translate | capitalize"
icon="red:exclude-pages"
tooltipPosition="above"
@ -152,7 +152,7 @@
[verticalPadding]="40"
icon="iqser:document"
>
<ng-container *ngIf="isExcluded">
<ng-container *ngIf="currentPageIsExcluded">
{{ 'file-preview.tabs.annotations.page-is' | translate }}
<a (click)="viewExcludePages()" class="with-underline" translate="file-preview.excluded-from-redaction"></a
>.

View File

@ -90,7 +90,7 @@ export class FileWorkloadComponent {
return !this._permissionsService.canPerformAnnotationActions();
}
get isExcluded(): boolean {
get currentPageIsExcluded(): boolean {
return this.fileData?.file?.excludedPages?.includes(this.activeViewerPage);
}

View File

@ -1,4 +1,14 @@
import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, SimpleChanges } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
SimpleChanges,
} from '@angular/core';
import { AppStateService } from '@state/app-state.service';
import { PermissionsService } from '@services/permissions.service';
import { ConfigService } from '@services/config.service';
@ -11,6 +21,7 @@ import { AutoUnsubscribe } from '@iqser/common-ui';
selector: 'redaction-page-indicator',
templateUrl: './page-indicator.component.html',
styleUrls: ['./page-indicator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges, OnInit, OnDestroy {
@Input() active: boolean;
@ -62,12 +73,12 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges
}
}
toggleReadState() {
async toggleReadState() {
if (this.canMarkPagesAsViewed) {
if (this.read) {
this._markPageUnread();
await this._markPageUnread();
} else {
this._markPageRead();
await this._markPageRead();
}
}
}
@ -82,9 +93,9 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges
}
if (this.active && !this.read) {
this.pageReadTimeout = window.setTimeout(() => {
this.pageReadTimeout = window.setTimeout(async () => {
if (this.active && !this.read) {
this._markPageRead();
await this._markPageRead();
}
}, this._configService.values.AUTO_READ_TIME * 1000);
}
@ -107,26 +118,24 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnChanges
// }
// }
private _markPageRead() {
this.addSubscription = this._viewedPagesService
private async _markPageRead() {
await this._viewedPagesService
.addPage({ page: this.number }, this._dossiersService.activeDossierId, this._appStateService.activeFileId)
.subscribe(() => {
if (this.activePage) {
this.activePage.hasChanges = false;
} else {
this.viewedPages?.push({ page: this.number, fileId: this._appStateService.activeFileId });
}
});
.toPromise();
if (this.activePage) {
this.activePage.hasChanges = false;
} else {
this.viewedPages?.push({ page: this.number, fileId: this._appStateService.activeFileId });
}
}
private _markPageUnread() {
this.addSubscription = this._viewedPagesService
private async _markPageUnread() {
await this._viewedPagesService
.removePage(this._dossiersService.activeDossierId, this._appStateService.activeFileId, this.number)
.subscribe(() => {
this.viewedPages?.splice(
this.viewedPages?.findIndex(p => p.page === this.number),
1,
);
});
.toPromise();
this.viewedPages?.splice(
this.viewedPages?.findIndex(p => p.page === this.number),
1,
);
}
}

View File

@ -1,4 +1,4 @@
<section *ngIf="appStateService.activeFile" [class.fullscreen]="fullScreen">
<section *ngIf="appStateService.activeFile as file" [class.fullscreen]="fullScreen">
<div class="page-header">
<div class="flex flex-1">
<div
@ -30,8 +30,8 @@
</div>
<div *ngIf="dossiersService.activeDossier$ | async as dossier" class="flex-1 actions-container">
<ng-container *ngIf="!appStateService.activeFile.excluded">
<ng-container *ngIf="!appStateService.activeFile.isProcessing">
<ng-container *ngIf="!file.excluded">
<ng-container *ngIf="!file.isProcessing">
<iqser-status-bar [configs]="statusBarConfig" [small]="true"></iqser-status-bar>
<div class="all-caps-label mr-16 ml-8">
@ -142,7 +142,7 @@
[annotations]="annotations"
[canPerformActions]="canPerformAnnotationActions"
[fileData]="displayData"
[file]="appStateService.activeFile"
[file]="file"
[multiSelectActive]="multiSelectActive"
[shouldDeselectAnnotationsOnPageChange]="shouldDeselectAnnotationsOnPageChange"
></redaction-pdf-viewer>
@ -150,7 +150,7 @@
<div class="right-container">
<iqser-empty-state
*ngIf="appStateService.activeFile.excluded && !viewDocumentInfo && !excludePages"
*ngIf="file.excluded && !viewDocumentInfo && !excludePages"
[horizontalPadding]="40"
[text]="'file-preview.tabs.is-excluded' | translate"
icon="red:needs-work"
@ -170,7 +170,7 @@
(selectAnnotations)="selectAnnotations($event)"
(selectPage)="selectPage($event)"
(toggleSkipped)="toggleSkipped($event)"
*ngIf="!appStateService.activeFile.excluded"
*ngIf="!file.excluded"
[(shouldDeselectAnnotationsOnPageChange)]="shouldDeselectAnnotationsOnPageChange"
[activeViewerPage]="activeViewerPage"
[annotationActionsTemplate]="annotationActionsTemplate"

View File

@ -8,7 +8,6 @@ import {
CircleButtonTypes,
Debounce,
FilterService,
INestedFilter,
List,
LoadingService,
OnAttach,
@ -24,7 +23,7 @@ import { AnnotationData, FileDataModel } from '@models/file/file-data.model';
import { FileActionService } from '../../shared/services/file-action.service';
import { AnnotationDrawService } from '../../services/annotation-draw.service';
import { AnnotationProcessingService } from '../../services/annotation-processing.service';
import { Dossier, File, FileStatus, User, ViewMode } from '@red/domain';
import { Dossier, FileStatus, User, ViewMode } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
import { timer } from 'rxjs';
import { UserPreferenceService } from '@services/user-preference.service';
@ -42,6 +41,7 @@ import { FileActionsComponent } from '../../shared/components/file-actions/file-
import { FilesService } from '@services/entity-services/files.service';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { FileManagementService } from '../../shared/services/file-management.service';
import { filter, switchMapTo, tap } from 'rxjs/operators';
import Annotation = Core.Annotations.Annotation;
const ALL_HOTKEY_ARRAY = ['Escape', 'F', 'f'];
@ -78,7 +78,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
read: TemplateRef,
static: true,
})
private readonly _filterTemplate: TemplateRef<INestedFilter>;
private readonly _filterTemplate: TemplateRef<unknown>;
constructor(
readonly appStateService: AppStateService,
@ -311,6 +311,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
handleAnnotationSelected(annotationIds: string[]) {
// TODO: use includes() here
this.selectedAnnotations = annotationIds
.map(id => this.annotations.find(annotationWrapper => annotationWrapper.id === id))
.filter(ann => ann !== undefined);
@ -318,7 +319,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._workloadComponent.multiSelectActive = true;
}
this._workloadComponent.scrollToSelectedAnnotation();
this._changeDetectorRef.detectChanges();
this._changeDetectorRef.markForCheck();
}
selectAnnotations(annotations?: AnnotationWrapper[] | { annotations: AnnotationWrapper[]; multiSelect: boolean }) {
@ -396,7 +397,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return;
}
this._changeDetectorRef.detectChanges();
this._changeDetectorRef.markForCheck();
}
viewerPageChanged($event: any) {
@ -416,13 +417,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
};
this._router.navigate([], extras).then();
this._changeDetectorRef.detectChanges();
this._changeDetectorRef.markForCheck();
}
async viewerReady($event: WebViewerInstance) {
this._instance = $event;
await this._stampExcludedPages();
this._cleanupAndRedrawManualAnnotations();
this._cleanupAndRedrawManualAnnotations$();
this._updateCanPerformActions();
// Go to initial page from query params
@ -464,7 +465,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
case 'exclude-pages':
await this.appStateService.reloadActiveDossierFiles();
await this._loadFileData(true);
this._cleanupAndRedrawManualAnnotations();
this._cleanupAndRedrawManualAnnotations$();
await this._stampExcludedPages();
this._loadingService.stop();
return;
@ -521,12 +522,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
this._scrollViews();
}
downloadOriginalFile() {
this.addSubscription = this._fileManagementService
async downloadOriginalFile() {
const data = await this._fileManagementService
.downloadOriginalFile(this.fileData.file.dossierId, this.fileId, 'response', true, this.fileData.file.cacheIdentifier)
.subscribe(data => {
download(data, this.fileData.file.filename);
});
.toPromise();
download(data, this.fileData.file.filename);
}
toggleSkipped($event) {
@ -540,8 +540,8 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
private _setHiddenPropertyToNewAnnotations(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
newAnnotations.map((newAnnotation: AnnotationWrapper) => {
const oldAnnotation = oldAnnotations.find((a: AnnotationWrapper) => a.annotationId === newAnnotation.annotationId);
newAnnotations.map(newAnnotation => {
const oldAnnotation = oldAnnotations.find(a => a.annotationId === newAnnotation.annotationId);
if (oldAnnotation) {
newAnnotation.hidden = oldAnnotation.hidden;
}
@ -574,20 +574,20 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
await this._doStampExcludedPages(this.fileData.file.excludedPages);
this._instance.Core.documentViewer.refreshAll();
this._instance.Core.documentViewer.updateView([this.activeViewerPage], this.activeViewerPage);
this._changeDetectorRef.detectChanges();
this._changeDetectorRef.markForCheck();
}
private _subscribeToFileUpdates(): void {
this.addSubscription = timer(0, 10000).subscribe(async () => this.appStateService.reloadActiveFile());
this.addSubscription = this.appStateService.fileReanalysed$.subscribe(async (file: File) => {
if (file.fileId === this.fileId) {
this.addSubscription = timer(0, 10000).pipe(switchMapTo(this.appStateService.reloadActiveFile())).subscribe();
this.addSubscription = this.appStateService.fileReanalysed$
.pipe(filter(file => file.fileId === this.fileId))
.subscribe(async () => {
await this._loadFileData(!this._reloadFileOnReanalysis);
this._reloadFileOnReanalysis = false;
this._loadingService.stop();
this._updateCanPerformActions();
this._cleanupAndRedrawManualAnnotations();
}
});
this._cleanupAndRedrawManualAnnotations$();
});
}
private _updateCanPerformActions() {
@ -627,16 +627,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
/* Get the documentElement (<html>) to display the page in fullscreen */
private _cleanupAndRedrawManualAnnotations() {
this._fileDownloadService.loadActiveFileRedactionLog().subscribe(async redactionLogPreview => {
this.fileData.redactionLog = redactionLogPreview;
await this._annotationDrawService.drawAnnotations(
this._instance,
this.annotationData.allAnnotations,
this.hideSkipped,
this.viewerComponent.utils.isCompareMode,
);
});
private _cleanupAndRedrawManualAnnotations$() {
return this._fileDownloadService.loadActiveFileRedactionLog().pipe(
tap(redactionLog => (this.fileData.redactionLog = redactionLog)),
switchMapTo(this._redrawAnnotations()),
);
}
private async _cleanupAndRedrawManualAnnotationsForEntirePage(page: number) {
@ -654,15 +649,19 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
});
const newPageAnnotations = this.annotations.filter(item => item.pageNumber === page);
this._handleDeltaAnnotationFilters(currentPageAnnotations, newPageAnnotations);
await this._annotationDrawService.drawAnnotations(
this._instance,
newPageAnnotations,
this.hideSkipped,
this.viewerComponent.utils.isCompareMode,
);
await this._redrawAnnotations(newPageAnnotations);
}
}
private _redrawAnnotations(annotations = this.annotationData.allAnnotations) {
return this._annotationDrawService.drawAnnotations(
this._instance,
annotations,
this.hideSkipped,
this.viewerComponent.utils.isCompareMode,
);
}
private _handleDeltaAnnotationFilters(currentPageAnnotations: AnnotationWrapper[], newPageAnnotations: AnnotationWrapper[]) {
const primaryFilterGroup = this._filterService.getGroup('primaryFilters');
const primaryFilters = primaryFilterGroup.filters;

View File

@ -1,16 +0,0 @@
import { Injector } from '@angular/core';
import { FilterService, SearchService } from '@iqser/common-ui';
/**
* This should be removed when refactoring is done
* @param injector
* @constructor
*/
export const TEMPORARY_INJECTOR = injector =>
Injector.create({
providers: [
{ provide: FilterService, useClass: FilterService },
{ provide: SearchService, useClass: SearchService },
],
parent: injector,
});

View File

@ -9,10 +9,10 @@ import {
} from '@red/domain';
import { interval, Observable } from 'rxjs';
import { ConfigService } from '@services/config.service';
import { map, mergeMap, tap } from 'rxjs/operators';
import { filter, map, switchMapTo, tap, withLatestFrom } from 'rxjs/operators';
import { KeycloakService } from 'keycloak-angular';
import { UserService } from '@services/user.service';
import { EntitiesService, List, RequiredParam, Validate } from '@iqser/common-ui';
import { EntitiesService, List, mapEach, RequiredParam, Validate } from '@iqser/common-ui';
@Injectable()
export class FileDownloadService extends EntitiesService<DownloadStatus, IDownloadStatus> {
@ -25,23 +25,25 @@ export class FileDownloadService extends EntitiesService<DownloadStatus, IDownlo
protected readonly _injector: Injector,
) {
super(_injector, DownloadStatus, 'async/download');
interval(5000).subscribe(() => {
if (_userService.currentUser.isUser) {
this.loadAll().subscribe(() => {});
}
});
interval(5000)
.pipe(
withLatestFrom(_userService.currentUser$),
filter(([, user]) => user.isUser),
switchMapTo(this.loadAll()),
)
.subscribe();
}
downloadFiles(fileIds: List, dossierId: string): Observable<any> {
downloadFiles(fileIds: List, dossierId: string): Observable<DownloadStatus[]> {
return this.prepareDownload({
fileIds,
dossierId,
}).pipe(mergeMap(() => this.loadAll()));
}).pipe(switchMapTo(this.loadAll()));
}
loadAll(): Observable<DownloadStatus[]> {
return this.getStatuses().pipe(
map(entities => entities.map(entity => new DownloadStatus(entity))),
mapEach(entity => new DownloadStatus(entity)),
tap(entities => this.setEntities(entities)),
tap(() => (this.hasPendingDownloads = !!this.all.find(f => !f.lastDownload && f.isReady))),
);

View File

@ -1,4 +1,4 @@
import { catchError, filter, mergeMap, take, tap } from 'rxjs/operators';
import { catchError, filter, mergeMapTo, take, tap } from 'rxjs/operators';
import { ConfigService } from '@services/config.service';
import { Title } from '@angular/platform-browser';
import { of } from 'rxjs';
@ -15,7 +15,7 @@ export function configurationInitializer(
keycloakService.keycloakEvents$
.pipe(
filter(event => event.type === KeycloakEventType.OnReady),
mergeMap(() => generalSettingsService.getGeneralConfigurations()),
mergeMapTo(generalSettingsService.getGeneralConfigurations()),
tap(configuration => configService.updateDisplayName(configuration.displayName)),
catchError(() => {
title.setTitle('RedactManager');

@ -1 +1 @@
Subproject commit e1ce89e38d3520ad11960074f74c381429c0251a
Subproject commit 712178ea34c998098cd2c079a0be1dd863dd266e