Release Shakeout Day 2 (#1594)

* Fixed a bad color on the PWA titlebar

* Added more unit tests, cleaned up some dead code, and made it so when age restriction is Not Applicable, the Unknowns field disables

* Don't show an empty menu when user has no permissions

* Fixed deleting a library with relation causing library deleting to fail

* Consolidated some includes code into one method for Series Repo

* Small fixes
This commit is contained in:
Joe Milazzo 2022-10-19 17:48:52 -07:00 committed by GitHub
parent ef662d3235
commit 26d32cbf28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 252 additions and 100 deletions

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Data.Common; using System.Data.Common;
using System.IO.Abstractions.TestingHelpers; using System.IO.Abstractions.TestingHelpers;
using System.Linq; using System.Linq;
@ -125,7 +126,7 @@ public class SeriesServiceTests
return fileSystem; return fileSystem;
} }
private static UpdateRelatedSeriesDto InstantiateRelationsDto(Series series) private static UpdateRelatedSeriesDto CreateRelationsDto(Series series)
{ {
return new UpdateRelatedSeriesDto() return new UpdateRelatedSeriesDto()
{ {
@ -1178,7 +1179,7 @@ public class SeriesServiceTests
var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related); var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related);
// Add relations // Add relations
var addRelationDto = InstantiateRelationsDto(series1); var addRelationDto = CreateRelationsDto(series1);
addRelationDto.Adaptations.Add(2); addRelationDto.Adaptations.Add(2);
addRelationDto.Sequels.Add(3); addRelationDto.Sequels.Add(3);
await _seriesService.UpdateRelatedSeries(addRelationDto); await _seriesService.UpdateRelatedSeries(addRelationDto);
@ -1225,7 +1226,7 @@ public class SeriesServiceTests
var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related); var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related);
// Add relations // Add relations
var addRelationDto = InstantiateRelationsDto(series1); var addRelationDto = CreateRelationsDto(series1);
addRelationDto.Adaptations.Add(2); addRelationDto.Adaptations.Add(2);
addRelationDto.Sequels.Add(3); addRelationDto.Sequels.Add(3);
await _seriesService.UpdateRelatedSeries(addRelationDto); await _seriesService.UpdateRelatedSeries(addRelationDto);
@ -1233,7 +1234,7 @@ public class SeriesServiceTests
Assert.Equal(3, series1.Relations.Single(s => s.TargetSeriesId == 3).TargetSeriesId); Assert.Equal(3, series1.Relations.Single(s => s.TargetSeriesId == 3).TargetSeriesId);
// Remove relations // Remove relations
var removeRelationDto = InstantiateRelationsDto(series1); var removeRelationDto = CreateRelationsDto(series1);
await _seriesService.UpdateRelatedSeries(removeRelationDto); await _seriesService.UpdateRelatedSeries(removeRelationDto);
Assert.Empty(series1.Relations.Where(s => s.TargetSeriesId == 1)); Assert.Empty(series1.Relations.Where(s => s.TargetSeriesId == 1));
Assert.Empty(series1.Relations.Where(s => s.TargetSeriesId == 2)); Assert.Empty(series1.Relations.Where(s => s.TargetSeriesId == 2));
@ -1284,7 +1285,7 @@ public class SeriesServiceTests
series1.Relations.Add(relation); series1.Relations.Add(relation);
// Create a new dto with the previous relation as well // Create a new dto with the previous relation as well
var relationDto = InstantiateRelationsDto(series1); var relationDto = CreateRelationsDto(series1);
relationDto.Adaptations.Add(2); relationDto.Adaptations.Add(2);
await _seriesService.UpdateRelatedSeries(relationDto); await _seriesService.UpdateRelatedSeries(relationDto);
@ -1339,7 +1340,7 @@ public class SeriesServiceTests
await _context.SaveChangesAsync(); await _context.SaveChangesAsync();
var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related); var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related);
// Add relations // Add relations
var addRelationDto = InstantiateRelationsDto(series1); var addRelationDto = CreateRelationsDto(series1);
addRelationDto.Editions.Add(2); addRelationDto.Editions.Add(2);
addRelationDto.Prequels.Add(3); addRelationDto.Prequels.Add(3);
addRelationDto.Sequels.Add(4); addRelationDto.Sequels.Add(4);
@ -1353,5 +1354,172 @@ public class SeriesServiceTests
Assert.NotEmpty(_seriesService.GetRelatedSeries(1, 5).Result.Parent); Assert.NotEmpty(_seriesService.GetRelatedSeries(1, 5).Result.Parent);
} }
[Fact]
public async Task SeriesRelation_ShouldAllowDeleteOnLibrary()
{
await ResetDb();
_context.Library.Add(new Library()
{
AppUsers = new List<AppUser>()
{
new AppUser()
{
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
new Series()
{
Name = "Test Series",
Volumes = new List<Volume>(){}
},
new Series()
{
Name = "Test Series Prequels",
Volumes = new List<Volume>(){}
},
new Series()
{
Name = "Test Series Sequels",
Volumes = new List<Volume>(){}
}
}
});
await _context.SaveChangesAsync();
var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related);
// Add relations
var addRelationDto = CreateRelationsDto(series1);
addRelationDto.Adaptations.Add(2);
addRelationDto.Sequels.Add(3);
await _seriesService.UpdateRelatedSeries(addRelationDto);
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1);
_unitOfWork.LibraryRepository.Delete(library);
try
{
await _unitOfWork.CommitAsync();
}
catch (Exception)
{
Assert.False(true);
}
Assert.Null(await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1));
}
[Fact]
public async Task SeriesRelation_ShouldAllowDeleteOnLibrary_WhenSeriesCrossLibraries()
{
await ResetDb();
_context.Library.Add(new Library()
{
AppUsers = new List<AppUser>()
{
new AppUser()
{
UserName = "majora2007"
}
},
Name = "Test LIb",
Type = LibraryType.Book,
Series = new List<Series>()
{
new Series()
{
Name = "Test Series",
Volumes = new List<Volume>()
{
new Volume()
{
Chapters = new List<Chapter>()
{
new Chapter()
{
Files = new List<MangaFile>()
{
new MangaFile()
{
Pages = 1,
FilePath = "fake file"
}
}
}
}
}
}
},
new Series()
{
Name = "Test Series Prequels",
Volumes = new List<Volume>(){}
},
new Series()
{
Name = "Test Series Sequels",
Volumes = new List<Volume>(){}
}
}
});
_context.Library.Add(new Library()
{
AppUsers = new List<AppUser>()
{
new AppUser()
{
UserName = "majora2007"
}
},
Name = "Test LIb 2",
Type = LibraryType.Book,
Series = new List<Series>()
{
new Series()
{
Name = "Test Series 2",
Volumes = new List<Volume>(){}
},
new Series()
{
Name = "Test Series Prequels 2",
Volumes = new List<Volume>(){}
},
new Series()
{
Name = "Test Series Sequels 2",
Volumes = new List<Volume>(){}
}
}
});
await _context.SaveChangesAsync();
var series1 = await _unitOfWork.SeriesRepository.GetSeriesByIdAsync(1, SeriesIncludes.Related);
// Add relations
var addRelationDto = CreateRelationsDto(series1);
addRelationDto.Adaptations.Add(4); // cross library link
await _seriesService.UpdateRelatedSeries(addRelationDto);
var library = await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1, LibraryIncludes.Series);
_unitOfWork.LibraryRepository.Delete(library);
try
{
await _unitOfWork.CommitAsync();
}
catch (Exception)
{
Assert.False(true);
}
Assert.Null(await _unitOfWork.LibraryRepository.GetLibraryForIdAsync(1));
}
#endregion #endregion
} }

View File

@ -248,19 +248,24 @@ public class LibraryController : BaseApiController
if (TaskScheduler.HasScanTaskRunningForLibrary(libraryId)) if (TaskScheduler.HasScanTaskRunningForLibrary(libraryId))
{ {
// TODO: Figure out how to cancel a job // TODO: Figure out how to cancel a job
_logger.LogInformation("User is attempting to delete a library while a scan is in progress"); _logger.LogInformation("User is attempting to delete a library while a scan is in progress");
return BadRequest( return BadRequest(
"You cannot delete a library while a scan is in progress. Please wait for scan to continue then try to delete"); "You cannot delete a library while a scan is in progress. Please wait for scan to continue then try to delete");
} }
// Due to a bad schema that I can't figure out how to fix, we need to erase all RelatedSeries before we delete the library // Due to a bad schema that I can't figure out how to fix, we need to erase all RelatedSeries before we delete the library
foreach (var s in await _unitOfWork.SeriesRepository.GetSeriesForLibraryIdAsync(library.Id)) // Aka SeriesRelation has an invalid foreign key
foreach (var s in await _unitOfWork.SeriesRepository.GetSeriesForLibraryIdAsync(library.Id,
SeriesIncludes.Related))
{ {
s.Relations = new List<SeriesRelation>(); s.Relations = new List<SeriesRelation>();
_unitOfWork.SeriesRepository.Update(s); _unitOfWork.SeriesRepository.Update(s);
} }
await _unitOfWork.CommitAsync();
_unitOfWork.LibraryRepository.Delete(library); _unitOfWork.LibraryRepository.Delete(library);
await _unitOfWork.CommitAsync(); await _unitOfWork.CommitAsync();
if (chapterIds.Any()) if (chapterIds.Any())

View File

@ -165,7 +165,7 @@ namespace API.Data.Migrations
b.HasIndex("AppUserId"); b.HasIndex("AppUserId");
b.ToTable("AppUserBookmark"); b.ToTable("AppUserBookmark", (string)null);
}); });
modelBuilder.Entity("API.Entities.AppUserPreferences", b => modelBuilder.Entity("API.Entities.AppUserPreferences", b =>
@ -256,7 +256,7 @@ namespace API.Data.Migrations
b.HasIndex("ThemeId"); b.HasIndex("ThemeId");
b.ToTable("AppUserPreferences"); b.ToTable("AppUserPreferences", (string)null);
}); });
modelBuilder.Entity("API.Entities.AppUserProgress", b => modelBuilder.Entity("API.Entities.AppUserProgress", b =>
@ -295,7 +295,7 @@ namespace API.Data.Migrations
b.HasIndex("SeriesId"); b.HasIndex("SeriesId");
b.ToTable("AppUserProgresses"); b.ToTable("AppUserProgresses", (string)null);
}); });
modelBuilder.Entity("API.Entities.AppUserRating", b => modelBuilder.Entity("API.Entities.AppUserRating", b =>
@ -322,7 +322,7 @@ namespace API.Data.Migrations
b.HasIndex("SeriesId"); b.HasIndex("SeriesId");
b.ToTable("AppUserRating"); b.ToTable("AppUserRating", (string)null);
}); });
modelBuilder.Entity("API.Entities.AppUserRole", b => modelBuilder.Entity("API.Entities.AppUserRole", b =>
@ -413,7 +413,7 @@ namespace API.Data.Migrations
b.HasIndex("VolumeId"); b.HasIndex("VolumeId");
b.ToTable("Chapter"); b.ToTable("Chapter", (string)null);
}); });
modelBuilder.Entity("API.Entities.CollectionTag", b => modelBuilder.Entity("API.Entities.CollectionTag", b =>
@ -448,7 +448,7 @@ namespace API.Data.Migrations
b.HasIndex("Id", "Promoted") b.HasIndex("Id", "Promoted")
.IsUnique(); .IsUnique();
b.ToTable("CollectionTag"); b.ToTable("CollectionTag", (string)null);
}); });
modelBuilder.Entity("API.Entities.Device", b => modelBuilder.Entity("API.Entities.Device", b =>
@ -485,7 +485,7 @@ namespace API.Data.Migrations
b.HasIndex("AppUserId"); b.HasIndex("AppUserId");
b.ToTable("Device"); b.ToTable("Device", (string)null);
}); });
modelBuilder.Entity("API.Entities.FolderPath", b => modelBuilder.Entity("API.Entities.FolderPath", b =>
@ -507,7 +507,7 @@ namespace API.Data.Migrations
b.HasIndex("LibraryId"); b.HasIndex("LibraryId");
b.ToTable("FolderPath"); b.ToTable("FolderPath", (string)null);
}); });
modelBuilder.Entity("API.Entities.Genre", b => modelBuilder.Entity("API.Entities.Genre", b =>
@ -530,7 +530,7 @@ namespace API.Data.Migrations
b.HasIndex("NormalizedTitle", "ExternalTag") b.HasIndex("NormalizedTitle", "ExternalTag")
.IsUnique(); .IsUnique();
b.ToTable("Genre"); b.ToTable("Genre", (string)null);
}); });
modelBuilder.Entity("API.Entities.Library", b => modelBuilder.Entity("API.Entities.Library", b =>
@ -559,7 +559,7 @@ namespace API.Data.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Library"); b.ToTable("Library", (string)null);
}); });
modelBuilder.Entity("API.Entities.MangaFile", b => modelBuilder.Entity("API.Entities.MangaFile", b =>
@ -593,7 +593,7 @@ namespace API.Data.Migrations
b.HasIndex("ChapterId"); b.HasIndex("ChapterId");
b.ToTable("MangaFile"); b.ToTable("MangaFile", (string)null);
}); });
modelBuilder.Entity("API.Entities.Metadata.SeriesMetadata", b => modelBuilder.Entity("API.Entities.Metadata.SeriesMetadata", b =>
@ -689,7 +689,7 @@ namespace API.Data.Migrations
b.HasIndex("Id", "SeriesId") b.HasIndex("Id", "SeriesId")
.IsUnique(); .IsUnique();
b.ToTable("SeriesMetadata"); b.ToTable("SeriesMetadata", (string)null);
}); });
modelBuilder.Entity("API.Entities.Metadata.SeriesRelation", b => modelBuilder.Entity("API.Entities.Metadata.SeriesRelation", b =>
@ -713,7 +713,7 @@ namespace API.Data.Migrations
b.HasIndex("TargetSeriesId"); b.HasIndex("TargetSeriesId");
b.ToTable("SeriesRelation"); b.ToTable("SeriesRelation", (string)null);
}); });
modelBuilder.Entity("API.Entities.Person", b => modelBuilder.Entity("API.Entities.Person", b =>
@ -733,7 +733,7 @@ namespace API.Data.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("Person"); b.ToTable("Person", (string)null);
}); });
modelBuilder.Entity("API.Entities.ReadingList", b => modelBuilder.Entity("API.Entities.ReadingList", b =>
@ -776,7 +776,7 @@ namespace API.Data.Migrations
b.HasIndex("AppUserId"); b.HasIndex("AppUserId");
b.ToTable("ReadingList"); b.ToTable("ReadingList", (string)null);
}); });
modelBuilder.Entity("API.Entities.ReadingListItem", b => modelBuilder.Entity("API.Entities.ReadingListItem", b =>
@ -810,7 +810,7 @@ namespace API.Data.Migrations
b.HasIndex("VolumeId"); b.HasIndex("VolumeId");
b.ToTable("ReadingListItem"); b.ToTable("ReadingListItem", (string)null);
}); });
modelBuilder.Entity("API.Entities.Series", b => modelBuilder.Entity("API.Entities.Series", b =>
@ -897,7 +897,7 @@ namespace API.Data.Migrations
b.HasIndex("LibraryId"); b.HasIndex("LibraryId");
b.ToTable("Series"); b.ToTable("Series", (string)null);
}); });
modelBuilder.Entity("API.Entities.ServerSetting", b => modelBuilder.Entity("API.Entities.ServerSetting", b =>
@ -914,7 +914,7 @@ namespace API.Data.Migrations
b.HasKey("Key"); b.HasKey("Key");
b.ToTable("ServerSetting"); b.ToTable("ServerSetting", (string)null);
}); });
modelBuilder.Entity("API.Entities.SiteTheme", b => modelBuilder.Entity("API.Entities.SiteTheme", b =>
@ -946,7 +946,7 @@ namespace API.Data.Migrations
b.HasKey("Id"); b.HasKey("Id");
b.ToTable("SiteTheme"); b.ToTable("SiteTheme", (string)null);
}); });
modelBuilder.Entity("API.Entities.Tag", b => modelBuilder.Entity("API.Entities.Tag", b =>
@ -969,7 +969,7 @@ namespace API.Data.Migrations
b.HasIndex("NormalizedTitle", "ExternalTag") b.HasIndex("NormalizedTitle", "ExternalTag")
.IsUnique(); .IsUnique();
b.ToTable("Tag"); b.ToTable("Tag", (string)null);
}); });
modelBuilder.Entity("API.Entities.Volume", b => modelBuilder.Entity("API.Entities.Volume", b =>
@ -1015,7 +1015,7 @@ namespace API.Data.Migrations
b.HasIndex("SeriesId"); b.HasIndex("SeriesId");
b.ToTable("Volume"); b.ToTable("Volume", (string)null);
}); });
modelBuilder.Entity("AppUserLibrary", b => modelBuilder.Entity("AppUserLibrary", b =>
@ -1030,7 +1030,7 @@ namespace API.Data.Migrations
b.HasIndex("LibrariesId"); b.HasIndex("LibrariesId");
b.ToTable("AppUserLibrary"); b.ToTable("AppUserLibrary", (string)null);
}); });
modelBuilder.Entity("ChapterGenre", b => modelBuilder.Entity("ChapterGenre", b =>
@ -1045,7 +1045,7 @@ namespace API.Data.Migrations
b.HasIndex("GenresId"); b.HasIndex("GenresId");
b.ToTable("ChapterGenre"); b.ToTable("ChapterGenre", (string)null);
}); });
modelBuilder.Entity("ChapterPerson", b => modelBuilder.Entity("ChapterPerson", b =>
@ -1060,7 +1060,7 @@ namespace API.Data.Migrations
b.HasIndex("PeopleId"); b.HasIndex("PeopleId");
b.ToTable("ChapterPerson"); b.ToTable("ChapterPerson", (string)null);
}); });
modelBuilder.Entity("ChapterTag", b => modelBuilder.Entity("ChapterTag", b =>
@ -1075,7 +1075,7 @@ namespace API.Data.Migrations
b.HasIndex("TagsId"); b.HasIndex("TagsId");
b.ToTable("ChapterTag"); b.ToTable("ChapterTag", (string)null);
}); });
modelBuilder.Entity("CollectionTagSeriesMetadata", b => modelBuilder.Entity("CollectionTagSeriesMetadata", b =>
@ -1090,7 +1090,7 @@ namespace API.Data.Migrations
b.HasIndex("SeriesMetadatasId"); b.HasIndex("SeriesMetadatasId");
b.ToTable("CollectionTagSeriesMetadata"); b.ToTable("CollectionTagSeriesMetadata", (string)null);
}); });
modelBuilder.Entity("GenreSeriesMetadata", b => modelBuilder.Entity("GenreSeriesMetadata", b =>
@ -1105,7 +1105,7 @@ namespace API.Data.Migrations
b.HasIndex("SeriesMetadatasId"); b.HasIndex("SeriesMetadatasId");
b.ToTable("GenreSeriesMetadata"); b.ToTable("GenreSeriesMetadata", (string)null);
}); });
modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b => modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim<int>", b =>
@ -1204,7 +1204,7 @@ namespace API.Data.Migrations
b.HasIndex("SeriesMetadatasId"); b.HasIndex("SeriesMetadatasId");
b.ToTable("PersonSeriesMetadata"); b.ToTable("PersonSeriesMetadata", (string)null);
}); });
modelBuilder.Entity("SeriesMetadataTag", b => modelBuilder.Entity("SeriesMetadataTag", b =>
@ -1219,7 +1219,7 @@ namespace API.Data.Migrations
b.HasIndex("TagsId"); b.HasIndex("TagsId");
b.ToTable("SeriesMetadataTag"); b.ToTable("SeriesMetadataTag", (string)null);
}); });
modelBuilder.Entity("API.Entities.AppUserBookmark", b => modelBuilder.Entity("API.Entities.AppUserBookmark", b =>

View File

@ -33,7 +33,7 @@ public interface ILibraryRepository
void Delete(Library library); void Delete(Library library);
Task<IEnumerable<LibraryDto>> GetLibraryDtosAsync(); Task<IEnumerable<LibraryDto>> GetLibraryDtosAsync();
Task<bool> LibraryExists(string libraryName); Task<bool> LibraryExists(string libraryName);
Task<Library> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes); Task<Library> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes = LibraryIncludes.None);
Task<IEnumerable<LibraryDto>> GetLibraryDtosForUsernameAsync(string userName); Task<IEnumerable<LibraryDto>> GetLibraryDtosForUsernameAsync(string userName);
Task<IEnumerable<Library>> GetLibrariesAsync(LibraryIncludes includes = LibraryIncludes.None); Task<IEnumerable<Library>> GetLibrariesAsync(LibraryIncludes includes = LibraryIncludes.None);
Task<bool> DeleteLibrary(int libraryId); Task<bool> DeleteLibrary(int libraryId);
@ -203,14 +203,14 @@ public class LibraryRepository : ILibraryRepository
.ToListAsync(); .ToListAsync();
} }
public async Task<Library> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes) public async Task<Library> GetLibraryForIdAsync(int libraryId, LibraryIncludes includes = LibraryIncludes.None)
{ {
var query = _context.Library var query = _context.Library
.Where(x => x.Id == libraryId); .Where(x => x.Id == libraryId);
query = AddIncludesToQuery(query, includes); query = AddIncludesToQuery(query, includes);
return await query.SingleAsync(); return await query.SingleOrDefaultAsync();
} }
private static IQueryable<Library> AddIncludesToQuery(IQueryable<Library> query, LibraryIncludes includeFlags) private static IQueryable<Library> AddIncludesToQuery(IQueryable<Library> query, LibraryIncludes includeFlags)

View File

@ -63,9 +63,8 @@ public interface ISeriesRepository
/// <param name="searchQuery"></param> /// <param name="searchQuery"></param>
/// <returns></returns> /// <returns></returns>
Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, int[] libraryIds, string searchQuery); Task<SearchResultGroupDto> SearchSeries(int userId, bool isAdmin, int[] libraryIds, string searchQuery);
Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId); Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId, SeriesIncludes includes = SeriesIncludes.None);
Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId, int userId); Task<SeriesDto> GetSeriesDtoByIdAsync(int seriesId, int userId);
Task<bool> DeleteSeriesAsync(int seriesId);
Task<Series> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata); Task<Series> GetSeriesByIdAsync(int seriesId, SeriesIncludes includes = SeriesIncludes.Volumes | SeriesIncludes.Metadata);
Task<IList<Series>> GetSeriesByIdsAsync(IList<int> seriesIds); Task<IList<Series>> GetSeriesByIdsAsync(IList<int> seriesIds);
Task<int[]> GetChapterIdsForSeriesAsync(IList<int> seriesIds); Task<int[]> GetChapterIdsForSeriesAsync(IList<int> seriesIds);
@ -160,12 +159,14 @@ public class SeriesRepository : ISeriesRepository
} }
public async Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId) public async Task<IEnumerable<Series>> GetSeriesForLibraryIdAsync(int libraryId, SeriesIncludes includes = SeriesIncludes.None)
{ {
return await _context.Series var query = _context.Series
.Where(s => s.LibraryId == libraryId) .Where(s => s.LibraryId == libraryId);
.OrderBy(s => s.SortName)
.ToListAsync(); query = AddIncludesToQuery(query, includes);
return await query.OrderBy(s => s.SortName).ToListAsync();
} }
/// <summary> /// <summary>
@ -418,15 +419,6 @@ public class SeriesRepository : ISeriesRepository
return seriesList[0]; return seriesList[0];
} }
public async Task<bool> DeleteSeriesAsync(int seriesId)
{
var series = await _context.Series.Where(s => s.Id == seriesId).SingleOrDefaultAsync();
if (series != null) _context.Series.Remove(series);
return await _context.SaveChangesAsync() > 0;
}
/// <summary> /// <summary>
/// Returns Volumes, Metadata (Incl Genres and People), and Collection Tags /// Returns Volumes, Metadata (Incl Genres and People), and Collection Tags
/// </summary> /// </summary>
@ -439,29 +431,7 @@ public class SeriesRepository : ISeriesRepository
.Where(s => s.Id == seriesId) .Where(s => s.Id == seriesId)
.AsSplitQuery(); .AsSplitQuery();
if (includes.HasFlag(SeriesIncludes.Volumes)) query = AddIncludesToQuery(query, includes);
{
query = query.Include(s => s.Volumes);
}
if (includes.HasFlag(SeriesIncludes.Related))
{
query = query.Include(s => s.Relations)
.ThenInclude(r => r.TargetSeries)
.Include(s => s.RelationOf);
}
if (includes.HasFlag(SeriesIncludes.Metadata))
{
query = query.Include(s => s.Metadata)
.ThenInclude(m => m.CollectionTags)
.Include(s => s.Metadata)
.ThenInclude(m => m.Genres)
.Include(s => s.Metadata)
.ThenInclude(m => m.People)
.Include(s => s.Metadata)
.ThenInclude(m => m.Tags);
}
return await query.SingleOrDefaultAsync(); return await query.SingleOrDefaultAsync();
} }
@ -1561,27 +1531,37 @@ public class SeriesRepository : ISeriesRepository
private static IQueryable<Series> AddIncludesToQuery(IQueryable<Series> query, SeriesIncludes includeFlags) private static IQueryable<Series> AddIncludesToQuery(IQueryable<Series> query, SeriesIncludes includeFlags)
{ {
// TODO: Move this to an Extension Method
if (includeFlags.HasFlag(SeriesIncludes.Library)) if (includeFlags.HasFlag(SeriesIncludes.Library))
{ {
query = query.Include(u => u.Library); query = query.Include(u => u.Library);
} }
if (includeFlags.HasFlag(SeriesIncludes.Volumes))
{
query = query.Include(s => s.Volumes);
}
if (includeFlags.HasFlag(SeriesIncludes.Related)) if (includeFlags.HasFlag(SeriesIncludes.Related))
{ {
query = query.Include(u => u.Relations); query = query.Include(s => s.Relations)
.ThenInclude(r => r.TargetSeries)
.Include(s => s.RelationOf);
} }
if (includeFlags.HasFlag(SeriesIncludes.Metadata)) if (includeFlags.HasFlag(SeriesIncludes.Metadata))
{ {
query = query.Include(u => u.Metadata); query = query.Include(s => s.Metadata)
} .ThenInclude(m => m.CollectionTags)
.Include(s => s.Metadata)
if (includeFlags.HasFlag(SeriesIncludes.Volumes)) .ThenInclude(m => m.Genres)
{ .Include(s => s.Metadata)
query = query.Include(u => u.Volumes); .ThenInclude(m => m.People)
.Include(s => s.Metadata)
.ThenInclude(m => m.Tags);
} }
return query; return query.AsSplitQuery();
} }
} }

View File

@ -27,16 +27,4 @@ public class Library : IEntityDate
public ICollection<FolderPath> Folders { get; set; } public ICollection<FolderPath> Folders { get; set; }
public ICollection<AppUser> AppUsers { get; set; } public ICollection<AppUser> AppUsers { get; set; }
public ICollection<Series> Series { get; set; } public ICollection<Series> Series { get; set; }
// Methods
/// <summary>
/// Has there been any modifications to the FolderPath's directory since the <see cref="FolderPath.LastScanned"/> date
/// </summary>
/// <returns></returns>
public bool AnyModificationsSinceLastScan()
{
// NOTE: I don't think we can do this due to NTFS
return Folders.All(folder => File.GetLastWriteTimeUtc(folder.Path) > folder.LastScanned);
}
} }

View File

@ -31,6 +31,12 @@ export class CardActionablesComponent implements OnInit {
if (!user) return; if (!user) return;
this.isAdmin = this.accountService.hasAdminRole(user); this.isAdmin = this.accountService.hasAdminRole(user);
this.canDownload = this.accountService.hasDownloadRole(user); this.canDownload = this.accountService.hasDownloadRole(user);
// We want to avoid an empty menu when user doesn't have access to anything
const validActions = this.actions.filter(a => a.children.length > 0 || a.dynamicList);
if (!this.isAdmin && validActions.filter(a => !a.requiresAdmin).length === 0) {
this.actions = [];
}
this.cdRef.markForCheck(); this.cdRef.markForCheck();
}); });
} }

View File

@ -56,6 +56,11 @@ export class RestrictionSelectorComponent implements OnInit, OnChanges {
ageRating: parseInt(e, 10), ageRating: parseInt(e, 10),
includeUnknowns: this.restrictionForm?.get('ageRestrictionIncludeUnknowns')?.value includeUnknowns: this.restrictionForm?.get('ageRestrictionIncludeUnknowns')?.value
}); });
if (parseInt(e, 10) === AgeRating.NotApplicable) {
this.restrictionForm!.get('ageRestrictionIncludeUnknowns')?.disable();
} else {
this.restrictionForm!.get('ageRestrictionIncludeUnknowns')?.enable();
}
}); });
this.restrictionForm.get('ageRestrictionIncludeUnknowns')?.valueChanges.subscribe(e => { this.restrictionForm.get('ageRestrictionIncludeUnknowns')?.valueChanges.subscribe(e => {

View File

@ -12,7 +12,7 @@
<link rel="shortcut icon" href="assets/icons/favicon.ico"> <link rel="shortcut icon" href="assets/icons/favicon.ico">
<meta name="msapplication-TileColor" content="#4ac694"> <meta name="msapplication-TileColor" content="#4ac694">
<meta name="msapplication-config" content="assets/icons/browserconfig.xml"> <meta name="msapplication-config" content="assets/icons/browserconfig.xml">
<meta name="theme-color" content="#ffffff"> <meta name="theme-color" content="#000000">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">