mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Epub reader fixes (#2471)
This commit is contained in:
parent
0cb08de254
commit
6cbf071923
4
.github/workflows/codeql.yml
vendored
4
.github/workflows/codeql.yml
vendored
@ -48,6 +48,10 @@ jobs:
|
|||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Swashbuckle CLI
|
||||||
|
shell: powershell
|
||||||
|
run: dotnet tool install -g --version 6.5.0 Swashbuckle.AspNetCore.Cli
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v2
|
uses: github/codeql-action/init@v2
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{ColumnLayout}} {{WritingStyleClass}}"
|
<div class="container-flex {{darkMode ? 'dark-mode' : ''}} reader-container {{ColumnLayout}} {{WritingStyleClass}}"
|
||||||
tabindex="0" #reader (click)="handleContainerClick($event)" [ngClass]="{'clickable' : cursorIsPointer}">
|
tabindex="0" #reader>
|
||||||
<ng-container *transloco="let t; read: 'book-reader'">
|
<ng-container *transloco="let t; read: 'book-reader'">
|
||||||
<div class="fixed-top" #stickyTop>
|
<div class="fixed-top" #stickyTop>
|
||||||
<a class="visually-hidden-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">{{t('skip-header')}}</a>
|
<a class="visually-hidden-focusable focus-visible" href="javascript:void(0);" (click)="moveFocus()">{{t('skip-header')}}</a>
|
||||||
@ -104,12 +104,14 @@
|
|||||||
<div #readingSection class="reading-section {{ColumnLayout}} {{WritingStyleClass}}" [ngStyle]="{'width': PageWidthForPagination}"
|
<div #readingSection class="reading-section {{ColumnLayout}} {{WritingStyleClass}}" [ngStyle]="{'width': PageWidthForPagination}"
|
||||||
[ngClass]="{'immersive' : immersiveMode || !actionBarVisible}" [@isLoading]="isLoading" (click)="handleReaderClick($event)">
|
[ngClass]="{'immersive' : immersiveMode || !actionBarVisible}" [@isLoading]="isLoading" (click)="handleReaderClick($event)">
|
||||||
|
|
||||||
<ng-container *ngIf="clickToPaginate">
|
<ng-container *ngIf="clickToPaginate && !hidePagination">
|
||||||
<div class="left {{clickOverlayClass('left')}} no-pointer-events no-observe"
|
<div class="left {{clickOverlayClass('left')}} no-observe"
|
||||||
|
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
|
||||||
[ngClass]="{'immersive' : immersiveMode}"
|
[ngClass]="{'immersive' : immersiveMode}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
[ngStyle]="{height: PageHeightForPagination}"></div>
|
[ngStyle]="{height: PageHeightForPagination}"></div>
|
||||||
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-pointer-events no-observe"
|
<div class="{{scrollbarNeeded ? 'right-with-scrollbar' : 'right'}} {{clickOverlayClass('right')}} no-observe"
|
||||||
|
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)"
|
||||||
[ngClass]="{'immersive' : immersiveMode}"
|
[ngClass]="{'immersive' : immersiveMode}"
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
[ngStyle]="{height: PageHeightForPagination}"></div>
|
[ngStyle]="{height: PageHeightForPagination}"></div>
|
||||||
@ -121,7 +123,7 @@
|
|||||||
<div #readingHtml class="book-content {{ColumnLayout}} {{WritingStyleClass}}"
|
<div #readingHtml class="book-content {{ColumnLayout}} {{WritingStyleClass}}"
|
||||||
[ngStyle]="{'max-height': ColumnHeight, 'max-width': VerticalBookContentWidth, 'width': VerticalBookContentWidth, 'column-width': ColumnWidth}"
|
[ngStyle]="{'max-height': ColumnHeight, 'max-width': VerticalBookContentWidth, 'width': VerticalBookContentWidth, 'column-width': ColumnWidth}"
|
||||||
[ngClass]="{'immersive': immersiveMode && actionBarVisible}"
|
[ngClass]="{'immersive': immersiveMode && actionBarVisible}"
|
||||||
[innerHtml]="page" *ngIf="page !== undefined" (wheel)="onWheel($event)"></div>
|
[innerHtml]="page" *ngIf="page !== undefined" (click)="toggleMenu($event)" (mousedown)="mouseDown($event)" (wheel)="onWheel($event)"></div>
|
||||||
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode === BookPageLayoutMode.Default)"
|
<div *ngIf="page !== undefined && (scrollbarNeeded || layoutMode !== BookPageLayoutMode.Default) && !(writingStyle === WritingStyle.Vertical && layoutMode === BookPageLayoutMode.Default)"
|
||||||
(click)="$event.stopPropagation();"
|
(click)="$event.stopPropagation();"
|
||||||
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">
|
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">
|
||||||
|
@ -360,14 +360,11 @@ $pagination-opacity: 0;
|
|||||||
border-color: transparent;
|
border-color: transparent;
|
||||||
border: none !important;
|
border: none !important;
|
||||||
outline: none;
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&.immersive {
|
&.immersive {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.no-pointer-events {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
@ -382,14 +379,11 @@ $pagination-opacity: 0;
|
|||||||
z-index: 3;
|
z-index: 3;
|
||||||
outline: none;
|
outline: none;
|
||||||
height: 100vw;
|
height: 100vw;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
&.immersive {
|
&.immersive {
|
||||||
top: 0px;
|
top: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.no-pointer-events {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import { DOCUMENT, Location, NgTemplateOutlet, NgIf, NgStyle, NgClass } from '@a
|
|||||||
import { ActivatedRoute, Router } from '@angular/router';
|
import { ActivatedRoute, Router } from '@angular/router';
|
||||||
import { ToastrService } from 'ngx-toastr';
|
import { ToastrService } from 'ngx-toastr';
|
||||||
import { forkJoin, fromEvent, of } from 'rxjs';
|
import { forkJoin, fromEvent, of } from 'rxjs';
|
||||||
import { catchError, debounceTime, distinctUntilChanged, map, take } from 'rxjs/operators';
|
import {catchError, debounceTime, distinctUntilChanged, map, take, tap} from 'rxjs/operators';
|
||||||
import { Chapter } from 'src/app/_models/chapter';
|
import { Chapter } from 'src/app/_models/chapter';
|
||||||
import { AccountService } from 'src/app/_services/account.service';
|
import { AccountService } from 'src/app/_services/account.service';
|
||||||
import { NavService } from 'src/app/_services/nav.service';
|
import { NavService } from 'src/app/_services/nav.service';
|
||||||
@ -125,6 +125,12 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
private readonly themeService = inject(ThemeService);
|
private readonly themeService = inject(ThemeService);
|
||||||
private readonly cdRef = inject(ChangeDetectorRef);
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
|
protected readonly BookPageLayoutMode = BookPageLayoutMode;
|
||||||
|
protected readonly WritingStyle = WritingStyle;
|
||||||
|
protected readonly TabID = TabID;
|
||||||
|
protected readonly ReadingDirection = ReadingDirection;
|
||||||
|
protected readonly PAGING_DIRECTION = PAGING_DIRECTION;
|
||||||
|
|
||||||
libraryId!: number;
|
libraryId!: number;
|
||||||
seriesId!: number;
|
seriesId!: number;
|
||||||
volumeId!: number;
|
volumeId!: number;
|
||||||
@ -315,18 +321,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
writingStyle: WritingStyle = WritingStyle.Horizontal;
|
writingStyle: WritingStyle = WritingStyle.Horizontal;
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls whether the cursor is a pointer when tap to paginate is on and the cursor is over
|
|
||||||
* either of the pagination areas
|
|
||||||
*/
|
|
||||||
cursorIsPointer: boolean = false;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to keep track of the last time a paginator area was clicked to prevent the default
|
* When the user is highlighting something, then we remove pagination
|
||||||
* browser behavior of selecting words or lines when double- or triple-clicking, respectively,
|
|
||||||
* triggered by repeated click pagination (when enabled).
|
|
||||||
*/
|
*/
|
||||||
lastPaginatorClickTime: number = 0;
|
hidePagination = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to refresh the Personal PoC
|
* Used to refresh the Personal PoC
|
||||||
@ -344,26 +343,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
@ViewChild('stickyTop', {static: false}) stickyTopElemRef!: ElementRef<HTMLDivElement>;
|
@ViewChild('stickyTop', {static: false}) stickyTopElemRef!: ElementRef<HTMLDivElement>;
|
||||||
@ViewChild('reader', {static: false}) reader!: ElementRef;
|
@ViewChild('reader', {static: false}) reader!: ElementRef;
|
||||||
|
|
||||||
|
|
||||||
get BookPageLayoutMode() {
|
|
||||||
return BookPageLayoutMode;
|
|
||||||
}
|
|
||||||
|
|
||||||
get WritingStyle() {
|
|
||||||
return WritingStyle;
|
|
||||||
}
|
|
||||||
get TabID(): typeof TabID {
|
|
||||||
return TabID;
|
|
||||||
}
|
|
||||||
|
|
||||||
get ReadingDirection(): typeof ReadingDirection {
|
|
||||||
return ReadingDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
get PAGING_DIRECTION() {
|
|
||||||
return PAGING_DIRECTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disables the Left most button
|
* Disables the Left most button
|
||||||
*/
|
*/
|
||||||
@ -530,11 +509,28 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
fromEvent<MouseEvent>(this.bookContainerElemRef.nativeElement, 'mousemove')
|
fromEvent<MouseEvent>(this.bookContainerElemRef.nativeElement, 'mousemove')
|
||||||
.pipe(
|
.pipe(
|
||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
map(this.isCursorOverPaginationArea.bind(this)),
|
distinctUntilChanged(),
|
||||||
distinctUntilChanged())
|
tap((e) => {
|
||||||
.subscribe((isPointer) => {
|
const selection = window.getSelection();
|
||||||
this.changeCursor(isPointer);
|
this.hidePagination = selection !== null && selection.toString().trim() !== '';
|
||||||
});
|
console.log('hide pagination: ', this.hidePagination);
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
fromEvent<MouseEvent>(this.bookContainerElemRef.nativeElement, 'mouseup')
|
||||||
|
.pipe(
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
distinctUntilChanged(),
|
||||||
|
tap((e) => {
|
||||||
|
this.hidePagination = false;
|
||||||
|
console.log('hide pagination: ', this.hidePagination);
|
||||||
|
this.cdRef.markForCheck();
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
handleScrollEvent() {
|
handleScrollEvent() {
|
||||||
@ -1482,7 +1478,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
|
|
||||||
this.updateSingleImagePageStyles()
|
this.updateSingleImagePageStyles()
|
||||||
|
|
||||||
// Calulate if bottom actionbar is needed. On a timeout to get accurate heights
|
// Calculate if bottom actionbar is needed. On a timeout to get accurate heights
|
||||||
if (this.bookContentElemRef == null) {
|
if (this.bookContentElemRef == null) {
|
||||||
setTimeout(() => this.updateLayoutMode(this.layoutMode), 10);
|
setTimeout(() => this.updateLayoutMode(this.layoutMode), 10);
|
||||||
return;
|
return;
|
||||||
@ -1609,50 +1605,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
return side === 'right' ? 'highlight-2' : 'highlight';
|
return side === 'right' ? 'highlight-2' : 'highlight';
|
||||||
}
|
}
|
||||||
|
|
||||||
private isCursorOverLeftPaginationArea(event: MouseEvent): boolean {
|
|
||||||
const leftPaginationAreaEnd = window.innerWidth * 0.2;
|
|
||||||
//console.log('user clicked on ', event.clientX, ' and left pagination ends on ', leftPaginationAreaEnd);
|
|
||||||
return event.clientX <= leftPaginationAreaEnd;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isCursorOverRightPaginationArea(event: MouseEvent): boolean {
|
|
||||||
const rightPaginationAreaStart = window.innerWidth * 0.8;
|
|
||||||
//console.log('user clicked on ', event.clientX, ' and right pagination starts at ', rightPaginationAreaStart);
|
|
||||||
return event.clientX >= rightPaginationAreaStart;
|
|
||||||
}
|
|
||||||
|
|
||||||
private isCursorOverPaginationArea(event: MouseEvent): boolean {
|
|
||||||
if (this.isLoading || !this.clickToPaginate) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.isCursorOverLeftPaginationArea(event) || this.isCursorOverRightPaginationArea(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private changeCursor(isPointer: boolean) {
|
|
||||||
this.cursorIsPointer = isPointer;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
}
|
|
||||||
|
|
||||||
handleContainerClick(event: MouseEvent) {
|
|
||||||
|
|
||||||
if (this.drawerOpen || this.isLineOverlayOpen || ['action-bar', 'offcanvas-backdrop'].some(className => (event.target as Element).classList.contains(className))) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.clickToPaginate) {
|
|
||||||
if (this.isCursorOverLeftPaginationArea(event)) {
|
|
||||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD);
|
|
||||||
return;
|
|
||||||
} else if (this.isCursorOverRightPaginationArea(event)) {
|
|
||||||
this.movePage(this.readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.FORWARD : PAGING_DIRECTION.BACKWARDS)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.toggleMenu(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
handleReaderClick(event: MouseEvent) {
|
handleReaderClick(event: MouseEvent) {
|
||||||
if (!this.clickToPaginate) {
|
if (!this.clickToPaginate) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -1691,23 +1643,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
mouseDown($event: MouseEvent) {
|
mouseDown($event: MouseEvent) {
|
||||||
this.mousePosition.x = $event.clientX;
|
this.mousePosition.x = $event.clientX;
|
||||||
this.mousePosition.y = $event.clientY;
|
this.mousePosition.y = $event.clientY;
|
||||||
|
|
||||||
if (!this.clickToPaginate || !this.isCursorOverPaginationArea($event)) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// This value is completely arbitrary and should cover most
|
|
||||||
// double-click speeds
|
|
||||||
const halfASecond = 500;
|
|
||||||
const now = Date.now();
|
|
||||||
|
|
||||||
// This is to prevent selecting text when clicking repeatedly to switch pages,
|
|
||||||
// while also allowing selections to begin in a pagination area
|
|
||||||
if (now - this.lastPaginatorClickTime < halfASecond) {
|
|
||||||
$event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.lastPaginatorClickTime = now;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshPersonalToC() {
|
refreshPersonalToC() {
|
||||||
@ -1715,11 +1650,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateLineOverlayOpen(isOpen: boolean) {
|
updateLineOverlayOpen(isOpen: boolean) {
|
||||||
// HACK: This hack allows the boolean to be changed to false so that the pagination doesn't trigger and move us to the next page when
|
this.isLineOverlayOpen = isOpen;
|
||||||
// the book overlay is just closing
|
this.cdRef.markForCheck();
|
||||||
setTimeout(() => {
|
|
||||||
this.isLineOverlayOpen = isOpen;
|
|
||||||
this.cdRef.markForCheck();
|
|
||||||
}, 10);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user