Reading List Polish (#1879)

* Use Reading Order to count epub pages rather than raw HTML files.

* Send email on background thread for initial invite flow.

* Reorder default writing style for new users so Horizontal is default

* Changed reading activity to use average hours read rather than events to bring more meaningful data.

* added ability to start reading incognito from the top of series detail, needs a bit of styling help though.

* Refactored extensions out into their own package, added new fields for reading list to cover total run, cbl import now takes those dates and overrides on import. Replaced many instances of numbers to be comma separated.

* Added ability to edit reading list run start and end year/month. Refactored some code for valid month/year into a helper method.

* Added a way to see the reading list's release years.

* Added some merged image code, but had to remove due to cover dimensions not fixed.

* tweaked style for accessibility mode on reading list items

* Tweaked css for non virtualized and virtualized containers

* Fixed release updates failing

* Commented out the merge code.

* Typo on words read per year

* Fixed unit tests

* Fixed virtualized scroll

* Cleanup CSS
This commit is contained in:
Joe Milazzo 2023-03-14 08:45:22 -05:00 committed by GitHub
parent 266f302823
commit fd6ee42f5f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
60 changed files with 2847 additions and 430 deletions

View file

@ -3,6 +3,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using API.Entities;
using API.Entities.Enums;
using API.Entities.Enums.UserPreferences;
using API.Entities.Interfaces;
using API.Entities.Metadata;
@ -86,10 +87,12 @@ public sealed class DataContext : IdentityDbContext<AppUser, AppRole, int,
builder.Entity<AppUserPreferences>()
.Property(b => b.BackgroundColor)
.HasDefaultValue("#000000");
builder.Entity<AppUserPreferences>()
.Property(b => b.GlobalPageLayoutMode)
.HasDefaultValue(PageLayoutMode.Cards);
builder.Entity<AppUserPreferences>()
.Property(b => b.BookReaderWritingStyle)
.HasDefaultValue(WritingStyle.Horizontal);
builder.Entity<Library>()

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,80 @@
using Microsoft.EntityFrameworkCore.Migrations;
#nullable disable
namespace API.Data.Migrations
{
/// <inheritdoc />
public partial class ReadingListDateRange : Migration
{
/// <inheritdoc />
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<int>(
name: "EndingMonth",
table: "ReadingList",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "EndingYear",
table: "ReadingList",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "StartingMonth",
table: "ReadingList",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<int>(
name: "StartingYear",
table: "ReadingList",
type: "INTEGER",
nullable: false,
defaultValue: 0);
migrationBuilder.AlterColumn<int>(
name: "BookReaderWritingStyle",
table: "AppUserPreferences",
type: "INTEGER",
nullable: false,
defaultValue: 0,
oldClrType: typeof(int),
oldType: "INTEGER");
}
/// <inheritdoc />
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "EndingMonth",
table: "ReadingList");
migrationBuilder.DropColumn(
name: "EndingYear",
table: "ReadingList");
migrationBuilder.DropColumn(
name: "StartingMonth",
table: "ReadingList");
migrationBuilder.DropColumn(
name: "StartingYear",
table: "ReadingList");
migrationBuilder.AlterColumn<int>(
name: "BookReaderWritingStyle",
table: "AppUserPreferences",
type: "INTEGER",
nullable: false,
oldClrType: typeof(int),
oldType: "INTEGER",
oldDefaultValue: 0);
}
}
}

View file

@ -225,7 +225,9 @@ namespace API.Data.Migrations
.HasColumnType("INTEGER");
b.Property<int>("BookReaderWritingStyle")
.HasColumnType("INTEGER");
.ValueGeneratedOnAdd()
.HasColumnType("INTEGER")
.HasDefaultValue(0);
b.Property<string>("BookThemeName")
.ValueGeneratedOnAdd()
@ -871,6 +873,12 @@ namespace API.Data.Migrations
b.Property<DateTime>("CreatedUtc")
.HasColumnType("TEXT");
b.Property<int>("EndingMonth")
.HasColumnType("INTEGER");
b.Property<int>("EndingYear")
.HasColumnType("INTEGER");
b.Property<DateTime>("LastModified")
.HasColumnType("TEXT");
@ -883,6 +891,12 @@ namespace API.Data.Migrations
b.Property<bool>("Promoted")
.HasColumnType("INTEGER");
b.Property<int>("StartingMonth")
.HasColumnType("INTEGER");
b.Property<int>("StartingYear")
.HasColumnType("INTEGER");
b.Property<string>("Summary")
.HasColumnType("TEXT");

View file

@ -7,6 +7,7 @@ using API.DTOs.Metadata;
using API.DTOs.Reader;
using API.Entities;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;

View file

@ -6,6 +6,7 @@ using API.Data.Misc;
using API.DTOs.CollectionTags;
using API.Entities;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;

View file

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using API.DTOs.Metadata;
using API.Entities;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;

View file

@ -10,6 +10,7 @@ using API.DTOs.Metadata;
using API.Entities;
using API.Entities.Enums;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Kavita.Common.Extensions;

View file

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using API.DTOs;
using API.Entities;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;

View file

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using API.DTOs;
@ -6,6 +7,7 @@ using API.DTOs.ReadingLists;
using API.Entities;
using API.Entities.Enums;
using API.Extensions;
using API.Extensions.QueryExtensions;
using API.Helpers;
using API.Services;
using AutoMapper;
@ -15,10 +17,18 @@ using Microsoft.EntityFrameworkCore;
namespace API.Data.Repositories;
[Flags]
public enum ReadingListIncludes
{
None = 1,
Items = 2,
ItemChapter = 4,
}
public interface IReadingListRepository
{
Task<PagedList<ReadingListDto>> GetReadingListDtosForUserAsync(int userId, bool includePromoted, UserParams userParams);
Task<ReadingList?> GetReadingListByIdAsync(int readingListId);
Task<ReadingList?> GetReadingListByIdAsync(int readingListId, ReadingListIncludes includes = ReadingListIncludes.None);
Task<IEnumerable<ReadingListItemDto>> GetReadingListItemDtosByIdAsync(int readingListId, int userId);
Task<ReadingListDto?> GetReadingListDtoByIdAsync(int readingListId, int userId);
Task<IEnumerable<ReadingListItemDto>> AddReadingProgressModifiers(int userId, IList<ReadingListItemDto> items);
@ -34,9 +44,9 @@ public interface IReadingListRepository
Task<string?> GetCoverImageAsync(int readingListId);
Task<IList<string>> GetAllCoverImagesAsync();
Task<bool> ReadingListExists(string name);
Task<List<ReadingList>> GetAllReadingListsAsync();
IEnumerable<PersonDto> GetReadingListCharactersAsync(int readingListId);
Task<IList<ReadingList>> GetAllWithNonWebPCovers();
Task<IList<string>> GetFirstFourCoverImagesByReadingListId(int readingListId);
}
public class ReadingListRepository : IReadingListRepository
@ -88,15 +98,6 @@ public class ReadingListRepository : IReadingListRepository
.AnyAsync(x => x.NormalizedTitle != null && x.NormalizedTitle.Equals(normalized));
}
public async Task<List<ReadingList>> GetAllReadingListsAsync()
{
return await _context.ReadingList
.Include(r => r.Items.OrderBy(i => i.Order))
.AsSplitQuery()
.OrderBy(l => l.Title)
.ToListAsync();
}
public IEnumerable<PersonDto> GetReadingListCharactersAsync(int readingListId)
{
return _context.ReadingListItem
@ -114,6 +115,23 @@ public class ReadingListRepository : IReadingListRepository
.ToListAsync();
}
/// <summary>
/// If less than 4 images exist, will return nothing back. Will not be full paths, but just cover image filenames
/// </summary>
/// <param name="readingListId"></param>
/// <returns></returns>
/// <exception cref="NotImplementedException"></exception>
public async Task<IList<string>> GetFirstFourCoverImagesByReadingListId(int readingListId)
{
return await _context.ReadingListItem
.Where(ri => ri.ReadingListId == readingListId)
.Include(ri => ri.Chapter)
.Where(ri => ri.Chapter.CoverImage != null)
.Select(ri => ri.Chapter.CoverImage)
.Take(4)
.ToListAsync();
}
public void Remove(ReadingListItem item)
{
_context.ReadingListItem.Remove(item);
@ -151,10 +169,11 @@ public class ReadingListRepository : IReadingListRepository
return await query.ToListAsync();
}
public async Task<ReadingList?> GetReadingListByIdAsync(int readingListId)
public async Task<ReadingList?> GetReadingListByIdAsync(int readingListId, ReadingListIncludes includes = ReadingListIncludes.None)
{
return await _context.ReadingList
.Where(r => r.Id == readingListId)
.Includes(includes)
.Include(r => r.Items.OrderBy(item => item.Order))
.AsSplitQuery()
.SingleOrDefaultAsync();

View file

@ -17,6 +17,7 @@ using API.Entities;
using API.Entities.Enums;
using API.Entities.Metadata;
using API.Extensions;
using API.Extensions.QueryExtensions;
using API.Helpers;
using API.Services;
using API.Services.Tasks;

View file

@ -4,6 +4,7 @@ using System.Threading.Tasks;
using API.DTOs.Metadata;
using API.Entities;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;

View file

@ -9,6 +9,7 @@ using API.DTOs.Filtering;
using API.DTOs.Reader;
using API.Entities;
using API.Extensions;
using API.Extensions.QueryExtensions;
using AutoMapper;
using AutoMapper.QueryableExtensions;
using Microsoft.AspNetCore.Identity;