From 37cd94b07a4cf80da450a0467da41d6f14446c56 Mon Sep 17 00:00:00 2001 From: Joseph Milazzo Date: Wed, 4 Jun 2025 17:13:58 -0500 Subject: [PATCH] Fixed the webtoon reader issue by moving the reading profile into a resolver. Refactored the book reader to align with this new method as well. --- .../_resolvers/reading-profile.resolver.ts | 18 ++ .../app/_routes/book-reader.router.module.ts | 8 +- .../app/_routes/manga-reader.router.module.ts | 15 +- .../app/_routes/pdf-reader.router.module.ts | 8 +- .../book-reader/book-reader.component.html | 1 + .../book-reader/book-reader.component.ts | 17 +- .../reader-settings.component.ts | 119 +++---- .../manga-reader/manga-reader.component.html | 152 +++++---- .../manga-reader/manga-reader.component.ts | 302 ++++++++++++------ .../pdf-reader/pdf-reader.component.ts | 25 +- 10 files changed, 415 insertions(+), 250 deletions(-) create mode 100644 UI/Web/src/app/_resolvers/reading-profile.resolver.ts diff --git a/UI/Web/src/app/_resolvers/reading-profile.resolver.ts b/UI/Web/src/app/_resolvers/reading-profile.resolver.ts new file mode 100644 index 000000000..1d28adf95 --- /dev/null +++ b/UI/Web/src/app/_resolvers/reading-profile.resolver.ts @@ -0,0 +1,18 @@ +import {Injectable} from '@angular/core'; +import {ActivatedRouteSnapshot, Resolve, RouterStateSnapshot} from '@angular/router'; +import {Observable} from 'rxjs'; +import {ReadingProfileService} from "../_services/reading-profile.service"; + +@Injectable({ + providedIn: 'root' +}) +export class ReadingProfileResolver implements Resolve { + + constructor(private readingProfileService: ReadingProfileService) {} + + resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + // Extract seriesId from route params or parent route + const seriesId = route.params['seriesId'] || route.parent?.params['seriesId']; + return this.readingProfileService.getForSeries(seriesId); + } +} diff --git a/UI/Web/src/app/_routes/book-reader.router.module.ts b/UI/Web/src/app/_routes/book-reader.router.module.ts index 5083c2d4a..c9d6262ad 100644 --- a/UI/Web/src/app/_routes/book-reader.router.module.ts +++ b/UI/Web/src/app/_routes/book-reader.router.module.ts @@ -1,10 +1,14 @@ -import { Routes } from '@angular/router'; -import { BookReaderComponent } from '../book-reader/_components/book-reader/book-reader.component'; +import {Routes} from '@angular/router'; +import {BookReaderComponent} from '../book-reader/_components/book-reader/book-reader.component'; +import {ReadingProfileResolver} from "../_resolvers/reading-profile.resolver"; export const routes: Routes = [ { path: ':chapterId', component: BookReaderComponent, + resolve: { + readingProfile: ReadingProfileResolver + } } ]; diff --git a/UI/Web/src/app/_routes/manga-reader.router.module.ts b/UI/Web/src/app/_routes/manga-reader.router.module.ts index 04ff77b3c..e479e8ae6 100644 --- a/UI/Web/src/app/_routes/manga-reader.router.module.ts +++ b/UI/Web/src/app/_routes/manga-reader.router.module.ts @@ -1,15 +1,22 @@ -import { Routes } from '@angular/router'; -import { MangaReaderComponent } from '../manga-reader/_components/manga-reader/manga-reader.component'; +import {Routes} from '@angular/router'; +import {MangaReaderComponent} from '../manga-reader/_components/manga-reader/manga-reader.component'; +import {ReadingProfileResolver} from "../_resolvers/reading-profile.resolver"; export const routes: Routes = [ { path: ':chapterId', - component: MangaReaderComponent + component: MangaReaderComponent, + resolve: { + readingProfile: ReadingProfileResolver + } }, { // This will allow the MangaReader to have a list to use for next/prev chapters rather than natural sort order path: ':chapterId/list/:listId', - component: MangaReaderComponent + component: MangaReaderComponent, + resolve: { + readingProfile: ReadingProfileResolver + } } ]; diff --git a/UI/Web/src/app/_routes/pdf-reader.router.module.ts b/UI/Web/src/app/_routes/pdf-reader.router.module.ts index a55699280..7cb9f68e2 100644 --- a/UI/Web/src/app/_routes/pdf-reader.router.module.ts +++ b/UI/Web/src/app/_routes/pdf-reader.router.module.ts @@ -1,9 +1,13 @@ -import { Routes } from '@angular/router'; -import { PdfReaderComponent } from '../pdf-reader/_components/pdf-reader/pdf-reader.component'; +import {Routes} from '@angular/router'; +import {PdfReaderComponent} from '../pdf-reader/_components/pdf-reader/pdf-reader.component'; +import {ReadingProfileResolver} from "../_resolvers/reading-profile.resolver"; export const routes: Routes = [ { path: ':chapterId', component: PdfReaderComponent, + resolve: { + readingProfile: ReadingProfileResolver + } } ]; diff --git a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html index 99191a5e1..cda2aeb4d 100644 --- a/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html +++ b/UI/Web/src/app/book-reader/_components/book-reader/book-reader.component.html @@ -61,6 +61,7 @@ { + this.readingProfile = data['readingProfile']; + if (this.readingProfile == null) { + this.router.navigateByUrl('/home'); + return; + } + //this.setupReaderSettings(); // TODO: Implement this Amelia + this.cdRef.markForCheck(); + }); + this.libraryId = parseInt(libraryId, 10); this.seriesId = parseInt(seriesId, 10); @@ -668,7 +680,10 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.chapters = results.chapters; this.pageNum = results.progress.pageNum; this.cdRef.markForCheck(); - if (results.progress.bookScrollId) this.lastSeenScrollPartPath = results.progress.bookScrollId; + + if (results.progress.bookScrollId) { + this.lastSeenScrollPartPath = results.progress.bookScrollId; + } this.continuousChaptersStack.push(this.chapterId); diff --git a/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts b/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts index 6c550b8ef..646c38d39 100644 --- a/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts +++ b/UI/Web/src/app/book-reader/_components/reader-settings/reader-settings.component.ts @@ -1,31 +1,41 @@ -import { DOCUMENT, NgFor, NgTemplateOutlet, NgIf, NgClass, NgStyle, TitleCasePipe } from '@angular/common'; +import {DOCUMENT, NgClass, NgFor, NgIf, NgStyle, NgTemplateOutlet, TitleCasePipe} from '@angular/common'; import { ChangeDetectionStrategy, ChangeDetectorRef, - Component, DestroyRef, + Component, + DestroyRef, EventEmitter, inject, - Inject, Input, + Inject, + Input, OnInit, Output } from '@angular/core'; -import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms'; +import {FormControl, FormGroup, ReactiveFormsModule} from '@angular/forms'; import {skip, take} from 'rxjs'; -import { BookPageLayoutMode } from 'src/app/_models/readers/book-page-layout-mode'; -import { BookTheme } from 'src/app/_models/preferences/book-theme'; -import { ReadingDirection } from 'src/app/_models/preferences/reading-direction'; -import { WritingStyle } from 'src/app/_models/preferences/writing-style'; -import { ThemeProvider } from 'src/app/_models/preferences/site-theme'; -import { User } from 'src/app/_models/user'; -import { AccountService } from 'src/app/_services/account.service'; -import { ThemeService } from 'src/app/_services/theme.service'; -import { FontFamily, BookService } from '../../_services/book.service'; -import { BookBlackTheme } from '../../_models/book-black-theme'; -import { BookDarkTheme } from '../../_models/book-dark-theme'; -import { BookWhiteTheme } from '../../_models/book-white-theme'; -import { BookPaperTheme } from '../../_models/book-paper-theme'; +import {BookPageLayoutMode} from 'src/app/_models/readers/book-page-layout-mode'; +import {BookTheme} from 'src/app/_models/preferences/book-theme'; +import {ReadingDirection} from 'src/app/_models/preferences/reading-direction'; +import {WritingStyle} from 'src/app/_models/preferences/writing-style'; +import {ThemeProvider} from 'src/app/_models/preferences/site-theme'; +import {User} from 'src/app/_models/user'; +import {AccountService} from 'src/app/_services/account.service'; +import {ThemeService} from 'src/app/_services/theme.service'; +import {BookService, FontFamily} from '../../_services/book.service'; +import {BookBlackTheme} from '../../_models/book-black-theme'; +import {BookDarkTheme} from '../../_models/book-dark-theme'; +import {BookWhiteTheme} from '../../_models/book-white-theme'; +import {BookPaperTheme} from '../../_models/book-paper-theme'; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; -import { NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody, NgbTooltip } from '@ng-bootstrap/ng-bootstrap'; +import { + NgbAccordionBody, + NgbAccordionButton, + NgbAccordionCollapse, + NgbAccordionDirective, + NgbAccordionHeader, + NgbAccordionItem, + NgbTooltip +} from '@ng-bootstrap/ng-bootstrap'; import {TranslocoDirective} from "@jsverse/transloco"; import {ReadingProfileService} from "../../../_services/reading-profile.service"; import {ReadingProfile} from "../../../_models/preferences/reading-profiles"; @@ -92,10 +102,13 @@ const mobileBreakpointMarginOverride = 700; templateUrl: './reader-settings.component.html', styleUrls: ['./reader-settings.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, - imports: [ReactiveFormsModule, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionButton, NgbAccordionCollapse, NgbAccordionBody, NgFor, NgbTooltip, NgTemplateOutlet, NgIf, NgClass, NgStyle, TitleCasePipe, TranslocoDirective] + imports: [ReactiveFormsModule, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionButton, + NgbAccordionCollapse, NgbAccordionBody, NgFor, NgbTooltip, NgTemplateOutlet, NgIf, NgClass, NgStyle, + TitleCasePipe, TranslocoDirective] }) export class ReaderSettingsComponent implements OnInit { @Input({required:true}) seriesId!: number; + @Input({required:true}) readingProfile!: ReadingProfile; /** * Outputs when clickToPaginate is changed */ @@ -129,8 +142,6 @@ export class ReaderSettingsComponent implements OnInit { */ @Output() immersiveMode: EventEmitter = new EventEmitter(); - readingProfile: ReadingProfile | null = null; - user!: User; /** * List of all font families user can select from @@ -172,8 +183,6 @@ export class ReaderSettingsComponent implements OnInit { return WritingStyle; } - - constructor(private bookService: BookService, private accountService: AccountService, @Inject(DOCUMENT) private document: Document, private themeService: ThemeService, private readonly cdRef: ChangeDetectorRef, private readingProfileService: ReadingProfileService) {} @@ -184,44 +193,40 @@ export class ReaderSettingsComponent implements OnInit { this.fontOptions = this.fontFamilies.map(f => f.title); this.cdRef.markForCheck(); - this.readingProfileService.getForSeries(this.seriesId).subscribe(profile => { - this.readingProfile = profile; + if (this.readingProfile.bookReaderFontFamily === undefined) { + this.readingProfile.bookReaderFontFamily = 'default'; + } + if (this.readingProfile.bookReaderFontSize === undefined || this.readingProfile.bookReaderFontSize < 50) { + this.readingProfile.bookReaderFontSize = 100; + } + if (this.readingProfile.bookReaderLineSpacing === undefined || this.readingProfile.bookReaderLineSpacing < 100) { + this.readingProfile.bookReaderLineSpacing = 100; + } + if (this.readingProfile.bookReaderMargin === undefined) { + this.readingProfile.bookReaderMargin = 0; + } + if (this.readingProfile.bookReaderReadingDirection === undefined) { + this.readingProfile.bookReaderReadingDirection = ReadingDirection.LeftToRight; + } + if (this.readingProfile.bookReaderWritingStyle === undefined) { + this.readingProfile.bookReaderWritingStyle = WritingStyle.Horizontal; + } + this.readingDirectionModel = this.readingProfile.bookReaderReadingDirection; + this.writingStyleModel = this.readingProfile.bookReaderWritingStyle; - if (this.readingProfile.bookReaderFontFamily === undefined) { - this.readingProfile.bookReaderFontFamily = 'default'; - } - if (this.readingProfile.bookReaderFontSize === undefined || this.readingProfile.bookReaderFontSize < 50) { - this.readingProfile.bookReaderFontSize = 100; - } - if (this.readingProfile.bookReaderLineSpacing === undefined || this.readingProfile.bookReaderLineSpacing < 100) { - this.readingProfile.bookReaderLineSpacing = 100; - } - if (this.readingProfile.bookReaderMargin === undefined) { - this.readingProfile.bookReaderMargin = 0; - } - if (this.readingProfile.bookReaderReadingDirection === undefined) { - this.readingProfile.bookReaderReadingDirection = ReadingDirection.LeftToRight; - } - if (this.readingProfile.bookReaderWritingStyle === undefined) { - this.readingProfile.bookReaderWritingStyle = WritingStyle.Horizontal; - } - this.readingDirectionModel = this.readingProfile.bookReaderReadingDirection; - this.writingStyleModel = this.readingProfile.bookReaderWritingStyle; + this.setupSettings(); - this.setupSettings(); + this.setTheme(this.readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme, false); + this.cdRef.markForCheck(); - this.setTheme(this.readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme, false); - this.cdRef.markForCheck(); + // Emit first time so book reader gets the setting + this.readingDirection.emit(this.readingDirectionModel); + this.bookReaderWritingStyle.emit(this.writingStyleModel); + this.clickToPaginateChanged.emit(this.readingProfile.bookReaderTapToPaginate); + this.layoutModeUpdate.emit(this.readingProfile.bookReaderLayoutMode); + this.immersiveMode.emit(this.readingProfile.bookReaderImmersiveMode); - // Emit first time so book reader gets the setting - this.readingDirection.emit(this.readingDirectionModel); - this.bookReaderWritingStyle.emit(this.writingStyleModel); - this.clickToPaginateChanged.emit(this.readingProfile.bookReaderTapToPaginate); - this.layoutModeUpdate.emit(this.readingProfile.bookReaderLayoutMode); - this.immersiveMode.emit(this.readingProfile.bookReaderImmersiveMode); - - this.resetSettings(); - }) + this.resetSettings(); this.accountService.currentUser$.pipe(take(1)).subscribe(user => { if (user) { diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html index 7f12e3be6..1d93f36bc 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.html @@ -53,96 +53,94 @@
- @if (readingProfile !== null) { - @if (readerMode !== ReaderMode.Webtoon) { -
- - -
+ @if (readerMode !== ReaderMode.Webtoon) { +
+ + +
- -
-
- @if (showClickOverlay) { -
- -
- } -
-
+
+ @if (showClickOverlay) { +
+ +
+ } +
+
- @if (showClickOverlay) { -
- -
- } -
+ @if (showClickOverlay) { +
+ +
+ }
+
-
- - +
+ + - - + + - - + + - - + + +
+ } @else { + @if (!isLoading && !inSetup) { +
+ +
- } @else { - @if (!isLoading && !inSetup) { -
- - -
- } } - } + }
- @if (menuOpen && readingProfile !== null) { + @if (menuOpen) {
@if (pageOptions !== undefined && pageOptions.ceil !== undefined) {
diff --git a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts index ebfcc84ad..688c5c4f8 100644 --- a/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts +++ b/UI/Web/src/app/manga-reader/_components/manga-reader/manga-reader.component.ts @@ -197,7 +197,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { totalSeriesPages = 0; totalSeriesPagesRead = 0; user!: User; - readingProfile: ReadingProfile | null = null; + readingProfile!: ReadingProfile; generalSettingsForm!: FormGroup; readingDirection = ReadingDirection.LeftToRight; @@ -484,6 +484,17 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { return; } + this.route.data.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(data => { + this.readingProfile = data['readingProfile']; + if (this.readingProfile == null) { + this.router.navigateByUrl('/home'); + return; + } + this.setupReaderSettings(); + this.cdRef.markForCheck(); + }); + + this.getPageFn = this.getPage.bind(this); this.libraryId = parseInt(libraryId, 10); @@ -500,113 +511,108 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { this.continuousChaptersStack.push(this.chapterId); - forkJoin([ - this.accountService.currentUser$.pipe(take(1)), - this.readingProfileService.getForSeries(this.seriesId) - ]).subscribe(([user, profile]) => { + this.accountService.currentUser$.pipe(take(1)).subscribe(user => { if (!user) { this.router.navigateByUrl('/login'); return; } - this.readingProfile = profile; - if (!this.readingProfile) return; // type hints this.user = user; this.hasBookmarkRights = this.accountService.hasBookmarkRole(user) || this.accountService.hasAdminRole(user); - this.readingDirection = this.readingProfile.readingDirection; - this.scalingOption = this.readingProfile.scalingOption; - this.pageSplitOption = this.readingProfile.pageSplitOption; - this.autoCloseMenu = this.readingProfile.autoCloseMenu; - this.readerMode = this.readingProfile.readerMode; - this.layoutMode = this.readingProfile.layoutMode || LayoutMode.Single; - this.backgroundColor = this.readingProfile.backgroundColor || '#000000'; - this.readerService.setOverrideStyles(this.backgroundColor); + // this.readingDirection = this.readingProfile.readingDirection; + // this.scalingOption = this.readingProfile.scalingOption; + // this.pageSplitOption = this.readingProfile.pageSplitOption; + // this.autoCloseMenu = this.readingProfile.autoCloseMenu; + // this.readerMode = this.readingProfile.readerMode; + // this.layoutMode = this.readingProfile.layoutMode || LayoutMode.Single; + // this.backgroundColor = this.readingProfile.backgroundColor || '#000000'; + // this.readerService.setOverrideStyles(this.backgroundColor); + // + // this.generalSettingsForm = this.formBuilder.nonNullable.group({ + // autoCloseMenu: new FormControl(this.autoCloseMenu), + // pageSplitOption: new FormControl(this.pageSplitOption), + // fittingOption: new FormControl(this.mangaReaderService.translateScalingOption(this.scalingOption)), + // widthSlider: new FormControl(this.readingProfile.widthOverride ?? 'none'), + // layoutMode: new FormControl(this.layoutMode), + // darkness: new FormControl(100), + // emulateBook: new FormControl(this.readingProfile.emulateBook), + // swipeToPaginate: new FormControl(this.readingProfile.swipeToPaginate) + // }); + // + // this.readerModeSubject.next(this.readerMode); + // this.pagingDirectionSubject.next(this.pagingDirection); - this.generalSettingsForm = this.formBuilder.nonNullable.group({ - autoCloseMenu: new FormControl(this.autoCloseMenu), - pageSplitOption: new FormControl(this.pageSplitOption), - fittingOption: new FormControl(this.mangaReaderService.translateScalingOption(this.scalingOption)), - widthSlider: new FormControl(this.readingProfile.widthOverride ?? 'none'), - layoutMode: new FormControl(this.layoutMode), - darkness: new FormControl(100), - emulateBook: new FormControl(this.readingProfile.emulateBook), - swipeToPaginate: new FormControl(this.readingProfile.swipeToPaginate) - }); - - this.readerModeSubject.next(this.readerMode); - this.pagingDirectionSubject.next(this.pagingDirection); - - // We need a mergeMap when page changes - this.readerSettings$ = merge(this.generalSettingsForm.valueChanges, this.pagingDirection$, this.readerMode$).pipe( - map(_ => this.createReaderSettingsUpdate()), - takeUntilDestroyed(this.destroyRef), - ); - - this.updateForm(); - - this.pagingDirection$.pipe( - distinctUntilChanged(), - tap(dir => { - this.pagingDirection = dir; - this.cdRef.markForCheck(); - }), - takeUntilDestroyed(this.destroyRef) - ).subscribe(() => {}); - - this.readerMode$.pipe( - distinctUntilChanged(), - tap(mode => { - this.readerMode = mode; - this.disableDoubleRendererIfScreenTooSmall(); - this.cdRef.markForCheck(); - }), - takeUntilDestroyed(this.destroyRef) - ).subscribe(() => {}); - - this.setupWidthOverrideTriggers(); - - this.generalSettingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => { - - const changeOccurred = parseInt(val, 10) !== this.layoutMode; - this.layoutMode = parseInt(val, 10); - - if (this.layoutMode === LayoutMode.Single) { - this.generalSettingsForm.get('pageSplitOption')?.setValue(this.readingProfile!.pageSplitOption); - this.generalSettingsForm.get('pageSplitOption')?.enable(); - this.generalSettingsForm.get('widthSlider')?.enable(); - this.generalSettingsForm.get('fittingOption')?.enable(); - this.generalSettingsForm.get('emulateBook')?.enable(); - } else { - this.generalSettingsForm.get('pageSplitOption')?.setValue(PageSplitOption.NoSplit); - this.generalSettingsForm.get('pageSplitOption')?.disable(); - this.generalSettingsForm.get('widthSlider')?.disable(); - this.generalSettingsForm.get('fittingOption')?.setValue(this.mangaReaderService.translateScalingOption(ScalingOption.FitToHeight)); - this.generalSettingsForm.get('fittingOption')?.disable(); - this.generalSettingsForm.get('emulateBook')?.enable(); - } - this.cdRef.markForCheck(); - - // Re-render the current page when we switch layouts - if (changeOccurred) { - this.setPageNum(this.adjustPagesForDoubleRenderer(this.pageNum)); - this.loadPage(); - } - }); - - this.generalSettingsForm.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { - this.autoCloseMenu = this.generalSettingsForm.get('autoCloseMenu')?.value; - this.pageSplitOption = parseInt(this.generalSettingsForm.get('pageSplitOption')?.value, 10); - - const needsSplitting = this.mangaReaderService.isWidePage(this.readerService.imageUrlToPageNum(this.canvasImage.src)); - // If we need to split on a menu change, then we need to re-render. - if (needsSplitting) { - // If we need to re-render, to ensure things layout properly, let's update paging direction & reset render - this.pagingDirectionSubject.next(PAGING_DIRECTION.FORWARD); - this.canvasRenderer.reset(); - this.loadPage(); - } - }); + // // We need a mergeMap when page changes + // this.readerSettings$ = merge(this.generalSettingsForm.valueChanges, this.pagingDirection$, this.readerMode$).pipe( + // map(_ => this.createReaderSettingsUpdate()), + // takeUntilDestroyed(this.destroyRef), + // ); + // + // this.updateForm(); + // + // this.pagingDirection$.pipe( + // distinctUntilChanged(), + // tap(dir => { + // this.pagingDirection = dir; + // this.cdRef.markForCheck(); + // }), + // takeUntilDestroyed(this.destroyRef) + // ).subscribe(() => {}); + // + // this.readerMode$.pipe( + // distinctUntilChanged(), + // tap(mode => { + // this.readerMode = mode; + // this.disableDoubleRendererIfScreenTooSmall(); + // this.cdRef.markForCheck(); + // }), + // takeUntilDestroyed(this.destroyRef) + // ).subscribe(() => {}); + // + // this.setupWidthOverrideTriggers(); + // + // this.generalSettingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => { + // + // const changeOccurred = parseInt(val, 10) !== this.layoutMode; + // this.layoutMode = parseInt(val, 10); + // + // if (this.layoutMode === LayoutMode.Single) { + // this.generalSettingsForm.get('pageSplitOption')?.setValue(this.readingProfile!.pageSplitOption); + // this.generalSettingsForm.get('pageSplitOption')?.enable(); + // this.generalSettingsForm.get('widthSlider')?.enable(); + // this.generalSettingsForm.get('fittingOption')?.enable(); + // this.generalSettingsForm.get('emulateBook')?.enable(); + // } else { + // this.generalSettingsForm.get('pageSplitOption')?.setValue(PageSplitOption.NoSplit); + // this.generalSettingsForm.get('pageSplitOption')?.disable(); + // this.generalSettingsForm.get('widthSlider')?.disable(); + // this.generalSettingsForm.get('fittingOption')?.setValue(this.mangaReaderService.translateScalingOption(ScalingOption.FitToHeight)); + // this.generalSettingsForm.get('fittingOption')?.disable(); + // this.generalSettingsForm.get('emulateBook')?.enable(); + // } + // this.cdRef.markForCheck(); + // + // // Re-render the current page when we switch layouts + // if (changeOccurred) { + // this.setPageNum(this.adjustPagesForDoubleRenderer(this.pageNum)); + // this.loadPage(); + // } + // }); + // + // this.generalSettingsForm.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { + // this.autoCloseMenu = this.generalSettingsForm.get('autoCloseMenu')?.value; + // this.pageSplitOption = parseInt(this.generalSettingsForm.get('pageSplitOption')?.value, 10); + // + // const needsSplitting = this.mangaReaderService.isWidePage(this.readerService.imageUrlToPageNum(this.canvasImage.src)); + // // If we need to split on a menu change, then we need to re-render. + // if (needsSplitting) { + // // If we need to re-render, to ensure things layout properly, let's update paging direction & reset render + // this.pagingDirectionSubject.next(PAGING_DIRECTION.FORWARD); + // this.canvasRenderer.reset(); + // this.loadPage(); + // } + // }); this.memberService.hasReadingProgress(this.libraryId).pipe(take(1)).subscribe(progress => { if (!progress) { @@ -724,6 +730,104 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy { } } + setupReaderSettings() { + this.readingDirection = this.readingProfile.readingDirection; + this.scalingOption = this.readingProfile.scalingOption; + this.pageSplitOption = this.readingProfile.pageSplitOption; + this.autoCloseMenu = this.readingProfile.autoCloseMenu; + this.readerMode = this.readingProfile.readerMode; + this.layoutMode = this.readingProfile.layoutMode || LayoutMode.Single; + this.backgroundColor = this.readingProfile.backgroundColor || '#000000'; + this.readerService.setOverrideStyles(this.backgroundColor); + + this.generalSettingsForm = this.formBuilder.nonNullable.group({ + autoCloseMenu: new FormControl(this.autoCloseMenu), + pageSplitOption: new FormControl(this.pageSplitOption), + fittingOption: new FormControl(this.mangaReaderService.translateScalingOption(this.scalingOption)), + widthSlider: new FormControl(this.readingProfile.widthOverride ?? 'none'), + layoutMode: new FormControl(this.layoutMode), + darkness: new FormControl(100), + emulateBook: new FormControl(this.readingProfile.emulateBook), + swipeToPaginate: new FormControl(this.readingProfile.swipeToPaginate) + }); + + this.readerModeSubject.next(this.readerMode); + this.pagingDirectionSubject.next(this.pagingDirection); + + // We need a mergeMap when page changes + this.readerSettings$ = merge(this.generalSettingsForm.valueChanges, this.pagingDirection$, this.readerMode$).pipe( + map(_ => this.createReaderSettingsUpdate()), + takeUntilDestroyed(this.destroyRef), + ); + + this.updateForm(); + + this.pagingDirection$.pipe( + distinctUntilChanged(), + tap(dir => { + this.pagingDirection = dir; + this.cdRef.markForCheck(); + }), + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => {}); + + this.readerMode$.pipe( + distinctUntilChanged(), + tap(mode => { + this.readerMode = mode; + this.disableDoubleRendererIfScreenTooSmall(); + this.cdRef.markForCheck(); + }), + takeUntilDestroyed(this.destroyRef) + ).subscribe(() => {}); + + this.setupWidthOverrideTriggers(); + + this.generalSettingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => { + + const changeOccurred = parseInt(val, 10) !== this.layoutMode; + this.layoutMode = parseInt(val, 10); + + if (this.layoutMode === LayoutMode.Single) { + this.generalSettingsForm.get('pageSplitOption')?.setValue(this.readingProfile!.pageSplitOption); + this.generalSettingsForm.get('pageSplitOption')?.enable(); + this.generalSettingsForm.get('widthSlider')?.enable(); + this.generalSettingsForm.get('fittingOption')?.enable(); + this.generalSettingsForm.get('emulateBook')?.enable(); + } else { + this.generalSettingsForm.get('pageSplitOption')?.setValue(PageSplitOption.NoSplit); + this.generalSettingsForm.get('pageSplitOption')?.disable(); + this.generalSettingsForm.get('widthSlider')?.disable(); + this.generalSettingsForm.get('fittingOption')?.setValue(this.mangaReaderService.translateScalingOption(ScalingOption.FitToHeight)); + this.generalSettingsForm.get('fittingOption')?.disable(); + this.generalSettingsForm.get('emulateBook')?.enable(); + } + this.cdRef.markForCheck(); + + // Re-render the current page when we switch layouts + if (changeOccurred) { + this.setPageNum(this.adjustPagesForDoubleRenderer(this.pageNum)); + this.loadPage(); + } + }); + + this.generalSettingsForm.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(() => { + this.autoCloseMenu = this.generalSettingsForm.get('autoCloseMenu')?.value; + this.pageSplitOption = parseInt(this.generalSettingsForm.get('pageSplitOption')?.value, 10); + + const needsSplitting = this.mangaReaderService.isWidePage(this.readerService.imageUrlToPageNum(this.canvasImage.src)); + // If we need to split on a menu change, then we need to re-render. + if (needsSplitting) { + // If we need to re-render, to ensure things layout properly, let's update paging direction & reset render + this.pagingDirectionSubject.next(PAGING_DIRECTION.FORWARD); + this.canvasRenderer.reset(); + this.loadPage(); + } + }); + + this.cdRef.markForCheck(); + } + /** * Width override is only valid under the following conditions: * Image Scaling is Width diff --git a/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts b/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts index 9add32ff3..c6b7c0494 100644 --- a/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts +++ b/UI/Web/src/app/pdf-reader/_components/pdf-reader/pdf-reader.component.ts @@ -14,7 +14,7 @@ import { import {ActivatedRoute, Router} from '@angular/router'; import {NgxExtendedPdfViewerModule, PageViewModeType, ProgressBarEvent, ScrollModeType} from 'ngx-extended-pdf-viewer'; import {ToastrService} from 'ngx-toastr'; -import {forkJoin, take} from 'rxjs'; +import {take} from 'rxjs'; import {BookService} from 'src/app/book-reader/_services/book.service'; import {Breakpoint, KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service'; import {Chapter} from 'src/app/_models/chapter'; @@ -36,6 +36,7 @@ import {PdfScrollModeTypePipe} from "../../_pipe/pdf-scroll-mode.pipe"; import {PdfSpreadTypePipe} from "../../_pipe/pdf-spread-mode.pipe"; import {ReadingProfileService} from "../../../_services/reading-profile.service"; import {ReadingProfile} from "../../../_models/preferences/reading-profiles"; +import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; @Component({ selector: 'app-pdf-reader', @@ -166,6 +167,16 @@ export class PdfReaderComponent implements OnInit, OnDestroy { this.chapterId = parseInt(chapterId, 10); this.incognitoMode = this.route.snapshot.queryParamMap.get('incognitoMode') === 'true'; + this.route.data.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(data => { + this.readingProfile = data['readingProfile']; + if (this.readingProfile == null) { + this.router.navigateByUrl('/home'); + return; + } + this.setupReaderSettings(); + this.cdRef.markForCheck(); + }); + const readingListId = this.route.snapshot.queryParamMap.get('readingListId'); if (readingListId != null) { @@ -175,13 +186,9 @@ export class PdfReaderComponent implements OnInit, OnDestroy { this.cdRef.markForCheck(); - forkJoin([ - this.accountService.currentUser$.pipe(take(1)), - this.readingProfileService.getForSeries(this.seriesId) - ]).subscribe(([user, profile]) => { + this.accountService.currentUser$.pipe(take(1)).subscribe(user => { if (user) { this.user = user; - this.readingProfile = profile; this.init(); } }); @@ -242,12 +249,14 @@ export class PdfReaderComponent implements OnInit, OnDestroy { } } - init() { - + setupReaderSettings() { this.pageLayoutMode = this.convertPdfLayoutMode(PdfLayoutMode.Multiple); this.scrollMode = this.convertPdfScrollMode(this.readingProfile.pdfScrollMode || PdfScrollMode.Vertical); this.spreadMode = this.convertPdfSpreadMode(this.readingProfile.pdfSpreadMode || PdfSpreadMode.None); this.theme = this.convertPdfTheme(this.readingProfile.pdfTheme || PdfTheme.Dark); + } + + init() { this.backgroundColor = this.themeMap[this.theme].background; this.fontColor = this.themeMap[this.theme].font; // TODO: Move this to an observable or something