Change Detection: On Push aka UI Smoothness (#1369)

* Updated Series Info Cards to use OnPush and hooked in progress events when we do a mark as read/unread on entities. These events update progress bars but also will now trigger a re-calculation on Read Time Left.

* Removed Library Card Component

* Refactored manga reader title and subtitle calculation to the backend.

* Coverted card actionables to onPush

* Series Card on push cleanup

* Updated edit collection tags for on push

* Update cover image chooser for on push

* Cleaned up carsouel reel

* Updated cover image to allow for uploading gif and webp files

* Bulk add to collection on push

* Updated bulk operation to use on push. Updated bulk operation to have mark as unread and read buttons explicitly. Updated so add to collection is visible and delete.

Fixed a bug where manage library component wasn't invoking the trackBy function

* Updating entity title for on push

* Removed file info component

* Updated Mange Library for on push

* Entity info cards on push

* List item on push

* Updated icon and title for on push and fixed some missing change detection on series detail

* Restricted the typeahead interface to simplify the design

* Edit Series Relation now shows a value in the dropdown for Parent relationships and disables the field.

* Updated edit series relation to focus on new typeahead when adding a new relationship

* Added some documentation and when Scanning a library, don't allow the user to enqueue the same job multiple times.

* Applied the No-enqueue if already enqueued logic to other tasks

* Library detail on push

* Updated events widget to onpush

* Card detail drawer on push. Card detail cover chooser now will show all chapter's covers for selection in cover chooser.

* Chapter metadata detail on push

* Removed Card Detail modal

* All collections on push

* Removed some comments

* Updated bulk selection to use an observable rather than function calls so new on push strategy works

* collection detail now uses on push and scroller is placed on correct element

* Updated library recommended to on push. Ensure that when mark as read occurs, the appropriate streams are refreshed.

* Updated library detail to on push

* Update metadata fiter to onpush. Bugs found and reported to Project

* person badge on push

* Read more on push

* Updated tag badge to on push

* User login on push

* When initing side nav, don't call an authenticated api until we are sure a user is logged in

* Updated splash container to on push

* Dashboard on push

* Side nav slight refactor around some api calls

* Cleaned up series card on push to use same cdRef naming convention

* Updated Static Files to use caching

* Added width and height to logo image

* shortcuts modal on push

* reading lists on push

* Reading list detail on push

* draggable ordered list on push

* Refactored reading-list-detail to use a new item which drastically reduces renders on operations

* series format on push

* circular loader on push

* Badge Expander on push

* update notification modal on push

* drawer on push

* Edit Series Modal on push

* reset password on push

* review series modal on push

* series metadata detail on push

* theme manager on push

* confirm reset password on push

* register on push

* confirm migration email on push

* confirm email on push

* add email to account migration on push

* user preferences on push. Made global settings default open

* edit series relation on push

* Fixed an edge case bug for next chapter where if the current volume had a single chapter of 1 and the next volume had a chapter number of 0, it would say there are no more chapters.

* Updated infinite scroller with on push support

* Moved some animations over to typeahead, not integrated yet.

* Manga reader is now on push

* Reader settings on push

* refactored how we close the book

* Updated table of contents for on push

* Updated book reader for on push. Fixed a bug where table of contents wasn't showing current page anchor due to a scroll calulation bug

* Small code tweak

* Icon and title on push

* nav header on push

* grouped typeahead on push

* typeahead on push and added a new trackby identity function to allow even faster rendering of big lists

* pdf reader on push

* code cleanup
This commit is contained in:
Joseph Milazzo 2022-07-11 11:57:07 -04:00 committed by GitHub
parent f5be0fac58
commit 4e49aa47ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
126 changed files with 1658 additions and 1674 deletions

View file

@ -84,7 +84,7 @@
tabindex="-1" [ngStyle]="{height: PageHeightForPagination}"></div>
</ng-container>
<div class="book-container" [ngClass]="{'immersive' : immersiveMode}"> <!-- {{ColumnLayout}}-->
<div class="book-container" [ngClass]="{'immersive' : immersiveMode}">
<div #readingHtml class="book-content {{ColumnLayout}}" [ngStyle]="{'max-height': ColumnHeight, 'column-width': ColumnWidth}"
[ngClass]="{'immersive': immersiveMode && actionBarVisible}"

View file

@ -1,4 +1,4 @@
import { AfterViewInit, Component, ElementRef, HostListener, Inject, OnDestroy, OnInit, Renderer2, RendererStyleFlags2, ViewChild } from '@angular/core';
import { AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, HostListener, Inject, OnDestroy, OnInit, Renderer2, RendererStyleFlags2, ViewChild } from '@angular/core';
import {DOCUMENT, Location} from '@angular/common';
import { ActivatedRoute, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
@ -54,6 +54,7 @@ const elementLevelStyles = ['line-height', 'font-family'];
selector: 'app-book-reader',
templateUrl: './book-reader.component.html',
styleUrls: ['./book-reader.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
animations: [
trigger('isLoading', [
state('false', style({opacity: 1})),
@ -110,8 +111,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
/**
* A stack of the chapter ids we come across during continuous reading mode. When we traverse a boundary, we use this to avoid extra API calls.
* @see Stack
* TODO: See if continuousChaptersStack can be moved into reader service so we can reduce code duplication between readers (and also use ChapterInfo with it instead)
*/
continuousChaptersStack: Stack<number> = new Stack(); // TODO: See if continuousChaptersStack can be moved into reader service so we can reduce code duplication between readers (and also use ChapterInfo with it instead)
continuousChaptersStack: Stack<number> = new Stack();
/**
* Belongs to the drawer component
@ -383,10 +385,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
private renderer: Renderer2, private navService: NavService, private toastr: ToastrService,
private domSanitizer: DomSanitizer, private bookService: BookService, private memberService: MemberService,
private scrollService: ScrollService, private utilityService: UtilityService, private libraryService: LibraryService,
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService) {
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService, private readonly cdRef: ChangeDetectorRef) {
this.navService.hideNavBar();
this.themeService.clearThemes();
this.navService.hideSideNav();
this.cdRef.markForCheck();
}
/**
@ -411,7 +414,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// Highlight the current chapter we are on
if (Object.keys(this.pageAnchors).length !== 0) {
// get the height of the document so we can capture markers that are halfway on the document viewport
const verticalOffset = this.scrollService.scrollPosition + (this.document.body.offsetHeight / 2);
const verticalOffset = this.reader.nativeElement?.scrollTop || (this.scrollService.scrollPosition + (this.document.body.offsetHeight / 2));
const alreadyReached = Object.values(this.pageAnchors).filter((i: number) => i <= verticalOffset);
if (alreadyReached.length > 0) {
@ -419,6 +422,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
} else {
this.currentPageAnchor = '';
}
this.cdRef.markForCheck();
}
// Find the element that is on screen to bookmark against
@ -439,7 +444,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (!this.incognitoMode) {
this.readerService.saveProgress(this.seriesId, this.volumeId, this.chapterId, tempPageNum, this.lastSeenScrollPartPath).pipe(take(1)).subscribe(() => {/* No operation */});
}
}
ngOnDestroy(): void {
@ -481,6 +485,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.readingListMode = true;
this.readingListId = parseInt(readingListId, 10);
}
this.cdRef.markForCheck();
this.memberService.hasReadingProgress(this.libraryId).pipe(take(1)).subscribe(hasProgress => {
@ -504,18 +509,20 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.nextChapterDisabled = false;
this.prevChapterDisabled = false;
this.nextChapterPrefetched = false;
this.cdRef.markForCheck();
this.bookService.getBookInfo(this.chapterId).subscribe(info => {
this.bookTitle = info.bookTitle;
if (this.readingListMode && info.seriesFormat !== MangaFormat.EPUB) {
// Redirect to the manga reader.
const params = this.readerService.getQueryParamsObject(this.incognitoMode, this.readingListMode, this.readingListId);
this.router.navigate(this.readerService.getNavigationArray(info.libraryId, info.seriesId, this.chapterId, info.seriesFormat), {queryParams: params});
return;
}
this.bookTitle = info.bookTitle;
this.cdRef.markForCheck();
forkJoin({
chapter: this.seriesService.getChapter(this.chapterId),
@ -527,23 +534,21 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.maxPages = results.chapter.pages;
this.chapters = results.chapters;
this.pageNum = results.progress.pageNum;
this.cdRef.markForCheck();
if (results.progress.bookScrollId) this.lastSeenScrollPartPath = results.progress.bookScrollId;
this.continuousChaptersStack.push(this.chapterId);
this.libraryService.getLibraryType(this.libraryId).pipe(take(1)).subscribe(type => {
this.libraryType = type;
});
// We need to think about if the user modified this and this function call is a continuous reader one
//this.updateLayoutMode(this.user.preferences.bookReaderLayoutMode || BookPageLayoutMode.Default);
this.updateImagesWithHeight();
if (this.pageNum >= this.maxPages) {
this.pageNum = this.maxPages - 1;
this.cdRef.markForCheck();
this.saveProgress();
}
@ -552,6 +557,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (chapterId === CHAPTER_ID_DOESNT_EXIST || chapterId === this.chapterId) {
this.nextChapterDisabled = true;
this.nextChapterPrefetched = true;
this.cdRef.markForCheck();
return;
}
this.setPageNum(this.pageNum);
@ -561,6 +567,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (chapterId === CHAPTER_ID_DOESNT_EXIST || chapterId === this.chapterId) {
this.prevChapterDisabled = true;
this.prevChapterPrefetched = true; // If there is no prev chapter, then mark it as prefetched
this.cdRef.markForCheck();
return;
}
this.setPageNum(this.pageNum);
@ -577,20 +584,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
@HostListener('window:resize', ['$event'])
onResize(event: any){
// Update the window Height
this.updateWidthAndHeightCalcs();
const resumeElement = this.getFirstVisibleElementXPath();
if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
this.scrollTo(resumeElement); // This works pretty well, but not perfect
}
}
@HostListener('window:orientationchange', ['$event'])
onOrientationChange() {
onResize(){
// Update the window Height
this.updateWidthAndHeightCalcs();
const resumeElement = this.getFirstVisibleElementXPath();
if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
this.scrollTo(resumeElement); // This works pretty well, but not perfect
@ -616,6 +614,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
}
closeReader() {
this.readerService.closeReader(this.readingListMode, this.readingListId);
}
sortElements(a: Element, b: Element) {
const aTop = a.getBoundingClientRect().top;
const bTop = b.getBoundingClientRect().top;
@ -646,6 +648,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.prevPageDisabled) { return; }
this.isLoading = true;
this.cdRef.markForCheck();
this.continuousChaptersStack.pop();
const prevChapter = this.continuousChaptersStack.peek();
if (prevChapter != this.chapterId) {
@ -657,6 +660,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
if (this.prevChapterPrefetched && this.prevChapterId === CHAPTER_ID_DOESNT_EXIST) {
this.isLoading = false;
this.cdRef.markForCheck();
return;
}
@ -677,8 +682,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// Load chapter Id onto route but don't reload
const newRoute = this.readerService.getNextChapterUrl(this.router.url, this.chapterId, this.incognitoMode, this.readingListMode, this.readingListId);
window.history.replaceState({}, '', newRoute);
this.init();
this.toastr.info(direction + ' ' + this.utilityService.formatChapterName(this.libraryType).toLowerCase() + ' loaded', '', {timeOut: 3000});
this.cdRef.markForCheck();
this.init();
} else {
// This will only happen if no actual chapter can be found
this.toastr.warning('Could not find ' + direction.toLowerCase() + ' ' + this.utilityService.formatChapterName(this.libraryType).toLowerCase());
@ -688,6 +694,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
} else {
this.nextPageDisabled = true;
}
this.cdRef.markForCheck();
}
}
@ -696,15 +703,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.loadPage('id("' + event.part + '")');
}
closeReader() {
if (this.readingListMode) {
this.router.navigateByUrl('lists/' + this.readingListId);
} else {
this.location.back();
}
}
/**
* Adds a click handler for any anchors that have 'kavita-page'. If 'kavita-page' present, changes page to kavita-page and optionally passes a part value
* from 'kavita-part', which will cause the reader to scroll to the marker.
@ -775,14 +773,15 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
loadPage(part?: string | undefined, scrollTop?: number | undefined) {
this.isLoading = true;
this.cdRef.markForCheck();
this.bookService.getBookPage(this.chapterId, this.pageNum).pipe(take(1)).subscribe(content => {
this.page = this.domSanitizer.bypassSecurityTrustHtml(content); // PERF: Potential optimization to prefetch next/prev page and store in localStorage
this.cdRef.markForCheck();
setTimeout(() => {
this.addLinkClickHandlers();
this.updateReaderStyles(this.pageStyles);
this.updateReaderStyles(this.pageStyles);
const imgs = this.readingSectionElemRef.nativeElement.querySelectorAll('img');
if (imgs === null || imgs.length === 0) {
@ -805,7 +804,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
* Applies a max-height inline css property on each image in the page if the layout mode is column-based, else it removes the property
*/
updateImagesWithHeight() {
const images = this.readingSectionElemRef?.nativeElement.querySelectorAll('img') || [];
if (this.layoutMode !== BookPageLayoutMode.Default) {
@ -822,6 +820,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
setupPage(part?: string | undefined, scrollTop?: number | undefined) {
this.isLoading = false;
this.cdRef.markForCheck();
// Virtual Paging stuff
this.updateWidthAndHeightCalcs();
@ -853,6 +852,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// we need to click the document before arrow keys will scroll down.
this.reader.nativeElement.focus();
this.saveProgress();
this.isLoading = false;
this.cdRef.markForCheck();
}
@ -868,21 +869,24 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
setPageNum(pageNum: number) {
this.pageNum = Math.max(Math.min(pageNum, this.maxPages), 0);
this.cdRef.markForCheck();
if (this.pageNum >= this.maxPages - 10) {
// Tell server to cache the next chapter
if (!this.nextChapterPrefetched && this.nextChapterId !== CHAPTER_ID_DOESNT_EXIST) { // && !this.nextChapterDisabled
if (!this.nextChapterPrefetched && this.nextChapterId !== CHAPTER_ID_DOESNT_EXIST) {
this.readerService.getChapterInfo(this.nextChapterId).pipe(take(1), catchError(err => {
this.nextChapterDisabled = true;
this.cdRef.markForCheck();
return of(null);
})).subscribe(res => {
this.nextChapterPrefetched = true;
});
}
} else if (this.pageNum <= 10) {
if (!this.prevChapterPrefetched && this.prevChapterId !== CHAPTER_ID_DOESNT_EXIST) { // && !this.prevChapterDisabled
if (!this.prevChapterPrefetched && this.prevChapterId !== CHAPTER_ID_DOESNT_EXIST) {
this.readerService.getChapterInfo(this.prevChapterId).pipe(take(1), catchError(err => {
this.prevChapterDisabled = true;
this.cdRef.markForCheck();
return of(null);
})).subscribe(res => {
this.prevChapterPrefetched = true;
@ -1105,6 +1109,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// Recalculate if bottom action bar is needed
this.scrollbarNeeded = this.readingHtml.nativeElement.clientHeight > this.reader.nativeElement.clientHeight;
this.cdRef.markForCheck();
}
toggleDrawer() {
@ -1113,6 +1118,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.immersiveMode) {
this.actionBarVisible = false;
}
this.cdRef.markForCheck();
}
scrollTo(partSelector: string) {
@ -1133,7 +1139,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.layoutMode === BookPageLayoutMode.Default) {
const fromTopOffset = element.getBoundingClientRect().top + window.pageYOffset + TOP_OFFSET;
// We need to use a delay as webkit browsers (aka apple devices) don't always have the document rendered by this point
setTimeout(() => this.scrollService.scrollTo(fromTopOffset, this.reader.nativeElement), 10);
setTimeout(() => this.scrollService.scrollTo(fromTopOffset, this.reader.nativeElement), 10); // BUG: This is broken
} else {
setTimeout(() => (element as Element).scrollIntoView({'block': 'start', 'inline': 'start'}));
}
@ -1184,11 +1190,13 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.isFullscreen) {
this.readerService.exitFullscreen(() => {
this.isFullscreen = false;
this.cdRef.markForCheck();
this.renderer.removeStyle(this.reader.nativeElement, 'background');
});
} else {
this.readerService.enterFullscreen(this.reader.nativeElement, () => {
this.isFullscreen = true;
this.cdRef.markForCheck();
// HACK: This is a bug with how browsers change the background color for fullscreen mode
this.renderer.setStyle(this.reader.nativeElement, 'background', this.themeService.getCssVariable('--bs-body-color'));
if (!this.darkMode) {
@ -1200,6 +1208,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
updateLayoutMode(mode: BookPageLayoutMode) {
this.layoutMode = mode;
this.cdRef.markForCheck();
// Remove any max-heights from column layout
this.updateImagesWithHeight();
@ -1209,7 +1218,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
setTimeout(() => this.updateLayoutMode(this.layoutMode), 10);
return;
}
setTimeout(() => {this.scrollbarNeeded = this.readingHtml.nativeElement.clientHeight > this.reader.nativeElement.clientHeight;});
setTimeout(() => {
this.scrollbarNeeded = this.readingHtml.nativeElement.clientHeight > this.reader.nativeElement.clientHeight;
this.cdRef.markForCheck();
});
// When I switch layout, I might need to resume the progress point.
if (mode === BookPageLayoutMode.Default) {
@ -1220,6 +1232,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
updateReadingDirection(readingDirection: ReadingDirection) {
this.readingDirection = readingDirection;
this.cdRef.markForCheck();
}
updateImmersiveMode(immersiveMode: boolean) {
@ -1227,13 +1240,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.immersiveMode && !this.drawerOpen) {
this.actionBarVisible = false;
}
this.updateReadingSectionHeight();
this.cdRef.markForCheck();
}
updateReadingSectionHeight() {
setTimeout(() => {
//console.log('setting height on ', this.readingSectionElemRef)
if (this.immersiveMode) {
this.renderer.setStyle(this.readingSectionElemRef, 'height', 'calc(var(--vh, 1vh) * 100)', RendererStyleFlags2.Important);
} else {
@ -1263,6 +1275,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
setupPageAnchors() {
this.pageAnchors = {};
this.currentPageAnchor = '';
this.cdRef.markForCheck();
const ids = this.chapters.map(item => item.children).flat().filter(item => item.page === this.pageNum).map(item => item.part).filter(item => item.length > 0);
if (ids.length > 0) {
const elems = this.getPageMarkers(ids);
@ -1275,6 +1288,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
// Settings Handlers
showPaginationOverlay(clickToPaginate: boolean) {
this.clickToPaginate = clickToPaginate;
this.cdRef.markForCheck();
this.clearTimeout(this.clickToPaginateVisualOverlayTimeout2);
if (!clickToPaginate) { return; }
@ -1293,6 +1307,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
showClickToPaginateVisualOverlay() {
this.clickToPaginateVisualOverlay = true;
this.cdRef.markForCheck();
if (this.clickToPaginateVisualOverlay && this.clickToPaginateVisualOverlayTimeout !== undefined) {
clearTimeout(this.clickToPaginateVisualOverlayTimeout);
@ -1300,6 +1315,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
this.clickToPaginateVisualOverlayTimeout = setTimeout(() => {
this.clickToPaginateVisualOverlay = false;
this.cdRef.markForCheck();
}, 1000);
}
@ -1338,8 +1354,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
Math.abs(this.mousePosition.y - event.screenY) <= mouseOffset
) {
this.actionBarVisible = !this.actionBarVisible;
this.cdRef.markForCheck();
}
}
mouseDown($event: MouseEvent) {

View file

@ -1,5 +1,5 @@
import { DOCUMENT } from '@angular/common';
import { Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Inject, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { Subject, take, takeUntil } from 'rxjs';
import { BookPageLayoutMode } from 'src/app/_models/book-page-layout-mode';
@ -60,7 +60,8 @@ const mobileBreakpointMarginOverride = 700;
@Component({
selector: 'app-reader-settings',
templateUrl: './reader-settings.component.html',
styleUrls: ['./reader-settings.component.scss']
styleUrls: ['./reader-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ReaderSettingsComponent implements OnInit, OnDestroy {
/**
@ -131,12 +132,14 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
constructor(private bookService: BookService, private accountService: AccountService,
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService) {}
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService,
private readonly cdRef: ChangeDetectorRef) {}
ngOnInit(): void {
this.fontFamilies = this.bookService.getFontFamilies();
this.fontOptions = this.fontFamilies.map(f => f.title);
this.cdRef.markForCheck();
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
if (user) {
@ -211,6 +214,7 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
this.setTheme(this.user.preferences.bookReaderThemeName || this.themeService.defaultBookTheme);
this.cdRef.markForCheck();
// Emit first time so book reader gets the setting
this.readingDirection.emit(this.readingDirectionModel);
@ -241,6 +245,7 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
}
this.settingsForm.get('bookReaderFontFamily')?.setValue(this.user.preferences.bookReaderFontFamily);
this.cdRef.markForCheck();
this.styleUpdate.emit(this.pageStyles);
}
@ -264,12 +269,12 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
'margin-right': margin || this.pageStyles['margin-right'] || defaultMargin,
'line-height': lineHeight || this.pageStyles['line-height'] || '100%'
};
}
setTheme(themeName: string) {
const theme = this.themes.find(t => t.name === themeName);
this.activeTheme = theme;
this.cdRef.markForCheck();
this.colorThemeUpdate.emit(theme);
}
@ -280,11 +285,13 @@ export class ReaderSettingsComponent implements OnInit, OnDestroy {
this.readingDirectionModel = ReadingDirection.LeftToRight;
}
this.cdRef.markForCheck();
this.readingDirection.emit(this.readingDirectionModel);
}
toggleFullscreen() {
this.isFullscreen = !this.isFullscreen;
this.cdRef.markForCheck();
this.fullscreen.emit();
}
}

View file

@ -1,5 +1,4 @@
<div class="table-of-contents">
<!-- <h3>Table of Contents</h3> -->
<div *ngIf="chapters.length === 0">
<em>This book does not have Table of Contents set in the metadata or a toc file</em>
</div>

View file

@ -1,11 +1,12 @@
import { Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core';
import { Subject } from 'rxjs';
import { BookChapterItem } from '../_models/book-chapter-item';
@Component({
selector: 'app-table-of-contents',
templateUrl: './table-of-contents.component.html',
styleUrls: ['./table-of-contents.component.scss']
styleUrls: ['./table-of-contents.component.scss'],
changeDetection: ChangeDetectionStrategy.Default
})
export class TableOfContentsComponent implements OnInit, OnDestroy {
@ -16,11 +17,8 @@ export class TableOfContentsComponent implements OnInit, OnDestroy {
@Output() loadChapter: EventEmitter<{pageNum: number, part: string}> = new EventEmitter();
private onDestroy: Subject<void> = new Subject();
pageAnchors: {[n: string]: number } = {};
constructor() {}
@ -44,5 +42,4 @@ export class TableOfContentsComponent implements OnInit, OnDestroy {
loadChapterPage(pageNum: number, part: string) {
this.loadChapter.emit({pageNum, part});
}
}