Migrate usages to ReadingProfiles
TODO: - Settings - An exception in the book reader (probably to do with the settings being loaded later)
This commit is contained in:
parent
616916548a
commit
5741a92bb2
13 changed files with 559 additions and 426 deletions
|
|
@ -1,48 +1,7 @@
|
|||
import {LayoutMode} from 'src/app/manga-reader/_models/layout-mode';
|
||||
import {BookPageLayoutMode} from '../readers/book-page-layout-mode';
|
||||
import {PageLayoutMode} from '../page-layout-mode';
|
||||
import {PageSplitOption} from './page-split-option';
|
||||
import {ReaderMode} from './reader-mode';
|
||||
import {ReadingDirection} from './reading-direction';
|
||||
import {ScalingOption} from './scaling-option';
|
||||
import {SiteTheme} from './site-theme';
|
||||
import {WritingStyle} from "./writing-style";
|
||||
import {PdfTheme} from "./pdf-theme";
|
||||
import {PdfScrollMode} from "./pdf-scroll-mode";
|
||||
import {PdfLayoutMode} from "./pdf-layout-mode";
|
||||
import {PdfSpreadMode} from "./pdf-spread-mode";
|
||||
|
||||
export interface Preferences {
|
||||
// Manga Reader
|
||||
readingDirection: ReadingDirection;
|
||||
scalingOption: ScalingOption;
|
||||
pageSplitOption: PageSplitOption;
|
||||
readerMode: ReaderMode;
|
||||
autoCloseMenu: boolean;
|
||||
layoutMode: LayoutMode;
|
||||
backgroundColor: string;
|
||||
showScreenHints: boolean;
|
||||
emulateBook: boolean;
|
||||
swipeToPaginate: boolean;
|
||||
allowAutomaticWebtoonReaderDetection: boolean;
|
||||
|
||||
// Book Reader
|
||||
bookReaderMargin: number;
|
||||
bookReaderLineSpacing: number;
|
||||
bookReaderFontSize: number;
|
||||
bookReaderFontFamily: string;
|
||||
bookReaderTapToPaginate: boolean;
|
||||
bookReaderReadingDirection: ReadingDirection;
|
||||
bookReaderWritingStyle: WritingStyle;
|
||||
bookReaderThemeName: string;
|
||||
bookReaderLayoutMode: BookPageLayoutMode;
|
||||
bookReaderImmersiveMode: boolean;
|
||||
|
||||
// PDF Reader
|
||||
pdfTheme: PdfTheme;
|
||||
pdfScrollMode: PdfScrollMode;
|
||||
pdfSpreadMode: PdfSpreadMode;
|
||||
|
||||
// Global
|
||||
theme: SiteTheme;
|
||||
globalPageLayoutMode: PageLayoutMode;
|
||||
|
|
@ -58,15 +17,3 @@ export interface Preferences {
|
|||
wantToReadSync: boolean;
|
||||
}
|
||||
|
||||
export const readingDirections = [{text: 'left-to-right', value: ReadingDirection.LeftToRight}, {text: 'right-to-left', value: ReadingDirection.RightToLeft}];
|
||||
export const bookWritingStyles = [{text: 'horizontal', value: WritingStyle.Horizontal}, {text: 'vertical', value: WritingStyle.Vertical}];
|
||||
export const scalingOptions = [{text: 'automatic', value: ScalingOption.Automatic}, {text: 'fit-to-height', value: ScalingOption.FitToHeight}, {text: 'fit-to-width', value: ScalingOption.FitToWidth}, {text: 'original', value: ScalingOption.Original}];
|
||||
export const pageSplitOptions = [{text: 'fit-to-screen', value: PageSplitOption.FitSplit}, {text: 'right-to-left', value: PageSplitOption.SplitRightToLeft}, {text: 'left-to-right', value: PageSplitOption.SplitLeftToRight}, {text: 'no-split', value: PageSplitOption.NoSplit}];
|
||||
export const readingModes = [{text: 'left-to-right', value: ReaderMode.LeftRight}, {text: 'up-to-down', value: ReaderMode.UpDown}, {text: 'webtoon', value: ReaderMode.Webtoon}];
|
||||
export const layoutModes = [{text: 'single', value: LayoutMode.Single}, {text: 'double', value: LayoutMode.Double}, {text: 'double-manga', value: LayoutMode.DoubleReversed}]; // TODO: Build this, {text: 'Double (No Cover)', value: LayoutMode.DoubleNoCover}
|
||||
export const bookLayoutModes = [{text: 'scroll', value: BookPageLayoutMode.Default}, {text: '1-column', value: BookPageLayoutMode.Column1}, {text: '2-column', value: BookPageLayoutMode.Column2}];
|
||||
export const pageLayoutModes = [{text: 'cards', value: PageLayoutMode.Cards}, {text: 'list', value: PageLayoutMode.List}];
|
||||
export const pdfLayoutModes = [{text: 'pdf-multiple', value: PdfLayoutMode.Multiple}, {text: 'pdf-book', value: PdfLayoutMode.Book}];
|
||||
export const pdfScrollModes = [{text: 'pdf-vertical', value: PdfScrollMode.Vertical}, {text: 'pdf-horizontal', value: PdfScrollMode.Horizontal}, {text: 'pdf-page', value: PdfScrollMode.Page}];
|
||||
export const pdfSpreadModes = [{text: 'pdf-none', value: PdfSpreadMode.None}, {text: 'pdf-odd', value: PdfSpreadMode.Odd}, {text: 'pdf-even', value: PdfSpreadMode.Even}];
|
||||
export const pdfThemes = [{text: 'pdf-light', value: PdfTheme.Light}, {text: 'pdf-dark', value: PdfTheme.Dark}];
|
||||
|
|
|
|||
64
UI/Web/src/app/_models/preferences/reading-profiles.ts
Normal file
64
UI/Web/src/app/_models/preferences/reading-profiles.ts
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import {LayoutMode} from 'src/app/manga-reader/_models/layout-mode';
|
||||
import {BookPageLayoutMode} from '../readers/book-page-layout-mode';
|
||||
import {PageLayoutMode} from '../page-layout-mode';
|
||||
import {PageSplitOption} from './page-split-option';
|
||||
import {ReaderMode} from './reader-mode';
|
||||
import {ReadingDirection} from './reading-direction';
|
||||
import {ScalingOption} from './scaling-option';
|
||||
import {WritingStyle} from "./writing-style";
|
||||
import {PdfTheme} from "./pdf-theme";
|
||||
import {PdfScrollMode} from "./pdf-scroll-mode";
|
||||
import {PdfLayoutMode} from "./pdf-layout-mode";
|
||||
import {PdfSpreadMode} from "./pdf-spread-mode";
|
||||
|
||||
export interface ReadingProfile {
|
||||
|
||||
id: number;
|
||||
name: string;
|
||||
normalizedName: string;
|
||||
|
||||
// Manga Reader
|
||||
readingDirection: ReadingDirection;
|
||||
scalingOption: ScalingOption;
|
||||
pageSplitOption: PageSplitOption;
|
||||
readerMode: ReaderMode;
|
||||
autoCloseMenu: boolean;
|
||||
layoutMode: LayoutMode;
|
||||
backgroundColor: string;
|
||||
showScreenHints: boolean;
|
||||
emulateBook: boolean;
|
||||
swipeToPaginate: boolean;
|
||||
allowAutomaticWebtoonReaderDetection: boolean;
|
||||
widthOverride?: number;
|
||||
|
||||
// Book Reader
|
||||
bookReaderMargin: number;
|
||||
bookReaderLineSpacing: number;
|
||||
bookReaderFontSize: number;
|
||||
bookReaderFontFamily: string;
|
||||
bookReaderTapToPaginate: boolean;
|
||||
bookReaderReadingDirection: ReadingDirection;
|
||||
bookReaderWritingStyle: WritingStyle;
|
||||
bookReaderThemeName: string;
|
||||
bookReaderLayoutMode: BookPageLayoutMode;
|
||||
bookReaderImmersiveMode: boolean;
|
||||
|
||||
// PDF Reader
|
||||
pdfTheme: PdfTheme;
|
||||
pdfScrollMode: PdfScrollMode;
|
||||
pdfSpreadMode: PdfSpreadMode;
|
||||
|
||||
}
|
||||
|
||||
export const readingDirections = [{text: 'left-to-right', value: ReadingDirection.LeftToRight}, {text: 'right-to-left', value: ReadingDirection.RightToLeft}];
|
||||
export const bookWritingStyles = [{text: 'horizontal', value: WritingStyle.Horizontal}, {text: 'vertical', value: WritingStyle.Vertical}];
|
||||
export const scalingOptions = [{text: 'automatic', value: ScalingOption.Automatic}, {text: 'fit-to-height', value: ScalingOption.FitToHeight}, {text: 'fit-to-width', value: ScalingOption.FitToWidth}, {text: 'original', value: ScalingOption.Original}];
|
||||
export const pageSplitOptions = [{text: 'fit-to-screen', value: PageSplitOption.FitSplit}, {text: 'right-to-left', value: PageSplitOption.SplitRightToLeft}, {text: 'left-to-right', value: PageSplitOption.SplitLeftToRight}, {text: 'no-split', value: PageSplitOption.NoSplit}];
|
||||
export const readingModes = [{text: 'left-to-right', value: ReaderMode.LeftRight}, {text: 'up-to-down', value: ReaderMode.UpDown}, {text: 'webtoon', value: ReaderMode.Webtoon}];
|
||||
export const layoutModes = [{text: 'single', value: LayoutMode.Single}, {text: 'double', value: LayoutMode.Double}, {text: 'double-manga', value: LayoutMode.DoubleReversed}]; // TODO: Build this, {text: 'Double (No Cover)', value: LayoutMode.DoubleNoCover}
|
||||
export const bookLayoutModes = [{text: 'scroll', value: BookPageLayoutMode.Default}, {text: '1-column', value: BookPageLayoutMode.Column1}, {text: '2-column', value: BookPageLayoutMode.Column2}];
|
||||
export const pageLayoutModes = [{text: 'cards', value: PageLayoutMode.Cards}, {text: 'list', value: PageLayoutMode.List}];
|
||||
export const pdfLayoutModes = [{text: 'pdf-multiple', value: PdfLayoutMode.Multiple}, {text: 'pdf-book', value: PdfLayoutMode.Book}];
|
||||
export const pdfScrollModes = [{text: 'pdf-vertical', value: PdfScrollMode.Vertical}, {text: 'pdf-horizontal', value: PdfScrollMode.Horizontal}, {text: 'pdf-page', value: PdfScrollMode.Page}];
|
||||
export const pdfSpreadModes = [{text: 'pdf-none', value: PdfSpreadMode.None}, {text: 'pdf-odd', value: PdfSpreadMode.Odd}, {text: 'pdf-even', value: PdfSpreadMode.Even}];
|
||||
export const pdfThemes = [{text: 'pdf-light', value: PdfTheme.Light}, {text: 'pdf-dark', value: PdfTheme.Dark}];
|
||||
30
UI/Web/src/app/_services/reading-profile.service.ts
Normal file
30
UI/Web/src/app/_services/reading-profile.service.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {environment} from "../../environments/environment";
|
||||
import {ReadingProfile} from "../_models/preferences/reading-profiles";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ReadingProfileService {
|
||||
|
||||
baseUrl = environment.apiUrl;
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
getForSeries(seriesId: number) {
|
||||
return this.httpClient.get<ReadingProfile>(this.baseUrl + "ReadingProfile/"+seriesId);
|
||||
}
|
||||
|
||||
updateProfile(profile: ReadingProfile, seriesId?: number) {
|
||||
if (seriesId) {
|
||||
return this.httpClient.post(this.baseUrl + "ReadingProfile?seriesCtx="+seriesId, profile);
|
||||
}
|
||||
return this.httpClient.post(this.baseUrl + "ReadingProfile", profile);
|
||||
}
|
||||
|
||||
updateImplicit(profile: ReadingProfile, seriesId: number) {
|
||||
return this.httpClient.post(this.baseUrl + "ReadingProfile/series?seriesId="+seriesId, profile);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -9,12 +9,12 @@ import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
|||
import {allEncodeFormats} from '../_models/encode-format';
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
|
||||
import {allCoverImageSizes, CoverImageSize} from '../_models/cover-image-size';
|
||||
import {pageLayoutModes} from "../../_models/preferences/preferences";
|
||||
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
|
||||
import {EncodeFormatPipe} from "../../_pipes/encode-format.pipe";
|
||||
import {CoverImageSizePipe} from "../../_pipes/cover-image-size.pipe";
|
||||
import {ConfirmService} from "../../shared/confirm.service";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {pageLayoutModes} from "../../_models/preferences/reading-profiles";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-media-settings',
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
<a ngbNavLink>{{t('settings-header')}}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<app-reader-settings
|
||||
[seriesId]="seriesId"
|
||||
(colorThemeUpdate)="updateColorTheme($event)"
|
||||
(styleUpdate)="updateReaderStyles($event)"
|
||||
(clickToPaginateChanged)="showPaginationOverlay($event)"
|
||||
|
|
|
|||
|
|
@ -1,172 +1,179 @@
|
|||
<ng-container *transloco="let t; read: 'reader-settings'">
|
||||
<!-- IDEA: Move the whole reader drawer into this component and have it self contained -->
|
||||
<form [formGroup]="settingsForm">
|
||||
<div ngbAccordion [closeOthers]="false" #acc="ngbAccordion">
|
||||
<div ngbAccordionItem id="general-panel" title="General Settings" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button ngbAccordionButton class="accordion-button" type="button" [attr.aria-expanded]="acc.isExpanded('general-panel')" aria-controls="collapseOne">
|
||||
{{t('general-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="control-container" >
|
||||
<div class="controls">
|
||||
<div class="mb-3">
|
||||
<label for="library-type" class="form-label">{{t('font-family-label')}}</label>
|
||||
<select class="form-select" id="library-type" formControlName="bookReaderFontFamily">
|
||||
<option [value]="opt" *ngFor="let opt of fontOptions; let i = index">{{opt | titlecase}}</option>
|
||||
</select>
|
||||
@if (readingProfile !== null) {
|
||||
<ng-container *transloco="let t; read: 'reader-settings'">
|
||||
<!-- IDEA: Move the whole reader drawer into this component and have it self contained -->
|
||||
<form [formGroup]="settingsForm">
|
||||
<div ngbAccordion [closeOthers]="false" #acc="ngbAccordion">
|
||||
<div ngbAccordionItem id="general-panel" title="General Settings" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button ngbAccordionButton class="accordion-button" type="button" [attr.aria-expanded]="acc.isExpanded('general-panel')" aria-controls="collapseOne">
|
||||
{{t('general-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="control-container" >
|
||||
<div class="controls">
|
||||
<div class="mb-3">
|
||||
<label for="library-type" class="form-label">{{t('font-family-label')}}</label>
|
||||
<select class="form-select" id="library-type" formControlName="bookReaderFontFamily">
|
||||
<option [value]="opt" *ngFor="let opt of fontOptions; let i = index">{{opt | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0 controls">
|
||||
<label for="fontsize" class="form-label col-6">{{t('font-size-label')}}</label>
|
||||
<span class="col-6 float-end" style="display: inline-flex;">
|
||||
<div class="row g-0 controls">
|
||||
<label for="fontsize" class="form-label col-6">{{t('font-size-label')}}</label>
|
||||
<span class="col-6 float-end" style="display: inline-flex;">
|
||||
<i class="fa-solid fa-font" style="font-size: 12px;"></i>
|
||||
<input type="range" class="form-range ms-2 me-2" id="fontsize" min="50" max="300" step="10" formControlName="bookReaderFontSize" [ngbTooltip]="settingsForm.get('bookReaderFontSize')?.value + '%'">
|
||||
<i class="fa-solid fa-font" style="font-size: 24px;"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 controls">
|
||||
<label for="linespacing" class="form-label col-6">{{t('line-spacing-label')}}</label>
|
||||
<span class="col-6 float-end" style="display: inline-flex;">
|
||||
<div class="row g-0 controls">
|
||||
<label for="linespacing" class="form-label col-6">{{t('line-spacing-label')}}</label>
|
||||
<span class="col-6 float-end" style="display: inline-flex;">
|
||||
1x
|
||||
<input type="range" class="form-range ms-2 me-2" id="linespacing" min="100" max="200" step="10" formControlName="bookReaderLineSpacing" [ngbTooltip]="settingsForm.get('bookReaderLineSpacing')?.value + '%'">
|
||||
2.5x
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 controls">
|
||||
<label for="margin" class="form-label col-6">{{t('margin-label')}}</label>
|
||||
<span class="col-6 float-end" style="display: inline-flex;">
|
||||
<div class="row g-0 controls">
|
||||
<label for="margin" class="form-label col-6">{{t('margin-label')}}</label>
|
||||
<span class="col-6 float-end" style="display: inline-flex;">
|
||||
<i class="fa-solid fa-outdent"></i>
|
||||
<input type="range" class="form-range ms-2 me-2" id="margin" min="0" max="30" step="5" formControlName="bookReaderMargin" [ngbTooltip]="settingsForm.get('bookReaderMargin')?.value + '%'">
|
||||
<i class="fa-solid fa-indent"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 justify-content-between mt-2">
|
||||
<button (click)="resetSettings()" class="btn btn-primary col">{{t('reset-to-defaults')}}</button>
|
||||
<div class="row g-0 justify-content-between mt-2">
|
||||
<button (click)="resetSettings()" class="btn btn-primary col">{{t('reset-to-defaults')}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div ngbAccordionItem id="reader-panel" title="Reader Settings" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded('reader-panel')" aria-controls="collapseOne">
|
||||
{{t('reader-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<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')}}">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<ng-template #writingStyleTooltip>{{t('writing-style-tooltip')}}</ng-template>
|
||||
<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')}}">
|
||||
<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>
|
||||
</button>
|
||||
|
||||
<div ngbAccordionItem id="reader-panel" title="Reader Settings" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded('reader-panel')" aria-controls="collapseOne">
|
||||
{{t('reader-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<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')}}">
|
||||
<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>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<ng-template #writingStyleTooltip>{{t('writing-style-tooltip')}}</ng-template>
|
||||
<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')}}">
|
||||
<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>
|
||||
</button>
|
||||
|
||||
</div>
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<label for="tap-pagination" class="form-label">{{t('tap-to-paginate-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="tapPaginationTooltip" role="button" tabindex="0" aria-describedby="tapPagination-help"></i></label>
|
||||
<ng-template #tapPaginationTooltip>{{t('tap-to-paginate-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="tapPagination-help">
|
||||
</div>
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<label for="tap-pagination" class="form-label">{{t('tap-to-paginate-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="tapPaginationTooltip" role="button" tabindex="0" aria-describedby="tapPagination-help"></i></label>
|
||||
<ng-template #tapPaginationTooltip>{{t('tap-to-paginate-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="tapPagination-help">
|
||||
<ng-container [ngTemplateOutlet]="tapPaginationTooltip"></ng-container>
|
||||
</span>
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="tap-pagination" formControlName="bookReaderTapToPaginate" class="form-check-input" aria-labelledby="tapPagination-help">
|
||||
<label>{{settingsForm.get('bookReaderTapToPaginate')?.value ? t('on') : t('off')}} </label>
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="tap-pagination" formControlName="bookReaderTapToPaginate" class="form-check-input" aria-labelledby="tapPagination-help">
|
||||
<label>{{settingsForm.get('bookReaderTapToPaginate')?.value ? t('on') : t('off')}} </label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<label for="immersive-mode" class="form-label">{{t('immersive-mode-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="immersiveModeTooltip" role="button" tabindex="0" aria-describedby="immersiveMode-help"></i></label>
|
||||
<ng-template #immersiveModeTooltip>{{t('immersive-mode-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="immersiveMode-help">
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<label for="immersive-mode" class="form-label">{{t('immersive-mode-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="immersiveModeTooltip" role="button" tabindex="0" aria-describedby="immersiveMode-help"></i></label>
|
||||
<ng-template #immersiveModeTooltip>{{t('immersive-mode-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="immersiveMode-help">
|
||||
<ng-container [ngTemplateOutlet]="immersiveModeTooltip"></ng-container>
|
||||
</span>
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="immersive-mode" formControlName="bookReaderImmersiveMode" class="form-check-input" aria-labelledby="immersiveMode-help">
|
||||
<label>{{settingsForm.get('bookReaderImmersiveMode')?.value ? t('on') : t('off')}} </label>
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="immersive-mode" formControlName="bookReaderImmersiveMode" class="form-check-input" aria-labelledby="immersiveMode-help">
|
||||
<label>{{settingsForm.get('bookReaderImmersiveMode')?.value ? t('on') : t('off')}} </label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<label id="fullscreen" class="form-label">{{t('fullscreen-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top"
|
||||
[ngbTooltip]="fullscreenTooltip" role="button" tabindex="1" aria-describedby="fullscreen-help"></i></label>
|
||||
<ng-template #fullscreenTooltip>{{t('fullscreen-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="fullscreen-help">
|
||||
<div class="controls" style="display:flex; justify-content:space-between; align-items:center;">
|
||||
<label id="fullscreen" class="form-label">{{t('fullscreen-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top"
|
||||
[ngbTooltip]="fullscreenTooltip" role="button" tabindex="1" aria-describedby="fullscreen-help"></i></label>
|
||||
<ng-template #fullscreenTooltip>{{t('fullscreen-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="fullscreen-help">
|
||||
<ng-container [ngTemplateOutlet]="fullscreenTooltip"></ng-container>
|
||||
</span>
|
||||
<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>
|
||||
<span *ngIf="activeTheme?.isDarkTheme"> {{isFullscreen ? t('exit') : t('enter')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
<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>
|
||||
<span *ngIf="activeTheme?.isDarkTheme"> {{isFullscreen ? t('exit') : t('enter')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="controls">
|
||||
<label id="layout-mode" class="form-label" style="margin-bottom:0.5rem">{{t('layout-mode-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="layoutTooltip" role="button" tabindex="1" aria-describedby="layout-help"></i></label>
|
||||
<ng-template #layoutTooltip><span [innerHTML]="t('layout-mode-tooltip')"></span></ng-template>
|
||||
<span class="visually-hidden" id="layout-help">
|
||||
<div class="controls">
|
||||
<label id="layout-mode" class="form-label" style="margin-bottom:0.5rem">{{t('layout-mode-label')}}<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="top" [ngbTooltip]="layoutTooltip" role="button" tabindex="1" aria-describedby="layout-help"></i></label>
|
||||
<ng-template #layoutTooltip><span [innerHTML]="t('layout-mode-tooltip')"></span></ng-template>
|
||||
<span class="visually-hidden" id="layout-help">
|
||||
<ng-container [ngTemplateOutlet]="layoutTooltip"></ng-container>
|
||||
</span>
|
||||
<br>
|
||||
<div class="btn-group d-flex justify-content-center" role="group" [attr.aria-label]="t('layout-mode-label')">
|
||||
<input type="radio" formControlName="layoutMode" [value]="BookPageLayoutMode.Default" class="btn-check" id="layout-mode-default" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="layout-mode-default">{{t('layout-mode-option-scroll')}}</label>
|
||||
<br>
|
||||
<div class="btn-group d-flex justify-content-center" role="group" [attr.aria-label]="t('layout-mode-label')">
|
||||
<input type="radio" formControlName="layoutMode" [value]="BookPageLayoutMode.Default" class="btn-check" id="layout-mode-default" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="layout-mode-default">{{t('layout-mode-option-scroll')}}</label>
|
||||
|
||||
<input type="radio" formControlName="layoutMode" [value]="BookPageLayoutMode.Column1" class="btn-check" id="layout-mode-col1" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="layout-mode-col1">{{t('layout-mode-option-1col')}}</label>
|
||||
<input type="radio" formControlName="layoutMode" [value]="BookPageLayoutMode.Column1" class="btn-check" id="layout-mode-col1" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="layout-mode-col1">{{t('layout-mode-option-1col')}}</label>
|
||||
|
||||
<input type="radio" formControlName="layoutMode" [value]="BookPageLayoutMode.Column2" class="btn-check" id="layout-mode-col2" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="layout-mode-col2">{{t('layout-mode-option-2col')}}</label>
|
||||
<input type="radio" formControlName="layoutMode" [value]="BookPageLayoutMode.Column2" class="btn-check" id="layout-mode-col2" autocomplete="off">
|
||||
<label class="btn btn-outline-primary" for="layout-mode-col2">{{t('layout-mode-option-2col')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbAccordionItem id="color-panel" [title]="t('color-theme-title')" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded('color-panel')" aria-controls="collapseOne">
|
||||
{{t('color-theme-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="controls">
|
||||
<ng-container *ngFor="let theme of themes">
|
||||
<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>
|
||||
{{t(theme.translationKey)}}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
<div ngbAccordionItem id="color-panel" [title]="t('color-theme-title')" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded('color-panel')" aria-controls="collapseOne">
|
||||
{{t('color-theme-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="controls">
|
||||
<ng-container *ngFor="let theme of themes">
|
||||
<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>
|
||||
{{t(theme.translationKey)}}
|
||||
</button>
|
||||
</ng-container>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</ng-container>
|
||||
<div class="row g-0 justify-content-between mt-2">
|
||||
<button (click)="savePref()" class="btn btn-primary col">{{t('save-global')}}</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</ng-container>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import {
|
|||
Component, DestroyRef,
|
||||
EventEmitter,
|
||||
inject,
|
||||
Inject,
|
||||
Inject, Input,
|
||||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
|
|
@ -27,6 +27,9 @@ import { BookPaperTheme } from '../../_models/book-paper-theme';
|
|||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import { NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody, NgbTooltip } from '@ng-bootstrap/ng-bootstrap';
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
||||
import {ReadingProfile} from "../../../_models/preferences/reading-profiles";
|
||||
import {debounceTime, distinctUntilChanged, tap} from "rxjs/operators";
|
||||
|
||||
/**
|
||||
* Used for book reader. Do not use for other components
|
||||
|
|
@ -89,9 +92,10 @@ const mobileBreakpointMarginOverride = 700;
|
|||
templateUrl: './reader-settings.component.html',
|
||||
styleUrls: ['./reader-settings.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
imports: [ReactiveFormsModule, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody, NgFor, NgbTooltip, NgTemplateOutlet, NgIf, NgClass, NgStyle, TitleCasePipe, TranslocoDirective]
|
||||
imports: [ReactiveFormsModule, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionButton, NgbAccordionCollapse, NgbAccordionBody, NgFor, NgbTooltip, NgTemplateOutlet, NgIf, NgClass, NgStyle, TitleCasePipe, TranslocoDirective]
|
||||
})
|
||||
export class ReaderSettingsComponent implements OnInit {
|
||||
@Input({required:true}) seriesId!: number;
|
||||
/**
|
||||
* Outputs when clickToPaginate is changed
|
||||
*/
|
||||
|
|
@ -125,6 +129,8 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
*/
|
||||
@Output() immersiveMode: EventEmitter<boolean> = new EventEmitter();
|
||||
|
||||
readingProfile: ReadingProfile | null = null;
|
||||
|
||||
user!: User;
|
||||
/**
|
||||
* List of all font families user can select from
|
||||
|
|
@ -170,7 +176,7 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
|
||||
constructor(private bookService: BookService, private accountService: AccountService,
|
||||
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService,
|
||||
private readonly cdRef: ChangeDetectorRef) {}
|
||||
private readonly cdRef: ChangeDetectorRef, private readingProfileService: ReadingProfileService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
|
||||
|
|
@ -178,124 +184,146 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
this.fontOptions = this.fontFamilies.map(f => f.title);
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.readingProfileService.getForSeries(this.seriesId).subscribe(profile => {
|
||||
this.readingProfile = profile;
|
||||
|
||||
if (this.readingProfile.bookReaderFontFamily === undefined) {
|
||||
this.readingProfile.bookReaderFontFamily = 'default';
|
||||
}
|
||||
if (this.readingProfile.bookReaderFontSize === undefined || this.readingProfile.bookReaderFontSize < 50) {
|
||||
this.readingProfile.bookReaderFontSize = 100;
|
||||
}
|
||||
if (this.readingProfile.bookReaderLineSpacing === undefined || this.readingProfile.bookReaderLineSpacing < 100) {
|
||||
this.readingProfile.bookReaderLineSpacing = 100;
|
||||
}
|
||||
if (this.readingProfile.bookReaderMargin === undefined) {
|
||||
this.readingProfile.bookReaderMargin = 0;
|
||||
}
|
||||
if (this.readingProfile.bookReaderReadingDirection === undefined) {
|
||||
this.readingProfile.bookReaderReadingDirection = ReadingDirection.LeftToRight;
|
||||
}
|
||||
if (this.readingProfile.bookReaderWritingStyle === undefined) {
|
||||
this.readingProfile.bookReaderWritingStyle = WritingStyle.Horizontal;
|
||||
}
|
||||
this.readingDirectionModel = this.readingProfile.bookReaderReadingDirection;
|
||||
this.writingStyleModel = this.readingProfile.bookReaderWritingStyle;
|
||||
|
||||
this.setupSettings();
|
||||
|
||||
this.setTheme(this.readingProfile.bookReaderThemeName || this.themeService.defaultBookTheme);
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
// Emit first time so book reader gets the setting
|
||||
this.readingDirection.emit(this.readingDirectionModel);
|
||||
this.bookReaderWritingStyle.emit(this.writingStyleModel);
|
||||
this.clickToPaginateChanged.emit(this.readingProfile.bookReaderTapToPaginate);
|
||||
this.layoutModeUpdate.emit(this.readingProfile.bookReaderLayoutMode);
|
||||
this.immersiveMode.emit(this.readingProfile.bookReaderImmersiveMode);
|
||||
|
||||
this.resetSettings();
|
||||
})
|
||||
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.user = user;
|
||||
|
||||
if (this.user.preferences.bookReaderFontFamily === undefined) {
|
||||
this.user.preferences.bookReaderFontFamily = 'default';
|
||||
}
|
||||
if (this.user.preferences.bookReaderFontSize === undefined || this.user.preferences.bookReaderFontSize < 50) {
|
||||
this.user.preferences.bookReaderFontSize = 100;
|
||||
}
|
||||
if (this.user.preferences.bookReaderLineSpacing === undefined || this.user.preferences.bookReaderLineSpacing < 100) {
|
||||
this.user.preferences.bookReaderLineSpacing = 100;
|
||||
}
|
||||
if (this.user.preferences.bookReaderMargin === undefined) {
|
||||
this.user.preferences.bookReaderMargin = 0;
|
||||
}
|
||||
if (this.user.preferences.bookReaderReadingDirection === undefined) {
|
||||
this.user.preferences.bookReaderReadingDirection = ReadingDirection.LeftToRight;
|
||||
}
|
||||
if (this.user.preferences.bookReaderWritingStyle === undefined) {
|
||||
this.user.preferences.bookReaderWritingStyle = WritingStyle.Horizontal;
|
||||
}
|
||||
this.readingDirectionModel = this.user.preferences.bookReaderReadingDirection;
|
||||
this.writingStyleModel = this.user.preferences.bookReaderWritingStyle;
|
||||
|
||||
|
||||
|
||||
this.settingsForm.addControl('bookReaderFontFamily', new FormControl(this.user.preferences.bookReaderFontFamily, []));
|
||||
this.settingsForm.get('bookReaderFontFamily')!.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(fontName => {
|
||||
const familyName = this.fontFamilies.filter(f => f.title === fontName)[0].family;
|
||||
if (familyName === 'default') {
|
||||
this.pageStyles['font-family'] = 'inherit';
|
||||
} else {
|
||||
this.pageStyles['font-family'] = "'" + familyName + "'";
|
||||
}
|
||||
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderFontSize', new FormControl(this.user.preferences.bookReaderFontSize, []));
|
||||
this.settingsForm.get('bookReaderFontSize')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.pageStyles['font-size'] = value + '%';
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderTapToPaginate', new FormControl(this.user.preferences.bookReaderTapToPaginate, []));
|
||||
this.settingsForm.get('bookReaderTapToPaginate')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.clickToPaginateChanged.emit(value);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderLineSpacing', new FormControl(this.user.preferences.bookReaderLineSpacing, []));
|
||||
this.settingsForm.get('bookReaderLineSpacing')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.pageStyles['line-height'] = value + '%';
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderMargin', new FormControl(this.user.preferences.bookReaderMargin, []));
|
||||
this.settingsForm.get('bookReaderMargin')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.pageStyles['margin-left'] = value + 'vw';
|
||||
this.pageStyles['margin-right'] = value + 'vw';
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
|
||||
|
||||
this.settingsForm.addControl('layoutMode', new FormControl(this.user.preferences.bookReaderLayoutMode || BookPageLayoutMode.Default, []));
|
||||
this.settingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((layoutMode: BookPageLayoutMode) => {
|
||||
this.layoutModeUpdate.emit(layoutMode);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderImmersiveMode', new FormControl(this.user.preferences.bookReaderImmersiveMode, []));
|
||||
this.settingsForm.get('bookReaderImmersiveMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((immersiveMode: boolean) => {
|
||||
if (immersiveMode) {
|
||||
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(true);
|
||||
}
|
||||
this.immersiveMode.emit(immersiveMode);
|
||||
});
|
||||
|
||||
|
||||
this.setTheme(this.user.preferences.bookReaderThemeName || this.themeService.defaultBookTheme);
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
// Emit first time so book reader gets the setting
|
||||
this.readingDirection.emit(this.readingDirectionModel);
|
||||
this.bookReaderWritingStyle.emit(this.writingStyleModel);
|
||||
this.clickToPaginateChanged.emit(this.user.preferences.bookReaderTapToPaginate);
|
||||
this.layoutModeUpdate.emit(this.user.preferences.bookReaderLayoutMode);
|
||||
this.immersiveMode.emit(this.user.preferences.bookReaderImmersiveMode);
|
||||
|
||||
this.resetSettings();
|
||||
} else {
|
||||
this.resetSettings();
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
setupSettings() {
|
||||
if (!this.readingProfile) return;
|
||||
|
||||
this.settingsForm.addControl('bookReaderFontFamily', new FormControl(this.readingProfile.bookReaderFontFamily, []));
|
||||
this.settingsForm.get('bookReaderFontFamily')!.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(fontName => {
|
||||
const familyName = this.fontFamilies.filter(f => f.title === fontName)[0].family;
|
||||
if (familyName === 'default') {
|
||||
this.pageStyles['font-family'] = 'inherit';
|
||||
} else {
|
||||
this.pageStyles['font-family'] = "'" + familyName + "'";
|
||||
}
|
||||
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderFontSize', new FormControl(this.readingProfile.bookReaderFontSize, []));
|
||||
this.settingsForm.get('bookReaderFontSize')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.pageStyles['font-size'] = value + '%';
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderTapToPaginate', new FormControl(this.readingProfile.bookReaderTapToPaginate, []));
|
||||
this.settingsForm.get('bookReaderTapToPaginate')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.clickToPaginateChanged.emit(value);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderLineSpacing', new FormControl(this.readingProfile.bookReaderLineSpacing, []));
|
||||
this.settingsForm.get('bookReaderLineSpacing')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.pageStyles['line-height'] = value + '%';
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderMargin', new FormControl(this.readingProfile.bookReaderMargin, []));
|
||||
this.settingsForm.get('bookReaderMargin')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(value => {
|
||||
this.pageStyles['margin-left'] = value + 'vw';
|
||||
this.pageStyles['margin-right'] = value + 'vw';
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('layoutMode', new FormControl(this.readingProfile.bookReaderLayoutMode || BookPageLayoutMode.Default, []));
|
||||
this.settingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((layoutMode: BookPageLayoutMode) => {
|
||||
this.layoutModeUpdate.emit(layoutMode);
|
||||
});
|
||||
|
||||
this.settingsForm.addControl('bookReaderImmersiveMode', new FormControl(this.readingProfile.bookReaderImmersiveMode, []));
|
||||
this.settingsForm.get('bookReaderImmersiveMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe((immersiveMode: boolean) => {
|
||||
if (immersiveMode) {
|
||||
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(true);
|
||||
}
|
||||
this.immersiveMode.emit(immersiveMode);
|
||||
});
|
||||
|
||||
// Update implicit reading profile while changing settings
|
||||
this.settingsForm.valueChanges.pipe(
|
||||
debounceTime(300),
|
||||
distinctUntilChanged(),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
tap(_ => this.updateImplicit())
|
||||
).subscribe();
|
||||
}
|
||||
|
||||
resetSettings() {
|
||||
if (!this.readingProfile) return;
|
||||
|
||||
if (this.user) {
|
||||
this.setPageStyles(this.user.preferences.bookReaderFontFamily, this.user.preferences.bookReaderFontSize + '%', this.user.preferences.bookReaderMargin + 'vw', this.user.preferences.bookReaderLineSpacing + '%');
|
||||
this.setPageStyles(this.readingProfile.bookReaderFontFamily, this.readingProfile.bookReaderFontSize + '%', this.readingProfile.bookReaderMargin + 'vw', this.readingProfile.bookReaderLineSpacing + '%');
|
||||
} else {
|
||||
this.setPageStyles();
|
||||
}
|
||||
|
||||
this.settingsForm.get('bookReaderFontFamily')?.setValue(this.user.preferences.bookReaderFontFamily);
|
||||
this.settingsForm.get('bookReaderFontSize')?.setValue(this.user.preferences.bookReaderFontSize);
|
||||
this.settingsForm.get('bookReaderLineSpacing')?.setValue(this.user.preferences.bookReaderLineSpacing);
|
||||
this.settingsForm.get('bookReaderMargin')?.setValue(this.user.preferences.bookReaderMargin);
|
||||
this.settingsForm.get('bookReaderReadingDirection')?.setValue(this.user.preferences.bookReaderReadingDirection);
|
||||
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(this.user.preferences.bookReaderTapToPaginate);
|
||||
this.settingsForm.get('bookReaderLayoutMode')?.setValue(this.user.preferences.bookReaderLayoutMode);
|
||||
this.settingsForm.get('bookReaderImmersiveMode')?.setValue(this.user.preferences.bookReaderImmersiveMode);
|
||||
this.settingsForm.get('bookReaderWritingStyle')?.setValue(this.user.preferences.bookReaderWritingStyle);
|
||||
this.settingsForm.get('bookReaderFontFamily')?.setValue(this.readingProfile.bookReaderFontFamily);
|
||||
this.settingsForm.get('bookReaderFontSize')?.setValue(this.readingProfile.bookReaderFontSize);
|
||||
this.settingsForm.get('bookReaderLineSpacing')?.setValue(this.readingProfile.bookReaderLineSpacing);
|
||||
this.settingsForm.get('bookReaderMargin')?.setValue(this.readingProfile.bookReaderMargin);
|
||||
this.settingsForm.get('bookReaderReadingDirection')?.setValue(this.readingProfile.bookReaderReadingDirection);
|
||||
this.settingsForm.get('bookReaderTapToPaginate')?.setValue(this.readingProfile.bookReaderTapToPaginate);
|
||||
this.settingsForm.get('bookReaderLayoutMode')?.setValue(this.readingProfile.bookReaderLayoutMode);
|
||||
this.settingsForm.get('bookReaderImmersiveMode')?.setValue(this.readingProfile.bookReaderImmersiveMode);
|
||||
this.settingsForm.get('bookReaderWritingStyle')?.setValue(this.readingProfile.bookReaderWritingStyle);
|
||||
|
||||
this.cdRef.detectChanges();
|
||||
this.styleUpdate.emit(this.pageStyles);
|
||||
}
|
||||
|
||||
updateImplicit() {
|
||||
this.readingProfileService.updateImplicit(this.packReadingProfile(), this.seriesId).subscribe({
|
||||
error: err => {
|
||||
console.error(err);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal method to be used by resetSettings. Pass items in with quantifiers
|
||||
*/
|
||||
|
|
@ -323,6 +351,7 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
this.activeTheme = theme;
|
||||
this.cdRef.markForCheck();
|
||||
this.colorThemeUpdate.emit(theme);
|
||||
this.updateImplicit();
|
||||
}
|
||||
|
||||
toggleReadingDirection() {
|
||||
|
|
@ -334,6 +363,7 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
|
||||
this.cdRef.markForCheck();
|
||||
this.readingDirection.emit(this.readingDirectionModel);
|
||||
this.updateImplicit();
|
||||
}
|
||||
|
||||
toggleWritingStyle() {
|
||||
|
|
@ -345,6 +375,7 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
|
||||
this.cdRef.markForCheck();
|
||||
this.bookReaderWritingStyle.emit(this.writingStyleModel);
|
||||
this.updateImplicit();
|
||||
}
|
||||
|
||||
toggleFullscreen() {
|
||||
|
|
@ -352,4 +383,29 @@ export class ReaderSettingsComponent implements OnInit {
|
|||
this.cdRef.markForCheck();
|
||||
this.fullscreen.emit();
|
||||
}
|
||||
|
||||
savePref() {
|
||||
this.readingProfileService.updateProfile(this.packReadingProfile(), this.seriesId).subscribe()
|
||||
}
|
||||
|
||||
private packReadingProfile(): ReadingProfile {
|
||||
const modelSettings = this.settingsForm.getRawValue();
|
||||
const data = {...this.readingProfile!};
|
||||
data.bookReaderFontFamily = modelSettings.bookReaderFontFamily;
|
||||
data.bookReaderFontSize = modelSettings.bookReaderFontSize
|
||||
data.bookReaderLineSpacing = modelSettings.bookReaderLineSpacing;
|
||||
data.bookReaderMargin = modelSettings.bookReaderMargin;
|
||||
data.bookReaderTapToPaginate = modelSettings.bookReaderTapToPaginate;
|
||||
data.bookReaderLayoutMode = modelSettings.layoutMode;
|
||||
data.bookReaderImmersiveMode = modelSettings.bookReaderImmersiveMode;
|
||||
|
||||
data.bookReaderReadingDirection = this.readingDirectionModel;
|
||||
data.bookReaderWritingStyle = this.writingStyleModel;
|
||||
if (this.activeTheme) {
|
||||
data.bookReaderThemeName = this.activeTheme.name;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,92 +53,93 @@
|
|||
<div class="reading-area"
|
||||
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
|
||||
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : '100dvh'}" #readingArea>
|
||||
|
||||
@if (readerMode !== ReaderMode.Webtoon) {
|
||||
<div appDblClick (dblclick)="bookmarkPage($event)" (singleClick)="toggleMenu()">
|
||||
<app-canvas-renderer
|
||||
[readerSettings$]="readerSettings$"
|
||||
[image$]="currentImage$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$">
|
||||
</app-canvas-renderer>
|
||||
</div>
|
||||
|
||||
<!-- Pagination controls and screen hints-->
|
||||
<div class="pagination-area">
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, KeyDirection.Left)"
|
||||
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? MaxHeight: '25%'), 'max-height': MaxHeight}">
|
||||
@if (showClickOverlay) {
|
||||
<div>
|
||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
||||
[title]="t('prev-page-tooltip')" aria-hidden="true"></i>
|
||||
</div>
|
||||
}
|
||||
@if (readingProfile !== null) {
|
||||
@if (readerMode !== ReaderMode.Webtoon) {
|
||||
<div appDblClick (dblclick)="bookmarkPage($event)" (singleClick)="toggleMenu()">
|
||||
<app-canvas-renderer
|
||||
[readerSettings$]="readerSettings$"
|
||||
[image$]="currentImage$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$">
|
||||
</app-canvas-renderer>
|
||||
</div>
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, KeyDirection.Right)"
|
||||
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? MaxHeight: '25%'),
|
||||
|
||||
<!-- Pagination controls and screen hints-->
|
||||
<div class="pagination-area">
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'left' : 'top'}} {{clickOverlayClass('left')}}" (click)="handlePageChange($event, KeyDirection.Left)"
|
||||
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? MaxHeight: '25%'), 'max-height': MaxHeight}">
|
||||
@if (showClickOverlay) {
|
||||
<div>
|
||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.RightToLeft ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'left' : 'up'}}"
|
||||
[title]="t('prev-page-tooltip')" aria-hidden="true"></i>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="{{readerMode === ReaderMode.LeftRight ? 'right' : 'bottom'}} {{clickOverlayClass('right')}}" (click)="handlePageChange($event, KeyDirection.Right)"
|
||||
[ngStyle]="{'height': (readerMode === ReaderMode.LeftRight ? MaxHeight: '25%'),
|
||||
'left': 'inherit',
|
||||
'right': RightPaginationOffset + 'px',
|
||||
'max-height': MaxHeight}">
|
||||
@if (showClickOverlay) {
|
||||
<div>
|
||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.LeftToRight ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'right' : 'down'}}"
|
||||
[title]="t('next-page-tooltip')" aria-hidden="true"></i>
|
||||
</div>
|
||||
}
|
||||
@if (showClickOverlay) {
|
||||
<div>
|
||||
<i class="fa fa-angle-{{readingDirection === ReadingDirection.LeftToRight ? 'double-' : ''}}{{readerMode === ReaderMode.LeftRight ? 'right' : 'down'}}"
|
||||
[title]="t('next-page-tooltip')" aria-hidden="true"></i>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div appDblClick (doubleClick)="bookmarkPage($event)" (singleClick)="toggleMenu()">
|
||||
<app-single-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[pageNum$]="pageNum$"
|
||||
[showClickOverlay$]="showClickOverlay$">
|
||||
</app-single-renderer>
|
||||
<div appDblClick (doubleClick)="bookmarkPage($event)" (singleClick)="toggleMenu()">
|
||||
<app-single-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[pageNum$]="pageNum$"
|
||||
[showClickOverlay$]="showClickOverlay$">
|
||||
</app-single-renderer>
|
||||
|
||||
<app-double-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$"
|
||||
[pageNum$]="pageNum$"
|
||||
[getPage]="getPageFn">
|
||||
</app-double-renderer>
|
||||
<app-double-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$"
|
||||
[pageNum$]="pageNum$"
|
||||
[getPage]="getPageFn">
|
||||
</app-double-renderer>
|
||||
|
||||
<app-double-reverse-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$"
|
||||
[pageNum$]="pageNum$"
|
||||
[getPage]="getPageFn">
|
||||
</app-double-reverse-renderer>
|
||||
<app-double-reverse-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$"
|
||||
[pageNum$]="pageNum$"
|
||||
[getPage]="getPageFn">
|
||||
</app-double-reverse-renderer>
|
||||
|
||||
<app-double-no-cover-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$"
|
||||
[pageNum$]="pageNum$"
|
||||
[getPage]="getPageFn">
|
||||
</app-double-no-cover-renderer>
|
||||
</div>
|
||||
} @else {
|
||||
@if (!isLoading && !inSetup) {
|
||||
<div class="webtoon-images" appDblClick (doubleClick)="bookmarkPage($event)" (singleClick)="toggleMenu()">
|
||||
<app-infinite-scroller [pageNum]="pageNum"
|
||||
[bufferPages]="5"
|
||||
[goToPage]="goToPageEvent"
|
||||
(pageNumberChange)="handleWebtoonPageChange($event)"
|
||||
[totalPages]="maxPages"
|
||||
[urlProvider]="getPageUrl"
|
||||
(loadNextChapter)="loadNextChapter()"
|
||||
(loadPrevChapter)="loadPrevChapter()"
|
||||
[bookmarkPage]="showBookmarkEffectEvent"
|
||||
[fullscreenToggled]="fullscreenEvent"
|
||||
[readerSettings$]="readerSettings$">
|
||||
</app-infinite-scroller>
|
||||
<app-double-no-cover-renderer [image$]="currentImage$"
|
||||
[readerSettings$]="readerSettings$"
|
||||
[bookmark$]="showBookmarkEffect$"
|
||||
[showClickOverlay$]="showClickOverlay$"
|
||||
[pageNum$]="pageNum$"
|
||||
[getPage]="getPageFn">
|
||||
</app-double-no-cover-renderer>
|
||||
</div>
|
||||
} @else {
|
||||
@if (!isLoading && !inSetup) {
|
||||
<div class="webtoon-images" appDblClick (doubleClick)="bookmarkPage($event)" (singleClick)="toggleMenu()">
|
||||
<app-infinite-scroller [pageNum]="pageNum"
|
||||
[bufferPages]="5"
|
||||
[goToPage]="goToPageEvent"
|
||||
(pageNumberChange)="handleWebtoonPageChange($event)"
|
||||
[totalPages]="maxPages"
|
||||
[urlProvider]="getPageUrl"
|
||||
(loadNextChapter)="loadNextChapter()"
|
||||
(loadPrevChapter)="loadPrevChapter()"
|
||||
[bookmarkPage]="showBookmarkEffectEvent"
|
||||
[fullscreenToggled]="fullscreenEvent"
|
||||
[readerSettings$]="readerSettings$">
|
||||
</app-infinite-scroller>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
||||
@if (menuOpen) {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ import {Breakpoint, KEY_CODES, UtilityService} from 'src/app/shared/_services/ut
|
|||
import {LibraryType} from 'src/app/_models/library/library';
|
||||
import {MangaFormat} from 'src/app/_models/manga-format';
|
||||
import {PageSplitOption} from 'src/app/_models/preferences/page-split-option';
|
||||
import {layoutModes, pageSplitOptions} from 'src/app/_models/preferences/preferences';
|
||||
import {ReaderMode} from 'src/app/_models/preferences/reader-mode';
|
||||
import {ReadingDirection} from 'src/app/_models/preferences/reading-direction';
|
||||
import {ScalingOption} from 'src/app/_models/preferences/scaling-option';
|
||||
|
|
@ -70,6 +69,8 @@ import {LoadingComponent} from '../../../shared/loading/loading.component';
|
|||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {shareReplay} from "rxjs/operators";
|
||||
import {DblClickDirective} from "../../../_directives/dbl-click.directive";
|
||||
import {layoutModes, pageSplitOptions, ReadingProfile} from "../../../_models/preferences/reading-profiles";
|
||||
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
||||
|
||||
|
||||
const PREFETCH_PAGES = 10;
|
||||
|
|
@ -150,6 +151,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
private readonly modalService = inject(NgbModal);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly readingProfileService = inject(ReadingProfileService);
|
||||
public readonly readerService = inject(ReaderService);
|
||||
public readonly utilityService = inject(UtilityService);
|
||||
public readonly mangaReaderService = inject(MangaReaderService);
|
||||
|
|
@ -194,6 +196,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
totalSeriesPages = 0;
|
||||
totalSeriesPagesRead = 0;
|
||||
user!: User;
|
||||
readingProfile: ReadingProfile | null = null;
|
||||
generalSettingsForm!: FormGroup;
|
||||
|
||||
readingDirection = ReadingDirection.LeftToRight;
|
||||
|
|
@ -496,34 +499,54 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
this.continuousChaptersStack.push(this.chapterId);
|
||||
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
forkJoin([
|
||||
this.accountService.currentUser$.pipe(take(1)),
|
||||
this.readingProfileService.getForSeries(this.seriesId)])
|
||||
.subscribe(([user, profile]) => {
|
||||
if (!user) {
|
||||
this.router.navigateByUrl('/login');
|
||||
return;
|
||||
}
|
||||
|
||||
this.readingProfile = profile;
|
||||
|
||||
this.user = user;
|
||||
this.hasBookmarkRights = this.accountService.hasBookmarkRole(user) || this.accountService.hasAdminRole(user);
|
||||
this.readingDirection = this.user.preferences.readingDirection;
|
||||
this.scalingOption = this.user.preferences.scalingOption;
|
||||
this.pageSplitOption = this.user.preferences.pageSplitOption;
|
||||
this.autoCloseMenu = this.user.preferences.autoCloseMenu;
|
||||
this.readerMode = this.user.preferences.readerMode;
|
||||
this.layoutMode = this.user.preferences.layoutMode || LayoutMode.Single;
|
||||
this.backgroundColor = this.user.preferences.backgroundColor || '#000000';
|
||||
this.readingDirection = this.readingProfile.readingDirection;
|
||||
this.scalingOption = this.readingProfile.scalingOption;
|
||||
this.pageSplitOption = this.readingProfile.pageSplitOption;
|
||||
this.autoCloseMenu = this.readingProfile.autoCloseMenu;
|
||||
this.readerMode = this.readingProfile.readerMode;
|
||||
this.layoutMode = this.readingProfile.layoutMode || LayoutMode.Single;
|
||||
this.backgroundColor = this.readingProfile.backgroundColor || '#000000';
|
||||
this.readerService.setOverrideStyles(this.backgroundColor);
|
||||
|
||||
this.generalSettingsForm = this.formBuilder.nonNullable.group({
|
||||
autoCloseMenu: new FormControl(this.autoCloseMenu),
|
||||
pageSplitOption: new FormControl(this.pageSplitOption),
|
||||
fittingOption: new FormControl(this.mangaReaderService.translateScalingOption(this.scalingOption)),
|
||||
widthSlider: new FormControl('none'),
|
||||
widthSlider: new FormControl(this.readingProfile.widthOverride ?? 'none'),
|
||||
layoutMode: new FormControl(this.layoutMode),
|
||||
darkness: new FormControl(100),
|
||||
emulateBook: new FormControl(this.user.preferences.emulateBook),
|
||||
swipeToPaginate: new FormControl(this.user.preferences.swipeToPaginate)
|
||||
emulateBook: new FormControl(this.readingProfile.emulateBook),
|
||||
swipeToPaginate: new FormControl(this.readingProfile.swipeToPaginate)
|
||||
});
|
||||
|
||||
// Update implicit reading profile while changing settings
|
||||
this.generalSettingsForm.valueChanges.pipe(
|
||||
debounceTime(300),
|
||||
distinctUntilChanged(),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
tap(_ => {
|
||||
this.readingProfileService.updateImplicit(this.packReadingProfile(), this.seriesId).subscribe({
|
||||
error: err => {
|
||||
console.error(err);
|
||||
}
|
||||
})
|
||||
})
|
||||
).subscribe();
|
||||
|
||||
|
||||
this.readerModeSubject.next(this.readerMode);
|
||||
this.pagingDirectionSubject.next(this.pagingDirection);
|
||||
|
||||
|
|
@ -562,7 +585,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.layoutMode = parseInt(val, 10);
|
||||
|
||||
if (this.layoutMode === LayoutMode.Single) {
|
||||
this.generalSettingsForm.get('pageSplitOption')?.setValue(this.user.preferences.pageSplitOption);
|
||||
this.generalSettingsForm.get('pageSplitOption')?.setValue(this.readingProfile!.pageSplitOption);
|
||||
this.generalSettingsForm.get('pageSplitOption')?.enable();
|
||||
this.generalSettingsForm.get('widthSlider')?.enable();
|
||||
this.generalSettingsForm.get('fittingOption')?.enable();
|
||||
|
|
@ -604,9 +627,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.toastr.info(translate('manga-reader.first-time-reading-manga'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
this.init();
|
||||
this.init();
|
||||
});
|
||||
}
|
||||
|
||||
ngAfterViewInit() {
|
||||
|
|
@ -780,7 +803,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
switchToWebtoonReaderIfPagesLikelyWebtoon() {
|
||||
if (this.readerMode === ReaderMode.Webtoon) return;
|
||||
if (!this.user.preferences.allowAutomaticWebtoonReaderDetection) return;
|
||||
if (!this.readingProfile!.allowAutomaticWebtoonReaderDetection) return;
|
||||
|
||||
if (this.mangaReaderService.shouldBeWebtoonMode()) {
|
||||
this.readerMode = ReaderMode.Webtoon;
|
||||
|
|
@ -1460,7 +1483,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
this.readingDirection = ReadingDirection.LeftToRight;
|
||||
}
|
||||
|
||||
if (this.menuOpen && this.user.preferences.showScreenHints) {
|
||||
if (this.menuOpen && this.readingProfile!.showScreenHints) {
|
||||
this.showClickOverlay = true;
|
||||
this.showClickOverlaySubject.next(true);
|
||||
setTimeout(() => {
|
||||
|
|
@ -1731,28 +1754,9 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
|
||||
// menu only code
|
||||
savePref() {
|
||||
const modelSettings = this.generalSettingsForm.getRawValue();
|
||||
// Get latest preferences from user, overwrite with what we manage in this UI, then save
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (!user) return;
|
||||
const data = {...user.preferences};
|
||||
data.layoutMode = parseInt(modelSettings.layoutMode, 10);
|
||||
data.readerMode = this.readerMode;
|
||||
data.autoCloseMenu = this.autoCloseMenu;
|
||||
data.readingDirection = this.readingDirection;
|
||||
data.emulateBook = modelSettings.emulateBook;
|
||||
data.swipeToPaginate = modelSettings.swipeToPaginate;
|
||||
data.pageSplitOption = parseInt(modelSettings.pageSplitOption, 10);
|
||||
data.locale = data.locale || 'en';
|
||||
|
||||
this.accountService.updatePreferences(data).subscribe(updatedPrefs => {
|
||||
this.toastr.success(translate('manga-reader.user-preferences-updated'));
|
||||
if (this.user) {
|
||||
this.user.preferences = updatedPrefs;
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
})
|
||||
});
|
||||
this.readingProfileService.updateProfile(this.packReadingProfile(), this.seriesId).subscribe(_ => {
|
||||
this.toastr.success(translate('manga-reader.user-preferences-updated'));
|
||||
})
|
||||
}
|
||||
|
||||
translatePrefOptions(o: {text: string, value: any}) {
|
||||
|
|
@ -1761,4 +1765,19 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
return d;
|
||||
}
|
||||
|
||||
private packReadingProfile(): ReadingProfile {
|
||||
const modelSettings = this.generalSettingsForm.getRawValue();
|
||||
const data = {...this.readingProfile!};
|
||||
data.layoutMode = parseInt(modelSettings.layoutMode, 10);
|
||||
data.readerMode = this.readerMode;
|
||||
data.autoCloseMenu = this.autoCloseMenu;
|
||||
data.readingDirection = this.readingDirection;
|
||||
data.emulateBook = modelSettings.emulateBook;
|
||||
data.swipeToPaginate = modelSettings.swipeToPaginate;
|
||||
data.pageSplitOption = parseInt(modelSettings.pageSplitOption, 10);
|
||||
// TODO: Check if this saves correctly!
|
||||
data.widthOverride = modelSettings.widthSlider === 'none' ? null : modelSettings.widthOverride;
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ import {
|
|||
import {ActivatedRoute, Router} from '@angular/router';
|
||||
import {NgxExtendedPdfViewerModule, PageViewModeType, ProgressBarEvent, ScrollModeType} from 'ngx-extended-pdf-viewer';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {take} from 'rxjs';
|
||||
import {forkJoin, take} from 'rxjs';
|
||||
import {BookService} from 'src/app/book-reader/_services/book.service';
|
||||
import {Breakpoint, KEY_CODES, UtilityService} from 'src/app/shared/_services/utility.service';
|
||||
import {Chapter} from 'src/app/_models/chapter';
|
||||
|
|
@ -34,6 +34,8 @@ import {PdfSpreadMode} from "../../../_models/preferences/pdf-spread-mode";
|
|||
import {SpreadType} from "ngx-extended-pdf-viewer/lib/options/spread-type";
|
||||
import {PdfScrollModeTypePipe} from "../../_pipe/pdf-scroll-mode.pipe";
|
||||
import {PdfSpreadTypePipe} from "../../_pipe/pdf-spread-mode.pipe";
|
||||
import {ReadingProfileService} from "../../../_services/reading-profile.service";
|
||||
import {ReadingProfile} from "../../../_models/preferences/reading-profiles";
|
||||
|
||||
@Component({
|
||||
selector: 'app-pdf-reader',
|
||||
|
|
@ -54,6 +56,7 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
|
|||
private readonly themeService = inject(ThemeService);
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
public readonly accountService = inject(AccountService);
|
||||
private readonly readingProfileService = inject(ReadingProfileService);
|
||||
public readonly readerService = inject(ReaderService);
|
||||
public readonly utilityService = inject(UtilityService);
|
||||
public readonly destroyRef = inject(DestroyRef);
|
||||
|
|
@ -69,6 +72,7 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
|
|||
chapterId!: number;
|
||||
chapter!: Chapter;
|
||||
user!: User;
|
||||
readingProfile!: ReadingProfile;
|
||||
|
||||
/**
|
||||
* Reading List id. Defaults to -1.
|
||||
|
|
@ -171,9 +175,13 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
|
|||
|
||||
this.cdRef.markForCheck();
|
||||
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
forkJoin([
|
||||
this.accountService.currentUser$.pipe(take(1)),
|
||||
this.readingProfileService.getForSeries(this.seriesId)
|
||||
]).subscribe(([user, profile]) => {
|
||||
if (user) {
|
||||
this.user = user;
|
||||
this.readingProfile = profile;
|
||||
this.init();
|
||||
}
|
||||
});
|
||||
|
|
@ -237,9 +245,9 @@ export class PdfReaderComponent implements OnInit, OnDestroy {
|
|||
init() {
|
||||
|
||||
this.pageLayoutMode = this.convertPdfLayoutMode(PdfLayoutMode.Multiple);
|
||||
this.scrollMode = this.convertPdfScrollMode(this.user.preferences.pdfScrollMode || PdfScrollMode.Vertical);
|
||||
this.spreadMode = this.convertPdfSpreadMode(this.user.preferences.pdfSpreadMode || PdfSpreadMode.None);
|
||||
this.theme = this.convertPdfTheme(this.user.preferences.pdfTheme || PdfTheme.Dark);
|
||||
this.scrollMode = this.convertPdfScrollMode(this.readingProfile.pdfScrollMode || PdfScrollMode.Vertical);
|
||||
this.spreadMode = this.convertPdfSpreadMode(this.readingProfile.pdfSpreadMode || PdfSpreadMode.None);
|
||||
this.theme = this.convertPdfTheme(this.readingProfile.pdfTheme || PdfTheme.Dark);
|
||||
this.backgroundColor = this.themeMap[this.theme].background;
|
||||
this.fontColor = this.themeMap[this.theme].font; // TODO: Move this to an observable or something
|
||||
|
||||
|
|
|
|||
|
|
@ -201,6 +201,7 @@
|
|||
</app-setting-item>
|
||||
</div>
|
||||
|
||||
<!--
|
||||
<div class="row g-0 mt-4 mb-4">
|
||||
<app-setting-item [title]="t('background-color-label')" [subtitle]="t('background-color-tooltip')">
|
||||
<ng-template #view>
|
||||
|
|
@ -217,6 +218,7 @@
|
|||
</ng-template>
|
||||
</app-setting-item>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<div class="row g-0 mt-4 mb-4">
|
||||
<app-setting-switch [title]="t('auto-close-menu-label')" [subtitle]="t('auto-close-menu-tooltip')">
|
||||
|
|
|
|||
|
|
@ -1,17 +1,7 @@
|
|||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {
|
||||
bookLayoutModes,
|
||||
bookWritingStyles,
|
||||
layoutModes,
|
||||
pageSplitOptions,
|
||||
pdfScrollModes,
|
||||
pdfSpreadModes,
|
||||
pdfThemes,
|
||||
Preferences,
|
||||
readingDirections,
|
||||
readingModes,
|
||||
scalingOptions
|
||||
Preferences
|
||||
} from "../../_models/preferences/preferences";
|
||||
import {AccountService} from "../../_services/account.service";
|
||||
import {BookService} from "../../book-reader/_services/book.service";
|
||||
|
|
@ -44,6 +34,13 @@ import {PdfThemePipe} from "../../_pipes/pdf-theme.pipe";
|
|||
import {PdfScrollModePipe} from "../../_pipes/pdf-scroll-mode.pipe";
|
||||
import {LicenseService} from "../../_services/license.service";
|
||||
import {ColorPickerDirective} from "ngx-color-picker";
|
||||
import {
|
||||
bookLayoutModes, bookWritingStyles,
|
||||
layoutModes, pageSplitOptions,
|
||||
pdfScrollModes,
|
||||
pdfSpreadModes,
|
||||
pdfThemes, readingDirections, readingModes, scalingOptions
|
||||
} from "../../_models/preferences/reading-profiles";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manga-user-preferences',
|
||||
|
|
@ -140,7 +137,7 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
this.user = results.user;
|
||||
this.user.preferences = results.pref;
|
||||
|
||||
if (this.fontFamilies.indexOf(this.user.preferences.bookReaderFontFamily) < 0) {
|
||||
/*if (this.fontFamilies.indexOf(this.user.preferences.bookReaderFontFamily) < 0) {
|
||||
this.user.preferences.bookReaderFontFamily = 'default';
|
||||
}
|
||||
|
||||
|
|
@ -169,7 +166,7 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
|
||||
this.settingsForm.addControl('pdfTheme', new FormControl(this.user?.preferences.pdfTheme || PdfTheme.Dark, []));
|
||||
this.settingsForm.addControl('pdfScrollMode', new FormControl(this.user?.preferences.pdfScrollMode || PdfScrollMode.Vertical, []));
|
||||
this.settingsForm.addControl('pdfSpreadMode', new FormControl(this.user?.preferences.pdfSpreadMode || PdfSpreadMode.None, []));
|
||||
this.settingsForm.addControl('pdfSpreadMode', new FormControl(this.user?.preferences.pdfSpreadMode || PdfSpreadMode.None, []));*/
|
||||
|
||||
this.settingsForm.addControl('theme', new FormControl(this.user.preferences.theme, []));
|
||||
this.settingsForm.addControl('globalPageLayoutMode', new FormControl(this.user.preferences.globalPageLayoutMode, []));
|
||||
|
|
@ -217,7 +214,7 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
reset() {
|
||||
if (!this.user) return;
|
||||
|
||||
this.settingsForm.get('readingDirection')?.setValue(this.user.preferences.readingDirection, {onlySelf: true, emitEvent: false});
|
||||
/*this.settingsForm.get('readingDirection')?.setValue(this.user.preferences.readingDirection, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('scalingOption')?.setValue(this.user.preferences.scalingOption, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('pageSplitOption')?.setValue(this.user.preferences.pageSplitOption, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('autoCloseMenu')?.setValue(this.user.preferences.autoCloseMenu, {onlySelf: true, emitEvent: false});
|
||||
|
|
@ -242,7 +239,7 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
|
||||
this.settingsForm.get('pdfTheme')?.setValue(this.user?.preferences.pdfTheme || PdfTheme.Dark, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('pdfScrollMode')?.setValue(this.user?.preferences.pdfScrollMode || PdfScrollMode.Vertical, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('pdfSpreadMode')?.setValue(this.user?.preferences.pdfSpreadMode || PdfSpreadMode.None, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('pdfSpreadMode')?.setValue(this.user?.preferences.pdfSpreadMode || PdfSpreadMode.None, {onlySelf: true, emitEvent: false});*/
|
||||
|
||||
this.settingsForm.get('theme')?.setValue(this.user.preferences.theme, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('globalPageLayoutMode')?.setValue(this.user.preferences.globalPageLayoutMode, {onlySelf: true, emitEvent: false});
|
||||
|
|
@ -260,7 +257,7 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
packSettings(): Preferences {
|
||||
const modelSettings = this.settingsForm.value;
|
||||
return {
|
||||
readingDirection: parseInt(modelSettings.readingDirection, 10),
|
||||
/*readingDirection: parseInt(modelSettings.readingDirection, 10),
|
||||
scalingOption: parseInt(modelSettings.scalingOption, 10),
|
||||
pageSplitOption: parseInt(modelSettings.pageSplitOption, 10),
|
||||
autoCloseMenu: modelSettings.autoCloseMenu,
|
||||
|
|
@ -277,21 +274,21 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
bookReaderReadingDirection: parseInt(modelSettings.bookReaderReadingDirection, 10),
|
||||
bookReaderWritingStyle: parseInt(modelSettings.bookReaderWritingStyle, 10),
|
||||
bookReaderLayoutMode: parseInt(modelSettings.bookReaderLayoutMode, 10),
|
||||
bookReaderThemeName: modelSettings.bookReaderThemeName,
|
||||
bookReaderThemeName: modelSettings.bookReaderThemeName,*/
|
||||
theme: modelSettings.theme,
|
||||
bookReaderImmersiveMode: modelSettings.bookReaderImmersiveMode,
|
||||
//bookReaderImmersiveMode: modelSettings.bookReaderImmersiveMode,
|
||||
globalPageLayoutMode: parseInt(modelSettings.globalPageLayoutMode, 10),
|
||||
blurUnreadSummaries: modelSettings.blurUnreadSummaries,
|
||||
promptForDownloadSize: modelSettings.promptForDownloadSize,
|
||||
noTransitions: modelSettings.noTransitions,
|
||||
emulateBook: modelSettings.emulateBook,
|
||||
swipeToPaginate: modelSettings.swipeToPaginate,
|
||||
//emulateBook: modelSettings.emulateBook,
|
||||
//swipeToPaginate: modelSettings.swipeToPaginate,
|
||||
collapseSeriesRelationships: modelSettings.collapseSeriesRelationships,
|
||||
shareReviews: modelSettings.shareReviews,
|
||||
locale: modelSettings.locale || 'en',
|
||||
pdfTheme: parseInt(modelSettings.pdfTheme, 10),
|
||||
pdfScrollMode: parseInt(modelSettings.pdfScrollMode, 10),
|
||||
pdfSpreadMode: parseInt(modelSettings.pdfSpreadMode, 10),
|
||||
//pdfTheme: parseInt(modelSettings.pdfTheme, 10),
|
||||
//pdfScrollMode: parseInt(modelSettings.pdfScrollMode, 10),
|
||||
//pdfSpreadMode: parseInt(modelSettings.pdfSpreadMode, 10),
|
||||
aniListScrobblingEnabled: modelSettings.aniListScrobblingEnabled,
|
||||
wantToReadSync: modelSettings.wantToReadSync
|
||||
};
|
||||
|
|
@ -301,7 +298,7 @@ export class ManageUserPreferencesComponent implements OnInit {
|
|||
this.settingsForm.markAsDirty();
|
||||
this.settingsForm.markAsTouched();
|
||||
if (this.user?.preferences) {
|
||||
this.user.preferences.backgroundColor = color;
|
||||
//this.user.preferences.backgroundColor = color;
|
||||
}
|
||||
|
||||
this.settingsForm.get('backgroundColor')?.setValue(color);
|
||||
|
|
|
|||
|
|
@ -1181,6 +1181,7 @@
|
|||
"line-spacing-label": "{{user-preferences.line-height-book-label}}",
|
||||
"margin-label": "{{user-preferences.margin-book-label}}",
|
||||
"reset-to-defaults": "Reset to Defaults",
|
||||
"save-global": "Save to your reading profile",
|
||||
"reader-settings-title": "Reader Settings",
|
||||
"reading-direction-label": "{{user-preferences.reading-direction-book-label}}",
|
||||
"right-to-left": "Right to Left",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue