mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-08-11 09:13:42 -04:00
Polish Round 4 (#2429)
This commit is contained in:
parent
cd7f876140
commit
ee72727841
@ -19,3 +19,6 @@ trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
indent_size = 2
|
||||
|
||||
[*.csproj]
|
||||
indent_size = 2
|
||||
|
@ -62,7 +62,7 @@ public class DefaultParserTests
|
||||
[Theory]
|
||||
[InlineData("/manga/Btooom!/Vol.1/Chapter 1/1.cbz", "Btooom!")]
|
||||
[InlineData("/manga/Btooom!/Vol.1 Chapter 2/1.cbz", "Btooom!")]
|
||||
[InlineData("/manga/Monster #8 (Digital)/Ch. 001-016 [MangaPlus] [Digital] [amit34521]/Monster #8 Ch. 001 [MangaPlus] [Digital] [amit34521]/13.jpg", "Monster")]
|
||||
[InlineData("/manga/Monster #8 (Digital)/Ch. 001-016 [MangaPlus] [Digital] [amit34521]/Monster #8 Ch. 001 [MangaPlus] [Digital] [amit34521]/13.jpg", "manga")]
|
||||
[InlineData("/manga/Monster (Digital)/Ch. 001-016 [MangaPlus] [Digital] [amit34521]/Monster Ch. 001 [MangaPlus] [Digital] [amit34521]/13.jpg", "Monster")]
|
||||
[InlineData("/manga/Foo 50/Specials/Foo 50 SP01.cbz", "Foo 50")]
|
||||
[InlineData("/manga/Foo 50 (kiraa)/Specials/Foo 50 SP01.cbz", "Foo 50")]
|
||||
@ -293,7 +293,7 @@ public class DefaultParserTests
|
||||
var expectedInfo2 = new ParserInfo
|
||||
{
|
||||
Series = "Monster #8", Volumes = "0", Edition = "",
|
||||
Chapters = "1", Filename = "13.jpg", Format = MangaFormat.Image,
|
||||
Chapters = "8", Filename = "13.jpg", Format = MangaFormat.Image,
|
||||
FullFilePath = filepath, IsSpecial = false
|
||||
};
|
||||
var actual2 = _defaultParser.Parse(filepath, @"E:\Manga\Monster #8");
|
||||
@ -314,7 +314,7 @@ public class DefaultParserTests
|
||||
Assert.Equal(expectedInfo2.FullFilePath, actual2.FullFilePath);
|
||||
_testOutputHelper.WriteLine("FullFilePath ✓");
|
||||
|
||||
filepath = @"E:\Manga\Extra layer for no reason\Just Images the second\Vol19\ch186\Vol. 19 p106.gif";
|
||||
filepath = @"E:\Manga\Extra layer for no reason\Just Images the second\Vol19\ch. 186\Vol. 19 p106.gif";
|
||||
expectedInfo2 = new ParserInfo
|
||||
{
|
||||
Series = "Just Images the second", Volumes = "19", Edition = "",
|
||||
@ -340,7 +340,7 @@ public class DefaultParserTests
|
||||
Assert.Equal(expectedInfo2.FullFilePath, actual2.FullFilePath);
|
||||
_testOutputHelper.WriteLine("FullFilePath ✓");
|
||||
|
||||
filepath = @"E:\Manga\Extra layer for no reason\Just Images the second\Blank Folder\Vol19\ch186\Vol. 19 p106.gif";
|
||||
filepath = @"E:\Manga\Extra layer for no reason\Just Images the second\Blank Folder\Vol19\ch. 186\Vol. 19 p106.gif";
|
||||
expectedInfo2 = new ParserInfo
|
||||
{
|
||||
Series = "Just Images the second", Volumes = "19", Edition = "",
|
||||
|
@ -657,7 +657,7 @@ public class SeriesServiceTests : AbstractDbTest
|
||||
{
|
||||
SeriesId = 1,
|
||||
Publishers = new List<PersonDto>() {new () {Id = 0, Name = "Existing Person", Role = PersonRole.Publisher}},
|
||||
PublishersLocked = true
|
||||
PublisherLocked = true
|
||||
},
|
||||
CollectionTags = new List<CollectionTagDto>()
|
||||
});
|
||||
|
@ -53,6 +53,10 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.13">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
|
||||
<PackageReference Include="Docnet.Core" Version="2.6.0" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||
@ -60,28 +64,15 @@
|
||||
<PackageReference Include="Flurl" Version="3.0.7" />
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.8.6" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.6.0" />
|
||||
<PackageReference Include="Hangfire.MaximumConcurrentExecutions" Version="1.1.0" />
|
||||
<PackageReference Include="Hangfire.MemoryStorage.Core" Version="1.4.0" />
|
||||
<PackageReference Include="Hangfire.Storage.SQLite" Version="0.3.4" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" />
|
||||
<PackageReference Include="MarkdownDeep.NET.Core" Version="1.5.0.4" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.13">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
|
||||
<PackageReference Include="EasyCaching.InMemory" Version="1.9.2" />
|
||||
<PackageReference Include="ExCSS" Version="4.2.4" />
|
||||
<PackageReference Include="Hangfire" Version="1.8.6" />
|
||||
<PackageReference Include="Hangfire.AspNetCore" Version="1.8.6" />
|
||||
<PackageReference Include="Hangfire.InMemory" Version="0.6.0" />
|
||||
<PackageReference Include="HtmlAgilityPack" Version="1.11.54" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.SignalR" Version="1.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="7.0.13" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="7.0.13" />
|
||||
|
@ -437,8 +437,7 @@ public class SeriesController : BaseApiController
|
||||
[HttpGet("metadata")]
|
||||
public async Task<ActionResult<SeriesMetadataDto>> GetSeriesMetadata(int seriesId)
|
||||
{
|
||||
var metadata = await _unitOfWork.SeriesRepository.GetSeriesMetadata(seriesId);
|
||||
return Ok(metadata);
|
||||
return Ok(await _unitOfWork.SeriesRepository.GetSeriesMetadata(seriesId));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -75,16 +75,16 @@ public class SeriesMetadataDto
|
||||
public bool PublicationStatusLocked { get; set; }
|
||||
public bool GenresLocked { get; set; }
|
||||
public bool TagsLocked { get; set; }
|
||||
public bool WritersLocked { get; set; }
|
||||
public bool CharactersLocked { get; set; }
|
||||
public bool ColoristsLocked { get; set; }
|
||||
public bool EditorsLocked { get; set; }
|
||||
public bool InkersLocked { get; set; }
|
||||
public bool LetterersLocked { get; set; }
|
||||
public bool PencillersLocked { get; set; }
|
||||
public bool PublishersLocked { get; set; }
|
||||
public bool TranslatorsLocked { get; set; }
|
||||
public bool CoverArtistsLocked { get; set; }
|
||||
public bool WriterLocked { get; set; }
|
||||
public bool CharacterLocked { get; set; }
|
||||
public bool ColoristLocked { get; set; }
|
||||
public bool EditorLocked { get; set; }
|
||||
public bool InkerLocked { get; set; }
|
||||
public bool LettererLocked { get; set; }
|
||||
public bool PencillerLocked { get; set; }
|
||||
public bool PublisherLocked { get; set; }
|
||||
public bool TranslatorLocked { get; set; }
|
||||
public bool CoverArtistLocked { get; set; }
|
||||
public bool ReleaseYearLocked { get; set; }
|
||||
|
||||
|
||||
|
@ -73,12 +73,14 @@ public class AccountService : IAccountService
|
||||
basePart = serverSettings.HostName;
|
||||
if (!serverSettings.BaseUrl.Equals(Configuration.DefaultBaseUrl))
|
||||
{
|
||||
basePart += serverSettings.BaseUrl.Substring(0, serverSettings.BaseUrl.Length - 1);
|
||||
var removeCount = serverSettings.BaseUrl.EndsWith("/") ? 2 : 1;
|
||||
basePart += serverSettings.BaseUrl.Substring(0, serverSettings.BaseUrl.Length - removeCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (withHost) return $"{basePart}/registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}".Replace("//", "/");
|
||||
return $"registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}".Replace("//", "/");
|
||||
if (withHost) return $"{basePart}/registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}";
|
||||
return $"registration/{routePart}?token={HttpUtility.UrlEncode(token)}&email={HttpUtility.UrlEncode(email)}"
|
||||
.Replace("//", "/");
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ApiException>> ChangeUserPassword(AppUser user, string newPassword)
|
||||
|
@ -107,6 +107,7 @@ public class SeriesService : ISeriesService
|
||||
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(seriesId);
|
||||
if (series == null) return false;
|
||||
var allCollectionTags = (await _unitOfWork.CollectionTagRepository.GetAllTagsAsync()).ToList();
|
||||
// TODO: This is Diesel's performance problem with Komf. For some systems, this is too heavy of a call if komf is spamming updates.
|
||||
var allGenres = (await _unitOfWork.GenreRepository.GetAllGenresAsync()).ToList();
|
||||
var allPeople = (await _unitOfWork.PersonRepository.GetAllPeople()).ToList();
|
||||
var allTags = (await _unitOfWork.TagRepository.GetAllTagsAsync()).ToList();
|
||||
@ -219,16 +220,16 @@ public class SeriesService : ISeriesService
|
||||
series.Metadata.LanguageLocked = updateSeriesMetadataDto.SeriesMetadata.LanguageLocked;
|
||||
series.Metadata.GenresLocked = updateSeriesMetadataDto.SeriesMetadata.GenresLocked;
|
||||
series.Metadata.TagsLocked = updateSeriesMetadataDto.SeriesMetadata.TagsLocked;
|
||||
series.Metadata.CharacterLocked = updateSeriesMetadataDto.SeriesMetadata.CharactersLocked;
|
||||
series.Metadata.ColoristLocked = updateSeriesMetadataDto.SeriesMetadata.ColoristsLocked;
|
||||
series.Metadata.EditorLocked = updateSeriesMetadataDto.SeriesMetadata.EditorsLocked;
|
||||
series.Metadata.InkerLocked = updateSeriesMetadataDto.SeriesMetadata.InkersLocked;
|
||||
series.Metadata.LettererLocked = updateSeriesMetadataDto.SeriesMetadata.LetterersLocked;
|
||||
series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillersLocked;
|
||||
series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublishersLocked;
|
||||
series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorsLocked;
|
||||
series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistsLocked;
|
||||
series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WritersLocked;
|
||||
series.Metadata.CharacterLocked = updateSeriesMetadataDto.SeriesMetadata.CharacterLocked;
|
||||
series.Metadata.ColoristLocked = updateSeriesMetadataDto.SeriesMetadata.ColoristLocked;
|
||||
series.Metadata.EditorLocked = updateSeriesMetadataDto.SeriesMetadata.EditorLocked;
|
||||
series.Metadata.InkerLocked = updateSeriesMetadataDto.SeriesMetadata.InkerLocked;
|
||||
series.Metadata.LettererLocked = updateSeriesMetadataDto.SeriesMetadata.LettererLocked;
|
||||
series.Metadata.PencillerLocked = updateSeriesMetadataDto.SeriesMetadata.PencillerLocked;
|
||||
series.Metadata.PublisherLocked = updateSeriesMetadataDto.SeriesMetadata.PublisherLocked;
|
||||
series.Metadata.TranslatorLocked = updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked;
|
||||
series.Metadata.CoverArtistLocked = updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked;
|
||||
series.Metadata.WriterLocked = updateSeriesMetadataDto.SeriesMetadata.WriterLocked;
|
||||
series.Metadata.SummaryLocked = updateSeriesMetadataDto.SeriesMetadata.SummaryLocked;
|
||||
series.Metadata.ReleaseYearLocked = updateSeriesMetadataDto.SeriesMetadata.ReleaseYearLocked;
|
||||
|
||||
|
@ -119,19 +119,46 @@ public class DefaultParser : IDefaultParser
|
||||
{
|
||||
ret.Volumes = Parser.DefaultVolume;
|
||||
ret.Chapters = Parser.DefaultChapter;
|
||||
// Next we need to see if the image has a folder between rootPath and filePath.
|
||||
// if so, take that folder as a volume 0 chapter 0 special and group everything under there (if we can't parse a volume/chapter)
|
||||
var directoryName = _directoryService.FileSystem.DirectoryInfo.New(rootPath).Name;
|
||||
ret.Series = directoryName;
|
||||
|
||||
ParseFromFallbackFolders(filePath, rootPath, LibraryType.Image, ref ret);
|
||||
if ((string.IsNullOrEmpty(ret.Chapters) || ret.Chapters == Parser.DefaultChapter) &&
|
||||
(string.IsNullOrEmpty(ret.Volumes) || ret.Volumes == Parser.DefaultVolume))
|
||||
|
||||
|
||||
if (IsEmptyOrDefault(ret.Volumes, ret.Chapters))
|
||||
{
|
||||
ret.IsSpecial = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
var parsedVolume = Parser.ParseVolume(ret.Filename);
|
||||
var parsedChapter = Parser.ParseChapter(ret.Filename);
|
||||
if (IsEmptyOrDefault(ret.Volumes, string.Empty) && !parsedVolume.Equals(Parser.DefaultVolume))
|
||||
{
|
||||
ret.Volumes = parsedVolume;
|
||||
}
|
||||
if (IsEmptyOrDefault(string.Empty, ret.Chapters) && !parsedChapter.Equals(Parser.DefaultChapter))
|
||||
{
|
||||
ret.Chapters = parsedChapter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Override the series name, as fallback folders needs it to try and parse folder name
|
||||
if (string.IsNullOrEmpty(ret.Series) || ret.Series.Equals(directoryName))
|
||||
{
|
||||
ret.Series = Parser.CleanTitle(directoryName, replaceSpecials: false);
|
||||
}
|
||||
|
||||
ret.Series = _directoryService.FileSystem.DirectoryInfo.New(rootPath).Name;
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static bool IsEmptyOrDefault(string volumes, string chapters)
|
||||
{
|
||||
return (string.IsNullOrEmpty(chapters) || chapters == Parser.DefaultChapter) &&
|
||||
(string.IsNullOrEmpty(volumes) || volumes == Parser.DefaultVolume);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fills out <see cref="ParserInfo"/> by trying to parse volume, chapters, and series from folders
|
||||
/// </summary>
|
||||
@ -193,7 +220,7 @@ public class DefaultParser : IDefaultParser
|
||||
break;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(series) && (string.IsNullOrEmpty(ret.Series) || !folder.Contains(ret.Series)))
|
||||
if (!string.IsNullOrEmpty(series) && (string.IsNullOrEmpty(ret.Series) && !folder.Contains(ret.Series)))
|
||||
{
|
||||
ret.Series = series;
|
||||
break;
|
||||
|
@ -843,23 +843,27 @@ public static class Parser
|
||||
/// <param name="isComic"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
public static string CleanTitle(string title, bool isComic = false)
|
||||
public static string CleanTitle(string title, bool isComic = false, bool replaceSpecials = true)
|
||||
{
|
||||
|
||||
title = ReplaceUnderscores(title);
|
||||
|
||||
title = RemoveEditionTagHolders(title);
|
||||
|
||||
if (isComic)
|
||||
if (replaceSpecials)
|
||||
{
|
||||
title = RemoveComicSpecialTags(title);
|
||||
title = RemoveEuropeanTags(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
title = RemoveMangaSpecialTags(title);
|
||||
if (isComic)
|
||||
{
|
||||
title = RemoveComicSpecialTags(title);
|
||||
title = RemoveEuropeanTags(title);
|
||||
}
|
||||
else
|
||||
{
|
||||
title = RemoveMangaSpecialTags(title);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
title = title.Trim(SpacesAndSeparators);
|
||||
|
||||
title = EmptySpaceRegex.Replace(title, " ");
|
||||
|
@ -293,6 +293,9 @@ public class ProcessSeries : IProcessSeries
|
||||
|
||||
|
||||
if ((maxChapter == 0 || maxChapter > series.Metadata.TotalCount) && maxVolume <= series.Metadata.TotalCount)
|
||||
{
|
||||
series.Metadata.MaxCount = maxVolume;
|
||||
} else if (maxVolume == series.Metadata.TotalCount)
|
||||
{
|
||||
series.Metadata.MaxCount = maxVolume;
|
||||
}
|
||||
|
170
UI/Web/package-lock.json
generated
170
UI/Web/package-lock.json
generated
@ -8,16 +8,16 @@
|
||||
"name": "kavita-webui",
|
||||
"version": "0.4.2",
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.0.1",
|
||||
"@angular/animations": "^17.0.2",
|
||||
"@angular/cdk": "^17.0.0",
|
||||
"@angular/common": "^17.0.1",
|
||||
"@angular/compiler": "^17.0.1",
|
||||
"@angular/core": "^17.0.1",
|
||||
"@angular/forms": "^17.0.1",
|
||||
"@angular/localize": "^17.0.1",
|
||||
"@angular/platform-browser": "^17.0.1",
|
||||
"@angular/platform-browser-dynamic": "^17.0.1",
|
||||
"@angular/router": "^17.0.1",
|
||||
"@angular/common": "^17.0.2",
|
||||
"@angular/compiler": "^17.0.2",
|
||||
"@angular/core": "^17.0.2",
|
||||
"@angular/forms": "^17.0.2",
|
||||
"@angular/localize": "^17.0.2",
|
||||
"@angular/platform-browser": "^17.0.2",
|
||||
"@angular/platform-browser-dynamic": "^17.0.2",
|
||||
"@angular/router": "^17.0.2",
|
||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||
"@iharbeck/ngx-virtual-scroller": "^16.0.0",
|
||||
"@iplab/ngx-file-upload": "^16.0.2",
|
||||
@ -29,7 +29,7 @@
|
||||
"@ngneat/transloco-persist-translations": "^5.0.0",
|
||||
"@ngneat/transloco-preload-langs": "^5.0.0",
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"@swimlane/ngx-charts": "^20.1.2",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"@tweenjs/tween.js": "^21.0.0",
|
||||
"bootstrap": "^5.3.2",
|
||||
"charts.css": "^1.1.0",
|
||||
@ -52,13 +52,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.0.0",
|
||||
"@angular-eslint/builder": "^17.0.0",
|
||||
"@angular-eslint/builder": "^17.0.1",
|
||||
"@angular-eslint/eslint-plugin": "^17.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^17.0.0",
|
||||
"@angular-eslint/schematics": "^17.0.1",
|
||||
"@angular-eslint/template-parser": "^17.0.1",
|
||||
"@angular/cli": "^17.0.0",
|
||||
"@angular/compiler-cli": "^17.0.1",
|
||||
"@angular/compiler-cli": "^17.0.2",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/luxon": "^3.3.4",
|
||||
@ -744,9 +744,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular-eslint/builder": {
|
||||
"version": "17.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-17.0.0.tgz",
|
||||
"integrity": "sha512-cquqJH0R/IIh2PElcGXdo9FTcrkwO78H2MXk9ChGFBjQrYjihFLhFm12VuQsih7X6bJjA0cmr2PL1KbtgjMk1Q==",
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-17.0.1.tgz",
|
||||
"integrity": "sha512-bNXi5tdqIFdNDHxphDRUUbzA+7v6emOX2B/PFLG2pe+K6/JpHS0auwY/nq7hCroH7pMS5HZ+Q4i90q0GN/DWPg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@nx/devkit": "17.0.3",
|
||||
@ -842,9 +842,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/animations": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.1.tgz",
|
||||
"integrity": "sha512-Uee6E8zyU6XjDfKFozybcf+JZy0nUFQ1bUEmRwFP5HvYJSSJ5YiUDokNiVxyn9znwZ7zKHlM6Bq9ZY9cCmeKKQ==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/animations/-/animations-17.0.2.tgz",
|
||||
"integrity": "sha512-32RHWhTgFLMonI3kRdstACay/nvetfxXjdwcTtABjcvBoND7nD9GMhkISQdgS+hcR/IhgXxaPidq8f2UAY5DBw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -852,7 +852,7 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "17.0.1"
|
||||
"@angular/core": "17.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/cdk": {
|
||||
@ -939,9 +939,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@angular/common": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.1.tgz",
|
||||
"integrity": "sha512-AvvhZc+PhX5lVEW/Vorxe3Zf1rIEJJvfduRuRv+nsjijo3ZGjdgYjTYEx4ighZgH60RLIAuwyBE24gPkT2Pm7g==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/common/-/common-17.0.2.tgz",
|
||||
"integrity": "sha512-hCW0njHgrcwTWNoKZDwf02DnhYLVWNXM2FMw66MKpfxTp7McSyaXjGBU9/hchW3dZJ0xTwyxoyoqJFoHYvg0yg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -949,14 +949,14 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "17.0.1",
|
||||
"@angular/core": "17.0.2",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.1.tgz",
|
||||
"integrity": "sha512-qlKqCvjoxPHJ1e8+UMaBl/n9zzrmGXI5eWMVhULSvQnQvPWkwNlUh5XFeoSFcTEQxORjaO2/08Z31DmTJAqlPA==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-17.0.2.tgz",
|
||||
"integrity": "sha512-ewUFbKhMEhAmw2dGfk0ImhTlyrO2y4pJSKIZdFrkR1d0HiJX8bCHUdTiiR/2jeP7w2eamjXj15Rptb+iZZes2Q==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -964,7 +964,7 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/core": "17.0.1"
|
||||
"@angular/core": "17.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/core": {
|
||||
@ -973,9 +973,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/compiler-cli": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.1.tgz",
|
||||
"integrity": "sha512-Rnvh2V2CYhG7NR5VI4cESGKk9jyqLat0HoqXa06v3TtbjkiZyjjwh0SyZ8NYOBMkQeWiQTHGcgxGvjKD3L3qqA==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-17.0.2.tgz",
|
||||
"integrity": "sha512-IUYL3Yz5RbR0Z0/x7it4GK3sMb2qVihxu0tlgfUW53P1Vi6nU/Zda0bCJTu6Z64qEtS8zwCwF1Ekomuq6UaiKg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/core": "7.23.2",
|
||||
@ -996,14 +996,14 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/compiler": "17.0.1",
|
||||
"@angular/compiler": "17.0.2",
|
||||
"typescript": ">=5.2 <5.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/core": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.1.tgz",
|
||||
"integrity": "sha512-yVwU+oz0G8g6Q5ORyOCpgqMPdSiCdfW+uQhjI37WROnXHja3jY843AqrYTKE6mMx1r6q9h1wbDy+x2E61OWP7A==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/core/-/core-17.0.2.tgz",
|
||||
"integrity": "sha512-MjDxWeyn3Txi0qo/V/I+B/gndh0uptQ0XWgBRwOx6Wcr5zRGeZIFlXBxPpyXnGTlJkeyErsTN7FfFCZ4C3kCPA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -1016,9 +1016,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/forms": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.1.tgz",
|
||||
"integrity": "sha512-FpmUf2kgzwZXVbFB4VrwbnrO0m88QLUBsDsbLfQVQQwb7KxwSaftUu/aIrjst1gFCdl9k0Vqtrq2gwLZKzdSGQ==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/forms/-/forms-17.0.2.tgz",
|
||||
"integrity": "sha512-w1QKifaVG4daxUktcBNZqBtOH1vn8t0YiwDR3woEdUYt0XYKMipfDzQfyIK+6fIVPOJUd42pRns1nbWJQHOInA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -1026,16 +1026,16 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "17.0.1",
|
||||
"@angular/core": "17.0.1",
|
||||
"@angular/platform-browser": "17.0.1",
|
||||
"@angular/common": "17.0.2",
|
||||
"@angular/core": "17.0.2",
|
||||
"@angular/platform-browser": "17.0.2",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/localize": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.1.tgz",
|
||||
"integrity": "sha512-pNLLnEbXjoW1agKwA4cBcM/HnqGuwQMpIhx9H46Y/oC2JkAvTCMVyXLbZUESeXmhysC9x2JDmF+Awhu7JzVVCA==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/localize/-/localize-17.0.2.tgz",
|
||||
"integrity": "sha512-ct8xEy8Xk+PRfjrHLu7uywSQDzozmzlz6ptUCuYkRHrS4rJabXn3c0Sz4w+mh9B58qrK6KM+JSmXEZngEMXMTw==",
|
||||
"dependencies": {
|
||||
"@babel/core": "7.23.2",
|
||||
"fast-glob": "3.3.1",
|
||||
@ -1050,14 +1050,14 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/compiler": "17.0.1",
|
||||
"@angular/compiler-cli": "17.0.1"
|
||||
"@angular/compiler": "17.0.2",
|
||||
"@angular/compiler-cli": "17.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.1.tgz",
|
||||
"integrity": "sha512-JpvU0YDEM5KYdHtxC0Kdzk/hdwvZPq5vju5lTmIjTVa2OOabApOrQ6cq1MpKlrvjv1rw8MClHIM0l5Y0g9KH5g==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-17.0.2.tgz",
|
||||
"integrity": "sha512-eTnPILEA/eAMkVUR/+g6fWhhMTmnmOzcZSGX/bBgQcvOhayZrDDxA6/Qf+jIB4RwC0wd3KA9zT5BCMmNojoUsg==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -1065,9 +1065,9 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/animations": "17.0.1",
|
||||
"@angular/common": "17.0.1",
|
||||
"@angular/core": "17.0.1"
|
||||
"@angular/animations": "17.0.2",
|
||||
"@angular/common": "17.0.2",
|
||||
"@angular/core": "17.0.2"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@angular/animations": {
|
||||
@ -1076,9 +1076,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/platform-browser-dynamic": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.1.tgz",
|
||||
"integrity": "sha512-xEcbB/ukXc65LaX4JBQYEM7D5Z8LcUIZniSJFneY7deZt3wNiKgmPZrPoXUyDV26QULh7N0IADEzvbcMF60AFQ==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-17.0.2.tgz",
|
||||
"integrity": "sha512-clcHqHcfD00/TlTixDbJ3q4EQxpm0t2ZFG76rRFmGrmE5tKYUPfaofIa3hQCxy3q269MAYuF16wALhUtrEWyUA==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -1086,16 +1086,16 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "17.0.1",
|
||||
"@angular/compiler": "17.0.1",
|
||||
"@angular/core": "17.0.1",
|
||||
"@angular/platform-browser": "17.0.1"
|
||||
"@angular/common": "17.0.2",
|
||||
"@angular/compiler": "17.0.2",
|
||||
"@angular/core": "17.0.2",
|
||||
"@angular/platform-browser": "17.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@angular/router": {
|
||||
"version": "17.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.1.tgz",
|
||||
"integrity": "sha512-73PCDDsRAjemODMRndZhwEN6Tb9rVVbDfMWgLQ4HgfgKnjek8P9BoYf8rOf3qV5fXf3c1Sm9MmKtaPv+l5lU9Q==",
|
||||
"version": "17.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@angular/router/-/router-17.0.2.tgz",
|
||||
"integrity": "sha512-A1Ulv4qBAtJyK5g1yBlK1qZHe+KaaL5vMPAaPWUxICH8lHEodDkJlbYAUI2e4VL2BN7zBmdOep6tlBKPmHY3mw==",
|
||||
"dependencies": {
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
@ -1103,9 +1103,9 @@
|
||||
"node": "^18.13.0 || >=20.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@angular/common": "17.0.1",
|
||||
"@angular/core": "17.0.1",
|
||||
"@angular/platform-browser": "17.0.1",
|
||||
"@angular/common": "17.0.2",
|
||||
"@angular/core": "17.0.2",
|
||||
"@angular/platform-browser": "17.0.2",
|
||||
"rxjs": "^6.5.3 || ^7.4.0"
|
||||
}
|
||||
},
|
||||
@ -4251,9 +4251,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@swimlane/ngx-charts": {
|
||||
"version": "20.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-20.4.1.tgz",
|
||||
"integrity": "sha512-DyTQe0fcqLDoLEZca45gkdjxP8iLH7kh4pCkr+TCFIkmgEdfQ5DpavNBOOVO0qd5J5uV/tbtSnkYWSx8JkbFpg==",
|
||||
"version": "20.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@swimlane/ngx-charts/-/ngx-charts-20.5.0.tgz",
|
||||
"integrity": "sha512-PNBIHdu/R3ceD7jnw1uCBVOj4k3T6IxfdW6xsDsglGkZyoWMEEq4tLoEurjLEKzmDtRv9c35kVNOXy0lkOuXeA==",
|
||||
"dependencies": {
|
||||
"d3-array": "^3.1.1",
|
||||
"d3-brush": "^3.0.0",
|
||||
@ -4262,6 +4262,7 @@
|
||||
"d3-format": "^3.1.0",
|
||||
"d3-hierarchy": "^3.1.0",
|
||||
"d3-interpolate": "^3.0.1",
|
||||
"d3-sankey": "^0.12.3",
|
||||
"d3-scale": "^4.0.2",
|
||||
"d3-selection": "^3.0.0",
|
||||
"d3-shape": "^3.2.0",
|
||||
@ -7166,6 +7167,41 @@
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-sankey": {
|
||||
"version": "0.12.3",
|
||||
"resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz",
|
||||
"integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==",
|
||||
"dependencies": {
|
||||
"d3-array": "1 - 2",
|
||||
"d3-shape": "^1.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-sankey/node_modules/d3-array": {
|
||||
"version": "2.12.1",
|
||||
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz",
|
||||
"integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==",
|
||||
"dependencies": {
|
||||
"internmap": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-sankey/node_modules/d3-path": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
|
||||
"integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
|
||||
},
|
||||
"node_modules/d3-sankey/node_modules/d3-shape": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz",
|
||||
"integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==",
|
||||
"dependencies": {
|
||||
"d3-path": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/d3-sankey/node_modules/internmap": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz",
|
||||
"integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw=="
|
||||
},
|
||||
"node_modules/d3-scale": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
|
||||
|
@ -13,16 +13,16 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "^17.0.1",
|
||||
"@angular/animations": "^17.0.2",
|
||||
"@angular/cdk": "^17.0.0",
|
||||
"@angular/common": "^17.0.1",
|
||||
"@angular/compiler": "^17.0.1",
|
||||
"@angular/core": "^17.0.1",
|
||||
"@angular/forms": "^17.0.1",
|
||||
"@angular/localize": "^17.0.1",
|
||||
"@angular/platform-browser": "^17.0.1",
|
||||
"@angular/platform-browser-dynamic": "^17.0.1",
|
||||
"@angular/router": "^17.0.1",
|
||||
"@angular/common": "^17.0.2",
|
||||
"@angular/compiler": "^17.0.2",
|
||||
"@angular/core": "^17.0.2",
|
||||
"@angular/forms": "^17.0.2",
|
||||
"@angular/localize": "^17.0.2",
|
||||
"@angular/platform-browser": "^17.0.2",
|
||||
"@angular/platform-browser-dynamic": "^17.0.2",
|
||||
"@angular/router": "^17.0.2",
|
||||
"@fortawesome/fontawesome-free": "^6.4.2",
|
||||
"@iharbeck/ngx-virtual-scroller": "^16.0.0",
|
||||
"@iplab/ngx-file-upload": "^16.0.2",
|
||||
@ -34,7 +34,7 @@
|
||||
"@ngneat/transloco-persist-translations": "^5.0.0",
|
||||
"@ngneat/transloco-preload-langs": "^5.0.0",
|
||||
"@popperjs/core": "^2.11.7",
|
||||
"@swimlane/ngx-charts": "^20.1.2",
|
||||
"@swimlane/ngx-charts": "^20.5.0",
|
||||
"@tweenjs/tween.js": "^21.0.0",
|
||||
"bootstrap": "^5.3.2",
|
||||
"charts.css": "^1.1.0",
|
||||
@ -57,13 +57,13 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "^17.0.0",
|
||||
"@angular-eslint/builder": "^17.0.0",
|
||||
"@angular-eslint/builder": "^17.0.1",
|
||||
"@angular-eslint/eslint-plugin": "^17.0.0",
|
||||
"@angular-eslint/eslint-plugin-template": "^17.0.0",
|
||||
"@angular-eslint/schematics": "^17.0.1",
|
||||
"@angular-eslint/template-parser": "^17.0.1",
|
||||
"@angular/cli": "^17.0.0",
|
||||
"@angular/compiler-cli": "^17.0.1",
|
||||
"@angular/compiler-cli": "^17.0.2",
|
||||
"@types/d3": "^7.4.3",
|
||||
"@types/file-saver": "^2.0.7",
|
||||
"@types/luxon": "^3.3.4",
|
||||
|
@ -34,18 +34,18 @@ export interface SeriesMetadata {
|
||||
summaryLocked: boolean;
|
||||
genresLocked: boolean;
|
||||
tagsLocked: boolean;
|
||||
writersLocked: boolean;
|
||||
coverArtistsLocked: boolean;
|
||||
publishersLocked: boolean;
|
||||
charactersLocked: boolean;
|
||||
pencillersLocked: boolean;
|
||||
inkersLocked: boolean;
|
||||
coloristsLocked: boolean;
|
||||
letterersLocked: boolean;
|
||||
editorsLocked: boolean;
|
||||
translatorsLocked: boolean;
|
||||
writerLocked: boolean;
|
||||
coverArtistLocked: boolean;
|
||||
publisherLocked: boolean;
|
||||
characterLocked: boolean;
|
||||
pencillerLocked: boolean;
|
||||
inkerLocked: boolean;
|
||||
coloristLocked: boolean;
|
||||
lettererLocked: boolean;
|
||||
editorLocked: boolean;
|
||||
translatorLocked: boolean;
|
||||
ageRatingLocked: boolean;
|
||||
releaseYearLocked: boolean;
|
||||
languageLocked: boolean;
|
||||
publicationStatusLocked: boolean;
|
||||
}
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ export class UtcToLocalTimePipe implements PipeTransform {
|
||||
case 'short':
|
||||
return dateTime.toLocaleString(DateTime.DATETIME_SHORT);
|
||||
case 'shortDate':
|
||||
return dateTime.toLocaleString(DateTime.DATE_MED);
|
||||
return dateTime.toLocaleString(DateTime.DATE_SHORT);
|
||||
case 'shortTime':
|
||||
return dateTime.toLocaleString(DateTime.TIME_SIMPLE);
|
||||
case 'full':
|
||||
|
@ -8,7 +8,7 @@
|
||||
<div class="mb-3">
|
||||
<label for="typeahead-focus" class="form-label">{{t('path-label')}}</label>
|
||||
<div class="input-group">
|
||||
<input id="typeahead-focus" type="text" class="form-control" [(ngModel)]="path" [ngbTypeahead]="search"
|
||||
<input id="typeahead-focus" type="text" class="form-control" style="width: 100%" [(ngModel)]="path" [ngbTypeahead]="search"
|
||||
(focus)="focus$.next($any($event).target.value)" (click)="click$.next($any($event).target.value)"
|
||||
(ngModelChange)="updateTable()" #instance="ngbTypeahead" [placeholder]="t('path-placeholder')"
|
||||
[resultTemplate]="rt" />
|
||||
|
@ -17,10 +17,10 @@ export interface DirectoryPickerResult {
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'app-directory-picker',
|
||||
templateUrl: './directory-picker.component.html',
|
||||
styleUrls: ['./directory-picker.component.scss'],
|
||||
standalone: true,
|
||||
selector: 'app-directory-picker',
|
||||
templateUrl: './directory-picker.component.html',
|
||||
styleUrls: ['./directory-picker.component.scss'],
|
||||
standalone: true,
|
||||
imports: [ReactiveFormsModule, NgbTypeahead, FormsModule, NgbHighlight, NgIf, NgFor, NgClass, TranslocoDirective]
|
||||
})
|
||||
export class DirectoryPickerComponent implements OnInit {
|
||||
@ -37,7 +37,7 @@ export class DirectoryPickerComponent implements OnInit {
|
||||
|
||||
|
||||
path: string = '';
|
||||
@ViewChild('instance', {static: true}) instance!: NgbTypeahead;
|
||||
@ViewChild('instance', {static: false}) instance!: NgbTypeahead;
|
||||
focus$ = new Subject<string>();
|
||||
click$ = new Subject<string>();
|
||||
searching: boolean = false;
|
||||
|
@ -24,9 +24,6 @@
|
||||
<ng-container *ngIf="tab.fragment === TabID.Libraries">
|
||||
<app-manage-library></app-manage-library>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === TabID.Logs">
|
||||
<app-manage-logs></app-manage-logs>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === TabID.System">
|
||||
<app-manage-system></app-manage-system>
|
||||
</ng-container>
|
||||
|
@ -38,7 +38,10 @@ enum TabID {
|
||||
templateUrl: './dashboard.component.html',
|
||||
styleUrls: ['./dashboard.component.scss'],
|
||||
standalone: true,
|
||||
imports: [SideNavCompanionBarComponent, NgbNav, NgFor, NgbNavItem, NgbNavItemRole, NgbNavLink, RouterLink, NgbNavContent, NgIf, ManageSettingsComponent, ManageEmailSettingsComponent, ManageMediaSettingsComponent, ManageUsersComponent, ManageLibraryComponent, ManageLogsComponent, ManageSystemComponent, ServerStatsComponent, ManageTasksSettingsComponent, LicenseComponent, NgbNavOutlet, SentenceCasePipe, TranslocoDirective],
|
||||
imports: [SideNavCompanionBarComponent, NgbNav, NgFor, NgbNavItem, NgbNavItemRole, NgbNavLink, RouterLink,
|
||||
NgbNavContent, NgIf, ManageSettingsComponent, ManageEmailSettingsComponent, ManageMediaSettingsComponent,
|
||||
ManageUsersComponent, ManageLibraryComponent, ManageSystemComponent, ServerStatsComponent,
|
||||
ManageTasksSettingsComponent, LicenseComponent, NgbNavOutlet, SentenceCasePipe, TranslocoDirective],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class DashboardComponent implements OnInit {
|
||||
@ -47,7 +50,6 @@ export class DashboardComponent implements OnInit {
|
||||
{title: 'general-tab', fragment: TabID.General},
|
||||
{title: 'users-tab', fragment: TabID.Users},
|
||||
{title: 'libraries-tab', fragment: TabID.Libraries},
|
||||
//{title: 'logs-tab', fragment: TabID.Logs},
|
||||
{title: 'media-tab', fragment: TabID.Media},
|
||||
{title: 'email-tab', fragment: TabID.Email},
|
||||
{title: 'tasks-tab', fragment: TabID.Tasks},
|
||||
|
@ -44,7 +44,7 @@ import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
|
||||
})
|
||||
export class AllSeriesComponent implements OnInit {
|
||||
|
||||
title!: string;
|
||||
title: string = translate('side-nav.all-series');
|
||||
series: Series[] = [];
|
||||
loadingSeries = false;
|
||||
pagination: Pagination = new Pagination();
|
||||
@ -115,10 +115,8 @@ export class AllSeriesComponent implements OnInit {
|
||||
|
||||
this.filterUtilityService.filterPresetsFromUrl(this.route.snapshot).subscribe(filter => {
|
||||
this.filter = filter;
|
||||
|
||||
this.title = this.route.snapshot.queryParamMap.get('title') || this.filter.name || this.title;
|
||||
this.titleService.setTitle('Kavita - ' + this.title);
|
||||
|
||||
this.filterActiveCheck = this.filterUtilityService.createSeriesV2Filter();
|
||||
this.filterActiveCheck!.statements.push(this.filterUtilityService.createSeriesV2DefaultStatement());
|
||||
this.filterSettings.presetsV2 = this.filter;
|
||||
@ -128,7 +126,6 @@ export class AllSeriesComponent implements OnInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.title = translate('all-series.title');
|
||||
this.hubService.messages$.pipe(debounceTime(6000), takeUntilDestroyed(this.destroyRef)).subscribe((event: Message<any>) => {
|
||||
if (event.event !== EVENTS.SeriesAdded) return;
|
||||
this.loadPage();
|
||||
|
@ -81,6 +81,7 @@ const routes: Routes = [
|
||||
},
|
||||
]
|
||||
},
|
||||
{path: '', pathMatch: 'full', redirectTo: 'home'},
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -91,9 +92,10 @@ const routes: Routes = [
|
||||
path: 'login',
|
||||
loadChildren: () => import('./_routes/registration.router.module').then(m => m.routes) // TODO: Refactor so we just use /registration/login going forward
|
||||
},
|
||||
{path: '**', pathMatch: 'full', redirectTo: 'home'},
|
||||
{path: 'libraries', pathMatch: 'full', redirectTo: 'home'},
|
||||
{path: '**', pathMatch: 'prefix', redirectTo: 'home'},
|
||||
{path: '**', pathMatch: 'full', redirectTo: 'home'},
|
||||
{path: '', pathMatch: 'full', redirectTo: 'home'},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
@ -1617,9 +1617,7 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {
|
||||
// Responsible for handling pagination only
|
||||
handleContainerClick(event: MouseEvent) {
|
||||
|
||||
console.log('target: ', event.target);
|
||||
if (this.actionBarVisible || ['action-bar'].some(className => (event.target as Element).classList.contains(className))) {
|
||||
//console.log('exiting early')
|
||||
if (this.drawerOpen || ['action-bar'].some(className => (event.target as Element).classList.contains(className))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -187,8 +187,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="writer" class="form-label">{{t('writer-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Writer)" [settings]="getPersonsSettings(PersonRole.Writer)"
|
||||
[(locked)]="metadata.writersLocked" (onUnlock)="metadata.writersLocked = false"
|
||||
(newItemAdded)="metadata.writersLocked = true" (selectedData)="metadata.writersLocked = true">
|
||||
[(locked)]="metadata.writerLocked" (onUnlock)="metadata.writerLocked = false"
|
||||
(newItemAdded)="metadata.writerLocked = true" (selectedData)="metadata.writerLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -202,8 +202,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="cover-artist" class="form-label">{{t('cover-artist-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.CoverArtist)" [settings]="getPersonsSettings(PersonRole.CoverArtist)"
|
||||
[(locked)]="metadata.coverArtistsLocked" (onUnlock)="metadata.coverArtistsLocked = false"
|
||||
(newItemAdded)="metadata.coverArtistsLocked = true" (selectedData)="metadata.coverArtistsLocked = true">
|
||||
[(locked)]="metadata.coverArtistLocked" (onUnlock)="metadata.coverArtistLocked = false"
|
||||
(newItemAdded)="metadata.coverArtistLocked = true" (selectedData)="metadata.coverArtistLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -219,8 +219,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="publisher" class="form-label">{{t('publisher-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Publisher)" [settings]="getPersonsSettings(PersonRole.Publisher)"
|
||||
[(locked)]="metadata.publishersLocked" (onUnlock)="metadata.publishersLocked = false"
|
||||
(newItemAdded)="metadata.publishersLocked = true" (selectedData)="metadata.publishersLocked = true">
|
||||
[(locked)]="metadata.publisherLocked" (onUnlock)="metadata.publisherLocked = false"
|
||||
(newItemAdded)="metadata.publisherLocked = true" (selectedData)="metadata.publisherLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -234,8 +234,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="penciller" class="form-label">{{t('penciller-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Penciller)" [settings]="getPersonsSettings(PersonRole.Penciller)"
|
||||
[(locked)]="metadata.pencillersLocked" (onUnlock)="metadata.pencillersLocked = false"
|
||||
(newItemAdded)="metadata.pencillersLocked = true" (selectedData)="metadata.pencillersLocked = true">
|
||||
[(locked)]="metadata.pencillerLocked" (onUnlock)="metadata.pencillerLocked = false"
|
||||
(newItemAdded)="metadata.pencillerLocked = true" (selectedData)="metadata.pencillerLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -251,8 +251,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="letterer" class="form-label">{{t('letterer-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Letterer)" [settings]="getPersonsSettings(PersonRole.Letterer)"
|
||||
[(locked)]="metadata.letterersLocked" (onUnlock)="metadata.letterersLocked = false"
|
||||
(newItemAdded)="metadata.letterersLocked = true" (selectedData)="metadata.letterersLocked = true">
|
||||
[(locked)]="metadata.lettererLocked" (onUnlock)="metadata.lettererLocked = false"
|
||||
(newItemAdded)="metadata.lettererLocked = true" (selectedData)="metadata.lettererLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -266,8 +266,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="inker" class="form-label">{{t('inker-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Inker)" [settings]="getPersonsSettings(PersonRole.Inker)"
|
||||
[(locked)]="metadata.inkersLocked" (onUnlock)="metadata.inkersLocked = false"
|
||||
(newItemAdded)="metadata.inkersLocked = true" (selectedData)="metadata.inkersLocked = true">
|
||||
[(locked)]="metadata.inkerLocked" (onUnlock)="metadata.inkerLocked = false"
|
||||
(newItemAdded)="metadata.inkerLocked = true" (selectedData)="metadata.inkerLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -284,8 +284,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="editor" class="form-label">{{t('editor-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Editor)" [settings]="getPersonsSettings(PersonRole.Editor)"
|
||||
[(locked)]="metadata.editorsLocked" (onUnlock)="metadata.editorsLocked = false"
|
||||
(newItemAdded)="metadata.editorsLocked = true" (selectedData)="metadata.editorsLocked = true">
|
||||
[(locked)]="metadata.editorLocked" (onUnlock)="metadata.editorLocked = false"
|
||||
(newItemAdded)="metadata.editorLocked = true" (selectedData)="metadata.editorLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -299,8 +299,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="colorist" class="form-label">{{t('colorist-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Colorist)" [settings]="getPersonsSettings(PersonRole.Colorist)"
|
||||
[(locked)]="metadata.coloristsLocked" (onUnlock)="metadata.coloristsLocked = false"
|
||||
(newItemAdded)="metadata.coloristsLocked = true" (selectedData)="metadata.coloristsLocked = true">
|
||||
[(locked)]="metadata.coloristLocked" (onUnlock)="metadata.coloristLocked = false"
|
||||
(newItemAdded)="metadata.coloristLocked = true" (selectedData)="metadata.coloristLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -317,8 +317,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="character" class="form-label">{{t('character-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Character)" [settings]="getPersonsSettings(PersonRole.Character)"
|
||||
[(locked)]="metadata.charactersLocked" (onUnlock)="metadata.charactersLocked = false"
|
||||
(newItemAdded)="metadata.charactersLocked = true" (selectedData)="metadata.charactersLocked = true">
|
||||
[(locked)]="metadata.characterLocked" (onUnlock)="metadata.characterLocked = false"
|
||||
(newItemAdded)="metadata.characterLocked = true" (selectedData)="metadata.characterLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
@ -332,8 +332,8 @@
|
||||
<div class="mb-3">
|
||||
<label for="translator" class="form-label">{{t('translator-label')}}</label>
|
||||
<app-typeahead (selectedData)="updatePerson($event, PersonRole.Translator)" [settings]="getPersonsSettings(PersonRole.Translator)"
|
||||
[(locked)]="metadata.translatorsLocked" (onUnlock)="metadata.translatorsLocked = false"
|
||||
(newItemAdded)="metadata.translatorsLocked = true" (selectedData)="metadata.translatorsLocked = true">
|
||||
[(locked)]="metadata.translatorLocked" (onUnlock)="metadata.translatorLocked = false"
|
||||
(newItemAdded)="metadata.translatorLocked = true" (selectedData)="metadata.translatorLocked = true">
|
||||
<ng-template #badgeItem let-item let-position="idx">
|
||||
{{item.name}}
|
||||
</ng-template>
|
||||
|
@ -4,6 +4,7 @@ import {ImageComponent} from "../../shared/image/image.component";
|
||||
import {NextExpectedChapter} from "../../_models/series-detail/next-expected-chapter";
|
||||
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
|
||||
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
|
||||
import {translate} from "@ngneat/transloco";
|
||||
|
||||
@Component({
|
||||
selector: 'app-next-expected-card',
|
||||
@ -39,7 +40,7 @@ export class NextExpectedCardComponent {
|
||||
|
||||
if (this.entity.expectedDate) {
|
||||
const utcPipe = new UtcToLocalTimePipe();
|
||||
this.title = '~ ' + utcPipe.transform(this.entity.expectedDate, 'shortDate');
|
||||
this.title = translate('next-expected-card.title', {date: utcPipe.transform(this.entity.expectedDate, 'shortDate')});
|
||||
}
|
||||
this.cdRef.markForCheck();
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ export class ReadingListDetailComponent implements OnInit {
|
||||
this.router.navigateByUrl('/home');
|
||||
return;
|
||||
}
|
||||
this.titleService.setTitle('Kavita - ' + translate('side-nav.reading-lists'));
|
||||
|
||||
this.listId = parseInt(listId, 10);
|
||||
this.characters$ = this.readingListService.getCharacters(this.listId);
|
||||
|
||||
@ -94,6 +94,8 @@ export class ReadingListDetailComponent implements OnInit {
|
||||
const libraries = results[0];
|
||||
const readingList = results[1];
|
||||
|
||||
this.titleService.setTitle('Kavita - ' + readingList.title);
|
||||
|
||||
libraries.forEach(lib => {
|
||||
this.libraryTypes[lib.id] = lib.type;
|
||||
});
|
||||
|
@ -17,9 +17,9 @@ import { CardItemComponent } from '../../../cards/card-item/card-item.component'
|
||||
import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/card-detail-layout.component';
|
||||
import { NgIf, DecimalPipe } from '@angular/common';
|
||||
import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
|
||||
import {TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||
import {translate, TranslocoDirective, TranslocoService} from "@ngneat/transloco";
|
||||
import {CardActionablesComponent} from "../../../_single-module/card-actionables/card-actionables.component";
|
||||
import {CollectionTag} from "../../../_models/collection-tag";
|
||||
import {Title} from "@angular/platform-browser";
|
||||
|
||||
@Component({
|
||||
selector: 'app-reading-lists',
|
||||
@ -43,13 +43,14 @@ export class ReadingListsComponent implements OnInit {
|
||||
translocoService = inject(TranslocoService);
|
||||
constructor(private readingListService: ReadingListService, public imageService: ImageService, private actionFactoryService: ActionFactoryService,
|
||||
private accountService: AccountService, private toastr: ToastrService, private router: Router, private actionService: ActionService,
|
||||
private jumpbarService: JumpbarService, private readonly cdRef: ChangeDetectorRef, private ngbModal: NgbModal) { }
|
||||
private jumpbarService: JumpbarService, private readonly cdRef: ChangeDetectorRef, private ngbModal: NgbModal, private titleService: Title) { }
|
||||
|
||||
ngOnInit(): void {
|
||||
this.accountService.currentUser$.pipe(take(1)).subscribe(user => {
|
||||
if (user) {
|
||||
this.isAdmin = this.accountService.hasAdminRole(user);
|
||||
this.loadPage();
|
||||
this.titleService.setTitle('Kavita - ' + translate('side-nav.reading-lists'));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -64,7 +64,7 @@ export class ImportCblModalComponent {
|
||||
{title: this.translocoService.translate('import-cbl-modal.import-step'), index: Step.Import, active: true, icon: 'fa-solid fa-file-arrow-up'},
|
||||
{title: this.translocoService.translate('import-cbl-modal.validate-cbl-step'), index: Step.Validate, active: false, icon: 'fa-solid fa-spell-check'},
|
||||
{title: this.translocoService.translate('import-cbl-modal.dry-run-step'), index: Step.DryRun, active: false, icon: 'fa-solid fa-gears'},
|
||||
{title: this.translocoService.translate('import-cbl-final-import.import-step'), index: Step.Finalize, active: false, icon: 'fa-solid fa-floppy-disk'},
|
||||
{title: this.translocoService.translate('import-cbl-modal.final-import-step'), index: Step.Finalize, active: false, icon: 'fa-solid fa-floppy-disk'},
|
||||
];
|
||||
currentStepIndex = this.steps[0].index;
|
||||
|
||||
|
@ -80,7 +80,7 @@
|
||||
<p *ngIf="isAddLibrary" class="alert alert-secondary" role="alert">{{t('cover-description')}}</p>
|
||||
<p>{{t('cover-description-extra')}}</p>
|
||||
<app-cover-image-chooser [(imageUrls)]="imageUrls" (imageSelected)="updateCoverImageIndex($event)"
|
||||
(selectedBase64Url)="applyCoverImage($event)" [showReset]="library.coverImage !== null"
|
||||
(selectedBase64Url)="applyCoverImage($event)" [showReset]="library?.coverImage !== null"
|
||||
(resetClicked)="resetCoverImage()"></app-cover-image-chooser>
|
||||
</ng-template>
|
||||
</li>
|
||||
|
@ -1,5 +1,5 @@
|
||||
.confirm-icon {
|
||||
color: var(--primary-color);
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
@ -9,15 +9,17 @@
|
||||
<li *ngFor="let tab of tabs" [ngbNavItem]="tab">
|
||||
<a ngbNavLink routerLink="." [fragment]="tab.fragment">{{ t(tab.title) }}</a>
|
||||
<ng-template ngbNavContent>
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Account">
|
||||
<app-change-email></app-change-email>
|
||||
@defer (when tab.fragment === FragmentID.Account; prefetch on idle) {
|
||||
<app-change-email></app-change-email>
|
||||
<app-change-password></app-change-password>
|
||||
<app-change-age-restriction></app-change-age-restriction>
|
||||
<app-anilist-key></app-anilist-key>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
@defer (when tab.fragment === FragmentID.Preferences; prefetch on idle) {
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Preferences">
|
||||
<p>
|
||||
{{t('pref-description')}}
|
||||
{{t('pref-description')}}
|
||||
</p>
|
||||
|
||||
<form [formGroup]="settingsForm" *ngIf="user !== undefined">
|
||||
@ -25,420 +27,418 @@
|
||||
<div ngbAccordionItem [id]="AccordionPanelID.GlobalSettings" [collapsed]="false">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded(AccordionPanelID.GlobalSettings)" aria-controls="collapseOne">
|
||||
{{t('global-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-global-layoutmode" class="form-label">{{t('page-layout-mode-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="layoutModeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #layoutModeTooltip>{{t('page-layout-mode-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-layoutmode-help">
|
||||
<ng-container [ngTemplateOutlet]="layoutModeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="globalPageLayoutMode" id="settings-global-layoutmode">
|
||||
<option *ngFor="let opt of pageLayoutModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-global-locale" class="form-label">{{t('locale-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1"
|
||||
aria-hidden="true" placement="right" [ngbTooltip]="localeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #localeTooltip>{{t('locale-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-locale-help">
|
||||
<ng-container [ngTemplateOutlet]="localeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="locale" id="settings-global-locale">
|
||||
<option *ngFor="let opt of locales" [value]="opt.isoCode">{{opt.title | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="blur-unread-summaries" role="switch" formControlName="blurUnreadSummaries" class="form-check-input" aria-describedby="settings-global-blurUnreadSummaries-help" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="blur-unread-summaries">{{t('blur-unread-summaries-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="blurUnreadSummariesTooltip" role="button" tabindex="0"></i>
|
||||
{{t('global-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-global-layoutmode" class="form-label">{{t('page-layout-mode-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="layoutModeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #layoutModeTooltip>{{t('page-layout-mode-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-layoutmode-help">
|
||||
<ng-container [ngTemplateOutlet]="layoutModeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="globalPageLayoutMode" id="settings-global-layoutmode">
|
||||
<option *ngFor="let opt of pageLayoutModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<ng-template #blurUnreadSummariesTooltip>{{t('blur-unread-summaries-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-blurUnreadSummaries-help">
|
||||
<ng-container [ngTemplateOutlet]="blurUnreadSummariesTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="prompt-download" role="switch" formControlName="promptForDownloadSize" class="form-check-input" aria-describedby="settings-global-promptForDownloadSize-help" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="prompt-download">{{t('prompt-on-download-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="promptForDownloadSizeTooltip" role="button" tabindex="0"></i>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-global-locale" class="form-label">{{t('locale-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1"
|
||||
aria-hidden="true" placement="right" [ngbTooltip]="localeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #localeTooltip>{{t('locale-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-locale-help">
|
||||
<ng-container [ngTemplateOutlet]="localeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="locale" id="settings-global-locale">
|
||||
<option *ngFor="let opt of locales" [value]="opt.isoCode">{{opt.title | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<ng-template #promptForDownloadSizeTooltip>{{t('prompt-on-download-tooltip', {size: '100'})}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-promptForDownloadSize-help">
|
||||
<ng-container [ngTemplateOutlet]="promptForDownloadSizeTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="no-transitions" role="switch" formControlName="noTransitions" class="form-check-input"
|
||||
aria-describedby="settings-global-noTransitions-help" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="no-transitions">{{t('disable-animations-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="noTransitionsTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
|
||||
<ng-template #noTransitionsTooltip>{{t('disable-animations-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-noTransitions-help">
|
||||
<ng-container [ngTemplateOutlet]="noTransitionsTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-12 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="collapse-relationships" role="switch" formControlName="collapseSeriesRelationships"
|
||||
aria-describedby="settings-collapse-relationships-help" class="form-check-input" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="collapse-relationships">{{t('collapse-series-relationships-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="collapseSeriesRelationshipsTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
<ng-template #collapseSeriesRelationshipsTooltip>{{t('collapse-series-relationships-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-collapse-relationships-help">
|
||||
<ng-container [ngTemplateOutlet]="collapseSeriesRelationshipsTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-12 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="share-reviews" role="switch" formControlName="shareReviews"
|
||||
aria-describedby="settings-share-reviews-help" class="form-check-input" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="share-reviews">{{t('share-series-reviews-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="shareReviewsTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
<ng-template #shareReviewsTooltip>{{t('share-series-reviews-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-share-reviews-help">
|
||||
<ng-container [ngTemplateOutlet]="shareReviewsTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()" aria-describedby="reading-panel">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="save()" aria-describedby="reading-panel" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbAccordionItem [id]="AccordionPanelID.ImageReader">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded(AccordionPanelID.ImageReader)" aria-controls="collapseOne">
|
||||
{{t('image-reader-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-reading-direction" class="form-label">{{t('reading-direction-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="readingDirectionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #readingDirectionTooltip>{{t('reading-direction-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-reading-direction-help">
|
||||
<ng-container [ngTemplateOutlet]="readingDirectionTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="readingDirection" id="settings-reading-direction">
|
||||
<option *ngFor="let opt of readingDirectionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-scaling-option" class="form-label">{{t('scaling-option-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="scalingOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #scalingOptionTooltip>{{t('scaling-option-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-scaling-option-help">
|
||||
<ng-container [ngTemplateOutlet]="scalingOptionTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="scalingOption" id="settings-scaling-option">
|
||||
<option *ngFor="let opt of scalingOptionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-pagesplit-option" class="form-label">{{t('page-splitting-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="pageSplitOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #pageSplitOptionTooltip>{{t('page-splitting-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-pagesplit-option-help">
|
||||
<ng-container [ngTemplateOutlet]="pageSplitOptionTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="pageSplitOption" id="settings-pagesplit-option">
|
||||
<option *ngFor="let opt of pageSplitOptionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-readingmode-option" class="form-label">{{t('reading-mode-label')}}</label>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="readerMode" id="settings-readingmode-option">
|
||||
<option *ngFor="let opt of readingModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2" *ngIf="true">
|
||||
<label for="settings-layoutmode-option" class="form-label">{{t('layout-mode-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="layoutModeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #layoutModeTooltip>{{t('layout-mode-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-layoutmode-option-help">
|
||||
<ng-container [ngTemplateOutlet]="layoutModeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="layoutMode" id="settings-layoutmode-option">
|
||||
<option *ngFor="let opt of layoutModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-background-color-option" class="form-label">{{t('background-color-label')}}</label>
|
||||
<input [value]="user!.preferences!.backgroundColor"
|
||||
class="form-control"
|
||||
id="settings-background-color-option"
|
||||
(colorPickerChange)="handleBackgroundColorChange()"
|
||||
[style.background]="user!.preferences!.backgroundColor"
|
||||
[cpAlphaChannel]="'disabled'"
|
||||
[(colorPicker)]="user!.preferences!.backgroundColor"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="auto-close" role="switch" formControlName="autoCloseMenu" class="form-check-input" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="auto-close">{{t('auto-close-menu-label')}}</label>
|
||||
<input type="checkbox" id="blur-unread-summaries" role="switch" formControlName="blurUnreadSummaries" class="form-check-input" aria-describedby="settings-global-blurUnreadSummaries-help" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="blur-unread-summaries">{{t('blur-unread-summaries-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="blurUnreadSummariesTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="show-screen-hints" role="switch" formControlName="showScreenHints" class="form-check-input" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="show-screen-hints">{{t('show-screen-hints-label')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="emulate-book" role="switch" formControlName="emulateBook" class="form-check-input" [value]="true">
|
||||
<label class="form-check-label me-1" for="emulate-book">{{t('emulate-comic-book-label')}}</label><i class="fa fa-info-circle" aria-hidden="true" placement="top" ngbTooltip="Applies a shadow effect to emulate reading from a book" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="swipe-to-paginate" role="switch" formControlName="swipeToPaginate" class="form-check-input" [value]="true">
|
||||
<label class="form-check-label me-1" for="swipe-to-paginate">{{t('swipe-to-paginate-label')}}</label><i class="fa fa-info-circle" aria-hidden="true" placement="top" ngbTooltip="Should swiping on the screen cause the next or previous page to be triggered" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()" aria-describedby="reading-panel">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="save()" aria-describedby="reading-panel" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbAccordionItem [id]="AccordionPanelID.BookReader">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded(AccordionPanelID.BookReader)" aria-controls="collapseOne">
|
||||
{{t('book-reader-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<label id="taptopaginate-label" class="form-label"></label>
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" role="switch" id="taptopaginate" formControlName="bookReaderTapToPaginate" class="form-check-input" [value]="true" aria-labelledby="taptopaginate-label">
|
||||
<label for="taptopaginate" class="form-check-label">{{t('tap-to-paginate-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="tapToPaginateOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #tapToPaginateOptionTooltip>{{t('tap-to-paginate-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-taptopaginate-option-help">
|
||||
<ng-container [ngTemplateOutlet]="tapToPaginateOptionTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<label id="immersivemode-label" class="form-label"></label>
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" role="switch" id="immersivemode" formControlName="bookReaderImmersiveMode" class="form-check-input" [value]="true" aria-labelledby="immersivemode-label">
|
||||
<label for="immersivemode" class="form-check-label">{{t('immersive-mode-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="immersivemodeOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #immersivemodeOptionTooltip>{{t('immersive-mode-label')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-immersivemode-option-help">
|
||||
<ng-container [ngTemplateOutlet]="immersivemodeOptionTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-book-reading-direction" class="form-label">{{t('reading-direction-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookReadingDirectionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookReadingDirectionTooltip>{{t('reading-direction-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-book-reading-direction-book-help">
|
||||
<ng-container [ngTemplateOutlet]="bookReadingDirectionTooltip"></ng-container>
|
||||
</span>
|
||||
<select id="settings-book-reading-direction" class="form-select" aria-describedby="settings-book-reading-direction-help" formControlName="bookReaderReadingDirection">
|
||||
<option *ngFor="let opt of readingDirectionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-fontfamily-option" class="form-label">{{t('font-family-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="fontFamilyOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #fontFamilyOptionTooltip>{{t('font-family-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-fontfamily-option-help">
|
||||
<ng-container [ngTemplateOutlet]="fontFamilyOptionTooltip"></ng-container>
|
||||
</span>
|
||||
<select id="settings-fontfamily-option" class="form-select" aria-describedby="settings-fontfamily-option-help" formControlName="bookReaderFontFamily">
|
||||
<option *ngFor="let opt of fontFamilies" [value]="opt">{{opt | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-book-writing-style" class="form-label me-1">{{t('writing-style-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" aria-describedby="settings-book-writing-style-help" placement="right" [ngbTooltip]="bookWritingStyleToolTip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookWritingStyleToolTip>{{t('writing-style-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-book-writing-style-help">
|
||||
<ng-container [ngTemplateOutlet]="bookWritingStyleToolTip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="settings-book-writing-style-help" formControlName="bookReaderWritingStyle" id="settings-book-writing-style" >
|
||||
<option *ngFor="let opt of bookWritingStylesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-book-layout-mode" class="form-label">{{t('layout-mode-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookLayoutModeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookLayoutModeTooltip>{{t('layout-mode-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-book-layout-mode-help">
|
||||
<ng-container [ngTemplateOutlet]="bookLayoutModeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="settings-book-layout-mode-help" formControlName="bookReaderLayoutMode" id="settings-book-layout-mode">
|
||||
<option *ngFor="let opt of bookLayoutModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-color-theme-option" class="form-label">{{t('color-theme-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookColorThemeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookColorThemeTooltip>{{t('color-theme-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-color-theme-option-help">
|
||||
<ng-container [ngTemplateOutlet]="bookColorThemeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="settings-color-theme-option-help" formControlName="bookReaderThemeName" id="settings-color-theme-option">
|
||||
<option *ngFor="let opt of bookColorThemesTranslated" [value]="opt.name">{{opt.name | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<label for="fontsize" class="form-label range-label">{{t('font-size-book-label')}}</label>
|
||||
<input type="range" class="form-range" id="fontsize"
|
||||
min="50" max="300" step="10" formControlName="bookReaderFontSize">
|
||||
<span class="range-text">{{settingsForm.get('bookReaderFontSize')?.value + '%'}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<div class="range-label">
|
||||
<label class="form-label" for="linespacing">{{t('line-height-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookLineHeightOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookLineHeightOptionTooltip>{{t('line-height-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-booklineheight-option-help">
|
||||
<ng-container [ngTemplateOutlet]="bookLineHeightOptionTooltip"></ng-container>
|
||||
<ng-template #blurUnreadSummariesTooltip>{{t('blur-unread-summaries-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-blurUnreadSummaries-help">
|
||||
<ng-container [ngTemplateOutlet]="blurUnreadSummariesTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
<input type="range" class="form-range" id="linespacing" min="100" max="200" step="10"
|
||||
formControlName="bookReaderLineSpacing" aria-describedby="settings-booklineheight-option-help">
|
||||
<span class="range-text">{{settingsForm.get('bookReaderLineSpacing')?.value + '%'}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<div class="range-label">
|
||||
<label class="form-label">{{t('margin-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookReaderMarginOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookReaderMarginOptionTooltip>{{t('margin-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-bookmargin-option-help">
|
||||
<ng-container [ngTemplateOutlet]="bookReaderMarginOptionTooltip"></ng-container>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="prompt-download" role="switch" formControlName="promptForDownloadSize" class="form-check-input" aria-describedby="settings-global-promptForDownloadSize-help" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="prompt-download">{{t('prompt-on-download-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="promptForDownloadSizeTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
|
||||
<ng-template #promptForDownloadSizeTooltip>{{t('prompt-on-download-tooltip', {size: '100'})}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-promptForDownloadSize-help">
|
||||
<ng-container [ngTemplateOutlet]="promptForDownloadSizeTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<input type="range" class="form-range" id="margin" min="0" max="30" step="5" formControlName="bookReaderMargin" aria-describedby="bookmargin">
|
||||
<span class="range-text">{{settingsForm.get('bookReaderMargin')?.value + '%'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()" aria-describedby="reading-panel">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="save()" aria-describedby="reading-panel" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="no-transitions" role="switch" formControlName="noTransitions" class="form-check-input"
|
||||
aria-describedby="settings-global-noTransitions-help" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="no-transitions">{{t('disable-animations-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="noTransitionsTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
|
||||
<ng-template #noTransitionsTooltip>{{t('disable-animations-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-global-noTransitions-help">
|
||||
<ng-container [ngTemplateOutlet]="noTransitionsTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-12 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="collapse-relationships" role="switch" formControlName="collapseSeriesRelationships"
|
||||
aria-describedby="settings-collapse-relationships-help" class="form-check-input" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="collapse-relationships">{{t('collapse-series-relationships-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="collapseSeriesRelationshipsTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
<ng-template #collapseSeriesRelationshipsTooltip>{{t('collapse-series-relationships-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-collapse-relationships-help">
|
||||
<ng-container [ngTemplateOutlet]="collapseSeriesRelationshipsTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-12 col-sm-12 pe-2 mb-2">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="share-reviews" role="switch" formControlName="shareReviews"
|
||||
aria-describedby="settings-share-reviews-help" class="form-check-input" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="share-reviews">{{t('share-series-reviews-label')}}</label>
|
||||
<i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="shareReviewsTooltip" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
<ng-template #shareReviewsTooltip>{{t('share-series-reviews-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-share-reviews-help">
|
||||
<ng-container [ngTemplateOutlet]="shareReviewsTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()" aria-describedby="reading-panel">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="save()" aria-describedby="reading-panel" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbAccordionItem [id]="AccordionPanelID.ImageReader">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded(AccordionPanelID.ImageReader)" aria-controls="collapseOne">
|
||||
{{t('image-reader-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-reading-direction" class="form-label">{{t('reading-direction-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="readingDirectionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #readingDirectionTooltip>{{t('reading-direction-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-reading-direction-help">
|
||||
<ng-container [ngTemplateOutlet]="readingDirectionTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="readingDirection" id="settings-reading-direction">
|
||||
<option *ngFor="let opt of readingDirectionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-scaling-option" class="form-label">{{t('scaling-option-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="scalingOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #scalingOptionTooltip>{{t('scaling-option-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-scaling-option-help">
|
||||
<ng-container [ngTemplateOutlet]="scalingOptionTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="scalingOption" id="settings-scaling-option">
|
||||
<option *ngFor="let opt of scalingOptionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-pagesplit-option" class="form-label">{{t('page-splitting-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="pageSplitOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #pageSplitOptionTooltip>{{t('page-splitting-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-pagesplit-option-help">
|
||||
<ng-container [ngTemplateOutlet]="pageSplitOptionTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="pageSplitOption" id="settings-pagesplit-option">
|
||||
<option *ngFor="let opt of pageSplitOptionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-readingmode-option" class="form-label">{{t('reading-mode-label')}}</label>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="readerMode" id="settings-readingmode-option">
|
||||
<option *ngFor="let opt of readingModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2" *ngIf="true">
|
||||
<label for="settings-layoutmode-option" class="form-label">{{t('layout-mode-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="layoutModeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #layoutModeTooltip>{{t('layout-mode-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-layoutmode-option-help">
|
||||
<ng-container [ngTemplateOutlet]="layoutModeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="manga-header" formControlName="layoutMode" id="settings-layoutmode-option">
|
||||
<option *ngFor="let opt of layoutModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<label for="settings-background-color-option" class="form-label">{{t('background-color-label')}}</label>
|
||||
<input [value]="user!.preferences!.backgroundColor"
|
||||
class="form-control"
|
||||
id="settings-background-color-option"
|
||||
(colorPickerChange)="handleBackgroundColorChange()"
|
||||
[style.background]="user!.preferences!.backgroundColor"
|
||||
[cpAlphaChannel]="'disabled'"
|
||||
[(colorPicker)]="user!.preferences!.backgroundColor"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="auto-close" role="switch" formControlName="autoCloseMenu" class="form-check-input" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="auto-close">{{t('auto-close-menu-label')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="show-screen-hints" role="switch" formControlName="showScreenHints" class="form-check-input" [value]="true" aria-labelledby="auto-close-label">
|
||||
<label class="form-check-label" for="show-screen-hints">{{t('show-screen-hints-label')}}</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="emulate-book" role="switch" formControlName="emulateBook" class="form-check-input" [value]="true">
|
||||
<label class="form-check-label me-1" for="emulate-book">{{t('emulate-comic-book-label')}}</label><i class="fa fa-info-circle" aria-hidden="true" placement="top" ngbTooltip="Applies a shadow effect to emulate reading from a book" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-2">
|
||||
<div class="mb-3 mt-1">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" id="swipe-to-paginate" role="switch" formControlName="swipeToPaginate" class="form-check-input" [value]="true">
|
||||
<label class="form-check-label me-1" for="swipe-to-paginate">{{t('swipe-to-paginate-label')}}</label><i class="fa fa-info-circle" aria-hidden="true" placement="top" ngbTooltip="Should swiping on the screen cause the next or previous page to be triggered" role="button" tabindex="0"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()" aria-describedby="reading-panel">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="save()" aria-describedby="reading-panel" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ngbAccordionItem [id]="AccordionPanelID.BookReader">
|
||||
<h2 class="accordion-header" ngbAccordionHeader>
|
||||
<button class="accordion-button" ngbAccordionButton type="button" [attr.aria-expanded]="acc.isExpanded(AccordionPanelID.BookReader)" aria-controls="collapseOne">
|
||||
{{t('book-reader-settings-title')}}
|
||||
</button>
|
||||
</h2>
|
||||
<div ngbAccordionCollapse>
|
||||
<div ngbAccordionBody>
|
||||
<ng-template>
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<label id="taptopaginate-label" class="form-label"></label>
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" role="switch" id="taptopaginate" formControlName="bookReaderTapToPaginate" class="form-check-input" [value]="true" aria-labelledby="taptopaginate-label">
|
||||
<label for="taptopaginate" class="form-check-label">{{t('tap-to-paginate-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="tapToPaginateOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #tapToPaginateOptionTooltip>{{t('tap-to-paginate-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-taptopaginate-option-help">
|
||||
<ng-container [ngTemplateOutlet]="tapToPaginateOptionTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<label id="immersivemode-label" class="form-label"></label>
|
||||
<div class="mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input type="checkbox" role="switch" id="immersivemode" formControlName="bookReaderImmersiveMode" class="form-check-input" [value]="true" aria-labelledby="immersivemode-label">
|
||||
<label for="immersivemode" class="form-check-label">{{t('immersive-mode-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="immersivemodeOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #immersivemodeOptionTooltip>{{t('immersive-mode-label')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-immersivemode-option-help">
|
||||
<ng-container [ngTemplateOutlet]="immersivemodeOptionTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-book-reading-direction" class="form-label">{{t('reading-direction-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookReadingDirectionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookReadingDirectionTooltip>{{t('reading-direction-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-book-reading-direction-book-help">
|
||||
<ng-container [ngTemplateOutlet]="bookReadingDirectionTooltip"></ng-container>
|
||||
</span>
|
||||
<select id="settings-book-reading-direction" class="form-select" aria-describedby="settings-book-reading-direction-help" formControlName="bookReaderReadingDirection">
|
||||
<option *ngFor="let opt of readingDirectionsTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-fontfamily-option" class="form-label">{{t('font-family-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="fontFamilyOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #fontFamilyOptionTooltip>{{t('font-family-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-fontfamily-option-help">
|
||||
<ng-container [ngTemplateOutlet]="fontFamilyOptionTooltip"></ng-container>
|
||||
</span>
|
||||
<select id="settings-fontfamily-option" class="form-select" aria-describedby="settings-fontfamily-option-help" formControlName="bookReaderFontFamily">
|
||||
<option *ngFor="let opt of fontFamilies" [value]="opt">{{opt | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-book-writing-style" class="form-label me-1">{{t('writing-style-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" aria-describedby="settings-book-writing-style-help" placement="right" [ngbTooltip]="bookWritingStyleToolTip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookWritingStyleToolTip>{{t('writing-style-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-book-writing-style-help">
|
||||
<ng-container [ngTemplateOutlet]="bookWritingStyleToolTip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="settings-book-writing-style-help" formControlName="bookReaderWritingStyle" id="settings-book-writing-style" >
|
||||
<option *ngFor="let opt of bookWritingStylesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-book-layout-mode" class="form-label">{{t('layout-mode-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookLayoutModeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookLayoutModeTooltip>{{t('layout-mode-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-book-layout-mode-help">
|
||||
<ng-container [ngTemplateOutlet]="bookLayoutModeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="settings-book-layout-mode-help" formControlName="bookReaderLayoutMode" id="settings-book-layout-mode">
|
||||
<option *ngFor="let opt of bookLayoutModesTranslated" [value]="opt.value">{{opt.text | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
|
||||
<div class="col-md-6 col-sm-12 pe-2 mb-3">
|
||||
<label for="settings-color-theme-option" class="form-label">{{t('color-theme-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookColorThemeTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookColorThemeTooltip>{{t('color-theme-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-color-theme-option-help">
|
||||
<ng-container [ngTemplateOutlet]="bookColorThemeTooltip"></ng-container>
|
||||
</span>
|
||||
<select class="form-select" aria-describedby="settings-color-theme-option-help" formControlName="bookReaderThemeName" id="settings-color-theme-option">
|
||||
<option *ngFor="let opt of bookColorThemesTranslated" [value]="opt.name">{{opt.name | titlecase}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-0">
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<label for="fontsize" class="form-label range-label">{{t('font-size-book-label')}}</label>
|
||||
<input type="range" class="form-range" id="fontsize"
|
||||
min="50" max="300" step="10" formControlName="bookReaderFontSize">
|
||||
<span class="range-text">{{settingsForm.get('bookReaderFontSize')?.value + '%'}}</span>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<div class="range-label">
|
||||
<label class="form-label" for="linespacing">{{t('line-height-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookLineHeightOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookLineHeightOptionTooltip>{{t('line-height-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-booklineheight-option-help">
|
||||
<ng-container [ngTemplateOutlet]="bookLineHeightOptionTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
<input type="range" class="form-range" id="linespacing" min="100" max="200" step="10"
|
||||
formControlName="bookReaderLineSpacing" aria-describedby="settings-booklineheight-option-help">
|
||||
<span class="range-text">{{settingsForm.get('bookReaderLineSpacing')?.value + '%'}}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4 col-sm-12 pe-2 mb-3">
|
||||
<div class="range-label">
|
||||
<label class="form-label">{{t('margin-book-label')}}</label><i class="fa fa-info-circle ms-1" aria-hidden="true" placement="right" [ngbTooltip]="bookReaderMarginOptionTooltip" role="button" tabindex="0"></i>
|
||||
<ng-template #bookReaderMarginOptionTooltip>{{t('margin-book-tooltip')}}</ng-template>
|
||||
<span class="visually-hidden" id="settings-bookmargin-option-help">
|
||||
<ng-container [ngTemplateOutlet]="bookReaderMarginOptionTooltip"></ng-container>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<input type="range" class="form-range" id="margin" min="0" max="30" step="5" formControlName="bookReaderMargin" aria-describedby="bookmargin">
|
||||
<span class="range-text">{{settingsForm.get('bookReaderMargin')?.value + '%'}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-auto d-flex d-md-block justify-content-sm-center text-md-end mb-3">
|
||||
<button type="button" class="flex-fill btn btn-secondary me-2" (click)="resetForm()" aria-describedby="reading-panel">{{t('reset')}}</button>
|
||||
<button type="submit" class="flex-fill btn btn-primary" (click)="save()" aria-describedby="reading-panel" [disabled]="!settingsForm.dirty">{{t('save')}}</button>
|
||||
</div>
|
||||
</ng-template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</ng-container>
|
||||
</form>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Clients">
|
||||
|
||||
@defer (when tab.fragment === FragmentID.Clients; prefetch on idle) {
|
||||
<div class="alert alert-warning" role="alert" *ngIf="!opdsEnabled">{{t('clients-opds-alert')}}</div>
|
||||
<p>{{t('clients-opds-description')}}</p>
|
||||
<app-api-key [tooltipText]="t('clients-api-key-tooltip')" [hideData]="true"></app-api-key>
|
||||
<app-api-key [title]="t('clients-opds-url-tooltip')" [hideData]="true" [showRefresh]="false" [transform]="makeUrl"></app-api-key>
|
||||
</ng-container>
|
||||
<!-- @defer (when tab.fragment === FragmentID.Theme; prefetch on idle) {-->
|
||||
<!-- <app-theme-manager></app-theme-manager>-->
|
||||
<!-- }-->
|
||||
<!-- @placeholder {-->
|
||||
<!-- <app-loading [loading]="true"></app-loading>-->
|
||||
<!-- }-->
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Theme">
|
||||
}
|
||||
@defer (when tab.fragment === FragmentID.Theme; prefetch on idle) {
|
||||
<app-theme-manager></app-theme-manager>
|
||||
</ng-container>
|
||||
}
|
||||
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Devices">
|
||||
@defer (when tab.fragment === FragmentID.Devices; prefetch on idle) {
|
||||
<app-manage-devices></app-manage-devices>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Stats">
|
||||
}
|
||||
|
||||
@defer (when tab.fragment === FragmentID.Stats; prefetch on idle) {
|
||||
<app-user-stats></app-user-stats>
|
||||
</ng-container>
|
||||
<ng-container *ngIf="tab.fragment === FragmentID.Scrobbling">
|
||||
}
|
||||
|
||||
@defer (when tab.fragment === FragmentID.Scrobbling; prefetch on idle) {
|
||||
<app-user-scrobble-history></app-user-scrobble-history>
|
||||
<app-user-holds></app-user-holds>
|
||||
</ng-container>
|
||||
}
|
||||
</ng-template>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -49,11 +49,6 @@ import { SideNavCompanionBarComponent } from '../../sidenav/_components/side-nav
|
||||
import {LocalizationService} from "../../_services/localization.service";
|
||||
import {Language} from "../../_models/metadata/language";
|
||||
import {translate, TranslocoDirective} from "@ngneat/transloco";
|
||||
import {
|
||||
provideTranslocoPersistTranslations,
|
||||
TranslocoPersistTranslations
|
||||
} from "@ngneat/transloco-persist-translations";
|
||||
import {HttpLoader} from "../../../httpLoader";
|
||||
import {LoadingComponent} from "../../shared/loading/loading.component";
|
||||
|
||||
enum AccordionPanelID {
|
||||
@ -333,4 +328,6 @@ export class UserPreferencesComponent implements OnInit, OnDestroy {
|
||||
d.text = translate('preferences.' + o.text);
|
||||
return d;
|
||||
}
|
||||
|
||||
protected readonly undefined = undefined;
|
||||
}
|
||||
|
@ -1707,6 +1707,10 @@
|
||||
"add-to-want-to-read": "{{actionable.add-to-want-to-read}}"
|
||||
},
|
||||
|
||||
"next-expected-card": {
|
||||
"title": "~{{date}}"
|
||||
},
|
||||
|
||||
"server-stats": {
|
||||
"total-series-label": "Total Series",
|
||||
"total-series-tooltip": "Total Series: {{count}}",
|
||||
|
20
openapi.json
20
openapi.json
@ -17860,34 +17860,34 @@
|
||||
"tagsLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"writersLocked": {
|
||||
"writerLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"charactersLocked": {
|
||||
"characterLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"coloristsLocked": {
|
||||
"coloristLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"editorsLocked": {
|
||||
"editorLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"inkersLocked": {
|
||||
"inkerLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"letterersLocked": {
|
||||
"lettererLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"pencillersLocked": {
|
||||
"pencillerLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"publishersLocked": {
|
||||
"publisherLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"translatorsLocked": {
|
||||
"translatorLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"coverArtistsLocked": {
|
||||
"coverArtistLocked": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"releaseYearLocked": {
|
||||
|
Loading…
x
Reference in New Issue
Block a user