make page rotate

This commit is contained in:
Dan Percic 2022-03-04 20:16:27 +02:00
parent 8c5276fc1d
commit a80f54cdbb
7 changed files with 67 additions and 49 deletions

View File

@ -7,11 +7,12 @@
class="page-wrapper"
>
<mat-icon [svgIcon]="showDottedIcon ? 'red:excluded-page' : 'red:page'"></mat-icon>
<div class="text">
{{ number }}
</div>
<div class="text">{{ number }}</div>
<div *ngIf="activeSelection" class="dot"></div>
<div *ngIf="rotation$ | async" [class.not-applied]="notAppliedRotation$ | async" class="rotated">
<div *ngIf="isRotated$ | async" class="rotated">
<mat-icon svgIcon="red:rotation"></mat-icon>
</div>
</div>

View File

@ -57,22 +57,13 @@
top: 8px;
display: flex;
justify-content: center;
background-color: var(--iqser-white);
color: var(--iqser-accent);
background-color: var(--iqser-primary);
color: var(--iqser-white);
mat-icon {
width: 12px;
height: 10px;
opacity: 50%;
}
&.not-applied {
background-color: var(--iqser-primary);
color: var(--iqser-white);
mat-icon {
opacity: 100%;
}
opacity: 100%;
}
}
}

View File

@ -1,11 +1,21 @@
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnDestroy, Output } from '@angular/core';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
} from '@angular/core';
import { PermissionsService } from '@services/permissions.service';
import { ConfigService } from '@services/config.service';
import { ViewedPagesService } from '@services/entity-services/viewed-pages.service';
import { IViewedPage } from '@red/domain';
import { AutoUnsubscribe } from '@iqser/common-ui';
import { FilePreviewStateService } from '../../services/file-preview-state.service';
import { BehaviorSubject, firstValueFrom } from 'rxjs';
import { firstValueFrom, Observable } from 'rxjs';
import { PageRotationService } from '../../services/page-rotation.service';
@Component({
@ -14,7 +24,7 @@ import { PageRotationService } from '../../services/page-rotation.service';
styleUrls: ['./page-indicator.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy, OnChanges {
export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy, OnChanges, OnInit {
@Input() active = false;
@Input() showDottedIcon = false;
@Input() number: number;
@ -25,9 +35,7 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy
pageReadTimeout: number = null;
read = false;
rotation$ = new BehaviorSubject(true);
notAppliedRotation$ = new BehaviorSubject(true);
isRotated$: Observable<boolean>;
constructor(
private readonly _viewedPagesService: ViewedPagesService,
@ -52,6 +60,10 @@ export class PageIndicatorComponent extends AutoUnsubscribe implements OnDestroy
return this._stateService.fileId;
}
ngOnInit() {
this.isRotated$ = this._pageRotationService.isRotated(this.number);
}
ngOnChanges() {
this._setReadState();
return this.handlePageRead();

View File

@ -27,7 +27,7 @@ import { AnnotationActionsService } from '../../services/annotation-actions.serv
import { UserPreferenceService } from '@services/user-preference.service';
import { BASE_HREF } from '../../../../../../tokens';
import { ConfigService } from '@services/config.service';
import { AutoUnsubscribe, ConfirmationDialogInput, LoadingService } from '@iqser/common-ui';
import { AutoUnsubscribe, ConfirmationDialogInput, LoadingService, log } from '@iqser/common-ui';
import { DossiersDialogService } from '../../../../services/dossiers-dialog.service';
import { loadCompareDocumentWrapper } from '../../../../utils/compare-mode.utils';
import { PdfViewerUtils } from '../../../../utils/pdf-viewer.utils';
@ -393,7 +393,10 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
cursor: pointer;
margin: 0 12px;
`;
paragraph.addEventListener('click', () => this._pageRotationService.applyRotation());
paragraph.addEventListener('click', () => {
this._pageRotationService.applyRotation();
this._showRotationConfirmationButtons();
});
return paragraph;
},
};
@ -411,7 +414,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
opacity: 0.7;
`;
paragraph.addEventListener('click', () => {
this._pageRotationService.rotations.clear();
this._pageRotationService.clearRotations();
this._showRotationConfirmationButtons();
});
return paragraph;
@ -452,6 +455,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
img: this._convertPath('/assets/icons/general/rotate-left.svg'),
onClick: () => {
this._pageRotationService.addRotation(this.utils.currentPage, RotationTypes.LEFT);
this.documentViewer.rotateCounterClockwise(this.utils.currentPage);
this._showRotationConfirmationButtons();
},
},
@ -462,6 +466,7 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
img: this._convertPath('/assets/icons/general/rotate-right.svg'),
onClick: () => {
this._pageRotationService.addRotation(this.utils.currentPage, RotationTypes.RIGHT);
this.documentViewer.rotateClockwise(this.utils.currentPage);
this._showRotationConfirmationButtons();
},
},
@ -514,17 +519,12 @@ export class PdfViewerComponent extends AutoUnsubscribe implements OnInit, OnCha
}
private _showRotationConfirmationButtons() {
const rotationValues = Array.from(this._pageRotationService.rotations.values());
const rotationElements = [elements.APPLY_ROTATION, elements.DISCARD_ROTATION];
if (rotationValues.length > 0 && rotationValues.find(v => v !== 0)) {
if (!this._pageRotationService.isRotated) {
this.instance.UI.enableElements(rotationElements);
this._pageRotationService.setRotation(true);
}
if (this._pageRotationService.hasRotations()) {
this.instance.UI.enableElements(rotationElements);
} else {
this.instance.UI.disableElements(rotationElements);
this._pageRotationService.setRotation(false);
}
}

View File

@ -1,15 +1,16 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject, firstValueFrom, Observable, pairwise, switchMap } from 'rxjs';
import { BehaviorSubject, firstValueFrom, from, Observable, pairwise, switchMap } from 'rxjs';
import { FileDataModel } from '@models/file/file-data.model';
import { Dossier, File } from '@red/domain';
import { DossiersService } from '@services/entity-services/dossiers.service';
import { ActivatedRoute } from '@angular/router';
import { FilesMapService } from '@services/entity-services/files-map.service';
import { PermissionsService } from '../../../../../services/permissions.service';
import { boolFactory, shareLast } from '@iqser/common-ui';
import { boolFactory } from '@iqser/common-ui';
import { filter, startWith } from 'rxjs/operators';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { DOSSIER_ID, FILE_ID } from '@utils/constants';
import { wipeFilesCache } from '../../../../../../../../../libs/red-cache/src';
@Injectable()
export class FilePreviewStateService {
@ -71,11 +72,11 @@ export class FilePreviewStateService {
pairwise(),
filter(([oldFile, newFile]) => oldFile?.cacheIdentifier !== newFile.cacheIdentifier),
switchMap(([, newFile]) => this.#downloadOriginalFile(newFile.cacheIdentifier)),
shareLast(),
);
}
#downloadOriginalFile(cacheIdentifier: string): Observable<Blob> {
return this._fileManagementService.downloadOriginalFile(this.dossierId, this.fileId, 'body', cacheIdentifier);
#downloadOriginalFile(cacheIdentifier?: string): Observable<Blob> {
const downloadFile = this._fileManagementService.downloadOriginalFile(this.dossierId, this.fileId, 'body', cacheIdentifier);
return from(wipeFilesCache()).pipe(switchMap(() => downloadFile));
}
}

View File

@ -4,11 +4,12 @@ import { PermissionsService } from '@services/permissions.service';
import { RotationType } from '@red/domain';
import { FileManagementService } from '@services/entity-services/file-management.service';
import { FilePreviewStateService } from './file-preview-state.service';
import { distinctUntilChanged, map } from 'rxjs/operators';
import { log } from '@iqser/common-ui';
@Injectable()
export class PageRotationService {
readonly rotations = new Map<number, number>();
private readonly _isRotated$ = new BehaviorSubject<boolean>(false);
readonly rotations$ = new BehaviorSubject<Record<number, number>>({});
constructor(
private readonly _permissionsService: PermissionsService,
@ -16,30 +17,38 @@ export class PageRotationService {
private readonly _screenState: FilePreviewStateService,
) {}
get isRotated(): boolean {
return this._isRotated$.value;
}
get canRotate() {
return this._screenState.file.then(file => this._permissionsService.isFileAssignee(file));
}
setRotation(value: boolean): void {
this._isRotated$.next(value);
isRotated(page: number) {
return this.rotations$.pipe(
map(rotations => !!rotations[page]),
distinctUntilChanged(),
);
}
hasRotations() {
return Object.values(this.rotations$.value).filter(v => !!v).length > 0;
}
applyRotation() {
const pages = Object.fromEntries(this.rotations);
const pages = this.rotations$.value;
const { dossierId, fileId } = this._screenState;
const request = this._fileManagementService.rotatePage({ pages }, dossierId, fileId);
this.clearRotations();
return firstValueFrom(request);
}
addRotation(pageNumber: number, rotation: RotationType): void {
const pageRotation = this.rotations.get(pageNumber);
const pageRotation = this.rotations$.value[pageNumber];
const rotationValue = pageRotation ? (pageRotation + Number(rotation)) % 360 : rotation;
this.rotations.set(pageNumber, rotationValue);
this.rotations$.next({ ...this.rotations$.value, [pageNumber]: rotationValue });
}
clearRotations() {
this.rotations$.next({});
}
}

View File

@ -31,6 +31,10 @@ export async function wipeCaches(logoutDependant: boolean = false) {
}
}
export function wipeFilesCache() {
return caches.delete('files');
}
export async function wipeCacheEntry(cacheName: string, entry: string) {
caches.open(cacheName).then(cache => {
cache.delete(entry, { ignoreSearch: false });