Fixed a bug with double clicking manga reader on mobile no longer triggering bookmarking

This commit is contained in:
Joseph Milazzo 2024-12-08 11:09:43 -06:00
parent 2a43deea24
commit 0a7bc4b3f6
16 changed files with 250 additions and 203 deletions

View file

@ -0,0 +1,27 @@
import {Directive, EventEmitter, HostListener, Output} from '@angular/core';
@Directive({
selector: '[appDblClick]',
standalone: true
})
export class DblClickDirective {
@Output() doubleClick = new EventEmitter<Event>();
private lastTapTime = 0;
private tapTimeout = 300; // Time threshold for a double tap (in milliseconds)
@HostListener('click', ['$event'])
handleClick(event: Event): void {
event.stopPropagation();
event.preventDefault();
const currentTime = new Date().getTime();
if (currentTime - this.lastTapTime < this.tapTimeout) {
// Detected a double click/tap
this.doubleClick.emit(event);
}
this.lastTapTime = currentTime;
}
}

View file

@ -48,7 +48,7 @@ import {FilterUtilitiesService} from "../shared/_services/filter-utilities.servi
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {ReadingList} from "../_models/reading-list";
import {ReadingListService} from "../_services/reading-list.service";
import {RelatedTabComponent} from "../_single-modules/related-tab/related-tab.component";
import {RelatedTabComponent} from "../_single-module/related-tab/related-tab.component";
import {BadgeExpanderComponent} from "../shared/badge-expander/badge-expander.component";
import {
MetadataDetailRowComponent

View file

@ -3,47 +3,58 @@
@if(debugMode) {
<div class="fixed-top overlay">
@for(img of cachedImages; track img.src) {
<ng-container *ngIf="this.readerService.imageUrlToPageNum(img.src) as imageNum">
@if (this.readerService.imageUrlToPageNum(img.src); as imageNum) {
<span class="me-1" [ngClass]="{'current': imageNum === this.pageNum, 'loaded': img.complete}">{{this.readerService.imageUrlToPageNum(img.src)}}</span>
</ng-container>
}
}
</div>
}
<div class="fixed-top overlay" *ngIf="menuOpen" [@slideFromTop]="menuOpen">
<div style="display: flex; margin-top: 5px;">
<button class="btn btn-icon" style="height: 100%" [title]="t('back')" (click)="closeReader()">
<i class="fa fa-arrow-left" aria-hidden="true"></i>
<span class="visually-hidden">{{t('back')}}</span>
</button>
@if (menuOpen) {
<div class="fixed-top overlay" [@slideFromTop]="menuOpen">
<div style="display: flex; margin-top: 5px;">
<button class="btn btn-icon" style="height: 100%" [title]="t('back')" (click)="closeReader()">
<i class="fa fa-arrow-left" aria-hidden="true"></i>
<span class="visually-hidden">{{t('back')}}</span>
</button>
<div>
<div style="font-weight: bold;">{{title}} <span class="clickable" *ngIf="incognitoMode" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-alt')">(<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-title')}}</span>)</span></div>
<div class="subtitle">
{{subtitle}} <span *ngIf="totalSeriesPages > 0">{{t('series-progress', {percentage: (Math.min(1, ((totalSeriesPagesRead + pageNum) / totalSeriesPages)) | percent)}) }}</span>
<div>
<div style="font-weight: bold;">{{title}}
@if (incognitoMode) {
<span class="clickable" (click)="turnOffIncognito()" role="button" [attr.aria-label]="t('incognito-alt')">(<i class="fa fa-glasses" aria-hidden="true"></i><span class="visually-hidden">{{t('incognito-title')}}</span>)</span>
}
</div>
<div class="subtitle">
{{subtitle}}
@if (totalSeriesPages > 0) {
<span>{{t('series-progress', {percentage: (Math.min(1, ((totalSeriesPagesRead + pageNum) / totalSeriesPages)) | percent)}) }}</span>
}
</div>
</div>
<div style="margin-left: auto; padding-right: 3%;">
<button class="btn btn-icon" title="Shortcuts" (click)="openShortcutModal()">
<i class="fa-regular fa-rectangle-list" aria-hidden="true"></i>
<span class="visually-hidden">{{t('shortcuts-menu-alt')}}</span>
</button>
@if (!bookmarkMode && hasBookmarkRights) {
<button class="btn btn-icon" role="checkbox" [attr.aria-checked]="CurrentPageBookmarked"
title="{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}" (click)="bookmarkPage()">
<i class="{{CurrentPageBookmarked ? 'fa' : 'far'}} fa-bookmark" aria-hidden="true"></i>
<span class="visually-hidden">{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}</span>
</button>
}
</div>
</div>
<div style="margin-left: auto; padding-right: 3%;">
<button class="btn btn-icon" title="Shortcuts" (click)="openShortcutModal()">
<i class="fa-regular fa-rectangle-list" aria-hidden="true"></i>
<span class="visually-hidden">{{t('shortcuts-menu-alt')}}</span>
</button>
@if (!bookmarkMode && hasBookmarkRights) {
<button class="btn btn-icon" role="checkbox" [attr.aria-checked]="CurrentPageBookmarked"
title="{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}" (click)="bookmarkPage()">
<i class="{{CurrentPageBookmarked ? 'fa' : 'far'}} fa-bookmark" aria-hidden="true"></i>
<span class="visually-hidden">{{t(CurrentPageBookmarked ? 'unbookmark-page-tooltip' : 'bookmark-page-tooltip')}}</span>
</button>
}
</div>
</div>
</div>
}
<app-loading [loading]="isLoading || (!(currentImage$ | async)?.complete && this.readerMode !== ReaderMode.Webtoon)" [absolute]="true"></app-loading>
<div class="reading-area"
ngSwipe (swipeEnd)="onSwipeEnd($event)" (swipeMove)="onSwipeMove($event)"
[ngStyle]="{'background-color': backgroundColor, 'height': readerMode === ReaderMode.Webtoon ? 'inherit' : '100dvh'}" #readingArea>
<ng-container *ngIf="readerMode !== ReaderMode.Webtoon; else webtoon">
@if (readerMode !== ReaderMode.Webtoon) {
<div (dblclick)="bookmarkPage($event)">
<app-canvas-renderer
[readerSettings$]="readerSettings$"
@ -57,24 +68,28 @@
<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}">
<div *ngIf="showClickOverlay">
<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 (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}">
<div *ngIf="showClickOverlay">
<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 (dblclick)="bookmarkPage($event)">
<div appDblClick (doubleClick)="bookmarkPage($event)">
<app-single-renderer [image$]="currentImage$"
[readerSettings$]="readerSettings$"
[bookmark$]="showBookmarkEffect$"
@ -106,196 +121,200 @@
[getPage]="getPageFn">
</app-double-no-cover-renderer>
</div>
</ng-container>
<ng-template #webtoon>
<div class="webtoon-images" *ngIf="!isLoading && !inSetup">
<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>
</ng-template>
} @else {
@if (!isLoading && !inSetup) {
<div class="webtoon-images">
<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>
<div class="fixed-bottom overlay" *ngIf="menuOpen" [@slideFromBottom]="menuOpen">
<div class="mb-3" *ngIf="pageOptions !== undefined && pageOptions.ceil !== undefined">
<span class="visually-hidden" id="slider-info"></span>
<div class="row g-0">
<button class="btn btn-icon col-1" [disabled]="prevChapterDisabled" (click)="loadPrevChapter();resetMenuCloseTimer();" [title]="t('prev-chapter-tooltip')"><i class="fa fa-fast-backward" aria-hidden="true"></i></button>
<button class="btn btn-icon col-2" [disabled]="prevPageDisabled || pageNum === 0" (click)="goToPage(0);resetMenuCloseTimer();" [title]="t('first-page-tooltip')"><i class="fa fa-step-backward" aria-hidden="true"></i></button>
<div class="col custom-slider" *ngIf="pageOptions.ceil > 0; else noSlider">
<ngx-slider [options]="pageOptions" [value]="pageNum" aria-describedby="slider-info" [manualRefresh]="refreshSlider" (userChangeEnd)="sliderPageUpdate($event);startMenuCloseTimer()" (userChange)="sliderDragUpdate($event)" (userChangeStart)="cancelMenuCloseTimer();"></ngx-slider>
</div>
<ng-template #noSlider>
<div class="col custom-slider">
<ngx-slider [options]="pageOptions" [value]="pageNum" aria-describedby="slider-info" (userChangeEnd)="startMenuCloseTimer()" (userChangeStart)="cancelMenuCloseTimer();"></ngx-slider>
@if (menuOpen) {
<div class="fixed-bottom overlay" [@slideFromBottom]="menuOpen">
@if (pageOptions !== undefined && pageOptions.ceil !== undefined) {
<div class="mb-3">
<span class="visually-hidden" id="slider-info"></span>
<div class="row g-0">
<button class="btn btn-icon col-1" [disabled]="prevChapterDisabled" (click)="loadPrevChapter();resetMenuCloseTimer();" [title]="t('prev-chapter-tooltip')"><i class="fa fa-fast-backward" aria-hidden="true"></i></button>
<button class="btn btn-icon col-2" [disabled]="prevPageDisabled || pageNum === 0" (click)="goToPage(0);resetMenuCloseTimer();" [title]="t('first-page-tooltip')"><i class="fa fa-step-backward" aria-hidden="true"></i></button>
@if (pageOptions.ceil > 0) {
<div class="col custom-slider">
<ngx-slider [options]="pageOptions" [value]="pageNum" aria-describedby="slider-info" [manualRefresh]="refreshSlider" (userChangeEnd)="sliderPageUpdate($event);startMenuCloseTimer()" (userChange)="sliderDragUpdate($event)" (userChangeStart)="cancelMenuCloseTimer();"></ngx-slider>
</div>
} @else {
<div class="col custom-slider">
<ngx-slider [options]="pageOptions" [value]="pageNum" aria-describedby="slider-info" (userChangeEnd)="startMenuCloseTimer()" (userChangeStart)="cancelMenuCloseTimer();"></ngx-slider>
</div>
}
<button class="btn btn-icon col-2" [disabled]="nextPageDisabled || pageNum >= maxPages - 1" (click)="goToPage(this.maxPages);resetMenuCloseTimer();" [title]="t('last-page-tooltip')"><i class="fa fa-step-forward" aria-hidden="true"></i></button>
<button class="btn btn-icon col-1" [disabled]="nextChapterDisabled" (click)="loadNextChapter();resetMenuCloseTimer();" [title]="t('next-chapter-tooltip')"><i class="fa fa-fast-forward" aria-hidden="true"></i></button>
</div>
</ng-template>
<button class="btn btn-icon col-2" [disabled]="nextPageDisabled || pageNum >= maxPages - 1" (click)="goToPage(this.maxPages);resetMenuCloseTimer();" [title]="t('last-page-tooltip')"><i class="fa fa-step-forward" aria-hidden="true"></i></button>
<button class="btn btn-icon col-1" [disabled]="nextChapterDisabled" (click)="loadNextChapter();resetMenuCloseTimer();" [title]="t('next-chapter-tooltip')"><i class="fa fa-fast-forward" aria-hidden="true"></i></button>
</div>
}
<div class="row pt-4 ms-2 me-2 mb-2">
<div class="col">
<button class="btn btn-icon" (click)="setReadingDirection();resetMenuCloseTimer();" [disabled]="readerMode === ReaderMode.Webtoon || readerMode === ReaderMode.UpDown" aria-describedby="reading-direction" [title]="t('reading-direction-tooltip') + readingDirection === ReadingDirection.LeftToRight ? t('left-to-right-alt') : t('right-to-left-alt')">
<i class="fa fa-angle-double-{{readingDirection === ReadingDirection.LeftToRight ? 'right' : 'left'}}" aria-hidden="true"></i>
<span id="reading-direction" class="visually-hidden">{{readingDirection === ReadingDirection.LeftToRight ? t('left-to-right-alt') : t('right-to-left-alt')}}</span>
</button>
</div>
<div class="col">
<button class="btn btn-icon" [title]="t('reading-mode-tooltip')" (click)="toggleReaderMode();resetMenuCloseTimer();">
<i class="fa {{this.readerMode | readerModeIcon}}" aria-hidden="true"></i>
<span class="visually-hidden">{{t('reading-mode-tooltip')}}</span>
</button>
</div>
<div class="col">
<button class="btn btn-icon" title="{{this.isFullscreen ? t('collapse') : t('fullscreen')}}" (click)="toggleFullscreen();resetMenuCloseTimer();">
<i class="fa {{this.isFullscreen | fullscreenIcon}}" aria-hidden="true"></i>
<span class="visually-hidden">{{this.isFullscreen ? t('collapse') : t('fullscreen')}}</span>
</button>
</div>
<div class="col">
<button class="btn btn-icon" [title]="t('settings-tooltip')" (click)="settingsOpen = !settingsOpen;resetMenuCloseTimer();">
<i class="fa fa-sliders-h" aria-hidden="true"></i>
<span class="visually-hidden">{{t('settings-tooltip')}}</span>
</button>
</div>
</div>
</div>
<div class="row pt-4 ms-2 me-2 mb-2">
<div class="col">
<button class="btn btn-icon" (click)="setReadingDirection();resetMenuCloseTimer();" [disabled]="readerMode === ReaderMode.Webtoon || readerMode === ReaderMode.UpDown" aria-describedby="reading-direction" [title]="t('reading-direction-tooltip') + readingDirection === ReadingDirection.LeftToRight ? t('left-to-right-alt') : t('right-to-left-alt')">
<i class="fa fa-angle-double-{{readingDirection === ReadingDirection.LeftToRight ? 'right' : 'left'}}" aria-hidden="true"></i>
<span id="reading-direction" class="visually-hidden">{{readingDirection === ReadingDirection.LeftToRight ? t('left-to-right-alt') : t('right-to-left-alt')}}</span>
</button>
</div>
<div class="col">
<button class="btn btn-icon" [title]="t('reading-mode-tooltip')" (click)="toggleReaderMode();resetMenuCloseTimer();">
<i class="fa {{this.readerMode | readerModeIcon}}" aria-hidden="true"></i>
<span class="visually-hidden">{{t('reading-mode-tooltip')}}</span>
</button>
</div>
<div class="col">
<button class="btn btn-icon" title="{{this.isFullscreen ? t('collapse') : t('fullscreen')}}" (click)="toggleFullscreen();resetMenuCloseTimer();">
<i class="fa {{this.isFullscreen | fullscreenIcon}}" aria-hidden="true"></i>
<span class="visually-hidden">{{this.isFullscreen ? t('collapse') : t('fullscreen')}}</span>
</button>
</div>
<div class="col">
<button class="btn btn-icon" [title]="t('settings-tooltip')" (click)="settingsOpen = !settingsOpen;resetMenuCloseTimer();">
<i class="fa fa-sliders-h" aria-hidden="true"></i>
<span class="visually-hidden">{{t('settings-tooltip')}}</span>
</button>
</div>
</div>
<div class="bottom-menu" *ngIf="settingsOpen && generalSettingsForm">
<form [formGroup]="generalSettingsForm">
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="page-splitting" class="form-label">{{t('image-splitting-label')}}</label>&nbsp;
<div class="split fa fa-image">
<div class="{{SplitIconClass}}"></div>
@if (settingsOpen && generalSettingsForm) {
<div class="bottom-menu">
<form [formGroup]="generalSettingsForm">
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="page-splitting" class="form-label">{{t('image-splitting-label')}}</label>&nbsp;
<div class="split fa fa-image">
<div class="{{SplitIconClass}}"></div>
</div>
<select class="form-control" id="page-splitting" formControlName="pageSplitOption">
<option *ngFor="let opt of pageSplitOptionsTranslated" [value]="opt.value">{{opt.text}}</option>
</select>
</div>
<div class="col-md-6 col-sm-12">
<label for="page-fitting" class="form-label">{{t('image-scaling-label')}}</label>&nbsp;<i class="{{FittingOption | fittingIcon}}" aria-hidden="true"></i>
<select class="form-control" id="page-fitting" formControlName="fittingOption">
<option value="full-height">{{t('height')}}</option>
<option value="full-width">{{t('width')}}</option>
<option value="original">{{t('original')}}</option>
</select>
</div>
</div>
<select class="form-control" id="page-splitting" formControlName="pageSplitOption">
<option *ngFor="let opt of pageSplitOptionsTranslated" [value]="opt.value">{{opt.text}}</option>
</select>
</div>
<div class="col-md-6 col-sm-12">
<label for="page-fitting" class="form-label">{{t('image-scaling-label')}}</label>&nbsp;<i class="{{FittingOption | fittingIcon}}" aria-hidden="true"></i>
<select class="form-control" id="page-fitting" formControlName="fittingOption">
<option value="full-height">{{t('height')}}</option>
<option value="full-width">{{t('width')}}</option>
<option value="original">{{t('original')}}</option>
</select>
</div>
</div>
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="layout-mode" class="form-label">Layout Mode</label>&nbsp;
<ng-container [ngSwitch]="layoutMode">
<ng-container *ngSwitchCase="LayoutMode.Single">
<div class="split-double">
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="layout-mode" class="form-label">Layout Mode</label>&nbsp;
<ng-container [ngSwitch]="layoutMode">
<ng-container *ngSwitchCase="LayoutMode.Single">
<div class="split-double">
<span class="fa-stack fa-1x">
<i class="fa-regular fa-square-full fa-stack-2x"></i>
<i class="fa fa-image fa-stack-1x"></i>
</span>
</div>
</ng-container>
<ng-container *ngSwitchCase="LayoutMode.Double">
<div class="split-double">
</div>
</ng-container>
<ng-container *ngSwitchCase="LayoutMode.Double">
<div class="split-double">
<span class="fa-stack fa-1x">
<i class="fa-regular fa-square-full fa-stack-2x"></i>
<i class="fab fa-1 fa-stack-1x"></i>
</span>
<span class="fa-stack fa right">
<span class="fa-stack fa right">
<i class="fa-regular fa-square-full fa-stack-2x"></i>
<i class="fab fa-2 fa-stack-1x"></i>
</span>
</div>
</ng-container>
<ng-container *ngSwitchCase="LayoutMode.DoubleReversed">
<div class="split-double">
</div>
</ng-container>
<ng-container *ngSwitchCase="LayoutMode.DoubleReversed">
<div class="split-double">
<span class="fa-stack fa-1x">
<i class="fa-regular fa-square-full fa-stack-2x"></i>
<i class="fab fa-2 fa-stack-1x"></i>
</span>
<span class="fa-stack fa right">
<span class="fa-stack fa right">
<i class="fa-regular fa-square-full fa-stack-2x"></i>
<i class="fab fa-1 fa-stack-1x"></i>
</span>
</div>
</ng-container>
</ng-container>
<select class="form-control" id="layout-mode" formControlName="layoutMode">
<option [value]="opt.value" *ngFor="let opt of layoutModesTranslated">{{opt.text}}</option>
</select>
</div>
<div class="col-md-3 col-sm-12">
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="auto-close" formControlName="autoCloseMenu" class="form-check-input" >
<label class="form-check-label" for="auto-close">{{t('auto-close-menu-label')}}</label>
</div>
</div>
</ng-container>
</ng-container>
<select class="form-control" id="layout-mode" formControlName="layoutMode">
<option [value]="opt.value" *ngFor="let opt of layoutModesTranslated">{{opt.text}}</option>
</select>
</div>
</div>
<div class="col-md-3 col-sm-12">
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="auto-close" formControlName="autoCloseMenu" class="form-check-input" >
<label class="form-check-label" for="auto-close">{{t('auto-close-menu-label')}}</label>
</div>
</div>
</div>
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="swipe-to-paginate" formControlName="swipeToPaginate" class="form-check-input" >
<label class="form-check-label" for="swipe-to-paginate">{{t('swipe-enabled-label')}}</label>
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="swipe-to-paginate" formControlName="swipeToPaginate" class="form-check-input" >
<label class="form-check-label" for="swipe-to-paginate">{{t('swipe-enabled-label')}}</label>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-12">
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="emulate-book" formControlName="emulateBook" class="form-check-input">
<label class="form-check-label" for="emulate-book">{{t('emulate-comic-book-label')}}</label>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3 col-sm-12">
<div class="mb-3">
<div class="mb-3">
<div class="form-check form-switch">
<input type="checkbox" id="emulate-book" formControlName="emulateBook" class="form-check-input">
<label class="form-check-label" for="emulate-book">{{t('emulate-comic-book-label')}}</label>
</div>
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="darkness" class="form-label range-label">{{t('brightness-label')}}</label>
<span class="ms-1 range-text">{{generalSettingsForm.get('darkness')?.value + '%'}}</span>
<input type="range" class="form-range" id="darkness"
min="10" max="100" step="1" formControlName="darkness">
</div>
<div class="col-md-6 col-sm-12">
<label for="width-override-slider" class="form-label">{{t('width-override-label')}}:
@if (widthOverrideLabel$ | async; as widthOverrideLabel) {
{{ widthOverrideLabel ? widthOverrideLabel : t('off') }}
}
@else {
{{t('off')}}
}
</label>
<input id="width-override-slider" type="range" min="0" max="100" class="form-range" formControlName="widthSlider">
</div>
<div class="col-md-6 col-sm-12">
<button class="btn btn-primary" (click)="savePref()">{{t('save-globally')}}</button>
</div>
</div>
</div>
</form>
</div>
<div class="row mb-2">
<div class="col-md-6 col-sm-12">
<label for="darkness" class="form-label range-label">{{t('brightness-label')}}</label>
<span class="ms-1 range-text">{{generalSettingsForm.get('darkness')?.value + '%'}}</span>
<input type="range" class="form-range" id="darkness"
min="10" max="100" step="1" formControlName="darkness">
</div>
<div class="col-md-6 col-sm-12">
<label for="width-override-slider" class="form-label">{{t('width-override-label')}}:
@if (widthOverrideLabel$ | async; as widthOverrideLabel) {
{{ widthOverrideLabel ? widthOverrideLabel : t('off') }}
}
@else {
{{t('off')}}
}
</label>
<input id="width-override-slider" type="range" min="0" max="100" class="form-range" formControlName="widthSlider">
</div>
<div class="col-md-6 col-sm-12">
<button class="btn btn-primary" (click)="savePref()">{{t('save-globally')}}</button>
</div>
</div>
</form>
}
</div>
</div>
}
</div>
</ng-container>

View file

@ -13,7 +13,7 @@ import {
OnInit,
ViewChild
} from '@angular/core';
import {AsyncPipe, DOCUMENT, NgClass, NgFor, NgIf, NgStyle, NgSwitch, NgSwitchCase, PercentPipe} from '@angular/common';
import {AsyncPipe, DOCUMENT, NgClass, NgFor, NgStyle, NgSwitch, NgSwitchCase, PercentPipe} from '@angular/common';
import {ActivatedRoute, Router} from '@angular/router';
import {
BehaviorSubject,
@ -70,6 +70,7 @@ import {SwipeDirective} from '../../../ng-swipe/ng-swipe.directive';
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";
const PREFETCH_PAGES = 10;
@ -123,10 +124,10 @@ enum KeyDirection {
])
],
standalone: true,
imports: [NgStyle, NgIf, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
imports: [NgStyle, LoadingComponent, SwipeDirective, CanvasRendererComponent, SingleRendererComponent,
DoubleRendererComponent, DoubleReverseRendererComponent, DoubleNoCoverRendererComponent, InfiniteScrollerComponent,
NgxSliderModule, ReactiveFormsModule, NgFor, NgSwitch, NgSwitchCase, FittingIconPipe, ReaderModeIconPipe,
FullscreenIconPipe, TranslocoDirective, NgbProgressbar, PercentPipe, NgClass, AsyncPipe]
FullscreenIconPipe, TranslocoDirective, PercentPipe, NgClass, AsyncPipe, DblClickDirective]
})
export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
@ -1656,7 +1657,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
/**
* Bookmarks the current page for the chapter
*/
bookmarkPage(event: MouseEvent | undefined = undefined) {
bookmarkPage(event: Event | undefined = undefined) {
if (event) {
event.stopPropagation();
event.preventDefault();

View file

@ -1,5 +1,5 @@
import {ChangeDetectionStrategy, Component, inject, Input} from '@angular/core';
import {AgeRatingImageComponent} from "../../../_single-modules/age-rating-image/age-rating-image.component";
import {AgeRatingImageComponent} from "../../../_single-module/age-rating-image/age-rating-image.component";
import {CompactNumberPipe} from "../../../_pipes/compact-number.pipe";
import {ReadTimeLeftPipe} from "../../../_pipes/read-time-left.pipe";
import {ReadTimePipe} from "../../../_pipes/read-time.pipe";
@ -17,7 +17,7 @@ import {FilterComparison} from "../../../_models/metadata/v2/filter-comparison";
import {FilterField} from "../../../_models/metadata/v2/filter-field";
import {MangaFormat} from "../../../_models/manga-format";
import {SeriesFormatComponent} from "../../../shared/series-format/series-format.component";
import {PublisherFlipperComponent} from "../../../_single-modules/publisher-flipper/publisher-flipper.component";
import {PublisherFlipperComponent} from "../../../_single-module/publisher-flipper/publisher-flipper.component";
@Component({
selector: 'app-metadata-detail-row',

View file

@ -115,7 +115,7 @@ import {DownloadButtonComponent} from "../download-button/download-button.compon
import {hasAnyCast} from "../../../_models/common/i-has-cast";
import {EditVolumeModalComponent} from "../../../_single-module/edit-volume-modal/edit-volume-modal.component";
import {CoverUpdateEvent} from "../../../_models/events/cover-update-event";
import {RelatedSeriesPair, RelatedTabComponent} from "../../../_single-modules/related-tab/related-tab.component";
import {RelatedSeriesPair, RelatedTabComponent} from "../../../_single-module/related-tab/related-tab.component";
import {CollectionTagService} from "../../../_services/collection-tag.service";
import {UserCollection} from "../../../_models/collection-tag";
import {CoverImageComponent} from "../../../_single-module/cover-image/cover-image.component";

View file

@ -59,7 +59,7 @@ import {
} from "../_single-module/edit-volume-modal/edit-volume-modal.component";
import {Genre} from "../_models/metadata/genre";
import {Tag} from "../_models/tag";
import {RelatedTabComponent} from "../_single-modules/related-tab/related-tab.component";
import {RelatedTabComponent} from "../_single-module/related-tab/related-tab.component";
import {ReadingList} from "../_models/reading-list";
import {ReadingListService} from "../_services/reading-list.service";
import {BadgeExpanderComponent} from "../shared/badge-expander/badge-expander.component";