Swipe Issues (#1745)

* Updated theme support to be able to customize the tile color dynamically from a theme via --tile-color. In addition, --theme-color will update apple-mobile-web-app-status-bar-style as well as the non-apple variants

* Removed --manga-reader-bg-color as it wasn't used anywhere. Fixed double pagination on swipe.

* Cleaned up some dead threshold code for swipe.

* Started refactoring tests to use an abstract test class. Stopping because I should do on the .net 7 branch to avoid large merge conflicts. Tests need to be re-designed so they can run in parallel.

* Fixed a bug in reading lists where when deleting an item, order could be miscalculated.

* Started adding new information for stat service. Refactored time spent reading to be more accurate by taking average time against how much of the chapter the user has read.

* Hooked up total time reading at server stat level. Don't show fancy graphs on mobile.

* Added new stats for v0.7

* Added a test for Clearing want to read

* Fixed a few tests that weren't resetting state between runs

* Fixed some broken unit tests

* Ensure all Series queries sort by a case invariant string.

* Added more aggressive caching of images. This will result in a min delay on pages after a cover is changed.

* Fixed a bug where if during new word count calculation, new word count is zero, restoring the old count wasn't working.

* Cleaned up some of the code for getting time estimates

* Fixed a bug where triggering swipe right wasn't working when there was no scroll

* Delete the temp folder for creating a download after a full zip is created.
This commit is contained in:
Joe Milazzo 2023-01-12 19:24:58 -06:00 committed by GitHub
parent 3d6de68089
commit 549e52b458
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 488 additions and 339 deletions

View file

@ -299,6 +299,8 @@ public class ArchiveService : IArchiveService
try
{
ZipFile.CreateFromDirectory(tempLocation, zipPath);
// Remove the folder as we have the zip
_directoryService.ClearAndDeleteDirectory(tempLocation);
}
catch (AggregateException ex)
{

View file

@ -575,39 +575,20 @@ public class ReaderService : IReaderService
{
var minHours = Math.Max((int) Math.Round((wordCount / MinWordsPerHour)), 0);
var maxHours = Math.Max((int) Math.Round((wordCount / MaxWordsPerHour)), 0);
if (maxHours < minHours)
{
return new HourEstimateRangeDto
{
MinHours = maxHours,
MaxHours = minHours,
AvgHours = (int) Math.Round((wordCount / AvgWordsPerHour))
};
}
return new HourEstimateRangeDto
{
MinHours = minHours,
MaxHours = maxHours,
MinHours = Math.Min(minHours, maxHours),
MaxHours = Math.Max(minHours, maxHours),
AvgHours = (int) Math.Round((wordCount / AvgWordsPerHour))
};
}
var minHoursPages = Math.Max((int) Math.Round((pageCount / MinPagesPerMinute / 60F)), 0);
var maxHoursPages = Math.Max((int) Math.Round((pageCount / MaxPagesPerMinute / 60F)), 0);
if (maxHoursPages < minHoursPages)
{
return new HourEstimateRangeDto
{
MinHours = maxHoursPages,
MaxHours = minHoursPages,
AvgHours = (int) Math.Round((pageCount / AvgPagesPerMinute / 60F))
};
}
return new HourEstimateRangeDto
{
MinHours = minHoursPages,
MaxHours = maxHoursPages,
MinHours = Math.Min(minHoursPages, maxHoursPages),
MaxHours = Math.Max(minHoursPages, maxHoursPages),
AvgHours = (int) Math.Round((pageCount / AvgPagesPerMinute / 60F))
};
}

View file

@ -107,7 +107,7 @@ public class ReadingListService : IReadingListService
public async Task<bool> DeleteReadingListItem(UpdateReadingListPosition dto)
{
var readingList = await _unitOfWork.ReadingListRepository.GetReadingListByIdAsync(dto.ReadingListId);
readingList.Items = readingList.Items.Where(r => r.Id != dto.ReadingListItemId).ToList();
readingList.Items = readingList.Items.Where(r => r.Id != dto.ReadingListItemId).OrderBy(r => r.Order).ToList();
var index = 0;
foreach (var readingListItem in readingList.Items)

View file

@ -32,6 +32,7 @@ public interface IStatisticService
IEnumerable<StatCount<int>> GetPagesReadCountByYear(int userId = 0);
IEnumerable<StatCount<int>> GetWordsReadCountByYear(int userId = 0);
Task UpdateServerStatistics();
Task<long> TimeSpentReadingForUsersAsync(IList<int> userIds, IList<int> libraryIds);
}
/// <summary>
@ -62,18 +63,20 @@ public class StatisticService : IStatisticService
.Where(p => libraryIds.Contains(p.LibraryId))
.SumAsync(p => p.PagesRead);
var ids = await _context.AppUserProgresses
.Where(p => p.AppUserId == userId)
.Where(p => libraryIds.Contains(p.LibraryId))
.Where(p => p.PagesRead > 0)
.Select(p => new {p.ChapterId, p.SeriesId})
.ToListAsync();
// var ids = await _context.AppUserProgresses
// .Where(p => p.AppUserId == userId)
// .Where(p => libraryIds.Contains(p.LibraryId))
// .Where(p => p.PagesRead > 0)
// .Select(p => new {p.ChapterId, p.SeriesId})
// .ToListAsync();
var chapterIds = ids.Select(id => id.ChapterId);
//var chapterIds = ids.Select(id => id.ChapterId);
var timeSpentReading = await _context.Chapter
.Where(c => chapterIds.Contains(c.Id))
.SumAsync(c => c.AvgHoursToRead);
// var timeSpentReading = await _context.Chapter
// .Where(c => chapterIds.Contains(c.Id))
// .SumAsync(c => c.AvgHoursToRead);
var timeSpentReading = await TimeSpentReadingForUsersAsync(new List<int>() {userId}, libraryIds);
var totalWordsRead = (long) Math.Round(await _context.AppUserProgresses
.Where(p => p.AppUserId == userId)
@ -275,6 +278,8 @@ public class StatisticService : IStatisticService
.Distinct()
.Count();
return new ServerStatisticsDto()
{
ChapterCount = await _context.Chapter.CountAsync(),
@ -289,7 +294,8 @@ public class StatisticService : IStatisticService
MostActiveLibraries = mostActiveLibrary,
MostPopularSeries = mostPopularSeries,
MostReadSeries = mostReadSeries,
RecentlyRead = recentlyRead
RecentlyRead = recentlyRead,
TotalReadingTime = await TimeSpentReadingForUsersAsync(ArraySegment<int>.Empty, ArraySegment<int>.Empty)
};
}
@ -483,6 +489,30 @@ public class StatisticService : IStatisticService
await _unitOfWork.CommitAsync();
}
public async Task<long> TimeSpentReadingForUsersAsync(IList<int> userIds, IList<int> libraryIds)
{
var query = _context.AppUserProgresses
.AsSplitQuery();
if (userIds.Any())
{
query = query.Where(p => userIds.Contains(p.AppUserId));
}
if (libraryIds.Any())
{
query = query.Where(p => libraryIds.Contains(p.LibraryId));
}
return (long) Math.Round(await query
.Join(_context.Chapter,
p => p.ChapterId,
c => c.Id,
(progress, chapter) => new {chapter, progress})
.Where(p => p.chapter.AvgHoursToRead > 0)
.SumAsync(p =>
p.chapter.AvgHoursToRead * (p.progress.PagesRead / (1.0f * p.chapter.Pages))));
}
public async Task<IEnumerable<TopReadDto>> GetTopUsers(int days)
{
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList();

View file

@ -223,7 +223,7 @@ public class WordCountAnalyzerService : IWordCountAnalyzerService
}
if (series.WordCount == 0 && series.WordCount != 0) series.WordCount = existingWordCount; // Restore original word count if the file hasn't changed
if (series.WordCount == 0 && existingWordCount != 0) series.WordCount = existingWordCount; // Restore original word count if the file hasn't changed
var seriesEstimate = _readerService.GetTimeEstimate(series.WordCount, series.Pages, isEpub);
series.MinHoursToRead = seriesEstimate.MinHours;
series.MaxHoursToRead = seriesEstimate.MaxHours;

View file

@ -25,18 +25,23 @@ public interface IStatsService
Task<ServerInfoDto> GetServerInfo();
Task SendCancellation();
}
/// <summary>
/// This is for reporting to the stat server
/// </summary>
public class StatsService : IStatsService
{
private readonly ILogger<StatsService> _logger;
private readonly IUnitOfWork _unitOfWork;
private readonly DataContext _context;
private readonly IStatisticService _statisticService;
private const string ApiUrl = "https://stats.kavitareader.com";
public StatsService(ILogger<StatsService> logger, IUnitOfWork unitOfWork, DataContext context)
public StatsService(ILogger<StatsService> logger, IUnitOfWork unitOfWork, DataContext context, IStatisticService statisticService)
{
_logger = logger;
_unitOfWork = unitOfWork;
_context = context;
_statisticService = statisticService;
FlurlHttp.ConfigureClient(ApiUrl, cli =>
cli.Settings.HttpClientFactory = new UntrustedCertClientFactory());
@ -116,6 +121,14 @@ public class StatsService : IStatsService
DotnetVersion = Environment.Version.ToString(),
IsDocker = new OsInfo().IsDocker,
NumOfCores = Math.Max(Environment.ProcessorCount, 1),
UsersWithEmulateComicBook = await _context.AppUserPreferences.CountAsync(p => p.EmulateBook),
TotalReadingHours = await _statisticService.TimeSpentReadingForUsersAsync(ArraySegment<int>.Empty, ArraySegment<int>.Empty),
PercentOfLibrariesWithFolderWatchingEnabled = await GetPercentageOfLibrariesWithFolderWatchingEnabled(),
PercentOfLibrariesIncludedInRecommended = await GetPercentageOfLibrariesIncludedInRecommended(),
PercentOfLibrariesIncludedInDashboard = await GetPercentageOfLibrariesIncludedInDashboard(),
PercentOfLibrariesIncludedInSearch = await GetPercentageOfLibrariesIncludedInSearch(),
HasBookmarks = (await _unitOfWork.UserRepository.GetAllBookmarksAsync()).Any(),
NumberOfLibraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).Count(),
NumberOfCollections = (await _unitOfWork.CollectionTagRepository.GetAllTagsAsync()).Count(),
@ -127,6 +140,7 @@ public class StatsService : IStatsService
TotalPeople = await _unitOfWork.PersonRepository.GetCountAsync(),
UsingSeriesRelationships = await GetIfUsingSeriesRelationship(),
StoreBookmarksAsWebP = serverSettings.ConvertBookmarkToWebP,
StoreCoversAsWebP = serverSettings.ConvertCoverToWebP,
MaxSeriesInALibrary = await MaxSeriesInAnyLibrary(),
MaxVolumesInASeries = await MaxVolumesInASeries(),
MaxChaptersInASeries = await MaxChaptersInASeries(),
@ -190,6 +204,30 @@ public class StatsService : IStatsService
}
}
private async Task<float> GetPercentageOfLibrariesWithFolderWatchingEnabled()
{
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList();
return libraries.Count(l => l.FolderWatching) / (1.0f * libraries.Count);
}
private async Task<float> GetPercentageOfLibrariesIncludedInRecommended()
{
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList();
return libraries.Count(l => l.IncludeInRecommended) / (1.0f * libraries.Count);
}
private async Task<float> GetPercentageOfLibrariesIncludedInDashboard()
{
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList();
return libraries.Count(l => l.IncludeInDashboard) / (1.0f * libraries.Count);
}
private async Task<float> GetPercentageOfLibrariesIncludedInSearch()
{
var libraries = (await _unitOfWork.LibraryRepository.GetLibrariesAsync()).ToList();
return libraries.Count(l => l.IncludeInSearch) / (1.0f * libraries.Count);
}
private Task<bool> GetIfUsingSeriesRelationship()
{
return _context.SeriesRelation.AnyAsync();
@ -242,6 +280,7 @@ public class StatsService : IStatsService
return await _context.AppUserPreferences.Select(p => p.PageSplitOption).Distinct().ToListAsync();
}
private async Task<IEnumerable<LayoutMode>> AllMangaReaderLayoutModes()
{
return await _context.AppUserPreferences.Select(p => p.LayoutMode).Distinct().ToListAsync();