diff --git a/API.Tests/Comparers/SortComparerZeroLastTests.cs b/API.Tests/Comparers/SortComparerZeroLastTests.cs new file mode 100644 index 000000000..37699d110 --- /dev/null +++ b/API.Tests/Comparers/SortComparerZeroLastTests.cs @@ -0,0 +1,17 @@ +using System.Linq; +using API.Comparators; +using Xunit; + +namespace API.Tests.Comparers; + +public class SortComparerZeroLastTests +{ + [Theory] + [InlineData(new[] {0, 1, 2,}, new[] {1, 2, 0})] + [InlineData(new[] {3, 1, 2}, new[] {1, 2, 3})] + [InlineData(new[] {0, 0, 1}, new[] {1, 0, 0})] + public void SortComparerZeroLastTest(int[] input, int[] expected) + { + Assert.Equal(expected, input.OrderBy(f => f, new SortComparerZeroLast()).ToArray()); + } +} diff --git a/API.Tests/Services/ReaderServiceTests.cs b/API.Tests/Services/ReaderServiceTests.cs index 968eada19..8439c69d3 100644 --- a/API.Tests/Services/ReaderServiceTests.cs +++ b/API.Tests/Services/ReaderServiceTests.cs @@ -1625,6 +1625,11 @@ public class ReaderServiceTests EntityFactory.CreateChapter("2", false, new List(), 1), EntityFactory.CreateChapter("3", false, new List(), 1), }), + EntityFactory.CreateVolume("1", new List() + { + EntityFactory.CreateChapter("11", false, new List(), 1), + EntityFactory.CreateChapter("22", false, new List(), 1), + }), } }); @@ -1638,33 +1643,13 @@ public class ReaderServiceTests var readerService = new ReaderService(_unitOfWork, Substitute.For>()); // Save progress on first volume chapters and 1st of second volume - 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 = 1 - }, 1); - + var user = await _unitOfWork.UserRepository.GetUserByIdAsync(1, AppUserIncludes.Progress); + await readerService.MarkSeriesAsRead(user, 1); await _context.SaveChangesAsync(); var nextChapter = await readerService.GetContinuePoint(1, 1); - Assert.Equal("1", nextChapter.Range); + Assert.Equal("11", nextChapter.Range); } [Fact] diff --git a/API/Comparators/ChapterSortComparer.cs b/API/Comparators/ChapterSortComparer.cs index 189919a33..0e7f61a61 100644 --- a/API/Comparators/ChapterSortComparer.cs +++ b/API/Comparators/ChapterSortComparer.cs @@ -45,4 +45,18 @@ namespace API.Comparators return x.CompareTo(y); } } + + public class SortComparerZeroLast : IComparer + { + public int Compare(double x, double y) + { + if (x == 0.0 && y == 0.0) return 0; + // if x is 0, it comes last + if (x == 0.0) return 1; + // if y is 0, it comes last + if (y == 0.0) return -1; + + return x.CompareTo(y); + } + } } diff --git a/API/Services/ReaderService.cs b/API/Services/ReaderService.cs index 0c989900e..ab586486d 100644 --- a/API/Services/ReaderService.cs +++ b/API/Services/ReaderService.cs @@ -403,37 +403,36 @@ public class ReaderService : IReaderService .ToList(); // If there are any volumes that have progress, return those. If not, move on. - var currentlyReadingChapter = volumeChapters.FirstOrDefault(chapter => chapter.PagesRead < chapter.Pages); // (removed for GetContinuePoint_ShouldReturnFirstVolumeChapter_WhenPreExistingProgress), not sure if needed && chapter.PagesRead > 0 + var currentlyReadingChapter = volumeChapters.FirstOrDefault(chapter => chapter.PagesRead < chapter.Pages); if (currentlyReadingChapter != null) return currentlyReadingChapter; - // Check loose leaf chapters (and specials). First check if there are any - var volume = volumes.SingleOrDefault(v => v.Number == 0); - return FindNextReadingChapter(volume == null ? volumeChapters : volume.Chapters.OrderBy(c => float.Parse(c.Number)).ToList()); + // Order with volume 0 last so we prefer the natural order + return FindNextReadingChapter(volumes.OrderBy(v => v.Number, new SortComparerZeroLast()).SelectMany(v => v.Chapters).ToList()); } private static ChapterDto FindNextReadingChapter(IList volumeChapters) { var chaptersWithProgress = volumeChapters.Where(c => c.PagesRead > 0).ToList(); - if (chaptersWithProgress.Count > 0) + if (chaptersWithProgress.Count <= 0) return volumeChapters.First(); + + + var last = chaptersWithProgress.FindLastIndex(c => c.PagesRead > 0); + if (last + 1 < chaptersWithProgress.Count) { - var last = chaptersWithProgress.FindLastIndex(c => c.PagesRead > 0); - if (last + 1 < chaptersWithProgress.Count) - { - return chaptersWithProgress.ElementAt(last + 1); - } + return chaptersWithProgress.ElementAt(last + 1); + } - var lastChapter = chaptersWithProgress.ElementAt(last); - if (lastChapter.PagesRead < lastChapter.Pages) - { - return chaptersWithProgress.ElementAt(last); - } + var lastChapter = chaptersWithProgress.ElementAt(last); + if (lastChapter.PagesRead < lastChapter.Pages) + { + return chaptersWithProgress.ElementAt(last); + } - // 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) - { - return volumeChapters.ElementAt(lastIndexWithProgress + 1); - } + // 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) + { + return volumeChapters.ElementAt(lastIndexWithProgress + 1); } return volumeChapters.First(); diff --git a/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts b/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts index 8953578dd..395ce39fb 100644 --- a/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts +++ b/UI/Web/src/app/book-reader/book-reader/book-reader.component.ts @@ -740,7 +740,8 @@ export class BookReaderComponent implements OnInit, AfterViewInit, OnDestroy { setTimeout(() => { this.addLinkClickHandlers(); this.updateReaderStyles(); - this.topOffset = this.stickyTopElemRef.nativeElement?.offsetHeight; + // We need to get the offset after we ensure the title has rendered + requestAnimationFrame(() => this.topOffset = this.stickyTopElemRef.nativeElement?.getBoundingClientRect().height); const imgs = this.readingSectionElemRef.nativeElement.querySelectorAll('img'); if (imgs === null || imgs.length === 0) { diff --git a/UI/Web/src/app/series-detail/series-detail.component.html b/UI/Web/src/app/series-detail/series-detail.component.html index a7af84f15..c9749f5f7 100644 --- a/UI/Web/src/app/series-detail/series-detail.component.html +++ b/UI/Web/src/app/series-detail/series-detail.component.html @@ -31,6 +31,12 @@ +
+
+ +
+
+