From 094c12d43d90fbf5483c9409417926e5352c6b0a Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Wed, 10 Nov 2021 10:58:45 -0600 Subject: [PATCH] Webtoon Polish Part 2 (#739) * Change css scoping to be on the book content itself only to prevent any leaking on the reading section code. Apply a margin so that book margins on the whole content doesn't show black lines. * Take out debug outline on webtoon reader * Removed some imports * Fixed an issue where when restoring current page in webtoon mode, the page number would jump forward * Last page on webtoon reader now properly counts. This was due to a - 1 issue fixing previous issues. * Fixed an issue where scrollToPage (from progress bar or go to page) wouldn't work if the page somehow was visible. * Ready for testing on beta users --- API/Services/BookService.cs | 10 ++-- .../book-reader/book-reader.component.html | 2 +- .../infinite-scroller.component.ts | 56 ++++++++++--------- .../manga-reader/manga-reader.component.html | 2 +- .../manga-reader/manga-reader.component.ts | 4 +- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/API/Services/BookService.cs b/API/Services/BookService.cs index cb31f2cc8..137f21dca 100644 --- a/API/Services/BookService.cs +++ b/API/Services/BookService.cs @@ -30,6 +30,7 @@ namespace API.Services private readonly ILogger _logger; private readonly StylesheetParser _cssParser = new (); private static readonly RecyclableMemoryStreamManager StreamManager = new (); + private const string CssScopeClass = ".book-content"; public BookService(ILogger logger) { @@ -152,22 +153,23 @@ namespace API.Services EscapeCssImageReferences(ref stylesheetHtml, apiBase, book); var styleContent = RemoveWhiteSpaceFromStylesheets(stylesheetHtml); - styleContent = styleContent.Replace("body", ".reading-section"); + + styleContent = styleContent.Replace("body", CssScopeClass); if (string.IsNullOrEmpty(styleContent)) return string.Empty; var stylesheet = await _cssParser.ParseAsync(styleContent); foreach (var styleRule in stylesheet.StyleRules) { - if (styleRule.Selector.Text == ".reading-section") continue; + if (styleRule.Selector.Text == CssScopeClass) continue; if (styleRule.Selector.Text.Contains(",")) { styleRule.Text = styleRule.Text.Replace(styleRule.SelectorText, string.Join(", ", - styleRule.Selector.Text.Split(",").Select(s => ".reading-section " + s))); + styleRule.Selector.Text.Split(",").Select(s => $"{CssScopeClass} " + s))); continue; } - styleRule.Text = ".reading-section " + styleRule.Text; + styleRule.Text = $"{CssScopeClass} " + styleRule.Text; } return RemoveWhiteSpaceFromStylesheets(stylesheet.ToCss()); } diff --git a/UI/Web/src/app/book-reader/book-reader/book-reader.component.html b/UI/Web/src/app/book-reader/book-reader/book-reader.component.html index 5f209490e..f014507d4 100644 --- a/UI/Web/src/app/book-reader/book-reader/book-reader.component.html +++ b/UI/Web/src/app/book-reader/book-reader/book-reader.component.html @@ -100,7 +100,7 @@
-
+
diff --git a/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.ts b/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.ts index 62309d310..2fd52520f 100644 --- a/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.ts +++ b/UI/Web/src/app/manga-reader/infinite-scroller/infinite-scroller.component.ts @@ -1,8 +1,6 @@ import { Component, EventEmitter, Input, OnChanges, OnDestroy, OnInit, Output, Renderer2, SimpleChanges } from '@angular/core'; -import { ToastrService } from 'ngx-toastr'; import { BehaviorSubject, fromEvent, ReplaySubject, Subject } from 'rxjs'; import { debounceTime, takeUntil } from 'rxjs/operators'; -import { UtilityService } from 'src/app/shared/_services/utility.service'; import { ReaderService } from '../../_services/reader.service'; import { PAGING_DIRECTION } from '../_models/reader-enums'; import { WebtoonImage } from '../_models/webtoon-image'; @@ -119,7 +117,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { /** * Debug mode. Will show extra information. Use bitwise (|) operators between different modes to enable different output */ - debugMode: DEBUG_MODES = DEBUG_MODES.Outline; + debugMode: DEBUG_MODES = DEBUG_MODES.None; get minPageLoaded() { return Math.min(...Object.values(this.imagesLoaded)); @@ -138,7 +136,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { private readonly onDestroy = new Subject(); - constructor(private readerService: ReaderService, private renderer: Renderer2, private utilityService: UtilityService) {} + constructor(private readerService: ReaderService, private renderer: Renderer2) {} ngOnChanges(changes: SimpleChanges): void { if (changes.hasOwnProperty('totalPages') && changes['totalPages'].previousValue != changes['totalPages'].currentValue) { @@ -197,11 +195,6 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { || document.documentElement.scrollTop || document.body.scrollTop || 0); - if (this.isScrolling && this.currentPageElem != null && this.isElementVisible(this.currentPageElem)) { - this.debugLog('[Scroll] Image is visible from scroll, isScrolling is now false'); - this.isScrolling = false; - } - if (verticalOffset > this.prevScrollPosition) { this.scrollingDirection = PAGING_DIRECTION.FORWARD; } else { @@ -209,15 +202,23 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { } this.prevScrollPosition = verticalOffset; - // Use offset of the image against the scroll container to test if the most of the image is visible on the screen. We can use this - // to mark the current page and separate the prefetching code. - const midlineImages = Array.from(document.querySelectorAll('img[id^="page-"]')) - .filter(entry => this.shouldElementCountAsCurrentPage(entry)); - - if (midlineImages.length > 0) { - this.setPageNum(parseInt(midlineImages[0].getAttribute('page') || this.pageNum + '', 10)); + if (this.isScrolling && this.currentPageElem != null && this.isElementVisible(this.currentPageElem)) { + this.debugLog('[Scroll] Image is visible from scroll, isScrolling is now false'); + this.isScrolling = false; } + if (!this.isScrolling) { + // Use offset of the image against the scroll container to test if the most of the image is visible on the screen. We can use this + // to mark the current page and separate the prefetching code. + const midlineImages = Array.from(document.querySelectorAll('img[id^="page-"]')) + .filter(entry => this.shouldElementCountAsCurrentPage(entry)); + + if (midlineImages.length > 0) { + this.setPageNum(parseInt(midlineImages[0].getAttribute('page') || this.pageNum + '', 10)); + } + } + + // Check if we hit the last page this.checkIfShouldTriggerContinuousReader(); @@ -389,8 +390,6 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { this.debugLog('[Intersection] Page ' + imagePage + ' is visible: ', entry.isIntersecting); if (entry.isIntersecting) { this.debugLog('[Intersection] ! Page ' + imagePage + ' just entered screen'); - //this.setPageNum(imagePage); - // ?! Changing this so that this just triggers a prefetch this.prefetchWebtoonImages(imagePage); } }); @@ -414,10 +413,9 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { if (scrollToPage) { const currentImage = document.querySelector('img#page-' + this.pageNum); - if (currentImage !== null && !this.isElementVisible(currentImage)) { - this.debugLog('[GoToPage] Scrolling to page', this.pageNum); - this.scrollToCurrentPage(); - } + if (currentImage === null) return; + this.debugLog('[GoToPage] Scrolling to page', this.pageNum); + this.scrollToCurrentPage(); } } @@ -478,6 +476,12 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { } } + /** + * Finds the ranges of indecies to load from backend. totalPages - 1 is due to backend will automatically return last page for any page number + * above totalPages. Webtoon reader might ask for that which results in duplicate last pages. + * @param pageNum + * @returns + */ calculatePrefetchIndecies(pageNum: number = -1) { if (pageNum == -1) { pageNum = this.pageNum; @@ -486,15 +490,15 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy { let startingIndex = 0; let endingIndex = 0; if (this.isScrollingForwards()) { - startingIndex = Math.min(Math.max(pageNum - this.bufferPages, 0), this.totalPages); - endingIndex = Math.min(Math.max(pageNum + this.bufferPages, 0), this.totalPages); + startingIndex = Math.min(Math.max(pageNum - this.bufferPages, 0), this.totalPages - 1); + endingIndex = Math.min(Math.max(pageNum + this.bufferPages, 0), this.totalPages - 1); if (startingIndex === this.totalPages) { return [0, 0]; } } else { - startingIndex = Math.min(Math.max(pageNum - this.bufferPages, 0), this.totalPages); - endingIndex = Math.min(Math.max(pageNum + this.bufferPages, 0), this.totalPages); + startingIndex = Math.min(Math.max(pageNum - this.bufferPages, 0), this.totalPages - 1); + endingIndex = Math.min(Math.max(pageNum + this.bufferPages, 0), this.totalPages - 1); } diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.html b/UI/Web/src/app/manga-reader/manga-reader.component.html index 74f222882..0ca77dd92 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.html +++ b/UI/Web/src/app/manga-reader/manga-reader.component.html @@ -32,7 +32,7 @@ [bufferPages]="5" [goToPage]="goToPageEvent" (pageNumberChange)="handleWebtoonPageChange($event)" - [totalPages]="maxPages - 1" + [totalPages]="maxPages" [urlProvider]="getPageUrl" (loadNextChapter)="loadNextChapter()" (loadPrevChapter)="loadPrevChapter()" diff --git a/UI/Web/src/app/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/manga-reader.component.ts index 88cad1623..08a3a5dc4 100644 --- a/UI/Web/src/app/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/manga-reader.component.ts @@ -20,7 +20,7 @@ import { ChangeContext, LabelType, Options } from '@angular-slider/ngx-slider'; import { trigger, state, style, transition, animate } from '@angular/animations'; import { ChapterInfo } from './_models/chapter-info'; import { COLOR_FILTER, FITTING_OPTION, PAGING_DIRECTION, SPLIT_PAGE_PART } from './_models/reader-enums'; -import { Preferences, scalingOptions } from '../_models/preferences/preferences'; +import { scalingOptions } from '../_models/preferences/preferences'; import { READER_MODE } from '../_models/preferences/reader-mode'; import { MangaFormat } from '../_models/manga-format'; import { LibraryService } from '../_services/library.service'; @@ -950,7 +950,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { sliderDragUpdate(context: ChangeContext) { // This will update the value for value except when in webtoon due to how the webtoon reader // responds to page changes - if (this.readerMode != READER_MODE.WEBTOON) { + if (this.readerMode !== READER_MODE.WEBTOON) { this.setPageNum(context.value); } }