RED-3988: move compare mode to pdf

This commit is contained in:
Dan Percic 2022-05-20 11:27:11 +03:00
parent ba8265c049
commit fe37a78488
8 changed files with 60 additions and 56 deletions

View File

@ -9,8 +9,8 @@
<input <input
#pageInput #pageInput
(change)="pdfViewer.navigateToPage(pageInput.value)" (change)="pdfViewer.navigateToPage(pageInput.value)"
[max]="pdfViewer.totalPages" [max]="reusablePdf.totalPages$ | async"
[value]="pdfViewer.currentPage" [value]="reusablePdf.currentPage$ | async"
class="page-number-input" class="page-number-input"
min="1" min="1"
type="number" type="number"
@ -20,7 +20,7 @@
<div class="separator">/</div> <div class="separator">/</div>
<div> <div>
{{ pdfViewer.totalPages }} {{ reusablePdf.totalPages$ | async }}
</div> </div>
<div (click)="pdfViewer.navigateNextPage()"> <div (click)="pdfViewer.navigateNextPage()">

View File

@ -30,7 +30,6 @@ import { AutoUnsubscribe, ConfirmationDialogInput, ErrorService, LoadingService,
import { PdfViewer } from '../../services/pdf-viewer.service'; import { PdfViewer } from '../../services/pdf-viewer.service';
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker'; import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
import { toPosition } from '../../utils/pdf-calculation.utils'; import { toPosition } from '../../utils/pdf-calculation.utils';
import { ViewModeService } from '../../services/view-mode.service';
import { MultiSelectService } from '../../services/multi-select.service'; import { MultiSelectService } from '../../services/multi-select.service';
import { FilePreviewStateService } from '../../services/file-preview-state.service'; import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators'; import { map, switchMap, tap, withLatestFrom } from 'rxjs/operators';
@ -84,7 +83,6 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
private readonly _annotationManager: REDAnnotationManager, private readonly _annotationManager: REDAnnotationManager,
readonly reusablePdf: ReusablePdfViewer, readonly reusablePdf: ReusablePdfViewer,
readonly stateService: FilePreviewStateService, readonly stateService: FilePreviewStateService,
readonly viewModeService: ViewModeService,
readonly multiSelectService: MultiSelectService, readonly multiSelectService: MultiSelectService,
readonly pdfViewer: PdfViewer, readonly pdfViewer: PdfViewer,
) { ) {
@ -143,7 +141,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
this.instance, this.instance,
file, file,
() => { () => {
this.viewModeService.compareMode = true; this.reusablePdf.openCompareMode();
}, },
() => { () => {
this.pdfViewer.navigateToPage(1); this.pdfViewer.navigateToPage(1);
@ -256,7 +254,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
const file = this.stateService.file; const file = this.stateService.file;
if (this.viewModeService.isCompare && pageNumber % 2 === 0) { if (this.reusablePdf.isCompare && pageNumber % 2 === 0) {
this.instance.UI.disableElements(['textPopup']); this.instance.UI.disableElements(['textPopup']);
} else { } else {
this.instance.UI.enableElements(['textPopup']); this.instance.UI.enableElements(['textPopup']);
@ -285,7 +283,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
private _setInitialDisplayMode() { private _setInitialDisplayMode() {
this.instance.UI.setFitMode('FitPage'); this.instance.UI.setFitMode('FitPage');
const instanceDisplayMode = this.documentViewer.getDisplayModeManager().getDisplayMode(); const instanceDisplayMode = this.documentViewer.getDisplayModeManager().getDisplayMode();
instanceDisplayMode.mode = this.viewModeService.isCompare ? 'Facing' : 'Single'; instanceDisplayMode.mode = this.reusablePdf.isCompare ? 'Facing' : 'Single';
this.documentViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode); this.documentViewer.getDisplayModeManager().setDisplayMode(instanceDisplayMode);
} }
@ -362,7 +360,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
} }
private _configureRectangleAnnotationPopup(annotation: Annotation) { private _configureRectangleAnnotationPopup(annotation: Annotation) {
if (!this.viewModeService.isCompare || annotation.getPageNumber() % 2 === 1) { if (!this.reusablePdf.isCompare || annotation.getPageNumber() % 2 === 1) {
this.instance.UI.annotationPopup.add([ this.instance.UI.annotationPopup.add([
{ {
type: 'actionButton', type: 'actionButton',

View File

@ -129,7 +129,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
private get _canPerformAnnotationActions$() { private get _canPerformAnnotationActions$() {
const viewMode$ = this._viewModeService.viewMode$.pipe(tap(() => this.#deactivateMultiSelect())); const viewMode$ = this._viewModeService.viewMode$.pipe(tap(() => this.#deactivateMultiSelect()));
return combineLatest([this.state.file$, this.state.dossier$, viewMode$, this._viewModeService.compareMode$]).pipe( return combineLatest([this.state.file$, this.state.dossier$, viewMode$, this.reusablePdf.compareMode$]).pipe(
map( map(
([file, dossier, viewMode]) => ([file, dossier, viewMode]) =>
this.permissionsService.canPerformAnnotationActions(file, dossier) && viewMode === 'STANDARD', this.permissionsService.canPerformAnnotationActions(file, dossier) && viewMode === 'STANDARD',
@ -204,7 +204,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
return this._navigateToDossier(); return this._navigateToDossier();
} }
this._viewModeService.compareMode = false;
this._viewModeService.switchToStandard(); this._viewModeService.switchToStandard();
this.state.reloadBlob(); this.state.reloadBlob();

View File

@ -12,7 +12,6 @@ import { firstValueFrom } from 'rxjs';
import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service'; import { DictionariesMapService } from '@services/entity-services/dictionaries-map.service';
import { PdfViewer } from './pdf-viewer.service'; import { PdfViewer } from './pdf-viewer.service';
import { FilePreviewStateService } from './file-preview-state.service'; import { FilePreviewStateService } from './file-preview-state.service';
import { ViewModeService } from './view-mode.service';
import { FileDataService } from './file-data.service'; import { FileDataService } from './file-data.service';
import { SuperTypes } from '@models/file/super-types'; import { SuperTypes } from '@models/file/super-types';
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service'; import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
@ -32,7 +31,6 @@ export class AnnotationDrawService {
private readonly _pdf: PdfViewer, private readonly _pdf: PdfViewer,
private readonly _reusablePdf: ReusablePdfViewer, private readonly _reusablePdf: ReusablePdfViewer,
private readonly _state: FilePreviewStateService, private readonly _state: FilePreviewStateService,
private readonly _viewModeService: ViewModeService,
private readonly _fileDataService: FileDataService, private readonly _fileDataService: FileDataService,
) {} ) {}
@ -135,7 +133,7 @@ export class AnnotationDrawService {
} }
private _computeAnnotation(annotationWrapper: AnnotationWrapper) { private _computeAnnotation(annotationWrapper: AnnotationWrapper) {
const pageNumber = this._viewModeService.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber; const pageNumber = this._reusablePdf.isCompare ? annotationWrapper.pageNumber * 2 - 1 : annotationWrapper.pageNumber;
if (pageNumber > this._reusablePdf.pageCount) { if (pageNumber > this._reusablePdf.pageCount) {
// skip imported annotations from files that have more pages than the current one // skip imported annotations from files that have more pages than the current one
return; return;

View File

@ -1,7 +1,6 @@
import { translateQuads } from '../../../utils'; import { translateQuads } from '../../../utils';
import { AnnotationWrapper } from '@models/file/annotation.wrapper'; import { AnnotationWrapper } from '@models/file/annotation.wrapper';
import { Core } from '@pdftron/webviewer'; import { Core } from '@pdftron/webviewer';
import { ViewModeService } from './view-mode.service';
import { File } from '@red/domain'; import { File } from '@red/domain';
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { ListingService } from '@iqser/common-ui'; import { ListingService } from '@iqser/common-ui';
@ -12,7 +11,6 @@ import { REDAnnotationManager } from '../../shared/components/reusable-pdf-viewe
@Injectable() @Injectable()
export class PdfViewer { export class PdfViewer {
constructor( constructor(
private readonly _viewModeService: ViewModeService,
private readonly _multiSelectService: MultiSelectService, private readonly _multiSelectService: MultiSelectService,
private readonly _reusablePdf: ReusablePdfViewer, private readonly _reusablePdf: ReusablePdfViewer,
private readonly _annotationManager: REDAnnotationManager, private readonly _annotationManager: REDAnnotationManager,
@ -20,16 +18,11 @@ export class PdfViewer {
) {} ) {}
get paginationOffset() { get paginationOffset() {
return this._viewModeService.isCompare ? 2 : 1; return this._reusablePdf.isCompare ? 2 : 1;
} }
get currentPage() { get currentPage() {
return this._viewModeService.isCompare ? Math.ceil(this._currentInternalPage / 2) : this._currentInternalPage; return this._reusablePdf.isCompare ? Math.ceil(this._currentInternalPage / 2) : this._currentInternalPage;
}
get totalPages() {
const pageCount = this._reusablePdf.pageCount;
return this._viewModeService.isCompare ? Math.ceil(pageCount / 2) : pageCount;
} }
private get _currentInternalPage() { private get _currentInternalPage() {

View File

@ -7,17 +7,14 @@ import { shareDistinctLast } from '@iqser/common-ui';
@Injectable() @Injectable()
export class ViewModeService { export class ViewModeService {
readonly viewMode$: Observable<ViewMode>; readonly viewMode$: Observable<ViewMode>;
readonly compareMode$: Observable<boolean>;
readonly isRedacted$: Observable<boolean>; readonly isRedacted$: Observable<boolean>;
readonly isStandard$: Observable<boolean>; readonly isStandard$: Observable<boolean>;
readonly isDelta$: Observable<boolean>; readonly isDelta$: Observable<boolean>;
private readonly _viewMode$ = new BehaviorSubject<ViewMode>('STANDARD'); readonly #viewMode$ = new BehaviorSubject<ViewMode>('STANDARD');
private readonly _compareMode$ = new BehaviorSubject<boolean>(false);
constructor() { constructor() {
this.viewMode$ = this._viewMode$.asObservable(); this.viewMode$ = this.#viewMode$.asObservable();
this.compareMode$ = this._compareMode$.asObservable();
this.isRedacted$ = this._is('REDACTED'); this.isRedacted$ = this._is('REDACTED');
this.isStandard$ = this._is('STANDARD'); this.isStandard$ = this._is('STANDARD');
this.isDelta$ = this._is('DELTA'); this.isDelta$ = this._is('DELTA');
@ -28,35 +25,27 @@ export class ViewModeService {
} }
get viewMode() { get viewMode() {
return this._viewMode$.value; return this.#viewMode$.value;
} }
set viewMode(mode: ViewMode) { set viewMode(mode: ViewMode) {
this._viewMode$.next(mode); this.#viewMode$.next(mode);
} }
get isStandard() { get isStandard() {
return this._viewMode$.value === 'STANDARD'; return this.#viewMode$.value === 'STANDARD';
} }
get isDelta() { get isDelta() {
return this._viewMode$.value === 'DELTA'; return this.#viewMode$.value === 'DELTA';
} }
get isRedacted() { get isRedacted() {
return this._viewMode$.value === 'REDACTED'; return this.#viewMode$.value === 'REDACTED';
} }
get isTextHighlights() { get isTextHighlights() {
return this._viewMode$.value === 'TEXT_HIGHLIGHTS'; return this.#viewMode$.value === 'TEXT_HIGHLIGHTS';
}
get isCompare() {
return this._compareMode$.value;
}
set compareMode(compareMode: boolean) {
this._compareMode$.next(compareMode);
} }
switchToStandard() { switchToStandard() {
@ -76,7 +65,7 @@ export class ViewModeService {
} }
private _switchTo(mode: ViewMode) { private _switchTo(mode: ViewMode) {
this._viewMode$.next(mode); this.#viewMode$.next(mode);
} }
private _is(mode: ViewMode) { private _is(mode: ViewMode) {

View File

@ -6,7 +6,6 @@ import { BASE_HREF } from '../../../tokens';
import { PdfViewer } from './pdf-viewer.service'; import { PdfViewer } from './pdf-viewer.service';
import { TooltipsService } from './tooltips.service'; import { TooltipsService } from './tooltips.service';
import { environment } from '@environments/environment'; import { environment } from '@environments/environment';
import { ViewModeService } from './view-mode.service';
import { FilePreviewStateService } from './file-preview-state.service'; import { FilePreviewStateService } from './file-preview-state.service';
import { PageRotationService } from './page-rotation.service'; import { PageRotationService } from './page-rotation.service';
import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service'; import { ReusablePdfViewer } from '../../shared/components/reusable-pdf-viewer/reusable-pdf-viewer.service';
@ -36,7 +35,6 @@ export class ViewerHeaderConfigService {
private readonly _pdfViewer: PdfViewer, private readonly _pdfViewer: PdfViewer,
private readonly _reusablePdf: ReusablePdfViewer, private readonly _reusablePdf: ReusablePdfViewer,
private readonly _tooltipsService: TooltipsService, private readonly _tooltipsService: TooltipsService,
private readonly _viewModeService: ViewModeService,
private readonly _stateService: FilePreviewStateService, private readonly _stateService: FilePreviewStateService,
) {} ) {}
@ -164,7 +162,7 @@ export class ViewerHeaderConfigService {
} }
private async _closeCompareMode() { private async _closeCompareMode() {
this._viewModeService.compareMode = false; this._reusablePdf.closeCompareMode();
const pdfNet = this._reusablePdf.PDFNet; const pdfNet = this._reusablePdf.PDFNet;
await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null); await pdfNet.initialize(environment.licenseKey ? atob(environment.licenseKey) : null);
const blob = await this._stateService.blob; const blob = await this._stateService.blob;

View File

@ -3,19 +3,19 @@ import WebViewer, { Core, WebViewerInstance, WebViewerOptions } from '@pdftron/w
import { environment } from '@environments/environment'; import { environment } from '@environments/environment';
import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens'; import { BASE_HREF_FN, BaseHrefFn } from '../../../../tokens';
import { File } from '@red/domain'; import { File } from '@red/domain';
import { ErrorService, shareDistinctLast, shareLast } from '@iqser/common-ui'; import { ErrorService, log, shareDistinctLast, shareLast } from '@iqser/common-ui';
import { ActivatedRoute } from '@angular/router'; import { ActivatedRoute } from '@angular/router';
import { debounceTime, map, tap } from 'rxjs/operators'; import { debounceTime, map, tap } from 'rxjs/operators';
import { fromEvent, merge, Observable, Subject } from 'rxjs'; import { BehaviorSubject, combineLatest, fromEvent, merge, Observable } from 'rxjs';
import { ConfigService } from '@services/config.service'; import { ConfigService } from '@services/config.service';
import { NGXLogger } from 'ngx-logger'; import { NGXLogger } from 'ngx-logger';
import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, USELESS_ELEMENTS } from './constants'; import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, USELESS_ELEMENTS } from './constants';
import { Rgb } from '@shared/components/reusable-pdf-viewer/types'; import { Rgb } from '@shared/components/reusable-pdf-viewer/types';
import DocumentViewer = Core.DocumentViewer;
import AnnotationManager = Core.AnnotationManager; import AnnotationManager = Core.AnnotationManager;
import TextTool = Core.Tools.TextTool; import TextTool = Core.Tools.TextTool;
import Annotation = Core.Annotations.Annotation; import Annotation = Core.Annotations.Annotation;
import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation; import TextHighlightAnnotation = Core.Annotations.TextHighlightAnnotation;
import DocumentViewer = Core.DocumentViewer;
@Injectable({ @Injectable({
providedIn: 'root', providedIn: 'root',
@ -30,9 +30,11 @@ export class ReusablePdfViewer {
documentLoaded$: Observable<boolean>; documentLoaded$: Observable<boolean>;
pageComplete$: Observable<unknown>; pageComplete$: Observable<unknown>;
compareMode$: Observable<boolean>;
totalPages$: Observable<number>;
#instance: WebViewerInstance; #instance: WebViewerInstance;
#documentClosed$ = new Subject<boolean>(); readonly #compareMode$ = new BehaviorSubject(false);
constructor( constructor(
@Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn, @Inject(BASE_HREF_FN) private readonly _convertPath: BaseHrefFn,
@ -58,6 +60,10 @@ export class ReusablePdfViewer {
return this.#instance.Core.PDFNet; return this.#instance.Core.PDFNet;
} }
get isCompare() {
return this.#compareMode$.value;
}
get pageCount() { get pageCount() {
try { try {
return this.#instance.Core.documentViewer.getPageCount(); return this.#instance.Core.documentViewer.getPageCount();
@ -67,17 +73,30 @@ export class ReusablePdfViewer {
} }
} }
get #totalPages$() {
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged');
const pageCount$ = layoutChanged$.pipe(map(() => this.pageCount));
const docChanged$ = combineLatest([pageCount$, this.compareMode$]).pipe(log('total pages'));
return docChanged$.pipe(map(([pageCount, isCompare]) => (isCompare ? Math.ceil(pageCount / 2) : pageCount)));
}
get #pageComplete$() { get #pageComplete$() {
return fromEvent(this.documentViewer, 'pageComplete').pipe(debounceTime(300)); return fromEvent(this.documentViewer, 'pageComplete').pipe(debounceTime(300));
} }
get #documentLoaded$() { get #documentLoaded$() {
const event$ = fromEvent(this.documentViewer, this.#instance.UI.Events.DOCUMENT_LOADED); const event$ = fromEvent(this.documentViewer, 'documentLoaded');
const toBool$ = event$.pipe(map(() => true)); const toBool$ = event$.pipe(map(() => true));
const updateCurrentPage$ = toBool$.pipe(tap(() => this.#setCurrentPage())); const updateCurrentPage$ = toBool$.pipe(tap(() => this.#setCurrentPage()));
const log = tap<boolean>(() => this._logger.info('[PDF] Document loaded')); return updateCurrentPage$.pipe(tap(() => this._logger.info('[PDF] Document loaded')));
return updateCurrentPage$.pipe(log); }
get #documentUnloaded$() {
const event$ = fromEvent(this.documentViewer, 'documentUnloaded');
const toBool$ = event$.pipe(map(() => false));
return toBool$.pipe(tap(() => this._logger.info('[PDF] Document unloaded')));
} }
async init(htmlElement: HTMLElement) { async init(htmlElement: HTMLElement) {
@ -87,8 +106,10 @@ export class ReusablePdfViewer {
this.documentViewer = this.#instance.Core.documentViewer; this.documentViewer = this.#instance.Core.documentViewer;
this.annotationManager = this.#instance.Core.annotationManager; this.annotationManager = this.#instance.Core.annotationManager;
this.documentLoaded$ = merge(this.#documentClosed$, this.#documentLoaded$).pipe(shareLast()); this.documentLoaded$ = merge(this.#documentUnloaded$, this.#documentLoaded$).pipe(shareLast());
this.pageComplete$ = this.#pageComplete$; this.compareMode$ = this.#compareMode$.asObservable();
this.pageComplete$ = this.#pageComplete$.pipe(shareLast());
this.totalPages$ = this.#totalPages$.pipe(shareLast());
this.#setSelectionMode(); this.#setSelectionMode();
this.#configureElements(); this.#configureElements();
this.#disableHotkeys(); this.#disableHotkeys();
@ -107,10 +128,18 @@ export class ReusablePdfViewer {
closeDocument() { closeDocument() {
this._logger.info('[PDF] Closing document'); this._logger.info('[PDF] Closing document');
this.#documentClosed$.next(false); this.closeCompareMode();
this.documentViewer.closeDocument(); this.documentViewer.closeDocument();
} }
openCompareMode() {
this.#compareMode$.next(true);
}
closeCompareMode() {
this.#compareMode$.next(false);
}
async lockDocument() { async lockDocument() {
const document = await this.PDFDoc; const document = await this.PDFDoc;
if (!document) { if (!document) {