mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-31 04:04:19 -04:00
Width Override Fixes + Swagger (again) (#3034)
This commit is contained in:
parent
2326c9f437
commit
8cc1edc38d
@ -489,8 +489,6 @@ public class ScannerService : IScannerService
|
|||||||
// We don't need to send SignalR event as this is a background job that user doesn't need insight into
|
// We don't need to send SignalR event as this is a background job that user doesn't need insight into
|
||||||
_logger.LogInformation("[ScannerService] Scan library invoked via nightly scan job but a task is already running for {LibraryName}. Rescheduling for 4 hours", lib.Name);
|
_logger.LogInformation("[ScannerService] Scan library invoked via nightly scan job but a task is already running for {LibraryName}. Rescheduling for 4 hours", lib.Name);
|
||||||
await Task.Delay(TimeSpan.FromHours(4));
|
await Task.Delay(TimeSpan.FromHours(4));
|
||||||
//BackgroundJob.Schedule(() => ScanLibraries(forceUpdate), TimeSpan.FromHours(4));
|
|
||||||
//return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await ScanLibrary(lib.Id, forceUpdate, true);
|
await ScanLibrary(lib.Id, forceUpdate, true);
|
||||||
|
@ -137,7 +137,7 @@ public class Startup
|
|||||||
{
|
{
|
||||||
c.SwaggerDoc("v1", new OpenApiInfo
|
c.SwaggerDoc("v1", new OpenApiInfo
|
||||||
{
|
{
|
||||||
Version = "2.0",
|
Version = "3.1.0",
|
||||||
Title = "Kavita",
|
Title = "Kavita",
|
||||||
Description = $"Kavita provides a set of APIs that are authenticated by JWT. JWT token can be copied from local storage. Assume all fields of a payload are required. Built against v{BuildInfo.Version.ToString()}",
|
Description = $"Kavita provides a set of APIs that are authenticated by JWT. JWT token can be copied from local storage. Assume all fields of a payload are required. Built against v{BuildInfo.Version.ToString()}",
|
||||||
License = new OpenApiLicense
|
License = new OpenApiLicense
|
||||||
@ -176,7 +176,7 @@ public class Startup
|
|||||||
Url = "{protocol}://{hostpath}",
|
Url = "{protocol}://{hostpath}",
|
||||||
Variables = new Dictionary<string, OpenApiServerVariable>
|
Variables = new Dictionary<string, OpenApiServerVariable>
|
||||||
{
|
{
|
||||||
{ "protocol", new OpenApiServerVariable { Default = "http", Enum = new List<string> { "http", "https" } } },
|
{ "protocol", new OpenApiServerVariable { Default = "http", Enum = ["http", "https"]} },
|
||||||
{ "hostpath", new OpenApiServerVariable { Default = "localhost:5000" } }
|
{ "hostpath", new OpenApiServerVariable { Default = "localhost:5000" } }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -207,7 +207,7 @@ public class Startup
|
|||||||
.UseSimpleAssemblyNameTypeSerializer()
|
.UseSimpleAssemblyNameTypeSerializer()
|
||||||
.UseRecommendedSerializerSettings()
|
.UseRecommendedSerializerSettings()
|
||||||
.UseInMemoryStorage());
|
.UseInMemoryStorage());
|
||||||
//.UseSQLiteStorage("config/Hangfire.db")); // UseSQLiteStorage - SQLite has some issues around resuming jobs when aborted (and locking can cause high utilization)
|
//.UseSQLiteStorage("config/Hangfire.db")); // UseSQLiteStorage - SQLite has some issues around resuming jobs when aborted (and locking can cause high utilization) (NOTE: There is code to clear jobs on startup a redditor gave me)
|
||||||
|
|
||||||
// Add the processing server as IHostedService
|
// Add the processing server as IHostedService
|
||||||
services.AddHangfireServer(options =>
|
services.AddHangfireServer(options =>
|
||||||
|
@ -1,39 +1,49 @@
|
|||||||
<ng-container *transloco="let t; read: 'infinite-scroller'">
|
<ng-container *transloco="let t; read: 'infinite-scroller'">
|
||||||
|
|
||||||
<div class="fixed-top overlay" *ngIf="showDebugBar()">
|
@if (showDebugBar()) {
|
||||||
<strong>Captures Scroll Events:</strong> {{!this.isScrolling && this.allImagesLoaded}}
|
<div class="fixed-top overlay">
|
||||||
<strong>Is Scrolling:</strong> {{isScrollingForwards() ? 'Forwards' : 'Backwards'}} {{this.isScrolling}}
|
<strong>Captures Scroll Events:</strong> {{!this.isScrolling && this.allImagesLoaded}}
|
||||||
<strong>All Images Loaded:</strong> {{this.allImagesLoaded}}
|
<strong>Is Scrolling:</strong> {{isScrollingForwards() ? 'Forwards' : 'Backwards'}} {{this.isScrolling}}
|
||||||
<strong>Prefetched</strong> {{minPageLoaded}}-{{maxPageLoaded}}
|
<strong>All Images Loaded:</strong> {{this.allImagesLoaded}}
|
||||||
<strong>Pages:</strong> {{pageNum}} / {{totalPages - 1}}
|
<strong>Prefetched</strong> {{minPageLoaded}}-{{maxPageLoaded}}
|
||||||
<strong>At Top:</strong> {{atTop}}
|
<strong>Pages:</strong> {{pageNum}} / {{totalPages - 1}}
|
||||||
<strong>At Bottom:</strong> {{atBottom}}
|
<strong>At Top:</strong> {{atTop}}
|
||||||
<strong>Total Height:</strong> {{getTotalHeight()}}
|
<strong>At Bottom:</strong> {{atBottom}}
|
||||||
<strong>Total Scroll:</strong> {{getTotalScroll()}}
|
<strong>Total Height:</strong> {{getTotalHeight()}}
|
||||||
<strong>Scroll Top:</strong> {{getScrollTop()}}
|
<strong>Total Scroll:</strong> {{getTotalScroll()}}
|
||||||
</div>
|
<strong>Scroll Top:</strong> {{getScrollTop()}}
|
||||||
|
|
||||||
<div *ngIf="atTop" #topSpacer class="spacer top" role="alert" (click)="loadPrevChapter.emit()">
|
|
||||||
<div style="height: 200px"></div>
|
|
||||||
<div>
|
|
||||||
<button class="btn btn-icon mx-auto">
|
|
||||||
<i class="fa fa-angle-double-up animate" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<span class="mx-auto text">{{t('continuous-reading-prev-chapter')}}</span>
|
|
||||||
<button class="btn btn-icon mx-auto">
|
|
||||||
<i class="fa fa-angle-double-up animate" aria-hidden="true"></i>
|
|
||||||
</button>
|
|
||||||
<span class="visually-hidden">{{t('continuous-reading-prev-chapter-alt')}}</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
}
|
||||||
|
|
||||||
|
@if (atTop) {
|
||||||
|
<div #topSpacer class="spacer top" role="alert" (click)="loadPrevChapter.emit()">
|
||||||
|
<div class="empty-space"></div>
|
||||||
|
<div>
|
||||||
|
<button class="btn btn-icon mx-auto">
|
||||||
|
<i class="fa fa-angle-double-up animate" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<span class="mx-auto text">{{t('continuous-reading-prev-chapter')}}</span>
|
||||||
|
<button class="btn btn-icon mx-auto">
|
||||||
|
<i class="fa fa-angle-double-up animate" aria-hidden="true"></i>
|
||||||
|
</button>
|
||||||
|
<span class="visually-hidden">{{t('continuous-reading-prev-chapter-alt')}}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div infinite-scroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50">
|
<div infinite-scroll [infiniteScrollDistance]="1" [infiniteScrollThrottle]="50">
|
||||||
<ng-container *ngFor="let item of webtoonImages | async; let index = index;">
|
@for(item of webtoonImages | async; let index = $index; track item.src) {
|
||||||
<img src="{{item.src}}" style="display: block; width: {{widthOverride$ | async}}"
|
<img src="{{item.src}}" style="display: block;" [ngStyle]="{'width': widthOverride$ | async}"
|
||||||
[style.filter]="(darkness$ | async) ?? '' | safeStyle"
|
[style.filter]="(darkness$ | async) ?? '' | safeStyle"
|
||||||
class="mx-auto {{pageNum === item.page && showDebugOutline() ? 'active': ''}} {{areImagesWiderThanWindow ? 'full-width' : ''}}"
|
class="mx-auto {{pageNum === item.page && showDebugOutline() ? 'active': ''}} {{areImagesWiderThanWindow ? 'full-width' : ''}}"
|
||||||
rel="nofollow" alt="image" (load)="onImageLoad($event)" id="page-{{item.page}}" [attr.page]="item.page" ondragstart="return false;" onselectstart="return false;">
|
rel="nofollow"
|
||||||
</ng-container>
|
alt="image"
|
||||||
|
(load)="onImageLoad($event)"
|
||||||
|
id="page-{{item.page}}"
|
||||||
|
[attr.page]="item.page"
|
||||||
|
ondragstart="return false;"
|
||||||
|
onselectstart="return false;">
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div #bottomSpacer class="spacer bottom" role="alert" (click)="loadNextChapter.emit()">
|
<div #bottomSpacer class="spacer bottom" role="alert" (click)="loadNextChapter.emit()">
|
||||||
@ -47,7 +57,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<span class="visually-hidden">{{t('continuous-reading-next-chapter-alt')}}</span>
|
<span class="visually-hidden">{{t('continuous-reading-next-chapter-alt')}}</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="height: 200px"></div>
|
<div class="empty-space"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
@ -21,8 +21,12 @@
|
|||||||
|
|
||||||
.text {
|
.text {
|
||||||
z-index: 101;
|
z-index: 101;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-space {
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { DOCUMENT, NgIf, NgFor, AsyncPipe } from '@angular/common';
|
import {DOCUMENT, AsyncPipe, NgStyle} from '@angular/common';
|
||||||
import {
|
import {
|
||||||
AfterViewInit,
|
AfterViewInit,
|
||||||
ChangeDetectionStrategy,
|
ChangeDetectionStrategy,
|
||||||
@ -16,7 +16,7 @@ import {
|
|||||||
Renderer2,
|
Renderer2,
|
||||||
SimpleChanges, ViewChild
|
SimpleChanges, ViewChild
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {BehaviorSubject, filter, fromEvent, map, Observable, of, ReplaySubject} from 'rxjs';
|
import {BehaviorSubject, fromEvent, map, Observable, of, ReplaySubject} from 'rxjs';
|
||||||
import { debounceTime } from 'rxjs/operators';
|
import { debounceTime } from 'rxjs/operators';
|
||||||
import { ScrollService } from 'src/app/_services/scroll.service';
|
import { ScrollService } from 'src/app/_services/scroll.service';
|
||||||
import { ReaderService } from '../../../_services/reader.service';
|
import { ReaderService } from '../../../_services/reader.service';
|
||||||
@ -62,7 +62,7 @@ const enum DEBUG_MODES {
|
|||||||
styleUrls: ['./infinite-scroller.component.scss'],
|
styleUrls: ['./infinite-scroller.component.scss'],
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [NgIf, NgFor, AsyncPipe, TranslocoDirective, InfiniteScrollModule, SafeStylePipe]
|
imports: [AsyncPipe, TranslocoDirective, InfiniteScrollModule, SafeStylePipe, NgStyle]
|
||||||
})
|
})
|
||||||
export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
|
export class InfiniteScrollerComponent implements OnInit, OnChanges, OnDestroy, AfterViewInit {
|
||||||
|
|
||||||
|
@ -19,6 +19,7 @@ import {
|
|||||||
BehaviorSubject,
|
BehaviorSubject,
|
||||||
debounceTime,
|
debounceTime,
|
||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
forkJoin,
|
forkJoin,
|
||||||
fromEvent,
|
fromEvent,
|
||||||
map,
|
map,
|
||||||
@ -399,7 +400,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
debugMode: boolean = false;
|
debugMode: boolean = false;
|
||||||
/**
|
/**
|
||||||
* Width override label for maunal width control
|
* Width override label for manual width control
|
||||||
*/
|
*/
|
||||||
widthOverrideLabel$ : Observable<string> = new Observable<string>();
|
widthOverrideLabel$ : Observable<string> = new Observable<string>();
|
||||||
|
|
||||||
@ -554,55 +555,7 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
takeUntilDestroyed(this.destroyRef)
|
takeUntilDestroyed(this.destroyRef)
|
||||||
).subscribe(() => {});
|
).subscribe(() => {});
|
||||||
|
|
||||||
//only enable the width override slider under certain conditions
|
this.setupWidthOverrideTriggers();
|
||||||
// width mode selected
|
|
||||||
// splitting is set to fit to screen, otherwise disable
|
|
||||||
// when disable set the value to 0
|
|
||||||
// to use the default of the current single page reader
|
|
||||||
this.generalSettingsForm.get('pageSplitOption')?.valueChanges.pipe(
|
|
||||||
tap(val => {
|
|
||||||
const fitting = this.generalSettingsForm.get('fittingOption')?.value;
|
|
||||||
const widthOverrideControl = this.generalSettingsForm.get('widthSlider')!;
|
|
||||||
|
|
||||||
if (PageSplitOption.FitSplit == val && FITTING_OPTION.WIDTH == fitting) {
|
|
||||||
widthOverrideControl?.enable();
|
|
||||||
} else {
|
|
||||||
widthOverrideControl?.setValue(0);
|
|
||||||
widthOverrideControl?.disable();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
).subscribe(() => {});
|
|
||||||
|
|
||||||
//only enable the width override slider under certain conditions
|
|
||||||
// width mode selected
|
|
||||||
// splitting is set to fit to screen, otherwise disable
|
|
||||||
// when disable set the value to 0
|
|
||||||
// to use the default of the current single page reader
|
|
||||||
this.generalSettingsForm.get('fittingOption')?.valueChanges.pipe(
|
|
||||||
tap(val => {
|
|
||||||
const splitting = this.generalSettingsForm.get('pageSplitOption')?.value;
|
|
||||||
const widthOverrideControl = this.generalSettingsForm.get('widthSlider')!;
|
|
||||||
|
|
||||||
if (PageSplitOption.FitSplit == splitting && FITTING_OPTION.WIDTH == val){
|
|
||||||
widthOverrideControl?.enable();
|
|
||||||
} else {
|
|
||||||
widthOverrideControl?.setValue(0);
|
|
||||||
widthOverrideControl?.disable();
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
).subscribe(() => {});
|
|
||||||
|
|
||||||
//sets the default override to 0, fixing the none% bug
|
|
||||||
this.generalSettingsForm.get('widthSlider')!.setValue(0);
|
|
||||||
|
|
||||||
//send the current width override value to the label
|
|
||||||
this.widthOverrideLabel$ = this.readerSettings$?.pipe(
|
|
||||||
map(values => (parseInt(values.widthSlider) <= 0) ? '' : values.widthSlider + '%'),
|
|
||||||
takeUntilDestroyed(this.destroyRef)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
this.generalSettingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => {
|
this.generalSettingsForm.get('layoutMode')?.valueChanges.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(val => {
|
||||||
|
|
||||||
@ -746,6 +699,68 @@ export class MangaReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Width override is only valid under the following conditions:
|
||||||
|
* Image Scaling is Width
|
||||||
|
* Reader Mode is Webtoon
|
||||||
|
*
|
||||||
|
* In all other cases, the form will be disabled and set to 0 which indicates default/off state.
|
||||||
|
*/
|
||||||
|
setupWidthOverrideTriggers() {
|
||||||
|
const widthOverrideControl = this.generalSettingsForm.get('widthSlider')!;
|
||||||
|
|
||||||
|
const enableWidthOverride = () => {
|
||||||
|
widthOverrideControl.enable();
|
||||||
|
};
|
||||||
|
|
||||||
|
const disableWidthOverride = () => {
|
||||||
|
widthOverrideControl.setValue(0);
|
||||||
|
widthOverrideControl.disable();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleControlChanges = () => {
|
||||||
|
const fitting = this.generalSettingsForm.get('fittingOption')?.value;
|
||||||
|
const splitting = this.generalSettingsForm.get('pageSplitOption')?.value;
|
||||||
|
|
||||||
|
if ((PageSplitOption.FitSplit == splitting && FITTING_OPTION.WIDTH == fitting) || this.readerMode === ReaderMode.Webtoon) {
|
||||||
|
enableWidthOverride();
|
||||||
|
} else {
|
||||||
|
disableWidthOverride();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Reader mode changes
|
||||||
|
this.readerModeSubject.asObservable()
|
||||||
|
.pipe(
|
||||||
|
filter(v => v === ReaderMode.Webtoon),
|
||||||
|
tap(enableWidthOverride),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
|
)
|
||||||
|
.subscribe();
|
||||||
|
|
||||||
|
// Page split option changes
|
||||||
|
this.generalSettingsForm.get('pageSplitOption')?.valueChanges.pipe(
|
||||||
|
distinctUntilChanged(),
|
||||||
|
tap(handleControlChanges),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
|
).subscribe();
|
||||||
|
|
||||||
|
// Fitting option changes
|
||||||
|
this.generalSettingsForm.get('fittingOption')?.valueChanges.pipe(
|
||||||
|
tap(handleControlChanges),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
|
).subscribe();
|
||||||
|
|
||||||
|
// Set the default override to 0
|
||||||
|
widthOverrideControl.setValue(0);
|
||||||
|
|
||||||
|
//send the current width override value to the label
|
||||||
|
this.widthOverrideLabel$ = this.readerSettings$?.pipe(
|
||||||
|
map(values => (parseInt(values.widthSlider) <= 0) ? '' : values.widthSlider + '%'),
|
||||||
|
takeUntilDestroyed(this.destroyRef)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
createReaderSettingsUpdate() {
|
createReaderSettingsUpdate() {
|
||||||
return {
|
return {
|
||||||
pageSplit: parseInt(this.generalSettingsForm.get('pageSplitOption')?.value, 10),
|
pageSplit: parseInt(this.generalSettingsForm.get('pageSplitOption')?.value, 10),
|
||||||
|
2074
openapi.json
2074
openapi.json
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user