Swipe Issues (#1745)
* Updated theme support to be able to customize the tile color dynamically from a theme via --tile-color. In addition, --theme-color will update apple-mobile-web-app-status-bar-style as well as the non-apple variants * Removed --manga-reader-bg-color as it wasn't used anywhere. Fixed double pagination on swipe. * Cleaned up some dead threshold code for swipe. * Started refactoring tests to use an abstract test class. Stopping because I should do on the .net 7 branch to avoid large merge conflicts. Tests need to be re-designed so they can run in parallel. * Fixed a bug in reading lists where when deleting an item, order could be miscalculated. * Started adding new information for stat service. Refactored time spent reading to be more accurate by taking average time against how much of the chapter the user has read. * Hooked up total time reading at server stat level. Don't show fancy graphs on mobile. * Added new stats for v0.7 * Added a test for Clearing want to read * Fixed a few tests that weren't resetting state between runs * Fixed some broken unit tests * Ensure all Series queries sort by a case invariant string. * Added more aggressive caching of images. This will result in a min delay on pages after a cover is changed. * Fixed a bug where if during new word count calculation, new word count is zero, restoring the old count wasn't working. * Cleaned up some of the code for getting time estimates * Fixed a bug where triggering swipe right wasn't working when there was no scroll * Delete the temp folder for creating a download after a full zip is created.
This commit is contained in:
parent
3d6de68089
commit
549e52b458
26 changed files with 488 additions and 339 deletions
|
@ -64,15 +64,23 @@ export class ThemeService implements OnDestroy {
|
|||
getColorScheme() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--color-scheme').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* --theme-color from theme. Updates the meta tag
|
||||
* @returns
|
||||
*/
|
||||
getThemeColor() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--theme-color').trim();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* --theme-color from theme. Updates the meta tag
|
||||
* @returns
|
||||
*/
|
||||
getThemeColor() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--theme-color').trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* --msapplication-TileColor from theme. Updates the meta tag
|
||||
* @returns
|
||||
*/
|
||||
getTileColor() {
|
||||
return getComputedStyle(this.document.body).getPropertyValue('--title-color').trim();
|
||||
}
|
||||
|
||||
getCssVariable(variable: string) {
|
||||
return getComputedStyle(this.document.body).getPropertyValue(variable).trim();
|
||||
}
|
||||
|
@ -155,6 +163,12 @@ export class ThemeService implements OnDestroy {
|
|||
const themeColor = this.getThemeColor();
|
||||
if (themeColor) {
|
||||
this.document.querySelector('meta[name="theme-color"]')?.setAttribute('content', themeColor);
|
||||
this.document.querySelector('meta[name="apple-mobile-web-app-status-bar-style"]')?.setAttribute('content', themeColor);
|
||||
}
|
||||
|
||||
const tileColor = this.getTileColor();
|
||||
if (themeColor) {
|
||||
this.document.querySelector('meta[name="msapplication-TileColor"]')?.setAttribute('content', themeColor);
|
||||
}
|
||||
|
||||
const colorScheme = this.getColorScheme();
|
||||
|
|
|
@ -973,11 +973,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
triggerSwipePagination(direction: KeyDirection) {
|
||||
|
||||
if (this.readingDirection === ReadingDirection.LeftToRight) {
|
||||
if (direction === KeyDirection.Right)
|
||||
this.readingDirection === ReadingDirection.LeftToRight ? this.nextPage() : this.prevPage();
|
||||
}
|
||||
switch(direction) {
|
||||
case KeyDirection.Down:
|
||||
this.nextPage();
|
||||
|
@ -996,8 +991,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
}
|
||||
|
||||
onSwipeEnd(event: SwipeEvent) {
|
||||
const threshold = .12;
|
||||
|
||||
// Positive number means swiping right/down, negative means left
|
||||
switch (this.readerMode) {
|
||||
case ReaderMode.Webtoon: break;
|
||||
|
@ -1014,6 +1007,10 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
// We just came from a swipe where pagination was required and we are now at the end of the swipe, so make the user do it once more
|
||||
if (direction === KeyDirection.Right) {
|
||||
this.hasHitZeroScroll = false;
|
||||
if (scrollLeft === 0 && this.ReadingAreaWidth === 0) {
|
||||
this.triggerSwipePagination(direction);
|
||||
return;
|
||||
}
|
||||
if (!this.hasHitRightScroll && this.checkIfPaginationAllowed(direction)) {
|
||||
this.hasHitRightScroll = true;
|
||||
return;
|
||||
|
@ -1036,7 +1033,6 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('Next page triggered');
|
||||
this.triggerSwipePagination(direction);
|
||||
break;
|
||||
}
|
||||
|
@ -1072,32 +1068,8 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
console.log('Next page triggered');
|
||||
this.triggerSwipePagination(direction);
|
||||
break;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const height = (this.readingArea?.nativeElement.scrollHeight === this.readingArea?.nativeElement.clientHeight)
|
||||
? this.readingArea?.nativeElement.clientHeight : this.ReadingAreaHeight;
|
||||
|
||||
if (direction === KeyDirection.Down && this.readingArea?.nativeElement?.scrollTop === height && this.prevScrollTop != 0) {
|
||||
this.prevScrollTop = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === KeyDirection.Up && this.readingArea?.nativeElement?.scrollTop === 0 && this.prevScrollTop != 0) {
|
||||
this.prevScrollTop = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
const thresholdMet = Math.abs(event.distance) >= height * threshold;
|
||||
if (!thresholdMet) return;
|
||||
|
||||
this.triggerSwipePagination(direction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { PageSplitOption } from 'src/app/_models/preferences/page-split-option';
|
|||
import { ScalingOption } from 'src/app/_models/preferences/scaling-option';
|
||||
import { ReaderService } from 'src/app/_services/reader.service';
|
||||
import { ChapterInfo } from '../_models/chapter-info';
|
||||
import { DimensionMap, FileDimension } from '../_models/file-dimension';
|
||||
import { DimensionMap } from '../_models/file-dimension';
|
||||
import { FITTING_OPTION } from '../_models/reader-enums';
|
||||
|
||||
@Injectable({
|
||||
|
|
|
@ -17,16 +17,7 @@
|
|||
</div>
|
||||
<div class="vr d-none d-lg-block m-2"></div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container>
|
||||
<div class="col-auto mb-2">
|
||||
<app-icon-and-title label="Total Chapters" [clickable]="false" fontClasses="fa-regular fa-file-lines" title="Total Chapters">
|
||||
{{stats.chapterCount | compactNumber}} Chapters
|
||||
</app-icon-and-title>
|
||||
</div>
|
||||
<div class="vr d-none d-lg-block m-2"></div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
<ng-container>
|
||||
<div class="col-auto mb-2">
|
||||
<app-icon-and-title label="Total Files" [clickable]="false" fontClasses="fa-regular fa-file" title="Total Files">
|
||||
|
@ -69,6 +60,15 @@
|
|||
{{stats.totalPeople | compactNumber}} People
|
||||
</app-icon-and-title>
|
||||
</div>
|
||||
<div class="vr d-none d-lg-block m-2"></div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container>
|
||||
<div class="col-auto mb-2">
|
||||
<app-icon-and-title label="Total Read Time" [clickable]="false" fontClasses="fas fa-eye" title="Total Read Time">
|
||||
{{stats.totalReadingTime | compactNumber}} Hours
|
||||
</app-icon-and-title>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
|
@ -91,28 +91,33 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 pt-2 pb-2 ">
|
||||
<app-top-readers></app-top-readers>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 pt-4 pb-2" style="height: 242px">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-file-breakdown-stats></app-file-breakdown-stats>
|
||||
<ng-container *ngIf="breakpoint$ | async as bp">
|
||||
<div class="row g-0 pt-2 pb-2" *ngIf="bp > Breakpoint.Mobile">
|
||||
<app-top-readers></app-top-readers>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-publication-status-stats></app-publication-status-stats>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-0 pt-4 pb-2 " style="height: 242px">
|
||||
<div class="col-md-12 col-sm-12 mt-4 pt-2">
|
||||
<app-read-by-day-and [isAdmin]="true"></app-read-by-day-and>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 pt-4 pb-2 " style="height: 242px">
|
||||
<div class="col-md-12 col-sm-12 mt-4 pt-2">
|
||||
<app-day-breakdown></app-day-breakdown>
|
||||
<div class="row g-0 pt-4 pb-2" style="height: 242px" *ngIf="bp > Breakpoint.Mobile">
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-file-breakdown-stats></app-file-breakdown-stats>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
<app-publication-status-stats></app-publication-status-stats>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 pt-4 pb-2 " style="height: 242px" *ngIf="bp > Breakpoint.Mobile">
|
||||
<div class="col-md-12 col-sm-12 mt-4 pt-2">
|
||||
<app-read-by-day-and [isAdmin]="true"></app-read-by-day-and>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0 pt-4 pb-2 " style="height: 242px" *ngIf="bp > Breakpoint.Mobile">
|
||||
<div class="col-md-12 col-sm-12 mt-4 pt-2">
|
||||
<app-day-breakdown></app-day-breakdown>
|
||||
</div>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { ChangeDetectionStrategy, Component, OnDestroy, OnInit } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, HostListener, OnDestroy, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { map, Observable, shareReplay, Subject, takeUntil, tap } from 'rxjs';
|
||||
import { BehaviorSubject, map, Observable, of, shareReplay, Subject, takeUntil, tap } from 'rxjs';
|
||||
import { FilterQueryParam } from 'src/app/shared/_services/filter-utilities.service';
|
||||
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import { MetadataService } from 'src/app/_services/metadata.service';
|
||||
|
@ -32,13 +33,27 @@ export class ServerStatsComponent implements OnInit, OnDestroy {
|
|||
this.router.navigate(['library', series.libraryId, 'series', series.id]);
|
||||
}
|
||||
|
||||
breakpointSubject = new BehaviorSubject<Breakpoint>(1);
|
||||
breakpoint$: Observable<Breakpoint> = this.breakpointSubject.asObservable();
|
||||
|
||||
@HostListener('window:resize', ['$event'])
|
||||
@HostListener('window:orientationchange', ['$event'])
|
||||
onResize() {
|
||||
this.breakpointSubject.next(this.utilityService.getActiveBreakpoint());
|
||||
}
|
||||
|
||||
|
||||
get Breakpoint() { return Breakpoint; }
|
||||
|
||||
constructor(private statService: StatisticsService, private router: Router, private imageService: ImageService,
|
||||
private metadataService: MetadataService, private modalService: NgbModal) {
|
||||
private metadataService: MetadataService, private modalService: NgbModal, private utilityService: UtilityService) {
|
||||
this.seriesImage = (data: PieDataItem) => {
|
||||
if (data.extra) return this.imageService.getSeriesCoverImage(data.extra.id);
|
||||
return '';
|
||||
}
|
||||
|
||||
this.breakpointSubject.next(this.utilityService.getActiveBreakpoint());
|
||||
|
||||
this.stats$ = this.statService.getServerStatistics().pipe(takeUntil(this.onDestroy), shareReplay());
|
||||
this.releaseYears$ = this.statService.getTopYears().pipe(takeUntil(this.onDestroy));
|
||||
this.mostActiveUsers$ = this.stats$.pipe(
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
<ng-container >
|
||||
<div class="col-auto mb-2">
|
||||
<app-icon-and-title label="Time Spent Reading" [clickable]="false" fontClasses="fas fa-eye" title="Time Spent Reading">
|
||||
{{timeSpentReading}} hours
|
||||
{{timeSpentReading | compactNumber}} hours
|
||||
</app-icon-and-title>
|
||||
</div>
|
||||
<div class="vr d-none d-lg-block m-2"></div>
|
||||
|
|
|
@ -12,6 +12,7 @@ export interface ServerStatistics {
|
|||
totalGenres: number;
|
||||
totalTags: number;
|
||||
totalPeople: number;
|
||||
totalReadingTime: number;
|
||||
mostActiveUsers: Array<StatCount<User>>;
|
||||
mostActiveLibraries: Array<StatCount<Library>>;
|
||||
mostReadSeries: Array<StatCount<Series>>;
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<meta name="msapplication-TileColor" content="#4ac694">
|
||||
<meta name="msapplication-config" content="assets/icons/browserconfig.xml">
|
||||
<meta name="theme-color" content="#000000">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="#000000">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
//
|
||||
:root, :root .default {
|
||||
--theme-color: #000000;
|
||||
--color-scheme: dark;
|
||||
/* Base colors */
|
||||
--primary-color: #4ac694;
|
||||
--primary-color-dark-shade: #3B9E76;
|
||||
--primary-color-darker-shade: #338A67;
|
||||
|
@ -11,6 +10,11 @@
|
|||
--body-text-color: #efefef;
|
||||
--btn-icon-filter: invert(1) grayscale(100%) brightness(200%);
|
||||
--primary-color-scrollbar: rgba(74,198,148,0.75);
|
||||
|
||||
/* Meta and Globals */
|
||||
--theme-color: #000000;
|
||||
--color-scheme: dark;
|
||||
--tile-color: var(--primary-color);
|
||||
|
||||
|
||||
/* Navbar */
|
||||
|
@ -116,7 +120,7 @@
|
|||
|
||||
|
||||
/* List items */
|
||||
--list-group-item-text-color: var(--body-text-color); /*rgba(74, 198, 148, 0.9)*/
|
||||
--list-group-item-text-color: var(--body-text-color);
|
||||
--list-group-item-bg-color: #343a40;
|
||||
--list-group-item-border-color: rgba(239, 239, 239, 0.125);
|
||||
--list-group-hover-text-color: white;
|
||||
|
@ -176,6 +180,7 @@
|
|||
--ratingstar-star-filled: var(--primary-color);
|
||||
|
||||
/* Global */
|
||||
//--hr-color: transparent;
|
||||
--hr-color: rgba(239, 239, 239, 0.125);
|
||||
--accent-bg-color: rgba(1, 4, 9, 0.5);
|
||||
--accent-text-color: lightgrey;
|
||||
|
@ -207,7 +212,6 @@
|
|||
--manga-reader-overlay-filter: blur(10px);
|
||||
--manga-reader-overlay-bg-color: rgba(0,0,0,0.5);
|
||||
--manga-reader-overlay-text-color: white;
|
||||
--manga-reader-bg-color: black; // TODO: Remove this
|
||||
--manga-reader-next-highlight-bg-color: rgba(65, 225, 100, 0.5);
|
||||
--manga-reader-prev-highlight-bg-color: rgba(65, 105, 225, 0.5);
|
||||
|
||||
|
@ -242,7 +246,4 @@
|
|||
|
||||
/* List Card Item */
|
||||
--card-list-item-bg-color: linear-gradient(180deg, rgba(0,0,0,0.15) 0%, rgba(0,0,0,0.15) 1%, rgba(0,0,0,0) 100%);
|
||||
|
||||
/* Bootstrap overrides */
|
||||
--hr-color: transparent;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue