Background Prefetching for Kavita+ (#2707)
This commit is contained in:
parent
f616b99585
commit
5dc5029a75
35 changed files with 3300 additions and 100 deletions
|
@ -552,11 +552,9 @@ export class ActionService implements OnDestroy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Mark all chapters and the volumes as Read. All volumes and chapters must belong to a series
|
||||
* @param seriesId Series Id
|
||||
* @param volumes Volumes, should have id, chapters and pagesRead populated
|
||||
* @param chapters? Chapters, should have id
|
||||
* @param callback Optional callback to perform actions after API completes
|
||||
* Deletes all series
|
||||
* @param seriesIds - List of series
|
||||
* @param callback - Optional callback once complete
|
||||
*/
|
||||
async deleteMultipleSeries(seriesIds: Array<Series>, callback?: BooleanActionCallback) {
|
||||
if (!await this.confirmService.confirm(translate('toasts.confirm-delete-multiple-series', {count: seriesIds.length}))) {
|
||||
|
@ -565,11 +563,15 @@ export class ActionService implements OnDestroy {
|
|||
}
|
||||
return;
|
||||
}
|
||||
this.seriesService.deleteMultipleSeries(seriesIds.map(s => s.id)).pipe(take(1)).subscribe(() => {
|
||||
this.toastr.success(translate('toasts.series-deleted'));
|
||||
this.seriesService.deleteMultipleSeries(seriesIds.map(s => s.id)).pipe(take(1)).subscribe(res => {
|
||||
if (res) {
|
||||
this.toastr.success(translate('toasts.series-deleted'));
|
||||
} else {
|
||||
this.toastr.error(translate('errors.generic'));
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
callback(true);
|
||||
callback(res);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -584,7 +586,12 @@ export class ActionService implements OnDestroy {
|
|||
|
||||
this.seriesService.delete(series.id).subscribe((res: boolean) => {
|
||||
if (callback) {
|
||||
this.toastr.success(translate('toasts.series-deleted'));
|
||||
if (res) {
|
||||
this.toastr.success(translate('toasts.series-deleted'));
|
||||
} else {
|
||||
this.toastr.error(translate('errors.generic'));
|
||||
}
|
||||
|
||||
callback(res);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -80,11 +80,11 @@ export class SeriesService {
|
|||
}
|
||||
|
||||
delete(seriesId: number) {
|
||||
return this.httpClient.delete<boolean>(this.baseUrl + 'series/' + seriesId);
|
||||
return this.httpClient.delete<string>(this.baseUrl + 'series/' + seriesId, TextResonse).pipe(map(s => s === "true"));
|
||||
}
|
||||
|
||||
deleteMultipleSeries(seriesIds: Array<number>) {
|
||||
return this.httpClient.post<boolean>(this.baseUrl + 'series/delete-multiple', {seriesIds});
|
||||
return this.httpClient.post<string>(this.baseUrl + 'series/delete-multiple', {seriesIds}, TextResonse).pipe(map(s => s === "true"));
|
||||
}
|
||||
|
||||
updateRating(seriesId: number, userRating: number) {
|
||||
|
|
|
@ -15,8 +15,6 @@ import {NgForOf, NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
|||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {ManageAlertsComponent} from "../manage-alerts/manage-alerts.component";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {filter} from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-email-settings',
|
||||
|
|
|
@ -8,8 +8,8 @@
|
|||
<i *ngIf="iconClasses !== ''" class="{{iconClasses}} title-icon ms-1" aria-hidden="true"></i>
|
||||
</h3>
|
||||
<div class="float-end" *ngIf="swiper">
|
||||
<button class="btn btn-icon" [disabled]="swiper.isBeginning" (click)="prevPage()"><i class="fa fa-angle-left" aria-hidden="true"></i><span class="visually-hidden">{{t('prev-items')}}</span></button>
|
||||
<button class="btn btn-icon" [disabled]="swiper.isEnd" (click)="nextPage()"><i class="fa fa-angle-right" aria-hidden="true"></i><span class="visually-hidden">{{t('next-items')}}</span></button>
|
||||
<button class="btn btn-icon carousel-btn" [disabled]="swiper.isBeginning" (click)="prevPage()"><i class="fa fa-angle-left" aria-hidden="true"></i><span class="visually-hidden">{{t('prev-items')}}</span></button>
|
||||
<button class="btn btn-icon carousel-btn" [disabled]="swiper.isEnd" (click)="nextPage()"><i class="fa fa-angle-right" aria-hidden="true"></i><span class="visually-hidden">{{t('next-items')}}</span></button>
|
||||
</div>
|
||||
</div>
|
||||
@if (items.length > 0) {
|
||||
|
|
|
@ -21,6 +21,10 @@
|
|||
.non-selectable {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.carousel-btn > i {
|
||||
color: var(--carousel-btn-color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,11 +3,15 @@
|
|||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: var(--badge-text-color);
|
||||
}
|
||||
|
||||
.sm-popover {
|
||||
width: 150px;
|
||||
|
||||
> .popover-body {
|
||||
padding-top: 0px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,7 +19,7 @@
|
|||
width: 214px;
|
||||
|
||||
> .popover-body {
|
||||
padding-top: 0px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +27,7 @@
|
|||
width: 320px;
|
||||
|
||||
> .popover-body {
|
||||
padding-top: 0px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import {
|
|||
tap,
|
||||
finalize,
|
||||
of,
|
||||
filter, Subject,
|
||||
filter,
|
||||
} from 'rxjs';
|
||||
import { download, Download } from '../_models/download';
|
||||
import { PageBookmark } from 'src/app/_models/readers/page-bookmark';
|
||||
|
@ -71,6 +71,10 @@ export class DownloadService {
|
|||
* Size in bytes in which to inform the user for confirmation before download starts. Defaults to 100 MB.
|
||||
*/
|
||||
public SIZE_WARNING = 104_857_600;
|
||||
/**
|
||||
* Sie in bytes in which to inform the user that anything above may fail on iOS due to device limits. (200MB)
|
||||
*/
|
||||
private IOS_SIZE_WARNING = 209_715_200;
|
||||
|
||||
private downloadsSource: BehaviorSubject<DownloadEvent[]> = new BehaviorSubject<DownloadEvent[]>([]);
|
||||
/**
|
||||
|
@ -290,41 +294,18 @@ export class DownloadService {
|
|||
|
||||
private downloadChapter(chapter: Chapter) {
|
||||
return this.downloadEntity(chapter);
|
||||
|
||||
// const downloadType = 'chapter';
|
||||
// const subtitle = this.downloadSubtitle(downloadType, chapter);
|
||||
// return this.httpClient.get(this.baseUrl + 'download/chapter?chapterId=' + chapter.id,
|
||||
// {observe: 'events', responseType: 'blob', reportProgress: true}
|
||||
// ).pipe(
|
||||
// throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
|
||||
// download((blob, filename) => {
|
||||
// this.save(blob, decodeURIComponent(filename));
|
||||
// }),
|
||||
// tap((d) => this.updateDownloadState(d, downloadType, subtitle, chapter.id)),
|
||||
// finalize(() => this.finalizeDownloadState(downloadType, subtitle))
|
||||
// );
|
||||
}
|
||||
|
||||
private downloadVolume(volume: Volume) {
|
||||
return this.downloadEntity(volume);
|
||||
// const downloadType = 'volume';
|
||||
// const subtitle = this.downloadSubtitle(downloadType, volume);
|
||||
// return this.httpClient.get(this.baseUrl + 'download/volume?volumeId=' + volume.id,
|
||||
// {observe: 'events', responseType: 'blob', reportProgress: true}
|
||||
// ).pipe(
|
||||
// throttleTime(DEBOUNCE_TIME, asyncScheduler, { leading: true, trailing: true }),
|
||||
// download((blob, filename) => {
|
||||
// this.save(blob, decodeURIComponent(filename));
|
||||
// }),
|
||||
// tap((d) => this.updateDownloadState(d, downloadType, subtitle, volume.id)),
|
||||
// finalize(() => this.finalizeDownloadState(downloadType, subtitle))
|
||||
// );
|
||||
}
|
||||
|
||||
private async confirmSize(size: number, entityType: DownloadEntityType) {
|
||||
const showIosWarning = size > this.IOS_SIZE_WARNING && /iPad|iPhone|iPod/.test(navigator.userAgent);
|
||||
return (size < this.SIZE_WARNING ||
|
||||
await this.confirmService.confirm(translate('toasts.confirm-download-size',
|
||||
{entityType: translate('entity-type.' + entityType), size: bytesPipe.transform(size)})));
|
||||
{entityType: translate('entity-type.' + entityType), size: bytesPipe.transform(size)})
|
||||
+ (!showIosWarning ? '' : '<br/><br/>' + translate('toasts.confirm-download-size-ios'))));
|
||||
}
|
||||
|
||||
private downloadBookmarks(bookmarks: PageBookmark[]) {
|
||||
|
|
|
@ -100,7 +100,7 @@
|
|||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--side-nav-color);
|
||||
color: var(--side-nav-text-color);
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
|
|
|
@ -2060,6 +2060,7 @@
|
|||
"confirm-library-delete": "Are you sure you want to delete the {{name}} library? You cannot undo this action.",
|
||||
"confirm-library-type-change": "Changing library type will trigger a new scan with different parsing rules and may lead to series being re-created and hence you may loose progress and bookmarks. You should backup before you do this. Are you sure you want to continue?",
|
||||
"confirm-download-size": "The {{entityType}} is {{size}}. Are you sure you want to continue?",
|
||||
"confirm-download-size-ios": "iOS has issues downloading files that are larger than 200MB, this download might not complete.",
|
||||
"list-doesnt-exist": "This list doesn't exist",
|
||||
"confirm-delete-smart-filter": "Are you sure you want to delete this Smart Filter?",
|
||||
"smart-filter-deleted": "Smart Filter Deleted",
|
||||
|
|
|
@ -110,7 +110,7 @@
|
|||
--side-nav-mobile-box-shadow: 3px 0em 5px 10em rgb(0 0 0 / 50%);
|
||||
--side-nav-hover-text-color: white;
|
||||
--side-nav-hover-bg-color: black;
|
||||
--side-nav-color: white;
|
||||
--side-nav-text-color: white;
|
||||
--side-nav-border-radius: 5px;
|
||||
--side-nav-border: none;
|
||||
--side-nav-border-closed: none;
|
||||
|
@ -228,6 +228,7 @@
|
|||
--carousel-header-text-color: var(--body-text-color);
|
||||
--carousel-header-text-decoration: none;
|
||||
--carousel-hover-header-text-decoration: none;
|
||||
--carousel-btn-color: var(--body-text-color);
|
||||
|
||||
/** Drawer */
|
||||
--drawer-bg-color: #292929;
|
||||
|
@ -259,4 +260,7 @@
|
|||
/** Rating Star Color **/
|
||||
--rating-star-color: var(--primary-color);
|
||||
|
||||
}
|
||||
/** Badge **/
|
||||
--badge-text-color: var(--bs-badge-color);
|
||||
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
--side-nav-mobile-box-shadow: 3px 0em 5px 10em rgb(0 0 0 / 50%);
|
||||
--side-nav-hover-text-color: white;
|
||||
--side-nav-hover-bg-color: black;
|
||||
--side-nav-color: black;
|
||||
--side-nav-text-color: black;
|
||||
--side-nav-border-radius: 5px;
|
||||
--side-nav-border: none;
|
||||
--side-nav-border-closed: none;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
--body-text-color: #efefef;
|
||||
--btn-icon-filter: invert(1) grayscale(100%) brightness(200%);
|
||||
--primary-color-scrollbar: rgba(74,198,148,0.75);
|
||||
|
||||
|
||||
|
||||
/* Navbar */
|
||||
--navbar-bg-color: black;
|
||||
|
@ -100,7 +100,7 @@
|
|||
--side-nav-mobile-box-shadow: 3px 0em 5px 10em rgb(0 0 0 / 50%);
|
||||
--side-nav-hover-text-color: white;
|
||||
--side-nav-hover-bg-color: black;
|
||||
--side-nav-color: white;
|
||||
--side-nav-text-color: white;
|
||||
--side-nav-border-radius: 5px;
|
||||
--side-nav-border: none;
|
||||
--side-nav-border-closed: none;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue