Remove From On Deck (#2131)
* Allow admins to customize the amount of progress time or last item added time for on deck calculation * Implemented the ability to remove series from on deck. They will be removed until the user reads a new chapter. Quite a few db lookup reduction calls for reading based stuff, like continue point, bookmarks, etc.
This commit is contained in:
parent
90a6c89486
commit
348bc062ee
24 changed files with 2597 additions and 87 deletions
|
|
@ -88,6 +88,10 @@ export enum Action {
|
|||
* Import some data into Kavita
|
||||
*/
|
||||
Import = 18,
|
||||
/**
|
||||
* Removes the Series from On Deck inclusion
|
||||
*/
|
||||
RemoveFromOnDeck = 19,
|
||||
}
|
||||
|
||||
export interface ActionItem<T> {
|
||||
|
|
@ -563,9 +567,7 @@ export class ActionFactoryService {
|
|||
|
||||
// Checks the whole tree for the action and returns true if it exists
|
||||
public hasAction(actions: Array<ActionItem<any>>, action: Action) {
|
||||
var actionFound = false;
|
||||
|
||||
if (actions.length === 0) return actionFound;
|
||||
if (actions.length === 0) return false;
|
||||
|
||||
for (let i = 0; i < actions.length; i++)
|
||||
{
|
||||
|
|
@ -573,8 +575,7 @@ export class ActionFactoryService {
|
|||
if (this.hasAction(actions[i].children, action)) return true;
|
||||
}
|
||||
|
||||
|
||||
return actionFound;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,4 +226,8 @@ export class SeriesService {
|
|||
getOverallRating(seriesId: number) {
|
||||
return this.httpClient.get<Rating>(this.baseUrl + 'rating/overall?seriesId=' + seriesId);
|
||||
}
|
||||
|
||||
removeFromOnDeck(seriesId: number) {
|
||||
return this.httpClient.post(this.baseUrl + 'series/remove-from-on-deck?seriesId=' + seriesId, {});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,4 +18,6 @@ export interface ServerSettings {
|
|||
enableFolderWatching: boolean;
|
||||
hostName: string;
|
||||
cacheSize: number;
|
||||
onDeckProgressDays: number;
|
||||
onDeckUpdateDays: number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,38 @@
|
|||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12 pe-2">
|
||||
<label for="on-deck-progress-days" class="form-label">On Deck Last Progress (days)</label> <i class="fa fa-info-circle" placement="right" [ngbTooltip]="onDeckProgressDaysTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #onDeckProgressDaysTooltip>The number of days since last progress before kicking something off On Deck.</ng-template>
|
||||
<span class="visually-hidden" id="on-deck-progress-days-help">The number of days since last progress before kicking something off On Deck.</span>
|
||||
<input id="on-deck-progress-days" aria-describedby="on-deck-progress-days-help" class="form-control" formControlName="onDeckProgressDays"
|
||||
type="number" inputmode="numeric" step="1" min="1"
|
||||
[class.is-invalid]="settingsForm.get('onDeckProgressDays')?.invalid && settingsForm.get('onDeckProgressDays')?.touched">
|
||||
<ng-container *ngIf="settingsForm.get('onDeckProgressDays')?.errors as errors">
|
||||
<p class="invalid-feedback" *ngIf="errors.min">
|
||||
Must be at least 1 day
|
||||
</p>
|
||||
<p class="invalid-feedback" *ngIf="errors.required">
|
||||
This field is required
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12 pe-2">
|
||||
<label for="on-deck-update-days" class="form-label">On Deck Last Chapter Add (days)</label> <i class="fa fa-info-circle" placement="right" [ngbTooltip]="onDeckUpdateDaysTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #onDeckUpdateDaysTooltip>The number of days since last chapter was added to include something On Deck.</ng-template>
|
||||
<span class="visually-hidden" id="on-deck-update-days-help">The number of days since last chapter was added to include something On Deck.</span>
|
||||
<input id="on-deck-update-days" aria-describedby="on-deck-update-days-help" class="form-control" formControlName="onDeckUpdateDays"
|
||||
type="number" inputmode="numeric" step="1" min="1"
|
||||
[class.is-invalid]="settingsForm.get('onDeckUpdateDays')?.invalid && settingsForm.get('onDeckUpdateDays')?.touched">
|
||||
<ng-container *ngIf="settingsForm.get('onDeckUpdateDays')?.errors as errors">
|
||||
<p class="invalid-feedback" *ngIf="errors.min">
|
||||
Must be at least 1 day
|
||||
</p>
|
||||
<p class="invalid-feedback" *ngIf="errors.required">
|
||||
This field is required
|
||||
</p>
|
||||
</ng-container>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3 mt-3">
|
||||
|
|
@ -125,7 +157,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TODO: Move this to Plugins tab once we build out some basic tables -->
|
||||
<div class="mb-3">
|
||||
<label for="opds" aria-describedby="opds-info" class="form-label">OPDS</label>
|
||||
<p class="accent" id="opds-info">OPDS support will allow all users to use OPDS to read and download content from the server.</p>
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ export class ManageSettingsComponent implements OnInit {
|
|||
taskFrequencies: Array<string> = [];
|
||||
logLevels: Array<string> = [];
|
||||
|
||||
get TagBadgeCursor() {
|
||||
return TagBadgeCursor;
|
||||
}
|
||||
|
||||
constructor(private settingsService: SettingsService, private toastr: ToastrService,
|
||||
private serverService: ServerService) { }
|
||||
|
||||
|
|
@ -57,6 +53,8 @@ export class ManageSettingsComponent implements OnInit {
|
|||
this.settingsForm.addControl('enableFolderWatching', new FormControl(this.serverSettings.enableFolderWatching, [Validators.required]));
|
||||
this.settingsForm.addControl('encodeMediaAs', new FormControl(this.serverSettings.encodeMediaAs, []));
|
||||
this.settingsForm.addControl('hostName', new FormControl(this.serverSettings.hostName, [Validators.pattern(/^(http:|https:)+[^\s]+[\w]$/)]));
|
||||
this.settingsForm.addControl('onDeckProgressDays', new FormControl(this.serverSettings.onDeckProgressDays, [Validators.required]));
|
||||
this.settingsForm.addControl('onDeckUpdateDays', new FormControl(this.serverSettings.onDeckUpdateDays, [Validators.required]));
|
||||
|
||||
this.serverService.getServerInfo().subscribe(info => {
|
||||
if (info.isDocker) {
|
||||
|
|
@ -84,6 +82,8 @@ export class ManageSettingsComponent implements OnInit {
|
|||
this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs);
|
||||
this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName);
|
||||
this.settingsForm.get('cacheSize')?.setValue(this.serverSettings.cacheSize);
|
||||
this.settingsForm.get('onDeckProgressDays')?.setValue(this.serverSettings.onDeckProgressDays);
|
||||
this.settingsForm.get('onDeckUpdateDays')?.setValue(this.serverSettings.onDeckUpdateDays);
|
||||
this.settingsForm.markAsPristine();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ import {
|
|||
OnInit,
|
||||
Output
|
||||
} from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { Series } from 'src/app/_models/series';
|
||||
import { ImageService } from 'src/app/_services/image.service';
|
||||
import { ActionFactoryService, Action, ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
import { ActionService } from 'src/app/_services/action.service';
|
||||
import { EditSeriesModalComponent } from '../_modals/edit-series-modal/edit-series-modal.component';
|
||||
import { RelationKind } from 'src/app/_models/series-detail/relation-kind';
|
||||
import {Router} from '@angular/router';
|
||||
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {Series} from 'src/app/_models/series';
|
||||
import {ImageService} from 'src/app/_services/image.service';
|
||||
import {Action, ActionFactoryService, ActionItem} from 'src/app/_services/action-factory.service';
|
||||
import {SeriesService} from 'src/app/_services/series.service';
|
||||
import {ActionService} from 'src/app/_services/action.service';
|
||||
import {EditSeriesModalComponent} from '../_modals/edit-series-modal/edit-series-modal.component';
|
||||
import {RelationKind} from 'src/app/_models/series-detail/relation-kind';
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {CardItemComponent} from "../card-item/card-item.component";
|
||||
import {RelationshipPipe} from "../../pipe/relationship.pipe";
|
||||
|
|
@ -47,6 +47,10 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
* If the Series has a relationship to display
|
||||
*/
|
||||
@Input() relation: RelationKind | undefined = undefined;
|
||||
/**
|
||||
* When a series card is shown on deck, a special actionable is added to the list
|
||||
*/
|
||||
@Input() isOnDeck: boolean = false;
|
||||
|
||||
@Output() clicked = new EventEmitter<Series>();
|
||||
/**
|
||||
|
|
@ -79,6 +83,19 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
ngOnChanges(changes: any) {
|
||||
if (this.data) {
|
||||
this.actions = this.actionFactoryService.getSeriesActions((action: ActionItem<Series>, series: Series) => this.handleSeriesActionCallback(action, series));
|
||||
if (this.isOnDeck) {
|
||||
const othersIndex = this.actions.findIndex(obj => obj.title === 'Others');
|
||||
if (this.actions[othersIndex].children.findIndex(o => o.action === Action.RemoveFromOnDeck) < 0) {
|
||||
this.actions[othersIndex].children.push({
|
||||
action: Action.RemoveFromOnDeck,
|
||||
title: 'Remove From On Deck',
|
||||
callback: (action: ActionItem<Series>, series: Series) => this.handleSeriesActionCallback(action, series),
|
||||
class: 'danger',
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
}
|
||||
|
|
@ -125,6 +142,9 @@ export class SeriesCardComponent implements OnInit, OnChanges {
|
|||
const device = (action._extra!.data as Device);
|
||||
this.actionService.sendSeriesToDevice(series.id, device);
|
||||
break;
|
||||
case Action.RemoveFromOnDeck:
|
||||
this.seriesService.removeFromOnDeck(series.id).subscribe(() => this.reload.emit(series.id));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,8 @@
|
|||
|
||||
<app-carousel-reel [items]="inProgress" title="On Deck" (sectionClick)="handleSectionClick($event)">
|
||||
<ng-template #carouselItem let-item let-position="idx">
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" (reload)="reloadInProgress(item)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
<app-series-card [data]="item" [libraryId]="item.libraryId" [suppressLibraryLink]="libraryId !== 0" [isOnDeck]="true"
|
||||
(reload)="reloadInProgress($event)" (dataChanged)="reloadInProgress($event)"></app-series-card>
|
||||
</ng-template>
|
||||
</app-carousel-reel>
|
||||
|
||||
|
|
|
|||
|
|
@ -120,15 +120,17 @@ export class DashboardComponent implements OnInit {
|
|||
this.loadRecentlyAddedSeries();
|
||||
}
|
||||
|
||||
reloadInProgress(series: Series | boolean) {
|
||||
if (series === true || series === false) {
|
||||
if (!series) {return;}
|
||||
}
|
||||
// If the update to Series doesn't affect the requirement to be in this stream, then ignore update request
|
||||
const seriesObj = (series as Series);
|
||||
if (seriesObj.pagesRead !== seriesObj.pages && seriesObj.pagesRead !== 0) {
|
||||
return;
|
||||
}
|
||||
reloadInProgress(series: Series | number) {
|
||||
// if (typeof series === 'number') {
|
||||
// this.loadOnDeck();
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // If the update to Series doesn't affect the requirement to be in this stream, then ignore update request
|
||||
// const seriesObj = (series as Series);
|
||||
// if (seriesObj.pagesRead !== seriesObj.pages && seriesObj.pagesRead !== 0) {
|
||||
// return;
|
||||
// }
|
||||
|
||||
this.loadOnDeck();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue