RED-3988: make pdf viewer reusable
This commit is contained in:
parent
6cdf4d7634
commit
7ad5fe09ac
@ -1,4 +1,9 @@
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<redaction-reusable-pdf-viewer
|
||||
[style.visibility]="(reusablePdfViewer.show$ | async) ? 'visible' : 'hidden'"
|
||||
></redaction-reusable-pdf-viewer>
|
||||
|
||||
<iqser-full-page-loading-indicator></iqser-full-page-loading-indicator>
|
||||
<iqser-connection-status></iqser-connection-status>
|
||||
<iqser-full-page-error></iqser-full-page-error>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { Component, ViewContainerRef } from '@angular/core';
|
||||
import { RouterHistoryService } from '@services/router-history.service';
|
||||
import { UserService } from '@services/user.service';
|
||||
import { ReusablePdfViewer } from './modules/shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-root',
|
||||
@ -14,5 +15,6 @@ export class AppComponent {
|
||||
public viewContainerRef: ViewContainerRef,
|
||||
private readonly _routerHistoryService: RouterHistoryService,
|
||||
private readonly _userService: UserService,
|
||||
readonly reusablePdfViewer: ReusablePdfViewer,
|
||||
) {}
|
||||
}
|
||||
|
||||
@ -20,7 +20,7 @@ import { AppRoutingModule } from './app-routing.module';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { FileUploadDownloadModule } from '@upload-download/file-upload-download.module';
|
||||
import { DatePipe as BaseDatePipe, PlatformLocation } from '@angular/common';
|
||||
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE, BASE_HREF } from './tokens';
|
||||
import { ACTIVE_DOSSIERS_SERVICE, ARCHIVED_DOSSIERS_SERVICE, BASE_HREF, BASE_HREF_FN } from './tokens';
|
||||
import { MonacoEditorModule } from '@materia-ui/ngx-monaco-editor';
|
||||
import { GlobalErrorHandler } from '@utils/global-error-handler.service';
|
||||
import { REDMissingTranslationHandler } from '@utils/missing-translations-handler';
|
||||
@ -52,6 +52,7 @@ import { LoggerModule, NgxLoggerLevel, TOKEN_LOGGER_CONFIG, TOKEN_LOGGER_RULES_S
|
||||
import { LoggerRulesService } from '@services/logger-rules.service';
|
||||
import { ILoggerConfig } from '@red/domain';
|
||||
import { SystemPreferencesService } from '@services/system-preferences.service';
|
||||
import { ReusablePdfViewerComponent } from './modules/shared/components/reusable-pdf-viewer/reusable-pdf-viewer.component';
|
||||
|
||||
export function httpLoaderFactory(httpClient: HttpClient, configService: ConfigService): PruningTranslationLoader {
|
||||
return new PruningTranslationLoader(httpClient, '/assets/i18n/', `.json?version=${configService.values.FRONTEND_APP_VERSION}`);
|
||||
@ -69,7 +70,15 @@ function cleanupBaseUrl(baseUrl: string) {
|
||||
|
||||
const screens = [BaseScreenComponent, DownloadsListScreenComponent];
|
||||
|
||||
const components = [AppComponent, AuthErrorComponent, NotificationsComponent, SpotlightSearchComponent, BreadcrumbsComponent, ...screens];
|
||||
const components = [
|
||||
AppComponent,
|
||||
ReusablePdfViewerComponent,
|
||||
AuthErrorComponent,
|
||||
NotificationsComponent,
|
||||
SpotlightSearchComponent,
|
||||
BreadcrumbsComponent,
|
||||
...screens,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
declarations: [...components],
|
||||
@ -149,6 +158,11 @@ const components = [AppComponent, AuthErrorComponent, NotificationsComponent, Sp
|
||||
useFactory: (s: PlatformLocation) => cleanupBaseUrl(s.getBaseHrefFromDOM()),
|
||||
deps: [PlatformLocation],
|
||||
},
|
||||
{
|
||||
provide: BASE_HREF_FN,
|
||||
useFactory: (baseHref: string) => (path: string) => baseHref + path,
|
||||
deps: [BASE_HREF],
|
||||
},
|
||||
{
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
multi: true,
|
||||
|
||||
@ -43,6 +43,7 @@ import { FileDataService } from '../../services/file-data.service';
|
||||
import { ViewerHeaderConfigService } from '../../services/viewer-header-config.service';
|
||||
import { TooltipsService } from '../../services/tooltips.service';
|
||||
import { ManualRedactionService } from '../../services/manual-redaction.service';
|
||||
import { ReusablePdfViewer } from '../../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
|
||||
import Tools = Core.Tools;
|
||||
import TextTool = Tools.TextTool;
|
||||
import Annotation = Core.Annotations.Annotation;
|
||||
@ -84,6 +85,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
private readonly _headerConfigService: ViewerHeaderConfigService,
|
||||
private readonly _tooltipsService: TooltipsService,
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _reusablePdfViewer: ReusablePdfViewer,
|
||||
readonly stateService: FilePreviewStateService,
|
||||
readonly viewModeService: ViewModeService,
|
||||
readonly multiSelectService: MultiSelectService,
|
||||
@ -102,6 +104,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
|
||||
withLatestFrom(this.stateService.file$),
|
||||
tap(() => this._errorService.clear()),
|
||||
tap(([blob, file]) => this._loadDocument(blob, file)),
|
||||
tap(([blob, file]) => this._reusablePdfViewer.loadDocument(blob, file)),
|
||||
)
|
||||
.subscribe();
|
||||
}
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
import { Component, ElementRef, OnInit, ViewChild } from '@angular/core';
|
||||
import { ReusablePdfViewer } from './reusable-pdf-viewer.service';
|
||||
|
||||
@Component({
|
||||
selector: 'redaction-reusable-pdf-viewer',
|
||||
template: ' <div #viewer></div>',
|
||||
styles: [
|
||||
`
|
||||
div {
|
||||
width: calc(100% - 350px);
|
||||
height: calc(100% - 111px);
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
}
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class ReusablePdfViewerComponent implements OnInit {
|
||||
@ViewChild('viewer', { static: true }) readonly viewer: ElementRef;
|
||||
|
||||
constructor(readonly reusablePdfViewer: ReusablePdfViewer) {}
|
||||
|
||||
ngOnInit() {
|
||||
return this.reusablePdfViewer.init(this.viewer.nativeElement);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
import { Inject, Injectable, Injector } from '@angular/core';
|
||||
import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/webviewer';
|
||||
import { environment } from '../../../../../environments/environment';
|
||||
import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens';
|
||||
import { File } from '@red/domain';
|
||||
import { CustomError, ErrorService, LoadingService, log, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker/';
|
||||
import { ActivatedRoute, ActivationStart, Router } from '@angular/router';
|
||||
import { filter, map } from 'rxjs/operators';
|
||||
import { fromEvent, merge, Observable } from 'rxjs';
|
||||
import { ConfigService } from '../../../../services/config.service';
|
||||
import DocumentViewer = Core.DocumentViewer;
|
||||
import AnnotationManager = Core.AnnotationManager;
|
||||
import TextTool = Core.Tools.TextTool;
|
||||
|
||||
const DocLoadingError = new CustomError(_('error.file-preview.label'), _('error.file-preview.action'), 'iqser:refresh');
|
||||
const uselessElements = [
|
||||
'pageNavOverlay',
|
||||
'menuButton',
|
||||
'selectToolButton',
|
||||
'textHighlightToolButton',
|
||||
'textUnderlineToolButton',
|
||||
'textSquigglyToolButton',
|
||||
'textStrikeoutToolButton',
|
||||
'viewControlsButton',
|
||||
'contextMenuPopup',
|
||||
'linkButton',
|
||||
'toggleNotesButton',
|
||||
'notesPanel',
|
||||
'thumbnailControl',
|
||||
'documentControl',
|
||||
'ribbons',
|
||||
'toolsHeader',
|
||||
'rotateClockwiseButton',
|
||||
'rotateCounterClockwiseButton',
|
||||
'annotationStyleEditButton',
|
||||
'annotationGroupButton',
|
||||
];
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ReusablePdfViewer {
|
||||
readonly currentPage$ = this._injector.get(ActivatedRoute).queryParamMap.pipe(
|
||||
map(params => Number(params.get('page') ?? '1')),
|
||||
shareDistinctLast(),
|
||||
);
|
||||
documentViewer: DocumentViewer;
|
||||
annotationManager: AnnotationManager;
|
||||
|
||||
show$: Observable<boolean>;
|
||||
documentLoaded$: Observable<boolean>;
|
||||
#instance: WebViewerInstance;
|
||||
|
||||
constructor(
|
||||
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _errorService: ErrorService,
|
||||
private readonly _router: Router,
|
||||
private readonly _configService: ConfigService,
|
||||
private readonly _injector: Injector,
|
||||
) {}
|
||||
|
||||
get #documentLoaded$() {
|
||||
const docLoadedEvent = this.#instance.UI.Events.DOCUMENT_LOADED;
|
||||
const event$ = fromEvent(this.documentViewer, docLoadedEvent).pipe(map(() => true));
|
||||
|
||||
return event$.pipe(log('[PDF] Document loaded'), shareLast());
|
||||
}
|
||||
|
||||
get #show$() {
|
||||
const routeChanged = this._router.events.pipe(
|
||||
filter(event => event instanceof ActivationStart),
|
||||
map(() => false),
|
||||
);
|
||||
|
||||
return merge(routeChanged, this.documentLoaded$);
|
||||
}
|
||||
|
||||
async init(htmlElement: HTMLElement) {
|
||||
this.#instance = await this.#getInstance(htmlElement);
|
||||
console.log('[PDF] Initialized');
|
||||
|
||||
this.documentViewer = this.#instance.Core.documentViewer;
|
||||
this.annotationManager = this.#instance.Core.annotationManager;
|
||||
|
||||
this.documentLoaded$ = this.#documentLoaded$;
|
||||
this.show$ = this.#show$;
|
||||
this.#setSelectionMode();
|
||||
this.#configureElements();
|
||||
}
|
||||
|
||||
loadDocument(blob: Blob, file: File) {
|
||||
console.log('[PDF] Loading document', blob, file);
|
||||
const onError = () => {
|
||||
this._loadingService.stop();
|
||||
this._errorService.set(DocLoadingError);
|
||||
// this.stateService.reloadBlob();
|
||||
};
|
||||
// const pdfNet = this._instance.Core.PDFNet;
|
||||
// console.log('pdfnet', pdfNet);
|
||||
// await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
|
||||
// console.log('pdfnet initialized');
|
||||
// const document = await pdfNet.PDFDoc.createFromBuffer(await blob.arrayBuffer());
|
||||
// console.log('document initialized');
|
||||
// await document.flattenAnnotations(false);
|
||||
// console.log(document);
|
||||
this.#instance.UI.loadDocument(blob, { filename: file?.filename + '.pdf' ?? 'document.pdf', onError });
|
||||
}
|
||||
|
||||
#configureElements() {
|
||||
this.#instance.UI.disableElements(uselessElements);
|
||||
}
|
||||
|
||||
#setSelectionMode(): void {
|
||||
const textTool = this.#instance.Core.Tools.TextTool as unknown as TextTool;
|
||||
textTool.SELECTION_MODE = this._configService.values.SELECTION_MODE;
|
||||
}
|
||||
|
||||
#getInstance(htmlElement: HTMLElement) {
|
||||
const options: WebViewerOptions = {
|
||||
licenseKey: environment.licenseKey ? atob(environment.licenseKey) : null,
|
||||
fullAPI: true,
|
||||
path: this._convertPath('/assets/wv-resources'),
|
||||
css: this._convertPath('/assets/pdftron/stylesheet.css'),
|
||||
backendType: 'ems',
|
||||
};
|
||||
|
||||
return WebViewer(options, htmlElement);
|
||||
}
|
||||
}
|
||||
@ -1,7 +1,9 @@
|
||||
import { InjectionToken } from '@angular/core';
|
||||
|
||||
export const BASE_HREF: InjectionToken<string> = new InjectionToken<string>('BASE_HREF');
|
||||
export const DOSSIER_ID: InjectionToken<string> = new InjectionToken<string>('DOSSIER_ID');
|
||||
export const BASE_HREF = new InjectionToken<string>('BASE_HREF');
|
||||
export type BaseHrefFn = (path: string) => string;
|
||||
export const BASE_HREF_FN = new InjectionToken<BaseHrefFn>('Convert path function');
|
||||
export const DOSSIER_ID = new InjectionToken<string>('DOSSIER_ID');
|
||||
|
||||
export const ACTIVE_DOSSIERS_SERVICE = new InjectionToken<string>('Active dossiers service');
|
||||
export const ARCHIVED_DOSSIERS_SERVICE = new InjectionToken<string>('Archived dossiers service');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user