RED-3837: finish select annotations on multiple pages
This commit is contained in:
parent
c1e3d342fa
commit
85f9cd85cf
@ -9,6 +9,7 @@ import {
|
|||||||
Debounce,
|
Debounce,
|
||||||
ErrorService,
|
ErrorService,
|
||||||
FilterService,
|
FilterService,
|
||||||
|
List,
|
||||||
LoadingService,
|
LoadingService,
|
||||||
NestedFilter,
|
NestedFilter,
|
||||||
OnAttach,
|
OnAttach,
|
||||||
@ -26,7 +27,7 @@ import { File, ViewMode, ViewModes } from '@red/domain';
|
|||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { combineLatest, firstValueFrom, from, of, pairwise } from 'rxjs';
|
import { combineLatest, firstValueFrom, from, of, pairwise } from 'rxjs';
|
||||||
import { UserPreferenceService } from '@services/user-preference.service';
|
import { UserPreferenceService } from '@services/user-preference.service';
|
||||||
import { download, handleFilterDelta } from '../../utils';
|
import { byId, byPage, download, handleFilterDelta } from '../../utils';
|
||||||
import { FilesService } from '@services/files/files.service';
|
import { FilesService } from '@services/files/files.service';
|
||||||
import { FileManagementService } from '@services/files/file-management.service';
|
import { FileManagementService } from '@services/files/file-management.service';
|
||||||
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
import { catchError, filter, map, startWith, switchMap, tap } from 'rxjs/operators';
|
||||||
@ -361,8 +362,15 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
return this.viewerReady();
|
return this.viewerReady();
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentPageAnnotations$ = combineLatest([this.pdf.currentPage$, this._fileDataService.annotations$]).pipe(
|
const currentPageAnnotations$ = combineLatest([this.pdf.currentPage$, this._fileDataService.annotations$]).pipe(
|
||||||
map(([page, annotations]) => annotations.filter(annotation => annotation.pageNumber === page)),
|
map(([, annotations]) => annotations),
|
||||||
|
startWith([] as List<AnnotationWrapper>),
|
||||||
|
pairwise(),
|
||||||
|
map(([oldAnnotations, newAnnotations]) => {
|
||||||
|
const page = this.pdf.currentPage;
|
||||||
|
return [oldAnnotations.filter(byPage(page)), newAnnotations.filter(byPage(page))] as const;
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
let start;
|
let start;
|
||||||
@ -370,8 +378,6 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
filter(([, loaded]) => loaded),
|
filter(([, loaded]) => loaded),
|
||||||
tap(() => (start = new Date().getTime())),
|
tap(() => (start = new Date().getTime())),
|
||||||
map(([annotations]) => annotations),
|
map(([annotations]) => annotations),
|
||||||
startWith([] as AnnotationWrapper[]),
|
|
||||||
pairwise(),
|
|
||||||
tap(annotations => this.deleteAnnotations(...annotations)),
|
tap(annotations => this.deleteAnnotations(...annotations)),
|
||||||
switchMap(annotations => this.drawChangedAnnotations(...annotations)),
|
switchMap(annotations => this.drawChangedAnnotations(...annotations)),
|
||||||
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)),
|
tap(([, newAnnotations]) => this.#highlightSelectedAnnotations(newAnnotations)),
|
||||||
@ -381,28 +387,20 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
}
|
}
|
||||||
|
|
||||||
deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
deleteAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||||
const annotationsToDelete = oldAnnotations.filter(
|
const annotationsToDelete = oldAnnotations.filter(oldAnnotation => !newAnnotations.some(byId(oldAnnotation.id)));
|
||||||
oldAnnotation => !newAnnotations.some(newAnnotation => newAnnotation.id === oldAnnotation.id),
|
|
||||||
);
|
|
||||||
if (annotationsToDelete.length === 0) {
|
if (annotationsToDelete.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._logger.info('[ANNOTATIONS] To delete: ', annotationsToDelete);
|
const toDelete = annotationsToDelete.filter(byPage(this.pdf.currentPage));
|
||||||
this._annotationManager.delete(annotationsToDelete);
|
|
||||||
|
this._logger.info('[ANNOTATIONS] To delete: ', toDelete);
|
||||||
|
this._annotationManager.delete(toDelete);
|
||||||
}
|
}
|
||||||
|
|
||||||
async drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
async drawChangedAnnotations(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||||
let annotationsToDraw: readonly AnnotationWrapper[];
|
const annotationsToDraw = this.#getAnnotationsToDraw(oldAnnotations, newAnnotations);
|
||||||
const annotations = this._annotationManager.annotations;
|
|
||||||
const ann = annotations.map(a => oldAnnotations.some(oldAnnotation => oldAnnotation.id === a.Id));
|
|
||||||
const hasAnnotations = ann.filter(a => !!a).length > 0;
|
|
||||||
|
|
||||||
if (hasAnnotations) {
|
|
||||||
annotationsToDraw = this.#getAnnotationsToDraw(newAnnotations, oldAnnotations);
|
|
||||||
} else {
|
|
||||||
annotationsToDraw = newAnnotations;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (annotationsToDraw.length === 0) {
|
if (annotationsToDraw.length === 0) {
|
||||||
return [oldAnnotations, newAnnotations];
|
return [oldAnnotations, newAnnotations];
|
||||||
@ -423,6 +421,18 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
return ActionsHelpModeKeys[type];
|
return ActionsHelpModeKeys[type];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#getAnnotationsToDraw(oldAnnotations: AnnotationWrapper[], newAnnotations: AnnotationWrapper[]) {
|
||||||
|
const annotations = this._annotationManager.annotations;
|
||||||
|
const ann = annotations.map(a => oldAnnotations.some(byId(a.Id)));
|
||||||
|
const hasAnnotations = ann.filter(a => !!a).length > 0;
|
||||||
|
|
||||||
|
if (hasAnnotations) {
|
||||||
|
return this.#findAnnotationsToDraw(newAnnotations, oldAnnotations);
|
||||||
|
} else {
|
||||||
|
return newAnnotations;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#rebuildFilters() {
|
#rebuildFilters() {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
|
|
||||||
@ -457,7 +467,7 @@ export class FilePreviewScreenComponent extends AutoUnsubscribe implements OnIni
|
|||||||
this._changeDetectorRef.markForCheck();
|
this._changeDetectorRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
#getAnnotationsToDraw(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
#findAnnotationsToDraw(newAnnotations: AnnotationWrapper[], oldAnnotations: AnnotationWrapper[]) {
|
||||||
return newAnnotations.filter(newAnnotation => {
|
return newAnnotations.filter(newAnnotation => {
|
||||||
const oldAnnotation = oldAnnotations.find(annotation => annotation.id === newAnnotation.id);
|
const oldAnnotation = oldAnnotations.find(annotation => annotation.id === newAnnotation.id);
|
||||||
if (!oldAnnotation) {
|
if (!oldAnnotation) {
|
||||||
|
|||||||
@ -26,19 +26,15 @@ export class AnnotationsListingService extends ListingService<AnnotationWrapper>
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
selectAnnotations(annotation: AnnotationWrapper);
|
selectAnnotations(annotations: AnnotationWrapper[] | AnnotationWrapper) {
|
||||||
selectAnnotations(annotations?: AnnotationWrapper[]);
|
|
||||||
selectAnnotations(annotations?: AnnotationWrapper[] | AnnotationWrapper) {
|
|
||||||
if (!annotations) {
|
|
||||||
return this._annotationManager.deselect();
|
|
||||||
}
|
|
||||||
annotations = Array.isArray(annotations) ? annotations : [annotations];
|
annotations = Array.isArray(annotations) ? annotations : [annotations];
|
||||||
|
const pageNumber = annotations[annotations.length - 1].pageNumber;
|
||||||
|
|
||||||
const annotationsToSelect = this._multiSelectService.isActive ? [...this.selected, ...annotations] : annotations;
|
const annotationsToSelect = this._multiSelectService.isActive ? [...this.selected, ...annotations] : annotations;
|
||||||
this.#selectAnnotations(annotationsToSelect);
|
this.#selectAnnotations(annotationsToSelect, pageNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
#selectAnnotations(annotations: AnnotationWrapper[]) {
|
#selectAnnotations(annotations: AnnotationWrapper[], pageNumber: number) {
|
||||||
if (!annotations.length) {
|
if (!annotations.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -47,8 +43,6 @@ export class AnnotationsListingService extends ListingService<AnnotationWrapper>
|
|||||||
this._annotationManager.deselect();
|
this._annotationManager.deselect();
|
||||||
}
|
}
|
||||||
|
|
||||||
const pageNumber = annotations[0].pageNumber;
|
|
||||||
|
|
||||||
if (pageNumber === this._pdf.currentPage) {
|
if (pageNumber === this._pdf.currentPage) {
|
||||||
return this._annotationManager.jumpAndSelect(annotations);
|
return this._annotationManager.jumpAndSelect(annotations);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import { shareDistinctLast } from '@iqser/common-ui';
|
|||||||
import { toPosition } from '../utils/pdf-calculation.utils';
|
import { toPosition } from '../utils/pdf-calculation.utils';
|
||||||
import { MultiSelectService } from './multi-select.service';
|
import { MultiSelectService } from './multi-select.service';
|
||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { filter, map, tap } from 'rxjs/operators';
|
import { map, tap } from 'rxjs/operators';
|
||||||
import { HeaderElements, TextPopups } from '../utils/constants';
|
import { HeaderElements, TextPopups } from '../utils/constants';
|
||||||
import { FileDataService } from './file-data.service';
|
import { FileDataService } from './file-data.service';
|
||||||
import { ViewerHeaderService } from '../../pdf-viewer/services/viewer-header.service';
|
import { ViewerHeaderService } from '../../pdf-viewer/services/viewer-header.service';
|
||||||
@ -89,7 +89,6 @@ export class PdfProxyService {
|
|||||||
|
|
||||||
get #annotationSelected$() {
|
get #annotationSelected$() {
|
||||||
return this._annotationManager.annotationSelected$.pipe(
|
return this._annotationManager.annotationSelected$.pipe(
|
||||||
filter(([, action]) => !(this._multiSelectService.isActive && action === 'deselected')),
|
|
||||||
map(value => this.#processSelectedAnnotations(...value)),
|
map(value => this.#processSelectedAnnotations(...value)),
|
||||||
tap(annotations => this.handleAnnotationSelected(annotations)),
|
tap(annotations => this.handleAnnotationSelected(annotations)),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
|||||||
import { fromEvent, Observable } from 'rxjs';
|
import { fromEvent, Observable } from 'rxjs';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { asList, getId, isStringOrWrapper } from '../utils/functions';
|
import { asList, getId, isStringOrWrapper } from '../utils/functions';
|
||||||
|
import { getLast } from '@utils/functions';
|
||||||
import AnnotationManager = Core.AnnotationManager;
|
import AnnotationManager = Core.AnnotationManager;
|
||||||
import Annotation = Core.Annotations.Annotation;
|
import Annotation = Core.Annotations.Annotation;
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ export class REDAnnotationManager {
|
|||||||
jumpAndSelect(annotations: List | List<AnnotationWrapper>) {
|
jumpAndSelect(annotations: List | List<AnnotationWrapper>) {
|
||||||
const annotationsFromViewer = this.get(annotations);
|
const annotationsFromViewer = this.get(annotations);
|
||||||
|
|
||||||
this.#manager.jumpToAnnotation(annotationsFromViewer[0]);
|
this.#manager.jumpToAnnotation(getLast(annotationsFromViewer));
|
||||||
this.#manager.selectAnnotations(annotationsFromViewer);
|
this.#manager.selectAnnotations(annotationsFromViewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { File, IHeaderElement } from '@red/domain';
|
|||||||
import { ErrorService, shareDistinctLast } from '@iqser/common-ui';
|
import { ErrorService, shareDistinctLast } from '@iqser/common-ui';
|
||||||
import { ActivatedRoute } from '@angular/router';
|
import { ActivatedRoute } from '@angular/router';
|
||||||
import { map, startWith } from 'rxjs/operators';
|
import { map, startWith } from 'rxjs/operators';
|
||||||
import { BehaviorSubject, combineLatest, fromEvent, Observable } from 'rxjs';
|
import { BehaviorSubject, combineLatest, fromEvent, Observable, switchMap } 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, SEARCH_OPTIONS, USELESS_ELEMENTS } from '../utils/constants';
|
import { DISABLED_HOTKEYS, DOCUMENT_LOADING_ERROR, SEARCH_OPTIONS, USELESS_ELEMENTS } from '../utils/constants';
|
||||||
@ -70,8 +70,9 @@ export class PdfViewer {
|
|||||||
|
|
||||||
get pageCount() {
|
get pageCount() {
|
||||||
try {
|
try {
|
||||||
return this.#instance.Core.documentViewer.getPageCount();
|
return this.documentViewer.getPageCount();
|
||||||
} catch {
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
// might throw Error: getPageCount was called before the 'documentLoaded' event
|
// might throw Error: getPageCount was called before the 'documentLoaded' event
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -83,7 +84,8 @@ export class PdfViewer {
|
|||||||
|
|
||||||
get #totalPages$() {
|
get #totalPages$() {
|
||||||
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged').pipe(startWith(''));
|
const layoutChanged$ = fromEvent(this.documentViewer, 'layoutChanged').pipe(startWith(''));
|
||||||
const pageCount$ = layoutChanged$.pipe(map(() => this.pageCount));
|
const docLoaded$ = fromEvent(this.documentViewer, 'documentLoaded');
|
||||||
|
const pageCount$ = docLoaded$.pipe(switchMap(() => layoutChanged$.pipe(map(() => this.pageCount))));
|
||||||
const docChanged$ = combineLatest([pageCount$, this.compareMode$]);
|
const docChanged$ = combineLatest([pageCount$, this.compareMode$]);
|
||||||
|
|
||||||
return docChanged$.pipe(map(([pageCount]) => this.#adjustPage(pageCount)));
|
return docChanged$.pipe(map(([pageCount]) => this.#adjustPage(pageCount)));
|
||||||
@ -99,10 +101,7 @@ export class PdfViewer {
|
|||||||
|
|
||||||
get #pageChanged$() {
|
get #pageChanged$() {
|
||||||
const page$ = fromEvent<number>(this.documentViewer, 'pageNumberUpdated');
|
const page$ = fromEvent<number>(this.documentViewer, 'pageNumberUpdated');
|
||||||
return page$.pipe(
|
return page$.pipe(map(page => this.#adjustPage(page)));
|
||||||
// tap(() => this._annotationManager.deselect()),
|
|
||||||
map(page => this.#adjustPage(page)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
navigateTo(page: string | number) {
|
navigateTo(page: string | number) {
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { List } from '@iqser/common-ui';
|
import type { List } from '@iqser/common-ui';
|
||||||
|
import type { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
|
|
||||||
export function groupBy(xs: List<unknown>, key: string) {
|
export function groupBy(xs: List<unknown>, key: string) {
|
||||||
return xs.reduce((rv, x) => {
|
return xs.reduce((rv, x) => {
|
||||||
@ -92,3 +93,10 @@ export function compareLists(l1: string[], l2: string[]) {
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const byPage = (page: number) => (annotation: AnnotationWrapper) => annotation.pageNumber === page;
|
||||||
|
export const byId = (id: string) => (annotation: AnnotationWrapper) => annotation.annotationId === id;
|
||||||
|
|
||||||
|
export function getLast<T>(list: List<T>) {
|
||||||
|
return list[list.length - 1];
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user