Add UserBreakpoint API, disable width override after configured breakpoint

This commit is contained in:
Amelia 2025-06-11 01:16:01 +02:00
parent 43c4969d5c
commit b6e46e2f2d
No known key found for this signature in database
GPG key ID: D6D0ECE365407EAA
19 changed files with 3942 additions and 47 deletions

View file

@ -1,20 +1,20 @@
import {AsyncPipe, DOCUMENT, NgStyle} from '@angular/common';
import {AsyncPipe, DOCUMENT} from '@angular/common';
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
DestroyRef,
Component, computed,
DestroyRef, effect,
ElementRef,
EventEmitter,
inject,
Inject,
Inject, Injector,
Input,
OnChanges,
OnDestroy,
OnInit,
Output,
Renderer2,
Renderer2, Signal,
SimpleChanges,
ViewChild
} from '@angular/core';
@ -25,11 +25,13 @@ import {ReaderService} from '../../../_services/reader.service';
import {PAGING_DIRECTION} from '../../_models/reader-enums';
import {WebtoonImage} from '../../_models/webtoon-image';
import {MangaReaderService} from '../../_service/manga-reader.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {takeUntilDestroyed, toSignal} from "@angular/core/rxjs-interop";
import {TranslocoDirective} from "@jsverse/transloco";
import {InfiniteScrollModule} from "ngx-infinite-scroll";
import {ReaderSetting} from "../../_models/reader-setting";
import {SafeStylePipe} from "../../../_pipes/safe-style.pipe";
import {UtilityService} from "../../../shared/_services/utility.service";
import {ReadingProfile} from "../../../_models/preferences/reading-profiles";
/**
* How much additional space should pass, past the original bottom of the document height before we trigger the next chapter load
@ -63,7 +65,7 @@ const enum DEBUG_MODES {
templateUrl: './infinite-scroller.component.html',
styleUrls: ['./infinite-scroller.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [AsyncPipe, TranslocoDirective, InfiniteScrollModule, SafeStylePipe, NgStyle]
imports: [AsyncPipe, TranslocoDirective, InfiniteScrollModule, SafeStylePipe]
})
export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
@ -71,6 +73,8 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
private readonly readerService = inject(ReaderService);
private readonly renderer = inject(Renderer2);
private readonly scrollService = inject(ScrollService);
private readonly utilityService = inject(UtilityService);
private readonly injector = inject(Injector);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly destroyRef = inject(DestroyRef);
@ -91,6 +95,7 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
*/
@Input({required: true}) urlProvider!: (page: number) => string;
@Input({required: true}) readerSettings$!: Observable<ReaderSetting>;
@Input({required: true}) readingProfile!: ReadingProfile;
@Output() pageNumberChange: EventEmitter<number> = new EventEmitter<number>();
@Output() loadNextChapter: EventEmitter<void> = new EventEmitter<void>();
@Output() loadPrevChapter: EventEmitter<void> = new EventEmitter<void>();
@ -174,13 +179,8 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
*/
debugLogFilter: Array<string> = ['[PREFETCH]', '[Intersection]', '[Visibility]', '[Image Load]'];
/**
* Width override for manual width control
* 2 observables needed to avoid flickering, probably due to data races, when changing the width
* this allows to precisely define execution order
*/
widthOverride$ : Observable<string> = new Observable<string>();
widthSliderValue$ : Observable<string> = new Observable<string>();
readerSettings!: Signal<ReaderSetting>;
widthOverride!: Signal<string>;
get minPageLoaded() {
return Math.min(...Object.values(this.imagesLoaded));
@ -240,30 +240,39 @@ export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy,
takeUntilDestroyed(this.destroyRef)
);
// We need the injector as toSignal is only allowed in injection context
// https://angular.dev/guide/signals#injection-context
this.readerSettings = toSignal(this.readerSettings$, {injector: this.injector}) as Signal<ReaderSetting>;
this.widthSliderValue$ = this.readerSettings$.pipe(
map(values => (parseInt(values.widthSlider) <= 0) ? '' : values.widthSlider + '%'),
takeUntilDestroyed(this.destroyRef)
);
// Automatically updates when the breakpoint changes, or when reader settings changes
this.widthOverride = computed(() => {
//console.log("updating widthOverride")
const breakpoint = this.utilityService.activeUserBreakpoint();
const value = this.readerSettings().widthSlider;
this.widthOverride$ = this.widthSliderValue$;
if (breakpoint <= this.readingProfile.disableWidthOverride) {
return '';
}
return (parseInt(value) <= 0) ? '' : value + '%';
});
//perform jump so the page stays in view
this.widthSliderValue$.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => {
//perform jump so the page stays in view (NOT WORKING)
effect(() => {
//console.log("width changing!")
this.currentPageElem = this.document.querySelector('img#page-' + this.pageNum);
if(!this.currentPageElem)
return;
const width = this.widthOverride();
let images = Array.from(document.querySelectorAll('img[id^="page-"]')) as HTMLImageElement[];
images.forEach((img) => {
this.renderer.setStyle(img, "width", val);
this.renderer.setStyle(img, "width", width);
});
this.widthOverride$ = this.widthSliderValue$;
this.prevScrollPosition = this.currentPageElem.getBoundingClientRect().top;
this.currentPageElem.scrollIntoView();
this.cdRef.markForCheck();
});
}, {injector: this.injector});
if (this.goToPage) {
this.goToPage.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(page => {