Filtering Bugs + OPDS Want To Read (#2210)
* Fixed Summary not allowing an empty field, as it should allow that. * Cleaned up some localization wording and put a todo for a bug with library filtering not working. * Added Want to Read to OPDS stream * Implemented the ability to disable adding filter rows for bookmarks page which only supports one filter type. * Fixed the library filtering code * Fixed a bunch of titles across the app. Fixed about system page not showing data quick enough. * Hide API key by default and show a button to unhide. Fixed a styling issue with input group buttons. * Fixed a hack to support zh_Hans language code to work for things like pt-br as well. * Fixed transloco not supporting same language scheme as Weblate, but somehow needs all languages. * Fixed the rating on series detail not being inline with other sections
This commit is contained in:
parent
f472745ae4
commit
59c7ef5aa5
25 changed files with 217 additions and 111 deletions
|
|
@ -142,6 +142,19 @@ public class OpdsController : BaseApiController
|
|||
}
|
||||
});
|
||||
feed.Entries.Add(new FeedEntry()
|
||||
{
|
||||
Id = "wantToRead",
|
||||
Title = await _localizationService.Translate(userId, "want-to-read"),
|
||||
Content = new FeedEntryContent()
|
||||
{
|
||||
Text = await _localizationService.Translate(userId, "browse-want-to-read")
|
||||
},
|
||||
Links = new List<FeedLink>()
|
||||
{
|
||||
CreateLink(FeedLinkRelation.SubSection, FeedLinkType.AtomNavigation, $"{prefix}{apiKey}/want-to-read"),
|
||||
}
|
||||
});
|
||||
feed.Entries.Add(new FeedEntry()
|
||||
{
|
||||
Id = "allLibraries",
|
||||
Title = await _localizationService.Translate(userId, "libraries"),
|
||||
|
|
@ -213,6 +226,27 @@ public class OpdsController : BaseApiController
|
|||
return CreateXmlResult(SerializeXml(feed));
|
||||
}
|
||||
|
||||
[HttpGet("{apiKey}/want-to-read")]
|
||||
[Produces("application/xml")]
|
||||
public async Task<IActionResult> GetWantToRead(string apiKey, [FromQuery] int pageNumber = 0)
|
||||
{
|
||||
var userId = await GetUser(apiKey);
|
||||
if (!(await _unitOfWork.SettingsRepository.GetSettingsDtoAsync()).EnableOpds)
|
||||
return BadRequest(await _localizationService.Translate(userId, "opds-disabled"));
|
||||
var (baseUrl, prefix) = await GetPrefix();
|
||||
var wantToReadSeries = await _unitOfWork.SeriesRepository.GetWantToReadForUserV2Async(userId, GetUserParams(pageNumber), _filterV2Dto);
|
||||
var seriesMetadatas = await _unitOfWork.SeriesRepository.GetSeriesMetadataForIds(wantToReadSeries.Select(s => s.Id));
|
||||
|
||||
var feed = CreateFeed(await _localizationService.Translate(userId, "want-to-read"), $"{apiKey}/want-to-read", apiKey, prefix);
|
||||
SetFeedId(feed, $"want-to-read");
|
||||
AddPagination(feed, wantToReadSeries, $"{prefix}{apiKey}/want-to-read");
|
||||
|
||||
feed.Entries.AddRange(wantToReadSeries.Select(seriesDto =>
|
||||
CreateSeries(seriesDto, seriesMetadatas.First(s => s.SeriesId == seriesDto.Id), apiKey, prefix, baseUrl)));
|
||||
|
||||
return CreateXmlResult(SerializeXml(feed));
|
||||
}
|
||||
|
||||
[HttpGet("{apiKey}/collections")]
|
||||
[Produces("application/xml")]
|
||||
public async Task<IActionResult> GetCollections(string apiKey)
|
||||
|
|
|
|||
|
|
@ -939,7 +939,6 @@ public class SeriesRepository : ISeriesRepository
|
|||
|
||||
private async Task<IQueryable<Series>> CreateFilteredSearchQueryableV2(int userId, FilterV2Dto filter, QueryContext queryContext, IQueryable<Series>? query = null)
|
||||
{
|
||||
// NOTE: Why do we even have libraryId when the filter has the actual libraryIds?
|
||||
var userLibraries = await GetUserLibrariesForFilteredQuery(0, userId, queryContext);
|
||||
var userRating = await _context.AppUser.GetUserAgeRestriction(userId);
|
||||
var onlyParentSeries = await _context.AppUserPreferences.Where(u => u.AppUserId == userId)
|
||||
|
|
@ -949,39 +948,66 @@ public class SeriesRepository : ISeriesRepository
|
|||
query ??= _context.Series
|
||||
.AsNoTracking();
|
||||
|
||||
var filterLibs = new List<int>();
|
||||
|
||||
|
||||
// First setup any FilterField.Libraries in the statements, as these don't have any traditional query statements applied here
|
||||
query = ApplyLibraryFilter(filter, query);
|
||||
|
||||
query = BuildFilterQuery(userId, filter, query);
|
||||
|
||||
|
||||
query = query
|
||||
.WhereIf(userLibraries.Count > 0, s => userLibraries.Contains(s.LibraryId))
|
||||
.WhereIf(onlyParentSeries, s =>
|
||||
s.RelationOf.Count == 0 ||
|
||||
s.RelationOf.All(p => p.RelationKind == RelationKind.Prequel))
|
||||
.RestrictAgainstAgeRestriction(userRating);
|
||||
|
||||
|
||||
return ApplyLimit(query
|
||||
.Sort(filter.SortOptions)
|
||||
.AsSplitQuery(), filter.LimitTo);
|
||||
}
|
||||
|
||||
private static IQueryable<Series> ApplyLibraryFilter(FilterV2Dto filter, IQueryable<Series> query)
|
||||
{
|
||||
var filterIncludeLibs = new List<int>();
|
||||
var filterExcludeLibs = new List<int>();
|
||||
if (filter.Statements != null)
|
||||
{
|
||||
foreach (var stmt in filter.Statements.Where(stmt => stmt.Field == FilterField.Libraries))
|
||||
{
|
||||
filterLibs.Add(int.Parse(stmt.Value));
|
||||
if (stmt.Comparison is FilterComparison.Equal or FilterComparison.Contains)
|
||||
{
|
||||
filterIncludeLibs.Add(int.Parse(stmt.Value));
|
||||
}
|
||||
else
|
||||
{
|
||||
filterExcludeLibs.Add(int.Parse(stmt.Value));
|
||||
}
|
||||
}
|
||||
|
||||
// Remove as filterLibs now has everything
|
||||
filter.Statements = filter.Statements.Where(stmt => stmt.Field != FilterField.Libraries).ToList();
|
||||
}
|
||||
|
||||
// We now have a list of libraries the user wants it restricted to and libraries the user doesn't want in the list
|
||||
// We need to check what the filer combo is to see how to next approach
|
||||
|
||||
query = BuildFilterQuery(userId, filter, query);
|
||||
|
||||
query = query
|
||||
.WhereIf(userLibraries.Count > 0, s => userLibraries.Contains(s.LibraryId))
|
||||
.WhereIf(filterLibs.Count > 0, s => filterLibs.Contains(s.LibraryId))
|
||||
.WhereIf(onlyParentSeries, s =>
|
||||
s.RelationOf.Count == 0 ||
|
||||
s.RelationOf.All(p => p.RelationKind == RelationKind.Prequel));
|
||||
|
||||
if (userRating.AgeRating != AgeRating.NotApplicable)
|
||||
if (filter.Combination == FilterCombination.And)
|
||||
{
|
||||
// this if statement is included in the extension
|
||||
query = query.RestrictAgainstAgeRestriction(userRating);
|
||||
// If the filter combo is AND, then we need 2 different queries
|
||||
query = query
|
||||
.WhereIf(filterIncludeLibs.Count > 0, s => filterIncludeLibs.Contains(s.LibraryId))
|
||||
.WhereIf(filterExcludeLibs.Count > 0, s => !filterExcludeLibs.Contains(s.LibraryId));
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is an OR statement. In that case we can just remove the filterExcludes
|
||||
query = query.WhereIf(filterIncludeLibs.Count > 0, s => filterIncludeLibs.Contains(s.LibraryId));
|
||||
}
|
||||
|
||||
return ApplyLimit(query
|
||||
.Sort(filter.SortOptions)
|
||||
.AsSplitQuery(), filter.LimitTo);
|
||||
return query;
|
||||
}
|
||||
|
||||
private static IQueryable<Series> BuildFilterQuery(int userId, FilterV2Dto filterDto, IQueryable<Series> query)
|
||||
|
|
|
|||
|
|
@ -483,7 +483,7 @@ public static class SeriesFilter
|
|||
public static IQueryable<Series> HasSummary(this IQueryable<Series> queryable, bool condition,
|
||||
FilterComparison comparison, string queryString)
|
||||
{
|
||||
if (string.IsNullOrEmpty(queryString) || !condition) return queryable;
|
||||
if (!condition) return queryable;
|
||||
|
||||
switch (comparison)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -140,6 +140,8 @@
|
|||
"on-deck": "On Deck",
|
||||
"browse-on-deck": "Browse On Deck",
|
||||
"recently-added": "Recently Added",
|
||||
"want-to-read": "Want to Read",
|
||||
"browse-want-to-read": "Browse Want to Read",
|
||||
"browse-recently-added": "Browse Recently Added",
|
||||
"reading-lists": "Reading Lists",
|
||||
"browse-reading-lists": "Browse by Reading Lists",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue