Want to Read List (#1392)
* Implemented a Want To Read list of series for all users, as a way to keep track of what you want to read. When canceling a bulk action, like Add to Reading list, the selected cards wont de-select. * Hooked up Remove from Want to Read * When making bulk selection, allow the user to click on anywhere on the card * Added no series messaging * Code cleanup
This commit is contained in:
parent
495c986000
commit
f130440bd0
36 changed files with 2209 additions and 48 deletions
1590
API/Data/Migrations/20220728193758_WantToReadList.Designer.cs
generated
Normal file
1590
API/Data/Migrations/20220728193758_WantToReadList.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load diff
45
API/Data/Migrations/20220728193758_WantToReadList.cs
Normal file
45
API/Data/Migrations/20220728193758_WantToReadList.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace API.Data.Migrations
|
||||
{
|
||||
public partial class WantToReadList : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<int>(
|
||||
name: "AppUserId",
|
||||
table: "Series",
|
||||
type: "INTEGER",
|
||||
nullable: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "IX_Series_AppUserId",
|
||||
table: "Series",
|
||||
column: "AppUserId");
|
||||
|
||||
migrationBuilder.AddForeignKey(
|
||||
name: "FK_Series_AspNetUsers_AppUserId",
|
||||
table: "Series",
|
||||
column: "AppUserId",
|
||||
principalTable: "AspNetUsers",
|
||||
principalColumn: "Id");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropForeignKey(
|
||||
name: "FK_Series_AspNetUsers_AppUserId",
|
||||
table: "Series");
|
||||
|
||||
migrationBuilder.DropIndex(
|
||||
name: "IX_Series_AppUserId",
|
||||
table: "Series");
|
||||
|
||||
migrationBuilder.DropColumn(
|
||||
name: "AppUserId",
|
||||
table: "Series");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -758,6 +758,9 @@ namespace API.Data.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int?>("AppUserId")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<int>("AvgHoursToRead")
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
|
|
@ -820,6 +823,8 @@ namespace API.Data.Migrations
|
|||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.HasIndex("AppUserId");
|
||||
|
||||
b.HasIndex("LibraryId");
|
||||
|
||||
b.ToTable("Series");
|
||||
|
|
@ -1339,6 +1344,10 @@ namespace API.Data.Migrations
|
|||
|
||||
modelBuilder.Entity("API.Entities.Series", b =>
|
||||
{
|
||||
b.HasOne("API.Entities.AppUser", null)
|
||||
.WithMany("WantToRead")
|
||||
.HasForeignKey("AppUserId");
|
||||
|
||||
b.HasOne("API.Entities.Library", "Library")
|
||||
.WithMany("Series")
|
||||
.HasForeignKey("LibraryId")
|
||||
|
|
@ -1533,6 +1542,8 @@ namespace API.Data.Migrations
|
|||
b.Navigation("UserPreferences");
|
||||
|
||||
b.Navigation("UserRoles");
|
||||
|
||||
b.Navigation("WantToRead");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("API.Entities.Chapter", b =>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,7 @@ public interface ISeriesRepository
|
|||
Task<PagedList<SeriesDto>> GetRediscover(int userId, int libraryId, UserParams userParams);
|
||||
Task<SeriesDto> GetSeriesForMangaFile(int mangaFileId, int userId);
|
||||
Task<SeriesDto> GetSeriesForChapter(int chapterId, int userId);
|
||||
Task<PagedList<SeriesDto>> GetWantToReadForUserAsync(int userId, UserParams userParams, FilterDto filter);
|
||||
}
|
||||
|
||||
public class SeriesRepository : ISeriesRepository
|
||||
|
|
@ -715,7 +716,6 @@ public class SeriesRepository : ISeriesRepository
|
|||
return await PagedList<SeriesDto>.CreateAsync(query, userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
|
||||
private async Task<IQueryable<Series>> CreateFilteredSearchQueryable(int userId, int libraryId, FilterDto filter)
|
||||
{
|
||||
var userLibraries = await GetUserLibraries(libraryId, userId);
|
||||
|
|
@ -778,6 +778,68 @@ public class SeriesRepository : ISeriesRepository
|
|||
return query;
|
||||
}
|
||||
|
||||
private async Task<IQueryable<Series>> CreateFilteredSearchQueryable(int userId, int libraryId, FilterDto filter, IQueryable<Series> sQuery)
|
||||
{
|
||||
var userLibraries = await GetUserLibraries(libraryId, userId);
|
||||
var formats = ExtractFilters(libraryId, userId, filter, ref userLibraries,
|
||||
out var allPeopleIds, out var hasPeopleFilter, out var hasGenresFilter,
|
||||
out var hasCollectionTagFilter, out var hasRatingFilter, out var hasProgressFilter,
|
||||
out var seriesIds, out var hasAgeRating, out var hasTagsFilter, out var hasLanguageFilter, out var hasPublicationFilter, out var hasSeriesNameFilter);
|
||||
|
||||
var query = sQuery
|
||||
.Where(s => userLibraries.Contains(s.LibraryId)
|
||||
&& formats.Contains(s.Format)
|
||||
&& (!hasGenresFilter || s.Metadata.Genres.Any(g => filter.Genres.Contains(g.Id)))
|
||||
&& (!hasPeopleFilter || s.Metadata.People.Any(p => allPeopleIds.Contains(p.Id)))
|
||||
&& (!hasCollectionTagFilter ||
|
||||
s.Metadata.CollectionTags.Any(t => filter.CollectionTags.Contains(t.Id)))
|
||||
&& (!hasRatingFilter || s.Ratings.Any(r => r.Rating >= filter.Rating && r.AppUserId == userId))
|
||||
&& (!hasProgressFilter || seriesIds.Contains(s.Id))
|
||||
&& (!hasAgeRating || filter.AgeRating.Contains(s.Metadata.AgeRating))
|
||||
&& (!hasTagsFilter || s.Metadata.Tags.Any(t => filter.Tags.Contains(t.Id)))
|
||||
&& (!hasLanguageFilter || filter.Languages.Contains(s.Metadata.Language))
|
||||
&& (!hasPublicationFilter || filter.PublicationStatus.Contains(s.Metadata.PublicationStatus)))
|
||||
.Where(s => !hasSeriesNameFilter ||
|
||||
EF.Functions.Like(s.Name, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.OriginalName, $"%{filter.SeriesNameQuery}%")
|
||||
|| EF.Functions.Like(s.LocalizedName, $"%{filter.SeriesNameQuery}%"))
|
||||
.AsNoTracking();
|
||||
|
||||
// If no sort options, default to using SortName
|
||||
filter.SortOptions ??= new SortOptions()
|
||||
{
|
||||
IsAscending = true,
|
||||
SortField = SortField.SortName
|
||||
};
|
||||
|
||||
if (filter.SortOptions.IsAscending)
|
||||
{
|
||||
query = filter.SortOptions.SortField switch
|
||||
{
|
||||
SortField.SortName => query.OrderBy(s => s.SortName),
|
||||
SortField.CreatedDate => query.OrderBy(s => s.Created),
|
||||
SortField.LastModifiedDate => query.OrderBy(s => s.LastModified),
|
||||
SortField.LastChapterAdded => query.OrderBy(s => s.LastChapterAdded),
|
||||
SortField.TimeToRead => query.OrderBy(s => s.AvgHoursToRead),
|
||||
_ => query
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
query = filter.SortOptions.SortField switch
|
||||
{
|
||||
SortField.SortName => query.OrderByDescending(s => s.SortName),
|
||||
SortField.CreatedDate => query.OrderByDescending(s => s.Created),
|
||||
SortField.LastModifiedDate => query.OrderByDescending(s => s.LastModified),
|
||||
SortField.LastChapterAdded => query.OrderByDescending(s => s.LastChapterAdded),
|
||||
SortField.TimeToRead => query.OrderByDescending(s => s.AvgHoursToRead),
|
||||
_ => query
|
||||
};
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
public async Task<SeriesMetadataDto> GetSeriesMetadata(int seriesId)
|
||||
{
|
||||
var metadataDto = await _context.SeriesMetadata
|
||||
|
|
@ -1074,6 +1136,21 @@ public class SeriesRepository : ISeriesRepository
|
|||
.SingleOrDefaultAsync();
|
||||
}
|
||||
|
||||
public async Task<PagedList<SeriesDto>> GetWantToReadForUserAsync(int userId, UserParams userParams, FilterDto filter)
|
||||
{
|
||||
var libraryIds = GetLibraryIdsForUser(userId);
|
||||
var query = _context.AppUser
|
||||
.Where(user => user.Id == userId)
|
||||
.SelectMany(u => u.WantToRead)
|
||||
.Where(s => libraryIds.Contains(s.LibraryId))
|
||||
.AsSplitQuery()
|
||||
.AsNoTracking();
|
||||
|
||||
var filteredQuery = await CreateFilteredSearchQueryable(userId, 0, filter, query);
|
||||
|
||||
return await PagedList<SeriesDto>.CreateAsync(filteredQuery.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider), userParams.PageNumber, userParams.PageSize);
|
||||
}
|
||||
|
||||
|
||||
public async Task<PagedList<SeriesDto>> GetHighlyRated(int userId, int libraryId, UserParams userParams)
|
||||
{
|
||||
|
|
@ -1238,7 +1315,6 @@ public class SeriesRepository : ISeriesRepository
|
|||
VolumeNumber = c.Volume.Number,
|
||||
ChapterTitle = c.Title
|
||||
})
|
||||
//.Take(maxRecords)
|
||||
.AsSplitQuery()
|
||||
.Where(c => c.Created >= withinLastWeek && libraryIds.Contains(c.LibraryId))
|
||||
.AsEnumerable();
|
||||
|
|
|
|||
|
|
@ -22,7 +22,8 @@ public enum AppUserIncludes
|
|||
Bookmarks = 4,
|
||||
ReadingLists = 8,
|
||||
Ratings = 16,
|
||||
UserPreferences = 32
|
||||
UserPreferences = 32,
|
||||
WantToRead = 64
|
||||
}
|
||||
|
||||
public interface IUserRepository
|
||||
|
|
@ -176,6 +177,11 @@ public class UserRepository : IUserRepository
|
|||
query = query.Include(u => u.UserPreferences);
|
||||
}
|
||||
|
||||
if (includeFlags.HasFlag(AppUserIncludes.WantToRead))
|
||||
{
|
||||
query = query.Include(u => u.WantToRead);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return query;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue