mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-07-09 03:04:19 -04:00
Prep for Hotfix (#2362)
This commit is contained in:
parent
0cf760ecd3
commit
30f1cd20a0
@ -56,7 +56,7 @@ public class SmartFilterHelperTests
|
|||||||
};
|
};
|
||||||
|
|
||||||
var encodedFilter = SmartFilterHelper.Encode(filter);
|
var encodedFilter = SmartFilterHelper.Encode(filter);
|
||||||
Assert.Equal("name=Test&stmts=comparison%253D0%252Cfield%253D4%252Cvalue%253D0&sortOptions=sortField%3D2%26isAscending%3DFalse&limitTo=10&combination=1", encodedFilter);
|
Assert.Equal("name=Test&stmts=comparison%253D0%252Cfield%253D4%252Cvalue%253D0&sortOptions=sortField%3D2%2CisAscending%3DFalse&limitTo=10&combination=1", encodedFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AssertStatementSame(FilterStatementDto statement, FilterField field, FilterComparison combination, string value)
|
private void AssertStatementSame(FilterStatementDto statement, FilterField field, FilterComparison combination, string value)
|
||||||
|
@ -412,14 +412,15 @@ public class LibraryController : BaseApiController
|
|||||||
[HttpPost("update")]
|
[HttpPost("update")]
|
||||||
public async Task<ActionResult> UpdateLibrary(UpdateLibraryDto dto)
|
public async Task<ActionResult> UpdateLibrary(UpdateLibraryDto dto)
|
||||||
{
|
{
|
||||||
|
var userId = User.GetUserId();
|
||||||
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(dto.Id, LibraryIncludes.Folders);
|
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(dto.Id, LibraryIncludes.Folders);
|
||||||
if (library == null) return BadRequest(await _localizationService.Translate(User.GetUserId(), "library-doesnt-exist"));
|
if (library == null) return BadRequest(await _localizationService.Translate(userId, "library-doesnt-exist"));
|
||||||
|
|
||||||
var newName = dto.Name.Trim();
|
var newName = dto.Name.Trim();
|
||||||
if (await _unitOfWork.LibraryRepository.LibraryExists(newName) && !library.Name.Equals(newName))
|
if (await _unitOfWork.LibraryRepository.LibraryExists(newName) && !library.Name.Equals(newName))
|
||||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "library-name-exists"));
|
return BadRequest(await _localizationService.Translate(userId, "library-name-exists"));
|
||||||
|
|
||||||
var originalFolders = library.Folders.Select(x => x.Path).ToList();
|
var originalFoldersCount = library.Folders.Count;
|
||||||
|
|
||||||
library.Name = newName;
|
library.Name = newName;
|
||||||
library.Folders = dto.Folders.Select(s => new FolderPath() {Path = s}).Distinct().ToList();
|
library.Folders = dto.Folders.Select(s => new FolderPath() {Path = s}).Distinct().ToList();
|
||||||
@ -445,8 +446,8 @@ public class LibraryController : BaseApiController
|
|||||||
|
|
||||||
_unitOfWork.LibraryRepository.Update(library);
|
_unitOfWork.LibraryRepository.Update(library);
|
||||||
|
|
||||||
if (!await _unitOfWork.CommitAsync()) return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-library-update"));
|
if (!await _unitOfWork.CommitAsync()) return BadRequest(await _localizationService.Translate(userId, "generic-library-update"));
|
||||||
if (originalFolders.Count != dto.Folders.Count() || typeUpdate)
|
if (originalFoldersCount != dto.Folders.Count() || typeUpdate)
|
||||||
{
|
{
|
||||||
await _libraryWatcher.RestartWatching();
|
await _libraryWatcher.RestartWatching();
|
||||||
_taskScheduler.ScanLibrary(library.Id);
|
_taskScheduler.ScanLibrary(library.Id);
|
||||||
@ -458,8 +459,9 @@ public class LibraryController : BaseApiController
|
|||||||
}
|
}
|
||||||
await _eventHub.SendMessageAsync(MessageFactory.LibraryModified,
|
await _eventHub.SendMessageAsync(MessageFactory.LibraryModified,
|
||||||
MessageFactory.LibraryModifiedEvent(library.Id, "update"), false);
|
MessageFactory.LibraryModifiedEvent(library.Id, "update"), false);
|
||||||
|
|
||||||
await _eventHub.SendMessageAsync(MessageFactory.SideNavUpdate,
|
await _eventHub.SendMessageAsync(MessageFactory.SideNavUpdate,
|
||||||
MessageFactory.SideNavUpdateEvent(User.GetUserId()), false);
|
MessageFactory.SideNavUpdateEvent(userId), false);
|
||||||
|
|
||||||
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
|
await _libraryCacheProvider.RemoveByPrefixAsync(CacheKey);
|
||||||
|
|
||||||
|
@ -1079,10 +1079,11 @@ public class OpdsController : BaseApiController
|
|||||||
/// <param name="volumeId"></param>
|
/// <param name="volumeId"></param>
|
||||||
/// <param name="chapterId"></param>
|
/// <param name="chapterId"></param>
|
||||||
/// <param name="pageNumber"></param>
|
/// <param name="pageNumber"></param>
|
||||||
|
/// <param name="saveProgress">Optional parameter. Can pass false and progress saving will be suppressed</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
[HttpGet("{apiKey}/image")]
|
[HttpGet("{apiKey}/image")]
|
||||||
public async Task<ActionResult> GetPageStreamedImage(string apiKey, [FromQuery] int libraryId, [FromQuery] int seriesId,
|
public async Task<ActionResult> GetPageStreamedImage(string apiKey, [FromQuery] int libraryId, [FromQuery] int seriesId,
|
||||||
[FromQuery] int volumeId,[FromQuery] int chapterId, [FromQuery] int pageNumber)
|
[FromQuery] int volumeId,[FromQuery] int chapterId, [FromQuery] int pageNumber, [FromQuery] bool saveProgress = true)
|
||||||
{
|
{
|
||||||
var userId = await GetUser(apiKey);
|
var userId = await GetUser(apiKey);
|
||||||
if (pageNumber < 0) return BadRequest(await _localizationService.Translate(userId, "greater-0", "Page"));
|
if (pageNumber < 0) return BadRequest(await _localizationService.Translate(userId, "greater-0", "Page"));
|
||||||
@ -1101,15 +1102,20 @@ public class OpdsController : BaseApiController
|
|||||||
// Calculates SHA1 Hash for byte[]
|
// Calculates SHA1 Hash for byte[]
|
||||||
Response.AddCacheHeader(content);
|
Response.AddCacheHeader(content);
|
||||||
|
|
||||||
// Save progress for the user
|
// Save progress for the user (except Panels, they will use a direct connection)
|
||||||
await _readerService.SaveReadingProgress(new ProgressDto()
|
var userAgent = Request.Headers["User-Agent"].ToString();
|
||||||
|
if (!userAgent.StartsWith("Panels", StringComparison.InvariantCultureIgnoreCase) || !saveProgress)
|
||||||
{
|
{
|
||||||
ChapterId = chapterId,
|
await _readerService.SaveReadingProgress(new ProgressDto()
|
||||||
PageNum = pageNumber,
|
{
|
||||||
SeriesId = seriesId,
|
ChapterId = chapterId,
|
||||||
VolumeId = volumeId,
|
PageNum = pageNumber,
|
||||||
LibraryId =libraryId
|
SeriesId = seriesId,
|
||||||
}, await GetUser(apiKey));
|
VolumeId = volumeId,
|
||||||
|
LibraryId =libraryId
|
||||||
|
}, await GetUser(apiKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return File(content, MimeTypeMap.GetMimeType(format));
|
return File(content, MimeTypeMap.GetMimeType(format));
|
||||||
}
|
}
|
||||||
|
55
API/Controllers/PanelsController.cs
Normal file
55
API/Controllers/PanelsController.cs
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using API.Data;
|
||||||
|
using API.DTOs;
|
||||||
|
using API.Services;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace API.Controllers;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For the Panels app explicitly
|
||||||
|
/// </summary>
|
||||||
|
[AllowAnonymous]
|
||||||
|
public class PanelsController : BaseApiController
|
||||||
|
{
|
||||||
|
private readonly IReaderService _readerService;
|
||||||
|
private readonly IUnitOfWork _unitOfWork;
|
||||||
|
|
||||||
|
public PanelsController(IReaderService readerService, IUnitOfWork unitOfWork)
|
||||||
|
{
|
||||||
|
_readerService = readerService;
|
||||||
|
_unitOfWork = unitOfWork;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Saves the progress of a given chapter.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dto"></param>
|
||||||
|
/// <param name="apiKey"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
[HttpPost("save-progress")]
|
||||||
|
public async Task<ActionResult> SaveProgress(ProgressDto dto, [FromQuery] string apiKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(apiKey)) return Unauthorized("ApiKey is required");
|
||||||
|
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||||
|
await _readerService.SaveReadingProgress(dto, userId);
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the Progress of a given chapter
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chapterId"></param>
|
||||||
|
/// <param name="apiKey"></param>
|
||||||
|
/// <returns>The number of pages read, 0 if none read</returns>
|
||||||
|
[HttpGet("get-progress")]
|
||||||
|
public async Task<ActionResult<int>> GetProgress(int chapterId, [FromQuery] string apiKey)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(apiKey)) return Unauthorized("ApiKey is required");
|
||||||
|
var userId = await _unitOfWork.UserRepository.GetUserIdByApiKeyAsync(apiKey);
|
||||||
|
|
||||||
|
var progress = await _unitOfWork.AppUserProgressRepository.GetUserProgressDtoAsync(chapterId, userId);
|
||||||
|
return Ok(progress?.PageNum ?? 0);
|
||||||
|
}
|
||||||
|
}
|
@ -72,7 +72,7 @@ public static class SmartFilterHelper
|
|||||||
|
|
||||||
private static string EncodeSortOptions(SortOptions sortOptions)
|
private static string EncodeSortOptions(SortOptions sortOptions)
|
||||||
{
|
{
|
||||||
return Uri.EscapeDataString($"sortField={(int) sortOptions.SortField}&isAscending={sortOptions.IsAscending}");
|
return Uri.EscapeDataString($"sortField={(int) sortOptions.SortField},isAscending={sortOptions.IsAscending}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string EncodeFilterStatementDtos(ICollection<FilterStatementDto> statements)
|
private static string EncodeFilterStatementDtos(ICollection<FilterStatementDto> statements)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import {Component, DestroyRef, HostListener, inject, Inject, OnInit} from '@angular/core';
|
import {ChangeDetectorRef, Component, DestroyRef, HostListener, inject, Inject, OnInit} from '@angular/core';
|
||||||
import {NavigationStart, Router, RouterOutlet} from '@angular/router';
|
import {NavigationStart, Router, RouterOutlet} from '@angular/router';
|
||||||
import {map, shareReplay, take} from 'rxjs/operators';
|
import {map, shareReplay, take} from 'rxjs/operators';
|
||||||
import { AccountService } from './_services/account.service';
|
import { AccountService } from './_services/account.service';
|
||||||
@ -26,8 +26,10 @@ export class AppComponent implements OnInit {
|
|||||||
|
|
||||||
private readonly destroyRef = inject(DestroyRef);
|
private readonly destroyRef = inject(DestroyRef);
|
||||||
private readonly offcanvas = inject(NgbOffcanvas);
|
private readonly offcanvas = inject(NgbOffcanvas);
|
||||||
|
public readonly navService = inject(NavService);
|
||||||
|
public readonly cdRef = inject(ChangeDetectorRef);
|
||||||
|
|
||||||
constructor(private accountService: AccountService, public navService: NavService,
|
constructor(private accountService: AccountService,
|
||||||
private libraryService: LibraryService,
|
private libraryService: LibraryService,
|
||||||
private router: Router, private ngbModal: NgbModal, ratingConfig: NgbRatingConfig,
|
private router: Router, private ngbModal: NgbModal, ratingConfig: NgbRatingConfig,
|
||||||
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService) {
|
@Inject(DOCUMENT) private document: Document, private themeService: ThemeService) {
|
||||||
@ -88,7 +90,7 @@ export class AppComponent implements OnInit {
|
|||||||
if (user) {
|
if (user) {
|
||||||
// Bootstrap anything that's needed
|
// Bootstrap anything that's needed
|
||||||
this.themeService.getThemes().subscribe();
|
this.themeService.getThemes().subscribe();
|
||||||
this.libraryService.getLibraryNames().pipe(take(1), shareReplay()).subscribe();
|
this.libraryService.getLibraryNames().pipe(take(1), shareReplay({refCount: true, bufferSize: 1})).subscribe();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,37 +121,37 @@ export class MetadataFilterComponent implements OnInit {
|
|||||||
this.loadFromPresetsAndSetup();
|
this.loadFromPresetsAndSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
loadSavedFilter(event: Select2UpdateEvent<any>) {
|
// loadSavedFilter(event: Select2UpdateEvent<any>) {
|
||||||
// Load the filter from the backend and update the screen
|
// // Load the filter from the backend and update the screen
|
||||||
if (event.value === undefined || typeof(event.value) === 'string') return;
|
// if (event.value === undefined || typeof(event.value) === 'string') return;
|
||||||
const smartFilter = event.value as SmartFilter;
|
// const smartFilter = event.value as SmartFilter;
|
||||||
this.filterV2 = this.filterUtilitiesService.decodeSeriesFilter(smartFilter.filter);
|
// this.filterV2 = this.filterUtilitiesService.decodeSeriesFilter(smartFilter.filter);
|
||||||
this.cdRef.markForCheck();
|
// this.cdRef.markForCheck();
|
||||||
console.log('update event: ', event);
|
// console.log('update event: ', event);
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
createFilterValue(event: Select2AutoCreateEvent<any>) {
|
// createFilterValue(event: Select2AutoCreateEvent<any>) {
|
||||||
// Create a new name and filter
|
// // Create a new name and filter
|
||||||
if (!this.filterV2) return;
|
// if (!this.filterV2) return;
|
||||||
this.filterV2.name = event.value;
|
// this.filterV2.name = event.value;
|
||||||
this.filterService.saveFilter(this.filterV2).subscribe(() => {
|
// this.filterService.saveFilter(this.filterV2).subscribe(() => {
|
||||||
|
//
|
||||||
const item = {
|
// const item = {
|
||||||
value: {
|
// value: {
|
||||||
filter: this.filterUtilitiesService.encodeSeriesFilter(this.filterV2!),
|
// filter: this.filterUtilitiesService.encodeSeriesFilter(this.filterV2!),
|
||||||
name: event.value,
|
// name: event.value,
|
||||||
} as SmartFilter,
|
// } as SmartFilter,
|
||||||
label: event.value
|
// label: event.value
|
||||||
};
|
// };
|
||||||
this.smartFilters.push(item);
|
// this.smartFilters.push(item);
|
||||||
this.sortGroup.get('name')?.setValue(item);
|
// this.sortGroup.get('name')?.setValue(item);
|
||||||
this.cdRef.markForCheck();
|
// this.cdRef.markForCheck();
|
||||||
this.toastr.success(translate('toasts.smart-filter-updated'));
|
// this.toastr.success(translate('toasts.smart-filter-updated'));
|
||||||
this.apply();
|
// this.apply();
|
||||||
});
|
// });
|
||||||
|
//
|
||||||
console.log('create event: ', event);
|
// console.log('create event: ', event);
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
|
@ -209,9 +209,9 @@ export class FilterUtilitiesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
decodeFilterStatements(encodedStatements: string): FilterStatement[] {
|
decodeFilterStatements(encodedStatements: string): FilterStatement[] {
|
||||||
const statementStrings = decodeURIComponent(encodedStatements).split(',');
|
const statementStrings = decodeURIComponent(encodedStatements).split(',').map(s => decodeURIComponent(s));
|
||||||
return statementStrings.map(statementString => {
|
return statementStrings.map(statementString => {
|
||||||
const parts = statementString.split('&');
|
const parts = statementString.split(',');
|
||||||
if (parts === null || parts.length < 3) return null;
|
if (parts === null || parts.length < 3) return null;
|
||||||
|
|
||||||
const comparisonStartToken = parts.find(part => part.startsWith('comparison='));
|
const comparisonStartToken = parts.find(part => part.startsWith('comparison='));
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
<ng-container [ngSwitch]="navStream.streamType">
|
<ng-container [ngSwitch]="navStream.streamType">
|
||||||
<ng-container *ngSwitchCase="SideNavStreamType.Library">
|
<ng-container *ngSwitchCase="SideNavStreamType.Library">
|
||||||
<app-side-nav-item [link]="'/library/' + navStream.libraryId + '/'"
|
<app-side-nav-item [link]="'/library/' + navStream.libraryId + '/'"
|
||||||
[icon]="getLibraryTypeIcon(navStream.library!.type)" [imageUrl]="getLibraryImage(navStream.library!)" [title]="navStream.name" [comparisonMethod]="'startsWith'">
|
[icon]="getLibraryTypeIcon(navStream.library!.type)" [imageUrl]="getLibraryImage(navStream.library!)" [title]="navStream.library!.name" [comparisonMethod]="'startsWith'">
|
||||||
<ng-container actions>
|
<ng-container actions>
|
||||||
<app-card-actionables [actions]="actions" [labelBy]="navStream.name" iconClass="fa-ellipsis-v"
|
<app-card-actionables [actions]="actions" [labelBy]="navStream.name" iconClass="fa-ellipsis-v"
|
||||||
(actionHandler)="performAction($event, navStream.library!)"></app-card-actionables>
|
(actionHandler)="performAction($event, navStream.library!)"></app-card-actionables>
|
||||||
|
@ -8,7 +8,7 @@ import {
|
|||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import { NavigationEnd, Router } from '@angular/router';
|
import { NavigationEnd, Router } from '@angular/router';
|
||||||
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
|
||||||
import {distinctUntilChanged, filter, map, shareReplay, take, tap} from 'rxjs/operators';
|
import {distinctUntilChanged, filter, map, take, tap} from 'rxjs/operators';
|
||||||
import { ImportCblModalComponent } from 'src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component';
|
import { ImportCblModalComponent } from 'src/app/reading-list/_modals/import-cbl-modal/import-cbl-modal.component';
|
||||||
import { ImageService } from 'src/app/_services/image.service';
|
import { ImageService } from 'src/app/_services/image.service';
|
||||||
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
|
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
|
||||||
@ -17,7 +17,6 @@ import { Library, LibraryType } from '../../../_models/library';
|
|||||||
import { AccountService } from '../../../_services/account.service';
|
import { AccountService } from '../../../_services/account.service';
|
||||||
import { Action, ActionFactoryService, ActionItem } from '../../../_services/action-factory.service';
|
import { Action, ActionFactoryService, ActionItem } from '../../../_services/action-factory.service';
|
||||||
import { ActionService } from '../../../_services/action.service';
|
import { ActionService } from '../../../_services/action.service';
|
||||||
import { LibraryService } from '../../../_services/library.service';
|
|
||||||
import { NavService } from '../../../_services/nav.service';
|
import { NavService } from '../../../_services/nav.service';
|
||||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||||
import {BehaviorSubject, merge, Observable, of, ReplaySubject, startWith, switchMap} from "rxjs";
|
import {BehaviorSubject, merge, Observable, of, ReplaySubject, startWith, switchMap} from "rxjs";
|
||||||
@ -56,7 +55,18 @@ export class SideNavComponent implements OnInit {
|
|||||||
}
|
}
|
||||||
showAll: boolean = false;
|
showAll: boolean = false;
|
||||||
totalSize = 0;
|
totalSize = 0;
|
||||||
|
|
||||||
protected readonly SideNavStreamType = SideNavStreamType;
|
protected readonly SideNavStreamType = SideNavStreamType;
|
||||||
|
private readonly router = inject(Router);
|
||||||
|
private readonly utilityService = inject(UtilityService);
|
||||||
|
private readonly messageHub = inject(MessageHubService);
|
||||||
|
private readonly actionService = inject(ActionService);
|
||||||
|
public readonly navService = inject(NavService);
|
||||||
|
private readonly cdRef = inject(ChangeDetectorRef);
|
||||||
|
private readonly ngbModal = inject(NgbModal);
|
||||||
|
private readonly imageService = inject(ImageService);
|
||||||
|
public readonly accountService = inject(AccountService);
|
||||||
|
|
||||||
|
|
||||||
private showAllSubject = new BehaviorSubject<boolean>(false);
|
private showAllSubject = new BehaviorSubject<boolean>(false);
|
||||||
showAll$ = this.showAllSubject.asObservable();
|
showAll$ = this.showAllSubject.asObservable();
|
||||||
@ -111,25 +121,22 @@ export class SideNavComponent implements OnInit {
|
|||||||
takeUntilDestroyed(this.destroyRef),
|
takeUntilDestroyed(this.destroyRef),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
collapseSideNavOnMobileNav$ = this.router.events.pipe(
|
||||||
|
filter(event => event instanceof NavigationEnd),
|
||||||
|
takeUntilDestroyed(this.destroyRef),
|
||||||
|
map(evt => evt as NavigationEnd),
|
||||||
|
filter(() => this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet),
|
||||||
|
switchMap(() => this.navService.sideNavCollapsed$),
|
||||||
|
take(1),
|
||||||
|
filter(collapsed => !collapsed)
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
|
||||||
public utilityService: UtilityService, private messageHub: MessageHubService,
|
|
||||||
private actionService: ActionService,
|
|
||||||
public navService: NavService, private router: Router, private readonly cdRef: ChangeDetectorRef,
|
|
||||||
private ngbModal: NgbModal, private imageService: ImageService, public readonly accountService: AccountService) {
|
|
||||||
|
|
||||||
this.router.events.pipe(
|
constructor() {
|
||||||
filter(event => event instanceof NavigationEnd),
|
this.collapseSideNavOnMobileNav$.subscribe(() => {
|
||||||
takeUntilDestroyed(this.destroyRef),
|
|
||||||
map(evt => evt as NavigationEnd),
|
|
||||||
filter(() => this.utilityService.getActiveBreakpoint() < Breakpoint.Tablet),
|
|
||||||
switchMap(() => this.navService.sideNavCollapsed$),
|
|
||||||
take(1),
|
|
||||||
filter(collapsed => !collapsed)
|
|
||||||
).subscribe(() => {
|
|
||||||
this.navService.toggleSideNav();
|
this.navService.toggleSideNav();
|
||||||
this.cdRef.markForCheck();
|
this.cdRef.markForCheck();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
|
110
openapi.json
110
openapi.json
@ -7,7 +7,7 @@
|
|||||||
"name": "GPL-3.0",
|
"name": "GPL-3.0",
|
||||||
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
"url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE"
|
||||||
},
|
},
|
||||||
"version": "0.7.9.4"
|
"version": "0.7.10.0"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
{
|
{
|
||||||
@ -4137,6 +4137,15 @@
|
|||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int32"
|
"format": "int32"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "saveProgress",
|
||||||
|
"in": "query",
|
||||||
|
"description": "Optional parameter. Can pass false and progress saving will be suppressed",
|
||||||
|
"schema": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"responses": {
|
"responses": {
|
||||||
@ -4168,6 +4177,101 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/Panels/save-progress": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Panels"
|
||||||
|
],
|
||||||
|
"summary": "Saves the progress of a given chapter.",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"in": "query",
|
||||||
|
"description": "",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"description": "",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProgressDto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProgressDto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/*+json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ProgressDto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/Panels/get-progress": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Panels"
|
||||||
|
],
|
||||||
|
"summary": "Gets the Progress of a given chapter",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "chapterId",
|
||||||
|
"in": "query",
|
||||||
|
"description": "",
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "apiKey",
|
||||||
|
"in": "query",
|
||||||
|
"description": "",
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "Success",
|
||||||
|
"content": {
|
||||||
|
"text/plain": {
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"text/json": {
|
||||||
|
"schema": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "int32"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/Plugin/authenticate": {
|
"/api/Plugin/authenticate": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@ -19687,6 +19791,10 @@
|
|||||||
"name": "Image",
|
"name": "Image",
|
||||||
"description": "Responsible for servicing up images stored in Kavita for entities"
|
"description": "Responsible for servicing up images stored in Kavita for entities"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "Panels",
|
||||||
|
"description": "For the Panels app explicitly"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Rating",
|
"name": "Rating",
|
||||||
"description": "Responsible for providing external ratings for Series"
|
"description": "Responsible for providing external ratings for Series"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user