Double Page Layout Fixes (#1380)

* Fixed an issue where sometimes when loading the next page, the pagination area wouldn't be properly setup due to a missed rendering cycle

* Refactored BookController to thin it out and refactor some of the functions to apply IOC. Added some split query statements on a few queries.

* Added Split Query to many queries

* Added a visual indicator for loading state of PDF. Will spruce up css later.

* Added back in logic

* Fixed flash of white when refreshing browser

* Hooked in a loading progress bar for the pdf reader

* Close the pdf reader when pressing ESC

* Code is completely broken. Slowly rewriting the double page reader.

* Fixed a weird scenario where double page reader wouldn't work with using prefetched images. The image would be fine at setting but at some point, the actual image would render as +1.

* Fixed up manga reader for double including a bug with double (manga) where bookmarking wouldn't bookmark both pages
This commit is contained in:
Joseph Milazzo 2022-07-17 11:13:19 -04:00 committed by GitHub
parent 35147d748c
commit 0a4252af32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -123,16 +123,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
isLoading = true;
/**
* A temp variable to allow us to update isLoose.
*/
pageAmount = 0;
/**
* For double page layout, if only one page will be rendered.
*/
isLoose = false;
private ctx!: CanvasRenderingContext2D;
/**
* Used to render a page on the canvas or in the image tag. This Image element is prefetched by the cachedImages buffer.
@ -276,10 +266,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
* A map of bookmarked pages to anything. Used for O(1) lookup time if a page is bookmarked or not.
*/
bookmarks: {[key: string]: number} = {};
/**
* Tracks if the first page is rendered or not. This is used to keep track of Automatic Scaling and adjusting decision after first page dimensions load up.
*/
firstPageRendered: boolean = false;
/**
* Library Type used for rendering chapter or issue
*/
@ -320,26 +306,33 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
* @remarks This will always fail if the window's width is greater than the height
*/
get ShouldRenderDoublePage() {
// window.innerWidth > window.innerHeight // Don't render double if orientation is portrait, mostly mobile
return (
this.layoutMode === LayoutMode.Double &&
!this.isCoverImage() &&
!this.isWideImage(this.canvasImage) &&
!this.isWideImage(this.canvasImageNext)
if (this.layoutMode !== LayoutMode.Double) return false;
return !(
this.isCoverImage()
|| this.isWideImage(this.canvasImage)
|| this.isWideImage(this.canvasImageNext)
);
}
/**
* We should Render 2 pages if:
* 1. We are not currently the first image (cover image)
* 2. The previous page is not a cover image
* 3. The current page is not a wide image
* 4. The next page is not a wide image
*/
get ShouldRenderReverseDouble() {
// NOTE: I'm not going to care about window.innerWidth > window.innerHeight since a higher level handler will manage
// window.innerWidth > window.innerHeight // Don't render double reversed if orientation is portrait, mostly mobile
return (
this.layoutMode === LayoutMode.DoubleReversed &&
!this.isCoverImage() &&
!this.isCoverImage(this.pageNum - 1) &&
!this.isWideImage(this.canvasImage) &&
!this.isWideImage(this.canvasImageNext) &&
!this.isLoose
if (this.layoutMode !== LayoutMode.DoubleReversed) return false;
const result = !(
this.isCoverImage()
|| this.isCoverImage(this.pageNum - 1)
|| this.isWideImage(this.canvasImage)
|| this.isWideImage(this.canvasImageNext)
);
return result;
}
get CurrentPageBookmarked() {
@ -981,26 +974,36 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
event.preventDefault();
}
const notInSplit = this.currentImageSplitPart !== (this.isSplitLeftToRight() ? SPLIT_PAGE_PART.LEFT_PART : SPLIT_PAGE_PART.RIGHT_PART);
let pageAmount = 1;
if (this.layoutMode === LayoutMode.Double) {
pageAmount = (
!this.isCoverImage() &&
!this.isWideImage() &&
!this.isWideImage(this.canvasImageNext) &&
!this.isSecondLastImage() &&
!this.isLastImage()
? 2 : 1);
} else if (this.layoutMode === LayoutMode.DoubleReversed) {
pageAmount = (
!this.isWideImage(this.canvasImageNext) &&
!this.isWideImage(this.canvasImageAheadBy2) && // Remember we are doing this logic before we've hit the next page, so we need this
!this.isSecondLastImage() &&
!this.isLastImage()
? 2 : 1);
// If we are on the cover image, always do 1 page
if (!this.isCoverImage()) {
if (this.layoutMode === LayoutMode.Double) {
pageAmount = (
!this.isCoverImage() &&
!this.isWideImage() &&
!this.isWideImage(this.canvasImageNext) &&
!this.isSecondLastImage() &&
!this.isLastImage()
? 2 : 1);
} else if (this.layoutMode === LayoutMode.DoubleReversed) {
// Move forward by 1 pages if:
// 1. The next page is a wide image
// 2. The next page + 1 is a wide image (why do we care at this point?)
// 3. We are on the second to last page
// 4. We are on the last page
pageAmount = !(
this.isWideImage(this.canvasImageNext)
|| this.isWideImage(this.canvasImageAheadBy2) // Remember we are doing this logic before we've hit the next page, so we need this
|| this.isSecondLastImage()
|| this.isLastImage()
) ? 2 : 1;
}
}
const notInSplit = this.currentImageSplitPart !== (this.isSplitLeftToRight() ? SPLIT_PAGE_PART.LEFT_PART : SPLIT_PAGE_PART.RIGHT_PART);
if ((this.pageNum + pageAmount >= this.maxPages && notInSplit) || this.isLoading) {
if (this.isLoading) { return; }
@ -1013,16 +1016,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.pagingDirection = PAGING_DIRECTION.FORWARD;
if (this.isNoSplit() || notInSplit) {
this.setPageNum(this.pageNum + pageAmount);
this.pageAmount = pageAmount;
if (this.readerMode !== ReaderMode.Webtoon) {
this.setCanvasImage();
}
}
if (this.readerMode !== ReaderMode.Webtoon) {
this.loadPage();
}
this.loadPage();
}
prevPage(event?: any) {
@ -1035,18 +1031,18 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
let pageAmount = 1;
if (this.layoutMode === LayoutMode.Double) {
pageAmount = (
!this.isCoverImage() &&
!this.isWideImage(this.canvasImagePrev)
? 2 : 1);
pageAmount = !(
this.isCoverImage()
|| this.isWideImage(this.canvasImagePrev)
) ? 2 : 1;
}
if (this.layoutMode === LayoutMode.DoubleReversed) {
pageAmount = (
!this.isCoverImage() &&
!this.isCoverImage(this.pageNum - 1) &&
!this.isWideImage(this.canvasImage) && // JOE: At this point, these aren't yet set to the new values
!this.isWideImage(this.canvasImageNext)
? 2 : 1);
pageAmount = !(
this.isCoverImage()
|| this.isCoverImage(this.pageNum - 1)
|| this.isWideImage(this.canvasImage) // JOE: At this point, these aren't yet set to the new values
|| this.isWideImage(this.canvasImagePrev) // This should be Prev, if prev image (original: canvasImageNext)
) ? 2 : 1;
}
if ((this.pageNum - 1 < 0 && notInSplit) || this.isLoading) {
@ -1060,26 +1056,27 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
this.pagingDirection = PAGING_DIRECTION.BACKWARDS;
if (this.isNoSplit() || notInSplit) {
this.setPageNum(this.pageNum - pageAmount);
if (this.readerMode !== ReaderMode.Webtoon) {
this.setCanvasImage();
}
}
if (this.readerMode !== ReaderMode.Webtoon) {
this.loadPage();
}
this.loadPage();
}
/**
* Sets canvasImage's src to current page, but first attempts to use a pre-fetched image
*/
setCanvasImage() {
const img = this.cachedImages.arr.find(img => this.readerService.imageUrlToPageNum(img.src) === this.pageNum);
if (img) {
this.canvasImage = img;
if (this.layoutMode === LayoutMode.Single) {
const img = this.cachedImages.arr.find(img => this.readerService.imageUrlToPageNum(img.src) === this.pageNum);
if (img) {
this.canvasImage = img; // If we tried to use this for double, then the src might change after being set (idk how tho)
} else {
this.canvasImage.src = this.getPageUrl(this.pageNum);
}
} else {
this.canvasImage.src = this.getPageUrl(this.pageNum);
}
this.canvasImage.onload = () => {
this.cdRef.markForCheck();
};
@ -1186,18 +1183,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
renderPage() {
const pageSplit = parseInt(this.generalSettingsForm.get('pageSplitOption')?.value, 10);
if (!this.ctx || !this.canvas || (pageSplit === PageSplitOption.FitSplit || pageSplit === PageSplitOption.NoSplit)) {
if (this.getFit() !== FITTING_OPTION.HEIGHT) {
this.readingArea.nativeElement.scroll(0,0);
}
this.isLoading = false;
this.cdRef.markForCheck();
return;
}
const needsSplitting = this.isWideImage();
if (!needsSplitting) {
if (!this.ctx || !this.canvas || this.isNoSplit() || !needsSplitting) {
this.renderWithCanvas = false;
if (this.getFit() !== FITTING_OPTION.HEIGHT) {
this.readingArea.nativeElement.scroll(0,0);
@ -1207,6 +1195,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
return;
}
this.renderWithCanvas = true;
this.canvasImage.onload = null;
this.setCanvasSize();
@ -1215,12 +1204,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (needsSplitting && this.currentImageSplitPart === SPLIT_PAGE_PART.LEFT_PART) {
this.canvas.nativeElement.width = this.canvasImage.width / 2;
this.ctx.drawImage(this.canvasImage, 0, 0, this.canvasImage.width, this.canvasImage.height, 0, 0, this.canvasImage.width, this.canvasImage.height);
this.renderWithCanvas = true;
this.cdRef.markForCheck();
} else if (needsSplitting && this.currentImageSplitPart === SPLIT_PAGE_PART.RIGHT_PART) {
this.canvas.nativeElement.width = this.canvasImage.width / 2;
this.ctx.drawImage(this.canvasImage, 0, 0, this.canvasImage.width, this.canvasImage.height, -this.canvasImage.width / 2, 0, this.canvasImage.width, this.canvasImage.height);
this.renderWithCanvas = true;
this.cdRef.markForCheck();
}
@ -1253,7 +1240,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
newScale = FITTING_OPTION.HEIGHT;
}
this.firstPageRendered = true;
this.generalSettingsForm.get('fittingOption')?.setValue(newScale, {emitEvent: false});
}
@ -1326,11 +1312,13 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
loadPage() {
if (this.readerMode === ReaderMode.Webtoon) return;
this.isLoading = true;
this.canvasImage2.src = '';
this.canvasImageAheadBy2.src = '';
this.isLoose = (this.pageAmount === 1 ? true : false);
this.setCanvasImage();
@ -1409,14 +1397,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.nextChapterId > 0 && !this.nextChapterPrefetched) {
this.readerService.getChapterInfo(this.nextChapterId).pipe(take(1)).subscribe(res => {
this.nextChapterPrefetched = true;
this.cdRef.markForCheck();
});
}
} else if (this.pageNum <= 10) {
if (this.prevChapterId > 0 && !this.prevChapterPrefetched) {
this.readerService.getChapterInfo(this.prevChapterId).pipe(take(1)).subscribe(res => {
this.prevChapterPrefetched = true;
this.cdRef.markForCheck();
});
}
}
@ -1469,14 +1455,12 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
if (this.isFullscreen) {
this.readerService.exitFullscreen(() => {
this.isFullscreen = false;
this.firstPageRendered = false;
this.fullscreenEvent.next(false);
this.render();
});
} else {
this.readerService.enterFullscreen(this.reader.nativeElement, () => {
this.isFullscreen = true;
this.firstPageRendered = false;
this.fullscreenEvent.next(true);
this.render();
});
@ -1536,16 +1520,17 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
*/
bookmarkPage() {
const pageNum = this.pageNum;
const isDouble = this.layoutMode === LayoutMode.Double || this.layoutMode === LayoutMode.DoubleReversed;
if (this.CurrentPageBookmarked) {
let apis = [this.readerService.unbookmark(this.seriesId, this.volumeId, this.chapterId, pageNum)];
if (this.layoutMode === LayoutMode.Double) apis.push(this.readerService.unbookmark(this.seriesId, this.volumeId, this.chapterId, pageNum + 1));
if (isDouble) apis.push(this.readerService.unbookmark(this.seriesId, this.volumeId, this.chapterId, pageNum + 1));
forkJoin(apis).pipe(take(1)).subscribe(() => {
delete this.bookmarks[pageNum];
});
} else {
let apis = [this.readerService.bookmark(this.seriesId, this.volumeId, this.chapterId, pageNum)];
if (this.layoutMode === LayoutMode.Double) apis.push(this.readerService.bookmark(this.seriesId, this.volumeId, this.chapterId, pageNum + 1));
if (isDouble) apis.push(this.readerService.bookmark(this.seriesId, this.volumeId, this.chapterId, pageNum + 1));
forkJoin(apis).pipe(take(1)).subscribe(() => {
this.bookmarks[pageNum] = 1;
});