RED-3800: Cleanup iqser-cache lib, small file preview refactor

This commit is contained in:
Adina Țeudan 2022-07-12 23:13:52 +03:00
parent a47cbdfacb
commit 4d2fa2119b
28 changed files with 110 additions and 250 deletions

View File

@ -11,7 +11,6 @@
{
"enforceBuildableLibDependency": true,
"allow": [
"@redaction/red-cache",
"@services/**",
"@components/**",
"@guards/**",

View File

@ -1,11 +1,11 @@
{
"version": 1,
"projects": {
"red-cache": {
"iqser-cache": {
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "library",
"root": "libs/red-cache",
"sourceRoot": "libs/red-cache/src",
"root": "libs/iqser-cache",
"sourceRoot": "libs/iqser-cache/src",
"prefix": "redaction",
"schematics": {
"@schematics/angular:component": {

View File

@ -13,7 +13,7 @@ import { ServiceWorkerModule } from '@angular/service-worker';
import { environment } from '@environments/environment';
import { AuthModule } from './modules/auth/auth.module';
import { AuthErrorComponent } from '@components/auth-error/auth-error.component';
import { HttpCacheInterceptor } from '@red/cache';
import { HttpCacheInterceptor } from '@iqser/cache';
import { NotificationsComponent } from '@components/notifications/notifications.component';
import { DownloadsListScreenComponent } from '@components/downloads-list-screen/downloads-list-screen.component';
import { AppRoutingModule } from './app-routing.module';

View File

@ -0,0 +1,27 @@
<ng-container *ngIf="state.file$ | async as file">
<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
*ngIf="!file.excluded"
[activeViewerPage]="pdf.currentPage$ | async"
[annotationActionsTemplate]="annotationActionsTemplate"
[dialogRef]="state.dialogRef"
[file]="file"
></redaction-file-workload>
<ng-template #annotationActionsTemplate let-annotation="annotation">
<redaction-annotation-actions
[annotations]="[annotation]"
[canPerformAnnotationActions]="pdfProxyService.canPerformAnnotationActions$ | async"
[iqserHelpMode]="getActionsHelpModeKey(annotation)"
[scrollableParentView]="scrollableParentView"
></redaction-annotation-actions>
</ng-template>
</ng-container>

View File

@ -0,0 +1,38 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { DocumentInfoService } from '../../services/document-info.service';
import { ExcludedPagesService } from '../../services/excluded-pages.service';
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { PdfProxyService } from '../../services/pdf-proxy.service';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { ActionsHelpModeKeys } from '../../utils/constants';
import { ScrollableParentView, ScrollableParentViews } from '@iqser/common-ui';
@Component({
selector: 'redaction-file-preview-right-container',
templateUrl: './file-preview-right-container.component.html',
styleUrls: ['./file-preview-right-container.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FilePreviewRightContainerComponent {
constructor(
readonly pdf: PdfViewer,
readonly pdfProxyService: PdfProxyService,
readonly state: FilePreviewStateService,
readonly documentInfoService: DocumentInfoService,
readonly excludedPagesService: ExcludedPagesService,
) {}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.ANNOTATIONS_LIST;
}
getActionsHelpModeKey(annotation: AnnotationWrapper): string {
const type = annotation?.typeLabel?.split('.')[1];
const typeValue = annotation?.typeValue;
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
return ActionsHelpModeKeys[`${type}-${typeValue}`];
}
return ActionsHelpModeKeys[type];
}
}

View File

@ -70,36 +70,12 @@
</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>
<redaction-document-info *ngIf="documentInfoService.shown$ | async"></redaction-document-info>
<redaction-file-workload
*ngIf="!file.excluded"
[activeViewerPage]="pdf.currentPage$ | async"
[annotationActionsTemplate]="annotationActionsTemplate"
[dialogRef]="dialogRef"
[file]="file"
></redaction-file-workload>
<redaction-file-preview-right-container></redaction-file-preview-right-container>
</div>
</div>
</section>
</ng-container>
<ng-template #annotationActionsTemplate let-annotation="annotation">
<redaction-annotation-actions
[annotations]="[annotation]"
[canPerformAnnotationActions]="pdfProxyService.canPerformAnnotationActions$ | async"
[iqserHelpMode]="getActionsHelpModeKey(annotation)"
[scrollableParentView]="scrollableParentView"
></redaction-annotation-actions>
</ng-template>
<ng-template #annotationFilterTemplate let-filter="filter">
<redaction-type-filter [dossierTemplateId]="state.dossierTemplateId" [filter]="filter"></redaction-type-filter>
</ng-template>

View File

@ -14,10 +14,8 @@ import {
OnAttach,
OnDetach,
processFilters,
ScrollableParentView,
ScrollableParentViews,
} from '@iqser/common-ui';
import { MatDialogRef, MatDialogState } from '@angular/material/dialog';
import { MatDialogState } from '@angular/material/dialog';
import { ManualRedactionEntryWrapper } from '@models/file/manual-redaction-entry.wrapper';
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.service';
@ -31,10 +29,7 @@ import { FilesService } from '@services/files/files.service';
import { FileManagementService } from '@services/files/file-management.service';
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
import { FilesMapService } from '@services/files/files-map.service';
import { ExcludedPagesService } from './services/excluded-pages.service';
import { ViewModeService } from './services/view-mode.service';
import { MultiSelectService } from './services/multi-select.service';
import { DocumentInfoService } from './services/document-info.service';
import { ReanalysisService } from '@services/reanalysis.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { FilePreviewStateService } from './services/file-preview-state.service';
@ -45,7 +40,7 @@ import { PageRotationService } from '../pdf-viewer/services/page-rotation.servic
import { ComponentCanDeactivate } from '@guards/can-deactivate.guard';
import { FilePreviewDialogService } from './services/file-preview-dialog.service';
import { FileDataService } from './services/file-data.service';
import { ActionsHelpModeKeys, ALL_HOTKEYS, TextPopups } from './utils/constants';
import { ALL_HOTKEYS, TextPopups } from './utils/constants';
import { NGXLogger } from 'ngx-logger';
import { StampService } from './services/stamp.service';
import { PdfViewer } from '../pdf-viewer/services/pdf-viewer.service';
@ -68,7 +63,6 @@ const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnInit, OnDestroy, OnAttach, OnDetach, ComponentCanDeactivate {
readonly circleButtonTypes = CircleButtonTypes;
dialogRef: MatDialogRef<unknown>;
fullScreen = false;
readonly fileId = this.state.fileId;
readonly dossierId = this.state.dossierId;
@ -81,13 +75,11 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
constructor(
readonly pdf: PdfViewer,
readonly documentInfoService: DocumentInfoService,
readonly state: FilePreviewStateService,
readonly listingService: AnnotationsListingService,
readonly permissionsService: PermissionsService,
readonly multiSelectService: MultiSelectService,
readonly excludedPagesService: ExcludedPagesService,
readonly userPreferenceService: UserPreferenceService,
readonly pdfProxyService: PdfProxyService,
private readonly _listingService: AnnotationsListingService,
private readonly _router: Router,
private readonly _ngZone: NgZone,
private readonly _logger: NGXLogger,
@ -102,14 +94,13 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private readonly _fileDataService: FileDataService,
private readonly _viewModeService: ViewModeService,
private readonly _documentViewer: REDDocumentViewer,
private readonly _changeDetectorRef: ChangeDetectorRef,
private readonly _changeRef: ChangeDetectorRef,
private readonly _dialogService: FilePreviewDialogService,
private readonly _pageRotationService: PageRotationService,
private readonly _viewerHeaderService: ViewerHeaderService,
private readonly _annotationDrawService: AnnotationDrawService,
private readonly _annotationProcessingService: AnnotationProcessingService,
private readonly _stampService: StampService,
readonly pdfProxyService: PdfProxyService,
private readonly _injector: Injector,
) {
super();
@ -124,10 +115,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return this._pageRotationService.hasRotations;
}
get scrollableParentView(): ScrollableParentView {
return ScrollableParentViews.ANNOTATIONS_LIST;
}
get #textSelected$() {
const textSelected$ = combineLatest([
this._documentViewer.textSelected$,
@ -206,7 +193,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
ngOnDetach() {
this._viewerHeaderService.resetCompareButtons();
super.ngOnDetach();
this._changeDetectorRef.markForCheck();
this._changeRef.markForCheck();
}
async ngOnAttach(previousRoute: ActivatedRouteSnapshot) {
@ -220,7 +207,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
await this._fileDataService.loadRedactionLog();
this._viewerHeaderService.updateElements();
await this.#updateQueryParamsPage(Number(previousRoute.queryParams.page ?? '1'));
this._changeDetectorRef.markForCheck();
this._changeRef.markForCheck();
}
async ngOnInit(): Promise<void> {
@ -247,7 +234,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return this._ngZone.run(() => {
const file = this.state.file;
this.dialogRef = this._dialogService.openDialog(
this.state.dialogRef = this._dialogService.openDialog(
'manualAnnotation',
null,
{ manualRedactionEntryWrapper, dossierId: this.dossierId, file },
@ -286,14 +273,14 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return;
}
if (!ALL_HOTKEYS.includes($event.key) || this.dialogRef?.getState() === MatDialogState.OPEN) {
if (!ALL_HOTKEYS.includes($event.key) || this.state.dialogRef?.getState() === MatDialogState.OPEN) {
return;
}
if (['Escape'].includes($event.key)) {
this.fullScreen = false;
this.closeFullScreen();
this._changeDetectorRef.markForCheck();
this._changeRef.markForCheck();
}
if (['f', 'F'].includes($event.key)) {
@ -325,7 +312,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
}
this._loadingService.stop();
this._changeDetectorRef.markForCheck();
this._changeRef.markForCheck();
}
closeFullScreen() {
@ -402,15 +389,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return [oldAnnotations, newAnnotations];
}
getActionsHelpModeKey(annotation: AnnotationWrapper): string {
const type = annotation?.typeLabel?.split('.')[1];
const typeValue = annotation?.typeValue;
if (type === 'hint' && (typeValue === 'ocr' || typeValue === 'formula' || typeValue === 'image')) {
return ActionsHelpModeKeys[`${type}-${typeValue}`];
}
return ActionsHelpModeKeys[type];
}
#getAnnotationsToDraw(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
const currentPage = this.pdf.currentPage;
const currentPageAnnotations = this._annotationManager.get(a => a.getPageNumber() === currentPage);
@ -456,7 +434,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
};
await this._router.navigate([], extras);
this._changeDetectorRef.markForCheck();
this._changeRef.markForCheck();
}
#findAnnotationsToDraw(
@ -646,7 +624,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
#highlightSelectedAnnotations(newAnnotations: AnnotationWrapper[]) {
const annotationsIds = newAnnotations.map(annotation => annotation.id);
const selected = this.listingService.selected.filter(a => annotationsIds.includes(a.id));
const selected = this._listingService.selected.filter(a => annotationsIds.includes(a.id));
const annotations = this._annotationManager.get(selected);
this._annotationManager.select(annotations);
}

View File

@ -37,6 +37,7 @@ import { AnnotationWrapperComponent } from './components/annotation-wrapper/anno
import { AnnotationReferenceComponent } from './components/annotation-reference/annotation-reference.component';
import { ImportRedactionsDialogComponent } from './dialogs/import-redactions-dialog/import-redactions-dialog';
import { DocumentUnloadedGuard } from './services/document-unloaded.guard';
import { FilePreviewRightContainerComponent } from './components/right-container/file-preview-right-container.component';
const routes: Routes = [
{
@ -79,6 +80,7 @@ const components = [
HighlightsSeparatorComponent,
AnnotationReferenceComponent,
FilePreviewScreenComponent,
FilePreviewRightContainerComponent,
];
@NgModule({

View File

@ -8,13 +8,14 @@ import { boolFactory, getParam, LoadingService } from '@iqser/common-ui';
import { filter, map, startWith, tap, withLatestFrom } from 'rxjs/operators';
import { FileManagementService } from '@services/files/file-management.service';
import { dossiersServiceResolver } from '@services/entity-services/dossiers.service.provider';
import { wipeFilesCache } from '@red/cache';
import { wipeFilesCache } from '@iqser/cache';
import { DossiersService } from '@services/dossiers/dossiers.service';
import { FilesService } from '@services/files/files.service';
import { DictionaryService } from '@services/entity-services/dictionary.service';
import { HttpEvent, HttpEventType, HttpProgressEvent, HttpResponse } from '@angular/common/http';
import { TranslateService } from '@ngx-translate/core';
import { DictionariesMapService } from '../../../services/entity-services/dictionaries-map.service';
import { MatDialogRef } from '@angular/material/dialog';
const ONE_MEGABYTE = 1024 * 1024;
@ -42,6 +43,7 @@ export class FilePreviewStateService {
readonly dossierTemplateId: string;
readonly fileId: string = getParam(FILE_ID);
dossier: Dossier;
dialogRef: MatDialogRef<unknown>;
file: File;
#dossierDictionary: Dictionary;
readonly #reloadBlob$ = new Subject();

View File

@ -3,7 +3,7 @@ import { HttpClient } from '@angular/common/http';
import { Title } from '@angular/platform-browser';
import packageInfo from '../../../../../package.json';
import envConfig from '../../assets/config/config.json';
import { CacheApiService, wipeAllCaches } from '@red/cache';
import { CacheApiService, wipeAllCaches } from '@iqser/cache';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
import { AppConfig } from '@red/domain';

View File

@ -2,7 +2,7 @@ import { inject, Inject, Injectable } from '@angular/core';
import { KeycloakService } from 'keycloak-angular';
import jwt_decode from 'jwt-decode';
import { ICreateUserRequest, IMyProfileUpdateRequest, IProfileUpdateRequest, IResetPasswordRequest, IUser, User } from '@red/domain';
import { wipeCaches } from '@red/cache';
import { wipeCaches } from '@iqser/cache';
import { BASE_HREF } from '../tokens';
import { BehaviorSubject, firstValueFrom, Observable } from 'rxjs';
import { EntitiesService, List, mapEach, QueryParam, RequiredParam, Validate } from '@iqser/common-ui';

View File

@ -5,7 +5,7 @@
{
"files": ["**/*.ts"],
"parserOptions": {
"project": ["libs/red-cache/tsconfig.json"]
"project": ["libs/iqser-cache/tsconfig.json"]
},
"rules": {
"@angular-eslint/directive-selector": [

View File

@ -1,7 +1,7 @@
# red-cache
# iqser-cache
This library was generated with [Nx](https://nx.dev).
## Running unit tests
Run `nx test red-cache` to execute the unit tests.
Run `nx test iqser-cache` to execute the unit tests.

View File

@ -41,9 +41,8 @@ export class CacheApiService {
if (parseInt(expires, 10) > new Date().getTime()) {
// console.log('[CACHE-API] Returning from cache: ', url);
return this._toHttpResponse(response);
} else {
// console.log('[CACHE-API] cache expired: ', url);
}
// console.log('[CACHE-API] cache expired: ', url);
} else {
// console.log('[CACHE-API] Returning from cache: ', url);
return this._toHttpResponse(response);
@ -68,34 +67,9 @@ export class CacheApiService {
// console.log('should cache', valueReference, string, response);
return cache.put(request, response);
});
} else {
}
return Promise.resolve();
}
}
removeCache(cacheId: string): Promise<any> {
if (this.cachesAvailable) {
return caches.open(APP_LEVEL_CACHE).then(cache => cache.delete(cacheId));
} else {
return Promise.resolve(undefined);
}
}
deleteDynamicCache(cacheName: string): Promise<any> {
if (this.cachesAvailable && DYNAMIC_CACHES.some(cache => cache.name === cacheName)) {
return caches.delete(cacheName);
} else {
return Promise.resolve(undefined);
}
}
deleteDynamicCacheEntry(cacheName: string, cacheEntry: string): Promise<any> {
if (this.cachesAvailable && DYNAMIC_CACHES.some(cache => cache.name === cacheName)) {
return caches.open(cacheName).then(cache => cache.delete(cacheEntry));
} else {
return Promise.resolve(undefined);
}
}
getCachedValue(name: string): Promise<any> {
if (this.cachesAvailable) {
@ -112,9 +86,8 @@ export class CacheApiService {
return undefined;
}),
);
} else {
return Promise.resolve(undefined);
}
return Promise.resolve(undefined);
}
_buildUrl(request: HttpRequest<any>) {

View File

@ -0,0 +1,3 @@
export * from './cache-api.service';
export * from './cache-utils';
export * from './http-cache-interceptor';

View File

@ -0,0 +1 @@
export * from './caches/index';

View File

@ -1,5 +0,0 @@
export interface Cacheable {
restoreFromCache(): Promise<any>;
storeToCache(): Promise<any>;
}

View File

@ -1,11 +0,0 @@
import { IdToObjectListCacheStoreService } from './id-to-object-list-cache-store.service';
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class IdToObjectCacheRegisterService {
constructor(private _idToObjectListCacheStoreService: IdToObjectListCacheStoreService) {}
registerCaches() {}
}

View File

@ -1,95 +0,0 @@
import { Injectable } from '@angular/core';
import { from, Observable, of } from 'rxjs';
import { CacheApiService } from './cache-api.service';
import { catchError, map, mergeMap } from 'rxjs/operators';
export interface IdToObject {
id: string;
object: any;
}
export interface RegisteredCache {
name: string;
keyConversionFunction: (id: string, input: any) => string;
cacheFunction: (ids: string[], ...params: any) => Observable<{ [key: string]: any }>;
}
@Injectable({
providedIn: 'root'
})
export class IdToObjectListCacheStoreService {
private _cachesList: { [key: string]: RegisteredCache } = {};
constructor(private _cacheApiService: CacheApiService) {}
registerCache(
name: string,
cacheFunction: (ids: string[], ...params: any) => Observable<{ [key: string]: any }>,
keyConversionFunction: (id: string, ...params: any) => string = id => id
) {
this._cachesList[name] = {
name,
keyConversionFunction,
cacheFunction
};
}
invokeCache(name: string, ids: string[], ...params: any): Observable<{ [key: string]: any }> {
const promises = [];
const cache = this._cachesList[name];
ids.map(id => this._toUrl(name, cache.keyConversionFunction(id, params))).forEach(url => {
promises.push(this._cacheApiService.getCachedValue(url));
});
return from(Promise.all(promises))
.pipe(catchError(() => of([])))
.pipe(
mergeMap((resolvedValues: IdToObject[]) => {
const partialResult = {};
resolvedValues
.filter(v => !!v)
.forEach(foundValue => {
partialResult[foundValue.id] = foundValue.object;
});
const existingIds = Object.keys(partialResult);
const requestIds = ids.filter(el => !existingIds.includes(el));
if (requestIds.length > 0) {
return cache.cacheFunction(requestIds, params).pipe(
map(data => {
// new items
for (const key of Object.keys(data)) {
const idToObject = {
id: key,
object: data[key]
};
// cache each new result
this._cacheApiService.cacheValue(
this._toUrl(name, cache.keyConversionFunction(key, params)),
idToObject
);
}
// add existing results to final result
for (const existingKey of Object.keys(partialResult)) {
data[existingKey] = partialResult[existingKey];
}
return data;
})
);
} else {
return of(partialResult);
}
})
);
}
expireCache(name: string, id: string, ...params: any) {
const cache = this._cachesList[name];
const cacheUrl = this._toUrl(name, cache.keyConversionFunction(id, params));
this._cacheApiService.removeCache(cacheUrl);
}
private _toUrl(cacheName: string, id: string) {
return `/${cacheName}/${id}`;
}
}

View File

@ -1,18 +0,0 @@
/*
* Copyright (c) 2010 - 2030 by ACI Worldwide Inc.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of ACI Worldwide Inc ("Confidential Information"). You shall
* not disclose such Confidential Information and shall use it
* only in accordance with the terms of the license agreement
* you entered with ACI Worldwide Inc.
*
*/
export * from './cache-api.service';
export * from './cache-utils';
export * from './cacheable';
export * from './http-cache-interceptor';
export * from './id-to-object-cache.register.service';
export * from './id-to-object-list-cache-store.service';

View File

@ -1,13 +0,0 @@
/*
* Copyright (c) 2010 - 2030 by ACI Worldwide Inc.
* All rights reserved.
*
* This software is the confidential and proprietary information
* of ACI Worldwide Inc ("Confidential Information"). You shall
* not disclose such Confidential Information and shall use it
* only in accordance with the terms of the license agreement
* you entered with ACI Worldwide Inc.
*
*/
export * from './caches/index';

View File

@ -3,11 +3,11 @@ const scanner = require('sonarqube-scanner');
scanner(
{
serverUrl: 'https://sonarqube.iqser.com',
token : "362e7bc3a14436f2d6ee72c5ed379681d4b7963c",
token: '362e7bc3a14436f2d6ee72c5ed379681d4b7963c',
options: {
'sonar.projectKey': 'RED_ui',
'sonar.sources': 'apps/red-ui,libs/common-ui,libs/red-cache',
}
'sonar.sources': 'apps/red-ui,libs/common-ui,libs/iqser-cache',
},
() => process.exit()
)
},
() => process.exit(),
);

View File

@ -23,7 +23,7 @@
"@i18n/*": ["apps/red-ui/src/app/i18n/*"],
"@iqser/common-ui": ["libs/common-ui/src/index.ts"],
"@models/*": ["apps/red-ui/src/app/models/*"],
"@red/cache": ["libs/red-cache/src/index.ts"],
"@iqser/cache": ["libs/iqser-cache/src/index.ts"],
"@red/domain": ["libs/red-domain/src/index.ts"],
"@services/*": ["apps/red-ui/src/app/services/*"],
"@shared/*": ["apps/red-ui/src/app/modules/shared/*"],