RED-5908: update viewed pages when a change occurs
This commit is contained in:
parent
341ec38841
commit
e69bb94f8f
@ -86,16 +86,12 @@
|
|||||||
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
<mat-icon svgIcon="red:nav-first"></mat-icon>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="pages" id="pages">
|
<redaction-pages
|
||||||
<redaction-page-indicator
|
(click)="pagesPanelActive = true"
|
||||||
(pageSelected)="pageSelectedByClick($event)"
|
[activePage]="activeViewerPage"
|
||||||
*ngFor="let pageNumber of displayedPages"
|
[displayedAnnotations]="displayedAnnotations"
|
||||||
[activeSelection]="pageHasSelection(pageNumber)"
|
[pages]="displayedPages"
|
||||||
[active]="pageNumber === activeViewerPage"
|
></redaction-pages>
|
||||||
[number]="pageNumber"
|
|
||||||
[showDottedIcon]="hasOnlyManualRedactionsAndIsExcluded(pageNumber)"
|
|
||||||
></redaction-page-indicator>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
(click)="scrollQuickNavLast()"
|
(click)="scrollQuickNavLast()"
|
||||||
|
|||||||
@ -77,12 +77,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pages {
|
|
||||||
@include common-mixins.no-scroll-bar;
|
|
||||||
overflow: auto;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.annotations {
|
.annotations {
|
||||||
|
|||||||
@ -185,15 +185,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
hasOnlyManualRedactionsAndIsExcluded(pageNumber: number): boolean {
|
|
||||||
const hasOnlyManualRedactions = this.displayedAnnotations.get(pageNumber)?.every(annotation => annotation.manual);
|
|
||||||
return hasOnlyManualRedactions && this.file.excludedPages.includes(pageNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
pageHasSelection(page: number) {
|
|
||||||
return this.multiSelectService.isActive && !!this.listingService.selected.find(a => a.pageNumber === page);
|
|
||||||
}
|
|
||||||
|
|
||||||
selectAllOnActivePage() {
|
selectAllOnActivePage() {
|
||||||
this.listingService.selectAnnotations(this.activeAnnotations);
|
this.listingService.selectAnnotations(this.activeAnnotations);
|
||||||
}
|
}
|
||||||
@ -286,11 +277,6 @@ export class FileWorkloadComponent extends AutoUnsubscribe implements OnDestroy
|
|||||||
return firstValueFrom(this.state.file$).then(file => this.pdf.navigateTo(file.numberOfPages));
|
return firstValueFrom(this.state.file$).then(file => this.pdf.navigateTo(file.numberOfPages));
|
||||||
}
|
}
|
||||||
|
|
||||||
pageSelectedByClick($event: number): void {
|
|
||||||
this.pagesPanelActive = true;
|
|
||||||
this.pdf.navigateTo($event);
|
|
||||||
}
|
|
||||||
|
|
||||||
preventKeyDefault($event: KeyboardEvent): void {
|
preventKeyDefault($event: KeyboardEvent): void {
|
||||||
if (COMMAND_KEY_ARRAY.includes($event.key) && !(($event.target as any).localName === 'input')) {
|
if (COMMAND_KEY_ARRAY.includes($event.key) && !(($event.target as any).localName === 'input')) {
|
||||||
$event.preventDefault();
|
$event.preventDefault();
|
||||||
|
|||||||
@ -1,59 +1,45 @@
|
|||||||
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
|
import { ChangeDetectorRef, Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
|
||||||
import { PermissionsService } from '@services/permissions.service';
|
import { PermissionsService } from '@services/permissions.service';
|
||||||
import { ConfigService } from '@services/config.service';
|
|
||||||
import { ViewedPagesService } from '@services/files/viewed-pages.service';
|
import { ViewedPagesService } from '@services/files/viewed-pages.service';
|
||||||
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
import { PageRotationService } from '../../../pdf-viewer/services/page-rotation.service';
|
||||||
import { ContextComponent } from '@iqser/common-ui';
|
import { ContextComponent, getConfig } from '@iqser/common-ui';
|
||||||
import { tap } from 'rxjs/operators';
|
import { tap } from 'rxjs/operators';
|
||||||
import { FileDataService } from '../../services/file-data.service';
|
import { AppConfig, ViewedPage } from '@red/domain';
|
||||||
|
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||||
|
|
||||||
interface PageIndicatorContext {
|
interface PageIndicatorContext {
|
||||||
isRotated: boolean;
|
isRotated: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'redaction-page-indicator',
|
selector: 'redaction-page-indicator [number] [read]',
|
||||||
templateUrl: './page-indicator.component.html',
|
templateUrl: './page-indicator.component.html',
|
||||||
styleUrls: ['./page-indicator.component.scss'],
|
styleUrls: ['./page-indicator.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
||||||
})
|
})
|
||||||
export class PageIndicatorComponent extends ContextComponent<PageIndicatorContext> implements OnChanges, OnInit {
|
export class PageIndicatorComponent extends ContextComponent<PageIndicatorContext> implements OnChanges, OnInit {
|
||||||
@Input() active = false;
|
@Input() active = false;
|
||||||
@Input() showDottedIcon = false;
|
@Input() showDottedIcon = false;
|
||||||
@Input() number: number;
|
@Input() number: number;
|
||||||
@Input() activeSelection = false;
|
@Input() activeSelection = false;
|
||||||
|
@Input() read = false;
|
||||||
@Output() readonly pageSelected = new EventEmitter<number>();
|
@Output() readonly pageSelected = new EventEmitter<number>();
|
||||||
|
|
||||||
pageReadTimeout: number = null;
|
pageReadTimeout: number = null;
|
||||||
read = false;
|
|
||||||
isRotated = false;
|
isRotated = false;
|
||||||
|
readonly #config = getConfig<AppConfig>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private readonly _viewedPagesService: ViewedPagesService,
|
private readonly _viewedPagesService: ViewedPagesService,
|
||||||
private readonly _configService: ConfigService,
|
private readonly _viewedPagesMapService: ViewedPagesMapService,
|
||||||
private readonly _changeDetectorRef: ChangeDetectorRef,
|
private readonly _changeDetectorRef: ChangeDetectorRef,
|
||||||
private readonly _permissionService: PermissionsService,
|
private readonly _permissionService: PermissionsService,
|
||||||
private readonly _stateService: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _fileDataService: FileDataService,
|
|
||||||
readonly pageRotationService: PageRotationService,
|
readonly pageRotationService: PageRotationService,
|
||||||
) {
|
) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
get activePage() {
|
|
||||||
return this._fileDataService.viewedPages.find(p => p.page === this.number);
|
|
||||||
}
|
|
||||||
|
|
||||||
get dossierId() {
|
|
||||||
return this._stateService.dossierId;
|
|
||||||
}
|
|
||||||
|
|
||||||
get fileId() {
|
|
||||||
return this._stateService.fileId;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
const isRotated$ = this.pageRotationService.isRotated$(this.number).pipe(
|
const isRotated$ = this.pageRotationService.isRotated$(this.number).pipe(
|
||||||
tap(value => {
|
tap(value => {
|
||||||
@ -65,24 +51,21 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
|
|||||||
}
|
}
|
||||||
|
|
||||||
ngOnChanges() {
|
ngOnChanges() {
|
||||||
this._setReadState();
|
|
||||||
this.handlePageRead();
|
this.handlePageRead();
|
||||||
}
|
}
|
||||||
|
|
||||||
async toggleReadState() {
|
async toggleReadState() {
|
||||||
const file = this._stateService.file;
|
if (this._permissionService.canMarkPagesAsViewed(this._state.file)) {
|
||||||
if (this._permissionService.canMarkPagesAsViewed(file)) {
|
|
||||||
if (this.read) {
|
if (this.read) {
|
||||||
await this._markPageUnread();
|
await this.#markPageUnread();
|
||||||
} else {
|
} else {
|
||||||
await this._markPageRead();
|
await this.#markPageRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handlePageRead(): void {
|
handlePageRead(): void {
|
||||||
const file = this._stateService.file;
|
if (!this._permissionService.canMarkPagesAsViewed(this._state.file)) {
|
||||||
if (!this._permissionService.canMarkPagesAsViewed(file)) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,36 +76,22 @@ export class PageIndicatorComponent extends ContextComponent<PageIndicatorContex
|
|||||||
if (this.active && !this.read) {
|
if (this.active && !this.read) {
|
||||||
this.pageReadTimeout = window.setTimeout(async () => {
|
this.pageReadTimeout = window.setTimeout(async () => {
|
||||||
if (this.active && !this.read) {
|
if (this.active && !this.read) {
|
||||||
await this._markPageRead();
|
await this.#markPageRead();
|
||||||
}
|
}
|
||||||
}, this._configService.values.AUTO_READ_TIME * 1000);
|
}, this.#config.AUTO_READ_TIME * 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setReadState() {
|
async #markPageRead() {
|
||||||
const activePage = this.activePage;
|
const fileId = this._state.fileId;
|
||||||
if (!activePage) {
|
await this._viewedPagesService.add({ page: this.number }, this._state.dossierId, fileId);
|
||||||
this.read = false;
|
const viewedPage = new ViewedPage({ page: this.number, fileId });
|
||||||
} else {
|
this._viewedPagesMapService.add(fileId, viewedPage);
|
||||||
this.read = !activePage.showAsUnseen;
|
|
||||||
}
|
|
||||||
this._changeDetectorRef.markForCheck();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _markPageRead() {
|
async #markPageUnread() {
|
||||||
await this._viewedPagesService.addPage({ page: this.number }, this.dossierId, this.fileId);
|
const fileId = this._state.fileId;
|
||||||
if (this.activePage) {
|
await this._viewedPagesService.remove(this._state.dossierId, fileId, this.number);
|
||||||
this.activePage.showAsUnseen = false;
|
this._viewedPagesMapService.delete(fileId, this.number);
|
||||||
} else {
|
|
||||||
this._fileDataService.viewedPages.push({ page: this.number, fileId: this.fileId });
|
|
||||||
}
|
|
||||||
this._setReadState();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _markPageUnread() {
|
|
||||||
await this._viewedPagesService.removePage(this.dossierId, this.fileId, this.number);
|
|
||||||
const pageToDelete = this._fileDataService.viewedPages.findIndex(p => p.page === this.number);
|
|
||||||
this._fileDataService.viewedPages.splice(pageToDelete, 1);
|
|
||||||
this._setReadState();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
<div *ngIf="viewedPages$ | async as viewedPages" class="pages" id="pages">
|
||||||
|
<redaction-page-indicator
|
||||||
|
(pageSelected)="pageSelectedByClick($event)"
|
||||||
|
*ngFor="let pageNumber of pages; trackBy: trackBy"
|
||||||
|
[activeSelection]="pageHasSelection(pageNumber)"
|
||||||
|
[active]="pageNumber === activePage"
|
||||||
|
[number]="pageNumber"
|
||||||
|
[read]="!!getViewedPage(viewedPages, pageNumber)"
|
||||||
|
[showDottedIcon]="hasOnlyManualRedactionsAndIsExcluded(pageNumber)"
|
||||||
|
></redaction-page-indicator>
|
||||||
|
</div>
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
@use 'common-mixins';
|
||||||
|
|
||||||
|
:host {
|
||||||
|
display: contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pages {
|
||||||
|
@include common-mixins.no-scroll-bar;
|
||||||
|
overflow: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { ChangeDetectionStrategy, Component, inject, Input } from '@angular/core';
|
||||||
|
import { List } from '@iqser/common-ui';
|
||||||
|
import { PdfViewer } from '../../../pdf-viewer/services/pdf-viewer.service';
|
||||||
|
import { MultiSelectService } from '../../services/multi-select.service';
|
||||||
|
import { AnnotationsListingService } from '../../services/annotations-listing.service';
|
||||||
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
|
import { FilePreviewStateService } from '../../services/file-preview-state.service';
|
||||||
|
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||||
|
import { ViewedPage } from '@red/domain';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'redaction-pages [pages] [activePage] [displayedAnnotations]',
|
||||||
|
templateUrl: './pages.component.html',
|
||||||
|
styleUrls: ['./pages.component.scss'],
|
||||||
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
|
})
|
||||||
|
export class PagesComponent {
|
||||||
|
@Input() pages: List<number>;
|
||||||
|
@Input() activePage: number;
|
||||||
|
@Input() displayedAnnotations: Map<number, AnnotationWrapper[]>;
|
||||||
|
|
||||||
|
readonly #pdf = inject(PdfViewer);
|
||||||
|
readonly #state = inject(FilePreviewStateService);
|
||||||
|
readonly viewedPages$ = inject(ViewedPagesMapService).get$(this.#state.fileId);
|
||||||
|
readonly #multiSelectService = inject(MultiSelectService);
|
||||||
|
readonly #listingService = inject(AnnotationsListingService);
|
||||||
|
|
||||||
|
pageSelectedByClick($event: number): void {
|
||||||
|
this.#pdf.navigateTo($event);
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly trackBy = (_index: number, item: number) => item;
|
||||||
|
|
||||||
|
pageHasSelection(page: number) {
|
||||||
|
return this.#multiSelectService.isActive && !!this.#listingService.selected.find(a => a.pageNumber === page);
|
||||||
|
}
|
||||||
|
|
||||||
|
hasOnlyManualRedactionsAndIsExcluded(pageNumber: number): boolean {
|
||||||
|
const hasOnlyManualRedactions = this.displayedAnnotations.get(pageNumber)?.every(annotation => annotation.manual);
|
||||||
|
return hasOnlyManualRedactions && this.#state.file.excludedPages.includes(pageNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
getViewedPage(viewedPages: ViewedPage[], pageNumber: number) {
|
||||||
|
return viewedPages.find(p => p.page === pageNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -54,6 +54,7 @@ import { FilePreviewRightContainerComponent } from './components/right-container
|
|||||||
import { RssDialogComponent } from './dialogs/rss-dialog/rss-dialog.component';
|
import { RssDialogComponent } from './dialogs/rss-dialog/rss-dialog.component';
|
||||||
import { ReadonlyBannerComponent } from './components/readonly-banner/readonly-banner.component';
|
import { ReadonlyBannerComponent } from './components/readonly-banner/readonly-banner.component';
|
||||||
import { SuggestionsService } from './services/suggestions.service';
|
import { SuggestionsService } from './services/suggestions.service';
|
||||||
|
import { PagesComponent } from './components/pages/pages.component';
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
{
|
{
|
||||||
@ -85,6 +86,7 @@ const components = [
|
|||||||
AnnotationWrapperComponent,
|
AnnotationWrapperComponent,
|
||||||
AnnotationsListComponent,
|
AnnotationsListComponent,
|
||||||
PageIndicatorComponent,
|
PageIndicatorComponent,
|
||||||
|
PagesComponent,
|
||||||
PageExclusionComponent,
|
PageExclusionComponent,
|
||||||
AnnotationActionsComponent,
|
AnnotationActionsComponent,
|
||||||
CommentsComponent,
|
CommentsComponent,
|
||||||
|
|||||||
@ -6,13 +6,15 @@ import { annotationTypesTranslations } from '@translations/annotation-types-tran
|
|||||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||||
import { annotationDefaultColorConfig } from '@red/domain';
|
import { annotationDefaultColorConfig } from '@red/domain';
|
||||||
import { FilePreviewStateService } from './file-preview-state.service';
|
import { FilePreviewStateService } from './file-preview-state.service';
|
||||||
import { FileDataService } from './file-data.service';
|
|
||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { of } from 'rxjs';
|
import { of } from 'rxjs';
|
||||||
|
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||||
|
import { FileDataService } from './file-data.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AnnotationProcessingService {
|
export class AnnotationProcessingService {
|
||||||
constructor(
|
constructor(
|
||||||
|
private readonly _viewedPagesMapService: ViewedPagesMapService,
|
||||||
private readonly _fileDataService: FileDataService,
|
private readonly _fileDataService: FileDataService,
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _defaultColorsService: DefaultColorsService,
|
private readonly _defaultColorsService: DefaultColorsService,
|
||||||
@ -43,7 +45,7 @@ export class AnnotationProcessingService {
|
|||||||
checked: false,
|
checked: false,
|
||||||
topLevelFilter: true,
|
topLevelFilter: true,
|
||||||
checker: (annotation: AnnotationWrapper) =>
|
checker: (annotation: AnnotationWrapper) =>
|
||||||
!this._fileDataService.viewedPages.some(page => page.page === annotation.pageNumber),
|
!this._viewedPagesMapService.get(this._state.fileId).some(page => page.page === annotation.pageNumber),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'pages-without-annotations',
|
id: 'pages-without-annotations',
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ChangeType, File, IRedactionLog, IRedactionLogEntry, IViewedPage, ManualRedactionType, ViewMode, ViewModes } from '@red/domain';
|
import { ChangeType, File, IRedactionLog, IRedactionLogEntry, ManualRedactionType, ViewMode, ViewModes } from '@red/domain';
|
||||||
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
import { AnnotationWrapper } from '@models/file/annotation.wrapper';
|
||||||
import { BehaviorSubject, firstValueFrom, iif, Observable, Subject } from 'rxjs';
|
import { BehaviorSubject, firstValueFrom, iif, Observable, Subject } from 'rxjs';
|
||||||
import { RedactionLogEntry } from '@models/file/redaction-log.entry';
|
import { RedactionLogEntry } from '@models/file/redaction-log.entry';
|
||||||
@ -20,12 +20,12 @@ import { FilesService } from '@services/files/files.service';
|
|||||||
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
import { DefaultColorsService } from '@services/entity-services/default-colors.service';
|
||||||
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
import { DictionaryService } from '@services/entity-services/dictionary.service';
|
||||||
import { SuggestionsService } from './suggestions.service';
|
import { SuggestionsService } from './suggestions.service';
|
||||||
|
import { ViewedPagesMapService } from '@services/files/viewed-pages-map.service';
|
||||||
|
|
||||||
const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
const DELTA_VIEW_TIME = 10 * 60 * 1000; // 10 minutes;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FileDataService extends EntitiesService<AnnotationWrapper, AnnotationWrapper> {
|
export class FileDataService extends EntitiesService<AnnotationWrapper, AnnotationWrapper> {
|
||||||
viewedPages: IViewedPage[] = [];
|
|
||||||
missingTypes = new Set<string>();
|
missingTypes = new Set<string>();
|
||||||
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
readonly hasChangeLog$ = new BehaviorSubject<boolean>(false);
|
||||||
readonly annotations$: Observable<AnnotationWrapper[]>;
|
readonly annotations$: Observable<AnnotationWrapper[]>;
|
||||||
@ -37,6 +37,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
constructor(
|
constructor(
|
||||||
private readonly _state: FilePreviewStateService,
|
private readonly _state: FilePreviewStateService,
|
||||||
private readonly _viewedPagesService: ViewedPagesService,
|
private readonly _viewedPagesService: ViewedPagesService,
|
||||||
|
private readonly _viewedPagesMapService: ViewedPagesMapService,
|
||||||
private readonly _viewModeService: ViewModeService,
|
private readonly _viewModeService: ViewModeService,
|
||||||
private readonly _userPreferenceService: UserPreferenceService,
|
private readonly _userPreferenceService: UserPreferenceService,
|
||||||
private readonly _dictionaryService: DictionaryService,
|
private readonly _dictionaryService: DictionaryService,
|
||||||
@ -148,11 +149,12 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
|
|
||||||
async #loadViewedPages(file: File) {
|
async #loadViewedPages(file: File) {
|
||||||
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
|
if (!this._permissionsService.canMarkPagesAsViewed(file)) {
|
||||||
this.viewedPages = [];
|
this._viewedPagesMapService.set(file.fileId, []);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.viewedPages = await this._viewedPagesService.getViewedPages(file.dossierId, file.fileId);
|
const viewedPages = await this._viewedPagesService.load(file.dossierId, file.fileId);
|
||||||
|
this._viewedPagesMapService.set(file.fileId, viewedPages);
|
||||||
}
|
}
|
||||||
|
|
||||||
#getVisibleAnnotations(annotations: AnnotationWrapper[], viewMode: ViewMode) {
|
#getVisibleAnnotations(annotations: AnnotationWrapper[], viewMode: ViewMode) {
|
||||||
@ -268,8 +270,7 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
|
|
||||||
const lastChange = viableChanges.length >= 1 ? viableChanges[viableChanges.length - 1] : undefined;
|
const lastChange = viableChanges.length >= 1 ? viableChanges[viableChanges.length - 1] : undefined;
|
||||||
const page = redactionLogEntry.positions?.[0].page;
|
const page = redactionLogEntry.positions?.[0].page;
|
||||||
|
const viewedPage = this._viewedPagesMapService.get(file.fileId, page);
|
||||||
const viewedPage = this.viewedPages.filter(p => p.page === page).pop();
|
|
||||||
|
|
||||||
// page has been seen -> let's see if it's a change
|
// page has been seen -> let's see if it's a change
|
||||||
if (viewedPage) {
|
if (viewedPage) {
|
||||||
@ -279,7 +280,11 @@ export class FileDataService extends EntitiesService<AnnotationWrapper, Annotati
|
|||||||
// at least one unseen change
|
// at least one unseen change
|
||||||
if (relevantChanges.length > 0) {
|
if (relevantChanges.length > 0) {
|
||||||
// at least 1 relevant change
|
// at least 1 relevant change
|
||||||
viewedPage.showAsUnseen = dayjs(viewedPage.viewedTime).valueOf() < dayjs(lastChange.dateTime).valueOf();
|
const showAsUnseen = dayjs(viewedPage.viewedTime).valueOf() < dayjs(lastChange.dateTime).valueOf();
|
||||||
|
if (showAsUnseen) {
|
||||||
|
this._viewedPagesMapService.delete(this._state.fileId, viewedPage);
|
||||||
|
}
|
||||||
|
|
||||||
this.hasChangeLog$.next(true);
|
this.hasChangeLog$.next(true);
|
||||||
return {
|
return {
|
||||||
changeLogType: relevantChanges[relevantChanges.length - 1].type,
|
changeLogType: relevantChanges[relevantChanges.length - 1].type,
|
||||||
|
|||||||
@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from '@angular/core';
|
||||||
|
import { IViewedPage, ViewedPage } from '@red/domain';
|
||||||
|
import { EntitiesMapService } from '@iqser/common-ui';
|
||||||
|
|
||||||
|
@Injectable({ providedIn: 'root' })
|
||||||
|
export class ViewedPagesMapService extends EntitiesMapService<IViewedPage, ViewedPage> {
|
||||||
|
protected readonly _primaryKey = 'id';
|
||||||
|
}
|
||||||
@ -1,7 +1,7 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { GenericService, RequiredParam, Validate } from '@iqser/common-ui';
|
import { GenericService, mapEach, RequiredParam, Validate } from '@iqser/common-ui';
|
||||||
import { catchError, map } from 'rxjs/operators';
|
import { catchError, map } from 'rxjs/operators';
|
||||||
import { IViewedPage, IViewedPagesRequest } from '@red/domain';
|
import { IViewedPage, IViewedPagesRequest, ViewedPage } from '@red/domain';
|
||||||
import { firstValueFrom, of } from 'rxjs';
|
import { firstValueFrom, of } from 'rxjs';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
@ -11,22 +11,23 @@ export class ViewedPagesService extends GenericService<unknown> {
|
|||||||
protected readonly _defaultModelPath = 'viewedPages';
|
protected readonly _defaultModelPath = 'viewedPages';
|
||||||
|
|
||||||
@Validate()
|
@Validate()
|
||||||
addPage(@RequiredParam() body: IViewedPagesRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
add(@RequiredParam() body: IViewedPagesRequest, @RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||||
const modelPath = `${this._defaultModelPath}/${dossierId}/${fileId}`;
|
const modelPath = `${this._defaultModelPath}/${dossierId}/${fileId}`;
|
||||||
return firstValueFrom(this._post(body, modelPath));
|
return firstValueFrom(this._post(body, modelPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Validate()
|
@Validate()
|
||||||
removePage(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, @RequiredParam() page: number) {
|
remove(@RequiredParam() dossierId: string, @RequiredParam() fileId: string, @RequiredParam() page: number) {
|
||||||
const modelPath = `${this._defaultModelPath}/${dossierId}/${fileId}/${page}`;
|
const modelPath = `${this._defaultModelPath}/${dossierId}/${fileId}/${page}`;
|
||||||
return firstValueFrom(super.delete({}, modelPath));
|
return firstValueFrom(super.delete({}, modelPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Validate()
|
@Validate()
|
||||||
getViewedPages(@RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
load(@RequiredParam() dossierId: string, @RequiredParam() fileId: string) {
|
||||||
const request = this._getOne<{ pages?: IViewedPage[] }>([dossierId, fileId]).pipe(
|
const request = this._getOne<{ pages?: IViewedPage[] }>([dossierId, fileId]).pipe(
|
||||||
map(res => res.pages),
|
map(res => res.pages),
|
||||||
catchError(() => of([] as IViewedPage[])),
|
catchError(() => of([] as IViewedPage[])),
|
||||||
|
mapEach(page => new ViewedPage(page)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return firstValueFrom(request);
|
return firstValueFrom(request);
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
export * from './page-range';
|
export * from './page-range';
|
||||||
export * from './viewed-page';
|
export * from './viewed-page';
|
||||||
|
export * from './viewed-page.model';
|
||||||
export * from './viewed-pages.request';
|
export * from './viewed-pages.request';
|
||||||
export * from './page-exclusion.request';
|
export * from './page-exclusion.request';
|
||||||
export * from './page-rotation.request';
|
export * from './page-rotation.request';
|
||||||
|
|||||||
24
libs/red-domain/src/lib/pages/viewed-page.model.ts
Normal file
24
libs/red-domain/src/lib/pages/viewed-page.model.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Entity } from '@iqser/common-ui';
|
||||||
|
import { IViewedPage } from '.';
|
||||||
|
|
||||||
|
export class ViewedPage extends Entity<IViewedPage, number> {
|
||||||
|
readonly fileId: string;
|
||||||
|
readonly page: number;
|
||||||
|
readonly userId?: string;
|
||||||
|
readonly viewedTime?: string;
|
||||||
|
|
||||||
|
override readonly routerLink = undefined;
|
||||||
|
override readonly searchKey = '';
|
||||||
|
|
||||||
|
constructor(viewedPage: IViewedPage) {
|
||||||
|
super(viewedPage);
|
||||||
|
this.fileId = viewedPage.fileId;
|
||||||
|
this.page = viewedPage.page;
|
||||||
|
this.userId = viewedPage.userId;
|
||||||
|
this.viewedTime = viewedPage.viewedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
override get id() {
|
||||||
|
return this.page;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
export interface IViewedPage {
|
export interface IViewedPage {
|
||||||
fileId?: string;
|
readonly fileId: string;
|
||||||
page?: number;
|
readonly page: number;
|
||||||
userId?: string;
|
readonly userId?: string;
|
||||||
viewedTime?: string;
|
readonly viewedTime?: string;
|
||||||
showAsUnseen?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user