Angular 19 + Even more bugfixes (#3675)

This commit is contained in:
Joe Milazzo 2025-03-25 16:43:41 -05:00 committed by GitHub
parent 535165c445
commit cc3ae7f472
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
249 changed files with 4400 additions and 4315 deletions

View File

@ -985,6 +985,59 @@ public class SeriesServiceTests : AbstractDbTest
Assert.False(series.Metadata.People.Any());
}
/// <summary>
/// This emulates the UI operations wrt to locking
/// </summary>
[Fact]
public async Task UpdateSeriesMetadata_ShouldRemoveExistingPerson_AfterAdding()
{
await ResetDb();
var s = new SeriesBuilder("Test")
.WithMetadata(new SeriesMetadataBuilder().Build())
.Build();
s.Library = new LibraryBuilder("Test LIb", LibraryType.Book).Build();
var g = new PersonBuilder("Existing Person").Build();
_context.Series.Add(s);
_context.Person.Add(g);
await _context.SaveChangesAsync();
var success = await _seriesService.UpdateSeriesMetadata(new UpdateSeriesMetadataDto
{
SeriesMetadata = new SeriesMetadataDto
{
SeriesId = 1,
Publishers = new List<PersonDto>() {new PersonDto() {Name = "Test"}},
PublisherLocked = true
},
});
Assert.True(success);
var series = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1);
Assert.NotNull(series);
Assert.NotNull(series.Metadata);
Assert.True(series.Metadata.People.Count != 0);
Assert.True(series.Metadata.PublisherLocked);
success = await _seriesService.UpdateSeriesMetadata(new UpdateSeriesMetadataDto
{
SeriesMetadata = new SeriesMetadataDto
{
SeriesId = 1,
Publishers = new List<PersonDto>(),
PublisherLocked = false
},
});
Assert.True(success);
Assert.Empty(series.Metadata.People);
Assert.False(series.Metadata.PublisherLocked);
}
[Fact]
public async Task UpdateSeriesMetadata_ShouldLockIfTold()
{

View File

@ -235,131 +235,121 @@ public class ChapterController : BaseApiController
#region Genres
if (dto.Genres is {Count: > 0})
{
chapter.Genres ??= new List<Genre>();
await GenreHelper.UpdateChapterGenres(chapter, dto.Genres.Select(t => t.Title), _unitOfWork);
}
chapter.Genres ??= [];
await GenreHelper.UpdateChapterGenres(chapter, dto.Genres.Select(t => t.Title), _unitOfWork);
#endregion
#region Tags
if (dto.Tags is {Count: > 0})
{
chapter.Tags ??= new List<Tag>();
await TagHelper.UpdateChapterTags(chapter, dto.Tags.Select(t => t.Title), _unitOfWork);
}
chapter.Tags ??= [];
await TagHelper.UpdateChapterTags(chapter, dto.Tags.Select(t => t.Title), _unitOfWork);
#endregion
#region People
if (PersonHelper.HasAnyPeople(dto))
{
chapter.People ??= new List<ChapterPeople>();
chapter.People ??= [];
// Update writers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Writers.Select(p => p.Name).ToList(),
PersonRole.Writer,
_unitOfWork
);
// Update writers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Writers.Select(p => p.Name).ToList(),
PersonRole.Writer,
_unitOfWork
);
// Update characters
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Characters.Select(p => p.Name).ToList(),
PersonRole.Character,
_unitOfWork
);
// Update characters
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Characters.Select(p => p.Name).ToList(),
PersonRole.Character,
_unitOfWork
);
// Update pencillers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Pencillers.Select(p => p.Name).ToList(),
PersonRole.Penciller,
_unitOfWork
);
// Update pencillers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Pencillers.Select(p => p.Name).ToList(),
PersonRole.Penciller,
_unitOfWork
);
// Update inkers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Inkers.Select(p => p.Name).ToList(),
PersonRole.Inker,
_unitOfWork
);
// Update inkers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Inkers.Select(p => p.Name).ToList(),
PersonRole.Inker,
_unitOfWork
);
// Update colorists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Colorists.Select(p => p.Name).ToList(),
PersonRole.Colorist,
_unitOfWork
);
// Update colorists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Colorists.Select(p => p.Name).ToList(),
PersonRole.Colorist,
_unitOfWork
);
// Update letterers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Letterers.Select(p => p.Name).ToList(),
PersonRole.Letterer,
_unitOfWork
);
// Update letterers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Letterers.Select(p => p.Name).ToList(),
PersonRole.Letterer,
_unitOfWork
);
// Update cover artists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.CoverArtists.Select(p => p.Name).ToList(),
PersonRole.CoverArtist,
_unitOfWork
);
// Update cover artists
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.CoverArtists.Select(p => p.Name).ToList(),
PersonRole.CoverArtist,
_unitOfWork
);
// Update editors
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Editors.Select(p => p.Name).ToList(),
PersonRole.Editor,
_unitOfWork
);
// Update editors
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Editors.Select(p => p.Name).ToList(),
PersonRole.Editor,
_unitOfWork
);
// Update publishers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Publishers.Select(p => p.Name).ToList(),
PersonRole.Publisher,
_unitOfWork
);
// Update publishers
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Publishers.Select(p => p.Name).ToList(),
PersonRole.Publisher,
_unitOfWork
);
// Update translators
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Translators.Select(p => p.Name).ToList(),
PersonRole.Translator,
_unitOfWork
);
// Update translators
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Translators.Select(p => p.Name).ToList(),
PersonRole.Translator,
_unitOfWork
);
// Update imprints
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Imprints.Select(p => p.Name).ToList(),
PersonRole.Imprint,
_unitOfWork
);
// Update imprints
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Imprints.Select(p => p.Name).ToList(),
PersonRole.Imprint,
_unitOfWork
);
// Update teams
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Teams.Select(p => p.Name).ToList(),
PersonRole.Team,
_unitOfWork
);
// Update teams
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Teams.Select(p => p.Name).ToList(),
PersonRole.Team,
_unitOfWork
);
// Update locations
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Locations.Select(p => p.Name).ToList(),
PersonRole.Location,
_unitOfWork
);
}
// Update locations
await PersonHelper.UpdateChapterPeopleAsync(
chapter,
dto.Locations.Select(p => p.Name).ToList(),
PersonRole.Location,
_unitOfWork
);
#endregion
#region Locks

View File

@ -34,29 +34,26 @@ public class SettingsController : BaseApiController
{
private readonly ILogger<SettingsController> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly ITaskScheduler _taskScheduler;
private readonly IDirectoryService _directoryService;
private readonly IMapper _mapper;
private readonly IEmailService _emailService;
private readonly ILibraryWatcher _libraryWatcher;
private readonly ILocalizationService _localizationService;
private readonly ISettingsService _settingsService;
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, ITaskScheduler taskScheduler,
IDirectoryService directoryService, IMapper mapper, IEmailService emailService, ILibraryWatcher libraryWatcher,
ILocalizationService localizationService, ISettingsService settingsService)
public SettingsController(ILogger<SettingsController> logger, IUnitOfWork unitOfWork, IMapper mapper,
IEmailService emailService, ILocalizationService localizationService, ISettingsService settingsService)
{
_logger = logger;
_unitOfWork = unitOfWork;
_taskScheduler = taskScheduler;
_directoryService = directoryService;
_mapper = mapper;
_emailService = emailService;
_libraryWatcher = libraryWatcher;
_localizationService = localizationService;
_settingsService = settingsService;
}
/// <summary>
/// Returns the base url for this instance (if set)
/// </summary>
/// <returns></returns>
[HttpGet("base-url")]
public async Task<ActionResult<string>> GetBaseUrl()
{

View File

@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace API.Controllers;
#nullable enable
public class VolumeController : BaseApiController
{
@ -23,13 +24,15 @@ public class VolumeController : BaseApiController
_eventHub = eventHub;
}
/// <summary>
/// Returns the appropriate Volume
/// </summary>
/// <param name="volumeId"></param>
/// <returns></returns>
[HttpGet]
public async Task<ActionResult<VolumeDto>> GetVolume(int volumeId)
public async Task<ActionResult<VolumeDto?>> GetVolume(int volumeId)
{
var volume =
await _unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, User.GetUserId());
return Ok(volume);
return Ok(await _unitOfWork.VolumeRepository.GetVolumeDtoAsync(volumeId, User.GetUserId()));
}
[Authorize(Policy = "RequireAdminRole")]
@ -39,7 +42,7 @@ public class VolumeController : BaseApiController
var volume = await _unitOfWork.VolumeRepository.GetVolumeAsync(volumeId,
VolumeIncludes.Chapters | VolumeIncludes.People | VolumeIncludes.Tags);
if (volume == null)
return BadRequest(_localizationService.Translate(User.GetUserId(), "chapter-doesnt-exist"));
return BadRequest(_localizationService.Translate(User.GetUserId(), "volume-doesnt-exist"));
_unitOfWork.VolumeRepository.Remove(volume);

View File

@ -1,4 +1,5 @@
using System;
using System.Text.Json.Serialization;
using API.Entities.Enums;
using API.Services;
@ -45,6 +46,7 @@ public class ServerSettingDto
/// <summary>
/// Represents a unique Id to this Kavita installation. Only used in Stats to identify unique installs.
/// </summary>
public string InstallId { get; set; } = default!;
/// <summary>
/// The format that should be used when saving media for Kavita

View File

@ -0,0 +1,49 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using API.Entities.History;
using API.Services.Tasks.Scanner.Parser;
using Kavita.Common.EnvironmentInfo;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace API.Data.ManualMigrations;
/// <summary>
/// v0.8.6 - Change to not scrobble specials as they will never process, this migration removes all existing scrobble events
/// </summary>
public static class ManualMigrateScrobbleSpecials
{
public static async Task Migrate(DataContext context, ILogger<Program> logger)
{
if (await context.ManualMigrationHistory.AnyAsync(m => m.Name == "ManualMigrateScrobbleSpecials"))
{
return;
}
logger.LogCritical("Running ManualMigrateScrobbleSpecials migration - Please be patient, this may take some time. This is not an error");
// Get all series in the Blacklist table and set their IsBlacklist = true
var events = await context.ScrobbleEvent
.Where(se => se.VolumeNumber == Parser.SpecialVolumeNumber)
.ToListAsync();
context.ScrobbleEvent.RemoveRange(events);
if (context.ChangeTracker.HasChanges())
{
await context.SaveChangesAsync();
logger.LogInformation("Removed {Count} scrobble events that were specials", events.Count);
}
await context.ManualMigrationHistory.AddAsync(new ManualMigrationHistory()
{
Name = "ManualMigrateScrobbleSpecials",
ProductVersion = BuildInfo.Version.ToString(),
RanAt = DateTime.UtcNow
});
await context.SaveChangesAsync();
logger.LogCritical("Running ManualMigrateScrobbleSpecials migration - Completed. This is not an error");
}
}

View File

@ -193,6 +193,7 @@ public class AppUserProgressRepository : IAppUserProgressRepository
.Where(p => p.chapter.MaxNumber != Parser.SpecialVolumeNumber)
.Select(p => p.chapter.Volume.MaxNumber)
.ToListAsync();
return list.Count == 0 ? 0 : list.DefaultIfEmpty().Max();
}

View File

@ -9,6 +9,8 @@ public static class OrderableHelper
{
public static void ReorderItems(List<AppUserDashboardStream> items, int itemId, int toPosition)
{
if (toPosition < 0) throw new ArgumentException("toPosition cannot be less than 0");
var item = items.Find(r => r.Id == itemId);
if (item != null)
{
@ -24,6 +26,8 @@ public static class OrderableHelper
public static void ReorderItems(List<AppUserSideNavStream> items, int itemId, int toPosition)
{
if (toPosition < 0) throw new ArgumentException("toPosition cannot be less than 0");
var item = items.Find(r => r.Id == itemId);
if (item != null && toPosition < items.Count)
{
@ -48,10 +52,15 @@ public static class OrderableHelper
public static void ReorderItems(List<ReadingListItem> items, int readingListItemId, int toPosition)
{
if (toPosition < 0) throw new ArgumentException("toPosition cannot be less than 0");
var item = items.Find(r => r.Id == readingListItemId);
if (item != null)
{
items.Remove(item);
// Ensure toPosition is within the new list bounds
toPosition = Math.Min(toPosition, items.Count);
items.Insert(toPosition, item);
}

View File

@ -104,7 +104,7 @@ public static class TagHelper
public static void UpdateTagList(ICollection<TagDto>? existingDbTags, Series series, IReadOnlyCollection<Tag> newTags, Action<Tag> handleAdd, Action onModified)
{
UpdateTagList(existingDbTags.Select(t => t.Title).ToList(), series, newTags, handleAdd, onModified);
UpdateTagList((existingDbTags ?? []).Select(t => t.Title).ToList(), series, newTags, handleAdd, onModified);
}
public static void UpdateTagList(ICollection<string>? existingDbTags, Series series, IReadOnlyCollection<Tag> newTags, Action<Tag> handleAdd, Action onModified)
@ -155,5 +155,4 @@ public static class TagHelper
onModified();
}
}
}

View File

@ -407,6 +407,12 @@ public class ScrobblingService : IScrobblingService
Format = series.Library.Type.ConvertToPlusMediaFormat(series.Format),
};
if (evt.VolumeNumber is Parser.SpecialVolumeNumber)
{
// We don't process Specials because they will never match on AniList
return;
}
_unitOfWork.ScrobbleRepository.Attach(evt);
await _unitOfWork.CommitAsync();
_logger.LogDebug("Added Scrobbling Read update on {SeriesName} - Volume: {VolumeNumber} Chapter: {ChapterNumber} for User: {UserId}", series.Name, evt.VolumeNumber, evt.ChapterNumber, userId);

View File

@ -211,90 +211,87 @@ public class SeriesService : ISeriesService
}
}
// Update people and locks
if (updateSeriesMetadataDto.SeriesMetadata != null)
{
if (PersonHelper.HasAnyPeople(updateSeriesMetadataDto.SeriesMetadata))
series.Metadata.People ??= [];
// Writers
if (!series.Metadata.WriterLocked || !updateSeriesMetadataDto.SeriesMetadata.WriterLocked)
{
series.Metadata.People ??= [];
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Writers, PersonRole.Writer, _unitOfWork);
}
// Writers
if (!series.Metadata.WriterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Writers, PersonRole.Writer, _unitOfWork);
}
// Cover Artists
if (!series.Metadata.CoverArtistLocked || !updateSeriesMetadataDto.SeriesMetadata.CoverArtistLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, PersonRole.CoverArtist, _unitOfWork);
}
// Cover Artists
if (!series.Metadata.CoverArtistLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.CoverArtists, PersonRole.CoverArtist, _unitOfWork);
}
// Colorists
if (!series.Metadata.ColoristLocked || !updateSeriesMetadataDto.SeriesMetadata.ColoristLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Colorists, PersonRole.Colorist, _unitOfWork);
}
// Colorists
if (!series.Metadata.ColoristLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Colorists, PersonRole.Colorist, _unitOfWork);
}
// Editors
if (!series.Metadata.EditorLocked || !updateSeriesMetadataDto.SeriesMetadata.EditorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Editors, PersonRole.Editor, _unitOfWork);
}
// Editors
if (!series.Metadata.EditorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Editors, PersonRole.Editor, _unitOfWork);
}
// Inkers
if (!series.Metadata.InkerLocked || !updateSeriesMetadataDto.SeriesMetadata.InkerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Inkers, PersonRole.Inker, _unitOfWork);
}
// Inkers
if (!series.Metadata.InkerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Inkers, PersonRole.Inker, _unitOfWork);
}
// Letterers
if (!series.Metadata.LettererLocked || !updateSeriesMetadataDto.SeriesMetadata.LettererLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Letterers, PersonRole.Letterer, _unitOfWork);
}
// Letterers
if (!series.Metadata.LettererLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Letterers, PersonRole.Letterer, _unitOfWork);
}
// Pencillers
if (!series.Metadata.PencillerLocked || !updateSeriesMetadataDto.SeriesMetadata.PencillerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Pencillers, PersonRole.Penciller, _unitOfWork);
}
// Pencillers
if (!series.Metadata.PencillerLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Pencillers, PersonRole.Penciller, _unitOfWork);
}
// Publishers
if (!series.Metadata.PublisherLocked || !updateSeriesMetadataDto.SeriesMetadata.PublisherLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Publishers, PersonRole.Publisher, _unitOfWork);
}
// Publishers
if (!series.Metadata.PublisherLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Publishers, PersonRole.Publisher, _unitOfWork);
}
// Imprints
if (!series.Metadata.ImprintLocked || !updateSeriesMetadataDto.SeriesMetadata.ImprintLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Imprints, PersonRole.Imprint, _unitOfWork);
}
// Imprints
if (!series.Metadata.ImprintLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Imprints, PersonRole.Imprint, _unitOfWork);
}
// Teams
if (!series.Metadata.TeamLocked || !updateSeriesMetadataDto.SeriesMetadata.TeamLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Teams, PersonRole.Team, _unitOfWork);
}
// Teams
if (!series.Metadata.TeamLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Teams, PersonRole.Team, _unitOfWork);
}
// Locations
if (!series.Metadata.LocationLocked || !updateSeriesMetadataDto.SeriesMetadata.LocationLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Locations, PersonRole.Location, _unitOfWork);
}
// Locations
if (!series.Metadata.LocationLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Locations, PersonRole.Location, _unitOfWork);
}
// Translators
if (!series.Metadata.TranslatorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Translators, PersonRole.Translator, _unitOfWork);
}
// Characters
if (!series.Metadata.CharacterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Characters, PersonRole.Character, _unitOfWork);
}
// Translators
if (!series.Metadata.TranslatorLocked || !updateSeriesMetadataDto.SeriesMetadata.TranslatorLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Translators, PersonRole.Translator, _unitOfWork);
}
// Characters
if (!series.Metadata.CharacterLocked || !updateSeriesMetadataDto.SeriesMetadata.CharacterLocked)
{
await HandlePeopleUpdateAsync(series.Metadata, updateSeriesMetadataDto.SeriesMetadata.Characters, PersonRole.Character, _unitOfWork);
}
series.Metadata.AgeRatingLocked = updateSeriesMetadataDto.SeriesMetadata.AgeRatingLocked;

View File

@ -123,7 +123,10 @@ public class StreamService : IStreamService
AppUserIncludes.DashboardStreams);
var stream = user?.DashboardStreams.FirstOrDefault(d => d.Id == dto.Id);
if (stream == null)
{
throw new KavitaException(await _localizationService.Translate(userId, "dashboard-stream-doesnt-exist"));
}
if (stream.Order == dto.ToPosition) return;
var list = user!.DashboardStreams.OrderBy(s => s.Order).ToList();

View File

@ -547,6 +547,12 @@ public partial class VersionUpdaterService : IVersionUpdaterService
// Remove "Fixed:", "Added:" etc. if present
var cleanedItem = CleanSectionItem(trimmedLine);
// Some sections like API/Developer/Removed don't have the title repeated, so we need to check for an additional cleaning
if (cleanedItem.StartsWith("- "))
{
cleanedItem = trimmedLine.Substring(2);
}
// Only add non-empty items
if (!string.IsNullOrWhiteSpace(cleanedItem))
{

View File

@ -285,6 +285,9 @@ public class Startup
await ManualMigrateNeedsManualMatch.Migrate(dataContext, logger);
await MigrateProgressExportForV085.Migrate(dataContext, directoryService, logger);
// v0.8.6
await ManualMigrateScrobbleSpecials.Migrate(dataContext, logger);
// Update the version in the DB after all migrations are run
var installVersion = await unitOfWork.SettingsRepository.GetSettingAsync(ServerSettingKey.InstallVersion);
installVersion.Value = BuildInfo.Version.ToString();

View File

@ -8,6 +8,9 @@ indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
[*.json]
indent_size = 2
[*.html]
indent_size = 2

View File

@ -56,7 +56,12 @@
},
"extractLicenses": false,
"optimization": false,
"namedChunks": true
"namedChunks": true,
"stylePreprocessorOptions": {
"sass": {
"silenceDeprecations": ["mixed-decls", "color-functions", "global-builtin", "import"]
}
}
},
"configurations": {
"production": {

4782
UI/Web/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -16,66 +16,66 @@
},
"private": true,
"dependencies": {
"@angular-slider/ngx-slider": "^18.0.0",
"@angular/animations": "^18.2.9",
"@angular/cdk": "^18.2.10",
"@angular/common": "^18.2.9",
"@angular/compiler": "^18.2.9",
"@angular/core": "^18.2.9",
"@angular/forms": "^18.2.9",
"@angular/localize": "^18.2.9",
"@angular/platform-browser": "^18.2.9",
"@angular/platform-browser-dynamic": "^18.2.9",
"@angular/router": "^18.2.9",
"@fortawesome/fontawesome-free": "^6.6.0",
"@iharbeck/ngx-virtual-scroller": "^17.0.2",
"@iplab/ngx-file-upload": "^18.0.0",
"@jsverse/transloco": "^7.5.0",
"@angular-slider/ngx-slider": "^19.0.0",
"@angular/animations": "^19.2.3",
"@angular/cdk": "^19.2.6",
"@angular/common": "^19.2.3",
"@angular/compiler": "^19.2.3",
"@angular/core": "^19.2.3",
"@angular/forms": "^19.2.3",
"@angular/localize": "^19.2.3",
"@angular/platform-browser": "^19.2.3",
"@angular/platform-browser-dynamic": "^19.2.3",
"@angular/router": "^19.2.3",
"@fortawesome/fontawesome-free": "^6.7.2",
"@iharbeck/ngx-virtual-scroller": "^19.0.1",
"@iplab/ngx-file-upload": "^19.0.3",
"@jsverse/transloco": "^7.6.1",
"@jsverse/transloco-locale": "^7.0.1",
"@jsverse/transloco-persist-lang": "^7.0.2",
"@jsverse/transloco-persist-translations": "^7.0.1",
"@jsverse/transloco-preload-langs": "^7.0.1",
"@microsoft/signalr": "^8.0.7",
"@ng-bootstrap/ng-bootstrap": "^17.0.1",
"@ng-bootstrap/ng-bootstrap": "^18.0.0",
"@popperjs/core": "^2.11.7",
"@siemens/ngx-datatable": "^22.4.1",
"@swimlane/ngx-charts": "^20.5.0",
"@tweenjs/tween.js": "^23.1.3",
"@swimlane/ngx-charts": "^22.0.0-alpha.0",
"@tweenjs/tween.js": "^25.0.0",
"bootstrap": "^5.3.2",
"charts.css": "^1.1.0",
"file-saver": "^2.0.5",
"luxon": "^3.5.0",
"ng-circle-progress": "^1.7.1",
"ng-lazyload-image": "^9.1.3",
"ng-select2-component": "^14.0.1",
"ngx-color-picker": "^17.0.0",
"ngx-extended-pdf-viewer": "^21.4.6",
"ng-select2-component": "^17.2.1",
"ngx-color-picker": "^19.0.0",
"ngx-extended-pdf-viewer": "^22.3.9",
"ngx-file-drop": "^16.0.0",
"ngx-stars": "^1.6.5",
"ngx-toastr": "^19.0.0",
"nosleep.js": "^0.12.0",
"rxjs": "^7.8.0",
"rxjs": "^7.8.2",
"screenfull": "^6.0.2",
"swiper": "^8.4.6",
"tslib": "^2.8.0",
"zone.js": "^0.14.10"
"tslib": "^2.8.1",
"zone.js": "^0.15.0"
},
"devDependencies": {
"@angular-eslint/builder": "^18.4.0",
"@angular-eslint/eslint-plugin": "^18.4.0",
"@angular-eslint/eslint-plugin-template": "^18.4.0",
"@angular-eslint/schematics": "^18.4.0",
"@angular-eslint/template-parser": "^18.4.0",
"@angular/build": "^18.2.10",
"@angular/cli": "^18.2.10",
"@angular/compiler-cli": "^18.2.9",
"@angular-eslint/builder": "^19.3.0",
"@angular-eslint/eslint-plugin": "^19.3.0",
"@angular-eslint/eslint-plugin-template": "^19.3.0",
"@angular-eslint/schematics": "^19.3.0",
"@angular-eslint/template-parser": "^19.3.0",
"@angular/build": "^19.2.4",
"@angular/cli": "^19.2.4",
"@angular/compiler-cli": "^19.2.3",
"@types/d3": "^7.4.3",
"@types/file-saver": "^2.0.7",
"@types/luxon": "^3.4.0",
"@types/node": "^22.8.0",
"@typescript-eslint/eslint-plugin": "^8.11.0",
"@typescript-eslint/parser": "^8.11.0",
"eslint": "^8.57.0",
"@types/node": "^22.13.13",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"eslint": "^9.23.0",
"jsonminify": "^0.4.2",
"karma-coverage": "~2.2.0",
"ts-node": "~10.9.1",

View File

@ -1,4 +1,4 @@
@import './theme/variables';
@use './theme/variables' as theme;
.title {
color: white;
@ -149,7 +149,7 @@
object-fit: contain;
}
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
.carousel-tabs-container {
mask-image: linear-gradient(transparent, black 0%, black 90%, transparent 100%);
-webkit-mask-image: linear-gradient(to right, transparent, black 0%, black 90%, transparent 100%);
@ -162,7 +162,7 @@
}
/* col-lg */
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
.image-container.mobile-bg{
width: 100vw;
top: calc(var(--nav-offset) - 20px);
@ -201,7 +201,7 @@
font-size: 0.9rem;
}
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
.carousel-tabs-container {
mask-image: linear-gradient(to right, transparent, black 0%, black 90%, transparent 100%);
-webkit-mask-image: linear-gradient(to right, transparent, black 0%, black 90%, transparent 100%);

View File

@ -1,4 +1,4 @@
import { Pipe, PipeTransform } from '@angular/core';
import {Pipe, PipeTransform} from '@angular/core';
import {EncodeFormat} from "../admin/_models/encode-format";
@Pipe({

View File

@ -19,14 +19,13 @@ import {User} from "../../_models/user";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
@Component({
selector: 'app-actionable-modal',
standalone: true,
imports: [
TranslocoDirective
],
templateUrl: './actionable-modal.component.html',
styleUrl: './actionable-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-actionable-modal',
imports: [
TranslocoDirective
],
templateUrl: './actionable-modal.component.html',
styleUrl: './actionable-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ActionableModalComponent implements OnInit {

View File

@ -20,17 +20,15 @@ import {FilterField} from "../../_models/metadata/v2/filter-field";
const basePath = './assets/images/ratings/';
@Component({
selector: 'app-age-rating-image',
standalone: true,
imports: [
ImageComponent,
NgbTooltip,
AgeRatingPipe,
AsyncPipe
],
templateUrl: './age-rating-image.component.html',
styleUrl: './age-rating-image.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-age-rating-image',
imports: [
ImageComponent,
NgbTooltip,
AgeRatingPipe,
],
templateUrl: './age-rating-image.component.html',
styleUrl: './age-rating-image.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AgeRatingImageComponent implements OnInit, OnChanges {
private readonly cdRef = inject(ChangeDetectorRef);

View File

@ -19,12 +19,11 @@ import {Breakpoint, UtilityService} from "../../shared/_services/utility.service
import {ActionableModalComponent} from "../actionable-modal/actionable-modal.component";
@Component({
selector: 'app-card-actionables',
standalone: true,
imports: [NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, DynamicListPipe, TranslocoDirective, AsyncPipe, NgTemplateOutlet],
templateUrl: './card-actionables.component.html',
styleUrls: ['./card-actionables.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-card-actionables',
imports: [NgbDropdown, NgbDropdownToggle, NgbDropdownMenu, NgbDropdownItem, DynamicListPipe, TranslocoDirective, AsyncPipe, NgTemplateOutlet],
templateUrl: './card-actionables.component.html',
styleUrls: ['./card-actionables.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CardActionablesComponent implements OnInit {

View File

@ -9,19 +9,17 @@ import {IHasProgress} from "../../_models/common/i-has-progress";
* Used for the Series/Volume/Chapter Detail pages
*/
@Component({
selector: 'app-cover-image',
standalone: true,
imports: [
NgClass,
TranslocoDirective,
ImageComponent,
NgbProgressbar,
DecimalPipe,
NgbTooltip
],
templateUrl: './cover-image.component.html',
styleUrl: './cover-image.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-cover-image',
imports: [
TranslocoDirective,
ImageComponent,
NgbProgressbar,
DecimalPipe,
NgbTooltip
],
templateUrl: './cover-image.component.html',
styleUrl: './cover-image.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CoverImageComponent {

View File

@ -9,14 +9,11 @@ import {FilterComparison} from "../../_models/metadata/v2/filter-comparison";
import {FilterUtilitiesService} from "../../shared/_services/filter-utilities.service";
import {Genre} from "../../_models/metadata/genre";
import {Tag} from "../../_models/tag";
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
import {ImageComponent} from "../../shared/image/image.component";
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
import {ImageService} from "../../_services/image.service";
import {BadgeExpanderComponent} from "../../shared/badge-expander/badge-expander.component";
import {IHasReadingTime} from "../../_models/common/i-has-reading-time";
import {ReadTimePipe} from "../../_pipes/read-time.pipe";
import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe";
import {MangaFormat} from "../../_models/manga-format";
import {SeriesFormatComponent} from "../../shared/series-format/series-format.component";
import {MangaFormatPipe} from "../../_pipes/manga-format.pipe";
@ -26,17 +23,13 @@ import {SafeUrlPipe} from "../../_pipes/safe-url.pipe";
@Component({
selector: 'app-details-tab',
standalone: true,
imports: [
CarouselReelComponent,
PersonBadgeComponent,
TranslocoDirective,
TagBadgeComponent,
ImageComponent,
SafeHtmlPipe,
BadgeExpanderComponent,
ReadTimePipe,
SentenceCasePipe,
SeriesFormatComponent,
MangaFormatPipe,
LanguageNamePipe,

View File

@ -3,15 +3,12 @@ import {Breakpoint, UtilityService} from "../../shared/_services/utility.service
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule, Validators} from "@angular/forms";
import {
AsyncPipe,
DatePipe,
DecimalPipe,
NgClass,
NgTemplateOutlet,
TitleCasePipe
} from "@angular/common";
import {
NgbActiveModal,
NgbInputDatepicker,
NgbNav,
NgbNavContent,
NgbNavItem,
@ -79,44 +76,37 @@ export interface EditChapterModalCloseResult {
const blackList = [Action.Edit, Action.IncognitoRead, Action.AddToReadingList];
@Component({
selector: 'app-edit-chapter-modal',
standalone: true,
imports: [
FormsModule,
NgbNav,
NgbNavContent,
NgbNavLink,
TranslocoDirective,
AsyncPipe,
NgbNavOutlet,
ReactiveFormsModule,
NgbNavItem,
SettingItemComponent,
NgTemplateOutlet,
NgClass,
TypeaheadComponent,
EntityTitleComponent,
TitleCasePipe,
SettingButtonComponent,
CoverImageChooserComponent,
EditChapterProgressComponent,
NgbInputDatepicker,
CompactNumberPipe,
IconAndTitleComponent,
DefaultDatePipe,
TranslocoDatePipe,
UtcToLocalTimePipe,
BytesPipe,
ImageComponent,
SafeHtmlPipe,
DecimalPipe,
DatePipe,
ReadTimePipe,
SettingTitleComponent
],
templateUrl: './edit-chapter-modal.component.html',
styleUrl: './edit-chapter-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-edit-chapter-modal',
imports: [
FormsModule,
NgbNav,
NgbNavContent,
NgbNavLink,
TranslocoDirective,
AsyncPipe,
NgbNavOutlet,
ReactiveFormsModule,
NgbNavItem,
SettingItemComponent,
NgTemplateOutlet,
NgClass,
TypeaheadComponent,
EntityTitleComponent,
TitleCasePipe,
SettingButtonComponent,
CoverImageChooserComponent,
EditChapterProgressComponent,
CompactNumberPipe,
DefaultDatePipe,
UtcToLocalTimePipe,
BytesPipe,
ImageComponent,
SafeHtmlPipe,
ReadTimePipe,
],
templateUrl: './edit-chapter-modal.component.html',
styleUrl: './edit-chapter-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditChapterModalComponent implements OnInit {

View File

@ -1,30 +1,17 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular/forms";
import {
NgbActiveModal,
NgbInputDatepicker,
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavLink,
NgbNavOutlet
} from "@ng-bootstrap/ng-bootstrap";
import {NgbActiveModal, NgbNav, NgbNavContent, NgbNavItem, NgbNavLink, NgbNavOutlet} from "@ng-bootstrap/ng-bootstrap";
import {TranslocoDirective} from "@jsverse/transloco";
import {AsyncPipe, DatePipe, DecimalPipe, NgClass, NgTemplateOutlet, TitleCasePipe} from "@angular/common";
import {NgClass} from "@angular/common";
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
import {TypeaheadComponent} from "../../typeahead/_components/typeahead.component";
import {EntityTitleComponent} from "../../cards/entity-title/entity-title.component";
import {SettingButtonComponent} from "../../settings/_components/setting-button/setting-button.component";
import {CoverImageChooserComponent} from "../../cards/cover-image-chooser/cover-image-chooser.component";
import {EditChapterProgressComponent} from "../../cards/edit-chapter-progress/edit-chapter-progress.component";
import {CompactNumberPipe} from "../../_pipes/compact-number.pipe";
import {IconAndTitleComponent} from "../../shared/icon-and-title/icon-and-title.component";
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
import {TranslocoDatePipe} from "@jsverse/transloco-locale";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {BytesPipe} from "../../_pipes/bytes.pipe";
import {ImageComponent} from "../../shared/image/image.component";
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
import {ReadTimePipe} from "../../_pipes/read-time.pipe";
import {Action, ActionFactoryService, ActionItem} from "../../_services/action-factory.service";
import {Volume} from "../../_models/volume";
@ -37,11 +24,10 @@ import {DownloadService} from "../../shared/_services/download.service";
import {LibraryType} from "../../_models/library/library";
import {PersonRole} from "../../_models/metadata/person";
import {forkJoin} from "rxjs";
import { MangaFormat } from 'src/app/_models/manga-format';
import {MangaFormat} from 'src/app/_models/manga-format';
import {MangaFile} from "../../_models/manga-file";
import {VolumeService} from "../../_services/volume.service";
import {User} from "../../_models/user";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
enum TabID {
General = 'general-tab',
@ -63,37 +49,25 @@ const blackList = [Action.Edit, Action.IncognitoRead, Action.AddToReadingList];
@Component({
selector: 'app-edit-volume-modal',
standalone: true,
imports: [
FormsModule,
NgbNav,
NgbNavContent,
NgbNavLink,
TranslocoDirective,
AsyncPipe,
NgbNavOutlet,
ReactiveFormsModule,
NgbNavItem,
SettingItemComponent,
NgTemplateOutlet,
NgClass,
TypeaheadComponent,
EntityTitleComponent,
TitleCasePipe,
SettingButtonComponent,
CoverImageChooserComponent,
EditChapterProgressComponent,
NgbInputDatepicker,
CompactNumberPipe,
IconAndTitleComponent,
DefaultDatePipe,
TranslocoDatePipe,
UtcToLocalTimePipe,
BytesPipe,
ImageComponent,
SafeHtmlPipe,
DecimalPipe,
DatePipe,
ReadTimePipe
],
templateUrl: './edit-volume-modal.component.html',

View File

@ -14,20 +14,19 @@ import { ThemeService } from 'src/app/_services/theme.service';
import { AsyncPipe } from '@angular/common';
@Component({
selector: 'app-match-series-modal',
standalone: true,
imports: [
AsyncPipe,
TranslocoDirective,
MatchSeriesResultItemComponent,
LoadingComponent,
ReactiveFormsModule,
SettingItemComponent,
SettingSwitchComponent
],
templateUrl: './match-series-modal.component.html',
styleUrl: './match-series-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-match-series-modal',
imports: [
AsyncPipe,
TranslocoDirective,
MatchSeriesResultItemComponent,
LoadingComponent,
ReactiveFormsModule,
SettingItemComponent,
SettingSwitchComponent
],
templateUrl: './match-series-modal.component.html',
styleUrl: './match-series-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MatchSeriesModalComponent implements OnInit {
private readonly cdRef = inject(ChangeDetectorRef);

View File

@ -8,7 +8,7 @@
</div>
<div class="ms-1">
<div><span class="title">{{item.series.name}}</span> <span class="me-1 float-end">({{item.matchRating | translocoPercent}})</span></div>
<div class="text-muted">
<div class="text-body-secondary">
@for(synm of item.series.synonyms; track synm; let last = $last) {
{{synm}}
@if (!last) {
@ -42,6 +42,6 @@
<span class="me-1">{{item.series.plusMediaFormat | plusMediaFormat}}</span>
</div>
}
</div>
</ng-container>

View File

@ -18,19 +18,18 @@ import {PlusMediaFormatPipe} from "../../_pipes/plus-media-format.pipe";
import {LoadingComponent} from "../../shared/loading/loading.component";
@Component({
selector: 'app-match-series-result-item',
standalone: true,
imports: [
ImageComponent,
TranslocoPercentPipe,
ReadMoreComponent,
TranslocoDirective,
PlusMediaFormatPipe,
LoadingComponent
],
templateUrl: './match-series-result-item.component.html',
styleUrl: './match-series-result-item.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-match-series-result-item',
imports: [
ImageComponent,
TranslocoPercentPipe,
ReadMoreComponent,
TranslocoDirective,
PlusMediaFormatPipe,
LoadingComponent
],
templateUrl: './match-series-result-item.component.html',
styleUrl: './match-series-result-item.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class MatchSeriesResultItemComponent {

View File

@ -20,14 +20,13 @@ import {Router} from "@angular/router";
const ANIMATION_TIME = 3000;
@Component({
selector: 'app-publisher-flipper',
standalone: true,
imports: [
ImageComponent
],
templateUrl: './publisher-flipper.component.html',
styleUrl: './publisher-flipper.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-publisher-flipper',
imports: [
ImageComponent
],
templateUrl: './publisher-flipper.component.html',
styleUrl: './publisher-flipper.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PublisherFlipperComponent implements OnInit, OnDestroy, AfterViewInit, AfterViewChecked {

View File

@ -17,17 +17,16 @@ export interface RelatedSeriesPair {
}
@Component({
selector: 'app-related-tab',
standalone: true,
imports: [
CardItemComponent,
CarouselReelComponent,
TranslocoDirective,
SeriesCardComponent
],
templateUrl: './related-tab.component.html',
styleUrl: './related-tab.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-related-tab',
imports: [
CardItemComponent,
CarouselReelComponent,
TranslocoDirective,
SeriesCardComponent
],
templateUrl: './related-tab.component.html',
styleUrl: './related-tab.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class RelatedTabComponent {

View File

@ -1,9 +1,11 @@
import {
AfterViewInit,
ChangeDetectionStrategy,
Component, inject,
Component,
inject,
Inject,
Input, ViewChild,
Input,
ViewChild,
ViewContainerRef,
ViewEncapsulation
} from '@angular/core';
@ -14,17 +16,15 @@ import {UserReview} from "../review-card/user-review";
import {SpoilerComponent} from "../spoiler/spoiler.component";
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
import {TranslocoDirective} from "@jsverse/transloco";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
@Component({
selector: 'app-review-card-modal',
standalone: true,
imports: [ReactiveFormsModule, SpoilerComponent, SafeHtmlPipe, TranslocoDirective, DefaultValuePipe, NgOptimizedImage, ProviderImagePipe],
imports: [ReactiveFormsModule, SafeHtmlPipe, TranslocoDirective, NgOptimizedImage, ProviderImagePipe],
templateUrl: './review-card-modal.component.html',
styleUrls: ['./review-card-modal.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
encapsulation: ViewEncapsulation.None
})
export class ReviewCardModalComponent implements AfterViewInit {
@ -45,7 +45,8 @@ export class ReviewCardModalComponent implements AfterViewInit {
for (let i = 0; i < spoilers.length; i++) {
const spoiler = spoilers[i];
const componentRef = this.container.createComponent<SpoilerComponent>(SpoilerComponent);
const componentRef = this.container.createComponent<SpoilerComponent>(SpoilerComponent,
{projectableNodes: [[document.createTextNode('')]]});
componentRef.instance.html = spoiler.innerHTML;
if (spoiler.parentNode != null) {
spoiler.parentNode.replaceChild(componentRef.location.nativeElement, spoiler);

View File

@ -19,15 +19,13 @@ import {
} from "../review-series-modal/review-series-modal.component";
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {ImageComponent} from "../../shared/image/image.component";
import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
import {TranslocoDirective} from "@jsverse/transloco";
import {ScrobbleProvider} from "../../_services/scrobbling.service";
@Component({
selector: 'app-review-card',
standalone: true,
imports: [ReadMoreComponent, DefaultValuePipe, ImageComponent, NgOptimizedImage, ProviderImagePipe, TranslocoDirective],
imports: [ReadMoreComponent, DefaultValuePipe, NgOptimizedImage, ProviderImagePipe, TranslocoDirective],
templateUrl: './review-card.component.html',
styleUrls: ['./review-card.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -1,14 +1,7 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
inject,
Input,
OnInit
} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
import {FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {NgbActiveModal, NgbRating} from '@ng-bootstrap/ng-bootstrap';
import { SeriesService } from 'src/app/_services/series.service';
import {NgbActiveModal} from '@ng-bootstrap/ng-bootstrap';
import {SeriesService} from 'src/app/_services/series.service';
import {UserReview} from "../review-card/user-review";
import {translate, TranslocoDirective} from "@jsverse/transloco";
import {ConfirmService} from "../../shared/confirm.service";
@ -28,8 +21,7 @@ export interface ReviewSeriesModalCloseEvent {
@Component({
selector: 'app-review-series-modal',
standalone: true,
imports: [NgbRating, ReactiveFormsModule, TranslocoDirective],
imports: [ReactiveFormsModule, TranslocoDirective],
templateUrl: './review-series-modal.component.html',
styleUrls: ['./review-series-modal.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -6,11 +6,7 @@ import {ExternalSeriesDetail, SeriesStaff} from "../../_models/series-detail/ext
import {SeriesService} from "../../_services/series.service";
import {ImageComponent} from "../../shared/image/image.component";
import {LoadingComponent} from "../../shared/loading/loading.component";
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
import {A11yClickDirective} from "../../shared/a11y-click.directive";
import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component";
import {PersonBadgeComponent} from "../../shared/person-badge/person-badge.component";
import {TagBadgeComponent} from "../../shared/tag-badge/tag-badge.component";
import {ImageService} from "../../_services/image.service";
import {PublicationStatusPipe} from "../../_pipes/publication-status.pipe";
import {SeriesMetadata} from "../../_models/metadata/series-metadata";
@ -20,12 +16,12 @@ import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
import {FilterField} from "../../_models/metadata/v2/filter-field";
@Component({
selector: 'app-series-preview-drawer',
standalone: true,
imports: [TranslocoDirective, ImageComponent, LoadingComponent, SafeHtmlPipe, A11yClickDirective, MetadataDetailComponent, PersonBadgeComponent, TagBadgeComponent, PublicationStatusPipe, ReadMoreComponent, NgbTooltip, NgOptimizedImage, ProviderImagePipe],
templateUrl: './series-preview-drawer.component.html',
styleUrls: ['./series-preview-drawer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-series-preview-drawer',
imports: [TranslocoDirective, ImageComponent, LoadingComponent, MetadataDetailComponent,
PublicationStatusPipe, ReadMoreComponent, NgbTooltip, NgOptimizedImage, ProviderImagePipe],
templateUrl: './series-preview-drawer.component.html',
styleUrls: ['./series-preview-drawer.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SeriesPreviewDrawerComponent implements OnInit {

View File

@ -1,13 +1,7 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit} from '@angular/core';
import {NgbActiveOffcanvas, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
import {NgbActiveOffcanvas} from "@ng-bootstrap/ng-bootstrap";
import {UserCollection} from "../../_models/collection-tag";
import {ImageComponent} from "../../shared/image/image.component";
import {LoadingComponent} from "../../shared/loading/loading.component";
import {MetadataDetailComponent} from "../../series-detail/_components/metadata-detail/metadata-detail.component";
import {DatePipe, DecimalPipe, NgOptimizedImage} from "@angular/common";
import {ProviderImagePipe} from "../../_pipes/provider-image.pipe";
import {PublicationStatusPipe} from "../../_pipes/publication-status.pipe";
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
import {DecimalPipe} from "@angular/common";
import {TranslocoDirective} from "@jsverse/transloco";
import {Series} from "../../_models/series";
import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
@ -17,29 +11,19 @@ import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
@Component({
selector: 'app-smart-collection-drawer',
standalone: true,
imports: [
ImageComponent,
LoadingComponent,
MetadataDetailComponent,
NgOptimizedImage,
NgbTooltip,
ProviderImagePipe,
PublicationStatusPipe,
ReadMoreComponent,
TranslocoDirective,
SafeHtmlPipe,
RouterLink,
DatePipe,
DefaultDatePipe,
UtcToLocalTimePipe,
SettingItemComponent,
DecimalPipe
],
templateUrl: './smart-collection-drawer.component.html',
styleUrl: './smart-collection-drawer.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-smart-collection-drawer',
imports: [
TranslocoDirective,
SafeHtmlPipe,
RouterLink,
DefaultDatePipe,
UtcToLocalTimePipe,
SettingItemComponent,
DecimalPipe
],
templateUrl: './smart-collection-drawer.component.html',
styleUrl: './smart-collection-drawer.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SmartCollectionDrawerComponent implements OnInit {
private readonly activeOffcanvas = inject(NgbActiveOffcanvas);

View File

@ -11,13 +11,12 @@ import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
import {TranslocoDirective} from "@jsverse/transloco";
@Component({
selector: 'app-spoiler',
standalone: true,
selector: 'app-spoiler',
imports: [SafeHtmlPipe, TranslocoDirective],
templateUrl: './spoiler.component.html',
styleUrls: ['./spoiler.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
templateUrl: './spoiler.component.html',
styleUrls: ['./spoiler.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None
})
export class SpoilerComponent implements OnInit{

View File

@ -1,7 +1,7 @@
<ng-container *transloco="let t; read:'user-scrobble-history'">
<div class="position-relative">
<button class="btn btn-primary-outline position-absolute custom-position" [disabled]="events.length > 0" (click)="generateScrobbleEvents()" [title]="t('generate-scrobble-events')">
<button class="btn btn-outline-primary position-absolute custom-position" [disabled]="events.length > 0" (click)="generateScrobbleEvents()" [title]="t('generate-scrobble-events')">
<i class="fa fa-plus" aria-hidden="true"></i><span class="phone-hidden ms-1">{{t('generate-scrobble-events')}}</span>
</button>
</div>

View File

@ -27,13 +27,12 @@ export interface DataTablePage {
}
@Component({
selector: 'app-user-scrobble-history',
standalone: true,
imports: [ScrobbleEventTypePipe, ReactiveFormsModule, TranslocoModule,
DefaultValuePipe, TranslocoLocaleModule, UtcToLocalTimePipe, NgbTooltip, NgxDatatableModule, AsyncPipe],
templateUrl: './user-scrobble-history.component.html',
styleUrls: ['./user-scrobble-history.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-user-scrobble-history',
imports: [ScrobbleEventTypePipe, ReactiveFormsModule, TranslocoModule,
DefaultValuePipe, TranslocoLocaleModule, UtcToLocalTimePipe, NgbTooltip, NgxDatatableModule, AsyncPipe],
templateUrl: './user-scrobble-history.component.html',
styleUrls: ['./user-scrobble-history.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserScrobbleHistoryComponent implements OnInit {

View File

@ -5,15 +5,14 @@ import {TranslocoDirective} from "@jsverse/transloco";
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
@Component({
selector: 'app-copy-settings-from-library-modal',
standalone: true,
imports: [
TranslocoDirective,
ReactiveFormsModule,
],
templateUrl: './copy-settings-from-library-modal.component.html',
styleUrl: './copy-settings-from-library-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-copy-settings-from-library-modal',
imports: [
TranslocoDirective,
ReactiveFormsModule,
],
templateUrl: './copy-settings-from-library-modal.component.html',
styleUrl: './copy-settings-from-library-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CopySettingsFromLibraryModalComponent {
protected readonly modal = inject(NgbActiveModal);

View File

@ -16,11 +16,10 @@ export interface DirectoryPickerResult {
}
@Component({
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]
selector: 'app-directory-picker',
templateUrl: './directory-picker.component.html',
styleUrls: ['./directory-picker.component.scss'],
imports: [ReactiveFormsModule, NgbTypeahead, FormsModule, NgbHighlight, NgIf, NgFor, NgClass, TranslocoDirective]
})
export class DirectoryPickerComponent implements OnInit {

View File

@ -12,7 +12,6 @@ import {ToastrService} from "ngx-toastr";
selector: 'app-reset-password-modal',
templateUrl: './reset-password-modal.component.html',
styleUrls: ['./reset-password-modal.component.scss'],
standalone: true,
imports: [ReactiveFormsModule, NgIf, SentenceCasePipe, TranslocoDirective]
})
export class ResetPasswordModalComponent {

View File

@ -1,4 +1,4 @@
import { EncodeFormat } from "./encode-format";
import {EncodeFormat} from "./encode-format";
import {CoverImageSize} from "./cover-image-size";
import {SmtpConfig} from "./smtp-config";
@ -25,4 +25,6 @@ export interface ServerSettings {
onDeckUpdateDays: number;
coverImageSize: CoverImageSize;
smtpConfig: SmtpConfig;
installId: string;
installVersion: string;
}

View File

@ -22,9 +22,8 @@ const EmailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
selector: 'app-edit-user',
templateUrl: './edit-user.component.html',
styleUrls: ['./edit-user.component.scss'],
standalone: true,
imports: [ReactiveFormsModule, RoleSelectorComponent, LibrarySelectorComponent, RestrictionSelectorComponent, SentenceCasePipe, TranslocoDirective, AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush
imports: [ReactiveFormsModule, RoleSelectorComponent, LibrarySelectorComponent, RestrictionSelectorComponent, SentenceCasePipe, TranslocoDirective, AsyncPipe],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class EditUserComponent implements OnInit {

View File

@ -2,21 +2,16 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} f
import {TranslocoDirective} from "@jsverse/transloco";
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {EmailHistory} from "../../_models/email-history";
import {EmailService} from "../../_services/email.service";
import {LoadingComponent} from "../../shared/loading/loading.component";
import {ColumnMode, NgxDatatableModule} from "@siemens/ngx-datatable";
@Component({
selector: 'app-email-history',
standalone: true,
imports: [
TranslocoDirective,
VirtualScrollerModule,
UtcToLocalTimePipe,
LoadingComponent,
DefaultValuePipe,
NgxDatatableModule
],
templateUrl: './email-history.component.html',

View File

@ -19,7 +19,6 @@ import {SafeHtmlPipe} from "../../_pipes/safe-html.pipe";
selector: 'app-invite-user',
templateUrl: './invite-user.component.html',
styleUrls: ['./invite-user.component.scss'],
standalone: true,
imports: [NgIf, ReactiveFormsModule, RoleSelectorComponent, LibrarySelectorComponent, RestrictionSelectorComponent, ApiKeyComponent, TranslocoDirective, SafeHtmlPipe]
})
export class InviteUserComponent implements OnInit {

View File

@ -20,9 +20,8 @@ import {SelectionModel} from "../../typeahead/_models/selection-model";
selector: 'app-library-selector',
templateUrl: './library-selector.component.html',
styleUrls: ['./library-selector.component.scss'],
standalone: true,
imports: [ReactiveFormsModule, FormsModule, TranslocoDirective, LoadingComponent],
changeDetection: ChangeDetectionStrategy.OnPush
imports: [ReactiveFormsModule, FormsModule, TranslocoDirective, LoadingComponent],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class LibrarySelectorComponent implements OnInit {

View File

@ -1,7 +1,7 @@
<ng-container *transloco="let t; read: 'license'">
<div class="position-relative">
<a class="position-absolute custom-position btn btn-primary-outline" [href]="WikiLink.KavitaPlusFAQ" target="_blank" rel="noreferrer nofollow">{{t('faq-title')}}</a>
<a class="position-absolute custom-position btn btn-outline-primary" [href]="WikiLink.KavitaPlusFAQ" target="_blank" rel="noreferrer nofollow">{{t('faq-title')}}</a>
</div>
<div>
@ -99,9 +99,9 @@
<ng-template #titleActions>
@if (hasLicense) {
@if (licenseInfo?.isActive) {
<a class="btn btn-primary-outline btn-sm me-1" [href]="manageLink" target="_blank" rel="noreferrer nofollow">{{t('manage')}}</a>
<a class="btn btn-outline-primary btn-sm me-1" [href]="manageLink" target="_blank" rel="noreferrer nofollow">{{t('manage')}}</a>
} @else {
<a class="btn btn-primary-outline btn-sm me-1"
<a class="btn btn-outline-primary btn-sm me-1"
[ngbTooltip]="t('invalid-license-tooltip')"
href="mailto:kavitareader@gmail.com?subject=Kavita+Subscription+Renewal&body=Description%3A%0D%0A%0D%0ALicense%20Key%3A%0D%0A%0D%0AYour%20Email%3A"
>{{t('renew')}}</a>

View File

@ -24,13 +24,12 @@ import {SettingButtonComponent} from "../../settings/_components/setting-button/
import {LicenseService} from "../../_services/license.service";
@Component({
selector: 'app-license',
templateUrl: './license.component.html',
styleUrls: ['./license.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgbTooltip, LoadingComponent, ReactiveFormsModule, TranslocoDirective, SettingItemComponent,
DefaultValuePipe, UtcToLocalTimePipe, SettingButtonComponent, DecimalPipe]
selector: 'app-license',
templateUrl: './license.component.html',
styleUrls: ['./license.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgbTooltip, LoadingComponent, ReactiveFormsModule, TranslocoDirective, SettingItemComponent,
DefaultValuePipe, UtcToLocalTimePipe, SettingButtonComponent, DecimalPipe]
})
export class LicenseComponent implements OnInit {

View File

@ -1,7 +1,7 @@
<ng-container *transloco="let t; read: 'manage-email-settings'">
<div class="position-relative">
<button type="button" class="btn btn-primary-outline position-absolute custom-position" (click)="test()">{{t('test')}}</button>
<button type="button" class="btn btn-outline-primary position-absolute custom-position" (click)="test()">{{t('test')}}</button>
</div>
<p>{{t('description')}}</p>

View File

@ -13,12 +13,11 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {EnterBlurDirective} from "../../_directives/enter-blur.directive";
@Component({
selector: 'app-manage-email-settings',
templateUrl: './manage-email-settings.component.html',
styleUrls: ['./manage-email-settings.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, TranslocoModule, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, BytesPipe, EnterBlurDirective]
selector: 'app-manage-email-settings',
templateUrl: './manage-email-settings.component.html',
styleUrls: ['./manage-email-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, TranslocoModule, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, BytesPipe, EnterBlurDirective]
})
export class ManageEmailSettingsComponent implements OnInit {

View File

@ -1,11 +1,11 @@
<ng-container *transloco="let t; read: 'manage-library'">
<div class="position-relative">
<div class="position-absolute custom-position-2">
<app-card-actionables [actions]="bulkActions" btnClass="btn-primary-outline ms-1" [label]="t('bulk-action-label')" [disabled]="bulkMode" (actionHandler)="handleBulkAction($event, null)">
<app-card-actionables [actions]="bulkActions" btnClass="btn-outline-primary ms-1" [label]="t('bulk-action-label')" [disabled]="bulkMode" (actionHandler)="handleBulkAction($event, null)">
</app-card-actionables>
</div>
<button class="btn btn-primary-outline position-absolute custom-position" (click)="addLibrary()" [title]="t('add-library')">
<button class="btn btn-outline-primary position-absolute custom-position" (click)="addLibrary()" [title]="t('add-library')">
<i class="fa fa-plus" aria-hidden="true"></i><span class="phone-hidden ms-1">{{t('add-library')}}</span>
</button>
</div>
@ -83,9 +83,9 @@
}
@empty {
@if (loading) {
<tr><td colspan="4" style="text-align: center;"><app-loading [loading]="loading"></app-loading></td></tr>
<tr><td colspan="6" style="text-align: center;"><app-loading [loading]="loading"></app-loading></td></tr>
} @else {
<tr><td colspan="4" style="text-align: center;">{{t('no-data')}}</td></tr>
<tr><td colspan="6" style="text-align: center;">{{t('no-data')}}</td></tr>
}
}
</tbody>

View File

@ -1,4 +1,4 @@
@import "../../../theme/variables";
@use "../../../theme/variables" as theme;
.custom-position {
right: 15px;
@ -28,13 +28,13 @@
}
.table {
@media (max-width: $grid-breakpoints-sm) {
@media (max-width: theme.$grid-breakpoints-sm) {
overflow-x: auto;
width: 100% !important;
display: block;
}
.btn-container {
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
display: flex;
flex-direction: row;
flex-wrap: wrap;

View File

@ -33,21 +33,19 @@ import {Action, ActionFactoryService, ActionItem} from "../../_services/action-f
import {ActionService} from "../../_services/action.service";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
import {BehaviorSubject, catchError, Observable} from "rxjs";
import {Select2Module} from "ng-select2-component";
import {SelectionModel} from "../../typeahead/_models/selection-model";
import {
CopySettingsFromLibraryModalComponent
} from "../_modals/copy-settings-from-library-modal/copy-settings-from-library-modal.component";
import {FormControl, FormGroup} from "@angular/forms";
import {FormControl, FormGroup, FormsModule, ReactiveFormsModule} from "@angular/forms";
@Component({
selector: 'app-manage-library',
templateUrl: './manage-library.component.html',
styleUrls: ['./manage-library.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
selector: 'app-manage-library',
templateUrl: './manage-library.component.html',
styleUrls: ['./manage-library.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [RouterLink, NgbTooltip, LibraryTypePipe, TimeAgoPipe, SentenceCasePipe, TranslocoModule, DefaultDatePipe,
AsyncPipe, LoadingComponent, CardActionablesComponent, Select2Module, NgTemplateOutlet]
AsyncPipe, LoadingComponent, CardActionablesComponent, NgTemplateOutlet, ReactiveFormsModule, FormsModule]
})
export class ManageLibraryComponent implements OnInit {

View File

@ -10,7 +10,6 @@ import {ManageService} from "../../_services/manage.service";
import {ManageMatchSeries} from "../../_models/kavitaplus/manage-match-series";
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
import {FormControl, FormGroup, ReactiveFormsModule} from "@angular/forms";
import {Select2Module} from "ng-select2-component";
import {ManageMatchFilter} from "../../_models/kavitaplus/manage-match-filter";
import {allMatchStates, MatchStateOption} from "../../_models/kavitaplus/match-state-option";
import {MatchStateOptionPipe} from "../../_pipes/match-state.pipe";
@ -25,19 +24,17 @@ import {ScanSeriesEvent} from "../../_models/events/scan-series-event";
@Component({
selector: 'app-manage-matched-metadata',
standalone: true,
imports: [
TranslocoDirective,
ImageComponent,
VirtualScrollerModule,
ReactiveFormsModule,
Select2Module,
MatchStateOptionPipe,
UtcToLocalTimePipe,
DefaultValuePipe,
NgxDatatableModule,
LibraryNamePipe,
AsyncPipe,
TranslocoDirective,
ImageComponent,
VirtualScrollerModule,
ReactiveFormsModule,
MatchStateOptionPipe,
UtcToLocalTimePipe,
DefaultValuePipe,
NgxDatatableModule,
LibraryNamePipe,
AsyncPipe,
],
templateUrl: './manage-matched-metadata.component.html',
styleUrl: './manage-matched-metadata.component.scss',

View File

@ -1,13 +1,13 @@
@import "../../../theme/variables";
@use "../../../theme/variables" as theme;
.table {
@media (max-width: $grid-breakpoints-sm) {
@media (max-width: theme.$grid-breakpoints-sm) {
overflow-x: auto;
width: 100% !important;
display: block;
}
.btn-container {
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
display: flex;
flex-direction: row;
flex-wrap: wrap;

View File

@ -29,7 +29,6 @@ import {ColumnMode, NgxDatatableModule} from "@siemens/ngx-datatable";
templateUrl: './manage-media-issues.component.html',
styleUrls: ['./manage-media-issues.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ReactiveFormsModule, FilterPipe, TranslocoDirective, UtcToLocalTimePipe, DefaultDatePipe, NgxDatatableModule]
})
export class ManageMediaIssuesComponent implements OnInit {

View File

@ -14,7 +14,7 @@
@if(settingsForm.get('encodeMediaAs'); as formControl) {
<app-setting-item [title]="t('encode-as-label')" [subtitle]="t('encode-as-tooltip')">
<ng-template #view>
{{formControl!.value | encodeFormat}}
{{formControl.value | encodeFormat}}
</ng-template>
<ng-template #edit>
<select class="form-select" formControlName="encodeMediaAs">

View File

@ -5,25 +5,11 @@ import {debounceTime, distinctUntilChanged, filter, switchMap, take, tap} from '
import {SettingsService} from '../settings.service';
import {ServerSettings} from '../_models/server-settings';
import {DirectoryPickerComponent, DirectoryPickerResult} from '../_modals/directory-picker/directory-picker.component';
import {
NgbAccordionBody,
NgbAccordionButton,
NgbAccordionCollapse,
NgbAccordionDirective,
NgbAccordionHeader,
NgbAccordionItem,
NgbAccordionToggle,
NgbCollapse,
NgbModal,
NgbTooltip
} from '@ng-bootstrap/ng-bootstrap';
import {NgbModal} from '@ng-bootstrap/ng-bootstrap';
import {allEncodeFormats} from '../_models/encode-format';
import {ManageMediaIssuesComponent} from '../manage-media-issues/manage-media-issues.component';
import {NgFor, NgIf, NgTemplateOutlet} from '@angular/common';
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
import {allCoverImageSizes, CoverImageSize} from '../_models/cover-image-size';
import {pageLayoutModes} from "../../_models/preferences/preferences";
import {PageLayoutModePipe} from "../../_pipes/page-layout-mode.pipe";
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
import {EncodeFormatPipe} from "../../_pipes/encode-format.pipe";
import {CoverImageSizePipe} from "../../_pipes/cover-image-size.pipe";
@ -35,10 +21,7 @@ import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
templateUrl: './manage-media-settings.component.html',
styleUrls: ['./manage-media-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgIf, ReactiveFormsModule, NgbTooltip, NgTemplateOutlet, NgFor, NgbAccordionDirective, NgbAccordionItem,
NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody,
ManageMediaIssuesComponent, TranslocoDirective, PageLayoutModePipe, SettingItemComponent, EncodeFormatPipe, CoverImageSizePipe]
imports: [ReactiveFormsModule, TranslocoDirective, SettingItemComponent, EncodeFormatPipe, CoverImageSizePipe]
})
export class ManageMediaSettingsComponent implements OnInit {
@ -81,7 +64,12 @@ export class ManageMediaSettingsComponent implements OnInit {
this.toastr.info(translate('manage-media-settings.media-warning'));
}
this.serverSettings = settings;
if (settings.hasOwnProperty('result') && settings.hasOwnProperty('value')) {
this.serverSettings = (settings as any).value;
} else {
this.serverSettings = settings;
}
this.resetForm();
this.cdRef.markForCheck();
})

View File

@ -18,12 +18,10 @@ import {PersonRole} from "../../_models/metadata/person";
import {PersonRolePipe} from "../../_pipes/person-role.pipe";
import {allMetadataSettingField, MetadataSettingField} from "../_models/metadata-setting-field";
import {MetadataSettingFiledPipe} from "../../_pipes/metadata-setting-filed.pipe";
import {EnterBlurDirective} from "../../_directives/enter-blur.directive";
@Component({
selector: 'app-manage-metadata-settings',
standalone: true,
imports: [
TranslocoDirective,
ReactiveFormsModule,
@ -34,7 +32,7 @@ import {EnterBlurDirective} from "../../_directives/enter-blur.directive";
AgeRatingPipe,
PersonRolePipe,
MetadataSettingFiledPipe,
EnterBlurDirective,
],
templateUrl: './manage-metadata-settings.component.html',
styleUrl: './manage-metadata-settings.component.scss',

View File

@ -23,23 +23,19 @@ import {SeriesService} from "../../_services/series.service";
import {EditSeriesModalComponent} from "../../cards/_modals/edit-series-modal/edit-series-modal.component";
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
import {FilterPipe} from "../../_pipes/filter.pipe";
import {LoadingComponent} from "../../shared/loading/loading.component";
import {TranslocoModule} from "@jsverse/transloco";
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {TranslocoLocaleModule} from "@jsverse/transloco-locale";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {DefaultModalOptions} from "../../_models/default-modal-options";
import {ColumnMode, NgxDatatableModule} from "@siemens/ngx-datatable";
import {DevicePlatformPipe} from "../../_pipes/device-platform.pipe";
@Component({
selector: 'app-manage-scrobble-errors',
standalone: true,
imports: [ReactiveFormsModule, FilterPipe, LoadingComponent, SortableHeader, TranslocoModule, DefaultDatePipe, DefaultValuePipe, TranslocoLocaleModule, UtcToLocalTimePipe, DevicePlatformPipe, NgxDatatableModule],
templateUrl: './manage-scrobble-errors.component.html',
styleUrls: ['./manage-scrobble-errors.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-manage-scrobble-errors',
imports: [ReactiveFormsModule, FilterPipe, TranslocoModule, DefaultValuePipe, TranslocoLocaleModule, UtcToLocalTimePipe, NgxDatatableModule],
templateUrl: './manage-scrobble-errors.component.html',
styleUrls: ['./manage-scrobble-errors.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ManageScrobbleErrorsComponent implements OnInit {
@Output() scrobbleCount = new EventEmitter<number>();

View File

@ -8,16 +8,15 @@ import {
} from "../../_single-module/user-scrobble-history/user-scrobble-history.component";
@Component({
selector: 'app-manage-scrobling',
standalone: true,
imports: [
ManageScrobbleErrorsComponent,
AsyncPipe,
UserScrobbleHistoryComponent
],
templateUrl: './manage-scrobbling.component.html',
styleUrl: './manage-scrobbling.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-manage-scrobling',
imports: [
ManageScrobbleErrorsComponent,
AsyncPipe,
UserScrobbleHistoryComponent
],
templateUrl: './manage-scrobbling.component.html',
styleUrl: './manage-scrobbling.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ManageScrobblingComponent {

View File

@ -1,7 +1,7 @@
<ng-container *transloco="let t; read: 'manage-settings'">
<div class="position-relative">
<button type="button" class="btn btn-primary-outline position-absolute custom-position" (click)="resetToDefaults()">{{t('reset-to-default')}}</button>
<button type="button" class="btn btn-outline-primary position-absolute custom-position" (click)="resetToDefaults()">{{t('reset-to-default')}}</button>
</div>

View File

@ -11,7 +11,7 @@ import {WikiLink} from "../../_models/wiki";
import {SettingItemComponent} from "../../settings/_components/setting-item/setting-item.component";
import {SettingSwitchComponent} from "../../settings/_components/setting-switch/setting-switch.component";
import {ConfirmService} from "../../shared/confirm.service";
import {debounceTime, distinctUntilChanged, filter, of, switchMap, tap} from "rxjs";
import {debounceTime, distinctUntilChanged, filter, switchMap, tap} from "rxjs";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {EnterBlurDirective} from "../../_directives/enter-blur.directive";
@ -19,12 +19,11 @@ import {EnterBlurDirective} from "../../_directives/enter-blur.directive";
const ValidIpAddress = /^(\s*((([12]?\d{1,2}\.){3}[12]?\d{1,2})|(([\da-f]{0,4}\:){0,7}([\da-f]{0,4})))\s*\,)*\s*((([12]?\d{1,2}\.){3}[12]?\d{1,2})|(([\da-f]{0,4}\:){0,7}([\da-f]{0,4})))\s*$/i;
@Component({
selector: 'app-manage-settings',
templateUrl: './manage-settings.component.html',
styleUrls: ['./manage-settings.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, TitleCasePipe, TranslocoModule, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, EnterBlurDirective]
selector: 'app-manage-settings',
templateUrl: './manage-settings.component.html',
styleUrls: ['./manage-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, TitleCasePipe, TranslocoModule, SettingItemComponent, SettingSwitchComponent, DefaultValuePipe, EnterBlurDirective]
})
export class ManageSettingsComponent implements OnInit {
@ -134,6 +133,8 @@ export class ManageSettingsComponent implements OnInit {
const modelSettings = this.settingsForm.value;
modelSettings.bookmarksDirectory = this.serverSettings.bookmarksDirectory;
modelSettings.smtpConfig = this.serverSettings.smtpConfig;
modelSettings.installId = this.serverSettings.installId;
modelSettings.installVersion = this.serverSettings.installVersion;
return modelSettings;
}

View File

@ -9,9 +9,8 @@ import {ChangelogComponent} from "../../announcements/_components/changelog/chan
selector: 'app-manage-system',
templateUrl: './manage-system.component.html',
styleUrls: ['./manage-system.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TranslocoDirective, ChangelogComponent, DatePipe]
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [TranslocoDirective, ChangelogComponent, DatePipe]
})
export class ManageSystemComponent implements OnInit {

View File

@ -31,14 +31,13 @@ interface AdhocTask {
}
@Component({
selector: 'app-manage-tasks-settings',
templateUrl: './manage-tasks-settings.component.html',
styleUrls: ['./manage-tasks-settings.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-manage-tasks-settings',
templateUrl: './manage-tasks-settings.component.html',
styleUrls: ['./manage-tasks-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, AsyncPipe, TitleCasePipe, DefaultValuePipe,
TranslocoModule, TranslocoLocaleModule, UtcToLocalTimePipe, SettingItemComponent,
SettingButtonComponent, NgxDatatableModule]
SettingButtonComponent, NgxDatatableModule]
})
export class ManageTasksSettingsComponent implements OnInit {

View File

@ -2,31 +2,20 @@ import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} f
import {TranslocoDirective} from "@jsverse/transloco";
import {MemberService} from "../../_services/member.service";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {LoadingComponent} from "../../shared/loading/loading.component";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {VirtualScrollerModule} from "@iharbeck/ngx-virtual-scroller";
import {UserTokenInfo} from "../../_models/kavitaplus/user-token-info";
import {ServerService} from "../../_services/server.service";
import {SettingsService} from "../settings.service";
import {MessageHubService} from "../../_services/message-hub.service";
import {ConfirmService} from "../../shared/confirm.service";
import {ColumnMode, NgxDatatableModule} from "@siemens/ngx-datatable";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
import {ImageComponent} from "../../shared/image/image.component";
@Component({
selector: 'app-manage-user-tokens',
standalone: true,
imports: [
TranslocoDirective,
DefaultValuePipe,
LoadingComponent,
UtcToLocalTimePipe,
VirtualScrollerModule,
CardActionablesComponent,
ImageComponent,
NgxDatatableModule
],
imports: [
TranslocoDirective,
DefaultValuePipe,
UtcToLocalTimePipe,
VirtualScrollerModule,
NgxDatatableModule
],
templateUrl: './manage-user-tokens.component.html',
styleUrl: './manage-user-tokens.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -1,6 +1,6 @@
<ng-container *transloco="let t; read: 'manage-users'">
<div class="position-relative">
<button class="btn btn-primary-outline position-absolute custom-position" (click)="inviteUser()">
<button class="btn btn-outline-primary position-absolute custom-position" (click)="inviteUser()">
<i class="fa fa-plus" aria-hidden="true"></i><span class="phone-hidden">&nbsp;{{t('invite')}}</span>
</button>
</div>

View File

@ -1,4 +1,4 @@
@import '../../../theme/variables';
@use '../../../theme/variables' as theme;
.presence {
font-size: 12px;
@ -36,13 +36,13 @@
}
.table {
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
overflow-x: auto;
width: 100% !important;
display: block;
}
.btn-container {
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
display: flex;
flex-direction: row;
flex-wrap: wrap;

View File

@ -12,11 +12,10 @@ import {InviteUserComponent} from '../invite-user/invite-user.component';
import {EditUserComponent} from '../edit-user/edit-user.component';
import {Router} from '@angular/router';
import {TagBadgeComponent} from '../../shared/tag-badge/tag-badge.component';
import {AsyncPipe, DatePipe, NgClass, NgIf, TitleCasePipe} from '@angular/common';
import {AsyncPipe, NgClass, TitleCasePipe} from '@angular/common';
import {translate, TranslocoModule, TranslocoService} from "@jsverse/transloco";
import {DefaultDatePipe} from "../../_pipes/default-date.pipe";
import {DefaultValuePipe} from "../../_pipes/default-value.pipe";
import {ReadMoreComponent} from "../../shared/read-more/read-more.component";
import {UtcToLocalTimePipe} from "../../_pipes/utc-to-local-time.pipe";
import {makeBindingParser} from "@angular/compiler";
import {LoadingComponent} from "../../shared/loading/loading.component";
@ -26,19 +25,14 @@ import {DefaultModalOptions} from "../../_models/default-modal-options";
import {UtcToLocaleDatePipe} from "../../_pipes/utc-to-locale-date.pipe";
@Component({
selector: 'app-manage-users',
templateUrl: './manage-users.component.html',
styleUrls: ['./manage-users.component.scss'],
standalone: true,
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgbTooltip, TagBadgeComponent, AsyncPipe, TitleCasePipe, DatePipe, TranslocoModule, DefaultDatePipe, NgClass, DefaultValuePipe, ReadMoreComponent, UtcToLocalTimePipe, LoadingComponent, NgIf, TimeAgoPipe, SentenceCasePipe, UtcToLocaleDatePipe]
selector: 'app-manage-users',
templateUrl: './manage-users.component.html',
styleUrls: ['./manage-users.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [NgbTooltip, TagBadgeComponent, AsyncPipe, TitleCasePipe, TranslocoModule, DefaultDatePipe, NgClass, DefaultValuePipe, UtcToLocalTimePipe, LoadingComponent, TimeAgoPipe, SentenceCasePipe, UtcToLocaleDatePipe]
})
export class ManageUsersComponent implements OnInit {
members: Member[] = [];
loggedInUsername = '';
loadingMembers = false;
private readonly translocoService = inject(TranslocoService);
private readonly cdRef = inject(ChangeDetectorRef);
private readonly memberService = inject(MemberService);
@ -49,6 +43,11 @@ export class ManageUsersComponent implements OnInit {
public readonly messageHub = inject(MessageHubService);
private readonly router = inject(Router);
members: Member[] = [];
loggedInUsername = '';
loadingMembers = false;
constructor() {
this.accountService.currentUser$.pipe(take(1)).subscribe((user) => {
if (user) {

View File

@ -8,21 +8,19 @@ import {
OnInit,
Output
} from '@angular/core';
import { Member } from 'src/app/_models/auth/member';
import { User } from 'src/app/_models/user';
import {Member} from 'src/app/_models/auth/member';
import {User} from 'src/app/_models/user';
import {AccountService} from 'src/app/_services/account.service';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';
import { NgFor } from '@angular/common';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {TranslocoDirective,} from "@jsverse/transloco";
import {SelectionModel} from "../../typeahead/_models/selection-model";
@Component({
selector: 'app-role-selector',
templateUrl: './role-selector.component.html',
styleUrls: ['./role-selector.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [NgFor, ReactiveFormsModule, FormsModule, TranslocoDirective]
selector: 'app-role-selector',
templateUrl: './role-selector.component.html',
styleUrls: ['./role-selector.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [ReactiveFormsModule, FormsModule, TranslocoDirective]
})
export class RoleSelectorComponent implements OnInit {

View File

@ -1,29 +1,22 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, OnInit} from '@angular/core';
import {JumpKey} from "../_models/jumpbar/jump-key";
import {TranslocoDirective} from "@jsverse/transloco";
import {CardItemComponent} from "../cards/card-item/card-item.component";
import {
SideNavCompanionBarComponent
} from "../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component";
import {SmartFilter} from "../_models/metadata/v2/smart-filter";
import {FilterService} from "../_services/filter.service";
import {CardDetailLayoutComponent} from "../cards/card-detail-layout/card-detail-layout.component";
import {SafeHtmlPipe} from "../_pipes/safe-html.pipe";
import {Router, RouterLink} from "@angular/router";
import {Router} from "@angular/router";
import {Series} from "../_models/series";
import {JumpbarService} from "../_services/jumpbar.service";
import {Action, ActionFactoryService, ActionItem} from "../_services/action-factory.service";
import {CardActionablesComponent} from "../_single-module/card-actionables/card-actionables.component";
import {ActionService} from "../_services/action.service";
import {FilterPipe} from "../_pipes/filter.pipe";
import {filter} from "rxjs";
import {ManageSmartFiltersComponent} from "../sidenav/_components/manage-smart-filters/manage-smart-filters.component";
import {DecimalPipe} from "@angular/common";
@Component({
selector: 'app-all-filters',
standalone: true,
imports: [TranslocoDirective, CardItemComponent, SideNavCompanionBarComponent, CardDetailLayoutComponent, SafeHtmlPipe, CardActionablesComponent, RouterLink, FilterPipe, ManageSmartFiltersComponent, DecimalPipe],
imports: [TranslocoDirective, SideNavCompanionBarComponent, ManageSmartFiltersComponent, DecimalPipe],
templateUrl: './all-filters.component.html',
styleUrl: './all-filters.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -35,12 +35,11 @@ import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
@Component({
selector: 'app-all-series',
templateUrl: './all-series.component.html',
styleUrls: ['./all-series.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [SideNavCompanionBarComponent, NgIf, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, DecimalPipe, TranslocoDirective]
selector: 'app-all-series',
templateUrl: './all-series.component.html',
styleUrls: ['./all-series.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [SideNavCompanionBarComponent, NgIf, BulkOperationsComponent, CardDetailLayoutComponent, SeriesCardComponent, DecimalPipe, TranslocoDirective]
})
export class AllSeriesComponent implements OnInit {

View File

@ -7,8 +7,7 @@ import {TranslocoDirective} from "@jsverse/transloco";
selector: 'app-announcements',
templateUrl: './announcements.component.html',
styleUrls: ['./announcements.component.scss'],
standalone: true,
imports: [SideNavCompanionBarComponent, ChangelogComponent, TranslocoDirective]
imports: [SideNavCompanionBarComponent, ChangelogComponent, TranslocoDirective]
})
export class AnnouncementsComponent {

View File

@ -7,18 +7,17 @@ import {TranslocoDirective} from "@jsverse/transloco";
import {AccountService} from "../../../_services/account.service";
@Component({
selector: 'app-changelog-update-item',
standalone: true,
imports: [
SafeHtmlPipe,
UpdateSectionComponent,
AsyncPipe,
DatePipe,
TranslocoDirective
],
templateUrl: './changelog-update-item.component.html',
styleUrl: './changelog-update-item.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-changelog-update-item',
imports: [
SafeHtmlPipe,
UpdateSectionComponent,
AsyncPipe,
DatePipe,
TranslocoDirective
],
templateUrl: './changelog-update-item.component.html',
styleUrl: './changelog-update-item.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChangelogUpdateItemComponent {

View File

@ -15,13 +15,12 @@ import {
import {ChangelogUpdateItemComponent} from "../changelog-update-item/changelog-update-item.component";
@Component({
selector: 'app-changelog',
templateUrl: './changelog.component.html',
styleUrls: ['./changelog.component.scss'],
standalone: true,
imports: [LoadingComponent, TranslocoDirective, NgbAccordionDirective,
NgbAccordionItem, NgbAccordionButton, NgbAccordionHeader, NgbAccordionCollapse, NgbAccordionBody, ChangelogUpdateItemComponent],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-changelog',
templateUrl: './changelog.component.html',
styleUrls: ['./changelog.component.scss'],
imports: [LoadingComponent, TranslocoDirective, NgbAccordionDirective,
NgbAccordionItem, NgbAccordionButton, NgbAccordionHeader, NgbAccordionCollapse, NgbAccordionBody, ChangelogUpdateItemComponent],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChangelogComponent implements OnInit {

View File

@ -11,15 +11,14 @@ import {ChangelogUpdateItemComponent} from "../changelog-update-item/changelog-u
* This modal is used when an update occurred and the UI needs to be refreshed to get the latest JS libraries
*/
@Component({
selector: 'app-new-update-modal',
standalone: true,
imports: [
TranslocoDirective,
ChangelogUpdateItemComponent
],
templateUrl: './new-update-modal.component.html',
styleUrl: './new-update-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-new-update-modal',
imports: [
TranslocoDirective,
ChangelogUpdateItemComponent
],
templateUrl: './new-update-modal.component.html',
styleUrl: './new-update-modal.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class NewUpdateModalComponent {

View File

@ -1,26 +1,14 @@
import {Component, DestroyRef, inject, Input} from '@angular/core';
import {Component, inject, Input} from '@angular/core';
import {FormsModule} from "@angular/forms";
import {AsyncPipe, NgForOf, NgIf} from "@angular/common";
import {NgbActiveModal, NgbHighlight, NgbModal, NgbTypeahead} from "@ng-bootstrap/ng-bootstrap";
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
import {TranslocoDirective} from "@jsverse/transloco";
import {ServerService} from "../../../_services/server.service";
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {map} from "rxjs/operators";
import {ChangelogComponent} from "../changelog/changelog.component";
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
@Component({
selector: 'app-out-of-date-modal',
standalone: true,
imports: [
FormsModule,
NgForOf,
NgIf,
NgbHighlight,
NgbTypeahead,
TranslocoDirective,
AsyncPipe,
ChangelogComponent,
SafeHtmlPipe
],
templateUrl: './out-of-date-modal.component.html',

View File

@ -1,19 +1,15 @@
import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core';
import {NgbActiveModal, NgbModalModule} from '@ng-bootstrap/ng-bootstrap';
import { UpdateVersionEvent } from 'src/app/_models/events/update-version-event';
import {UpdateVersionEvent} from 'src/app/_models/events/update-version-event';
import {CommonModule} from "@angular/common";
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
import {TranslocoDirective} from "@jsverse/transloco";
import {WikiLink} from "../../../_models/wiki";
import {
ChangelogUpdateItemComponent
} from "../changelog-update-item/changelog-update-item.component";
import {ChangelogUpdateItemComponent} from "../changelog-update-item/changelog-update-item.component";
@Component({
selector: 'app-update-notification-modal',
standalone: true,
imports: [CommonModule, NgbModalModule, SafeHtmlPipe, TranslocoDirective, ChangelogUpdateItemComponent],
imports: [CommonModule, NgbModalModule, TranslocoDirective, ChangelogUpdateItemComponent],
templateUrl: './update-notification-modal.component.html',
styleUrls: ['./update-notification-modal.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -1,12 +1,11 @@
import {ChangeDetectionStrategy, Component, Input} from '@angular/core';
@Component({
selector: 'app-update-section',
standalone: true,
imports: [],
templateUrl: './update-section.component.html',
styleUrl: './update-section.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-update-section',
imports: [],
templateUrl: './update-section.component.html',
styleUrl: './update-section.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UpdateSectionComponent {
@Input({required: true}) items: Array<string> = [];

View File

@ -1,4 +1,4 @@
@import '../theme/variables';
@use '../theme/variables' as theme;
.content-wrapper {
padding: 0 0 0 10px;
@ -59,7 +59,7 @@
margin-left: var(--side-nav-width);
}
@media (max-width: $grid-breakpoints-lg) {
@media (max-width: theme.$grid-breakpoints-lg) {
::ng-deep html {
height: 100dvh !important;
}

View File

@ -30,9 +30,8 @@ import {LocalizationService} from "./_services/localization.service";
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss'],
standalone: true,
imports: [NgClass, SideNavComponent, RouterOutlet, AsyncPipe, NavHeaderComponent, PreferenceNavComponent],
changeDetection: ChangeDetectionStrategy.OnPush
imports: [NgClass, SideNavComponent, RouterOutlet, AsyncPipe, NavHeaderComponent, PreferenceNavComponent],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class AppComponent implements OnInit {
protected readonly Breakpoint = Breakpoint;

View File

@ -23,12 +23,11 @@ enum BookLineOverlayMode {
}
@Component({
selector: 'app-book-line-overlay',
standalone: true,
selector: 'app-book-line-overlay',
imports: [CommonModule, ReactiveFormsModule, TranslocoDirective],
templateUrl: './book-line-overlay.component.html',
styleUrls: ['./book-line-overlay.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
templateUrl: './book-line-overlay.component.html',
styleUrls: ['./book-line-overlay.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BookLineOverlayComponent implements OnInit {
@Input({required: true}) libraryId!: number;

View File

@ -112,10 +112,9 @@ const elementLevelStyles = ['line-height', 'font-family'];
transition('false <=> true', animate('4000ms'))
])
],
standalone: true,
imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink,
NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip,
BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective]
imports: [NgTemplateOutlet, DrawerComponent, NgIf, NgbProgressbar, NgbNav, NgbNavItem, NgbNavItemRole, NgbNavLink,
NgbNavContent, ReaderSettingsComponent, TableOfContentsComponent, NgbNavOutlet, NgStyle, NgClass, NgbTooltip,
BookLineOverlayComponent, PersonalTableOfContentsComponent, TranslocoDirective]
})
export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy {

View File

@ -21,12 +21,11 @@ export interface PersonalToCEvent {
}
@Component({
selector: 'app-personal-table-of-contents',
standalone: true,
imports: [NgbTooltip, TranslocoDirective],
templateUrl: './personal-table-of-contents.component.html',
styleUrls: ['./personal-table-of-contents.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-personal-table-of-contents',
imports: [NgbTooltip, TranslocoDirective],
templateUrl: './personal-table-of-contents.component.html',
styleUrls: ['./personal-table-of-contents.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class PersonalTableOfContentsComponent implements OnInit {

View File

@ -115,7 +115,7 @@
<ng-container [ngTemplateOutlet]="fullscreenTooltip"></ng-container>
</span>
<button (click)="toggleFullscreen()" class="btn btn-icon" aria-labelledby="fullscreen">
<i class="fa {{this.isFullscreen ? 'fa-compress-alt' : 'fa-expand-alt'}} {{isFullscreen ? 'icon-primary-color' : ''}}" aria-hidden="true"></i>
<i class="fa {{isFullscreen ? 'fa-compress-alt' : 'fa-expand-alt'}} {{isFullscreen ? 'icon-primary-color' : ''}}" aria-hidden="true"></i>
<span *ngIf="activeTheme?.isDarkTheme">&nbsp;{{isFullscreen ? t('exit') : t('enter')}}</span>
</button>
</div>

View File

@ -89,8 +89,7 @@ const mobileBreakpointMarginOverride = 700;
templateUrl: './reader-settings.component.html',
styleUrls: ['./reader-settings.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [ReactiveFormsModule, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody, NgFor, NgbTooltip, NgTemplateOutlet, NgIf, NgClass, NgStyle, TitleCasePipe, TranslocoDirective]
imports: [ReactiveFormsModule, NgbAccordionDirective, NgbAccordionItem, NgbAccordionHeader, NgbAccordionToggle, NgbAccordionButton, NgbCollapse, NgbAccordionCollapse, NgbAccordionBody, NgFor, NgbTooltip, NgTemplateOutlet, NgIf, NgClass, NgStyle, TitleCasePipe, TranslocoDirective]
})
export class ReaderSettingsComponent implements OnInit {
/**

View File

@ -1,26 +1,36 @@
<ng-container *transloco="let t; read: 'table-of-contents'">
<div class="table-of-contents">
<div *ngIf="chapters.length === 0">
<em>{{t('no-data')}}</em>
</div>
<div *ngIf="chapters.length === 1; else nestedChildren">
<ul>
<li *ngFor="let chapter of chapters[0].children">
<a href="javascript:void(0);" (click)="loadChapterPage(chapter.page, chapter.part)">{{chapter.title}}</a>
</li>
</ul>
</div>
<ng-template #nestedChildren>
<ul *ngFor="let chapterGroup of chapters" class="chapter-title">
<li class="{{chapterGroup.page === pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
{{chapterGroup.title}}
</li>
<ul *ngFor="let chapter of chapterGroup.children">
<li class="{{cleanIdSelector(chapter.part) === currentPageAnchor ? 'active' : ''}}">
<a href="javascript:void(0);" (click)="loadChapterPage(chapter.page, chapter.part)">{{chapter.title}}</a>
</li>
@if (chapters.length === 0) {
<div>
<em>{{t('no-data')}}</em>
</div>
} @else if (chapters.length === 1) {
<div>
<ul>
@for(chapter of chapters[0].children; track chapter.title) {
<li class="{{chapter.page === pageNum ? 'active': ''}}">
<a href="javascript:void(0);" (click)="loadChapterPage(chapter.page, chapter.part)">{{chapter.title}}</a>
</li>
}
</ul>
</ul>
</ng-template>
</div>
} @else {
@for (chapterGroup of chapters; track chapterGroup.title + chapterGroup.children.length) {
<ul class="chapter-title">
<li class="{{chapterGroup.page === pageNum ? 'active': ''}}" (click)="loadChapterPage(chapterGroup.page, '')">
{{chapterGroup.title}}
</li>
@for(chapter of chapterGroup.children; track chapter.title + chapter.part) {
<ul>
<li class="{{cleanIdSelector(chapter.part) === currentPageAnchor ? 'active' : ''}}">
<a href="javascript:void(0);" (click)="loadChapterPage(chapter.page, chapter.part)">{{chapter.title}}</a>
</li>
</ul>
}
</ul>
}
}
</div>
</ng-container>

View File

@ -1,17 +1,27 @@
import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core';
import { BookChapterItem } from '../../_models/book-chapter-item';
import { NgIf, NgFor } from '@angular/common';
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
inject,
Input,
OnChanges,
Output,
SimpleChanges
} from '@angular/core';
import {BookChapterItem} from '../../_models/book-chapter-item';
import {TranslocoDirective} from "@jsverse/transloco";
@Component({
selector: 'app-table-of-contents',
templateUrl: './table-of-contents.component.html',
styleUrls: ['./table-of-contents.component.scss'],
changeDetection: ChangeDetectionStrategy.Default,
standalone: true,
imports: [NgIf, NgFor, TranslocoDirective]
selector: 'app-table-of-contents',
templateUrl: './table-of-contents.component.html',
styleUrls: ['./table-of-contents.component.scss'],
imports: [TranslocoDirective],
changeDetection: ChangeDetectionStrategy.Default,
})
export class TableOfContentsComponent {
export class TableOfContentsComponent implements OnChanges {
private readonly cdRef = inject(ChangeDetectorRef);
@Input({required: true}) chapterId!: number;
@Input({required: true}) pageNum!: number;
@ -20,7 +30,11 @@ export class TableOfContentsComponent {
@Output() loadChapter: EventEmitter<{pageNum: number, part: string}> = new EventEmitter();
constructor() {}
ngOnChanges(changes: SimpleChanges) {
console.log('Current Page: ', this.pageNum, this.currentPageAnchor);
this.cdRef.markForCheck();
}
cleanIdSelector(id: string) {
const tokens = id.split('/');

View File

@ -1,34 +1,28 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
EventEmitter,
HostListener,
inject,
OnInit
} from '@angular/core';
import { ActivatedRoute, Router } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, inject, OnInit} from '@angular/core';
import {ActivatedRoute, Router} from '@angular/router';
import {ToastrService} from 'ngx-toastr';
import {take} from 'rxjs';
import { BulkSelectionService } from 'src/app/cards/bulk-selection.service';
import { FilterSettings } from 'src/app/metadata-filter/filter-settings';
import { ConfirmService } from 'src/app/shared/confirm.service';
import {BulkSelectionService} from 'src/app/cards/bulk-selection.service';
import {FilterSettings} from 'src/app/metadata-filter/filter-settings';
import {ConfirmService} from 'src/app/shared/confirm.service';
import {DownloadService} from 'src/app/shared/_services/download.service';
import { FilterUtilitiesService } from 'src/app/shared/_services/filter-utilities.service';
import { JumpKey } from 'src/app/_models/jumpbar/jump-key';
import { PageBookmark } from 'src/app/_models/readers/page-bookmark';
import { Pagination } from 'src/app/_models/pagination';
import { Series } from 'src/app/_models/series';
import { FilterEvent } from 'src/app/_models/metadata/series-filter';
import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/action-factory.service';
import { ImageService } from 'src/app/_services/image.service';
import { JumpbarService } from 'src/app/_services/jumpbar.service';
import { ReaderService } from 'src/app/_services/reader.service';
import {DecimalPipe, NgIf} from '@angular/common';
import { CardItemComponent } from '../../../cards/card-item/card-item.component';
import { CardDetailLayoutComponent } from '../../../cards/card-detail-layout/card-detail-layout.component';
import { BulkOperationsComponent } from '../../../cards/bulk-operations/bulk-operations.component';
import { SideNavCompanionBarComponent } from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
import {FilterUtilitiesService} from 'src/app/shared/_services/filter-utilities.service';
import {JumpKey} from 'src/app/_models/jumpbar/jump-key';
import {PageBookmark} from 'src/app/_models/readers/page-bookmark';
import {Pagination} from 'src/app/_models/pagination';
import {Series} from 'src/app/_models/series';
import {FilterEvent} from 'src/app/_models/metadata/series-filter';
import {Action, ActionFactoryService, ActionItem} from 'src/app/_services/action-factory.service';
import {ImageService} from 'src/app/_services/image.service';
import {JumpbarService} from 'src/app/_services/jumpbar.service';
import {ReaderService} from 'src/app/_services/reader.service';
import {DecimalPipe} from '@angular/common';
import {CardItemComponent} from '../../../cards/card-item/card-item.component';
import {CardDetailLayoutComponent} from '../../../cards/card-detail-layout/card-detail-layout.component';
import {BulkOperationsComponent} from '../../../cards/bulk-operations/bulk-operations.component';
import {
SideNavCompanionBarComponent
} from '../../../sidenav/_components/side-nav-companion-bar/side-nav-companion-bar.component';
import {translate, TranslocoDirective, TranslocoService} from "@jsverse/transloco";
import {SeriesFilterV2} from "../../../_models/metadata/v2/series-filter-v2";
import {Title} from "@angular/platform-browser";
@ -39,8 +33,7 @@ import {WikiLink} from "../../../_models/wiki";
templateUrl: './bookmarks.component.html',
styleUrls: ['./bookmarks.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: true,
imports: [SideNavCompanionBarComponent, BulkOperationsComponent, CardDetailLayoutComponent, CardItemComponent, DecimalPipe, TranslocoDirective, NgIf]
imports: [SideNavCompanionBarComponent, BulkOperationsComponent, CardDetailLayoutComponent, CardItemComponent, DecimalPipe, TranslocoDirective]
})
export class BookmarksComponent implements OnInit {

View File

@ -23,7 +23,6 @@ import {MessageHubService} from "../_services/message-hub.service";
import {UtilityService} from "../shared/_services/utility.service";
import {PersonService} from "../_services/person.service";
import {BrowsePerson} from "../_models/person/browse-person";
import {CardItemComponent} from "../cards/card-item/card-item.component";
import {JumpbarService} from "../_services/jumpbar.service";
import {PersonCardComponent} from "../cards/person-card/person-card.component";
import {ImageService} from "../_services/image.service";
@ -33,13 +32,11 @@ import {CompactNumberPipe} from "../_pipes/compact-number.pipe";
@Component({
selector: 'app-browse-authors',
standalone: true,
imports: [
SideNavCompanionBarComponent,
TranslocoDirective,
CardDetailLayoutComponent,
DecimalPipe,
CardItemComponent,
PersonCardComponent,
CompactNumberPipe,
],

View File

@ -22,13 +22,12 @@ import {translate, TranslocoDirective} from "@jsverse/transloco";
import {ScrobbleProvider} from "../../../_services/scrobbling.service";
@Component({
selector: 'app-bulk-add-to-collection',
standalone: true,
imports: [CommonModule, ReactiveFormsModule, FilterPipe, NgbModalModule, TranslocoDirective],
templateUrl: './bulk-add-to-collection.component.html',
styleUrls: ['./bulk-add-to-collection.component.scss'],
encapsulation: ViewEncapsulation.None, // This is needed as per the bootstrap modal documentation to get styles to work.
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-bulk-add-to-collection',
imports: [CommonModule, ReactiveFormsModule, FilterPipe, NgbModalModule, TranslocoDirective],
templateUrl: './bulk-add-to-collection.component.html',
styleUrls: ['./bulk-add-to-collection.component.scss'],
encapsulation: ViewEncapsulation.None, // This is needed as per the bootstrap modal documentation to get styles to work.
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BulkAddToCollectionComponent implements OnInit, AfterViewInit {

View File

@ -23,19 +23,15 @@ import {LibraryService} from 'src/app/_services/library.service';
import {SeriesService} from 'src/app/_services/series.service';
import {UploadService} from 'src/app/_services/upload.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {DatePipe, DecimalPipe, NgIf, NgTemplateOutlet} from "@angular/common";
import {DecimalPipe, NgTemplateOutlet} from "@angular/common";
import {CoverImageChooserComponent} from "../../cover-image-chooser/cover-image-chooser.component";
import {translate, TranslocoDirective} from "@jsverse/transloco";
import {ScrobbleProvider} from "../../../_services/scrobbling.service";
import {FilterPipe} from "../../../_pipes/filter.pipe";
import {AccountService} from "../../../_services/account.service";
import {DefaultDatePipe} from "../../../_pipes/default-date.pipe";
import {ReadMoreComponent} from "../../../shared/read-more/read-more.component";
import {SafeHtmlPipe} from "../../../_pipes/safe-html.pipe";
import {SafeUrlPipe} from "../../../_pipes/safe-url.pipe";
import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
import {SentenceCasePipe} from "../../../_pipes/sentence-case.pipe";
import {TagBadgeComponent} from "../../../shared/tag-badge/tag-badge.component";
import {SelectionModel} from "../../../typeahead/_models/selection-model";
import {UtcToLocalTimePipe} from "../../../_pipes/utc-to-local-time.pipe";
@ -49,9 +45,9 @@ enum TabID {
@Component({
selector: 'app-edit-collection-tags',
standalone: true,
imports: [NgbNav, NgbNavItem, NgbNavLink, NgbNavContent, ReactiveFormsModule, FormsModule, NgbPagination,
CoverImageChooserComponent, NgbNavOutlet, NgbTooltip, TranslocoDirective, NgTemplateOutlet, FilterPipe, DatePipe, DefaultDatePipe, ReadMoreComponent, SafeHtmlPipe, SafeUrlPipe, MangaFormatPipe, NgIf, SentenceCasePipe, TagBadgeComponent, DecimalPipe, UtcToLocalTimePipe],
CoverImageChooserComponent, NgbNavOutlet, NgbTooltip, TranslocoDirective, NgTemplateOutlet, FilterPipe, DefaultDatePipe,
SafeHtmlPipe, SafeUrlPipe, DecimalPipe, UtcToLocalTimePipe],
templateUrl: './edit-collection-tags.component.html',
styleUrls: ['./edit-collection-tags.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush

View File

@ -1,7 +1,8 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component, DestroyRef,
Component,
DestroyRef,
EventEmitter,
inject,
Input,
@ -9,32 +10,32 @@ import {
} from '@angular/core';
import {FormBuilder, FormControl, FormGroup, ReactiveFormsModule, Validators} from '@angular/forms';
import {
NgbActiveModal, NgbCollapse,
NgbActiveModal,
NgbCollapse,
NgbNav,
NgbNavContent,
NgbNavItem,
NgbNavLink,
NgbNavOutlet,
NgbTooltip
NgbNavOutlet
} from '@ng-bootstrap/ng-bootstrap';
import {forkJoin, Observable, of, tap} from 'rxjs';
import { map, switchMap } from 'rxjs/operators';
import { Breakpoint, UtilityService } from 'src/app/shared/_services/utility.service';
import { TypeaheadSettings } from 'src/app/typeahead/_models/typeahead-settings';
import {map, switchMap} from 'rxjs/operators';
import {Breakpoint, UtilityService} from 'src/app/shared/_services/utility.service';
import {TypeaheadSettings} from 'src/app/typeahead/_models/typeahead-settings';
import {Chapter, LooseLeafOrDefaultNumber, SpecialVolumeNumber} from 'src/app/_models/chapter';
import { Genre } from 'src/app/_models/metadata/genre';
import { AgeRatingDto } from 'src/app/_models/metadata/age-rating-dto';
import { Language } from 'src/app/_models/metadata/language';
import { PublicationStatusDto } from 'src/app/_models/metadata/publication-status-dto';
import { Person, PersonRole } from 'src/app/_models/metadata/person';
import { Series } from 'src/app/_models/series';
import { SeriesMetadata } from 'src/app/_models/metadata/series-metadata';
import { Tag } from 'src/app/_models/tag';
import { ImageService } from 'src/app/_services/image.service';
import { LibraryService } from 'src/app/_services/library.service';
import { MetadataService } from 'src/app/_services/metadata.service';
import { SeriesService } from 'src/app/_services/series.service';
import { UploadService } from 'src/app/_services/upload.service';
import {Genre} from 'src/app/_models/metadata/genre';
import {AgeRatingDto} from 'src/app/_models/metadata/age-rating-dto';
import {Language} from 'src/app/_models/metadata/language';
import {PublicationStatusDto} from 'src/app/_models/metadata/publication-status-dto';
import {Person, PersonRole} from 'src/app/_models/metadata/person';
import {Series} from 'src/app/_models/series';
import {SeriesMetadata} from 'src/app/_models/metadata/series-metadata';
import {Tag} from 'src/app/_models/tag';
import {ImageService} from 'src/app/_services/image.service';
import {LibraryService} from 'src/app/_services/library.service';
import {MetadataService} from 'src/app/_services/metadata.service';
import {SeriesService} from 'src/app/_services/series.service';
import {UploadService} from 'src/app/_services/upload.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {CommonModule} from "@angular/common";
import {TypeaheadComponent} from "../../../typeahead/_components/typeahead.component";
@ -44,13 +45,11 @@ import {SentenceCasePipe} from "../../../_pipes/sentence-case.pipe";
import {MangaFormatPipe} from "../../../_pipes/manga-format.pipe";
import {DefaultDatePipe} from "../../../_pipes/default-date.pipe";
import {TimeAgoPipe} from "../../../_pipes/time-ago.pipe";
import {TagBadgeComponent} from "../../../shared/tag-badge/tag-badge.component";
import {PublicationStatusPipe} from "../../../_pipes/publication-status.pipe";
import {BytesPipe} from "../../../_pipes/bytes.pipe";
import {ImageComponent} from "../../../shared/image/image.component";
import {DefaultValuePipe} from "../../../_pipes/default-value.pipe";
import {translate, TranslocoModule} from "@jsverse/transloco";
import {TranslocoDatePipe} from "@jsverse/transloco-locale";
import {UtcToLocalTimePipe} from "../../../_pipes/utc-to-local-time.pipe";
import {EditListComponent} from "../../../shared/edit-list/edit-list.component";
import {AccountService} from "../../../_services/account.service";
@ -61,7 +60,6 @@ import {SettingButtonComponent} from "../../../settings/_components/setting-butt
import {ActionService} from "../../../_services/action.service";
import {DownloadService} from "../../../shared/_services/download.service";
import {SettingItemComponent} from "../../../settings/_components/setting-item/setting-item.component";
import {ReadTimePipe} from "../../../_pipes/read-time.pipe";
import {LicenseService} from "../../../_services/license.service";
enum TabID {
@ -88,7 +86,6 @@ const blackList = [Action.Edit, Action.Info, Action.IncognitoRead, Action.Read,
@Component({
selector: 'app-edit-series-modal',
standalone: true,
imports: [
ReactiveFormsModule,
NgbNav,
@ -110,7 +107,6 @@ const blackList = [Action.Edit, Action.Info, Action.IncognitoRead, Action.Read,
NgbNavOutlet,
DefaultValuePipe,
TranslocoModule,
TranslocoDatePipe,
UtcToLocalTimePipe,
EditListComponent,
SettingButtonComponent,

View File

@ -17,19 +17,18 @@ import {CardActionablesComponent} from "../../_single-module/card-actionables/ca
import {KEY_CODES} from "../../shared/_services/utility.service";
@Component({
selector: 'app-bulk-operations',
standalone: true,
imports: [
AsyncPipe,
CardActionablesComponent,
TranslocoModule,
NgbTooltip,
NgStyle,
DecimalPipe
],
templateUrl: './bulk-operations.component.html',
styleUrls: ['./bulk-operations.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-bulk-operations',
imports: [
AsyncPipe,
CardActionablesComponent,
TranslocoModule,
NgbTooltip,
NgStyle,
DecimalPipe
],
templateUrl: './bulk-operations.component.html',
styleUrls: ['./bulk-operations.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class BulkOperationsComponent implements OnInit {

View File

@ -3,7 +3,8 @@ import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
ContentChild, DestroyRef,
ContentChild,
DestroyRef,
ElementRef,
EventEmitter,
HostListener,
@ -12,7 +13,9 @@ import {
Input,
OnChanges,
OnInit,
Output, SimpleChange, SimpleChanges,
Output,
SimpleChange,
SimpleChanges,
TemplateRef,
TrackByFunction,
ViewChild
@ -29,9 +32,6 @@ import {FilterEvent, FilterItem, SortField} from 'src/app/_models/metadata/serie
import {ActionItem} from 'src/app/_services/action-factory.service';
import {JumpbarService} from 'src/app/_services/jumpbar.service';
import {LoadingComponent} from "../../shared/loading/loading.component";
import {NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
import {MetadataFilterComponent} from "../../metadata-filter/metadata-filter.component";
import {TranslocoDirective} from "@jsverse/transloco";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.component";
@ -45,12 +45,11 @@ const ANIMATION_TIME_MS = 0;
@Component({
selector: 'app-card-detail-layout',
standalone: true,
imports: [LoadingComponent, VirtualScrollerModule, CardActionablesComponent, NgbTooltip, MetadataFilterComponent,
imports: [LoadingComponent, VirtualScrollerModule, CardActionablesComponent, MetadataFilterComponent,
TranslocoDirective, NgTemplateOutlet, NgClass, NgForOf],
templateUrl: './card-detail-layout.component.html',
styleUrls: ['./card-detail-layout.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CardDetailLayoutComponent implements OnInit, OnChanges {

View File

@ -1,41 +1,42 @@
import {
ChangeDetectionStrategy,
ChangeDetectorRef,
Component, ContentChild, DestroyRef,
Component,
ContentChild,
DestroyRef,
EventEmitter,
HostListener,
inject,
Input,
OnInit,
Output, TemplateRef
Output,
TemplateRef
} from '@angular/core';
import { Observable } from 'rxjs';
import { filter, map } from 'rxjs/operators';
import { DownloadEvent, DownloadService } from 'src/app/shared/_services/download.service';
import { UtilityService } from 'src/app/shared/_services/utility.service';
import { Chapter } from 'src/app/_models/chapter';
import { UserCollection } from 'src/app/_models/collection-tag';
import { UserProgressUpdateEvent } from 'src/app/_models/events/user-progress-update-event';
import { MangaFormat } from 'src/app/_models/manga-format';
import { PageBookmark } from 'src/app/_models/readers/page-bookmark';
import { RecentlyAddedItem } from 'src/app/_models/recently-added-item';
import { Series } from 'src/app/_models/series';
import { User } from 'src/app/_models/user';
import { Volume } from 'src/app/_models/volume';
import { AccountService } from 'src/app/_services/account.service';
import { Action, ActionFactoryService, ActionItem } from 'src/app/_services/action-factory.service';
import { ImageService } from 'src/app/_services/image.service';
import { LibraryService } from 'src/app/_services/library.service';
import { EVENTS, MessageHubService } from 'src/app/_services/message-hub.service';
import { ScrollService } from 'src/app/_services/scroll.service';
import { BulkSelectionService } from '../bulk-selection.service';
import {Observable} from 'rxjs';
import {filter, map} from 'rxjs/operators';
import {DownloadEvent, DownloadService} from 'src/app/shared/_services/download.service';
import {UtilityService} from 'src/app/shared/_services/utility.service';
import {Chapter} from 'src/app/_models/chapter';
import {UserCollection} from 'src/app/_models/collection-tag';
import {UserProgressUpdateEvent} from 'src/app/_models/events/user-progress-update-event';
import {MangaFormat} from 'src/app/_models/manga-format';
import {PageBookmark} from 'src/app/_models/readers/page-bookmark';
import {RecentlyAddedItem} from 'src/app/_models/recently-added-item';
import {Series} from 'src/app/_models/series';
import {User} from 'src/app/_models/user';
import {Volume} from 'src/app/_models/volume';
import {AccountService} from 'src/app/_services/account.service';
import {Action, ActionFactoryService, ActionItem} from 'src/app/_services/action-factory.service';
import {ImageService} from 'src/app/_services/image.service';
import {LibraryService} from 'src/app/_services/library.service';
import {EVENTS, MessageHubService} from 'src/app/_services/message-hub.service';
import {ScrollService} from 'src/app/_services/scroll.service';
import {BulkSelectionService} from '../bulk-selection.service';
import {takeUntilDestroyed} from "@angular/core/rxjs-interop";
import {ImageComponent} from "../../shared/image/image.component";
import {NgbProgressbar, NgbTooltip} from "@ng-bootstrap/ng-bootstrap";
import {DownloadIndicatorComponent} from "../download-indicator/download-indicator.component";
import {FormsModule} from "@angular/forms";
import {MangaFormatPipe} from "../../_pipes/manga-format.pipe";
import {MangaFormatIconPipe} from "../../_pipes/manga-format-icon.pipe";
import {SentenceCasePipe} from "../../_pipes/sentence-case.pipe";
import {DecimalPipe, NgTemplateOutlet} from "@angular/common";
import {RouterLink, RouterLinkActive} from "@angular/router";
@ -43,7 +44,6 @@ import {TranslocoModule} from "@jsverse/transloco";
import {CardActionablesComponent} from "../../_single-module/card-actionables/card-actionables.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 {PromotedIconComponent} from "../../shared/_components/promoted-icon/promoted-icon.component";
import {SeriesFormatComponent} from "../../shared/series-format/series-format.component";
import {BrowsePerson} from "../../_models/person/browse-person";
@ -53,20 +53,16 @@ export type CardEntity = Series | Volume | Chapter | UserCollection | PageBookma
@Component({
selector: 'app-card-item',
standalone: true,
imports: [
ImageComponent,
NgbProgressbar,
DownloadIndicatorComponent,
FormsModule,
NgbTooltip,
MangaFormatPipe,
MangaFormatIconPipe,
CardActionablesComponent,
SentenceCasePipe,
RouterLink,
TranslocoModule,
SafeHtmlPipe,
RouterLinkActive,
PromotedIconComponent,
SeriesFormatComponent,

View File

@ -38,23 +38,22 @@ import {ActionService} from "../../_services/action.service";
import {MangaFormat} from "../../_models/manga-format";
@Component({
selector: 'app-chapter-card',
standalone: true,
imports: [
NgbTooltip,
NgbProgressbar,
DecimalPipe,
ImageComponent,
DownloadIndicatorComponent,
FormsModule,
EntityTitleComponent,
CardActionablesComponent,
RouterLink,
TranslocoDirective
],
templateUrl: './chapter-card.component.html',
styleUrl: './chapter-card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-chapter-card',
imports: [
NgbTooltip,
NgbProgressbar,
DecimalPipe,
ImageComponent,
DownloadIndicatorComponent,
FormsModule,
EntityTitleComponent,
CardActionablesComponent,
RouterLink,
TranslocoDirective
],
templateUrl: './chapter-card.component.html',
styleUrl: './chapter-card.component.scss',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ChapterCardComponent implements OnInit {
private readonly destroyRef = inject(DestroyRef);

View File

@ -22,18 +22,17 @@ import {ImageComponent} from "../../shared/image/image.component";
import {translate, TranslocoModule} from "@jsverse/transloco";
@Component({
selector: 'app-cover-image-chooser',
standalone: true,
imports: [
ReactiveFormsModule,
NgxFileDropModule,
ImageComponent,
TranslocoModule,
NgClass
],
templateUrl: './cover-image-chooser.component.html',
styleUrls: ['./cover-image-chooser.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
selector: 'app-cover-image-chooser',
imports: [
ReactiveFormsModule,
NgxFileDropModule,
ImageComponent,
TranslocoModule,
NgClass
],
templateUrl: './cover-image-chooser.component.html',
styleUrls: ['./cover-image-chooser.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CoverImageChooserComponent implements OnInit {

Some files were not shown because too many files have changed in this diff Show More