RED-6412: pdf actions run with cleanup

This commit is contained in:
Dan Percic 2023-03-27 09:42:55 +03:00
parent 07abfefe56
commit a7eca09d8e
5 changed files with 66 additions and 57 deletions

View File

@ -146,6 +146,7 @@ const routes: IqserRoutes = [
},
{
path: 'downloads',
// TODO: transform into a lazy loaded module
component: DownloadsListScreenComponent,
canActivate: [CompositeRouteGuard, IqserPermissionsGuard],
data: {
@ -224,7 +225,7 @@ const routes: IqserRoutes = [
@NgModule({
imports: [RouterModule.forRoot(routes, { scrollPositionRestoration: 'enabled' })],
providers: [{ provide: RouteReuseStrategy, useClass: CustomRouteReuseStrategy }],
providers: [{ provide: RouteReuseStrategy, useExisting: CustomRouteReuseStrategy }],
exports: [RouterModule],
})
export class AppRoutingModule {}

View File

@ -40,7 +40,7 @@ import { AnnotationDrawService } from '../pdf-viewer/services/annotation-draw.se
import { AnnotationProcessingService } from './services/annotation-processing.service';
import { Dictionary, File, ViewModes } from '@red/domain';
import { PermissionsService } from '@services/permissions.service';
import { combineLatest, firstValueFrom, from, Observable, of, pairwise } from 'rxjs';
import { combineLatest, firstValueFrom, Observable, of, pairwise } from 'rxjs';
import { PreferencesKeys, UserPreferenceService } from '@users/user-preference.service';
import { byId, byPage, download, handleFilterDelta, hasChanges } from '../../utils';
import { FilesService } from '@services/files/files.service';
@ -617,9 +617,8 @@ export class FilePreviewScreenComponent
this.addActiveScreenSubscription = this.state.blob$
.pipe(
switchMap(blob => from(this._documentViewer.lock()).pipe(map(() => blob))),
tap(() => this._errorService.clear()),
tap(blob => this.pdf.loadDocument(blob, this.state.file, () => this.state.reloadBlob())),
switchMap(blob => this.pdf.loadDocument(blob, this.state.file, () => this.state.reloadBlob())),
)
.subscribe();

View File

@ -36,7 +36,7 @@ export class AnnotationDrawService {
async draw(annotations: List<AnnotationWrapper>, hideSkipped: boolean, dossierTemplateId: string) {
try {
await this._draw(annotations, hideSkipped, dossierTemplateId);
await this._pdf.runWithCleanup(async () => await this._draw(annotations, hideSkipped, dossierTemplateId));
} catch (e) {
console.error(e);
}

View File

@ -1,12 +1,12 @@
import { inject, Injectable } from '@angular/core';
import { Core } from '@pdftron/webviewer';
import { NGXLogger } from 'ngx-logger';
import { fromEvent, merge, Observable } from 'rxjs';
import { fromEvent, merge, Observable, Subject } from 'rxjs';
import { debounceTime, filter, map, tap } from 'rxjs/operators';
import { ActivatedRoute } from '@angular/router';
import { PdfViewer } from './pdf-viewer.service';
import { UserPreferenceService } from '@users/user-preference.service';
import { log, shareLast } from '@iqser/common-ui';
import { log, shareDistinctLast, shareLast } from '@iqser/common-ui';
import { stopAndPrevent, stopAndPreventIfNotAllowed } from '../utils/functions';
import { RotationType, RotationTypes } from '@red/domain';
import { AnnotationToolNames } from '../utils/constants';
@ -23,6 +23,8 @@ export class REDDocumentViewer {
selectedText = '';
#document: DocumentViewer;
readonly #documentClosed$ = new Subject<undefined>();
readonly #logger = inject(NGXLogger);
readonly #userPreferenceService = inject(UserPreferenceService);
readonly #pdf = inject(PdfViewer);
@ -37,7 +39,7 @@ export class REDDocumentViewer {
}
get #documentUnloaded$() {
const event$ = fromEvent(this.#document, 'documentUnloaded');
const event$ = merge(fromEvent(this.#document, 'documentUnloaded'), this.#documentClosed$);
const toBool$ = event$.pipe(map(() => false));
return toBool$.pipe(tap(() => this.#logger.info('[PDF] Document unloaded')));
@ -48,10 +50,14 @@ export class REDDocumentViewer {
const toBool$ = event$.pipe(map(() => true));
return toBool$.pipe(
tap(() => this.#flattenAnnotations()),
tap(() => this.#setCurrentPage()),
tap(() => this.#setInitialDisplayMode()),
tap(() => this.updateTooltipsVisibility()),
tap(() =>
this.#pdf.runWithCleanup(async () => {
await this.#flattenAnnotations();
this.#setCurrentPage();
this.#setInitialDisplayMode();
this.updateTooltipsVisibility();
}),
),
tap(() => this.#logger.info('[PDF] Document loaded')),
);
}
@ -86,7 +92,7 @@ export class REDDocumentViewer {
}
get #loaded$() {
return merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast());
return merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareDistinctLast());
}
clearSelection() {
@ -95,9 +101,16 @@ export class REDDocumentViewer {
}
close() {
this.#logger.info('[PDF] Closing document');
this.#document.closeDocument();
this.#pdf.closeCompareMode();
this.#documentClosed$.next(undefined);
const closeAction = async () => {
this.#logger.info('[PDF] Closing document');
this.#document.closeDocument();
this.#pdf.closeCompareMode();
await this.#pdf.instance.UI.closeDocument();
};
this.#pdf.runWithCleanup(closeAction).then();
}
updateTooltipsVisibility(): void {
@ -113,17 +126,6 @@ export class REDDocumentViewer {
this.textSelected$ = this.#textSelected$;
}
async lock() {
const document = await this.PDFDoc;
if (!document) {
return false;
}
await document.lock();
this.#logger.info('[PDF] Locked');
return true;
}
async blob() {
const data = await this.document.getFileData();
return new Blob([new Uint8Array(data)], { type: 'application/pdf' });

View File

@ -1,11 +1,10 @@
import { Inject, Injectable, Injector } from '@angular/core';
import { inject, Inject, Injectable, Injector } from '@angular/core';
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
import { BASE_HREF_FN, BaseHrefFn, ErrorService, shareDistinctLast } from '@iqser/common-ui';
import { File, IHeaderElement } from '@red/domain';
import { BASE_HREF_FN, BaseHrefFn, ErrorService, getConfig, shareDistinctLast } from '@iqser/common-ui';
import { AppConfig, File, IHeaderElement } from '@red/domain';
import { ActivatedRoute } from '@angular/router';
import { map, startWith } from 'rxjs/operators';
import { BehaviorSubject, combineLatest, fromEvent, Observable, switchMap } from 'rxjs';
import { ConfigService } from '@services/config.service';
import { NGXLogger } from 'ngx-logger';
import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, SEARCH_OPTIONS, USELESS_ELEMENTS } from '../utils/constants';
import { Rgb } from '../utils/types';
@ -22,7 +21,7 @@ import Quad = Core.Math.Quad;
@Injectable()
export class PdfViewer {
readonly currentPage$ = this._activatedRoute.queryParamMap.pipe(
readonly currentPage$ = inject(ActivatedRoute).queryParamMap.pipe(
map(params => Number(params.get('page') ?? '1')),
shareDistinctLast(),
);
@ -37,11 +36,13 @@ export class PdfViewer {
totalPages$: Observable<number>;
#instance: WebViewerInstance;
readonly #licenseKey = inject(LicenseService).activeLicenseKey;
readonly #config = getConfig<AppConfig>();
readonly #compareMode$ = new BehaviorSubject(false);
readonly #searchButton: IHeaderElement = {
type: 'actionButton',
img: this._convertPath('/assets/icons/general/pdftron-action-search.svg'),
title: this._translateService.instant('pdf-viewer.text-popup.actions.search'),
title: inject(TranslateService).instant('pdf-viewer.text-popup.actions.search'),
onClick: () => {
this.#instance.UI.openElements(['searchPanel']);
setTimeout(() => this.#searchForSelectedText(), 250);
@ -51,10 +52,7 @@ export class PdfViewer {
constructor(
private readonly _logger: NGXLogger,
private readonly _injector: Injector,
private readonly _activatedRoute: ActivatedRoute,
private readonly _licenseService: LicenseService,
private readonly _errorService: ErrorService,
private readonly _translateService: TranslateService,
private readonly _userPreferenceService: UserPreferenceService,
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
) {}
@ -154,26 +152,28 @@ export class PdfViewer {
this.#instance.Core.setCustomFontURL('https://' + window.location.host + this._convertPath('/assets/pdftron'));
}
try {
await this.PDFNet.initialize(this._licenseService.activeLicenseKey);
} catch (e) {
this._errorService.set(e);
throw e;
}
await this.runWithCleanup(async () => {
try {
await this.PDFNet.initialize(this.#licenseKey);
} catch (e) {
this._errorService.set(e);
throw e;
}
this.#instance.UI.setTheme(this._userPreferenceService.getTheme());
this._logger.info('[PDF] Initialized');
this.#instance.UI.setTheme(this._userPreferenceService.getTheme());
this._logger.info('[PDF] Initialized');
this.documentViewer = this.#instance.Core.documentViewer;
this.documentViewer = this.#instance.Core.documentViewer;
this.compareMode$ = this.#compareMode$.asObservable();
this.pageChanged$ = this.#pageChanged$.pipe(shareDistinctLast());
this.totalPages$ = this.#totalPages$.pipe(shareDistinctLast());
this.#setSelectionMode();
this.#configureElements();
this.#disableHotkeys();
this.#clearSearchResultsWhenVisibilityChanged();
this.#listenForCommandF();
this.compareMode$ = this.#compareMode$.asObservable();
this.pageChanged$ = this.#pageChanged$.pipe(shareDistinctLast());
this.totalPages$ = this.#totalPages$.pipe(shareDistinctLast());
this.#setSelectionMode();
this.#configureElements();
this.#disableHotkeys();
this.#clearSearchResultsWhenVisibilityChanged();
this.#listenForCommandF();
});
return this.#instance;
}
@ -196,6 +196,10 @@ export class PdfViewer {
this.#compareMode$.next(false);
}
runWithCleanup(action: () => Promise<void> | void) {
return this.PDFNet.runWithCleanup(action, this.#licenseKey);
}
async loadDocument(blob: Blob, file: File, actionOnError?: () => void) {
const onError = () => {
this._injector.get(ErrorService).set(DOCUMENT_LOADING_ERROR);
@ -208,7 +212,11 @@ export class PdfViewer {
this._logger.info('[PDF] Loading document...');
this.#instance.UI.loadDocument(blob, { documentId: file.fileId, filename: file?.filename ?? 'document.pdf', onError });
await this.runWithCleanup(async () => {
const document = await this.documentViewer.getDocument()?.getPDFDoc();
await document?.lock();
this.#instance.UI.loadDocument(blob, { documentId: file.fileId, filename: file?.filename ?? 'document.pdf', onError });
});
}
quad(x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, x4: number, y4: number) {
@ -307,13 +315,12 @@ export class PdfViewer {
#setSelectionMode(): void {
const textTool = this.#instance.Core.Tools.TextTool as unknown as TextTool;
const configService = this._injector.get(ConfigService);
textTool.SELECTION_MODE = configService.values.SELECTION_MODE;
textTool.SELECTION_MODE = this.#config.SELECTION_MODE;
}
#getInstance(htmlElement: HTMLElement) {
const options: WebViewerOptions = {
licenseKey: this._licenseService.activeLicenseKey,
licenseKey: this.#licenseKey,
fullAPI: true,
path: this._convertPath('/assets/wv-resources'),
css: this._convertPath('/assets/pdftron/stylesheet.css'),