RED-6829: update pdf event listeners to run in ng zone
This commit is contained in:
parent
f3e7811523
commit
04cc33b18e
@ -47,7 +47,7 @@
|
||||
|
||||
<router-outlet></router-outlet>
|
||||
|
||||
<redaction-pdf-viewer [style.visibility]="(documentViewer.loaded$ | async) ? 'visible' : 'hidden'"></redaction-pdf-viewer>
|
||||
<redaction-pdf-viewer [style.visibility]="documentViewer.loaded() ? 'visible' : 'hidden'"></redaction-pdf-viewer>
|
||||
|
||||
<iqser-skeleton [templates]="{ dashboard: dashboardSkeleton, dossier: dossierSkeleton }"></iqser-skeleton>
|
||||
|
||||
|
||||
@ -1,62 +1,58 @@
|
||||
<ng-container *ngIf="componentContext$ | async as ctx">
|
||||
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
|
||||
<div class="right-title heading" translate="file-preview.tabs.document-info.label">
|
||||
<div>
|
||||
<iqser-circle-button
|
||||
(action)="edit()"
|
||||
*ngIf="permissionsService.canEditFileAttributes(_state.file(), _state.dossier())"
|
||||
[tooltip]="'file-preview.tabs.document-info.edit' | translate"
|
||||
buttonId="edit-document-info-btn"
|
||||
icon="iqser:edit"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="documentInfoService.hide()"
|
||||
[tooltip]="'file-preview.tabs.document-info.close' | translate"
|
||||
buttonId="close-document-info-btn"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-content" iqserHasScrollbar>
|
||||
<div class="section">
|
||||
<div *ngFor="let attr of _fileAttributes()" class="attribute">
|
||||
<div class="small-label">{{ attr.label }}:</div>
|
||||
<div>{{ attr.value ? (isDate(attr) ? (attr.value | date : 'd MMM yyyy') : attr.value) : '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section small-label stats-subtitle">
|
||||
<div>
|
||||
<iqser-circle-button
|
||||
(action)="edit()"
|
||||
*ngIf="permissionsService.canEditFileAttributes(_state.file(), _state.dossier())"
|
||||
[tooltip]="'file-preview.tabs.document-info.edit' | translate"
|
||||
buttonId="edit-document-info-btn"
|
||||
icon="iqser:edit"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
<mat-icon svgIcon="red:folder"></mat-icon>
|
||||
<span
|
||||
[innerHTML]="'file-preview.tabs.document-info.details.dossier' | translate : { dossierName: _state.dossier().dossierName }"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<iqser-circle-button
|
||||
(action)="documentInfoService.hide()"
|
||||
[tooltip]="'file-preview.tabs.document-info.close' | translate"
|
||||
buttonId="close-document-info-btn"
|
||||
icon="iqser:close"
|
||||
tooltipPosition="before"
|
||||
></iqser-circle-button>
|
||||
<div>
|
||||
<mat-icon svgIcon="iqser:document"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate : { pages: _state.file().numberOfPages } }}</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="_state.file().added | date : 'mediumDate' as added">
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
<span [innerHTML]="'file-preview.tabs.document-info.details.created-on' | translate : { date: added }"></span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="_state.dossier().dueDate | date : 'mediumDate' as dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
<span [innerHTML]="'file-preview.tabs.document-info.details.due' | translate : { date: dueDate }"></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
{{ _dossierTemplate().name }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="right-content" iqserHasScrollbar>
|
||||
<div class="section">
|
||||
<div *ngFor="let attr of ctx.fileAttributes" class="attribute">
|
||||
<div class="small-label">{{ attr.label }}:</div>
|
||||
<div>{{ attr.value ? (isDate(attr) ? (attr.value | date : 'd MMM yyyy') : attr.value) : '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section small-label stats-subtitle">
|
||||
<div>
|
||||
<mat-icon svgIcon="red:folder"></mat-icon>
|
||||
<span
|
||||
[innerHTML]="
|
||||
'file-preview.tabs.document-info.details.dossier' | translate : { dossierName: _state.dossier().dossierName }
|
||||
"
|
||||
></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="iqser:document"></mat-icon>
|
||||
<span>{{ 'file-preview.tabs.document-info.details.pages' | translate : { pages: _state.file().numberOfPages } }}</span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="_state.file().added | date : 'mediumDate' as added">
|
||||
<mat-icon svgIcon="red:calendar"></mat-icon>
|
||||
<span [innerHTML]="'file-preview.tabs.document-info.details.created-on' | translate : { date: added }"></span>
|
||||
</div>
|
||||
|
||||
<div *ngIf="_state.dossier().dueDate | date : 'mediumDate' as dueDate">
|
||||
<mat-icon svgIcon="red:lightning"></mat-icon>
|
||||
<span [innerHTML]="'file-preview.tabs.document-info.details.due' | translate : { date: dueDate }"></span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<mat-icon svgIcon="red:template"></mat-icon>
|
||||
{{ ctx.dossierTemplateName }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
@ -1,14 +1,13 @@
|
||||
import { Component, inject } from '@angular/core';
|
||||
import { Component, computed, inject } from '@angular/core';
|
||||
import { DossierTemplatesService } from '@services/dossier-templates/dossier-templates.service';
|
||||
import { DocumentInfoService } from '../../services/document-info.service';
|
||||
import { combineLatest, switchMap } from 'rxjs';
|
||||
import { PermissionsService } from '@services/permissions.service';
|
||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||
import { map } from 'rxjs/operators';
|
||||
import { type FileAttributeConfigType, FileAttributeConfigTypes } from '@red/domain';
|
||||
import { FilePreviewDialogService } from '../../services/file-preview-dialog.service';
|
||||
import { FileAttributesService } from '@services/entity-services/file-attributes.service';
|
||||
import { ContextComponent } from '@iqser/common-ui';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
|
||||
interface FileAttribute {
|
||||
label: string;
|
||||
@ -17,7 +16,6 @@ interface FileAttribute {
|
||||
}
|
||||
|
||||
interface Context {
|
||||
readonly dossierTemplateName: string;
|
||||
readonly fileAttributes: FileAttribute[];
|
||||
}
|
||||
|
||||
@ -28,28 +26,22 @@ interface Context {
|
||||
})
|
||||
export class DocumentInfoComponent extends ContextComponent<Context> {
|
||||
protected readonly _state = inject(FilePreviewStateService);
|
||||
protected readonly _dossierTemplate = toSignal(inject(DossierTemplatesService).getEntityChanged$(this._state.dossierTemplateId));
|
||||
protected readonly _fileAttributesConfig = toSignal(inject(FileAttributesService).fileAttributesConfig$);
|
||||
protected readonly _attributes = toSignal(
|
||||
this.documentInfoService.fileAttributes$(this._state.fileId, this._state.dossierId, this._state.dossierTemplateId),
|
||||
);
|
||||
protected readonly _fileAttributes = computed(() => {
|
||||
this._fileAttributesConfig();
|
||||
return this._attributes();
|
||||
});
|
||||
|
||||
constructor(
|
||||
fileAttributesService: FileAttributesService,
|
||||
readonly permissionsService: PermissionsService,
|
||||
readonly documentInfoService: DocumentInfoService,
|
||||
private readonly _dialogService: FilePreviewDialogService,
|
||||
private readonly _dossierTemplatesService: DossierTemplatesService,
|
||||
) {
|
||||
super();
|
||||
const fileAttributes$ = combineLatest([this._state.file$, this._state.dossier$, fileAttributesService.fileAttributesConfig$]).pipe(
|
||||
switchMap(([file, dossier]) => this.documentInfoService.fileAttributes$(file.id, dossier.id, dossier.dossierTemplateId)),
|
||||
);
|
||||
|
||||
const dossierTemplateName$ = this._state.dossier$.pipe(
|
||||
switchMap(dossier => this._dossierTemplatesService.getEntityChanged$(dossier.dossierTemplateId)),
|
||||
map(dossierTemplate => dossierTemplate.name),
|
||||
);
|
||||
|
||||
super._initContext({
|
||||
dossierTemplateName: dossierTemplateName$,
|
||||
fileAttributes: fileAttributes$,
|
||||
});
|
||||
}
|
||||
|
||||
edit() {
|
||||
|
||||
@ -26,6 +26,7 @@
|
||||
></div>
|
||||
|
||||
<iqser-popup-filter
|
||||
*ngIf="documentInfoService.hidden()"
|
||||
[actionsTemplate]="annotationFilterActionTemplate"
|
||||
[attr.help-mode-key]="'workload_in_editor'"
|
||||
[primaryFiltersSlug]="'primaryFilters'"
|
||||
|
||||
@ -75,7 +75,6 @@ import { ConfigService } from '@services/config.service';
|
||||
import { ReadableRedactionsService } from '../pdf-viewer/services/readable-redactions.service';
|
||||
import { Roles } from '@users/roles';
|
||||
import { SuggestionsService } from './services/suggestions.service';
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
|
||||
const textActions = [TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE];
|
||||
|
||||
@ -147,32 +146,41 @@ export class FilePreviewScreenComponent
|
||||
const file = this.state.file();
|
||||
this._fileDataService.loadAnnotations(file).then();
|
||||
});
|
||||
|
||||
effect(
|
||||
() => {
|
||||
if (this._documentViewer.loaded()) {
|
||||
this._pageRotationService.clearRotations();
|
||||
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
|
||||
this.viewerReady().then();
|
||||
}
|
||||
},
|
||||
{ allowSignalWrites: true },
|
||||
);
|
||||
|
||||
effect(() => {
|
||||
if (this._documentViewer.pageComplete()) {
|
||||
this.#setExcludedPageStyles();
|
||||
}
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
const selectedText = this._documentViewer.selectedText();
|
||||
const canPerformActions = this.pdfProxyService.canPerformActions();
|
||||
const isCurrentPageExcluded = this.state.file().isPageExcluded(this.pdf.currentPage());
|
||||
|
||||
if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
|
||||
this.pdf.enable(textActions);
|
||||
} else {
|
||||
this.pdf.disable(textActions);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
get changed() {
|
||||
return this._pageRotationService.hasRotations();
|
||||
}
|
||||
|
||||
get #textSelected$() {
|
||||
const textSelected$ = combineLatest([
|
||||
this._documentViewer.textSelected$,
|
||||
toObservable(this.pdfProxyService.canPerformActions, { injector: this._injector }),
|
||||
this.state.file$,
|
||||
]);
|
||||
|
||||
return textSelected$.pipe(
|
||||
tap(([selectedText, canPerformActions, file]) => {
|
||||
const isCurrentPageExcluded = file.isPageExcluded(this.pdf.currentPage());
|
||||
|
||||
if ((selectedText.length > 2 || this._isJapaneseString(selectedText)) && canPerformActions && !isCurrentPageExcluded) {
|
||||
this.pdf.enable(textActions);
|
||||
} else {
|
||||
this.pdf.disable(textActions);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
get #earmarks$() {
|
||||
const isEarmarksViewMode$ = this._viewModeService.viewMode$.pipe(filter(() => this._viewModeService.isEarmarks()));
|
||||
|
||||
@ -440,17 +448,6 @@ export class FilePreviewScreenComponent
|
||||
}
|
||||
|
||||
loadAnnotations() {
|
||||
const documentLoaded$ = this._documentViewer.loaded$.pipe(
|
||||
tap(loaded => {
|
||||
if (!loaded) {
|
||||
return;
|
||||
}
|
||||
this._pageRotationService.clearRotations();
|
||||
this._viewerHeaderService.disable(ROTATION_ACTION_BUTTONS);
|
||||
return this.viewerReady();
|
||||
}),
|
||||
);
|
||||
|
||||
const annotations$ = this._fileDataService.annotations$.pipe(
|
||||
startWith([] as AnnotationWrapper[]),
|
||||
pairwise(),
|
||||
@ -471,7 +468,7 @@ export class FilePreviewScreenComponent
|
||||
),
|
||||
);
|
||||
|
||||
return combineLatest([currentPageAnnotations$, documentLoaded$]).pipe(
|
||||
return combineLatest([currentPageAnnotations$, this._documentViewer.loaded$]).pipe(
|
||||
filter(([, loaded]) => loaded),
|
||||
map(([annotations]) => annotations),
|
||||
map(annotations => this.drawChangedAnnotations(...annotations)),
|
||||
@ -608,23 +605,10 @@ export class FilePreviewScreenComponent
|
||||
.pipe(tap(() => this.#handleDeletedFile()))
|
||||
.subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = combineLatest([this._viewModeService.viewMode$, this.state.file$])
|
||||
.pipe(
|
||||
filter(([viewMode, file]) => viewMode === ViewModes.TEXT_HIGHLIGHTS && !file.hasHighlights),
|
||||
tap(() => this._viewModeService.switchToStandard()),
|
||||
)
|
||||
.subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = this._documentViewer.pageComplete$.subscribe(() => {
|
||||
this.#setExcludedPageStyles();
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this._documentViewer.keyUp$.subscribe($event => {
|
||||
this.handleKeyEvent($event);
|
||||
});
|
||||
|
||||
this.addActiveScreenSubscription = this.#textSelected$.subscribe();
|
||||
|
||||
this.addActiveScreenSubscription = this.#earmarks$.subscribe();
|
||||
this.addActiveScreenSubscription = this.deleteEarmarksOnViewChange$().subscribe();
|
||||
|
||||
|
||||
@ -23,12 +23,15 @@ export class DocumentInfoService {
|
||||
) {
|
||||
this.hidden = computed(() => !this.#show$());
|
||||
this.shown = this.#show$.asReadonly();
|
||||
effect(() => {
|
||||
if (this.shown()) {
|
||||
this._multiSelectService.deactivate();
|
||||
this._excludedPagesService.hide();
|
||||
}
|
||||
});
|
||||
effect(
|
||||
() => {
|
||||
if (this.shown()) {
|
||||
this._multiSelectService.deactivate();
|
||||
this._excludedPagesService.hide();
|
||||
}
|
||||
},
|
||||
{ allowSignalWrites: true },
|
||||
);
|
||||
}
|
||||
|
||||
fileAttributes$(fileId: string, dossierId: string, dossierTemplateId: string) {
|
||||
|
||||
@ -15,6 +15,7 @@ import { TranslateService } from '@ngx-translate/core';
|
||||
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
|
||||
import { DossierDictionariesMapService } from '@services/entity-services/dossier-dictionaries-map.service';
|
||||
import { toSignal } from '@angular/core/rxjs-interop';
|
||||
import { ViewModeService } from './view-mode.service';
|
||||
|
||||
const ONE_MEGABYTE = 1024 * 1024;
|
||||
|
||||
@ -64,6 +65,7 @@ export class FilePreviewStateService {
|
||||
private readonly _dictionariesMapService: DictionariesMapService,
|
||||
private readonly _translateService: TranslateService,
|
||||
private readonly _loadingService: LoadingService,
|
||||
private readonly _viewModeService: ViewModeService,
|
||||
) {
|
||||
const dossiersService = dossiersServiceResolver(_injector, router);
|
||||
this.dossier$ = dossiersService.getEntityChanged$(this.dossierId);
|
||||
@ -82,12 +84,20 @@ export class FilePreviewStateService {
|
||||
this.blob$ = this.#blob$;
|
||||
|
||||
this.dossierDictionary = toSignal(this._dossierDictionariesMapService.watch$(this.dossierId, 'dossier_redaction'));
|
||||
this.#dossierFileChange = toSignal(this.#dossierFilesChange$());
|
||||
this.#dossierFileChange = toSignal(this.#dossierFilesChange$);
|
||||
effect(() => {
|
||||
if (this.#dossierFileChange()) {
|
||||
this._filesService.loadAll(this.dossierId);
|
||||
}
|
||||
});
|
||||
effect(
|
||||
() => {
|
||||
if (this._viewModeService.isEarmarks() && !this.file().hasHighlights) {
|
||||
this._viewModeService.switchToStandard();
|
||||
}
|
||||
},
|
||||
{ allowSignalWrites: true },
|
||||
);
|
||||
}
|
||||
|
||||
get dictionaries(): Dictionary[] {
|
||||
@ -113,6 +123,13 @@ export class FilePreviewStateService {
|
||||
);
|
||||
}
|
||||
|
||||
get #dossierFilesChange$() {
|
||||
return this._dossiersService.dossierFileChanges$.pipe(
|
||||
filter(dossierId => dossierId === this.dossierId),
|
||||
map(() => true),
|
||||
);
|
||||
}
|
||||
|
||||
reloadBlob(): void {
|
||||
this.#reloadBlob$.next(true);
|
||||
}
|
||||
@ -127,13 +144,6 @@ export class FilePreviewStateService {
|
||||
return `${remainingTime} ${seconds}`;
|
||||
}
|
||||
|
||||
#dossierFilesChange$() {
|
||||
return this._dossiersService.dossierFileChanges$.pipe(
|
||||
filter(dossierId => dossierId === this.dossierId),
|
||||
map(() => true),
|
||||
);
|
||||
}
|
||||
|
||||
#downloadOriginalFile(cacheIdentifier: string, wipeCaches = true): Observable<Blob> {
|
||||
const downloadFile$ = this.#getFileToDownload(cacheIdentifier);
|
||||
const obs = wipeCaches ? from(wipeCache('files')) : of({});
|
||||
|
||||
@ -184,7 +184,7 @@ export class PdfProxyService {
|
||||
|
||||
private _addManualRedactionOfType(type: ManualRedactionEntryType) {
|
||||
const selectedQuads: Record<string, Quad[]> = this._pdf.documentViewer.getSelectedTextQuads();
|
||||
const text = this._documentViewer.selectedText;
|
||||
const text = this._documentViewer.selectedText();
|
||||
const manualRedactionEntry = this._getManualRedaction(selectedQuads, text, true);
|
||||
this.manualAnnotationRequested$.next({ manualRedactionEntry, type });
|
||||
}
|
||||
@ -208,7 +208,7 @@ export class PdfProxyService {
|
||||
this._pdf.enable(TEXT_POPUPS_TO_TOGGLE);
|
||||
this._viewerHeaderService.enable(HEADER_ITEMS_TO_TOGGLE);
|
||||
|
||||
if (this._documentViewer.selectedText.length > 2) {
|
||||
if (this._documentViewer.selectedText().length > 2) {
|
||||
this._pdf.enable([TextPopups.ADD_DICTIONARY, TextPopups.ADD_FALSE_POSITIVE]);
|
||||
}
|
||||
} else {
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div [style.visibility]="(documentViewer.loaded$ | async) ? 'visible' : 'hidden'" class="pagination noselect">
|
||||
<div [style.visibility]="documentViewer.loaded() ? 'visible' : 'hidden'" class="pagination noselect">
|
||||
<div (click)="pdf.navigatePreviousPage()">
|
||||
<mat-icon class="chevron-icon" svgIcon="red:nav-prev"></mat-icon>
|
||||
</div>
|
||||
@ -8,7 +8,7 @@
|
||||
#pageInput
|
||||
(change)="pdf.navigateTo(pageInput.value)"
|
||||
[max]="pdf.totalPages()"
|
||||
[value]="pdf.currentPage$ | async"
|
||||
[value]="pdf.currentPage()"
|
||||
class="page-number-input"
|
||||
id="currentPageNumber"
|
||||
min="1"
|
||||
|
||||
@ -66,8 +66,7 @@ export class AnnotationDrawService {
|
||||
const annotations = annotationWrappers
|
||||
?.map(annotation => this._computeAnnotation(annotation, hideSkipped, totalPages, dossierTemplateId))
|
||||
.filterTruthy();
|
||||
const documentLoaded = await firstValueFrom(this._documentViewer.loaded$);
|
||||
if (!documentLoaded) {
|
||||
if (!this._documentViewer.loaded()) {
|
||||
return;
|
||||
}
|
||||
await this._annotationManager.add(annotations);
|
||||
|
||||
@ -1,32 +1,45 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { inject, Injectable, NgZone, Signal, signal } from '@angular/core';
|
||||
import { Core } from '@pdftron/webviewer';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
import { fromEvent, merge, Observable } from 'rxjs';
|
||||
import { debounceTime, filter, map, tap } from 'rxjs/operators';
|
||||
import { fromEvent, Observable } from 'rxjs';
|
||||
import { filter, tap } from 'rxjs/operators';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { PdfViewer } from './pdf-viewer.service';
|
||||
import { UserPreferenceService } from '@users/user-preference.service';
|
||||
import { log, shareDistinctLast, shareLast } from '@iqser/common-ui';
|
||||
import { log } from '@iqser/common-ui';
|
||||
import { stopAndPrevent, stopAndPreventIfNotAllowed } from '../utils/functions';
|
||||
import { RotationType, RotationTypes } from '@red/domain';
|
||||
import { AnnotationToolNames } from '../utils/constants';
|
||||
import { toObservable } from '@angular/core/rxjs-interop';
|
||||
import DocumentViewer = Core.DocumentViewer;
|
||||
import Color = Core.Annotations.Color;
|
||||
import Quad = Core.Math.Quad;
|
||||
|
||||
@Injectable()
|
||||
export class REDDocumentViewer {
|
||||
loaded$: Observable<boolean>;
|
||||
pageComplete$: Observable<boolean>;
|
||||
readonly loaded$: Observable<boolean>;
|
||||
keyUp$: Observable<KeyboardEvent>;
|
||||
textSelected$: Observable<string>;
|
||||
selectedText = '';
|
||||
readonly selectedText: Signal<string>;
|
||||
readonly loaded: Signal<boolean>;
|
||||
readonly pageComplete: Signal<unknown | undefined>;
|
||||
|
||||
#document: DocumentViewer;
|
||||
readonly #loaded = signal(false);
|
||||
readonly #pageComplete = signal<unknown | undefined>(undefined);
|
||||
readonly #selectedText = signal('');
|
||||
|
||||
readonly #logger = inject(NGXLogger);
|
||||
readonly #userPreferenceService = inject(UserPreferenceService);
|
||||
readonly #pdf = inject(PdfViewer);
|
||||
readonly #activatedRoute = inject(ActivatedRoute);
|
||||
readonly #ngZone = inject(NgZone);
|
||||
|
||||
constructor() {
|
||||
this.loaded = this.#loaded.asReadonly();
|
||||
this.loaded$ = toObservable(this.#loaded);
|
||||
this.pageComplete = this.#pageComplete.asReadonly();
|
||||
this.selectedText = this.#selectedText.asReadonly();
|
||||
}
|
||||
|
||||
get PDFDoc() {
|
||||
return this.document?.getPDFDoc();
|
||||
@ -40,30 +53,6 @@ export class REDDocumentViewer {
|
||||
return this.document?.getPageRotation(this.#document.getCurrentPage());
|
||||
}
|
||||
|
||||
get #documentUnloaded$() {
|
||||
const event$ = fromEvent(this.#document, 'documentUnloaded');
|
||||
const toBool$ = event$.pipe(map(() => false));
|
||||
|
||||
return toBool$.pipe(tap(() => this.#logger.info('[PDF] Document unloaded')));
|
||||
}
|
||||
|
||||
get #documentLoaded$() {
|
||||
const event$ = fromEvent(this.#document, 'documentLoaded');
|
||||
const toBool$ = event$.pipe(map(() => true));
|
||||
|
||||
return toBool$.pipe(
|
||||
tap(() =>
|
||||
this.#pdf.runWithCleanup(async () => {
|
||||
await this.#flattenAnnotations();
|
||||
this.#setCurrentPage();
|
||||
this.#setInitialDisplayMode();
|
||||
this.updateTooltipsVisibility();
|
||||
}),
|
||||
),
|
||||
tap(() => this.#logger.info('[PDF] Document loaded')),
|
||||
);
|
||||
}
|
||||
|
||||
get #keyUp$() {
|
||||
return fromEvent<KeyboardEvent>(this.#document, 'keyUp').pipe(
|
||||
tap(stopAndPreventIfNotAllowed),
|
||||
@ -81,22 +70,6 @@ export class REDDocumentViewer {
|
||||
);
|
||||
}
|
||||
|
||||
get #pageComplete$() {
|
||||
return fromEvent(this.#document, 'pageComplete').pipe(debounceTime(300));
|
||||
}
|
||||
|
||||
get #textSelected$(): Observable<string> {
|
||||
return fromEvent<[Quad, string, number]>(this.#document, 'textSelected').pipe(
|
||||
tap(([, selectedText]) => (this.selectedText = selectedText)),
|
||||
tap(([, , pageNumber]) => this.#disableTextPopupIfCompareMode(pageNumber)),
|
||||
map(([, selectedText]) => selectedText),
|
||||
);
|
||||
}
|
||||
|
||||
get #loaded$() {
|
||||
return merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareDistinctLast());
|
||||
}
|
||||
|
||||
clearSelection() {
|
||||
this.#document.clearSelection();
|
||||
this.#pdf.disable('textPopup');
|
||||
@ -120,10 +93,8 @@ export class REDDocumentViewer {
|
||||
|
||||
init(document: DocumentViewer) {
|
||||
this.#document = document;
|
||||
this.loaded$ = this.#loaded$;
|
||||
this.pageComplete$ = this.#pageComplete$.pipe(shareLast());
|
||||
this.#listenForDocEvents();
|
||||
this.keyUp$ = this.#keyUp$;
|
||||
this.textSelected$ = this.#textSelected$;
|
||||
}
|
||||
|
||||
async blob() {
|
||||
@ -172,6 +143,45 @@ export class REDDocumentViewer {
|
||||
}
|
||||
}
|
||||
|
||||
#listenForDocEvents() {
|
||||
this.#document.addEventListener('textSelected', ([, selectedText, pageNumber]: [Quad, string, number]) => {
|
||||
this.#ngZone.run(() => {
|
||||
this.#disableTextPopupIfCompareMode(pageNumber);
|
||||
this.#selectedText.set(selectedText);
|
||||
});
|
||||
});
|
||||
|
||||
this.#document.addEventListener('pageComplete', event => {
|
||||
this.#ngZone.run(() => {
|
||||
setTimeout(() => {
|
||||
this.#pageComplete.set(event);
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
this.#document.addEventListener('documentUnloaded', () => {
|
||||
this.#ngZone.run(() => {
|
||||
this.#logger.info('[PDF] Document unloaded');
|
||||
this.#loaded.set(false);
|
||||
});
|
||||
});
|
||||
|
||||
this.#document.addEventListener('documentLoaded', () => {
|
||||
this.#ngZone.run(() => {
|
||||
this.#logger.info('[PDF] Document loaded');
|
||||
|
||||
this.#pdf.runWithCleanup(() => {
|
||||
this.#flattenAnnotations().then();
|
||||
this.#setCurrentPage();
|
||||
this.#setInitialDisplayMode();
|
||||
this.updateTooltipsVisibility();
|
||||
});
|
||||
|
||||
this.#loaded.set(true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async #flattenAnnotations() {
|
||||
const pdfDoc = await this.PDFDoc;
|
||||
await pdfDoc.flattenAnnotations(false);
|
||||
|
||||
@ -162,7 +162,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
type: ActionTypes.circleBtn,
|
||||
action: () => this._documentInfoService.toggle(),
|
||||
tooltip: _('file-preview.document-info'),
|
||||
ariaExpanded: toObservable(this._documentInfoService.shown, { injector: this._injector }),
|
||||
ariaExpanded: toObservable(this._documentInfoService?.shown, { injector: this._injector }),
|
||||
icon: 'red:status-info',
|
||||
show: !!this._documentInfoService,
|
||||
},
|
||||
@ -171,7 +171,7 @@ export class FileActionsComponent implements OnChanges {
|
||||
type: ActionTypes.circleBtn,
|
||||
action: () => this._excludedPagesService.toggle(),
|
||||
tooltip: _('file-preview.exclude-pages'),
|
||||
ariaExpanded: toObservable(this._excludedPagesService.shown, { injector: this._injector }),
|
||||
ariaExpanded: toObservable(this._excludedPagesService?.shown, { injector: this._injector }),
|
||||
showDot: !!this.file.excludedPages?.length,
|
||||
icon: 'red:exclude-pages',
|
||||
show:
|
||||
|
||||
@ -44,7 +44,7 @@
|
||||
<iqser-circle-button
|
||||
(menuClosed)="expanded = false"
|
||||
(menuOpened)="expanded = true"
|
||||
*ngIf="hiddenButtons.length > 0"
|
||||
*ngIf="hiddenButtons?.length > 0"
|
||||
[attr.aria-expanded]="expanded"
|
||||
[icon]="'iqser:more-actions'"
|
||||
[matMenuTriggerFor]="hiddenButtonsMenu"
|
||||
|
||||
@ -19,9 +19,9 @@ import { MatDialog } from '@angular/material/dialog';
|
||||
styleUrls: ['./expandable-file-actions.component.scss'],
|
||||
})
|
||||
export class ExpandableFileActionsComponent implements OnChanges {
|
||||
@Input({ required: true }) actions: Action[];
|
||||
@Input() maxWidth: number;
|
||||
@Input() minWidth: number;
|
||||
@Input() actions: Action[];
|
||||
@Input() buttonType: CircleButtonType;
|
||||
@Input() tooltipPosition: IqserTooltipPosition;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user