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:
Joe Milazzo 2023-08-13 12:39:28 -05:00 committed by GitHub
parent f472745ae4
commit 59c7ef5aa5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 217 additions and 111 deletions

View file

@ -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)

View file

@ -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)

View file

@ -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)
{

View file

@ -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",