mirror of
https://github.com/Kareadita/Kavita.git
synced 2025-05-24 00:52:23 -04:00
Angular 19 + Even more bugfixes (#3675)
This commit is contained in:
parent
535165c445
commit
cc3ae7f472
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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();
|
||||
|
@ -8,6 +8,9 @@ indent_size = 4
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.json]
|
||||
indent_size = 2
|
||||
|
||||
[*.html]
|
||||
indent_size = 2
|
||||
|
||||
|
@ -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
4782
UI/Web/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -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",
|
||||
|
@ -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%);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
import {Pipe, PipeTransform} from '@angular/core';
|
||||
import {EncodeFormat} from "../admin/_models/encode-format";
|
||||
|
||||
@Pipe({
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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{
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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">
|
||||
|
@ -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();
|
||||
})
|
||||
|
@ -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',
|
||||
|
@ -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>();
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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>
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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"> {{t('invite')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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',
|
||||
|
@ -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
|
||||
|
@ -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> = [];
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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"> {{isFullscreen ? t('exit') : t('enter')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -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 {
|
||||
/**
|
||||
|
@ -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>
|
||||
|
@ -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('/');
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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,
|
||||
],
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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 {
|
||||
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user