Epub reader fixes (#2471)

This commit is contained in:
Joe Milazzo 2023-12-03 14:28:56 -06:00 committed by GitHub
parent 0cb08de254
commit 6cbf071923
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 47 additions and 116 deletions

View File

@ -48,6 +48,10 @@ jobs:
- name: Checkout repository
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.
- name: Initialize CodeQL
uses: github/codeql-action/init@v2

View File

@ -1,5 +1,5 @@
<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'">
<div class="fixed-top" #stickyTop>
<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}"
[ngClass]="{'immersive' : immersiveMode || !actionBarVisible}" [@isLoading]="isLoading" (click)="handleReaderClick($event)">
<ng-container *ngIf="clickToPaginate">
<div class="left {{clickOverlayClass('left')}} no-pointer-events no-observe"
<ng-container *ngIf="clickToPaginate && !hidePagination">
<div class="left {{clickOverlayClass('left')}} no-observe"
(click)="movePage(readingDirection === ReadingDirection.LeftToRight ? PAGING_DIRECTION.BACKWARDS : PAGING_DIRECTION.FORWARD)"
[ngClass]="{'immersive' : immersiveMode}"
tabindex="-1"
[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}"
tabindex="-1"
[ngStyle]="{height: PageHeightForPagination}"></div>
@ -121,7 +123,7 @@
<div #readingHtml class="book-content {{ColumnLayout}} {{WritingStyleClass}}"
[ngStyle]="{'max-height': ColumnHeight, 'max-width': VerticalBookContentWidth, 'width': VerticalBookContentWidth, 'column-width': ColumnWidth}"
[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)"
(click)="$event.stopPropagation();"
[ngClass]="{'bottom-bar': layoutMode !== BookPageLayoutMode.Default}">

View File

@ -360,14 +360,11 @@ $pagination-opacity: 0;
border-color: transparent;
border: none !important;
outline: none;
cursor: pointer;
&.immersive {
top: 0px;
}
&.no-pointer-events {
pointer-events: none;
}
}
.left {
@ -382,14 +379,11 @@ $pagination-opacity: 0;
z-index: 3;
outline: none;
height: 100vw;
cursor: pointer;
&.immersive {
top: 0px;
}
&.no-pointer-events {
pointer-events: none;
}
}

View File

@ -17,7 +17,7 @@ import { DOCUMENT, Location, NgTemplateOutlet, NgIf, NgStyle, NgClass } from '@a
import { ActivatedRoute, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
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 { AccountService } from 'src/app/_services/account.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 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;
seriesId!: number;
volumeId!: number;
@ -315,18 +321,11 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
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
* browser behavior of selecting words or lines when double- or triple-clicking, respectively,
* triggered by repeated click pagination (when enabled).
* When the user is highlighting something, then we remove pagination
*/
lastPaginatorClickTime: number = 0;
hidePagination = false;
/**
* 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('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
*/
@ -530,11 +509,28 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
fromEvent<MouseEvent>(this.bookContainerElemRef.nativeElement, 'mousemove')
.pipe(
takeUntilDestroyed(this.destroyRef),
map(this.isCursorOverPaginationArea.bind(this)),
distinctUntilChanged())
.subscribe((isPointer) => {
this.changeCursor(isPointer);
});
distinctUntilChanged(),
tap((e) => {
const selection = window.getSelection();
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() {
@ -1482,7 +1478,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
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) {
setTimeout(() => this.updateLayoutMode(this.layoutMode), 10);
return;
@ -1609,50 +1605,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
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) {
if (!this.clickToPaginate) {
event.preventDefault();
@ -1691,23 +1643,6 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
mouseDown($event: MouseEvent) {
this.mousePosition.x = $event.clientX;
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() {
@ -1715,11 +1650,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
}
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
// the book overlay is just closing
setTimeout(() => {
this.isLineOverlayOpen = isOpen;
this.cdRef.markForCheck();
}, 10);
this.isLineOverlayOpen = isOpen;
this.cdRef.markForCheck();
}
}