From 7fcec4a1125cf88da46bb853654c71d1c1d6aec6 Mon Sep 17 00:00:00 2001 From: Joe Milazzo Date: Mon, 22 May 2023 09:25:56 -0500 Subject: [PATCH] Continue Reading Bugfix (#2010) * Fixed an edge case where continue point wasn't considering any chapters that had progress. Continue point is now slightly faster and uses less memory. * Added a unit test for a user's case. Still not reproducible --- API.Tests/Services/ReaderServiceTests.cs | 178 ++++++++++++++++++ .../Repositories/AppUserProgressRepository.cs | 8 + API/Services/ReaderService.cs | 8 +- openapi.json | 2 +- 4 files changed, 191 insertions(+), 5 deletions(-) diff --git a/API.Tests/Services/ReaderServiceTests.cs b/API.Tests/Services/ReaderServiceTests.cs index 01f94feae..36500093f 100644 --- a/API.Tests/Services/ReaderServiceTests.cs +++ b/API.Tests/Services/ReaderServiceTests.cs @@ -1986,6 +1986,184 @@ public class ReaderServiceTests Assert.Equal(4, nextChapter.VolumeId); } + + /// + /// Volume 1-10 are fully read (single volumes), + /// Special 1 is fully read + /// Chapters 56-90 are read + /// Chapter 91 has partial progress on + /// + [Fact] + public async Task GetContinuePoint_ShouldReturnLastLooseChapter() + { + await ResetDb(); + var series = new SeriesBuilder("Test") + .WithVolume(new VolumeBuilder("1") + .WithChapter(new ChapterBuilder("1").WithPages(1).Build()) + .Build()) + .WithVolume(new VolumeBuilder("2") + .WithChapter(new ChapterBuilder("21").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("22").WithPages(1).Build()) + .Build()) + .WithVolume(new VolumeBuilder("0") + .WithChapter(new ChapterBuilder("51").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("52").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("91").WithPages(2).Build()) + .WithChapter(new ChapterBuilder("Special").WithIsSpecial(true).WithPages(1).Build()) + .Build()) + .Build(); + series.Library = new LibraryBuilder("Test LIb", LibraryType.Manga).Build(); + + _context.Series.Add(series); + + _context.AppUser.Add(new AppUser() + { + UserName = "majora2007" + }); + + await _context.SaveChangesAsync(); + + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 1, + SeriesId = 1, + VolumeId = 1 + }, 1); + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 2, + SeriesId = 1, + VolumeId = 1 + }, 1); + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 3, + SeriesId = 1, + VolumeId = 2 + }, 1); + + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 4, + SeriesId = 1, + VolumeId = 2 + }, 1); + + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 5, + SeriesId = 1, + VolumeId = 2 + }, 1); + + // Chapter 91 has partial progress, hence it should resume there + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 6, + SeriesId = 1, + VolumeId = 2 + }, 1); + + // Special is fully read + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 7, + SeriesId = 1, + VolumeId = 2 + }, 1); + + await _context.SaveChangesAsync(); + + var nextChapter = await _readerService.GetContinuePoint(1, 1); + + Assert.Equal("91", nextChapter.Range); + } + + [Fact] + public async Task GetContinuePoint_DuplicateIssueNumberBetweenChapters() + { + await ResetDb(); + var series = new SeriesBuilder("Test") + .WithVolume(new VolumeBuilder("1") + .WithChapter(new ChapterBuilder("1").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("2").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("21").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("22").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("32").WithPages(1).Build()) + .Build()) + .WithVolume(new VolumeBuilder("2") + .WithChapter(new ChapterBuilder("1").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("2").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("21").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("22").WithPages(1).Build()) + .WithChapter(new ChapterBuilder("32").WithPages(1).Build()) + .Build()) + .Build(); + series.Library = new LibraryBuilder("Test LIb", LibraryType.Manga).Build(); + + _context.Series.Add(series); + + _context.AppUser.Add(new AppUser() + { + UserName = "majora2007" + }); + + await _context.SaveChangesAsync(); + + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 1, + SeriesId = 1, + VolumeId = 1 + }, 1); + + await _context.SaveChangesAsync(); + + var nextChapter = await _readerService.GetContinuePoint(1, 1); + + Assert.Equal("2", nextChapter.Range); + Assert.Equal(1, nextChapter.VolumeId); + + // Mark chapter 2 as read + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 2, + SeriesId = 1, + VolumeId = 1 + }, 1); + await _context.SaveChangesAsync(); + + nextChapter = await _readerService.GetContinuePoint(1, 1); + + Assert.Equal("21", nextChapter.Range); + Assert.Equal(1, nextChapter.VolumeId); + + // Mark chapter 21 as read + await _readerService.SaveReadingProgress(new ProgressDto() + { + PageNum = 1, + ChapterId = 3, + SeriesId = 1, + VolumeId = 1 + }, 1); + await _context.SaveChangesAsync(); + + nextChapter = await _readerService.GetContinuePoint(1, 1); + + Assert.Equal("22", nextChapter.Range); + Assert.Equal(1, nextChapter.VolumeId); + } + + #endregion #region MarkChaptersUntilAsRead diff --git a/API/Data/Repositories/AppUserProgressRepository.cs b/API/Data/Repositories/AppUserProgressRepository.cs index a25b22fb9..44e6a21e2 100644 --- a/API/Data/Repositories/AppUserProgressRepository.cs +++ b/API/Data/Repositories/AppUserProgressRepository.cs @@ -26,6 +26,7 @@ public interface IAppUserProgressRepository Task> GetUserProgressForSeriesAsync(int seriesId, int userId); Task> GetAllProgress(); Task GetUserProgressDtoAsync(int chapterId, int userId); + Task AnyUserProgressForSeriesAsync(int seriesId, int userId); } public class AppUserProgressRepository : IAppUserProgressRepository @@ -129,6 +130,13 @@ public class AppUserProgressRepository : IAppUserProgressRepository .FirstOrDefaultAsync(); } + public async Task AnyUserProgressForSeriesAsync(int seriesId, int userId) + { + return await _context.AppUserProgresses + .Where(p => p.SeriesId == seriesId && p.AppUserId == userId && p.PagesRead > 0) + .AnyAsync(); + } + public async Task GetUserProgressAsync(int chapterId, int userId) { return await _context.AppUserProgresses diff --git a/API/Services/ReaderService.cs b/API/Services/ReaderService.cs index 657431bf4..648f05dfa 100644 --- a/API/Services/ReaderService.cs +++ b/API/Services/ReaderService.cs @@ -478,10 +478,9 @@ public class ReaderService : IReaderService /// public async Task GetContinuePoint(int seriesId, int userId) { - var progress = (await _unitOfWork.AppUserProgressRepository.GetUserProgressForSeriesAsync(seriesId, userId)).ToList(); var volumes = (await _unitOfWork.VolumeRepository.GetVolumesDtoAsync(seriesId, userId)).ToList(); - if (progress.Count == 0) + if (!await _unitOfWork.AppUserProgressRepository.AnyUserProgressForSeriesAsync(seriesId, userId)) { // I think i need a way to sort volumes last return volumes.OrderBy(v => double.Parse(v.Number + string.Empty), _chapterSortComparer).First().Chapters @@ -523,13 +522,14 @@ public class ReaderService : IReaderService return lastChapter; } - // If the last chapter didn't fit, then we need the next chapter without any progress - var firstChapterWithoutProgress = volumeChapters.FirstOrDefault(c => c.PagesRead == 0); + // If the last chapter didn't fit, then we need the next chapter without full progress + var firstChapterWithoutProgress = volumeChapters.FirstOrDefault(c => c.PagesRead < c.Pages); if (firstChapterWithoutProgress != null) { return firstChapterWithoutProgress; } + // chaptersWithProgress are all read, then we need to get the next chapter that doesn't have progress var lastIndexWithProgress = volumeChapters.IndexOf(lastChapter); if (lastIndexWithProgress + 1 < volumeChapters.Count) diff --git a/openapi.json b/openapi.json index ee8713e0e..f3666479e 100644 --- a/openapi.json +++ b/openapi.json @@ -7,7 +7,7 @@ "name": "GPL-3.0", "url": "https://github.com/Kareadita/Kavita/blob/develop/LICENSE" }, - "version": "0.7.2.14" + "version": "0.7.2.16" }, "servers": [ {