mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
New UX Part 1.5 (#3105)
This commit is contained in:
parent
c188e0f23b
commit
ac21b04fa4
29
API/Controllers/ChapterController.cs
Normal file
29
API/Controllers/ChapterController.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Threading.Tasks;
|
||||
using API.Data;
|
||||
using API.Data.Repositories;
|
||||
using API.DTOs;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace API.Controllers;
|
||||
|
||||
public class ChapterController : BaseApiController
|
||||
{
|
||||
private readonly IUnitOfWork _unitOfWork;
|
||||
|
||||
public ChapterController(IUnitOfWork unitOfWork)
|
||||
{
|
||||
_unitOfWork = unitOfWork;
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<ActionResult<ChapterDto>> GetChapter(int chapterId)
|
||||
{
|
||||
var chapter =
|
||||
await _unitOfWork.ChapterRepository.GetChapterDtoAsync(chapterId,
|
||||
ChapterIncludes.People | ChapterIncludes.Files);
|
||||
|
||||
return Ok(chapter);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -229,22 +229,25 @@ public class SeriesController : BaseApiController
|
||||
{
|
||||
// Trigger a refresh when we are moving from a locked image to a non-locked
|
||||
needsRefreshMetadata = true;
|
||||
series.CoverImage = string.Empty;
|
||||
series.CoverImage = null;
|
||||
series.CoverImageLocked = updateSeries.CoverImageLocked;
|
||||
series.ResetColorScape();
|
||||
|
||||
}
|
||||
|
||||
_unitOfWork.SeriesRepository.Update(series);
|
||||
|
||||
if (await _unitOfWork.CommitAsync())
|
||||
if (!await _unitOfWork.CommitAsync())
|
||||
{
|
||||
if (needsRefreshMetadata)
|
||||
{
|
||||
_taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id);
|
||||
}
|
||||
return Ok();
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-series-update"));
|
||||
}
|
||||
|
||||
return BadRequest(await _localizationService.Translate(User.GetUserId(), "generic-series-update"));
|
||||
if (needsRefreshMetadata)
|
||||
{
|
||||
_taskScheduler.RefreshSeriesMetadata(series.LibraryId, series.Id);
|
||||
}
|
||||
|
||||
return Ok();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -377,6 +377,7 @@ public class UploadController : BaseApiController
|
||||
if (string.IsNullOrEmpty(uploadFileDto.Url))
|
||||
{
|
||||
library.CoverImage = null;
|
||||
library.ResetColorScape();
|
||||
_unitOfWork.LibraryRepository.Update(library);
|
||||
if (_unitOfWork.HasChanges())
|
||||
{
|
||||
|
@ -163,4 +163,10 @@ public class ChapterDto : IHasReadTimeEstimate, IHasCoverImage
|
||||
public string CoverImage { get; set; }
|
||||
public string PrimaryColor { get; set; }
|
||||
public string SecondaryColor { get; set; }
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ public class AppUserCollectionDto : IHasCoverImage
|
||||
/// </summary>
|
||||
public string? CoverImage { get; set; } = string.Empty;
|
||||
|
||||
public string PrimaryColor { get; set; }
|
||||
public string SecondaryColor { get; set; }
|
||||
public string PrimaryColor { get; set; } = string.Empty;
|
||||
public string SecondaryColor { get; set; } = string.Empty;
|
||||
public bool CoverImageLocked { get; set; }
|
||||
|
||||
/// <summary>
|
||||
@ -48,4 +48,10 @@ public class AppUserCollectionDto : IHasCoverImage
|
||||
/// A <br/> separated string of all missing series
|
||||
/// </summary>
|
||||
public string? MissingSeriesFromSource { get; set; }
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -19,8 +19,8 @@ public class ReadingListDto : IHasCoverImage
|
||||
/// </summary>
|
||||
public string? CoverImage { get; set; } = string.Empty;
|
||||
|
||||
public string PrimaryColor { get; set; }
|
||||
public string SecondaryColor { get; set; }
|
||||
public string PrimaryColor { get; set; } = string.Empty;
|
||||
public string SecondaryColor { get; set; } = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum Year the Reading List starts
|
||||
@ -39,4 +39,10 @@ public class ReadingListDto : IHasCoverImage
|
||||
/// </summary>
|
||||
public int EndingMonth { get; set; }
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -66,4 +66,10 @@ public class SeriesDto : IHasReadTimeEstimate, IHasCoverImage
|
||||
public string? CoverImage { get; set; }
|
||||
public string PrimaryColor { get; set; }
|
||||
public string SecondaryColor { get; set; }
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -66,4 +66,10 @@ public class VolumeDto : IHasReadTimeEstimate, IHasCoverImage
|
||||
public string CoverImage { get; set; }
|
||||
public string PrimaryColor { get; set; }
|
||||
public string SecondaryColor { get; set; }
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -59,6 +59,12 @@ public class AppUserCollection : IEntityDate, IHasCoverImage
|
||||
/// </summary>
|
||||
public string? MissingSeriesFromSource { get; set; }
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
|
||||
// Relationship
|
||||
public AppUser AppUser { get; set; } = null!;
|
||||
public int AppUserId { get; set; }
|
||||
|
@ -193,4 +193,10 @@ public class Chapter : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
|
||||
{
|
||||
return MinNumber.Is(Parser.DefaultChapterNumber) && !IsSpecial;
|
||||
}
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
namespace API.Entities.Interfaces;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public interface IHasCoverImage
|
||||
{
|
||||
/// <summary>
|
||||
@ -16,4 +18,9 @@ public interface IHasCoverImage
|
||||
/// Secondary color derived from the Cover Image
|
||||
/// </summary>
|
||||
public string? SecondaryColor { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Nulls out the ColorScape properties
|
||||
/// </summary>
|
||||
void ResetColorScape();
|
||||
}
|
||||
|
@ -80,4 +80,10 @@ public class Library : IEntityDate, IHasCoverImage
|
||||
LastScanned = (DateTime) time;
|
||||
}
|
||||
}
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -59,4 +59,10 @@ public class ReadingList : IEntityDate, IHasCoverImage
|
||||
// Relationships
|
||||
public int AppUserId { get; set; }
|
||||
public AppUser AppUser { get; set; } = null!;
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -145,4 +145,10 @@ public class Series : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
|
||||
NormalizedName == localizedNameNormalized ||
|
||||
NormalizedLocalizedName == localizedNameNormalized;
|
||||
}
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
}
|
||||
|
@ -73,4 +73,10 @@ public class Volume : IEntityDate, IHasReadTimeEstimate, IHasCoverImage
|
||||
return $"{MinNumber}-{MaxNumber}";
|
||||
}
|
||||
|
||||
public void ResetColorScape()
|
||||
{
|
||||
PrimaryColor = string.Empty;
|
||||
SecondaryColor = string.Empty;
|
||||
}
|
||||
|
||||
}
|
||||
|
168
UI/Web/package-lock.json
generated
168
UI/Web/package-lock.json
generated
@ -21,13 +21,13 @@
|
||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||
"@iharbeck/ngx-virtual-scroller": "^17.0.2",
|
||||
"@iplab/ngx-file-upload": "^17.1.0",
|
||||
"@jsverse/transloco": "^7.4.3",
|
||||
"@jsverse/transloco-locale": "^7.0.1",
|
||||
"@jsverse/transloco-persist-lang": "^7.0.1",
|
||||
"@jsverse/transloco-persist-translations": "^7.0.1",
|
||||
"@jsverse/transloco-preload-langs": "^7.0.1",
|
||||
"@microsoft/signalr": "^7.0.14",
|
||||
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
|
||||
"@ngneat/transloco": "^6.0.4",
|
||||
"@ngneat/transloco-locale": "^5.1.2",
|
||||
"@ngneat/transloco-persist-lang": "^5.0.0",
|
||||
"@ngneat/transloco-persist-translations": "^5.0.0",
|
||||
"@ngneat/transloco-preload-langs": "^5.0.1",
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"@tweenjs/tween.js": "^23.1.1",
|
||||
@ -3258,6 +3258,85 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsverse/transloco": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/@jsverse/transloco/-/transloco-7.4.3.tgz",
|
||||
"integrity": "sha512-QVzpbsfMN4oB01OfiGBz0f9/cw6nczF2EHIlhJG0455bMjiaR/tQTVGFmAGnm267iQFPtOL36yQyaHznXxPaqw==",
|
||||
"dependencies": {
|
||||
"@jsverse/transloco-utils": "^7.0.0",
|
||||
"flat": "6.0.1",
|
||||
"fs-extra": "^11.0.0",
|
||||
"glob": "^10.0.0",
|
||||
"lodash.kebabcase": "^4.1.1",
|
||||
"ora": "^5.4.1",
|
||||
"replace-in-file": "^7.0.1",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsverse/transloco-locale": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@jsverse/transloco-locale/-/transloco-locale-7.0.1.tgz",
|
||||
"integrity": "sha512-mx43h2FKMKxx+Er18qArBJMxmGGW2+EShkH+xueAp+VC/ivBNQDyXWpg8hOsfNFqFQAjzlCAie1mXpbGmbM0uw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@jsverse/transloco": ">=7.0.0",
|
||||
"rxjs": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsverse/transloco-persist-lang": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@jsverse/transloco-persist-lang/-/transloco-persist-lang-7.0.1.tgz",
|
||||
"integrity": "sha512-bCH5aECb6d/NbS3/oiTqgbWrMIzo1kJzJTOVF2uazZeMa8M4xcx1Am+cX/Fo4gxAFrrTLmI4yC2dde8JB/qdCA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@jsverse/transloco": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsverse/transloco-persist-translations": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@jsverse/transloco-persist-translations/-/transloco-persist-translations-7.0.1.tgz",
|
||||
"integrity": "sha512-BUGpcD4MrIBUbo7/G06yGdkWuVTKXVESyAJp107yUbE34Ami0+4BEK7vfLTl09ARwhBQsNKIzZgTAIpzrlK98A==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@jsverse/transloco": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsverse/transloco-preload-langs": {
|
||||
"version": "7.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@jsverse/transloco-preload-langs/-/transloco-preload-langs-7.0.1.tgz",
|
||||
"integrity": "sha512-J9G+r9g8UnLWsEdf0XTUhSIX/CFoKEPP6bEfyXQ7f36FFVu3raPRoEXnqE8gQGCPiyFPG0J8YSf7lyJtUHIgHA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@jsverse/transloco": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jsverse/transloco-utils": {
|
||||
"version": "7.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@jsverse/transloco-utils/-/transloco-utils-7.0.2.tgz",
|
||||
"integrity": "sha512-zud1M68mMC/Pu6irEba+Z2SzmwmmPzUPnBzMKlcGdIhzUe1z41cqQutK1M0QaQpY4h4yhumXcNaY/Ot6piv6QQ==",
|
||||
"dependencies": {
|
||||
"cosmiconfig": "^8.1.3",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@leichtgewicht/ip-codec": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz",
|
||||
@ -3304,85 +3383,6 @@
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/transloco": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/transloco/-/transloco-6.0.4.tgz",
|
||||
"integrity": "sha512-hQSPdmzuxJIu2SBwvoiwjoUjxSnUGFyCOkJnV8IwzzmBSdgQxqMMci5WXg/bQeCYggA+RyXpUjjTudEvkWy5Rw==",
|
||||
"dependencies": {
|
||||
"@ngneat/transloco-utils": "^5.0.0",
|
||||
"flat": "6.0.1",
|
||||
"fs-extra": "^11.0.0",
|
||||
"glob": "^10.0.0",
|
||||
"lodash.kebabcase": "^4.1.1",
|
||||
"ora": "^5.4.1",
|
||||
"replace-in-file": "^7.0.1",
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/transloco-locale": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/transloco-locale/-/transloco-locale-5.1.2.tgz",
|
||||
"integrity": "sha512-lIEW9rjpxamXyk39kGSykR6rEbVF/Fifvp62L/8eb18X9R0quPR4YnCCkAdioZvTX2EG2tgcNWvOD2fxdgxvlQ==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=13.0.0",
|
||||
"@ngneat/transloco": ">=4.0.0",
|
||||
"rxjs": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/transloco-persist-lang": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/transloco-persist-lang/-/transloco-persist-lang-5.0.0.tgz",
|
||||
"integrity": "sha512-vBpHQqTeKZT+V+uvIIEv+KyCq+8HFkCa7lnjvWwcgGupSYjTvZp4PxUm+KOLLmaTIzJDL1OQEaszQ84EzX6Mzg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@ngneat/transloco": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/transloco-persist-translations": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/transloco-persist-translations/-/transloco-persist-translations-5.0.0.tgz",
|
||||
"integrity": "sha512-QLM9X9aDRPLZhNK8f8h/4eqjhSJvHoGHRSQ+CoS3qkOXteEdOQXeYzWPHSmvDHc5lN3zNRy6sjHrBQEiZQLCKw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@ngneat/transloco": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/transloco-preload-langs": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/transloco-preload-langs/-/transloco-preload-langs-5.0.1.tgz",
|
||||
"integrity": "sha512-+HDsEtBCFTD8YY31VX9N0dPcVp/CozxmcHXTvqjJ3M0BEkkygZIoiTQwaOPiJziNjFKl8FRhAvovWVV/t8hd8g==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": ">=16.0.0",
|
||||
"@ngneat/transloco": ">=5.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngneat/transloco-utils": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@ngneat/transloco-utils/-/transloco-utils-5.0.0.tgz",
|
||||
"integrity": "sha512-e0S+GWyBTmLix9KfYWW/rScYdqQz3z3znNSb+foaA5T3jWs4CPLVo+PV0No7kGjqom8Wy8H3lLvztfhHxYSLyA==",
|
||||
"dependencies": {
|
||||
"cosmiconfig": "^8.1.3",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@ngtools/webpack": {
|
||||
"version": "17.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-17.3.4.tgz",
|
||||
|
@ -28,13 +28,13 @@
|
||||
"@fortawesome/fontawesome-free": "^6.5.2",
|
||||
"@iharbeck/ngx-virtual-scroller": "^17.0.2",
|
||||
"@iplab/ngx-file-upload": "^17.1.0",
|
||||
"@jsverse/transloco": "^7.4.3",
|
||||
"@jsverse/transloco-locale": "^7.0.1",
|
||||
"@jsverse/transloco-persist-lang": "^7.0.1",
|
||||
"@jsverse/transloco-persist-translations": "^7.0.1",
|
||||
"@jsverse/transloco-preload-langs": "^7.0.1",
|
||||
"@microsoft/signalr": "^7.0.14",
|
||||
"@ng-bootstrap/ng-bootstrap": "^16.0.0",
|
||||
"@ngneat/transloco": "^6.0.4",
|
||||
"@ngneat/transloco-locale": "^5.1.2",
|
||||
"@ngneat/transloco-persist-lang": "^5.0.0",
|
||||
"@ngneat/transloco-persist-translations": "^5.0.0",
|
||||
"@ngneat/transloco-preload-langs": "^5.0.1",
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"@tweenjs/tween.js": "^23.1.1",
|
||||
|
@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -4,7 +4,7 @@ import { ToastrService } from 'ngx-toastr';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map, take } from 'rxjs/operators';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
@ -10,7 +10,7 @@ import { Router } from '@angular/router';
|
||||
import { ToastrService } from 'ngx-toastr';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { AccountService } from '../_services/account.service';
|
||||
import {translate, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Injectable()
|
||||
export class ErrorInterceptor implements HttpInterceptor {
|
||||
|
@ -80,6 +80,6 @@ export interface Chapter {
|
||||
teams: Array<Person>;
|
||||
locations: Array<Person>;
|
||||
|
||||
primaryColor?: string;
|
||||
secondaryColor?: string;
|
||||
primaryColor: string;
|
||||
secondaryColor: string;
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { Observable, of } from 'rxjs';
|
||||
import { AgeRating } from '../_models/metadata/age-rating';
|
||||
import { AgeRatingDto } from '../_models/metadata/age-rating-dto';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'ageRating',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {BookPageLayoutMode} from "../_models/readers/book-page-layout-mode";
|
||||
|
||||
@Pipe({
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { CblBookResult } from 'src/app/_models/reading-list/cbl/cbl-book-result';
|
||||
import { CblImportReason } from 'src/app/_models/reading-list/cbl/cbl-import-reason.enum';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
const failIcon = '<i aria-hidden="true" class="reading-list-fail--item fa-solid fa-circle-xmark me-1"></i>';
|
||||
const successIcon = '<i aria-hidden="true" class="reading-list-success--item fa-solid fa-circle-check me-1"></i>';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { CblImportResult } from 'src/app/_models/reading-list/cbl/cbl-import-result.enum';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'cblImportResult',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {CoverImageSize} from "../admin/_models/cover-image-size";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'coverImageSize',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import { DayOfWeek } from 'src/app/_services/statistics.service';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'dayOfWeek',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'defaultDate',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { DevicePlatform } from 'src/app/_models/device/device-platform';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'devicePlatform',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {FileTypeGroup} from "../_models/library/file-type-group.enum";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'fileTypeGroup',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { FilterComparison } from 'src/app/_models/metadata/v2/filter-comparison';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'filterComparison',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import { FilterField } from 'src/app/_models/metadata/v2/filter-field';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'filterField',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {LayoutMode} from "../manga-reader/_models/layout-mode";
|
||||
|
||||
@Pipe({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { LibraryType } from '../_models/library/library';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
/**
|
||||
* Returns the name of the LibraryType
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import { MangaFormat } from '../_models/manga-format';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
/**
|
||||
* Returns the string name for the format
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {PageLayoutMode} from "../_models/page-layout-mode";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'pageLayoutMode',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {PageSplitOption} from "../_models/preferences/page-split-option";
|
||||
|
||||
@Pipe({
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {PdfScrollMode} from "../_models/preferences/pdf-scroll-mode";
|
||||
|
||||
@Pipe({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {PdfSpreadMode} from "../_models/preferences/pdf-spread-mode";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'pdfSpreadMode',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {PdfTheme} from "../_models/preferences/pdf-theme";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'pdfTheme',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { PersonRole } from '../_models/metadata/person';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'personRole',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import { PublicationStatus } from '../_models/metadata/publication-status';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'publicationStatus',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {ReadingDirection} from "../_models/preferences/reading-direction";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'readingDirection',
|
||||
@ -11,7 +11,7 @@ export class ReadingDirectionPipe implements PipeTransform {
|
||||
transform(value: ReadingDirection): string {
|
||||
switch (value) {
|
||||
case ReadingDirection.LeftToRight: return translate('preferences.left-to-right');
|
||||
case ReadingDirection.RightToLeft: return translate('preferences.right-to-right');
|
||||
case ReadingDirection.RightToLeft: return translate('preferences.right-to-left');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {ReaderMode} from "../_models/preferences/reader-mode";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'readerMode',
|
||||
@ -10,7 +10,7 @@ export class ReaderModePipe implements PipeTransform {
|
||||
|
||||
transform(value: ReaderMode): string {
|
||||
switch (value) {
|
||||
case ReaderMode.UpDown: return translate('preferences.up-down');
|
||||
case ReaderMode.UpDown: return translate('preferences.up-to-down');
|
||||
case ReaderMode.Webtoon: return translate('preferences.webtoon');
|
||||
case ReaderMode.LeftRight: return translate('preferences.left-to-right');
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { RelationKind } from '../_models/series-detail/relation-kind';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'relationship',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {ScalingOption} from "../_models/preferences/scaling-option";
|
||||
|
||||
@Pipe({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import {ScrobbleEventType} from "../_models/scrobbling/scrobble-event";
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'scrobbleEventType',
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {SettingsTabId} from "../sidenav/preference-nav/preference-nav.component";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
/**
|
||||
* Translates the fragment for Settings to a User title
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import { ThemeProvider } from 'src/app/_models/preferences/site-theme';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
|
||||
@Pipe({
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {SortField} from "../_models/metadata/series-filter";
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'sortField',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
@Pipe({
|
||||
name: 'streamName',
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {ChangeDetectorRef, NgZone, OnDestroy, Pipe, PipeTransform} from '@angular/core';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
/**
|
||||
* MIT License
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {inject, Pipe, PipeTransform} from '@angular/core';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
|
||||
/**
|
||||
* Converts hours -> days, months, years, etc
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {WritingStyle} from "../_models/preferences/writing-style";
|
||||
|
||||
@Pipe({
|
||||
|
@ -11,6 +11,7 @@ import { AccountService } from './account.service';
|
||||
import { DeviceService } from './device.service';
|
||||
import {SideNavStream} from "../_models/sidenav/sidenav-stream";
|
||||
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
|
||||
export enum Action {
|
||||
Submenu = -1,
|
||||
@ -117,6 +118,7 @@ export type ActionAllowedCallback<T> = (action: ActionItem<T>) => boolean;
|
||||
|
||||
export interface ActionItem<T> {
|
||||
title: string;
|
||||
description: string;
|
||||
action: Action;
|
||||
callback: ActionCallback<T>;
|
||||
requiresAdmin: boolean;
|
||||
@ -208,15 +210,6 @@ export class ActionFactoryService {
|
||||
return this.applyCallbackToList(this.bookmarkActions, callback);
|
||||
}
|
||||
|
||||
getMetadataFilterActions(callback: ActionCallback<any>) {
|
||||
const actions = [
|
||||
{title: 'add-rule-group-and', action: Action.AddRuleGroup, requiresAdmin: false, children: [], callback: this.dummyCallback},
|
||||
{title: 'add-rule-group-or', action: Action.AddRuleGroup, requiresAdmin: false, children: [], callback: this.dummyCallback},
|
||||
{title: 'remove-rule-group', action: Action.RemoveRuleGroup, requiresAdmin: false, children: [], callback: this.dummyCallback},
|
||||
];
|
||||
return this.applyCallbackToList(actions, callback);
|
||||
}
|
||||
|
||||
dummyCallback(action: ActionItem<any>, data: any) {}
|
||||
|
||||
filterSendToAction(actions: Array<ActionItem<Chapter>>, chapter: Chapter) {
|
||||
@ -227,11 +220,44 @@ export class ActionFactoryService {
|
||||
return actions;
|
||||
}
|
||||
|
||||
getActionablesForSettingsPage(actions: Array<ActionItem<any>>, blacklist: Array<Action> = []) {
|
||||
const tasks = [];
|
||||
|
||||
let actionItem;
|
||||
for (let parent of actions) {
|
||||
if (parent.action === Action.SendTo) continue;
|
||||
|
||||
if (parent.children.length === 0) {
|
||||
actionItem = {...parent};
|
||||
actionItem.title = translate('actionable.' + actionItem.title);
|
||||
if (actionItem.description !== '') {
|
||||
actionItem.description = translate('actionable.' + actionItem.description);
|
||||
}
|
||||
|
||||
tasks.push(actionItem);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (let child of parent.children) {
|
||||
actionItem = {...child};
|
||||
actionItem.title = translate('actionable.' + actionItem.title);
|
||||
if (actionItem.description !== '') {
|
||||
actionItem.description = translate('actionable.' + actionItem.description);
|
||||
}
|
||||
tasks.push(actionItem);
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out tasks that don't make sense
|
||||
return tasks.filter(t => !blacklist.includes(t.action));
|
||||
}
|
||||
|
||||
private _resetActions() {
|
||||
this.libraryActions = [
|
||||
{
|
||||
action: Action.Scan,
|
||||
title: 'scan-library',
|
||||
description: 'scan-library-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -239,12 +265,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'others',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [
|
||||
{
|
||||
action: Action.RefreshMetadata,
|
||||
title: 'refresh-covers',
|
||||
description: 'refresh-covers-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -252,6 +280,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.GenerateColorScape,
|
||||
title: 'generate-colorscape',
|
||||
description: 'generate-colorscape-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -259,6 +288,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.AnalyzeFiles,
|
||||
title: 'analyze-files',
|
||||
description: 'analyze-files-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -266,6 +296,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Delete,
|
||||
title: 'delete',
|
||||
description: 'delete-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -275,6 +306,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'settings',
|
||||
description: 'settings-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -285,6 +317,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'edit',
|
||||
description: 'edit-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -292,6 +325,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Delete,
|
||||
title: 'delete',
|
||||
description: 'delete-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
class: 'danger',
|
||||
@ -300,6 +334,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Promote,
|
||||
title: 'promote',
|
||||
description: 'promote-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -307,6 +342,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.UnPromote,
|
||||
title: 'unpromote',
|
||||
description: 'unpromote-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -317,6 +353,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsRead,
|
||||
title: 'mark-as-read',
|
||||
description: 'mark-as-read-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -324,6 +361,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsUnread,
|
||||
title: 'mark-as-unread',
|
||||
description: 'mark-as-unread-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -331,6 +369,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Scan,
|
||||
title: 'scan-series',
|
||||
description: 'scan-series-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -338,12 +377,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'add-to',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.AddToWantToReadList,
|
||||
title: 'add-to-want-to-read',
|
||||
description: 'add-to-want-to-read-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -351,6 +392,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.RemoveFromWantToReadList,
|
||||
title: 'remove-from-want-to-read',
|
||||
description: 'remove-to-want-to-read-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -358,6 +400,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.AddToReadingList,
|
||||
title: 'add-to-reading-list',
|
||||
description: 'add-to-reading-list-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -365,6 +408,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.AddToCollection,
|
||||
title: 'add-to-collection',
|
||||
description: 'add-to-collection-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -374,12 +418,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'send-to',
|
||||
description: 'send-to-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.SendTo,
|
||||
title: '',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
dynamicList: this.deviceService.devices$.pipe(map((devices: Array<Device>) => devices.map(d => {
|
||||
@ -392,12 +438,22 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'others',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [
|
||||
{
|
||||
action: Action.RefreshMetadata,
|
||||
title: 'refresh-covers',
|
||||
description: 'refresh-covers-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
},
|
||||
{
|
||||
action: Action.GenerateColorScape,
|
||||
title: 'generate-colorscape',
|
||||
description: 'generate-colorscape-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -405,6 +461,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.AnalyzeFiles,
|
||||
title: 'analyze-files',
|
||||
description: 'analyze-files-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -412,6 +469,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Delete,
|
||||
title: 'delete',
|
||||
description: 'delete-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
class: 'danger',
|
||||
@ -422,6 +480,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Download,
|
||||
title: 'download',
|
||||
description: 'download-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -429,6 +488,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'edit',
|
||||
description: 'edit-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: true,
|
||||
children: [],
|
||||
@ -439,6 +499,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.IncognitoRead,
|
||||
title: 'read-incognito',
|
||||
description: 'read-incognito-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -446,6 +507,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsRead,
|
||||
title: 'mark-as-read',
|
||||
description: 'mark-as-read-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -453,6 +515,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsUnread,
|
||||
title: 'mark-as-unread',
|
||||
description: 'mark-as-unread-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -460,12 +523,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'add-to',
|
||||
description: '=',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.AddToReadingList,
|
||||
title: 'add-to-reading-list',
|
||||
description: 'add-to-reading-list-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -475,12 +540,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'send-to',
|
||||
description: 'send-to-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.SendTo,
|
||||
title: '',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
dynamicList: this.deviceService.devices$.pipe(map((devices: Array<Device>) => devices.map(d => {
|
||||
@ -493,6 +560,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Download,
|
||||
title: 'download',
|
||||
description: 'download-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -500,6 +568,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'details',
|
||||
description: 'edit-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -510,6 +579,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.IncognitoRead,
|
||||
title: 'read-incognito',
|
||||
description: 'read-incognito-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -517,6 +587,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsRead,
|
||||
title: 'mark-as-read',
|
||||
description: 'mark-as-read-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -524,6 +595,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsUnread,
|
||||
title: 'mark-as-unread',
|
||||
description: 'mark-as-unread-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -531,12 +603,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'add-to',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.AddToReadingList,
|
||||
title: 'add-to-reading-list',
|
||||
description: 'add-to-reading-list-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -546,12 +620,14 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Submenu,
|
||||
title: 'send-to',
|
||||
description: 'send-to-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [
|
||||
{
|
||||
action: Action.SendTo,
|
||||
title: '',
|
||||
description: '',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
dynamicList: this.deviceService.devices$.pipe(map((devices: Array<Device>) => devices.map(d => {
|
||||
@ -565,6 +641,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Download,
|
||||
title: 'download',
|
||||
description: 'download-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -572,6 +649,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'details',
|
||||
description: 'edit-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -582,6 +660,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Edit,
|
||||
title: 'edit',
|
||||
description: 'edit-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -589,6 +668,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Delete,
|
||||
title: 'delete',
|
||||
description: 'delete-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
class: 'danger',
|
||||
@ -597,6 +677,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Promote,
|
||||
title: 'promote',
|
||||
description: 'promote-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -604,6 +685,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.UnPromote,
|
||||
title: 'unpromote',
|
||||
description: 'unpromote-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -614,6 +696,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.ViewSeries,
|
||||
title: 'view-series',
|
||||
description: 'view-series-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -621,6 +704,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.DownloadBookmark,
|
||||
title: 'download',
|
||||
description: 'download-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -628,6 +712,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Delete,
|
||||
title: 'clear',
|
||||
description: 'delete-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
class: 'danger',
|
||||
requiresAdmin: false,
|
||||
@ -639,6 +724,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsVisible,
|
||||
title: 'mark-visible',
|
||||
description: 'mark-visible-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -646,6 +732,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.MarkAsInvisible,
|
||||
title: 'mark-invisible',
|
||||
description: 'mark-invisible-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
@ -656,6 +743,7 @@ export class ActionFactoryService {
|
||||
{
|
||||
action: Action.Delete,
|
||||
title: 'delete',
|
||||
description: 'delete-tooltip',
|
||||
callback: this.dummyCallback,
|
||||
requiresAdmin: false,
|
||||
children: [],
|
||||
|
@ -19,7 +19,7 @@ import { LibraryService } from './library.service';
|
||||
import { MemberService } from './member.service';
|
||||
import { ReaderService } from './reader.service';
|
||||
import { SeriesService } from './series.service';
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {UserCollection} from "../_models/collection-tag";
|
||||
import {CollectionTagService} from "./collection-tag.service";
|
||||
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
|
||||
@ -87,7 +87,7 @@ export class ActionService implements OnDestroy {
|
||||
* @param forceUpdate Optional Should we force
|
||||
* @returns
|
||||
*/
|
||||
async refreshMetadata(library: Partial<Library>, callback?: LibraryActionCallback, forceUpdate: boolean = true) {
|
||||
async refreshLibraryMetadata(library: Partial<Library>, callback?: LibraryActionCallback, forceUpdate: boolean = true) {
|
||||
if (!library.hasOwnProperty('id') || library.id === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -102,8 +102,11 @@ export class ActionService implements OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
const message = forceUpdate ? 'toasts.refresh-covers-queued' : 'toasts.generate-colorscape-queued';
|
||||
|
||||
this.libraryService.refreshMetadata(library?.id, forceUpdate).subscribe((res: any) => {
|
||||
this.toastr.info(translate('toasts.scan-queued', {name: library.name}));
|
||||
this.toastr.info(translate(message, {name: library.name}));
|
||||
|
||||
if (callback) {
|
||||
callback(library);
|
||||
}
|
||||
@ -226,17 +229,24 @@ export class ActionService implements OnDestroy {
|
||||
* Start a metadata refresh for a Series
|
||||
* @param series Series, must have libraryId, id and name populated
|
||||
* @param callback Optional callback to perform actions after API completes
|
||||
* @param forceUpdate If cache should be checked or not
|
||||
*/
|
||||
async refreshMetdata(series: Series, callback?: SeriesActionCallback) {
|
||||
if (!await this.confirmService.confirm(translate('toasts.confirm-regen-covers'))) {
|
||||
if (callback) {
|
||||
callback(series);
|
||||
async refreshSeriesMetadata(series: Series, callback?: SeriesActionCallback, forceUpdate: boolean = true) {
|
||||
|
||||
// Prompt the user if we are doing a forced call
|
||||
if (forceUpdate) {
|
||||
if (!await this.confirmService.confirm(translate('toasts.confirm-regen-covers'))) {
|
||||
if (callback) {
|
||||
callback(series);
|
||||
}
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.seriesService.refreshMetadata(series).pipe(take(1)).subscribe((res: any) => {
|
||||
this.toastr.info(translate('toasts.refresh-covers-queued', {name: series.name}));
|
||||
const message = forceUpdate ? 'toasts.refresh-covers-queued' : 'toasts.generate-colorscape-queued';
|
||||
|
||||
this.seriesService.refreshMetadata(series, forceUpdate).pipe(take(1)).subscribe((res: any) => {
|
||||
this.toastr.info(translate(message, {name: series.name}));
|
||||
if (callback) {
|
||||
callback(series);
|
||||
}
|
||||
|
22
UI/Web/src/app/_services/chapter.service.ts
Normal file
22
UI/Web/src/app/_services/chapter.service.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {environment} from "../../environments/environment";
|
||||
import {HttpClient} from "@angular/common/http";
|
||||
import {AccountService} from "./account.service";
|
||||
import {UserCollection} from "../_models/collection-tag";
|
||||
import {Chapter} from "../_models/chapter";
|
||||
import {HourEstimateRange} from "../_models/series-detail/hour-estimate-range";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class ChapterService {
|
||||
|
||||
baseUrl = environment.apiUrl;
|
||||
|
||||
constructor(private httpClient: HttpClient) { }
|
||||
|
||||
getChapterMetadata(chapterId: number) {
|
||||
return this.httpClient.get<Chapter>(this.baseUrl + 'chapter/?chapterId=' + chapterId);
|
||||
}
|
||||
|
||||
}
|
@ -143,8 +143,8 @@ export class SeriesService {
|
||||
}
|
||||
|
||||
|
||||
refreshMetadata(series: Series) {
|
||||
return this.httpClient.post(this.baseUrl + 'series/refresh-metadata', {libraryId: series.libraryId, seriesId: series.id});
|
||||
refreshMetadata(series: Series, force = true) {
|
||||
return this.httpClient.post(this.baseUrl + 'series/refresh-metadata', {libraryId: series.libraryId, seriesId: series.id, forceUpdate: force});
|
||||
}
|
||||
|
||||
scan(libraryId: number, seriesId: number, force = false) {
|
||||
|
@ -13,7 +13,7 @@ import { StatCount } from '../statistics/_models/stat-count';
|
||||
import { PublicationStatus } from '../_models/metadata/publication-status';
|
||||
import { MangaFormat } from '../_models/manga-format';
|
||||
import { TextResonse } from '../_types/text-response';
|
||||
import {TranslocoService} from "@ngneat/transloco";
|
||||
import {TranslocoService} from "@jsverse/transloco";
|
||||
import {KavitaPlusMetadataBreakdown} from "../statistics/_models/kavitaplus-metadata-breakdown";
|
||||
import {throttleTime} from "rxjs/operators";
|
||||
import {DEBOUNCE_TIME} from "../shared/_services/download.service";
|
||||
|
@ -19,7 +19,7 @@ import {SiteTheme, ThemeProvider} from '../_models/preferences/site-theme';
|
||||
import {TextResonse} from '../_types/text-response';
|
||||
import {EVENTS, MessageHubService} from './message-hub.service';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
import {translate} from "@jsverse/transloco";
|
||||
import {DownloadableSiteTheme} from "../_models/theme/downloadable-site-theme";
|
||||
import {NgxFileDropEntry} from "ngx-file-drop";
|
||||
import {SiteThemeUpdatedEvent} from "../_models/events/site-theme-updated-event";
|
||||
|
@ -12,7 +12,7 @@ import {NgbDropdown, NgbDropdownItem, NgbDropdownMenu, NgbDropdownToggle} from '
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { Action, ActionItem } from 'src/app/_services/action-factory.service';
|
||||
import {AsyncPipe, CommonModule, NgTemplateOutlet} from "@angular/common";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {DynamicListPipe} from "./_pipes/dynamic-list.pipe";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {ReactiveFormsModule} from "@angular/forms";
|
||||
import {UserReview} from "../review-card/user-review";
|
||||
import {SpoilerComponent} from "../spoiler/spoiler.component";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<ng-container *transloco="let t; read:'review-card'">
|
||||
<div class="card clickable mb-3" style="max-width: 320px; max-height: 160px; height: 160px" (click)="showModal()">
|
||||
<div class="card review-card clickable mb-3" (click)="showModal()">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-2 d-none d-md-block">
|
||||
<i class="img-fluid rounded-start fa-solid fa-circle-user profile-image" aria-hidden="true"></i>
|
||||
|
@ -1,3 +1,10 @@
|
||||
.review-card {
|
||||
max-width: 320px;
|
||||
max-height: 160px;
|
||||
height: 160px;
|
||||
width: 320px;
|
||||
}
|
||||
|
||||
.profile-image {
|
||||
font-size: 1.2rem;
|
||||
padding: 20px;
|
||||
|
@ -22,7 +22,7 @@ import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ScrobbleProvider} from "../../_services/scrobbling.service";
|
||||
|
||||
@Component({
|
||||
|
@ -12,7 +12,7 @@ import {NgbActiveModal, NgbRating} from '@ng-bootstrap/ng-bootstrap';
|
||||
import { SeriesService } from 'src/app/_services/series.service';
|
||||
import {UserReview} from "../review-card/user-review";
|
||||
import {CommonModule} from "@angular/common";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ConfirmService} from "../../shared/confirm.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
|
||||
import {CommonModule, NgOptimizedImage} from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {NgbActiveOffcanvas, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {ExternalSeriesDetail, SeriesStaff} from "../../_models/series-detail/external-series-detail";
|
||||
import {SeriesService} from "../../_services/series.service";
|
||||
|
@ -9,7 +9,7 @@ import {
|
||||
} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
|
||||
@Component({
|
||||
selector: 'app-spoiler',
|
||||
|
@ -11,9 +11,9 @@ import {debounceTime, take} from "rxjs/operators";
|
||||
import {PaginatedResult, Pagination} from "../../_models/pagination";
|
||||
import {SortableHeader, SortEvent} from "../table/_directives/sortable-header.directive";
|
||||
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
|
||||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {translate, TranslocoModule} from "@jsverse/transloco";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {TranslocoLocaleModule} from "@jsverse/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {LooseLeafOrDefaultNumber, SpecialVolumeNumber} from "../../_models/chapter";
|
||||
|
@ -6,7 +6,7 @@ import { DirectoryDto } from 'src/app/_models/system/directory-dto';
|
||||
import { LibraryService } from '../../../_services/library.service';
|
||||
import { NgIf, NgFor, NgClass } from '@angular/common';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {WikiLink} from "../../../_models/wiki";
|
||||
|
||||
|
||||
|
@ -5,7 +5,7 @@ import {Member} from 'src/app/_models/auth/member';
|
||||
import {LibraryService} from 'src/app/_services/library.service';
|
||||
import {NgFor, NgIf} from '@angular/common';
|
||||
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {SelectionModel} from "../../../typeahead/_models/selection-model";
|
||||
|
||||
@Component({
|
||||
|
@ -5,7 +5,7 @@ import { Member } from 'src/app/_models/auth/member';
|
||||
import { AccountService } from 'src/app/_services/account.service';
|
||||
import { SentenceCasePipe } from '../../../_pipes/sentence-case.pipe';
|
||||
import { NgIf } from '@angular/common';
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
|
||||
@Component({
|
||||
|
@ -10,7 +10,7 @@ import {RestrictionSelectorComponent} from '../../user-settings/restriction-sele
|
||||
import {LibrarySelectorComponent} from '../library-selector/library-selector.component';
|
||||
import {RoleSelectorComponent} from '../role-selector/role-selector.component';
|
||||
import {NgIf} from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
|
||||
const AllowedUsernameCharacters = /^[\sa-zA-Z0-9\-._@+/\s]*$/;
|
||||
|
||||
|
@ -12,7 +12,7 @@ import { RestrictionSelectorComponent } from '../../user-settings/restriction-se
|
||||
import { LibrarySelectorComponent } from '../library-selector/library-selector.component';
|
||||
import { RoleSelectorComponent } from '../role-selector/role-selector.component';
|
||||
import { NgIf } from '@angular/common';
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
|
||||
@Component({
|
||||
|
@ -12,7 +12,7 @@ import {FormsModule, ReactiveFormsModule} from '@angular/forms';
|
||||
import {Library} from 'src/app/_models/library/library';
|
||||
import {Member} from 'src/app/_models/auth/member';
|
||||
import {LibraryService} from 'src/app/_services/library.service';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {SelectionModel} from "../../typeahead/_models/selection-model";
|
||||
|
||||
|
@ -13,7 +13,7 @@ import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||
import { NgbTooltip, NgbCollapse } from '@ng-bootstrap/ng-bootstrap';
|
||||
import { NgIf } from '@angular/common';
|
||||
import {environment} from "../../../environments/environment";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {catchError} from "rxjs";
|
||||
import {WikiLink} from "../../_models/wiki";
|
||||
import {RouterLink} from "@angular/router";
|
||||
|
@ -89,9 +89,7 @@
|
||||
<app-setting-switch [title]="t('enable-ssl-label')">
|
||||
<ng-template #switch>
|
||||
<div class="form-check form-switch">
|
||||
<div class="form-check form-switch">
|
||||
<input id="setting-enable-ssl" type="checkbox" class="form-check-input" formControlName="enableOpds">
|
||||
</div>
|
||||
<input id="setting-enable-ssl" type="checkbox" class="form-check-input" formControlName="enableOpds">
|
||||
</div>
|
||||
</ng-template>
|
||||
</app-setting-switch>
|
||||
@ -142,9 +140,7 @@
|
||||
<app-setting-switch [title]="t('customized-templates-label')" [subtitle]="t('customized-templates-tooltip')">
|
||||
<ng-template #switch>
|
||||
<div class="form-check form-switch">
|
||||
<div class="form-check form-switch">
|
||||
<input id="settings-customized-templates" type="checkbox" class="form-check-input" formControlName="customizedTemplates">
|
||||
</div>
|
||||
<input id="settings-customized-templates" type="checkbox" class="form-check-input" formControlName="customizedTemplates">
|
||||
</div>
|
||||
</ng-template>
|
||||
</app-setting-switch>
|
||||
@ -154,8 +150,6 @@
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mt-4">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="test()">{{t('test')}}</button>
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetToDefaults()">{{t('reset-to-default')}}</button>
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="saveSettings()" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {take} from 'rxjs';
|
||||
import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs';
|
||||
import {SettingsService} from '../settings.service';
|
||||
import {ServerSettings} from '../_models/server-settings';
|
||||
import {
|
||||
@ -9,13 +9,14 @@ import {
|
||||
NgbTooltip
|
||||
} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
||||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {translate, TranslocoModule} from "@jsverse/transloco";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {ManageMediaIssuesComponent} from "../manage-media-issues/manage-media-issues.component";
|
||||
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
|
||||
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {BytesPipe} from "../../_pipes/bytes.pipe";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-email-settings',
|
||||
@ -31,6 +32,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
||||
private readonly cdRef = inject(ChangeDetectorRef);
|
||||
private readonly settingsService = inject(SettingsService);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
serverSettings!: ServerSettings;
|
||||
settingsForm: FormGroup = new FormGroup({});
|
||||
@ -50,6 +52,23 @@ export class ManageEmailSettingsComponent implements OnInit {
|
||||
this.settingsForm.addControl('sizeLimit', new FormControl(this.serverSettings.smtpConfig.sizeLimit, [Validators.min(1)]));
|
||||
this.settingsForm.addControl('customizedTemplates', new FormControl(this.serverSettings.smtpConfig.customizedTemplates, [Validators.min(1)]));
|
||||
|
||||
// Automatically save settings as we edit them
|
||||
this.settingsForm.valueChanges.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(100),
|
||||
filter(_ => this.settingsForm.valid),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
switchMap(_ => {
|
||||
const data = this.packData();
|
||||
return this.settingsService.updateServerSettings(data);
|
||||
}),
|
||||
tap(settings => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.cdRef.markForCheck();
|
||||
})
|
||||
).subscribe();
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
@ -57,15 +76,15 @@ export class ManageEmailSettingsComponent implements OnInit {
|
||||
resetForm() {
|
||||
this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName);
|
||||
|
||||
this.settingsForm.addControl('host', new FormControl(this.serverSettings.smtpConfig.host, []));
|
||||
this.settingsForm.addControl('port', new FormControl(this.serverSettings.smtpConfig.port, []));
|
||||
this.settingsForm.addControl('userName', new FormControl(this.serverSettings.smtpConfig.userName, []));
|
||||
this.settingsForm.addControl('enableSsl', new FormControl(this.serverSettings.smtpConfig.enableSsl, []));
|
||||
this.settingsForm.addControl('password', new FormControl(this.serverSettings.smtpConfig.password, []));
|
||||
this.settingsForm.addControl('senderAddress', new FormControl(this.serverSettings.smtpConfig.senderAddress, []));
|
||||
this.settingsForm.addControl('senderDisplayName', new FormControl(this.serverSettings.smtpConfig.senderDisplayName, []));
|
||||
this.settingsForm.addControl('sizeLimit', new FormControl(this.serverSettings.smtpConfig.sizeLimit, [Validators.min(1)]));
|
||||
this.settingsForm.addControl('customizedTemplates', new FormControl(this.serverSettings.smtpConfig.customizedTemplates, [Validators.min(1)]));
|
||||
this.settingsForm.get('host')?.setValue(this.serverSettings.smtpConfig.host, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('port')?.setValue(this.serverSettings.smtpConfig.port, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('userName')?.setValue(this.serverSettings.smtpConfig.userName, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('enableSsl')?.setValue(this.serverSettings.smtpConfig.enableSsl, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('password')?.setValue(this.serverSettings.smtpConfig.password, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('senderAddress')?.setValue(this.serverSettings.smtpConfig.senderAddress, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('senderDisplayName')?.setValue(this.serverSettings.smtpConfig.senderDisplayName, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('sizeLimit')?.setValue(this.serverSettings.smtpConfig.sizeLimit, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('customizedTemplates')?.setValue(this.serverSettings.smtpConfig.customizedTemplates, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.markAsPristine();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
@ -88,7 +107,7 @@ export class ManageEmailSettingsComponent implements OnInit {
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
packData() {
|
||||
const modelSettings = Object.assign({}, this.serverSettings);
|
||||
modelSettings.emailServiceUrl = this.settingsForm.get('emailServiceUrl')?.value;
|
||||
modelSettings.hostName = this.settingsForm.get('hostName')?.value;
|
||||
@ -103,6 +122,12 @@ export class ManageEmailSettingsComponent implements OnInit {
|
||||
modelSettings.smtpConfig.sizeLimit = this.settingsForm.get('sizeLimit')?.value;
|
||||
modelSettings.smtpConfig.customizedTemplates = this.settingsForm.get('customizedTemplates')?.value;
|
||||
|
||||
return modelSettings;
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
const modelSettings = this.packData();
|
||||
|
||||
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
|
@ -21,7 +21,7 @@ import { SentenceCasePipe } from '../../_pipes/sentence-case.pipe';
|
||||
import { TimeAgoPipe } from '../../_pipes/time-ago.pipe';
|
||||
import { LibraryTypePipe } from '../../_pipes/library-type.pipe';
|
||||
import { RouterLink } from '@angular/router';
|
||||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {translate, TranslocoModule} from "@jsverse/transloco";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {AsyncPipe, TitleCasePipe} from "@angular/common";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
@ -164,10 +164,10 @@ export class ManageLibraryComponent implements OnInit {
|
||||
await this.actionService.scanLibrary(library);
|
||||
break;
|
||||
case(Action.RefreshMetadata):
|
||||
await this.actionService.refreshMetadata(library);
|
||||
await this.actionService.refreshLibraryMetadata(library);
|
||||
break;
|
||||
case(Action.GenerateColorScape):
|
||||
await this.actionService.refreshMetadata(library, undefined, false);
|
||||
await this.actionService.refreshLibraryMetadata(library, undefined, false);
|
||||
break;
|
||||
case(Action.Edit):
|
||||
this.editLibrary(library)
|
||||
|
@ -19,7 +19,7 @@ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import { FilterPipe } from '../../_pipes/filter.pipe';
|
||||
import { LoadingComponent } from '../../shared/loading/loading.component';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {WikiLink} from "../../_models/wiki";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
|
@ -71,8 +71,6 @@
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetToDefaults()">{{t('reset-to-default')}}</button>
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="saveSettings()" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {take} from 'rxjs';
|
||||
import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from 'rxjs';
|
||||
import {SettingsService} from '../settings.service';
|
||||
import {ServerSettings} from '../_models/server-settings';
|
||||
import {DirectoryPickerComponent, DirectoryPickerResult} from '../_modals/directory-picker/directory-picker.component';
|
||||
@ -20,7 +20,7 @@ import {
|
||||
import {allEncodeFormats} from '../_models/encode-format';
|
||||
import {ManageMediaIssuesComponent} from '../manage-media-issues/manage-media-issues.component';
|
||||
import {NgFor, NgIf, NgTemplateOutlet} from '@angular/common';
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
|
||||
import {allCoverImageSizes} from '../_models/cover-image-size';
|
||||
import {pageLayoutModes} from "../../_models/preferences/preferences";
|
||||
import {PageLayoutModePipe} from "../../_pipes/page-layout-mode.pipe";
|
||||
@ -28,6 +28,7 @@ import {SettingItemComponent} from "../../settings/_components/setting-item/sett
|
||||
import {EncodeFormatPipe} from "../../_pipes/encode-format.pipe";
|
||||
import {CoverImageSizePipe} from "../../_pipes/cover-image-size.pipe";
|
||||
import {ConfirmService} from "../../shared/confirm.service";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
@Component({
|
||||
selector: 'app-manage-media-settings',
|
||||
@ -47,6 +48,7 @@ export class ManageMediaSettingsComponent implements OnInit {
|
||||
private readonly settingsService = inject(SettingsService);
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly modalService = inject(NgbModal);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
protected readonly allEncodeFormats = allEncodeFormats;
|
||||
protected readonly allCoverImageSizes = allCoverImageSizes;
|
||||
@ -61,33 +63,46 @@ export class ManageMediaSettingsComponent implements OnInit {
|
||||
this.settingsForm.addControl('encodeMediaAs', new FormControl(this.serverSettings.encodeMediaAs, [Validators.required]));
|
||||
this.settingsForm.addControl('bookmarksDirectory', new FormControl(this.serverSettings.bookmarksDirectory, [Validators.required]));
|
||||
this.settingsForm.addControl('coverImageSize', new FormControl(this.serverSettings.coverImageSize, [Validators.required]));
|
||||
|
||||
// Automatically save settings as we edit them
|
||||
this.settingsForm.valueChanges.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(100),
|
||||
filter(_ => this.settingsForm.valid),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
switchMap(_ => {
|
||||
const data = this.packData();
|
||||
return this.settingsService.updateServerSettings(data);
|
||||
}),
|
||||
tap(settings => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.cdRef.markForCheck();
|
||||
})
|
||||
).subscribe();
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs);
|
||||
this.settingsForm.get('bookmarksDirectory')?.setValue(this.serverSettings.bookmarksDirectory);
|
||||
this.settingsForm.get('coverImageSize')?.setValue(this.serverSettings.coverImageSize);
|
||||
this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('bookmarksDirectory')?.setValue(this.serverSettings.bookmarksDirectory, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('coverImageSize')?.setValue(this.serverSettings.coverImageSize, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.markAsPristine();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
saveSettings() {
|
||||
packData() {
|
||||
const modelSettings = Object.assign({}, this.serverSettings);
|
||||
modelSettings.encodeMediaAs = parseInt(this.settingsForm.get('encodeMediaAs')?.value, 10);
|
||||
modelSettings.bookmarksDirectory = this.settingsForm.get('bookmarksDirectory')?.value;
|
||||
modelSettings.coverImageSize = parseInt(this.settingsForm.get('coverImageSize')?.value, 10);
|
||||
|
||||
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe(async (settings: ServerSettings) => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.toastr.success(this.translocoService.translate('toasts.server-settings-updated'));
|
||||
}, (err: any) => {
|
||||
console.error('error: ', err);
|
||||
});
|
||||
return modelSettings;
|
||||
}
|
||||
|
||||
|
||||
async resetToDefaults() {
|
||||
if (!await this.confirmService.confirm(translate('toasts.confirm-reset-server-settings'))) return;
|
||||
|
||||
|
@ -25,10 +25,10 @@ import {EditSeriesModalComponent} from "../../cards/_modals/edit-series-modal/ed
|
||||
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {FilterPipe} from "../../_pipes/filter.pipe";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
import {TranslocoModule} from "@ngneat/transloco";
|
||||
import {TranslocoModule} from "@jsverse/transloco";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {TranslocoLocaleModule} from "@jsverse/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
@Component({
|
||||
|
@ -318,8 +318,6 @@
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mt-4">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetToDefaults()">{{t('reset-to-default')}}</button>
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="saveSettings()" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
|
||||
import {ToastrService} from 'ngx-toastr';
|
||||
import {take} from 'rxjs/operators';
|
||||
@ -7,13 +7,15 @@ import {SettingsService} from '../settings.service';
|
||||
import {ServerSettings} from '../_models/server-settings';
|
||||
import {NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
||||
import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco";
|
||||
import {WikiLink} from "../../_models/wiki";
|
||||
import {PageLayoutModePipe} from "../../_pipes/page-layout-mode.pipe";
|
||||
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
|
||||
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {ConfirmService} from "../../shared/confirm.service";
|
||||
import {debounceTime, distinctUntilChanged, filter, switchMap, tap} from "rxjs";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
|
||||
const ValidIpAddress = /^(\s*((([12]?\d{1,2}\.){3}[12]?\d{1,2})|(([\da-f]{0,4}\:){0,7}([\da-f]{0,4})))\s*\,)*\s*((([12]?\d{1,2}\.){3}[12]?\d{1,2})|(([\da-f]{0,4}\:){0,7}([\da-f]{0,4})))\s*$/i;
|
||||
|
||||
@ -33,6 +35,7 @@ export class ManageSettingsComponent implements OnInit {
|
||||
private readonly toastr = inject(ToastrService);
|
||||
private readonly serverService = inject(ServerService);
|
||||
private readonly confirmService = inject(ConfirmService);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
protected readonly WikiLink = WikiLink;
|
||||
|
||||
serverSettings!: ServerSettings;
|
||||
@ -76,6 +79,23 @@ export class ManageSettingsComponent implements OnInit {
|
||||
this.settingsForm.addControl('onDeckProgressDays', new FormControl(this.serverSettings.onDeckProgressDays, [Validators.required]));
|
||||
this.settingsForm.addControl('onDeckUpdateDays', new FormControl(this.serverSettings.onDeckUpdateDays, [Validators.required]));
|
||||
|
||||
// Automatically save settings as we edit them
|
||||
this.settingsForm.valueChanges.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(100),
|
||||
filter(_ => this.settingsForm.valid),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
switchMap(_ => {
|
||||
const data = this.packData();
|
||||
return this.settingsService.updateServerSettings(data);
|
||||
}),
|
||||
tap(settings => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.cdRef.markForCheck();
|
||||
})
|
||||
).subscribe();
|
||||
|
||||
this.serverService.getServerInfo().subscribe(info => {
|
||||
if (info.isDocker) {
|
||||
this.settingsForm.get('ipAddresses')?.disable();
|
||||
@ -89,42 +109,35 @@ export class ManageSettingsComponent implements OnInit {
|
||||
}
|
||||
|
||||
resetForm() {
|
||||
this.settingsForm.get('cacheDirectory')?.setValue(this.serverSettings.cacheDirectory);
|
||||
this.settingsForm.get('scanTask')?.setValue(this.serverSettings.taskScan);
|
||||
this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup);
|
||||
this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup);
|
||||
this.settingsForm.get('ipAddresses')?.setValue(this.serverSettings.ipAddresses);
|
||||
this.settingsForm.get('port')?.setValue(this.serverSettings.port);
|
||||
this.settingsForm.get('loggingLevel')?.setValue(this.serverSettings.loggingLevel);
|
||||
this.settingsForm.get('allowStatCollection')?.setValue(this.serverSettings.allowStatCollection);
|
||||
this.settingsForm.get('enableOpds')?.setValue(this.serverSettings.enableOpds);
|
||||
this.settingsForm.get('baseUrl')?.setValue(this.serverSettings.baseUrl);
|
||||
this.settingsForm.get('emailServiceUrl')?.setValue(this.serverSettings.emailServiceUrl);
|
||||
this.settingsForm.get('totalBackups')?.setValue(this.serverSettings.totalBackups);
|
||||
this.settingsForm.get('totalLogs')?.setValue(this.serverSettings.totalLogs);
|
||||
this.settingsForm.get('enableFolderWatching')?.setValue(this.serverSettings.enableFolderWatching);
|
||||
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.get('cacheDirectory')?.setValue(this.serverSettings.cacheDirectory, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('scanTask')?.setValue(this.serverSettings.taskScan, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('ipAddresses')?.setValue(this.serverSettings.ipAddresses, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('port')?.setValue(this.serverSettings.port, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('loggingLevel')?.setValue(this.serverSettings.loggingLevel, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('allowStatCollection')?.setValue(this.serverSettings.allowStatCollection, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('enableOpds')?.setValue(this.serverSettings.enableOpds, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('baseUrl')?.setValue(this.serverSettings.baseUrl, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('emailServiceUrl')?.setValue(this.serverSettings.emailServiceUrl, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('totalBackups')?.setValue(this.serverSettings.totalBackups, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('totalLogs')?.setValue(this.serverSettings.totalLogs, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('enableFolderWatching')?.setValue(this.serverSettings.enableFolderWatching, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('encodeMediaAs')?.setValue(this.serverSettings.encodeMediaAs, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('hostName')?.setValue(this.serverSettings.hostName, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('cacheSize')?.setValue(this.serverSettings.cacheSize, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('onDeckProgressDays')?.setValue(this.serverSettings.onDeckProgressDays, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('onDeckUpdateDays')?.setValue(this.serverSettings.onDeckUpdateDays, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.markAsPristine();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
packData() {
|
||||
const modelSettings = this.settingsForm.value;
|
||||
modelSettings.bookmarksDirectory = this.serverSettings.bookmarksDirectory;
|
||||
modelSettings.smtpConfig = this.serverSettings.smtpConfig;
|
||||
|
||||
|
||||
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.toastr.success(this.translocoService.translate('toasts.server-settings-updated'));
|
||||
}, (err: any) => {
|
||||
console.error('error: ', err);
|
||||
});
|
||||
return modelSettings;
|
||||
}
|
||||
|
||||
async resetToDefaults() {
|
||||
|
@ -2,7 +2,7 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} f
|
||||
import {ServerService} from 'src/app/_services/server.service';
|
||||
import {ServerInfoSlim} from '../_models/server-info';
|
||||
import {DatePipe, NgIf} from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ChangelogComponent} from "../../announcements/_components/changelog/changelog.component";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
|
@ -125,8 +125,6 @@
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetToDefaults()">{{t('reset-to-default')}}</button>
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="saveSettings()" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
|
||||
<div class="setting-section-break"></div>
|
||||
|
@ -4,7 +4,7 @@ import {ToastrService} from 'ngx-toastr';
|
||||
import {SettingsService} from '../settings.service';
|
||||
import {ServerSettings} from '../_models/server-settings';
|
||||
import {shareReplay, take} from 'rxjs/operators';
|
||||
import {debounceTime, defer, forkJoin, Observable, of, switchMap, tap} from 'rxjs';
|
||||
import {debounceTime, defer, distinctUntilChanged, filter, forkJoin, Observable, of, switchMap, tap} from 'rxjs';
|
||||
import {ServerService} from 'src/app/_services/server.service';
|
||||
import {Job} from 'src/app/_models/job/job';
|
||||
import {UpdateNotificationModalComponent} from 'src/app/shared/update-notification/update-notification-modal.component';
|
||||
@ -12,8 +12,8 @@ import {NgbModal, NgbTooltip} from '@ng-bootstrap/ng-bootstrap';
|
||||
import {DownloadService} from 'src/app/shared/_services/download.service';
|
||||
import {DefaultValuePipe} from '../../_pipes/default-value.pipe';
|
||||
import {AsyncPipe, DatePipe, NgFor, NgIf, NgTemplateOutlet, TitleCasePipe} from '@angular/common';
|
||||
import {translate, TranslocoModule} from "@ngneat/transloco";
|
||||
import {TranslocoLocaleModule} from "@ngneat/transloco-locale";
|
||||
import {translate, TranslocoModule} from "@jsverse/transloco";
|
||||
import {TranslocoLocaleModule} from "@jsverse/transloco-locale";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
@ -136,6 +136,7 @@ export class ManageTasksSettingsComponent implements OnInit {
|
||||
|
||||
this.logLevels = result.levels;
|
||||
this.serverSettings = result.settings;
|
||||
|
||||
this.settingsForm.addControl('taskScan', new FormControl(this.serverSettings.taskScan, [Validators.required]));
|
||||
this.settingsForm.addControl('taskBackup', new FormControl(this.serverSettings.taskBackup, [Validators.required]));
|
||||
this.settingsForm.addControl('taskCleanup', new FormControl(this.serverSettings.taskCleanup, [Validators.required]));
|
||||
@ -203,6 +204,24 @@ export class ManageTasksSettingsComponent implements OnInit {
|
||||
takeUntilDestroyed(this.destroyRef)
|
||||
).subscribe();
|
||||
|
||||
// Automatically save settings as we edit them
|
||||
this.settingsForm.valueChanges.pipe(
|
||||
distinctUntilChanged(),
|
||||
debounceTime(100),
|
||||
filter(_ => this.settingsForm.valid),
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
switchMap(_ => {
|
||||
const data = this.packData();
|
||||
return this.settingsService.updateServerSettings(data);
|
||||
}),
|
||||
tap(settings => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.recurringTasks$ = this.serverService.getRecurringJobs().pipe(shareReplay());
|
||||
this.cdRef.markForCheck();
|
||||
})
|
||||
).subscribe();
|
||||
|
||||
this.cdRef.markForCheck();
|
||||
});
|
||||
|
||||
@ -212,33 +231,33 @@ export class ManageTasksSettingsComponent implements OnInit {
|
||||
|
||||
|
||||
resetForm() {
|
||||
this.settingsForm.get('taskScan')?.setValue(this.serverSettings.taskScan);
|
||||
this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup);
|
||||
this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup);
|
||||
this.settingsForm.get('taskScan')?.setValue(this.serverSettings.taskScan, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('taskBackup')?.setValue(this.serverSettings.taskBackup, {onlySelf: true, emitEvent: false});
|
||||
this.settingsForm.get('taskCleanup')?.setValue(this.serverSettings.taskCleanup, {onlySelf: true, emitEvent: false});
|
||||
|
||||
if (!this.taskFrequencies.includes(this.serverSettings.taskScan)) {
|
||||
this.settingsForm.get('taskScanCustom')?.setValue(this.serverSettings.taskScan);
|
||||
this.settingsForm.get('taskScanCustom')?.setValue(this.serverSettings.taskScan, {onlySelf: true, emitEvent: false});
|
||||
} else {
|
||||
this.settingsForm.get('taskScanCustom')?.setValue('');
|
||||
this.settingsForm.get('taskScanCustom')?.setValue('', {onlySelf: true, emitEvent: false});
|
||||
}
|
||||
|
||||
if (!this.taskFrequencies.includes(this.serverSettings.taskBackup)) {
|
||||
this.settingsForm.get('taskBackupCustom')?.setValue(this.serverSettings.taskBackup);
|
||||
this.settingsForm.get('taskBackupCustom')?.setValue(this.serverSettings.taskBackup, {onlySelf: true, emitEvent: false});
|
||||
} else {
|
||||
this.settingsForm.get('taskBackupCustom')?.setValue('');
|
||||
this.settingsForm.get('taskBackupCustom')?.setValue('', {onlySelf: true, emitEvent: false});
|
||||
}
|
||||
|
||||
if (!this.taskFrequencies.includes(this.serverSettings.taskCleanup)) {
|
||||
this.settingsForm.get('taskCleanupCustom')?.setValue(this.serverSettings.taskCleanup);
|
||||
this.settingsForm.get('taskCleanupCustom')?.setValue(this.serverSettings.taskCleanup, {onlySelf: true, emitEvent: false});
|
||||
} else {
|
||||
this.settingsForm.get('taskCleanupCustom')?.setValue('');
|
||||
this.settingsForm.get('taskCleanupCustom')?.setValue('', {onlySelf: true, emitEvent: false});
|
||||
}
|
||||
|
||||
this.settingsForm.markAsPristine();
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
||||
async saveSettings() {
|
||||
packData() {
|
||||
const modelSettings = Object.assign({}, this.serverSettings);
|
||||
modelSettings.taskBackup = this.settingsForm.get('taskBackup')?.value;
|
||||
modelSettings.taskScan = this.settingsForm.get('taskScan')?.value;
|
||||
@ -256,18 +275,10 @@ export class ManageTasksSettingsComponent implements OnInit {
|
||||
modelSettings.taskCleanup = this.settingsForm.get('taskCleanupCustom')?.value;
|
||||
}
|
||||
|
||||
|
||||
this.settingsService.updateServerSettings(modelSettings).pipe(take(1)).subscribe((settings: ServerSettings) => {
|
||||
this.serverSettings = settings;
|
||||
this.resetForm();
|
||||
this.recurringTasks$ = this.serverService.getRecurringJobs().pipe(shareReplay());
|
||||
this.toastr.success(translate('toasts.server-settings-updated'));
|
||||
this.cdRef.markForCheck();
|
||||
}, (err: any) => {
|
||||
console.error('error: ', err);
|
||||
});
|
||||
return modelSettings;
|
||||
}
|
||||
|
||||
|
||||
async resetToDefaults() {
|
||||
if (!await this.confirmService.confirm(translate('toasts.confirm-reset-server-settings'))) return;
|
||||
|
||||
|
@ -13,7 +13,7 @@ import {EditUserComponent} from '../edit-user/edit-user.component';
|
||||
import {Router} from '@angular/router';
|
||||
import {TagBadgeComponent} from '../../shared/tag-badge/tag-badge.component';
|
||||
import {AsyncPipe, DatePipe, NgClass, NgIf, TitleCasePipe} from '@angular/common';
|
||||
import {translate, TranslocoModule, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco";
|
||||
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
|
||||
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
|
||||
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
|
||||
|
@ -13,7 +13,7 @@ import { User } from 'src/app/_models/user';
|
||||
import {AccountService} from 'src/app/_services/account.service';
|
||||
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
|
||||
import { NgFor } from '@angular/common';
|
||||
import {TranslocoDirective,} from "@ngneat/transloco";
|
||||
import {TranslocoDirective,} from "@jsverse/transloco";
|
||||
import {SelectionModel} from "../../typeahead/_models/selection-model";
|
||||
|
||||
@Component({
|
||||
|
@ -1,8 +1,7 @@
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, inject, OnInit} from '@angular/core';
|
||||
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {JumpKey} from "../_models/jumpbar/jump-key";
|
||||
import {EVENTS, Message, MessageHubService} from "../_services/message-hub.service";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {CardItemComponent} from "../cards/card-item/card-item.component";
|
||||
import {
|
||||
SideNavCompanionBarComponent
|
||||
|
@ -29,7 +29,7 @@ import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/car
|
||||
import { BulkOperationsComponent } from '../../../cards/bulk-operations/bulk-operations.component';
|
||||
import { NgIf, DecimalPipe } from '@angular/common';
|
||||
import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
|
||||
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
import { ChangelogComponent } from '../changelog/changelog.component';
|
||||
import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
|
||||
@Component({
|
||||
selector: 'app-announcements',
|
||||
|
@ -4,7 +4,7 @@ import {ServerService} from 'src/app/_services/server.service';
|
||||
import {LoadingComponent} from '../../../shared/loading/loading.component';
|
||||
import {ReadMoreComponent} from '../../../shared/read-more/read-more.component';
|
||||
import {AsyncPipe, DatePipe} from '@angular/common';
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {AccountService} from "../../../_services/account.service";
|
||||
|
||||
@Component({
|
||||
|
@ -2,7 +2,7 @@ import {Component, DestroyRef, inject, Input} from '@angular/core';
|
||||
import {FormsModule} from "@angular/forms";
|
||||
import {AsyncPipe, NgForOf, NgIf} from "@angular/common";
|
||||
import {NgbActiveModal, NgbHighlight, NgbModal, NgbTypeahead} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
import {ServerService} from "../../../_services/server.service";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {map} from "rxjs/operators";
|
||||
|
@ -61,6 +61,11 @@ const routes: Routes = [
|
||||
pathMatch: 'full',
|
||||
loadComponent: () => import('../app/series-detail/_components/series-detail/series-detail.component').then(c => c.SeriesDetailComponent)
|
||||
},
|
||||
{
|
||||
path: ':libraryId/series/:seriesId/chapter/:chapterId',
|
||||
pathMatch: 'full',
|
||||
loadComponent: () => import('./chapter-detail/chapter-detail.component').then(c => c.ChapterDetailComponent)
|
||||
},
|
||||
{
|
||||
path: ':libraryId/series/:seriesId/manga',
|
||||
loadChildren: () => import('./_routes/manga-reader.router.module').then(m => m.routes)
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div [ngClass]="{'no-transitions' : (transitionState$ | async)}">
|
||||
@if (accountService.currentUser$ | async; as currentUser) {
|
||||
@if (currentUser) {
|
||||
@if (currentUser && (navService.navbarVisible$ | async) === true) {
|
||||
<div class="fullpage-background">
|
||||
<div class="background-area">
|
||||
<canvas id="backgroundCanvas" class="default-background" style="width: 100%; height: calc(var(--vh) * 100);"></canvas>
|
||||
|
@ -14,7 +14,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from "@angular/forms";
|
||||
import {ReaderService} from "../../../_services/reader.service";
|
||||
import {ToastrService} from "ngx-toastr";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
import {KEY_CODES} from "../../../shared/_services/utility.service";
|
||||
|
||||
enum BookLineOverlayMode {
|
||||
|
@ -51,7 +51,7 @@ import {
|
||||
PersonalTableOfContentsComponent,
|
||||
PersonalToCEvent
|
||||
} from "../personal-table-of-contents/personal-table-of-contents.component";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective} from "@jsverse/transloco";
|
||||
|
||||
|
||||
enum TabID {
|
||||
|
@ -13,7 +13,7 @@ import {ReaderService} from "../../../_services/reader.service";
|
||||
import {PersonalToC} from "../../../_models/readers/personal-toc";
|
||||
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
|
||||
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
|
||||
import {TranslocoDirective} from "@ngneat/transloco";
|
||||
import {TranslocoDirective} from "@jsverse/transloco";
|
||||
|
||||
export interface PersonalToCEvent {
|
||||
pageNum: number;
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user