This will be a long journey. LayoutMode isn't getting handled correctly, I need to refactor the epub service finally to signals and streamline all the state into one place.
This commit is contained in:
parent
9474e8264a
commit
d114949446
10 changed files with 186 additions and 140 deletions
|
|
@ -826,6 +826,37 @@ public class ReaderController : BaseApiController
|
||||||
return _readerService.GetTimeEstimate(0, pagesLeft, false);
|
return _readerService.GetTimeEstimate(0, pagesLeft, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For the current user, returns an estimate on how long it would take to finish reading the chapter.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>For Epubs, this does not check words inside a chapter due to overhead so may not work in all cases.</remarks>
|
||||||
|
/// <param name="seriesId"></param>
|
||||||
|
/// <param name="chapterId"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpGet("time-left-for-chapter")]
|
||||||
|
[ResponseCache(CacheProfileName = ResponseCacheProfiles.Hour, VaryByQueryKeys = ["seriesId", "chapterId"])]
|
||||||
|
public async Task<ActionResult<HourEstimateRangeDto>> GetEstimateToCompletionForChapter(int seriesId, int chapterId)
|
||||||
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
|
var series = await _unitOfWork.SeriesRepository.GetSeriesDtoByIdAsync(seriesId, userId);
|
||||||
|
var chapter = await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapterId);
|
||||||
|
if (series == null || chapter == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-error"));
|
||||||
|
|
||||||
|
// Patch in the reading progress
|
||||||
|
await _unitOfWork.ChapterRepository.AddChapterModifiers(User.GetUserId(), chapter);
|
||||||
|
|
||||||
|
if (series.Format == MangaFormat.Epub)
|
||||||
|
{
|
||||||
|
var progressCount = chapter.WordCount;
|
||||||
|
var wordsLeft = series.WordCount - progressCount;
|
||||||
|
return _readerService.GetTimeEstimate(wordsLeft, 0, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
var pagesLeft = chapter.Pages - chapter.PagesRead;
|
||||||
|
return _readerService.GetTimeEstimate(0, pagesLeft, false);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the annotations for the given chapter
|
/// Returns the annotations for the given chapter
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,4 @@ export interface IHasReadingTime {
|
||||||
avgHoursToRead: number;
|
avgHoursToRead: number;
|
||||||
pages: number;
|
pages: number;
|
||||||
wordCount: number;
|
wordCount: number;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -100,13 +100,13 @@ export class EpubReaderMenuService {
|
||||||
ref.componentInstance.seriesId.set(seriesId);
|
ref.componentInstance.seriesId.set(seriesId);
|
||||||
ref.componentInstance.readingProfile.set(readingProfile);
|
ref.componentInstance.readingProfile.set(readingProfile);
|
||||||
|
|
||||||
ref.componentInstance.updated.subscribe((res: ReaderSettingUpdate) => {
|
// ref.componentInstance.updated.subscribe((res: ReaderSettingUpdate) => {
|
||||||
// Check if we are on mobile to collapse the menu
|
// // Check if we are on mobile to collapse the menu
|
||||||
if (this.utilityService.activeUserBreakpoint() <= UserBreakpoint.Mobile) {
|
// if (this.utilityService.activeUserBreakpoint() <= UserBreakpoint.Mobile) {
|
||||||
this.closeAll();
|
// this.closeAll();
|
||||||
}
|
// }
|
||||||
callbackFn(res);
|
// callbackFn(res);
|
||||||
});
|
// });
|
||||||
ref.closed.subscribe(() => this.setDrawerClosed());
|
ref.closed.subscribe(() => this.setDrawerClosed());
|
||||||
ref.dismissed.subscribe(() => this.setDrawerClosed());
|
ref.dismissed.subscribe(() => this.setDrawerClosed());
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ import {ReadingProfileService} from "./reading-profile.service";
|
||||||
import {debounceTime, skip, tap} from "rxjs/operators";
|
import {debounceTime, skip, tap} from "rxjs/operators";
|
||||||
import {BookTheme} from "../_models/preferences/book-theme";
|
import {BookTheme} from "../_models/preferences/book-theme";
|
||||||
import {DOCUMENT} from "@angular/common";
|
import {DOCUMENT} from "@angular/common";
|
||||||
|
import {translate} from "@jsverse/transloco";
|
||||||
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
|
||||||
export interface ReaderSettingUpdate {
|
export interface ReaderSettingUpdate {
|
||||||
setting: 'pageStyle' | 'clickToPaginate' | 'fullscreen' | 'writingStyle' | 'layoutMode' | 'readingDirection' | 'immersiveMode' | 'theme';
|
setting: 'pageStyle' | 'clickToPaginate' | 'fullscreen' | 'writingStyle' | 'layoutMode' | 'readingDirection' | 'immersiveMode' | 'theme';
|
||||||
|
|
@ -28,6 +30,7 @@ export class EpubReaderSettingsService {
|
||||||
private readonly bookService = inject(BookService);
|
private readonly bookService = inject(BookService);
|
||||||
private readonly themeService = inject(ThemeService);
|
private readonly themeService = inject(ThemeService);
|
||||||
private readonly readingProfileService = inject(ReadingProfileService);
|
private readonly readingProfileService = inject(ReadingProfileService);
|
||||||
|
private readonly toastr = inject(ToastrService);
|
||||||
private readonly document = inject(DOCUMENT);
|
private readonly document = inject(DOCUMENT);
|
||||||
|
|
||||||
private pageStylesSubject = new BehaviorSubject<PageStyle>(this.getDefaultPageStyles());
|
private pageStylesSubject = new BehaviorSubject<PageStyle>(this.getDefaultPageStyles());
|
||||||
|
|
@ -46,6 +49,7 @@ export class EpubReaderSettingsService {
|
||||||
// Form and data
|
// Form and data
|
||||||
private settingsForm: FormGroup = new FormGroup({});
|
private settingsForm: FormGroup = new FormGroup({});
|
||||||
private currentReadingProfile: ReadingProfile | null = null;
|
private currentReadingProfile: ReadingProfile | null = null;
|
||||||
|
private parentReadingProfile: ReadingProfile | null = null;
|
||||||
private currentSeriesId: number | null = null;
|
private currentSeriesId: number | null = null;
|
||||||
private fontFamilies: FontFamily[] = this.bookService.getFontFamilies();
|
private fontFamilies: FontFamily[] = this.bookService.getFontFamilies();
|
||||||
|
|
||||||
|
|
@ -69,13 +73,15 @@ export class EpubReaderSettingsService {
|
||||||
async initialize(seriesId: number, readingProfile: ReadingProfile): Promise<void> {
|
async initialize(seriesId: number, readingProfile: ReadingProfile): Promise<void> {
|
||||||
this.currentSeriesId = seriesId;
|
this.currentSeriesId = seriesId;
|
||||||
this.currentReadingProfile = readingProfile;
|
this.currentReadingProfile = readingProfile;
|
||||||
|
console.log('init, reading profile: ', readingProfile);
|
||||||
this.readingProfileSubject.next(readingProfile);
|
this.readingProfileSubject.next(readingProfile);
|
||||||
|
|
||||||
// Load parent profile if needed
|
// Load parent profile if needed
|
||||||
if (readingProfile.kind === ReadingProfileKind.Implicit) {
|
if (readingProfile.kind === ReadingProfileKind.Implicit) {
|
||||||
try {
|
try {
|
||||||
// const parent = await this.readingProfileService.getForSeries(seriesId, true).toPromise();
|
const parent = await this.readingProfileService.getForSeries(seriesId, true).toPromise();
|
||||||
// Keep the implicit profile but use parent as reference
|
this.parentReadingProfile = parent || null;
|
||||||
|
// Keep the implicit profile but use parent as reference (TODO: Validate the code)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load parent reading profile:', error);
|
console.error('Failed to load parent reading profile:', error);
|
||||||
}
|
}
|
||||||
|
|
@ -83,7 +89,7 @@ export class EpubReaderSettingsService {
|
||||||
|
|
||||||
// Setup defaults and form
|
// Setup defaults and form
|
||||||
this.setupDefaultSettings();
|
this.setupDefaultSettings();
|
||||||
this.setupSettingsForm();
|
|
||||||
|
|
||||||
// Set initial theme
|
// Set initial theme
|
||||||
const themeName = readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme;
|
const themeName = readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme;
|
||||||
|
|
@ -227,6 +233,8 @@ export class EpubReaderSettingsService {
|
||||||
profile.bookReaderWritingStyle = WritingStyle.Horizontal;
|
profile.bookReaderWritingStyle = WritingStyle.Horizontal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.setupSettingsForm();
|
||||||
|
|
||||||
// Update internal state
|
// Update internal state
|
||||||
this.readingDirectionSubject.next(profile.bookReaderReadingDirection);
|
this.readingDirectionSubject.next(profile.bookReaderReadingDirection);
|
||||||
this.writingStyleSubject.next(profile.bookReaderWritingStyle);
|
this.writingStyleSubject.next(profile.bookReaderWritingStyle);
|
||||||
|
|
@ -418,6 +426,8 @@ export class EpubReaderSettingsService {
|
||||||
data.bookReaderThemeName = activeTheme.name;
|
data.bookReaderThemeName = activeTheme.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('packed reading profile:', data);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -455,4 +465,16 @@ export class EpubReaderSettingsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
createNewProfileFromImplicit() {
|
||||||
|
const rp = this.getCurrentReadingProfile();
|
||||||
|
if (rp === null || rp.kind !== ReadingProfileKind.Implicit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.promoteProfile().subscribe(newProfile => {
|
||||||
|
this.currentReadingProfile = newProfile;
|
||||||
|
this.parentReadingProfile = newProfile;
|
||||||
|
this.toastr.success(translate("manga-reader.reading-profile-promoted"));
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -224,6 +224,10 @@ export class ReaderService {
|
||||||
return this.httpClient.get<HourEstimateRange>(this.baseUrl + 'reader/time-left?seriesId=' + seriesId);
|
return this.httpClient.get<HourEstimateRange>(this.baseUrl + 'reader/time-left?seriesId=' + seriesId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getTimeLeftForChapter(seriesId: number, chapterId: number) {
|
||||||
|
return this.httpClient.get<HourEstimateRange>(this.baseUrl + `reader/time-left-for-chapter?seriesId=${seriesId}&chapterId=${chapterId}`);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Captures current body color and forces background color to be black. Call @see resetOverrideStyles() on destroy of component to revert changes
|
* Captures current body color and forces background color to be black. Call @see resetOverrideStyles() on destroy of component to revert changes
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,6 @@
|
||||||
<app-reader-settings
|
<app-reader-settings
|
||||||
[seriesId]="sId"
|
[seriesId]="sId"
|
||||||
[readingProfile]="rp"
|
[readingProfile]="rp"
|
||||||
(colorThemeUpdate)="updateColorTheme($event)"
|
|
||||||
(styleUpdate)="updateReaderStyles($event)"
|
|
||||||
(clickToPaginateChanged)="showPaginationOverlay($event)"
|
|
||||||
(fullscreen)="toggleFullscreen()"
|
|
||||||
(bookReaderWritingStyle)="updateWritingStyle($event)"
|
|
||||||
(layoutModeUpdate)="updateLayoutMode($event)"
|
|
||||||
(readingDirection)="updateReadingDirection($event)"
|
|
||||||
(immersiveMode)="updateImmersiveMode($event)"
|
|
||||||
></app-reader-settings>
|
></app-reader-settings>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,8 @@
|
||||||
import {
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, effect, inject, model} from '@angular/core';
|
||||||
ChangeDetectionStrategy,
|
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
effect,
|
|
||||||
EventEmitter,
|
|
||||||
inject,
|
|
||||||
model
|
|
||||||
} from '@angular/core';
|
|
||||||
import {NgbActiveOffcanvas} from "@ng-bootstrap/ng-bootstrap";
|
import {NgbActiveOffcanvas} from "@ng-bootstrap/ng-bootstrap";
|
||||||
import {PageStyle, ReaderSettingsComponent} from "../../reader-settings/reader-settings.component";
|
import {ReaderSettingsComponent} from "../../reader-settings/reader-settings.component";
|
||||||
import {ReadingProfile} from "../../../../_models/preferences/reading-profiles";
|
import {ReadingProfile} from "../../../../_models/preferences/reading-profiles";
|
||||||
import {BookTheme} from "../../../../_models/preferences/book-theme";
|
|
||||||
import {WritingStyle} from "../../../../_models/preferences/writing-style";
|
|
||||||
import {BookPageLayoutMode} from "../../../../_models/readers/book-page-layout-mode";
|
|
||||||
import {ReadingDirection} from "../../../../_models/preferences/reading-direction";
|
|
||||||
import {TranslocoDirective} from "@jsverse/transloco";
|
import {TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {ReaderSettingUpdate} from "../../../../_services/epub-reader-settings.service";
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-epub-setting-drawer',
|
selector: 'app-epub-setting-drawer',
|
||||||
|
|
@ -35,7 +22,7 @@ export class EpubSettingDrawerComponent {
|
||||||
seriesId = model<number>();
|
seriesId = model<number>();
|
||||||
readingProfile = model<ReadingProfile>();
|
readingProfile = model<ReadingProfile>();
|
||||||
|
|
||||||
updated = new EventEmitter<ReaderSettingUpdate>();
|
// updated = new EventEmitter<ReaderSettingUpdate>();
|
||||||
|
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
|
@ -49,46 +36,46 @@ export class EpubSettingDrawerComponent {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
updateColorTheme(theme: BookTheme) {
|
// updateColorTheme(theme: BookTheme) {
|
||||||
const evt = {setting: 'theme', object: theme} as ReaderSettingUpdate;
|
// const evt = {setting: 'theme', object: theme} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
updateReaderStyles(pageStyles: PageStyle) {
|
// updateReaderStyles(pageStyles: PageStyle) {
|
||||||
const evt = {setting: 'pageStyle', object: pageStyles} as ReaderSettingUpdate;
|
// const evt = {setting: 'pageStyle', object: pageStyles} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
showPaginationOverlay(clickToPaginate: boolean) {
|
// showPaginationOverlay(clickToPaginate: boolean) {
|
||||||
const evt = {setting: 'clickToPaginate', object: clickToPaginate} as ReaderSettingUpdate;
|
// const evt = {setting: 'clickToPaginate', object: clickToPaginate} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
toggleFullscreen() {
|
// toggleFullscreen() {
|
||||||
const evt = {setting: 'fullscreen', object: null} as ReaderSettingUpdate;
|
// const evt = {setting: 'fullscreen', object: null} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
updateWritingStyle(writingStyle: WritingStyle) {
|
// updateWritingStyle(writingStyle: WritingStyle) {
|
||||||
const evt = {setting: 'writingStyle', object: writingStyle} as ReaderSettingUpdate;
|
// const evt = {setting: 'writingStyle', object: writingStyle} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
updateLayoutMode(mode: BookPageLayoutMode) {
|
// updateLayoutMode(mode: BookPageLayoutMode) {
|
||||||
const evt = {setting: 'layoutMode', object: mode} as ReaderSettingUpdate;
|
// const evt = {setting: 'layoutMode', object: mode} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
updateReadingDirection(readingDirection: ReadingDirection) {
|
// updateReadingDirection(readingDirection: ReadingDirection) {
|
||||||
const evt = {setting: 'readingDirection', object: readingDirection} as ReaderSettingUpdate;
|
// const evt = {setting: 'readingDirection', object: readingDirection} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
updateImmersiveMode(immersiveMode: boolean) {
|
// updateImmersiveMode(immersiveMode: boolean) {
|
||||||
const evt = {setting: 'immersiveMode', object: immersiveMode} as ReaderSettingUpdate;
|
// const evt = {setting: 'immersiveMode', object: immersiveMode} as ReaderSettingUpdate;
|
||||||
this.updated.emit(evt);
|
// this.updated.emit(evt);
|
||||||
}
|
// }
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.activeOffcanvas.close();
|
this.activeOffcanvas.close();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{layoutMode | columnLayoutClass}} {{writingStyle | writingStyleClass}}"
|
<div class="container-flex {{darkMode() ? 'dark-mode' : ''}} reader-container {{layoutMode() | columnLayoutClass}} {{writingStyle | writingStyleClass}}"
|
||||||
tabindex="0" #reader>
|
tabindex="0" #reader>
|
||||||
<ng-container *transloco="let t; prefix: 'book-reader'">
|
<ng-container *transloco="let t; prefix: 'book-reader'">
|
||||||
<div class="fixed-top" #stickyTop>
|
<div class="fixed-top" #stickyTop>
|
||||||
|
|
@ -17,35 +17,35 @@
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #readingSection class="reading-section {{layoutMode | columnLayoutClass}} {{writingStyle | writingStyleClass}}" [ngStyle]="{'width': PageWidthForPagination}"
|
<div #readingSection class="reading-section {{layoutMode() | columnLayoutClass}} {{writingStyle | writingStyleClass}}" [ngStyle]="{'width': PageWidthForPagination}"
|
||||||
[ngClass]="{'immersive' : immersiveMode || !actionBarVisible}" [@isLoading]="isLoading" (click)="handleReaderClick($event)">
|
[ngClass]="{'immersive' : immersiveMode() || !actionBarVisible}" [@isLoading]="isLoading" (click)="handleReaderClick($event)">
|
||||||
|
|
||||||
@if (clickToPaginate && !hidePagination) {
|
@if (clickToPaginate && !hidePagination) {
|
||||||
<div class="left {{clickOverlayClass('left')}} no-observe"
|
<div class="left {{clickOverlayClass('left')}} no-observe"
|
||||||
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||||
[ngClass]="{'immersive' : immersiveMode}"
|
[ngClass]="{'immersive' : immersiveMode()}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
[ngStyle]="{height: PageHeightForPagination}"></div>
|
[ngStyle]="{height: PageHeightForPagination}"></div>
|
||||||
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe"
|
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe"
|
||||||
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
||||||
[ngClass]="{'immersive' : immersiveMode}"
|
[ngClass]="{'immersive' : immersiveMode()}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
[ngStyle]="{height: PageHeightForPagination}"></div>
|
[ngStyle]="{height: PageHeightForPagination}"></div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<div #bookContainer class="book-container {{writingStyle | writingStyleClass}}"
|
<div #bookContainer class="book-container {{writingStyle | writingStyleClass}}"
|
||||||
[ngClass]="{'immersive' : immersiveMode}"
|
[ngClass]="{'immersive' : immersiveMode()}"
|
||||||
(mousedown)="mouseDown($event)" >
|
(mousedown)="mouseDown($event)" >
|
||||||
|
|
||||||
@if (page !== undefined) {
|
@if (page !== undefined) {
|
||||||
<div #readingHtml class="book-content {{layoutMode | columnLayoutClass}} {{writingStyle | writingStyleClass}}"
|
<div #readingHtml class="book-content {{layoutMode() | columnLayoutClass}} {{writingStyle | writingStyleClass}}"
|
||||||
[ngStyle]="{'max-height': ColumnHeight, 'max-width': VerticalBookContentWidth, 'width': VerticalBookContentWidth, 'column-width': ColumnWidth}"
|
[ngStyle]="{'max-height': ColumnHeight, 'max-width': VerticalBookContentWidth, 'width': VerticalBookContentWidth, 'column-width': ColumnWidth}"
|
||||||
[ngClass]="{'immersive': immersiveMode && actionBarVisible}"
|
[ngClass]="{'immersive': immersiveMode() && actionBarVisible}"
|
||||||
[innerHtml]="page" (click)="toggleMenu($event)" (mousedown)="mouseDown($event)" (wheel)="onWheel($event)"></div>
|
[innerHtml]="page" (click)="toggleMenu($event)" (mousedown)="mouseDown($event)" (wheel)="onWheel($event)"></div>
|
||||||
|
|
||||||
@if ((scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode === BookPageLayoutMode.Default)) {
|
@if ((scrollbarNeeded || layoutMode() !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode() === BookPageLayoutMode.Default)) {
|
||||||
<div (click)="$event.stopPropagation();"
|
<div (click)="$event.stopPropagation();"
|
||||||
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">
|
[ngClass]="{'bottom-bar': layoutMode() !== BookPageLayoutMode.Default}">
|
||||||
<ng-container [ngTemplateOutlet]="actionBar" [ngTemplateOutletContext]="{isTop: false}"></ng-container>
|
<ng-container [ngTemplateOutlet]="actionBar" [ngTemplateOutletContext]="{isTop: false}"></ng-container>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
@ -55,7 +55,7 @@
|
||||||
|
|
||||||
|
|
||||||
<ng-template #topActionBar>
|
<ng-template #topActionBar>
|
||||||
@if (!immersiveMode || epubMenuService.isDrawerOpen() || actionBarVisible) {
|
@if (!immersiveMode() || epubMenuService.isDrawerOpen() || actionBarVisible) {
|
||||||
<div class="action-bar d-flex align-items-center px-2">
|
<div class="action-bar d-flex align-items-center px-2">
|
||||||
<!-- Left: Drawer toggle -->
|
<!-- Left: Drawer toggle -->
|
||||||
<button class="btn btn-secondary me-2" (click)="toggleDrawer()">
|
<button class="btn btn-secondary me-2" (click)="toggleDrawer()">
|
||||||
|
|
@ -105,7 +105,7 @@
|
||||||
|
|
||||||
|
|
||||||
<ng-template #actionBar let-isTop>
|
<ng-template #actionBar let-isTop>
|
||||||
@if (!immersiveMode || epubMenuService.isDrawerOpen() || actionBarVisible) {
|
@if (!immersiveMode() || epubMenuService.isDrawerOpen() || actionBarVisible) {
|
||||||
<div class="action-bar row g-0 justify-content-between">
|
<div class="action-bar row g-0 justify-content-between">
|
||||||
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
<button class="btn btn-outline-secondary btn-icon col-2 col-xs-1"
|
||||||
(click)="!isTop && movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
(click)="!isTop && movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||||
|
|
@ -127,7 +127,16 @@
|
||||||
<span class="visually-hidden">{{t('loading-book')}}</span>
|
<span class="visually-hidden">{{t('loading-book')}}</span>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
20% complete, 3 hrs to go
|
@let timeLeft = readingTimeLeft();
|
||||||
|
|
||||||
|
20% complete,
|
||||||
|
@if (timeLeft) {
|
||||||
|
<span class="time-left" [ngbTooltip]="t('time-left-alt')">
|
||||||
|
<i class="fa-solid fa-clock" aria-hidden="true"></i>
|
||||||
|
{{timeLeft | readTimeLeft }}
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,9 @@ import {LoadPageEvent} from "../_drawers/view-toc-drawer/view-toc-drawer.compone
|
||||||
import {EpubReaderSettingsService, ReaderSettingUpdate} from "../../../_services/epub-reader-settings.service";
|
import {EpubReaderSettingsService, ReaderSettingUpdate} from "../../../_services/epub-reader-settings.service";
|
||||||
import {ColumnLayoutClassPipe} from "../../_pipes/column-layout-class.pipe";
|
import {ColumnLayoutClassPipe} from "../../_pipes/column-layout-class.pipe";
|
||||||
import {WritingStyleClassPipe} from "../../_pipes/writing-style-class.pipe";
|
import {WritingStyleClassPipe} from "../../_pipes/writing-style-class.pipe";
|
||||||
|
import {ChapterService} from "../../../_services/chapter.service";
|
||||||
|
import {ReadTimeLeftPipe} from "../../../_pipes/read-time-left.pipe";
|
||||||
|
import {HourEstimateRange} from "../../../_models/series-detail/hour-estimate-range";
|
||||||
|
|
||||||
|
|
||||||
interface HistoryPoint {
|
interface HistoryPoint {
|
||||||
|
|
@ -100,7 +103,7 @@ const elementLevelStyles = ['line-height', 'font-family'];
|
||||||
])
|
])
|
||||||
],
|
],
|
||||||
imports: [NgTemplateOutlet, NgStyle, NgClass, NgbTooltip,
|
imports: [NgTemplateOutlet, NgStyle, NgClass, NgbTooltip,
|
||||||
BookLineOverlayComponent, TranslocoDirective, ColumnLayoutClassPipe, WritingStyleClassPipe]
|
BookLineOverlayComponent, TranslocoDirective, ColumnLayoutClassPipe, WritingStyleClassPipe, ReadTimeLeftPipe]
|
||||||
})
|
})
|
||||||
export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
|
@ -108,6 +111,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
private readonly router = inject(Router);
|
private readonly router = inject(Router);
|
||||||
private readonly seriesService = inject(SeriesService);
|
private readonly seriesService = inject(SeriesService);
|
||||||
private readonly readerService = inject(ReaderService);
|
private readonly readerService = inject(ReaderService);
|
||||||
|
private readonly chapterService = inject(ChapterService);
|
||||||
private readonly renderer = inject(Renderer2);
|
private readonly renderer = inject(Renderer2);
|
||||||
private readonly navService = inject(NavService);
|
private readonly navService = inject(NavService);
|
||||||
private readonly toastr = inject(ToastrService);
|
private readonly toastr = inject(ToastrService);
|
||||||
|
|
@ -191,7 +195,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Book reader setting that hides the menuing system
|
* Book reader setting that hides the menuing system
|
||||||
*/
|
*/
|
||||||
immersiveMode: boolean = false;
|
immersiveMode = model<boolean>(false);
|
||||||
/**
|
/**
|
||||||
* If we are loading from backend
|
* If we are loading from backend
|
||||||
*/
|
*/
|
||||||
|
|
@ -265,7 +269,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Used solely for fullscreen to apply a hack
|
* Used solely for fullscreen to apply a hack
|
||||||
*/
|
*/
|
||||||
darkMode = true;
|
darkMode = model<boolean>(true);
|
||||||
|
readingTimeLeft = model<HourEstimateRange | null>(null);
|
||||||
/**
|
/**
|
||||||
* Anchors that map to the page number. When you click on one of these, we will load a given page up for the user.
|
* Anchors that map to the page number. When you click on one of these, we will load a given page up for the user.
|
||||||
*/
|
*/
|
||||||
|
|
@ -287,7 +292,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* How to render the page content
|
* How to render the page content
|
||||||
*/
|
*/
|
||||||
layoutMode: BookPageLayoutMode = BookPageLayoutMode.Default;
|
//layoutMode: BookPageLayoutMode = BookPageLayoutMode.Default;
|
||||||
|
layoutMode = model<BookPageLayoutMode>(BookPageLayoutMode.Default);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Width of the document (in non-column layout), used for column layout virtual paging
|
* Width of the document (in non-column layout), used for column layout virtual paging
|
||||||
|
|
@ -360,7 +366,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
isNextPageDisabled() {
|
isNextPageDisabled() {
|
||||||
const [currentVirtualPage, totalVirtualPages, _] = this.getVirtualPage();
|
const [currentVirtualPage, totalVirtualPages, _] = this.getVirtualPage();
|
||||||
const condition = (this.nextPageDisabled || this.nextChapterId === CHAPTER_ID_DOESNT_EXIST) && this.pageNum + 1 > this.maxPages - 1;
|
const condition = (this.nextPageDisabled || this.nextChapterId === CHAPTER_ID_DOESNT_EXIST) && this.pageNum + 1 > this.maxPages - 1;
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default) {
|
||||||
return condition && currentVirtualPage === totalVirtualPages;
|
return condition && currentVirtualPage === totalVirtualPages;
|
||||||
}
|
}
|
||||||
return condition;
|
return condition;
|
||||||
|
|
@ -369,7 +375,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
isPrevPageDisabled() {
|
isPrevPageDisabled() {
|
||||||
const [currentVirtualPage,,] = this.getVirtualPage();
|
const [currentVirtualPage,,] = this.getVirtualPage();
|
||||||
const condition = (this.prevPageDisabled || this.prevChapterId === CHAPTER_ID_DOESNT_EXIST) && this.pageNum === 0;
|
const condition = (this.prevPageDisabled || this.prevChapterId === CHAPTER_ID_DOESNT_EXIST) && this.pageNum === 0;
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default) {
|
||||||
return condition && currentVirtualPage === 0;
|
return condition && currentVirtualPage === 0;
|
||||||
}
|
}
|
||||||
return condition;
|
return condition;
|
||||||
|
|
@ -379,7 +385,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
* Determines if we show >> or >
|
* Determines if we show >> or >
|
||||||
*/
|
*/
|
||||||
get IsNextChapter(): boolean {
|
get IsNextChapter(): boolean {
|
||||||
if (this.layoutMode === BookPageLayoutMode.Default) {
|
if (this.layoutMode() === BookPageLayoutMode.Default) {
|
||||||
return this.pageNum + 1 >= this.maxPages;
|
return this.pageNum + 1 >= this.maxPages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -392,7 +398,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
* Determines if we show << or <
|
* Determines if we show << or <
|
||||||
*/
|
*/
|
||||||
get IsPrevChapter(): boolean {
|
get IsPrevChapter(): boolean {
|
||||||
if (this.layoutMode === BookPageLayoutMode.Default) {
|
if (this.layoutMode() === BookPageLayoutMode.Default) {
|
||||||
return this.pageNum === 0;
|
return this.pageNum === 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -404,7 +410,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
get ColumnWidth() {
|
get ColumnWidth() {
|
||||||
const base = this.writingStyle === WritingStyle.Vertical ? this.windowHeight : this.windowWidth;
|
const base = this.writingStyle === WritingStyle.Vertical ? this.windowHeight : this.windowWidth;
|
||||||
switch (this.layoutMode) {
|
switch (this.layoutMode()) {
|
||||||
case BookPageLayoutMode.Default:
|
case BookPageLayoutMode.Default:
|
||||||
return 'unset';
|
return 'unset';
|
||||||
case BookPageLayoutMode.Column1:
|
case BookPageLayoutMode.Column1:
|
||||||
|
|
@ -417,7 +423,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
get ColumnHeight() {
|
get ColumnHeight() {
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default || this.writingStyle === WritingStyle.Vertical) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default || this.writingStyle === WritingStyle.Vertical) {
|
||||||
// Take the height after page loads, subtract the top/bottom bar
|
// Take the height after page loads, subtract the top/bottom bar
|
||||||
const height = this.windowHeight - (this.topOffset * 2);
|
const height = this.windowHeight - (this.topOffset * 2);
|
||||||
return height + 'px';
|
return height + 'px';
|
||||||
|
|
@ -426,7 +432,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
get VerticalBookContentWidth() {
|
get VerticalBookContentWidth() {
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Horizontal ) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default && this.writingStyle !== WritingStyle.Horizontal ) {
|
||||||
const width = this.getVerticalPageWidth()
|
const width = this.getVerticalPageWidth()
|
||||||
return width + 'px';
|
return width + 'px';
|
||||||
}
|
}
|
||||||
|
|
@ -435,23 +441,23 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
|
||||||
get PageWidthForPagination() {
|
get PageWidthForPagination() {
|
||||||
if (this.layoutMode === BookPageLayoutMode.Default && this.writingStyle === WritingStyle.Vertical && this.horizontalScrollbarNeeded) {
|
if (this.layoutMode() === BookPageLayoutMode.Default && this.writingStyle === WritingStyle.Vertical && this.horizontalScrollbarNeeded) {
|
||||||
return 'unset';
|
return 'unset';
|
||||||
}
|
}
|
||||||
return '100%'
|
return '100%'
|
||||||
}
|
}
|
||||||
|
|
||||||
get PageHeightForPagination() {
|
get PageHeightForPagination() {
|
||||||
if (this.layoutMode === BookPageLayoutMode.Default) {
|
if (this.layoutMode() === BookPageLayoutMode.Default) {
|
||||||
// if the book content is less than the height of the container, override and return height of container for pagination area
|
// if the book content is less than the height of the container, override and return height of container for pagination area
|
||||||
if (this.bookContainerElemRef?.nativeElement?.clientHeight > this.bookContentElemRef?.nativeElement?.clientHeight) {
|
if (this.bookContainerElemRef?.nativeElement?.clientHeight > this.bookContentElemRef?.nativeElement?.clientHeight) {
|
||||||
return (this.bookContainerElemRef?.nativeElement?.clientHeight || 0) + 'px';
|
return (this.bookContainerElemRef?.nativeElement?.clientHeight || 0) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
return (this.bookContentElemRef?.nativeElement?.scrollHeight || 0) - ((this.topOffset * (this.immersiveMode ? 0 : 1)) * 2) + 'px';
|
return (this.bookContentElemRef?.nativeElement?.scrollHeight || 0) - ((this.topOffset * (this.immersiveMode() ? 0 : 1)) * 2) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.immersiveMode) return this.windowHeight + 'px';
|
if (this.immersiveMode()) return this.windowHeight + 'px';
|
||||||
return (this.windowHeight) - (this.topOffset * 2) + 'px';
|
return (this.windowHeight) - (this.topOffset * 2) + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -543,6 +549,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
if (!this.incognitoMode) {
|
if (!this.incognitoMode) {
|
||||||
this.readerService.saveProgress(this.libraryId, this.seriesId, this.volumeId, this.chapterId, tempPageNum, this.lastSeenScrollPartPath).pipe(take(1)).subscribe(() => {/* No operation */});
|
this.readerService.saveProgress(this.libraryId, this.seriesId, this.volumeId, this.chapterId, tempPageNum, this.lastSeenScrollPartPath).pipe(take(1)).subscribe(() => {/* No operation */});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: TEMP: Trigger reading time calculation update
|
||||||
|
// this.readerService.getTimeLeftForChapter(this.seriesId, this.chapterId).subscribe(c => {
|
||||||
|
// this.readingTimeLeft.set(c);
|
||||||
|
// });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
ngOnDestroy(): void {
|
||||||
|
|
@ -718,7 +730,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.updateImageSizes();
|
this.updateImageSizes();
|
||||||
|
|
||||||
const resumeElement = this.getFirstVisibleElementXPath();
|
const resumeElement = this.getFirstVisibleElementXPath();
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
|
||||||
this.scrollTo(resumeElement); // This works pretty well, but not perfect
|
this.scrollTo(resumeElement); // This works pretty well, but not perfect
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -750,7 +762,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
onWheel(event: WheelEvent) {
|
onWheel(event: WheelEvent) {
|
||||||
// This allows the user to scroll the page horizontally without holding shift
|
// This allows the user to scroll the page horizontally without holding shift
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default || this.writingStyle !== WritingStyle.Vertical) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default || this.writingStyle !== WritingStyle.Vertical) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (event.deltaY !== 0) {
|
if (event.deltaY !== 0) {
|
||||||
|
|
@ -966,7 +978,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
const height = this.windowHeight - (this.topOffset * 2);
|
const height = this.windowHeight - (this.topOffset * 2);
|
||||||
let maxHeight = 'unset';
|
let maxHeight = 'unset';
|
||||||
let maxWidth = '';
|
let maxWidth = '';
|
||||||
switch (this.layoutMode) {
|
switch (this.layoutMode()) {
|
||||||
case BookPageLayoutMode.Default:
|
case BookPageLayoutMode.Default:
|
||||||
if (isVerticalWritingStyle) {
|
if (isVerticalWritingStyle) {
|
||||||
maxHeight = `${height}px`;
|
maxHeight = `${height}px`;
|
||||||
|
|
@ -995,7 +1007,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSingleImagePageStyles() {
|
updateSingleImagePageStyles() {
|
||||||
if (this.isSingleImagePage && this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.isSingleImagePage && this.layoutMode() !== BookPageLayoutMode.Default) {
|
||||||
this.document.documentElement.style.setProperty('--book-reader-content-position', 'absolute');
|
this.document.documentElement.style.setProperty('--book-reader-content-position', 'absolute');
|
||||||
this.document.documentElement.style.setProperty('--book-reader-content-top', '50%');
|
this.document.documentElement.style.setProperty('--book-reader-content-top', '50%');
|
||||||
this.document.documentElement.style.setProperty('--book-reader-content-left', '50%');
|
this.document.documentElement.style.setProperty('--book-reader-content-left', '50%');
|
||||||
|
|
@ -1032,7 +1044,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
// Virtual Paging stuff
|
// Virtual Paging stuff
|
||||||
this.updateWidthAndHeightCalcs();
|
this.updateWidthAndHeightCalcs();
|
||||||
this.updateLayoutMode(this.layoutMode);
|
this.updateLayoutMode(this.layoutMode());
|
||||||
this.addEmptyPageIfRequired();
|
this.addEmptyPageIfRequired();
|
||||||
|
|
||||||
// Find all the part ids and their top offset
|
// Find all the part ids and their top offset
|
||||||
|
|
@ -1043,11 +1055,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.scrollTo(part);
|
this.scrollTo(part);
|
||||||
} else if (scrollTop !== undefined && scrollTop !== 0) {
|
} else if (scrollTop !== undefined && scrollTop !== 0) {
|
||||||
setTimeout(() => this.scrollService.scrollTo(scrollTop, this.reader.nativeElement));
|
setTimeout(() => this.scrollService.scrollTo(scrollTop, this.reader.nativeElement));
|
||||||
} else if ((this.writingStyle === WritingStyle.Vertical) && (this.layoutMode === BookPageLayoutMode.Default)) {
|
} else if ((this.writingStyle === WritingStyle.Vertical) && (this.layoutMode() === BookPageLayoutMode.Default)) {
|
||||||
setTimeout(()=> this.scrollService.scrollToX(this.bookContentElemRef.nativeElement.clientWidth, this.reader.nativeElement));
|
setTimeout(()=> this.scrollService.scrollToX(this.bookContentElemRef.nativeElement.clientWidth, this.reader.nativeElement));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
if (this.layoutMode === BookPageLayoutMode.Default) {
|
if (this.layoutMode() === BookPageLayoutMode.Default) {
|
||||||
setTimeout(() => this.scrollService.scrollTo(0, this.reader.nativeElement));
|
setTimeout(() => this.scrollService.scrollTo(0, this.reader.nativeElement));
|
||||||
} else if (this.writingStyle === WritingStyle.Vertical) {
|
} else if (this.writingStyle === WritingStyle.Vertical) {
|
||||||
if (this.pagingDirection === PAGING_DIRECTION.BACKWARDS) {
|
if (this.pagingDirection === PAGING_DIRECTION.BACKWARDS) {
|
||||||
|
|
@ -1112,7 +1124,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
private addEmptyPageIfRequired(): void {
|
private addEmptyPageIfRequired(): void {
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Column2 || this.isSingleImagePage) {
|
if (this.layoutMode() !== BookPageLayoutMode.Column2 || this.isSingleImagePage) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1194,7 +1206,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.pagingDirection = PAGING_DIRECTION.BACKWARDS;
|
this.pagingDirection = PAGING_DIRECTION.BACKWARDS;
|
||||||
|
|
||||||
// We need to handle virtual paging before we increment the actual page
|
// We need to handle virtual paging before we increment the actual page
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default) {
|
||||||
const [currentVirtualPage, _, pageWidth] = this.getVirtualPage();
|
const [currentVirtualPage, _, pageWidth] = this.getVirtualPage();
|
||||||
|
|
||||||
if (currentVirtualPage > 1) {
|
if (currentVirtualPage > 1) {
|
||||||
|
|
@ -1229,7 +1241,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
this.pagingDirection = PAGING_DIRECTION.FORWARD;
|
this.pagingDirection = PAGING_DIRECTION.FORWARD;
|
||||||
// We need to handle virtual paging before we increment the actual page
|
// We need to handle virtual paging before we increment the actual page
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default) {
|
||||||
const [currentVirtualPage, totalVirtualPages, pageWidth] = this.getVirtualPage();
|
const [currentVirtualPage, totalVirtualPages, pageWidth] = this.getVirtualPage();
|
||||||
|
|
||||||
if (currentVirtualPage < totalVirtualPages) {
|
if (currentVirtualPage < totalVirtualPages) {
|
||||||
|
|
@ -1401,7 +1413,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
// After layout shifts, we need to refocus the scroll bar
|
// After layout shifts, we need to refocus the scroll bar
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default && resumeElement !== null && resumeElement !== undefined) {
|
||||||
this.updateWidthAndHeightCalcs();
|
this.updateWidthAndHeightCalcs();
|
||||||
this.scrollTo(resumeElement); // This works pretty well, but not perfect
|
this.scrollTo(resumeElement); // This works pretty well, but not perfect
|
||||||
}
|
}
|
||||||
|
|
@ -1415,7 +1427,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
// Remove all themes
|
// Remove all themes
|
||||||
Array.from(this.document.querySelectorAll('style[id^="brtheme-"]')).forEach(elem => elem.remove());
|
Array.from(this.document.querySelectorAll('style[id^="brtheme-"]')).forEach(elem => elem.remove());
|
||||||
|
|
||||||
this.darkMode = theme.isDarkTheme;
|
this.darkMode.set(theme.isDarkTheme);
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
const styleElem = this.renderer.createElement('style');
|
const styleElem = this.renderer.createElement('style');
|
||||||
styleElem.id = theme.selector;
|
styleElem.id = theme.selector;
|
||||||
|
|
@ -1471,13 +1484,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
if (drawerIsOpen) {
|
if (drawerIsOpen) {
|
||||||
this.epubMenuService.closeAll();
|
this.epubMenuService.closeAll();
|
||||||
} else {
|
} else {
|
||||||
// TODO: BUG: Migrating the reader settings over into the drawer means default settings aren't being set on load
|
|
||||||
this.epubMenuService.openSettingsDrawer(this.chapterId, this.seriesId, this.readingProfile, (res => {
|
this.epubMenuService.openSettingsDrawer(this.chapterId, this.seriesId, this.readingProfile, (res => {
|
||||||
this.handleReaderSettingsUpdate(res);
|
this.handleReaderSettingsUpdate(res);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.immersiveMode) { // NOTE: Shouldn't this check if drawer is open?
|
if (this.immersiveMode()) { // NOTE: Shouldn't this check if drawer is open?
|
||||||
this.actionBarVisible = false;
|
this.actionBarVisible = false;
|
||||||
}
|
}
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
@ -1498,12 +1510,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
if (element === null) return;
|
if (element === null) return;
|
||||||
|
|
||||||
if(this.layoutMode === BookPageLayoutMode.Default && this.writingStyle === WritingStyle.Vertical ) {
|
if(this.layoutMode() === BookPageLayoutMode.Default && this.writingStyle === WritingStyle.Vertical ) {
|
||||||
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
|
const windowWidth = window.innerWidth || document.documentElement.clientWidth;
|
||||||
const scrollLeft = element.getBoundingClientRect().left + window.scrollX - (windowWidth - element.getBoundingClientRect().width);
|
const scrollLeft = element.getBoundingClientRect().left + window.scrollX - (windowWidth - element.getBoundingClientRect().width);
|
||||||
setTimeout(() => this.scrollService.scrollToX(scrollLeft, this.reader.nativeElement, 'smooth'), 10);
|
setTimeout(() => this.scrollService.scrollToX(scrollLeft, this.reader.nativeElement, 'smooth'), 10);
|
||||||
}
|
}
|
||||||
else if ((this.layoutMode === BookPageLayoutMode.Default) && (this.writingStyle === WritingStyle.Horizontal)) {
|
else if ((this.layoutMode() === BookPageLayoutMode.Default) && (this.writingStyle === WritingStyle.Horizontal)) {
|
||||||
const fromTopOffset = element.getBoundingClientRect().top + window.scrollY + TOP_OFFSET;
|
const fromTopOffset = element.getBoundingClientRect().top + window.scrollY + TOP_OFFSET;
|
||||||
// We need to use a delay as webkit browsers (aka Apple devices) don't always have the document rendered by this point
|
// 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);
|
||||||
|
|
@ -1545,7 +1557,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
// HACK: This is a bug with how browsers change the background color for fullscreen mode
|
// 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'));
|
this.renderer.setStyle(this.reader.nativeElement, 'background', this.themeService.getCssVariable('--bs-body-color'));
|
||||||
if (!this.darkMode) {
|
if (!this.darkMode()) {
|
||||||
this.renderer.setStyle(this.reader.nativeElement, 'background', 'white');
|
this.renderer.setStyle(this.reader.nativeElement, 'background', 'white');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1555,7 +1567,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
updateWritingStyle(writingStyle: WritingStyle) {
|
updateWritingStyle(writingStyle: WritingStyle) {
|
||||||
this.writingStyle = writingStyle;
|
this.writingStyle = writingStyle;
|
||||||
setTimeout(() => this.updateImageSizes());
|
setTimeout(() => this.updateImageSizes());
|
||||||
if (this.layoutMode !== BookPageLayoutMode.Default) {
|
if (this.layoutMode() !== BookPageLayoutMode.Default) {
|
||||||
const lastSelector = this.lastSeenScrollPartPath;
|
const lastSelector = this.lastSeenScrollPartPath;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.scrollTo(lastSelector);
|
this.scrollTo(lastSelector);
|
||||||
|
|
@ -1572,11 +1584,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateLayoutMode(mode: BookPageLayoutMode) {
|
updateLayoutMode(mode: BookPageLayoutMode) {
|
||||||
const layoutModeChanged = mode !== this.layoutMode;
|
const layoutModeChanged = mode !== this.layoutMode();
|
||||||
this.layoutMode = mode;
|
this.layoutMode.set(mode);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
console.log('layout mode changed to: ', this.layoutMode);
|
console.log('layout mode changed to: ', this.layoutMode());
|
||||||
|
|
||||||
this.clearTimeout(this.updateImageSizeTimeout);
|
this.clearTimeout(this.updateImageSizeTimeout);
|
||||||
this.updateImageSizeTimeout = setTimeout( () => {
|
this.updateImageSizeTimeout = setTimeout( () => {
|
||||||
|
|
@ -1587,7 +1599,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
// Calculate if bottom actionbar is needed. On a timeout to get accurate heights
|
// Calculate if bottom actionbar is needed. On a timeout to get accurate heights
|
||||||
if (this.bookContentElemRef == null) {
|
if (this.bookContentElemRef == null) {
|
||||||
setTimeout(() => this.updateLayoutMode(this.layoutMode), 10);
|
setTimeout(() => this.updateLayoutMode(this.layoutMode()), 10);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -1609,8 +1621,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateImmersiveMode(immersiveMode: boolean) {
|
updateImmersiveMode(immersiveMode: boolean) {
|
||||||
this.immersiveMode = immersiveMode;
|
this.immersiveMode.set(immersiveMode);
|
||||||
if (this.immersiveMode && !this.epubMenuService.isDrawerOpen()) {
|
if (immersiveMode && !this.epubMenuService.isDrawerOpen()) {
|
||||||
this.actionBarVisible = false;
|
this.actionBarVisible = false;
|
||||||
this.updateReadingSectionHeight();
|
this.updateReadingSectionHeight();
|
||||||
}
|
}
|
||||||
|
|
@ -1623,8 +1635,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
const elem = this.readingSectionElemRef;
|
const elem = this.readingSectionElemRef;
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (renderer === undefined || elem === undefined) return;
|
if (renderer === undefined || elem === undefined) return;
|
||||||
if (this.immersiveMode) {
|
if (!this.immersiveMode()) {
|
||||||
} else {
|
|
||||||
renderer.setStyle(elem.nativeElement, 'height', 'calc(var(--vh, 1vh) * 100 - ' + this.topOffset + 'px)', RendererStyleFlags2.Important);
|
renderer.setStyle(elem.nativeElement, 'height', 'calc(var(--vh, 1vh) * 100 - ' + this.topOffset + 'px)', RendererStyleFlags2.Important);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1733,7 +1744,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
const targetElement = (event.target as Element);
|
const targetElement = (event.target as Element);
|
||||||
const mouseOffset = 5;
|
const mouseOffset = 5;
|
||||||
|
|
||||||
if (!this.immersiveMode) return;
|
if (!this.immersiveMode()) return;
|
||||||
if (targetElement.getAttribute('onclick') !== null || targetElement.getAttribute('href') !== null || targetElement.getAttribute('role') !== null || targetElement.getAttribute('kavita-part') != null) {
|
if (targetElement.getAttribute('onclick') !== null || targetElement.getAttribute('href') !== null || targetElement.getAttribute('role') !== null || targetElement.getAttribute('kavita-part') != null) {
|
||||||
// Don't do anything, it's actionable
|
// Don't do anything, it's actionable
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -271,16 +271,7 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
createNewProfileFromImplicit() {
|
createNewProfileFromImplicit() {
|
||||||
if (this.readingProfile.kind !== ReadingProfileKind.Implicit) {
|
this.readerSettingsService.createNewProfileFromImplicit();
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.readerSettingsService.promoteProfile().subscribe(newProfile => {
|
|
||||||
this.readingProfile = newProfile;
|
|
||||||
this.parentReadingProfile = newProfile;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
this.toastr.success(translate("manga-reader.reading-profile-promoted"));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue