Smart Filters & Dashboard Customization (#2282)
Co-authored-by: Robbie Davis <robbie@therobbiedavis.com>
This commit is contained in:
parent
3d501c9532
commit
84f85b4f24
92 changed files with 7149 additions and 555 deletions
60
API/Data/Repositories/AppUserSmartFilterRepository.cs
Normal file
60
API/Data/Repositories/AppUserSmartFilterRepository.cs
Normal file
|
@ -0,0 +1,60 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using API.DTOs.Dashboard;
|
||||
using API.Entities;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace API.Data.Repositories;
|
||||
|
||||
public interface IAppUserSmartFilterRepository
|
||||
{
|
||||
void Update(AppUserSmartFilter filter);
|
||||
void Attach(AppUserSmartFilter filter);
|
||||
void Delete(AppUserSmartFilter filter);
|
||||
IEnumerable<SmartFilterDto> GetAllDtosByUserId(int userId);
|
||||
Task<AppUserSmartFilter?> GetById(int smartFilterId);
|
||||
|
||||
}
|
||||
|
||||
public class AppUserSmartFilterRepository : IAppUserSmartFilterRepository
|
||||
{
|
||||
private readonly DataContext _context;
|
||||
private readonly IMapper _mapper;
|
||||
|
||||
public AppUserSmartFilterRepository(DataContext context, IMapper mapper)
|
||||
{
|
||||
_context = context;
|
||||
_mapper = mapper;
|
||||
}
|
||||
|
||||
public void Update(AppUserSmartFilter filter)
|
||||
{
|
||||
_context.Entry(filter).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Attach(AppUserSmartFilter filter)
|
||||
{
|
||||
_context.AppUserSmartFilter.Attach(filter);
|
||||
}
|
||||
|
||||
public void Delete(AppUserSmartFilter filter)
|
||||
{
|
||||
_context.AppUserSmartFilter.Remove(filter);
|
||||
}
|
||||
|
||||
public IEnumerable<SmartFilterDto> GetAllDtosByUserId(int userId)
|
||||
{
|
||||
return _context.AppUserSmartFilter
|
||||
.Where(f => f.AppUserId == userId)
|
||||
.ProjectTo<SmartFilterDto>(_mapper.ConfigurationProvider)
|
||||
.AsEnumerable();
|
||||
}
|
||||
|
||||
public async Task<AppUserSmartFilter?> GetById(int smartFilterId)
|
||||
{
|
||||
return await _context.AppUserSmartFilter.FirstOrDefaultAsync(d => d.Id == smartFilterId);
|
||||
}
|
||||
}
|
|
@ -8,6 +8,7 @@ using API.Data.Misc;
|
|||
using API.Data.Scanner;
|
||||
using API.DTOs;
|
||||
using API.DTOs.CollectionTags;
|
||||
using API.DTOs.Dashboard;
|
||||
using API.DTOs.Filtering;
|
||||
using API.DTOs.Filtering.v2;
|
||||
using API.DTOs.Metadata;
|
||||
|
@ -952,6 +953,9 @@ public class SeriesRepository : ISeriesRepository
|
|||
// First setup any FilterField.Libraries in the statements, as these don't have any traditional query statements applied here
|
||||
query = ApplyLibraryFilter(filter, query);
|
||||
|
||||
query = ApplyWantToReadFilter(filter, query, userId);
|
||||
|
||||
|
||||
query = BuildFilterQuery(userId, filter, query);
|
||||
|
||||
|
||||
|
@ -968,6 +972,24 @@ public class SeriesRepository : ISeriesRepository
|
|||
.AsSplitQuery(), filter.LimitTo);
|
||||
}
|
||||
|
||||
private IQueryable<Series> ApplyWantToReadFilter(FilterV2Dto filter, IQueryable<Series> query, int userId)
|
||||
{
|
||||
var wantToReadStmt = filter.Statements.FirstOrDefault(stmt => stmt.Field == FilterField.WantToRead);
|
||||
if (wantToReadStmt == null) return query;
|
||||
|
||||
var seriesIds = _context.AppUser.Where(u => u.Id == userId).SelectMany(u => u.WantToRead).Select(s => s.Id);
|
||||
if (bool.Parse(wantToReadStmt.Value))
|
||||
{
|
||||
query = query.Where(s => seriesIds.Contains(s.Id));
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(s => !seriesIds.Contains(s.Id));
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
private static IQueryable<Series> ApplyLibraryFilter(FilterV2Dto filter, IQueryable<Series> query)
|
||||
{
|
||||
var filterIncludeLibs = new List<int>();
|
||||
|
@ -1060,6 +1082,9 @@ public class SeriesRepository : ISeriesRepository
|
|||
FilterField.Libraries =>
|
||||
// This is handled in the code before this as it's handled in a more general, combined manner
|
||||
query,
|
||||
FilterField.WantToRead =>
|
||||
// This is handled in the higher level of code as it's more general
|
||||
query,
|
||||
FilterField.ReadProgress => query.HasReadingProgress(true, statement.Comparison, (int) value, userId),
|
||||
FilterField.Formats => query.HasFormat(true, statement.Comparison, (IList<MangaFormat>) value),
|
||||
FilterField.ReleaseYear => query.HasReleaseYear(true, statement.Comparison, (int) value),
|
||||
|
|
|
@ -6,7 +6,7 @@ using System.Threading.Tasks;
|
|||
using API.Constants;
|
||||
using API.DTOs;
|
||||
using API.DTOs.Account;
|
||||
using API.DTOs.Filtering;
|
||||
using API.DTOs.Dashboard;
|
||||
using API.DTOs.Filtering.v2;
|
||||
using API.DTOs.Reader;
|
||||
using API.DTOs.Scrobbling;
|
||||
|
@ -15,6 +15,7 @@ using API.Entities;
|
|||
using API.Extensions;
|
||||
using API.Extensions.QueryExtensions;
|
||||
using API.Extensions.QueryExtensions.Filtering;
|
||||
using API.Helpers;
|
||||
using AutoMapper;
|
||||
using AutoMapper.QueryableExtensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
|
@ -34,8 +35,9 @@ public enum AppUserIncludes
|
|||
WantToRead = 64,
|
||||
ReadingListsWithItems = 128,
|
||||
Devices = 256,
|
||||
ScrobbleHolds = 512
|
||||
|
||||
ScrobbleHolds = 512,
|
||||
SmartFilters = 1024,
|
||||
DashboardStreams = 2048
|
||||
}
|
||||
|
||||
public interface IUserRepository
|
||||
|
@ -43,9 +45,11 @@ public interface IUserRepository
|
|||
void Update(AppUser user);
|
||||
void Update(AppUserPreferences preferences);
|
||||
void Update(AppUserBookmark bookmark);
|
||||
void Update(AppUserDashboardStream stream);
|
||||
void Add(AppUserBookmark bookmark);
|
||||
public void Delete(AppUser? user);
|
||||
void Delete(AppUser? user);
|
||||
void Delete(AppUserBookmark bookmark);
|
||||
void Delete(IList<AppUserDashboardStream> streams);
|
||||
Task<IEnumerable<MemberDto>> GetEmailConfirmedMemberDtosAsync(bool emailConfirmed = true);
|
||||
Task<IEnumerable<AppUser>> GetAdminUsersAsync();
|
||||
Task<bool> IsUserAdminAsync(AppUser? user);
|
||||
|
@ -76,6 +80,9 @@ public interface IUserRepository
|
|||
Task<bool> HasHoldOnSeries(int userId, int seriesId);
|
||||
Task<IList<ScrobbleHoldDto>> GetHolds(int userId);
|
||||
Task<string> GetLocale(int userId);
|
||||
Task<IList<DashboardStreamDto>> GetDashboardStreams(int userId, bool visibleOnly = false);
|
||||
Task<AppUserDashboardStream?> GetDashboardStream(int streamId);
|
||||
Task<IList<AppUserDashboardStream>> GetDashboardStreamWithFilter(int filterId);
|
||||
}
|
||||
|
||||
public class UserRepository : IUserRepository
|
||||
|
@ -106,6 +113,11 @@ public class UserRepository : IUserRepository
|
|||
_context.Entry(bookmark).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Update(AppUserDashboardStream stream)
|
||||
{
|
||||
_context.Entry(stream).State = EntityState.Modified;
|
||||
}
|
||||
|
||||
public void Add(AppUserBookmark bookmark)
|
||||
{
|
||||
_context.AppUserBookmark.Add(bookmark);
|
||||
|
@ -122,6 +134,11 @@ public class UserRepository : IUserRepository
|
|||
_context.AppUserBookmark.Remove(bookmark);
|
||||
}
|
||||
|
||||
public void Delete(IList<AppUserDashboardStream> streams)
|
||||
{
|
||||
_context.AppUserDashboardStream.RemoveRange(streams);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A one stop shop to get a tracked AppUser instance with any number of JOINs generated by passing bitwise flags.
|
||||
/// </summary>
|
||||
|
@ -300,6 +317,42 @@ public class UserRepository : IUserRepository
|
|||
.SingleAsync();
|
||||
}
|
||||
|
||||
public async Task<IList<DashboardStreamDto>> GetDashboardStreams(int userId, bool visibleOnly = false)
|
||||
{
|
||||
return await _context.AppUserDashboardStream
|
||||
.Where(d => d.AppUserId == userId)
|
||||
.WhereIf(visibleOnly, d => d.Visible)
|
||||
.OrderBy(d => d.Order)
|
||||
.Include(d => d.SmartFilter)
|
||||
.Select(d => new DashboardStreamDto()
|
||||
{
|
||||
Id = d.Id,
|
||||
Name = d.Name,
|
||||
IsProvided = d.IsProvided,
|
||||
SmartFilterId = d.SmartFilter == null ? 0 : d.SmartFilter.Id,
|
||||
SmartFilterEncoded = d.SmartFilter == null ? null : d.SmartFilter.Filter,
|
||||
StreamType = d.StreamType,
|
||||
Order = d.Order,
|
||||
Visible = d.Visible
|
||||
})
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<AppUserDashboardStream?> GetDashboardStream(int streamId)
|
||||
{
|
||||
return await _context.AppUserDashboardStream
|
||||
.Include(d => d.SmartFilter)
|
||||
.FirstOrDefaultAsync(d => d.Id == streamId);
|
||||
}
|
||||
|
||||
public async Task<IList<AppUserDashboardStream>> GetDashboardStreamWithFilter(int filterId)
|
||||
{
|
||||
return await _context.AppUserDashboardStream
|
||||
.Include(d => d.SmartFilter)
|
||||
.Where(d => d.SmartFilter != null && d.SmartFilter.Id == filterId)
|
||||
.ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<AppUser>> GetAdminUsersAsync()
|
||||
{
|
||||
return await _userManager.GetUsersInRoleAsync(PolicyConstants.AdminRole);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue