Refactored reader settings to signals.
This commit is contained in:
parent
d114949446
commit
5ca7bf1a87
5 changed files with 488 additions and 375 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
import {DestroyRef, inject, Injectable} from '@angular/core';
|
import {computed, DestroyRef, effect, inject, Injectable, signal} from '@angular/core';
|
||||||
import {BehaviorSubject, distinctUntilChanged, Observable, Subject} from 'rxjs';
|
import {Observable, Subject} from 'rxjs';
|
||||||
import {bookColorThemes, PageStyle} from "../book-reader/_components/reader-settings/reader-settings.component";
|
import {bookColorThemes, PageStyle} from "../book-reader/_components/reader-settings/reader-settings.component";
|
||||||
import {ReadingDirection} from '../_models/preferences/reading-direction';
|
import {ReadingDirection} from '../_models/preferences/reading-direction';
|
||||||
import {WritingStyle} from '../_models/preferences/writing-style';
|
import {WritingStyle} from '../_models/preferences/writing-style';
|
||||||
|
|
@ -7,7 +7,6 @@ import {BookPageLayoutMode} from "../_models/readers/book-page-layout-mode";
|
||||||
import {FormControl, FormGroup} from "@angular/forms";
|
import {FormControl, FormGroup} from "@angular/forms";
|
||||||
import {ReadingProfile, ReadingProfileKind} from "../_models/preferences/reading-profiles";
|
import {ReadingProfile, ReadingProfileKind} from "../_models/preferences/reading-profiles";
|
||||||
import {BookService, FontFamily} from "../book-reader/_services/book.service";
|
import {BookService, FontFamily} from "../book-reader/_services/book.service";
|
||||||
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
|
||||||
import {ThemeService} from './theme.service';
|
import {ThemeService} from './theme.service';
|
||||||
import {ReadingProfileService} from "./reading-profile.service";
|
import {ReadingProfileService} from "./reading-profile.service";
|
||||||
import {debounceTime, skip, tap} from "rxjs/operators";
|
import {debounceTime, skip, tap} from "rxjs/operators";
|
||||||
|
|
@ -15,6 +14,7 @@ import {BookTheme} from "../_models/preferences/book-theme";
|
||||||
import {DOCUMENT} from "@angular/common";
|
import {DOCUMENT} from "@angular/common";
|
||||||
import {translate} from "@jsverse/transloco";
|
import {translate} from "@jsverse/transloco";
|
||||||
import {ToastrService} from "ngx-toastr";
|
import {ToastrService} from "ngx-toastr";
|
||||||
|
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
|
||||||
|
|
||||||
export interface ReaderSettingUpdate {
|
export interface ReaderSettingUpdate {
|
||||||
setting: 'pageStyle' | 'clickToPaginate' | 'fullscreen' | 'writingStyle' | 'layoutMode' | 'readingDirection' | 'immersiveMode' | 'theme';
|
setting: 'pageStyle' | 'clickToPaginate' | 'fullscreen' | 'writingStyle' | 'layoutMode' | 'readingDirection' | 'immersiveMode' | 'theme';
|
||||||
|
|
@ -33,70 +33,208 @@ export class EpubReaderSettingsService {
|
||||||
private readonly toastr = inject(ToastrService);
|
private readonly toastr = inject(ToastrService);
|
||||||
private readonly document = inject(DOCUMENT);
|
private readonly document = inject(DOCUMENT);
|
||||||
|
|
||||||
private pageStylesSubject = new BehaviorSubject<PageStyle>(this.getDefaultPageStyles());
|
// Core signals - these will be the single source of truth
|
||||||
private readingDirectionSubject = new BehaviorSubject<ReadingDirection>(ReadingDirection.LeftToRight);
|
private readonly _currentReadingProfile = signal<ReadingProfile | null>(null);
|
||||||
private writingStyleSubject = new BehaviorSubject<WritingStyle>(WritingStyle.Horizontal);
|
private readonly _parentReadingProfile = signal<ReadingProfile | null>(null);
|
||||||
// @ts-ignore
|
private readonly _currentSeriesId = signal<number | null>(null);
|
||||||
private activeThemeSubject = new BehaviorSubject<BookTheme | undefined>(undefined);
|
private readonly _isInitialized = signal<boolean>(false);
|
||||||
private clickToPaginateSubject = new BehaviorSubject<boolean>(false);
|
|
||||||
private layoutModeSubject = new BehaviorSubject<BookPageLayoutMode>(BookPageLayoutMode.Default);
|
|
||||||
private immersiveModeSubject = new BehaviorSubject<boolean>(false);
|
|
||||||
private readingProfileSubject = new BehaviorSubject<ReadingProfile | null>(null);
|
|
||||||
|
|
||||||
// Event subjects for component communication
|
// Settings signals
|
||||||
|
private readonly _pageStyles = signal<PageStyle>(this.getDefaultPageStyles()); // Internal property used to capture all the different css properties to render on all elements
|
||||||
|
private readonly _readingDirection = signal<ReadingDirection>(ReadingDirection.LeftToRight);
|
||||||
|
private readonly _writingStyle = signal<WritingStyle>(WritingStyle.Horizontal);
|
||||||
|
private readonly _activeTheme = signal<BookTheme | undefined>(undefined);
|
||||||
|
private readonly _clickToPaginate = signal<boolean>(false);
|
||||||
|
private readonly _layoutMode = signal<BookPageLayoutMode>(BookPageLayoutMode.Default);
|
||||||
|
private readonly _immersiveMode = signal<boolean>(false);
|
||||||
|
private readonly _isFullscreen = signal<boolean>(false);
|
||||||
|
|
||||||
|
// Form will be managed separately but updated from signals
|
||||||
|
private settingsForm: FormGroup = new FormGroup({});
|
||||||
|
private fontFamilies: FontFamily[] = this.bookService.getFontFamilies();
|
||||||
|
private isUpdatingFromForm = false; // Flag to prevent infinite loops
|
||||||
|
|
||||||
|
// Event subject for component communication (keep this for now, can be converted to effect later)
|
||||||
private settingUpdateSubject = new Subject<ReaderSettingUpdate>();
|
private settingUpdateSubject = new Subject<ReaderSettingUpdate>();
|
||||||
|
|
||||||
// Form and data
|
// Public readonly signals
|
||||||
private settingsForm: FormGroup = new FormGroup({});
|
public readonly currentReadingProfile = this._currentReadingProfile.asReadonly();
|
||||||
private currentReadingProfile: ReadingProfile | null = null;
|
public readonly parentReadingProfile = this._parentReadingProfile.asReadonly();
|
||||||
private parentReadingProfile: ReadingProfile | null = null;
|
public readonly isInitialized = this._isInitialized.asReadonly();
|
||||||
private currentSeriesId: number | null = null;
|
|
||||||
private fontFamilies: FontFamily[] = this.bookService.getFontFamilies();
|
|
||||||
|
|
||||||
// Public observables
|
// Settings as readonly signals
|
||||||
public readonly pageStyles$ = this.pageStylesSubject.asObservable();
|
public readonly pageStyles = this._pageStyles.asReadonly();
|
||||||
public readonly readingDirection$ = this.readingDirectionSubject.asObservable();
|
public readonly readingDirection = this._readingDirection.asReadonly();
|
||||||
public readonly writingStyle$ = this.writingStyleSubject.asObservable();
|
public readonly writingStyle = this._writingStyle.asReadonly();
|
||||||
public readonly activeTheme$ = this.activeThemeSubject.asObservable();
|
public readonly activeTheme = this._activeTheme.asReadonly();
|
||||||
public readonly clickToPaginate$ = this.clickToPaginateSubject.asObservable();
|
public readonly clickToPaginate = this._clickToPaginate.asReadonly();
|
||||||
public readonly layoutMode$ = this.layoutModeSubject.asObservable();
|
public readonly layoutMode = this._layoutMode.asReadonly();
|
||||||
public readonly immersiveMode$ = this.immersiveModeSubject.asObservable();
|
public readonly immersiveMode = this._immersiveMode.asReadonly();
|
||||||
public readonly readingProfile$ = this.readingProfileSubject.asObservable();
|
public readonly isFullscreen = this._isFullscreen.asReadonly();
|
||||||
|
|
||||||
|
// Computed signals for derived state
|
||||||
|
public readonly canPromoteProfile = computed(() => {
|
||||||
|
const profile = this._currentReadingProfile();
|
||||||
|
return profile !== null && profile.kind === ReadingProfileKind.Implicit;
|
||||||
|
});
|
||||||
|
|
||||||
|
public readonly hasParentProfile = computed(() => {
|
||||||
|
return this._parentReadingProfile() !== null;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Keep observable for now - can be converted to effect later
|
||||||
public readonly settingUpdates$ = this.settingUpdateSubject.asObservable();
|
public readonly settingUpdates$ = this.settingUpdateSubject.asObservable();
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
// Effect to update form when signals change (only when not updating from form)
|
||||||
|
effect(() => {
|
||||||
|
const profile = this._currentReadingProfile();
|
||||||
|
if (profile && this._isInitialized() && !this.isUpdatingFromForm) {
|
||||||
|
this.updateFormFromSignals();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Effect to emit setting updates when signals change
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'pageStyle',
|
||||||
|
object: this._pageStyles()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'clickToPaginate',
|
||||||
|
object: this._clickToPaginate()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'layoutMode',
|
||||||
|
object: this._layoutMode()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'readingDirection',
|
||||||
|
object: this._readingDirection()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'writingStyle',
|
||||||
|
object: this._writingStyle()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'immersiveMode',
|
||||||
|
object: this._immersiveMode()
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
effect(() => {
|
||||||
|
if (!this._isInitialized()) return;
|
||||||
|
|
||||||
|
const theme = this._activeTheme();
|
||||||
|
if (theme) {
|
||||||
|
this.settingUpdateSubject.next({
|
||||||
|
setting: 'theme',
|
||||||
|
object: theme
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the service with a reading profile and series ID
|
* Initialize the service with a reading profile and series ID
|
||||||
* This should be called when the reader starts up
|
|
||||||
*/
|
*/
|
||||||
async initialize(seriesId: number, readingProfile: ReadingProfile): Promise<void> {
|
async initialize(seriesId: number, readingProfile: ReadingProfile): Promise<void> {
|
||||||
this.currentSeriesId = seriesId;
|
this._currentSeriesId.set(seriesId);
|
||||||
this.currentReadingProfile = readingProfile;
|
this._currentReadingProfile.set(readingProfile);
|
||||||
|
|
||||||
console.log('init, reading profile: ', readingProfile);
|
console.log('init, reading profile: ', 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();
|
||||||
this.parentReadingProfile = parent || null;
|
this._parentReadingProfile.set(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup defaults and form
|
// Setup defaults and update signals
|
||||||
this.setupDefaultSettings();
|
this.setupDefaultsFromProfile(readingProfile);
|
||||||
|
this.setupSettingsForm();
|
||||||
|
|
||||||
// Set initial theme
|
// Set initial theme
|
||||||
const themeName = readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme;
|
const themeName = readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme;
|
||||||
this.setTheme(themeName, false);
|
this.setTheme(themeName, false);
|
||||||
|
|
||||||
// Emit initial values
|
// Mark as initialized - this will trigger effects to emit initial values
|
||||||
this.emitInitialSettings();
|
this._isInitialized.set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setup default values and update signals from profile
|
||||||
|
*/
|
||||||
|
private setupDefaultsFromProfile(profile: ReadingProfile): void {
|
||||||
|
// Set defaults if undefined
|
||||||
|
if (profile.bookReaderFontFamily === undefined) {
|
||||||
|
profile.bookReaderFontFamily = 'default';
|
||||||
|
}
|
||||||
|
if (profile.bookReaderFontSize === undefined || profile.bookReaderFontSize < 50) {
|
||||||
|
profile.bookReaderFontSize = 100;
|
||||||
|
}
|
||||||
|
if (profile.bookReaderLineSpacing === undefined || profile.bookReaderLineSpacing < 100) {
|
||||||
|
profile.bookReaderLineSpacing = 100;
|
||||||
|
}
|
||||||
|
if (profile.bookReaderMargin === undefined) {
|
||||||
|
profile.bookReaderMargin = 0;
|
||||||
|
}
|
||||||
|
if (profile.bookReaderReadingDirection === undefined) {
|
||||||
|
profile.bookReaderReadingDirection = ReadingDirection.LeftToRight;
|
||||||
|
}
|
||||||
|
if (profile.bookReaderWritingStyle === undefined) {
|
||||||
|
profile.bookReaderWritingStyle = WritingStyle.Horizontal;
|
||||||
|
}
|
||||||
|
if (profile.bookReaderLayoutMode === undefined) {
|
||||||
|
profile.bookReaderLayoutMode = BookPageLayoutMode.Default;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update signals from profile
|
||||||
|
this._readingDirection.set(profile.bookReaderReadingDirection);
|
||||||
|
this._writingStyle.set(profile.bookReaderWritingStyle);
|
||||||
|
this._clickToPaginate.set(profile.bookReaderTapToPaginate);
|
||||||
|
this._layoutMode.set(profile.bookReaderLayoutMode);
|
||||||
|
this._immersiveMode.set(profile.bookReaderImmersiveMode);
|
||||||
|
|
||||||
|
// Set up page styles
|
||||||
|
this.setPageStyles(
|
||||||
|
profile.bookReaderFontFamily,
|
||||||
|
profile.bookReaderFontSize + '%',
|
||||||
|
profile.bookReaderMargin + 'vw',
|
||||||
|
profile.bookReaderLineSpacing + '%'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -110,7 +248,7 @@ export class EpubReaderSettingsService {
|
||||||
* Get current reading profile
|
* Get current reading profile
|
||||||
*/
|
*/
|
||||||
getCurrentReadingProfile(): ReadingProfile | null {
|
getCurrentReadingProfile(): ReadingProfile | null {
|
||||||
return this.currentReadingProfile;
|
return this._currentReadingProfile();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -131,28 +269,26 @@ export class EpubReaderSettingsService {
|
||||||
* Toggle reading direction
|
* Toggle reading direction
|
||||||
*/
|
*/
|
||||||
toggleReadingDirection(): void {
|
toggleReadingDirection(): void {
|
||||||
const current = this.readingDirectionSubject.value;
|
const current = this._readingDirection();
|
||||||
const newDirection = current === ReadingDirection.LeftToRight
|
const newDirection = current === ReadingDirection.LeftToRight
|
||||||
? ReadingDirection.RightToLeft
|
? ReadingDirection.RightToLeft
|
||||||
: ReadingDirection.LeftToRight;
|
: ReadingDirection.LeftToRight;
|
||||||
|
|
||||||
this.readingDirectionSubject.next(newDirection);
|
this._readingDirection.set(newDirection);
|
||||||
this.settingUpdateSubject.next({ setting: 'readingDirection', object: newDirection });
|
this.debouncedUpdateProfile();
|
||||||
this.updateImplicitProfile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggle writing style
|
* Toggle writing style
|
||||||
*/
|
*/
|
||||||
toggleWritingStyle(): void {
|
toggleWritingStyle(): void {
|
||||||
const current = this.writingStyleSubject.value;
|
const current = this._writingStyle();
|
||||||
const newStyle = current === WritingStyle.Horizontal
|
const newStyle = current === WritingStyle.Horizontal
|
||||||
? WritingStyle.Vertical
|
? WritingStyle.Vertical
|
||||||
: WritingStyle.Horizontal;
|
: WritingStyle.Horizontal;
|
||||||
|
|
||||||
this.writingStyleSubject.next(newStyle);
|
this._writingStyle.set(newStyle);
|
||||||
this.settingUpdateSubject.next({ setting: 'writingStyle', object: newStyle });
|
this.debouncedUpdateProfile();
|
||||||
this.updateImplicitProfile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -161,34 +297,81 @@ export class EpubReaderSettingsService {
|
||||||
setTheme(themeName: string, update: boolean = true): void {
|
setTheme(themeName: string, update: boolean = true): void {
|
||||||
const theme = bookColorThemes.find(t => t.name === themeName);
|
const theme = bookColorThemes.find(t => t.name === themeName);
|
||||||
if (theme) {
|
if (theme) {
|
||||||
this.activeThemeSubject.next(theme);
|
this._activeTheme.set(theme);
|
||||||
this.settingUpdateSubject.next({ setting: 'theme', object: theme });
|
|
||||||
|
|
||||||
if (update) {
|
if (update) {
|
||||||
this.updateImplicitProfile();
|
this.debouncedUpdateProfile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateLayoutMode(mode: BookPageLayoutMode): void {
|
||||||
|
this._layoutMode.set(mode);
|
||||||
|
// Update form control to keep in sync
|
||||||
|
this.settingsForm.get('layoutMode')?.setValue(mode, { emitEvent: false });
|
||||||
|
this.debouncedUpdateProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateClickToPaginate(value: boolean): void {
|
||||||
|
this._clickToPaginate.set(value);
|
||||||
|
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(value, { emitEvent: false });
|
||||||
|
this.debouncedUpdateProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateReadingDirection(value: ReadingDirection): void {
|
||||||
|
this._readingDirection.set(value);
|
||||||
|
this.debouncedUpdateProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateWritingStyle(value: WritingStyle) {
|
||||||
|
this._writingStyle.set(value);
|
||||||
|
this.debouncedUpdateProfile();
|
||||||
|
}
|
||||||
|
|
||||||
|
updateFullscreen(value: boolean) {
|
||||||
|
this._isFullscreen.set(value);
|
||||||
|
this.settingUpdateSubject.next({ setting: 'fullscreen', object: null }); // TODO: Refactor into an effect
|
||||||
|
}
|
||||||
|
|
||||||
|
updateImmersiveMode(value: boolean): void {
|
||||||
|
this._immersiveMode.set(value);
|
||||||
|
if (value) {
|
||||||
|
this._clickToPaginate.set(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounced update method to prevent too many API calls
|
||||||
|
private updateTimeout: any;
|
||||||
|
private debouncedUpdateProfile(): void {
|
||||||
|
if (this.updateTimeout) {
|
||||||
|
clearTimeout(this.updateTimeout);
|
||||||
|
}
|
||||||
|
this.updateTimeout = setTimeout(() => {
|
||||||
|
this.updateImplicitProfile();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Emit fullscreen toggle event
|
* Emit fullscreen toggle event
|
||||||
*/
|
*/
|
||||||
toggleFullscreen(): void {
|
toggleFullscreen(): void {
|
||||||
this.settingUpdateSubject.next({ setting: 'fullscreen', object: null });
|
this.updateFullscreen(!this._isFullscreen());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update parent reading profile preferences
|
* Update parent reading profile preferences
|
||||||
*/
|
*/
|
||||||
updateParentProfile(): void {
|
updateParentProfile(): void {
|
||||||
if (!this.currentReadingProfile || this.currentReadingProfile.kind !== ReadingProfileKind.Implicit || !this.currentSeriesId) {
|
const currentRp = this._currentReadingProfile();
|
||||||
|
const seriesId = this._currentSeriesId();
|
||||||
|
if (!currentRp || currentRp.kind !== ReadingProfileKind.Implicit || !seriesId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.readingProfileService.updateParentProfile(this.currentSeriesId, this.packReadingProfile())
|
this.readingProfileService.updateParentProfile(seriesId, this.packReadingProfile())
|
||||||
.subscribe(newProfile => {
|
.subscribe(newProfile => {
|
||||||
this.currentReadingProfile = newProfile;
|
this._currentReadingProfile.set(newProfile);
|
||||||
this.readingProfileSubject.next(newProfile);
|
this.toastr.success(translate('manga-reader.reading-profile-updated'));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,166 +379,171 @@ export class EpubReaderSettingsService {
|
||||||
* Promote implicit profile to named profile
|
* Promote implicit profile to named profile
|
||||||
*/
|
*/
|
||||||
promoteProfile(): Observable<ReadingProfile> {
|
promoteProfile(): Observable<ReadingProfile> {
|
||||||
if (!this.currentReadingProfile || this.currentReadingProfile.kind !== ReadingProfileKind.Implicit) {
|
const currentRp = this._currentReadingProfile();
|
||||||
|
if (!currentRp || currentRp.kind !== ReadingProfileKind.Implicit) {
|
||||||
throw new Error('Can only promote implicit profiles');
|
throw new Error('Can only promote implicit profiles');
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.readingProfileService.promoteProfile(this.currentReadingProfile.id).pipe(
|
return this.readingProfileService.promoteProfile(currentRp.id).pipe(
|
||||||
tap(newProfile => {
|
tap(newProfile => {
|
||||||
this.currentReadingProfile = newProfile;
|
this._currentReadingProfile.set(newProfile);
|
||||||
this.readingProfileSubject.next(newProfile);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private setupDefaultSettings(): void {
|
/**
|
||||||
if (!this.currentReadingProfile) return;
|
* Update form controls from current signal values
|
||||||
|
*/
|
||||||
|
private updateFormFromSignals(): void {
|
||||||
|
const profile = this._currentReadingProfile();
|
||||||
|
if (!profile) return;
|
||||||
|
|
||||||
// Set up defaults
|
// Update form controls without triggering valueChanges
|
||||||
const profile = this.currentReadingProfile;
|
this.settingsForm.patchValue({
|
||||||
if (profile.bookReaderFontFamily === undefined) {
|
bookReaderFontFamily: profile.bookReaderFontFamily,
|
||||||
profile.bookReaderFontFamily = 'default';
|
bookReaderFontSize: profile.bookReaderFontSize,
|
||||||
}
|
bookReaderTapToPaginate: this._clickToPaginate(),
|
||||||
if (profile.bookReaderFontSize === undefined || profile.bookReaderFontSize < 50) {
|
bookReaderLineSpacing: profile.bookReaderLineSpacing,
|
||||||
profile.bookReaderFontSize = 100;
|
bookReaderMargin: profile.bookReaderMargin,
|
||||||
}
|
layoutMode: this._layoutMode(),
|
||||||
if (profile.bookReaderLineSpacing === undefined || profile.bookReaderLineSpacing < 100) {
|
bookReaderImmersiveMode: this._immersiveMode()
|
||||||
profile.bookReaderLineSpacing = 100;
|
}, { emitEvent: false });
|
||||||
}
|
|
||||||
if (profile.bookReaderMargin === undefined) {
|
|
||||||
profile.bookReaderMargin = 0;
|
|
||||||
}
|
|
||||||
if (profile.bookReaderReadingDirection === undefined) {
|
|
||||||
profile.bookReaderReadingDirection = ReadingDirection.LeftToRight;
|
|
||||||
}
|
|
||||||
if (profile.bookReaderWritingStyle === undefined) {
|
|
||||||
profile.bookReaderWritingStyle = WritingStyle.Horizontal;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setupSettingsForm();
|
|
||||||
|
|
||||||
// Update internal state
|
|
||||||
this.readingDirectionSubject.next(profile.bookReaderReadingDirection);
|
|
||||||
this.writingStyleSubject.next(profile.bookReaderWritingStyle);
|
|
||||||
this.clickToPaginateSubject.next(profile.bookReaderTapToPaginate);
|
|
||||||
this.layoutModeSubject.next(profile.bookReaderLayoutMode);
|
|
||||||
this.immersiveModeSubject.next(profile.bookReaderImmersiveMode);
|
|
||||||
|
|
||||||
// Set up page styles
|
|
||||||
this.setPageStyles(
|
|
||||||
profile.bookReaderFontFamily,
|
|
||||||
profile.bookReaderFontSize + '%',
|
|
||||||
profile.bookReaderMargin + 'vw',
|
|
||||||
profile.bookReaderLineSpacing + '%'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the reactive form and bidirectional binding with signals
|
||||||
|
*/
|
||||||
private setupSettingsForm(): void {
|
private setupSettingsForm(): void {
|
||||||
if (!this.currentReadingProfile) return;
|
const profile = this._currentReadingProfile();
|
||||||
|
if (!profile) return;
|
||||||
const profile = this.currentReadingProfile;
|
|
||||||
|
|
||||||
// Clear existing form
|
// Clear existing form
|
||||||
this.settingsForm = new FormGroup({});
|
this.settingsForm = new FormGroup({});
|
||||||
|
|
||||||
// Add controls
|
// Add controls with current values
|
||||||
this.settingsForm.addControl('bookReaderFontFamily', new FormControl(profile.bookReaderFontFamily));
|
this.settingsForm.addControl('bookReaderFontFamily', new FormControl(profile.bookReaderFontFamily));
|
||||||
this.settingsForm.addControl('bookReaderFontSize', new FormControl(profile.bookReaderFontSize));
|
this.settingsForm.addControl('bookReaderFontSize', new FormControl(profile.bookReaderFontSize));
|
||||||
this.settingsForm.addControl('bookReaderTapToPaginate', new FormControl(profile.bookReaderTapToPaginate));
|
this.settingsForm.addControl('bookReaderTapToPaginate', new FormControl(this._clickToPaginate()));
|
||||||
this.settingsForm.addControl('bookReaderLineSpacing', new FormControl(profile.bookReaderLineSpacing));
|
this.settingsForm.addControl('bookReaderLineSpacing', new FormControl(profile.bookReaderLineSpacing));
|
||||||
this.settingsForm.addControl('bookReaderMargin', new FormControl(profile.bookReaderMargin));
|
this.settingsForm.addControl('bookReaderMargin', new FormControl(profile.bookReaderMargin));
|
||||||
this.settingsForm.addControl('layoutMode', new FormControl(profile.bookReaderLayoutMode));
|
this.settingsForm.addControl('layoutMode', new FormControl(this._layoutMode()));
|
||||||
this.settingsForm.addControl('bookReaderImmersiveMode', new FormControl(profile.bookReaderImmersiveMode));
|
this.settingsForm.addControl('bookReaderImmersiveMode', new FormControl(this._immersiveMode()));
|
||||||
|
|
||||||
// Set up value change subscriptions
|
// Set up value change subscriptions
|
||||||
this.setupFormSubscriptions();
|
this.setupFormSubscriptions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up form value change subscriptions to update signals
|
||||||
|
*/
|
||||||
private setupFormSubscriptions(): void {
|
private setupFormSubscriptions(): void {
|
||||||
// Font family changes
|
// Font family changes
|
||||||
this.settingsForm.get('bookReaderFontFamily')?.valueChanges.pipe(
|
this.settingsForm.get('bookReaderFontFamily')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(fontName => {
|
).subscribe(fontName => {
|
||||||
const familyName = this.fontFamilies.find(f => f.title === fontName)?.family || 'default';
|
this.isUpdatingFromForm = true;
|
||||||
const currentStyles = this.pageStylesSubject.value;
|
|
||||||
|
|
||||||
|
const familyName = this.fontFamilies.find(f => f.title === fontName)?.family || 'default';
|
||||||
|
const currentStyles = this._pageStyles();
|
||||||
|
|
||||||
|
const newStyles = { ...currentStyles };
|
||||||
if (familyName === 'default') {
|
if (familyName === 'default') {
|
||||||
currentStyles['font-family'] = 'inherit';
|
newStyles['font-family'] = 'inherit';
|
||||||
} else {
|
} else {
|
||||||
currentStyles['font-family'] = `'${familyName}'`;
|
newStyles['font-family'] = `'${familyName}'`;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.pageStylesSubject.next({ ...currentStyles });
|
this._pageStyles.set(newStyles);
|
||||||
this.settingUpdateSubject.next({ setting: 'pageStyle', object: currentStyles });
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Font size changes
|
// Font size changes
|
||||||
this.settingsForm.get('bookReaderFontSize')?.valueChanges.pipe(
|
this.settingsForm.get('bookReaderFontSize')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(value => {
|
).subscribe(value => {
|
||||||
const currentStyles = this.pageStylesSubject.value;
|
this.isUpdatingFromForm = true;
|
||||||
currentStyles['font-size'] = value + '%';
|
|
||||||
this.pageStylesSubject.next({ ...currentStyles });
|
const currentStyles = this._pageStyles();
|
||||||
this.settingUpdateSubject.next({ setting: 'pageStyle', object: currentStyles });
|
const newStyles = { ...currentStyles };
|
||||||
|
newStyles['font-size'] = value + '%';
|
||||||
|
this._pageStyles.set(newStyles);
|
||||||
|
|
||||||
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Tap to paginate changes
|
// Tap to paginate changes
|
||||||
this.settingsForm.get('bookReaderTapToPaginate')?.valueChanges.pipe(
|
this.settingsForm.get('bookReaderTapToPaginate')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(value => {
|
).subscribe(value => {
|
||||||
this.clickToPaginateSubject.next(value);
|
this.isUpdatingFromForm = true;
|
||||||
this.settingUpdateSubject.next({ setting: 'clickToPaginate', object: value });
|
this._clickToPaginate.set(value);
|
||||||
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Line spacing changes
|
// Line spacing changes
|
||||||
this.settingsForm.get('bookReaderLineSpacing')?.valueChanges.pipe(
|
this.settingsForm.get('bookReaderLineSpacing')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(value => {
|
).subscribe(value => {
|
||||||
const currentStyles = this.pageStylesSubject.value;
|
this.isUpdatingFromForm = true;
|
||||||
currentStyles['line-height'] = value + '%';
|
|
||||||
this.pageStylesSubject.next({ ...currentStyles });
|
const currentStyles = this._pageStyles();
|
||||||
this.settingUpdateSubject.next({ setting: 'pageStyle', object: currentStyles });
|
const newStyles = { ...currentStyles };
|
||||||
|
newStyles['line-height'] = value + '%';
|
||||||
|
this._pageStyles.set(newStyles);
|
||||||
|
|
||||||
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Margin changes
|
// Margin changes
|
||||||
this.settingsForm.get('bookReaderMargin')?.valueChanges.pipe(
|
this.settingsForm.get('bookReaderMargin')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(value => {
|
).subscribe(value => {
|
||||||
const currentStyles = this.pageStylesSubject.value;
|
this.isUpdatingFromForm = true;
|
||||||
currentStyles['margin-left'] = value + 'vw';
|
|
||||||
currentStyles['margin-right'] = value + 'vw';
|
const currentStyles = this._pageStyles();
|
||||||
this.pageStylesSubject.next({ ...currentStyles });
|
const newStyles = { ...currentStyles };
|
||||||
this.settingUpdateSubject.next({ setting: 'pageStyle', object: currentStyles });
|
newStyles['margin-left'] = value + 'vw';
|
||||||
|
newStyles['margin-right'] = value + 'vw';
|
||||||
|
this._pageStyles.set(newStyles);
|
||||||
|
|
||||||
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Layout mode changes
|
// Layout mode changes
|
||||||
this.settingsForm.get('layoutMode')?.valueChanges.pipe(
|
this.settingsForm.get('layoutMode')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe((layoutMode: BookPageLayoutMode) => {
|
).subscribe((layoutMode: BookPageLayoutMode) => {
|
||||||
this.layoutModeSubject.next(layoutMode);
|
this.isUpdatingFromForm = true;
|
||||||
this.settingUpdateSubject.next({ setting: 'layoutMode', object: layoutMode });
|
this._layoutMode.set(layoutMode);
|
||||||
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Immersive mode changes
|
// Immersive mode changes
|
||||||
this.settingsForm.get('bookReaderImmersiveMode')?.valueChanges.pipe(
|
this.settingsForm.get('bookReaderImmersiveMode')?.valueChanges.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe((immersiveMode: boolean) => {
|
).subscribe((immersiveMode: boolean) => {
|
||||||
|
this.isUpdatingFromForm = true;
|
||||||
|
|
||||||
if (immersiveMode) {
|
if (immersiveMode) {
|
||||||
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(true);
|
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(true, { emitEvent: false });
|
||||||
|
this._clickToPaginate.set(true);
|
||||||
}
|
}
|
||||||
this.immersiveModeSubject.next(immersiveMode);
|
this._immersiveMode.set(immersiveMode);
|
||||||
this.settingUpdateSubject.next({ setting: 'immersiveMode', object: immersiveMode });
|
|
||||||
|
this.isUpdatingFromForm = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update implicit profile on form changes
|
// Update implicit profile on form changes (debounced) - ONLY source of profile updates
|
||||||
this.settingsForm.valueChanges.pipe(
|
this.settingsForm.valueChanges.pipe(
|
||||||
debounceTime(300),
|
debounceTime(500), // Increased debounce time
|
||||||
distinctUntilChanged(),
|
|
||||||
skip(1), // Skip initial form creation
|
skip(1), // Skip initial form creation
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(() => {
|
).subscribe(() => {
|
||||||
this.updateImplicitProfile();
|
// Only update if we're not currently updating from form changes
|
||||||
|
if (!this.isUpdatingFromForm) {
|
||||||
|
this.updateImplicitProfile();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -367,34 +555,33 @@ export class EpubReaderSettingsService {
|
||||||
this.setPageStyles(
|
this.setPageStyles(
|
||||||
defaultStyles["font-family"],
|
defaultStyles["font-family"],
|
||||||
defaultStyles["font-size"],
|
defaultStyles["font-size"],
|
||||||
defaultStyles['margin-left'] ,
|
defaultStyles['margin-left'],
|
||||||
defaultStyles['line-height'],
|
defaultStyles['line-height'],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private emitInitialSettings(): void {
|
// private emitInitialSettings(): void {
|
||||||
// Emit all current settings so the reader can initialize properly
|
// // Emit all current settings so the reader can initialize properly
|
||||||
this.settingUpdateSubject.next({ setting: 'pageStyle', object: this.pageStylesSubject.value });
|
// this.settingUpdateSubject.next({ setting: 'pageStyle', object: this.pageStylesSubject.value });
|
||||||
this.settingUpdateSubject.next({ setting: 'clickToPaginate', object: this.clickToPaginateSubject.value });
|
// this.settingUpdateSubject.next({ setting: 'clickToPaginate', object: this.clickToPaginateSubject.value });
|
||||||
this.settingUpdateSubject.next({ setting: 'layoutMode', object: this.layoutModeSubject.value });
|
// this.settingUpdateSubject.next({ setting: 'layoutMode', object: this.layoutModeSubject.value });
|
||||||
this.settingUpdateSubject.next({ setting: 'readingDirection', object: this.readingDirectionSubject.value });
|
// this.settingUpdateSubject.next({ setting: 'readingDirection', object: this.readingDirectionSubject.value });
|
||||||
this.settingUpdateSubject.next({ setting: 'writingStyle', object: this.writingStyleSubject.value });
|
// this.settingUpdateSubject.next({ setting: 'writingStyle', object: this.writingStyleSubject.value });
|
||||||
this.settingUpdateSubject.next({ setting: 'immersiveMode', object: this.immersiveModeSubject.value });
|
// this.settingUpdateSubject.next({ setting: 'immersiveMode', object: this.immersiveModeSubject.value });
|
||||||
|
//
|
||||||
const activeTheme = this.activeThemeSubject.value;
|
// const activeTheme = this.activeThemeSubject.value;
|
||||||
if (activeTheme) {
|
// if (activeTheme) {
|
||||||
this.settingUpdateSubject.next({ setting: 'theme', object: activeTheme });
|
// this.settingUpdateSubject.next({ setting: 'theme', object: activeTheme });
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
||||||
private updateImplicitProfile(): void {
|
private updateImplicitProfile(): void {
|
||||||
if (!this.currentReadingProfile || !this.currentSeriesId) return;
|
if (!this._currentReadingProfile() || !this._currentSeriesId()) return;
|
||||||
|
|
||||||
this.readingProfileService.updateImplicit(this.packReadingProfile(), this.currentSeriesId)
|
this.readingProfileService.updateImplicit(this.packReadingProfile(), this._currentSeriesId()!)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: newProfile => {
|
next: newProfile => {
|
||||||
this.currentReadingProfile = newProfile;
|
this._currentReadingProfile.set(newProfile);
|
||||||
this.readingProfileSubject.next(newProfile);
|
|
||||||
},
|
},
|
||||||
error: err => {
|
error: err => {
|
||||||
console.error('Failed to update implicit profile:', err);
|
console.error('Failed to update implicit profile:', err);
|
||||||
|
|
@ -402,26 +589,32 @@ export class EpubReaderSettingsService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Packs current settings into a ReadingProfile object
|
||||||
|
*/
|
||||||
private packReadingProfile(): ReadingProfile {
|
private packReadingProfile(): ReadingProfile {
|
||||||
if (!this.currentReadingProfile) {
|
const currentProfile = this._currentReadingProfile();
|
||||||
|
if (!currentProfile) {
|
||||||
throw new Error('No current reading profile');
|
throw new Error('No current reading profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelSettings = this.settingsForm.getRawValue();
|
const modelSettings = this.settingsForm.getRawValue();
|
||||||
const data = { ...this.currentReadingProfile };
|
const data = { ...currentProfile };
|
||||||
|
|
||||||
|
// Update from form values
|
||||||
data.bookReaderFontFamily = modelSettings.bookReaderFontFamily;
|
data.bookReaderFontFamily = modelSettings.bookReaderFontFamily;
|
||||||
data.bookReaderFontSize = modelSettings.bookReaderFontSize;
|
data.bookReaderFontSize = modelSettings.bookReaderFontSize;
|
||||||
data.bookReaderLineSpacing = modelSettings.bookReaderLineSpacing;
|
data.bookReaderLineSpacing = modelSettings.bookReaderLineSpacing;
|
||||||
data.bookReaderMargin = modelSettings.bookReaderMargin;
|
data.bookReaderMargin = modelSettings.bookReaderMargin;
|
||||||
data.bookReaderTapToPaginate = modelSettings.bookReaderTapToPaginate;
|
data.bookReaderTapToPaginate = this._clickToPaginate();
|
||||||
data.bookReaderLayoutMode = modelSettings.layoutMode;
|
data.bookReaderLayoutMode = this._layoutMode();
|
||||||
data.bookReaderImmersiveMode = modelSettings.bookReaderImmersiveMode;
|
data.bookReaderImmersiveMode = this._immersiveMode();
|
||||||
|
|
||||||
data.bookReaderReadingDirection = this.readingDirectionSubject.value;
|
// Update from signals
|
||||||
data.bookReaderWritingStyle = this.writingStyleSubject.value;
|
data.bookReaderReadingDirection = this._readingDirection();
|
||||||
|
data.bookReaderWritingStyle = this._writingStyle();
|
||||||
|
|
||||||
const activeTheme = this.activeThemeSubject.value;
|
const activeTheme = this._activeTheme();
|
||||||
if (activeTheme) {
|
if (activeTheme) {
|
||||||
data.bookReaderThemeName = activeTheme.name;
|
data.bookReaderThemeName = activeTheme.name;
|
||||||
}
|
}
|
||||||
|
|
@ -431,6 +624,28 @@ export class EpubReaderSettingsService {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// private setPageStyles(fontFamily?: string, fontSize?: string, margin?: string, lineHeight?: string): void {
|
||||||
|
// const windowWidth = window.innerWidth || this.document.documentElement.clientWidth || this.document.body.clientWidth;
|
||||||
|
// const mobileBreakpointMarginOverride = 700;
|
||||||
|
//
|
||||||
|
// let defaultMargin = '15vw';
|
||||||
|
// if (windowWidth <= mobileBreakpointMarginOverride) {
|
||||||
|
// defaultMargin = '5vw';
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const currentStyles = this.pageStylesSubject.value;
|
||||||
|
// const newStyles: PageStyle = {
|
||||||
|
// 'font-family': fontFamily || currentStyles['font-family'] || 'default',
|
||||||
|
// 'font-size': fontSize || currentStyles['font-size'] || '100%',
|
||||||
|
// 'margin-left': margin || currentStyles['margin-left'] || defaultMargin,
|
||||||
|
// 'margin-right': margin || currentStyles['margin-right'] || defaultMargin,
|
||||||
|
// 'line-height': lineHeight || currentStyles['line-height'] || '100%'
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// this.pageStylesSubject.next(newStyles);
|
||||||
|
// this.updateImplicitProfile();
|
||||||
|
// this.settingUpdateSubject.next({ setting: 'pageStyle', object: newStyles });
|
||||||
|
// }
|
||||||
private setPageStyles(fontFamily?: string, fontSize?: string, margin?: string, lineHeight?: string): void {
|
private setPageStyles(fontFamily?: string, fontSize?: string, margin?: string, lineHeight?: string): void {
|
||||||
const windowWidth = window.innerWidth || this.document.documentElement.clientWidth || this.document.body.clientWidth;
|
const windowWidth = window.innerWidth || this.document.documentElement.clientWidth || this.document.body.clientWidth;
|
||||||
const mobileBreakpointMarginOverride = 700;
|
const mobileBreakpointMarginOverride = 700;
|
||||||
|
|
@ -440,7 +655,7 @@ export class EpubReaderSettingsService {
|
||||||
defaultMargin = '5vw';
|
defaultMargin = '5vw';
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentStyles = this.pageStylesSubject.value;
|
const currentStyles = this._pageStyles();
|
||||||
const newStyles: PageStyle = {
|
const newStyles: PageStyle = {
|
||||||
'font-family': fontFamily || currentStyles['font-family'] || 'default',
|
'font-family': fontFamily || currentStyles['font-family'] || 'default',
|
||||||
'font-size': fontSize || currentStyles['font-size'] || '100%',
|
'font-size': fontSize || currentStyles['font-size'] || '100%',
|
||||||
|
|
@ -449,9 +664,7 @@ export class EpubReaderSettingsService {
|
||||||
'line-height': lineHeight || currentStyles['line-height'] || '100%'
|
'line-height': lineHeight || currentStyles['line-height'] || '100%'
|
||||||
};
|
};
|
||||||
|
|
||||||
this.pageStylesSubject.next(newStyles);
|
this._pageStyles.set(newStyles);
|
||||||
this.updateImplicitProfile();
|
|
||||||
this.settingUpdateSubject.next({ setting: 'pageStyle', object: newStyles });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getDefaultPageStyles(): PageStyle {
|
public getDefaultPageStyles(): PageStyle {
|
||||||
|
|
@ -472,8 +685,8 @@ export class EpubReaderSettingsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.promoteProfile().subscribe(newProfile => {
|
this.promoteProfile().subscribe(newProfile => {
|
||||||
this.currentReadingProfile = newProfile;
|
this._currentReadingProfile.set(newProfile);
|
||||||
this.parentReadingProfile = newProfile;
|
this._parentReadingProfile.set(newProfile);
|
||||||
this.toastr.success(translate("manga-reader.reading-profile-promoted"));
|
this.toastr.success(translate("manga-reader.reading-profile-promoted"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,33 +17,34 @@
|
||||||
}
|
}
|
||||||
</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>
|
||||||
|
|
@ -108,10 +109,10 @@
|
||||||
@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)"
|
||||||
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsPrevDisabled : IsNextDisabled"
|
[disabled]="readingDirection() === ReadingDirection.LeftToRight ? IsPrevDisabled : IsNextDisabled"
|
||||||
title="{{readingDirection === ReadingDirection.LeftToRight ? t('previous') : t('next')}} Page">
|
title="{{readingDirection() === ReadingDirection.LeftToRight ? t('previous') : t('next')}} Page">
|
||||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsPrevChapter : IsNextChapter) ? 'fa-angle-double-left' : 'fa-angle-left'}} {{readingDirection === ReadingDirection.RightToLeft ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
|
<i class="fa {{(readingDirection() === ReadingDirection.LeftToRight ? IsPrevChapter : IsNextChapter) ? 'fa-angle-double-left' : 'fa-angle-left'}} {{readingDirection() === ReadingDirection.RightToLeft ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
@if (!this.adhocPageHistory.isEmpty()) {
|
@if (!this.adhocPageHistory.isEmpty()) {
|
||||||
|
|
@ -141,10 +142,10 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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"
|
||||||
[disabled]="readingDirection === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled"
|
[disabled]="readingDirection() === ReadingDirection.LeftToRight ? IsNextDisabled : IsPrevDisabled"
|
||||||
(click)="!isTop && movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
(click)="!isTop && movePage(readingDirection() === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
||||||
title="{{readingDirection === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page">
|
title="{{readingDirection() === ReadingDirection.LeftToRight ? t('next') : t('previous')}} Page">
|
||||||
<i class="fa {{(readingDirection === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}} {{readingDirection === ReadingDirection.LeftToRight ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
|
<i class="fa {{(readingDirection() === ReadingDirection.LeftToRight ? IsNextChapter : IsPrevChapter) ? 'fa-angle-double-right' : 'fa-angle-right'}} {{readingDirection() === ReadingDirection.LeftToRight ? 'next-page-highlight' : ''}}" aria-hidden="true"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -195,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 = model<boolean>(false);
|
//immersiveMode = model<boolean>(false);
|
||||||
/**
|
/**
|
||||||
* If we are loading from backend
|
* If we are loading from backend
|
||||||
*/
|
*/
|
||||||
|
|
@ -251,7 +251,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Internal property used to capture all the different css properties to render on all elements. This is a cached version that is updated from reader-settings component
|
* Internal property used to capture all the different css properties to render on all elements. This is a cached version that is updated from reader-settings component
|
||||||
*/
|
*/
|
||||||
pageStyles = model<PageStyle>(this.readerSettingsService.getDefaultPageStyles());
|
//pageStyles = model<PageStyle>(this.readerSettingsService.getDefaultPageStyles());
|
||||||
//pageStyles!: PageStyle;
|
//pageStyles!: PageStyle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -264,8 +264,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
horizontalScrollbarNeeded = false;
|
horizontalScrollbarNeeded = false;
|
||||||
scrollbarNeeded = false;
|
scrollbarNeeded = false;
|
||||||
readingDirection: ReadingDirection = ReadingDirection.LeftToRight;
|
// readingDirection: ReadingDirection = ReadingDirection.LeftToRight;
|
||||||
clickToPaginate = false;
|
// clickToPaginate = false;
|
||||||
/**
|
/**
|
||||||
* Used solely for fullscreen to apply a hack
|
* Used solely for fullscreen to apply a hack
|
||||||
*/
|
*/
|
||||||
|
|
@ -293,7 +293,7 @@ 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);
|
//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
|
||||||
|
|
@ -314,7 +314,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
*/
|
*/
|
||||||
pagingDirection: PAGING_DIRECTION = PAGING_DIRECTION.FORWARD;
|
pagingDirection: PAGING_DIRECTION = PAGING_DIRECTION.FORWARD;
|
||||||
|
|
||||||
writingStyle: WritingStyle = WritingStyle.Horizontal;
|
//writingStyle: WritingStyle = WritingStyle.Horizontal;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -341,11 +341,19 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
@ViewChild('reader', {static: false}) reader!: ElementRef;
|
@ViewChild('reader', {static: false}) reader!: ElementRef;
|
||||||
@ViewChild('readingHtml', { read: ViewContainerRef }) readingContainer!: ViewContainerRef;
|
@ViewChild('readingHtml', { read: ViewContainerRef }) readingContainer!: ViewContainerRef;
|
||||||
|
|
||||||
|
|
||||||
|
protected readonly layoutMode = this.readerSettingsService.layoutMode;
|
||||||
|
protected readonly pageStyles = this.readerSettingsService.pageStyles;
|
||||||
|
protected readonly immersiveMode = this.readerSettingsService.immersiveMode;
|
||||||
|
protected readonly readingDirection = this.readerSettingsService.readingDirection;
|
||||||
|
protected readonly writingStyle = this.readerSettingsService.writingStyle;
|
||||||
|
protected readonly clickToPaginate = this.readerSettingsService.clickToPaginate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables the Left most button
|
* Disables the Left most button
|
||||||
*/
|
*/
|
||||||
get IsPrevDisabled(): boolean {
|
get IsPrevDisabled(): boolean {
|
||||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
if (this.readingDirection() === ReadingDirection.LeftToRight) {
|
||||||
// Acting as Previous button
|
// Acting as Previous button
|
||||||
return this.isPrevPageDisabled();
|
return this.isPrevPageDisabled();
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +363,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
get IsNextDisabled(): boolean {
|
get IsNextDisabled(): boolean {
|
||||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
if (this.readingDirection() === ReadingDirection.LeftToRight) {
|
||||||
// Acting as Next button
|
// Acting as Next button
|
||||||
return this.isNextPageDisabled();
|
return this.isNextPageDisabled();
|
||||||
}
|
}
|
||||||
|
|
@ -409,7 +417,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';
|
||||||
|
|
@ -423,7 +431,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';
|
||||||
|
|
@ -432,7 +440,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';
|
||||||
}
|
}
|
||||||
|
|
@ -441,7 +449,7 @@ 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%'
|
||||||
|
|
@ -639,25 +647,26 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.readerSettingsService.readingProfile$.pipe(
|
// this.readerSettingsService.readingProfile$.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
// takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(profile => {
|
// ).subscribe(profile => {
|
||||||
if (profile) {
|
// if (profile) {
|
||||||
this.readingProfile = profile;
|
// this.readingProfile = profile;
|
||||||
this.cdRef.markForCheck();
|
// this.cdRef.markForCheck();
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
this.readerSettingsService.settingUpdates$.pipe(
|
this.readerSettingsService.settingUpdates$.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe((update: ReaderSettingUpdate) => {
|
).subscribe((update: ReaderSettingUpdate) => {
|
||||||
|
console.log('Book reader received reader setting update: ', update);
|
||||||
this.handleReaderSettingsUpdate(update);
|
this.handleReaderSettingsUpdate(update);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.readerSettingsService.activeTheme$.pipe(take(1)).subscribe(res => {
|
// this.readerSettingsService.activeTheme$.pipe(take(1)).subscribe(res => {
|
||||||
if (!res) return;
|
// if (!res) return;
|
||||||
this.updateColorTheme(res);
|
// this.updateColorTheme(res);
|
||||||
});
|
// });
|
||||||
// TODO: I may need to do this for everything
|
// TODO: I may need to do this for everything
|
||||||
|
|
||||||
forkJoin({
|
forkJoin({
|
||||||
|
|
@ -742,9 +751,9 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
if (isInputFocused) return;
|
if (isInputFocused) return;
|
||||||
|
|
||||||
if (event.key === KEY_CODES.RIGHT_ARROW) {
|
if (event.key === KEY_CODES.RIGHT_ARROW) {
|
||||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS);
|
this.movePage(this.readingDirection() === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS);
|
||||||
} else if (event.key === KEY_CODES.LEFT_ARROW) {
|
} else if (event.key === KEY_CODES.LEFT_ARROW) {
|
||||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
this.movePage(this.readingDirection() === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
||||||
} else if (event.key === KEY_CODES.ESC_KEY) {
|
} else if (event.key === KEY_CODES.ESC_KEY) {
|
||||||
const isHighlighting = window.getSelection()?.toString() != '';
|
const isHighlighting = window.getSelection()?.toString() != '';
|
||||||
if (isHighlighting) return;
|
if (isHighlighting) return;
|
||||||
|
|
@ -762,7 +771,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) {
|
||||||
|
|
@ -948,10 +957,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.addLinkClickHandlers();
|
this.addLinkClickHandlers();
|
||||||
this.readerSettingsService.pageStyles$.pipe(take(1)).subscribe(res => {
|
this.applyPageStyles(this.pageStyles());
|
||||||
this.updateReaderStyles(res);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const imgs = this.readingSectionElemRef.nativeElement.querySelectorAll('img');
|
const imgs = this.readingSectionElemRef.nativeElement.querySelectorAll('img');
|
||||||
if (imgs === null || imgs.length === 0) {
|
if (imgs === null || imgs.length === 0) {
|
||||||
|
|
@ -974,7 +980,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
* Updates the image properties to fit the current layout mode and screen size
|
* Updates the image properties to fit the current layout mode and screen size
|
||||||
*/
|
*/
|
||||||
updateImageSizes() {
|
updateImageSizes() {
|
||||||
const isVerticalWritingStyle = this.writingStyle === WritingStyle.Vertical;
|
const isVerticalWritingStyle = this.writingStyle() === WritingStyle.Vertical;
|
||||||
const height = this.windowHeight - (this.topOffset * 2);
|
const height = this.windowHeight - (this.topOffset * 2);
|
||||||
let maxHeight = 'unset';
|
let maxHeight = 'unset';
|
||||||
let maxWidth = '';
|
let maxWidth = '';
|
||||||
|
|
@ -1055,13 +1061,13 @@ 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) {
|
||||||
setTimeout(() => this.scrollService.scrollTo(this.bookContentElemRef.nativeElement.scrollHeight, this.bookContentElemRef.nativeElement, 'auto'));
|
setTimeout(() => this.scrollService.scrollTo(this.bookContentElemRef.nativeElement.scrollHeight, this.bookContentElemRef.nativeElement, 'auto'));
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1211,7 +1217,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
if (currentVirtualPage > 1) {
|
if (currentVirtualPage > 1) {
|
||||||
// -2 apparently goes back 1 virtual page...
|
// -2 apparently goes back 1 virtual page...
|
||||||
if (this.writingStyle === WritingStyle.Vertical) {
|
if (this.writingStyle() === WritingStyle.Vertical) {
|
||||||
this.scrollService.scrollTo((currentVirtualPage - 2) * pageWidth, this.bookContentElemRef.nativeElement, 'auto');
|
this.scrollService.scrollTo((currentVirtualPage - 2) * pageWidth, this.bookContentElemRef.nativeElement, 'auto');
|
||||||
} else {
|
} else {
|
||||||
this.scrollService.scrollToX((currentVirtualPage - 2) * pageWidth, this.bookContentElemRef.nativeElement);
|
this.scrollService.scrollToX((currentVirtualPage - 2) * pageWidth, this.bookContentElemRef.nativeElement);
|
||||||
|
|
@ -1246,7 +1252,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
if (currentVirtualPage < totalVirtualPages) {
|
if (currentVirtualPage < totalVirtualPages) {
|
||||||
// +0 apparently goes forward 1 virtual page...
|
// +0 apparently goes forward 1 virtual page...
|
||||||
if (this.writingStyle === WritingStyle.Vertical) {
|
if (this.writingStyle() === WritingStyle.Vertical) {
|
||||||
this.scrollService.scrollTo( (currentVirtualPage) * pageWidth, this.bookContentElemRef.nativeElement, 'auto');
|
this.scrollService.scrollTo( (currentVirtualPage) * pageWidth, this.bookContentElemRef.nativeElement, 'auto');
|
||||||
} else {
|
} else {
|
||||||
this.scrollService.scrollToX((currentVirtualPage) * pageWidth, this.bookContentElemRef.nativeElement);
|
this.scrollService.scrollToX((currentVirtualPage) * pageWidth, this.bookContentElemRef.nativeElement);
|
||||||
|
|
@ -1330,17 +1336,17 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
private getScrollOffsetAndTotalScroll() {
|
private getScrollOffsetAndTotalScroll() {
|
||||||
const { nativeElement: bookContent } = this.bookContentElemRef;
|
const { nativeElement: bookContent } = this.bookContentElemRef;
|
||||||
const scrollOffset = this.writingStyle === WritingStyle.Vertical
|
const scrollOffset = this.writingStyle() === WritingStyle.Vertical
|
||||||
? bookContent.scrollTop
|
? bookContent.scrollTop
|
||||||
: bookContent.scrollLeft;
|
: bookContent.scrollLeft;
|
||||||
const totalScroll = this.writingStyle === WritingStyle.Vertical
|
const totalScroll = this.writingStyle() === WritingStyle.Vertical
|
||||||
? bookContent.scrollHeight
|
? bookContent.scrollHeight
|
||||||
: bookContent.scrollWidth;
|
: bookContent.scrollWidth;
|
||||||
return [scrollOffset, totalScroll];
|
return [scrollOffset, totalScroll];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPageSize() {
|
private getPageSize() {
|
||||||
return this.writingStyle === WritingStyle.Vertical
|
return this.writingStyle() === WritingStyle.Vertical
|
||||||
? this.getPageHeight()
|
? this.getPageHeight()
|
||||||
: this.getPageWidth();
|
: this.getPageWidth();
|
||||||
}
|
}
|
||||||
|
|
@ -1372,10 +1378,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
/**
|
/**
|
||||||
* Applies styles onto the html of the book page
|
* Applies styles onto the html of the book page
|
||||||
*/
|
*/
|
||||||
updateReaderStyles(pageStyles: PageStyle) {
|
applyPageStyles(pageStyles: PageStyle) {
|
||||||
console.log('Got styles from settings: ', pageStyles);
|
console.log('Got styles from settings: ', pageStyles);
|
||||||
|
|
||||||
this.pageStyles.set(pageStyles);
|
//this.pageStyles.set(pageStyles);
|
||||||
|
//this.readerSettingsService.
|
||||||
if (this.bookContentElemRef === undefined || !this.bookContentElemRef.nativeElement) return;
|
if (this.bookContentElemRef === undefined || !this.bookContentElemRef.nativeElement) return;
|
||||||
|
|
||||||
// Before we apply styles, let's get an element on the screen so we can scroll to it after any shifts
|
// Before we apply styles, let's get an element on the screen so we can scroll to it after any shifts
|
||||||
|
|
@ -1453,7 +1460,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
handleReaderSettingsUpdate(res: ReaderSettingUpdate) {
|
handleReaderSettingsUpdate(res: ReaderSettingUpdate) {
|
||||||
switch (res.setting) {
|
switch (res.setting) {
|
||||||
case "pageStyle":
|
case "pageStyle":
|
||||||
this.updateReaderStyles(res.object as PageStyle);
|
this.applyPageStyles(res.object as PageStyle);
|
||||||
break;
|
break;
|
||||||
case "clickToPaginate":
|
case "clickToPaginate":
|
||||||
this.showPaginationOverlay(res.object as boolean);
|
this.showPaginationOverlay(res.object as boolean);
|
||||||
|
|
@ -1485,7 +1492,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
this.epubMenuService.closeAll();
|
this.epubMenuService.closeAll();
|
||||||
} else {
|
} else {
|
||||||
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);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1510,12 +1517,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);
|
||||||
|
|
@ -1565,7 +1572,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateWritingStyle(writingStyle: WritingStyle) {
|
updateWritingStyle(writingStyle: WritingStyle) {
|
||||||
this.writingStyle = writingStyle;
|
this.readerSettingsService.updateWritingStyle(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;
|
||||||
|
|
@ -1585,7 +1592,7 @@ 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.set(mode);
|
this.readerSettingsService.updateLayoutMode(mode);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
console.log('layout mode changed to: ', this.layoutMode());
|
console.log('layout mode changed to: ', this.layoutMode());
|
||||||
|
|
@ -1616,12 +1623,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateReadingDirection(readingDirection: ReadingDirection) {
|
updateReadingDirection(readingDirection: ReadingDirection) {
|
||||||
this.readingDirection = readingDirection;
|
this.readerSettingsService.updateReadingDirection(readingDirection);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateImmersiveMode(immersiveMode: boolean) {
|
updateImmersiveMode(immersiveMode: boolean) {
|
||||||
this.immersiveMode.set(immersiveMode);
|
this.readerSettingsService.updateImmersiveMode(immersiveMode);
|
||||||
if (immersiveMode && !this.epubMenuService.isDrawerOpen()) {
|
if (immersiveMode && !this.epubMenuService.isDrawerOpen()) {
|
||||||
this.actionBarVisible = false;
|
this.actionBarVisible = false;
|
||||||
this.updateReadingSectionHeight();
|
this.updateReadingSectionHeight();
|
||||||
|
|
@ -1674,7 +1681,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
|
|
||||||
// Settings Handlers
|
// Settings Handlers
|
||||||
showPaginationOverlay(clickToPaginate: boolean) {
|
showPaginationOverlay(clickToPaginate: boolean) {
|
||||||
this.clickToPaginate = clickToPaginate;
|
this.readerSettingsService.updateClickToPaginate(clickToPaginate);
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
|
|
||||||
this.clearTimeout(this.clickToPaginateVisualOverlayTimeout2);
|
this.clearTimeout(this.clickToPaginateVisualOverlayTimeout2);
|
||||||
|
|
@ -1718,7 +1725,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
if (this.readingDirection() === ReadingDirection.LeftToRight) {
|
||||||
return side === 'right' ? 'highlight' : 'highlight-2';
|
return side === 'right' ? 'highlight' : 'highlight-2';
|
||||||
}
|
}
|
||||||
return side === 'right' ? 'highlight-2' : 'highlight';
|
return side === 'right' ? 'highlight-2' : 'highlight';
|
||||||
|
|
|
||||||
|
|
@ -71,18 +71,18 @@
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||||
<label id="readingdirection" class="form-label">{{t('reading-direction-label')}}</label>
|
<label id="readingdirection" class="form-label">{{t('reading-direction-label')}}</label>
|
||||||
<button (click)="toggleReadingDirection()" class="btn btn-icon" aria-labelledby="readingdirection" title="{{readingDirectionModel === ReadingDirection.LeftToRight ? t('left-to-right') : t('right-to-left')}}">
|
<button (click)="toggleReadingDirection()" class="btn btn-icon" aria-labelledby="readingdirection" title="{{readingDirectionModel() === ReadingDirection.LeftToRight ? t('left-to-right') : t('right-to-left')}}">
|
||||||
<i class="fa {{readingDirectionModel === ReadingDirection.LeftToRight ? 'fa-arrow-right' : 'fa-arrow-left'}} " aria-hidden="true"></i>
|
<i class="fa {{readingDirectionModel() === ReadingDirection.LeftToRight ? 'fa-arrow-right' : 'fa-arrow-left'}} " aria-hidden="true"></i>
|
||||||
<span class="phone-hidden"> {{readingDirectionModel === ReadingDirection.LeftToRight ? t('left-to-right') : t('right-to-left')}}</span>
|
<span class="phone-hidden"> {{readingDirectionModel() === ReadingDirection.LeftToRight ? t('left-to-right') : t('right-to-left')}}</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="controls" style="display: flex; justify-content: space-between; align-items: center; ">
|
<div class="controls" style="display: flex; justify-content: space-between; align-items: center; ">
|
||||||
<label for="writing-style" class="form-label">{{t('writing-style-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="writingStyleTooltip" role="button" tabindex="0" aria-describedby="writingStyle-help"></i></label>
|
<label for="writing-style" class="form-label">{{t('writing-style-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="writingStyleTooltip" role="button" tabindex="0" aria-describedby="writingStyle-help"></i></label>
|
||||||
<ng-template #writingStyleTooltip>{{t('writing-style-tooltip')}}</ng-template>
|
<ng-template #writingStyleTooltip>{{t('writing-style-tooltip')}}</ng-template>
|
||||||
<span class="visually-hidden" id="writingStyle-help"><ng-container [ngTemplateOutlet]="writingStyleTooltip"></ng-container></span>
|
<span class="visually-hidden" id="writingStyle-help"><ng-container [ngTemplateOutlet]="writingStyleTooltip"></ng-container></span>
|
||||||
<button (click)="toggleWritingStyle()" id="writing-style" class="btn btn-icon" aria-labelledby="writingStyle-help" title="{{writingStyleModel === WritingStyle.Horizontal ? t('horizontal') : t('vertical')}}">
|
<button (click)="toggleWritingStyle()" id="writing-style" class="btn btn-icon" aria-labelledby="writingStyle-help" title="{{writingStyleModel() === WritingStyle.Horizontal ? t('horizontal') : t('vertical')}}">
|
||||||
<i class="fa {{writingStyleModel === WritingStyle.Horizontal ? 'fa-arrows-left-right' : 'fa-arrows-up-down' }}" aria-hidden="true"></i>
|
<i class="fa {{writingStyleModel() === WritingStyle.Horizontal ? 'fa-arrows-left-right' : 'fa-arrows-up-down' }}" aria-hidden="true"></i>
|
||||||
<span class="phone-hidden"> {{writingStyleModel === WritingStyle.Horizontal ? t('horizontal') : t('vertical') }}</span>
|
<span class="phone-hidden"> {{writingStyleModel() === WritingStyle.Horizontal ? t('horizontal') : t('vertical') }}</span>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -117,9 +117,9 @@
|
||||||
<ng-container [ngTemplateOutlet]="fullscreenTooltip"></ng-container>
|
<ng-container [ngTemplateOutlet]="fullscreenTooltip"></ng-container>
|
||||||
</span>
|
</span>
|
||||||
<button (click)="toggleFullscreen()" class="btn btn-icon" aria-labelledby="fullscreen">
|
<button (click)="toggleFullscreen()" class="btn btn-icon" aria-labelledby="fullscreen">
|
||||||
<i class="fa {{isFullscreen ? 'fa-compress-alt' : 'fa-expand-alt'}} {{isFullscreen ? 'icon-primary-color' : ''}}" aria-hidden="true"></i>
|
<i class="fa {{isFullscreen() ? 'fa-compress-alt' : 'fa-expand-alt'}} {{isFullscreen() ? 'icon-primary-color' : ''}}" aria-hidden="true"></i>
|
||||||
@if (activeTheme?.isDarkTheme) {
|
@if (activeTheme()?.isDarkTheme) {
|
||||||
<span class="ms-1">{{isFullscreen ? t('exit') : t('enter')}}</span>
|
<span class="ms-1">{{isFullscreen() ? t('exit') : t('enter')}}</span>
|
||||||
}
|
}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -160,7 +160,7 @@
|
||||||
<ng-template>
|
<ng-template>
|
||||||
<div class="controls">
|
<div class="controls">
|
||||||
@for(theme of themes; track theme.name) {
|
@for(theme of themes; track theme.name) {
|
||||||
<button class="btn btn-icon color" (click)="setTheme(theme.name)" [ngClass]="{'active': activeTheme?.name === theme.name}">
|
<button class="btn btn-icon color" (click)="setTheme(theme.name)" [ngClass]="{'active': activeTheme()?.name === theme.name}">
|
||||||
<div class="dot" [ngStyle]="{'background-color': theme.colorHash}"></div>
|
<div class="dot" [ngStyle]="{'background-color': theme.colorHash}"></div>
|
||||||
{{t(theme.translationKey)}}
|
{{t(theme.translationKey)}}
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -171,15 +171,16 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@let currentRP = currentReadingProfile();
|
||||||
<div class="row g-0 mt-2">
|
<div class="row g-0 mt-2">
|
||||||
<button class="btn btn-primary col-12 mb-2"
|
<button class="btn btn-primary col-12 mb-2"
|
||||||
[disabled]="readingProfile.kind !== ReadingProfileKind.Implicit || !parentReadingProfile"
|
[disabled]="!currentRP || currentRP.kind !== ReadingProfileKind.Implicit || !hasParentProfile()"
|
||||||
(click)="updateParentPref()">
|
(click)="updateParentPref()">
|
||||||
{{ t('update-parent', {name: parentReadingProfile?.name || t('loading')}) }}
|
{{ t('update-parent', {name: parentReadingProfile()?.name || t('loading')}) }}
|
||||||
</button>
|
</button>
|
||||||
<button class="btn btn-primary col-12 mb-2"
|
<button class="btn btn-primary col-12 mb-2"
|
||||||
[ngbTooltip]="t('create-new-tooltip')"
|
[ngbTooltip]="t('create-new-tooltip')"
|
||||||
[disabled]="readingProfile.kind !== ReadingProfileKind.Implicit"
|
[disabled]="!canPromoteProfile()"
|
||||||
(click)="createNewProfileFromImplicit()">
|
(click)="createNewProfileFromImplicit()">
|
||||||
{{ t('create-new') }}
|
{{ t('create-new') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,5 @@
|
||||||
import {NgClass, NgStyle, NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
import {NgClass, NgStyle, NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
||||||
import {
|
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, Input, OnInit} from '@angular/core';
|
||||||
ChangeDetectionStrategy,
|
|
||||||
ChangeDetectorRef,
|
|
||||||
Component,
|
|
||||||
DestroyRef,
|
|
||||||
EventEmitter,
|
|
||||||
inject,
|
|
||||||
Input,
|
|
||||||
OnInit,
|
|
||||||
Output
|
|
||||||
} from '@angular/core';
|
|
||||||
import {FormGroup, ReactiveFormsModule} from '@angular/forms';
|
import {FormGroup, ReactiveFormsModule} from '@angular/forms';
|
||||||
import {BookPageLayoutMode} from 'src/app/_models/readers/book-page-layout-mode';
|
import {BookPageLayoutMode} from 'src/app/_models/readers/book-page-layout-mode';
|
||||||
import {BookTheme} from 'src/app/_models/preferences/book-theme';
|
import {BookTheme} from 'src/app/_models/preferences/book-theme';
|
||||||
|
|
@ -21,7 +11,6 @@ import {BookBlackTheme} from '../../_models/book-black-theme';
|
||||||
import {BookDarkTheme} from '../../_models/book-dark-theme';
|
import {BookDarkTheme} from '../../_models/book-dark-theme';
|
||||||
import {BookWhiteTheme} from '../../_models/book-white-theme';
|
import {BookWhiteTheme} from '../../_models/book-white-theme';
|
||||||
import {BookPaperTheme} from '../../_models/book-paper-theme';
|
import {BookPaperTheme} from '../../_models/book-paper-theme';
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
|
||||||
import {
|
import {
|
||||||
NgbAccordionBody,
|
NgbAccordionBody,
|
||||||
NgbAccordionButton,
|
NgbAccordionButton,
|
||||||
|
|
@ -31,12 +20,10 @@ import {
|
||||||
NgbAccordionItem,
|
NgbAccordionItem,
|
||||||
NgbTooltip
|
NgbTooltip
|
||||||
} from '@ng-bootstrap/ng-bootstrap';
|
} from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
import {TranslocoDirective} from "@jsverse/transloco";
|
||||||
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
||||||
import {ReadingProfile, ReadingProfileKind} from "../../../_models/preferences/reading-profiles";
|
import {ReadingProfile, ReadingProfileKind} from "../../../_models/preferences/reading-profiles";
|
||||||
import {ToastrService} from "ngx-toastr";
|
|
||||||
import {EpubReaderSettingsService} from "../../../_services/epub-reader-settings.service";
|
import {EpubReaderSettingsService} from "../../../_services/epub-reader-settings.service";
|
||||||
import {tap} from "rxjs/operators";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for book reader. Do not use for other components
|
* Used for book reader. Do not use for other components
|
||||||
|
|
@ -107,138 +94,44 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private readonly cdRef = inject(ChangeDetectorRef);
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
private readonly readingProfileService = inject(ReadingProfileService);
|
private readonly readingProfileService = inject(ReadingProfileService);
|
||||||
private readonly toastr = inject(ToastrService);
|
|
||||||
|
|
||||||
@Input({required:true}) seriesId!: number;
|
@Input({required:true}) seriesId!: number;
|
||||||
@Input({required:true}) readingProfile!: ReadingProfile;
|
@Input({required:true}) readingProfile!: ReadingProfile;
|
||||||
/**
|
|
||||||
* Outputs when clickToPaginate is changed
|
|
||||||
*/
|
|
||||||
@Output() clickToPaginateChanged: EventEmitter<boolean> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when a style is updated and the reader needs to render it
|
|
||||||
*/
|
|
||||||
@Output() styleUpdate: EventEmitter<PageStyle> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when a theme/dark mode is updated
|
|
||||||
*/
|
|
||||||
@Output() colorThemeUpdate: EventEmitter<BookTheme> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when a layout mode is updated
|
|
||||||
*/
|
|
||||||
@Output() layoutModeUpdate: EventEmitter<BookPageLayoutMode> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when fullscreen is toggled
|
|
||||||
*/
|
|
||||||
@Output() fullscreen: EventEmitter<void> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when reading direction is changed
|
|
||||||
*/
|
|
||||||
@Output() readingDirection: EventEmitter<ReadingDirection> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when reading mode is changed
|
|
||||||
*/
|
|
||||||
@Output() bookReaderWritingStyle: EventEmitter<WritingStyle> = new EventEmitter();
|
|
||||||
/**
|
|
||||||
* Outputs when immersive mode is changed
|
|
||||||
*/
|
|
||||||
@Output() immersiveMode: EventEmitter<boolean> = new EventEmitter();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of all font families user can select from
|
* List of all font families user can select from
|
||||||
*/
|
*/
|
||||||
fontOptions: Array<string> = [];
|
fontOptions: Array<string> = [];
|
||||||
fontFamilies: Array<FontFamily> = [];
|
fontFamilies: Array<FontFamily> = [];
|
||||||
/**
|
|
||||||
* Internal property used to capture all the different css properties to render on all elements
|
|
||||||
*/
|
|
||||||
pageStyles: PageStyle = this.readerSettingsService.getDefaultPageStyles();
|
|
||||||
|
|
||||||
readingDirectionModel: ReadingDirection = ReadingDirection.LeftToRight;
|
|
||||||
|
|
||||||
writingStyleModel: WritingStyle = WritingStyle.Horizontal;
|
|
||||||
|
|
||||||
|
|
||||||
activeTheme: BookTheme | undefined;
|
|
||||||
|
|
||||||
isFullscreen: boolean = false;
|
|
||||||
|
|
||||||
settingsForm: FormGroup = new FormGroup({});
|
settingsForm: FormGroup = new FormGroup({});
|
||||||
|
|
||||||
/**
|
|
||||||
* The reading profile itself, unless readingProfile is implicit
|
|
||||||
*/
|
|
||||||
parentReadingProfile: ReadingProfile | null = null;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* System provided themes
|
* System provided themes
|
||||||
*/
|
*/
|
||||||
themes: Array<BookTheme> = bookColorThemes;
|
themes: Array<BookTheme> = this.readerSettingsService.getThemes();
|
||||||
|
|
||||||
|
protected readonly pageStyles = this.readerSettingsService.pageStyles;
|
||||||
|
protected readonly readingDirectionModel = this.readerSettingsService.readingDirection;
|
||||||
|
protected readonly writingStyleModel = this.readerSettingsService.writingStyle;
|
||||||
|
protected readonly activeTheme = this.readerSettingsService.activeTheme;
|
||||||
|
protected readonly layoutMode = this.readerSettingsService.layoutMode;
|
||||||
|
protected readonly immersiveMode = this.readerSettingsService.immersiveMode;
|
||||||
|
protected readonly clickToPaginate = this.readerSettingsService.clickToPaginate;
|
||||||
|
protected readonly isFullscreen = this.readerSettingsService.isFullscreen;
|
||||||
|
protected readonly canPromoteProfile = this.readerSettingsService.canPromoteProfile;
|
||||||
|
protected readonly hasParentProfile = this.readerSettingsService.hasParentProfile;
|
||||||
|
protected readonly parentReadingProfile = this.readerSettingsService.parentReadingProfile;
|
||||||
|
protected readonly currentReadingProfile = this.readerSettingsService.currentReadingProfile;
|
||||||
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
|
|
||||||
// Initialize the service if not already done
|
// Initialize the service if not already done
|
||||||
if (!this.readerSettingsService.getCurrentReadingProfile()) {
|
if (!this.readerSettingsService.getCurrentReadingProfile()) {
|
||||||
await this.readerSettingsService.initialize(this.seriesId, this.readingProfile);
|
await this.readerSettingsService.initialize(this.seriesId, this.readingProfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.readerSettingsService.readingProfile$.pipe(
|
|
||||||
takeUntilDestroyed(this.destroyRef),
|
|
||||||
tap((profile) => {
|
|
||||||
if (profile) {
|
|
||||||
this.readingProfile = profile;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
).subscribe();
|
|
||||||
|
|
||||||
this.settingsForm = this.readerSettingsService.getSettingsForm();
|
this.settingsForm = this.readerSettingsService.getSettingsForm();
|
||||||
this.fontFamilies = this.readerSettingsService.getFontFamilies();
|
this.fontFamilies = this.readerSettingsService.getFontFamilies();
|
||||||
this.fontOptions = this.fontFamilies.map(f => f.title);
|
this.fontOptions = this.fontFamilies.map(f => f.title);
|
||||||
this.themes = this.readerSettingsService.getThemes();
|
|
||||||
|
|
||||||
|
|
||||||
// Subscribe to service state
|
|
||||||
this.readerSettingsService.pageStyles$.pipe(
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
).subscribe(styles => {
|
|
||||||
this.pageStyles = styles;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.readerSettingsService.readingDirection$.pipe(
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
).subscribe(direction => {
|
|
||||||
this.readingDirectionModel = direction;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.readerSettingsService.writingStyle$.pipe(
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
).subscribe(style => {
|
|
||||||
this.writingStyleModel = style;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.readerSettingsService.activeTheme$.pipe(
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
).subscribe(theme => {
|
|
||||||
this.activeTheme = theme;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle parent reading profile
|
|
||||||
if (this.readingProfile.kind === ReadingProfileKind.Implicit) {
|
|
||||||
this.readingProfileService.getForSeries(this.seriesId, true).subscribe(parent => {
|
|
||||||
this.parentReadingProfile = parent;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.parentReadingProfile = this.readingProfile;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -259,7 +152,6 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleFullscreen() {
|
toggleFullscreen() {
|
||||||
this.isFullscreen = !this.isFullscreen;
|
|
||||||
this.readerSettingsService.toggleFullscreen();
|
this.readerSettingsService.toggleFullscreen();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
}
|
}
|
||||||
|
|
@ -267,7 +159,6 @@ export class ReaderSettingsComponent implements OnInit {
|
||||||
// menu only code
|
// menu only code
|
||||||
updateParentPref() {
|
updateParentPref() {
|
||||||
this.readerSettingsService.updateParentProfile();
|
this.readerSettingsService.updateParentProfile();
|
||||||
this.toastr.success(translate('manga-reader.reading-profile-updated'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createNewProfileFromImplicit() {
|
createNewProfileFromImplicit() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue