Book Reader Issues (#906)
* Refactored the Font Escaping Regex with new unit tests. * Fonts are now properly escaped, somehow a regression was introduced. * Refactored most of the book page loading for the reader into the service. * Fixed a bug where going into fullscreen in non dark mode will cause the background of the reader to go black. Fixed a rendering issue with margin left/right screwing html up. Fixed an issue where line-height: 100% would break book's css, now we remove the styles if they are non-valuable. * Changed how I fixed the black mode in fullscreen * Fixed an issue where anchors wouldn't be colored blue in white mode * Fixed a bug in the code that checks if a filename is a cover where it would choose "backcover" as a cover, despite it not being a valid case. * Validate if ReleaseYear is a valid year and if not, set it to 0 to disable it. * Fixed an issue where some large images could blow out the screen when reading on mobile. Now images will force to be max of width of browser * Put my hack back in for fullscreen putting background color to black * Change forwarded headers from All to explicit names * Fixed an issue where Scheme was not https when it should have been. Now the browser will handle which scheme to request. * Cleaned up the user preferences to stack multiple controls onto one row * Fixed fullscreen scroll issue with progress, but now sticky top is missing. * Corrected the element on which we fullscreen
This commit is contained in:
parent
32bfe46187
commit
2b57449a63
14 changed files with 426 additions and 315 deletions
|
|
@ -1,4 +1,4 @@
|
|||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}}" #reader>
|
||||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}}" style="overflow: auto;" #reader>
|
||||
<div class="fixed-top" #stickyTop>
|
||||
<a class="sr-only sr-only-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">Skip to main content</a>
|
||||
<ng-container [ngTemplateOutlet]="actionBar"></ng-container>
|
||||
|
|
@ -94,7 +94,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<ng-template #nestedChildren>
|
||||
<ul *ngFor="let chapterGroup of chapters" style="padding-inline-start: 0px">
|
||||
<ul *ngFor="let chapterGroup of chapters" class="chapter-title">
|
||||
<li class="{{chapterGroup.page == pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
|
||||
{{chapterGroup.title}}
|
||||
</li>
|
||||
|
|
@ -110,8 +110,11 @@
|
|||
</app-drawer>
|
||||
</div>
|
||||
|
||||
<div #readingSection class="reading-section" [ngStyle]="{'padding-top': topOffset + 20 + 'px'}" [@isLoading]="isLoading ? true : false" (click)="handleReaderClick($event)">
|
||||
<div #readingHtml class="book-content" [ngStyle]="{'padding-bottom': topOffset + 20 + 'px', 'margin': '0px 0px'}" [innerHtml]="page" *ngIf="page !== undefined"></div>
|
||||
<div #readingSection class="reading-section" [ngStyle]="{'padding-top': topOffset + 20 + 'px'}"
|
||||
[@isLoading]="isLoading ? true : false" (click)="handleReaderClick($event)">
|
||||
|
||||
<div #readingHtml class="book-content" [ngStyle]="{'padding-bottom': topOffset + 20 + 'px', 'margin': '0px 0px'}"
|
||||
[innerHtml]="page" *ngIf="page !== undefined"></div>
|
||||
|
||||
<div class="left {{clickOverlayClass('left')}} no-observe" (click)="prevPage()" *ngIf="clickToPaginate">
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -154,19 +154,33 @@ $primary-color: #0062cc;
|
|||
}
|
||||
|
||||
.reading-section {
|
||||
height: 100vh;
|
||||
max-height: 100vh;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
//overflow: auto; // This will break progress reporting
|
||||
}
|
||||
|
||||
.book-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
// A bunch of resets so books render correctly
|
||||
::ng-deep .book-content {
|
||||
& a, & :link {
|
||||
color: blue;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-body {
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.chapter-title {
|
||||
padding-inline-start: 0px
|
||||
}
|
||||
|
||||
::ng-deep .scale-width {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
|
||||
// Click to Paginate styles
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { AfterViewInit, Component, ElementRef, HostListener, OnDestroy, OnInit, Renderer2, RendererStyleFlags2, ViewChild } from '@angular/core';
|
||||
import {Location} from '@angular/common';
|
||||
import { AfterViewInit, Component, ElementRef, HostListener, Inject, OnDestroy, OnInit, Renderer2, RendererStyleFlags2, ViewChild } from '@angular/core';
|
||||
import {DOCUMENT, Location} from '@angular/common';
|
||||
import { FormControl, FormGroup } from '@angular/forms';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
|
|
@ -243,7 +243,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
private seriesService: SeriesService, private readerService: ReaderService, private location: Location,
|
||||
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) {
|
||||
private scrollService: ScrollService, private utilityService: UtilityService, private libraryService: LibraryService,
|
||||
@Inject(DOCUMENT) private document: Document) {
|
||||
this.navService.hideNavBar();
|
||||
|
||||
this.darkModeStyleElem = this.renderer.createElement('style');
|
||||
|
|
@ -281,7 +282,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
});
|
||||
}
|
||||
|
||||
const bodyNode = document.querySelector('body');
|
||||
const bodyNode = this.document.querySelector('body');
|
||||
if (bodyNode !== undefined && bodyNode !== null) {
|
||||
this.originalBodyColor = bodyNode.style.background;
|
||||
}
|
||||
|
|
@ -296,14 +297,16 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
*/
|
||||
ngAfterViewInit() {
|
||||
// check scroll offset and if offset is after any of the "id" markers, save progress
|
||||
fromEvent(window, 'scroll')
|
||||
fromEvent(this.reader.nativeElement, 'scroll')
|
||||
.pipe(debounceTime(200), takeUntil(this.onDestroy)).subscribe((event) => {
|
||||
if (this.isLoading) return;
|
||||
|
||||
console.log('Scroll');
|
||||
|
||||
// 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 + (document.body.offsetHeight / 2);
|
||||
const verticalOffset = this.scrollService.scrollPosition + (this.document.body.offsetHeight / 2);
|
||||
|
||||
const alreadyReached = Object.values(this.pageAnchors).filter((i: number) => i <= verticalOffset);
|
||||
if (alreadyReached.length > 0) {
|
||||
|
|
@ -350,7 +353,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
const bodyNode = document.querySelector('body');
|
||||
const bodyNode = this.document.querySelector('body');
|
||||
if (bodyNode !== undefined && bodyNode !== null && this.originalBodyColor !== undefined) {
|
||||
bodyNode.style.background = this.originalBodyColor;
|
||||
if (this.user.preferences.siteDarkMode) {
|
||||
|
|
@ -359,7 +362,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
this.navService.showNavBar();
|
||||
|
||||
const head = document.querySelector('head');
|
||||
const head = this.document.querySelector('head');
|
||||
this.renderer.removeChild(head, this.darkModeStyleElem);
|
||||
|
||||
if (this.clickToPaginateVisualOverlayTimeout !== undefined) {
|
||||
|
|
@ -581,8 +584,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
resetSettings() {
|
||||
const windowWidth = window.innerWidth
|
||||
|| document.documentElement.clientWidth
|
||||
|| document.body.clientWidth;
|
||||
|| this.document.documentElement.clientWidth
|
||||
|| this.document.body.clientWidth;
|
||||
|
||||
let margin = '15%';
|
||||
if (windowWidth <= 700) {
|
||||
|
|
@ -631,7 +634,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
moveFocus() {
|
||||
const elems = document.getElementsByClassName('reading-section');
|
||||
const elems = this.document.getElementsByClassName('reading-section');
|
||||
if (elems.length > 0) {
|
||||
(elems[0] as HTMLDivElement).focus();
|
||||
}
|
||||
|
|
@ -679,10 +682,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
getPageMarkers(ids: Array<string>) {
|
||||
try {
|
||||
return document.querySelectorAll(ids.map(id => '#' + this.cleanIdSelector(id)).join(', '));
|
||||
return this.document.querySelectorAll(ids.map(id => '#' + this.cleanIdSelector(id)).join(', '));
|
||||
} catch (Exception) {
|
||||
// Fallback to anchors instead. Some books have ids that are not valid for querySelectors, so anchors should be used instead
|
||||
return document.querySelectorAll(ids.map(id => '[href="#' + id + '"]').join(', '));
|
||||
return this.document.querySelectorAll(ids.map(id => '[href="#' + id + '"]').join(', '));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -717,6 +720,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.setupPage(part, scrollTop);
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply scaling class to all images to ensure they scale down to max width to not blow out the reader
|
||||
Array.from(imgs).forEach(img => this.renderer.addClass(img, 'scale-width'));
|
||||
|
||||
Promise.all(Array.from(imgs)
|
||||
.filter(img => !img.complete)
|
||||
.map(img => new Promise(resolve => { img.onload = img.onerror = resolve; })))
|
||||
|
|
@ -868,14 +875,25 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
updateReaderStyles() {
|
||||
if (this.readingHtml != undefined && this.readingHtml.nativeElement) {
|
||||
for(let i = 0; i < this.readingHtml.nativeElement.children.length; i++) {
|
||||
const elem = this.readingHtml.nativeElement.children.item(i);
|
||||
if (elem?.tagName != 'STYLE') {
|
||||
Object.entries(this.pageStyles).forEach(item => {
|
||||
this.renderer.setStyle(elem, item[0], item[1], RendererStyleFlags2.Important);
|
||||
});
|
||||
// for(let i = 0; i < this.readingHtml.nativeElement.children.length; i++) {
|
||||
// const elem = this.readingHtml.nativeElement.children.item(i);
|
||||
// if (elem?.tagName != 'STYLE') {
|
||||
// Object.entries(this.pageStyles).forEach(item => {
|
||||
// if (item[1] == '100%' || item[1] == '0px' || item[1] == 'inherit') return;
|
||||
// this.renderer.setStyle(elem, item[0], item[1], RendererStyleFlags2.Important);
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
console.log('pageStyles: ', this.pageStyles);
|
||||
console.log('readingHtml: ', this.readingHtml.nativeElement);
|
||||
Object.entries(this.pageStyles).forEach(item => {
|
||||
if (item[1] == '100%' || item[1] == '0px' || item[1] == 'inherit') {
|
||||
// Remove the style or skip
|
||||
this.renderer.removeStyle(this.readingHtml.nativeElement, item[0]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.renderer.setStyle(this.readingHtml.nativeElement, item[0], item[1], RendererStyleFlags2.Important);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -903,7 +921,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
setOverrideStyles() {
|
||||
const bodyNode = document.querySelector('body');
|
||||
const bodyNode = this.document.querySelector('body');
|
||||
if (bodyNode !== undefined && bodyNode !== null) {
|
||||
if (this.user.preferences.siteDarkMode) {
|
||||
bodyNode.classList.remove('bg-dark');
|
||||
|
|
@ -912,7 +930,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
bodyNode.style.background = this.getDarkModeBackgroundColor();
|
||||
}
|
||||
this.backgroundColor = this.getDarkModeBackgroundColor();
|
||||
const head = document.querySelector('head');
|
||||
const head = this.document.querySelector('head');
|
||||
if (this.darkMode) {
|
||||
this.renderer.appendChild(head, this.darkModeStyleElem)
|
||||
} else {
|
||||
|
|
@ -948,7 +966,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
// Part selector is a XPATH
|
||||
element = this.getElementFromXPath(partSelector);
|
||||
} else {
|
||||
element = document.querySelector('*[id="' + partSelector + '"]');
|
||||
element = this.document.querySelector('*[id="' + partSelector + '"]');
|
||||
}
|
||||
|
||||
if (element === null) return;
|
||||
|
|
@ -984,7 +1002,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
getElementFromXPath(path: string) {
|
||||
const node = document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
const node = this.document.evaluate(path, this.document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
if (node?.nodeType === Node.ELEMENT_NODE) {
|
||||
return node as Element;
|
||||
}
|
||||
|
|
@ -994,7 +1012,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
getXPathTo(element: any): string {
|
||||
if (element === null) return '';
|
||||
if (element.id !== '') { return 'id("' + element.id + '")'; }
|
||||
if (element === document.body) { return element.tagName; }
|
||||
if (element === this.document.body) { return element.tagName; }
|
||||
|
||||
|
||||
let ix = 0;
|
||||
|
|
@ -1027,12 +1045,16 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
if (this.isFullscreen) {
|
||||
this.readerService.exitFullscreen(() => {
|
||||
this.isFullscreen = false;
|
||||
this.renderer.removeStyle(this.reader.nativeElement, 'background');
|
||||
});
|
||||
} else {
|
||||
this.readerService.enterFullscreen(this.reader.nativeElement, () => {
|
||||
this.isFullscreen = true;
|
||||
// HACK: This is a bug with how browsers change the background color for fullscreen mode
|
||||
if (!this.darkMode) {
|
||||
this.renderer.setStyle(this.reader.nativeElement, 'background', 'white');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue