Fixes, Tweaks, and Series Filtering (#1217)

* From previous fix, added the other locking conditions on the update series metadata.

* Fixed a bug where custom series, collection tag, and reading list covers weren't being removed on cleanup.

* Ensure reading list detail has a margin to align to the standard

* Refactored some event stuff to use dedicated consts. Introduced a new event when users read something, which can update progress bars on cards.

* Added recomended and library tags to the library detail page. This will eventually offer more custom analytics

* Cleanup some code onc arousel

* Adjusted scale to height/width css to better fit

* Small css tweaks to better center images in the manga reader in both axis. This takes care of double page rendering as well.

* When a special has a Title set in the metadata, on series detail page, show that on the card rather than filename.

* Fixed a bug where when paging in manga reader, the scroll to top wasn't working due to changing where scrolling is done

* More css goodness for rendering images in manga reader

* Fixed a bug where clearing a typeahead externally wouldn't clear the x button

* Fixed a bug where filering then using keyboard would select wrong option

* Added a new sorting field for Last Chapter Added (new field) to get a similar on deck feel.

* Tweaked recently updated to hit the NFR of 500ms (300ms fresh start) and still give a much better experience.

* Refactored On deck to now go to all series and also sort by last updated. Recently Added Series now loads all series with sort by created.

* Some tweaks on css for cover image chooser

* Fixed a bug in pagination control where multiple pagination events could trigger on load and thus multiple requests for data on parent controller.

* Updated edit series modal to show when the last chapter was added and when user last read it.

* Implemented a highlight on the fitler button when a filter is active.

* Refactored metadata filter screens to perserve the filters in the url and thus when navigating back and forth, it will retain. users should click side nav to reset the state.

* Hide middle section on companion bar on phones

* Cleaned up some prefilters and console.logs

* Don't open drawer by default when a filter is active
This commit is contained in:
Joseph Milazzo 2022-04-14 16:55:06 -05:00 committed by GitHub
parent 5e629913b7
commit 553f9b0d98
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
63 changed files with 864 additions and 537 deletions

View file

@ -82,6 +82,29 @@ namespace API.Data
};
}
public static ReadingList ReadingList(string title, string summary, bool promoted)
{
return new ReadingList()
{
NormalizedTitle = API.Parser.Parser.Normalize(title?.Trim()).ToUpper(),
Title = title?.Trim(),
Summary = summary?.Trim(),
Promoted = promoted,
Items = new List<ReadingListItem>()
};
}
public static ReadingListItem ReadingListItem(int index, int seriesId, int volumeId, int chapterId)
{
return new ReadingListItem()
{
Order = index,
ChapterId = chapterId,
SeriesId = seriesId,
VolumeId = volumeId
};
}
public static Genre Genre(string name, bool external)
{
return new Genre()

View file

@ -27,6 +27,7 @@ public interface IReadingListRepository
void Update(ReadingList list);
Task<int> Count();
Task<string> GetCoverImageAsync(int readingListId);
Task<IList<string>> GetAllCoverImagesAsync();
}
public class ReadingListRepository : IReadingListRepository
@ -59,6 +60,15 @@ public class ReadingListRepository : IReadingListRepository
.SingleOrDefaultAsync();
}
public async Task<IList<string>> GetAllCoverImagesAsync()
{
return await _context.ReadingList
.Select(t => t.CoverImage)
.Where(t => !string.IsNullOrEmpty(t))
.AsNoTracking()
.ToListAsync();
}
public void Remove(ReadingListItem item)
{
_context.ReadingListItem.Remove(item);

View file

@ -95,7 +95,7 @@ public interface ISeriesRepository
Task<IList<AgeRatingDto>> GetAllAgeRatingsDtosForLibrariesAsync(List<int> libraryIds);
Task<IList<LanguageDto>> GetAllLanguagesForLibrariesAsync(List<int> libraryIds);
Task<IList<PublicationStatusDto>> GetAllPublicationStatusesDtosForLibrariesAsync(List<int> libraryIds);
Task<IEnumerable<GroupedSeriesDto>> GetRecentlyUpdatedSeries(int userId);
Task<IEnumerable<GroupedSeriesDto>> GetRecentlyUpdatedSeries(int userId, int pageSize = 30);
}
public class SeriesRepository : ISeriesRepository
@ -231,7 +231,7 @@ public class SeriesRepository : ISeriesRepository
/// <summary>
/// Gets all series
/// </summary>
/// <param name="libraryId"></param>
/// <param name="libraryId">Restricts to just one library</param>
/// <param name="userId"></param>
/// <param name="userParams"></param>
/// <param name="filter"></param>
@ -617,6 +617,7 @@ public class SeriesRepository : ISeriesRepository
LastReadingProgress = _context.AppUserProgresses
.Where(p => p.Id == progress.Id && p.AppUserId == userId)
.Max(p => p.LastModified),
LastModified = _context.AppUserProgresses.Where(p => p.Id == progress.Id && p.AppUserId == userId).Max(p => p.LastModified),
s.LastChapterAdded
});
if (cutoffOnDate)
@ -628,8 +629,8 @@ public class SeriesRepository : ISeriesRepository
var retSeries = query.Where(s => s.AppUserId == userId
&& s.PagesRead > 0
&& s.PagesRead < s.Series.Pages)
.OrderByDescending(s => s.LastReadingProgress)
.ThenByDescending(s => s.LastChapterAdded)
.OrderByDescending(s => s.LastChapterAdded)
.ThenByDescending(s => s.LastReadingProgress)
.Select(s => s.Series)
.ProjectTo<SeriesDto>(_mapper.ConfigurationProvider)
.AsSplitQuery()
@ -680,6 +681,7 @@ public class SeriesRepository : ISeriesRepository
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),
_ => query
};
}
@ -690,6 +692,7 @@ public class SeriesRepository : ISeriesRepository
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),
_ => query
};
}
@ -900,16 +903,18 @@ public class SeriesRepository : ISeriesRepository
/// <summary>
/// Return recently updated series, regardless of read progress, and group the number of volume or chapters added.
/// </summary>
/// <remarks>This provides 2 levels of pagination. Fetching the individual chapters only looks at 3000. Then when performing grouping
/// in memory, we stop after 30 series. </remarks>
/// <param name="userId">Used to ensure user has access to libraries</param>
/// <returns></returns>
public async Task<IEnumerable<GroupedSeriesDto>> GetRecentlyUpdatedSeries(int userId)
public async Task<IEnumerable<GroupedSeriesDto>> GetRecentlyUpdatedSeries(int userId, int pageSize = 30)
{
var ret = await GetRecentlyAddedChaptersQuery(userId, 150);
var seriesMap = new Dictionary<string, GroupedSeriesDto>();
var seriesMap = new Dictionary<string, GroupedSeriesDto>();
var index = 0;
foreach (var item in ret)
foreach (var item in await GetRecentlyAddedChaptersQuery(userId))
{
if (seriesMap.Keys.Count == pageSize) break;
if (seriesMap.ContainsKey(item.SeriesName))
{
seriesMap[item.SeriesName].Count += 1;
@ -932,43 +937,9 @@ public class SeriesRepository : ISeriesRepository
}
return seriesMap.Values.AsEnumerable();
//return seriesMap.Values.ToList();
// var libraries = await _context.AppUser
// .Where(u => u.Id == userId)
// .SelectMany(u => u.Libraries.Select(l => new {LibraryId = l.Id, LibraryType = l.Type}))
// .ToListAsync();
// var libraryIds = libraries.Select(l => l.LibraryId).ToList();
//
// var cuttoffDate = DateTime.Now - TimeSpan.FromDays(12);
//
// var ret2 = _context.Series
// .Where(s => s.LastChapterAdded >= cuttoffDate
// && libraryIds.Contains(s.LibraryId))
// .Select((s) => new GroupedSeriesDto
// {
// LibraryId = s.LibraryId,
// LibraryType = s.Library.Type,
// SeriesId = s.Id,
// SeriesName = s.Name,
// //Created = s.LastChapterAdded, // Hmm on first migration this wont work
// Created = s.Volumes.SelectMany(v => v.Chapters).Max(c => c.Created), // Hmm on first migration this wont work
// Count = s.Volumes.SelectMany(v => v.Chapters).Count(c => c.Created >= cuttoffDate),
// //Id = index,
// Format = s.Format
// })
// .Take(50)
// .OrderByDescending(c => c.Created)
// .AsSplitQuery()
// .AsEnumerable();
//
// return ret2;
}
private async Task<IEnumerable<RecentlyAddedSeries>> GetRecentlyAddedChaptersQuery(int userId, int maxRecords = 50)
private async Task<IEnumerable<RecentlyAddedSeries>> GetRecentlyAddedChaptersQuery(int userId, int maxRecords = 3000)
{
var libraries = await _context.AppUser
.Where(u => u.Id == userId)
@ -1000,7 +971,7 @@ public class SeriesRepository : ISeriesRepository
VolumeNumber = c.Volume.Number,
ChapterTitle = c.Title
})
.Take(maxRecords)
//.Take(maxRecords)
.AsSplitQuery()
.Where(c => c.Created >= withinLastWeek && libraryIds.Contains(c.LibraryId))
.AsEnumerable();