From 6d538db5f2afc45976a65ae5d202a490d2e08352 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Thu, 2 Nov 2023 08:18:19 +0600 Subject: [PATCH] Show missing chapter count between two chapters in chapter list (#10096) * Show missing chapter count between two chapters in chapter list Closes #8460 * Fix crash * Lint * Review changes * Lint --- .../domain/chapter/model/ChapterFilter.kt | 4 +- .../kanade/presentation/manga/MangaScreen.kt | 196 +++++++++++------- .../tachiyomi/ui/manga/MangaScreenModel.kt | 92 +++++--- .../util/chapter/ChapterGetNextUnread.kt | 4 +- 4 files changed, 189 insertions(+), 107 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt index 623ddd7516..6ddf2f33fe 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt @@ -2,7 +2,7 @@ package eu.kanade.domain.chapter.model import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.tachiyomi.data.download.DownloadManager -import eu.kanade.tachiyomi.ui.manga.ChapterItem +import eu.kanade.tachiyomi.ui.manga.ChapterList import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.service.getChapterSort import tachiyomi.domain.manga.model.Manga @@ -34,7 +34,7 @@ fun List.applyFilters(manga: Manga, downloadManager: DownloadManager): * Applies the view filters to the list of chapters obtained from the database. * @return an observable of the list of chapters filtered and sorted. */ -fun List.applyFilters(manga: Manga): Sequence { +fun List.applyFilters(manga: Manga): Sequence { val isLocalManga = manga.isLocal() val unreadFilter = manga.unreadFilter val downloadedFilter = manga.downloadedFilter diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 82c266f655..83b35bd465 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -5,9 +5,11 @@ import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.asPaddingValues @@ -26,7 +28,9 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text @@ -44,6 +48,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalLayoutDirection +import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastAny @@ -61,7 +66,7 @@ import eu.kanade.presentation.util.formatChapterNumber import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.source.getNameForMangaInfo -import eu.kanade.tachiyomi.ui.manga.ChapterItem +import eu.kanade.tachiyomi.ui.manga.ChapterList import eu.kanade.tachiyomi.ui.manga.MangaScreenModel import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.system.copyToClipboard @@ -75,8 +80,10 @@ import tachiyomi.presentation.core.components.VerticalFastScroller import tachiyomi.presentation.core.components.material.ExtendedFloatingActionButton import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.components.material.padding import tachiyomi.presentation.core.util.isScrolledToEnd import tachiyomi.presentation.core.util.isScrollingUp +import tachiyomi.presentation.core.util.secondaryItemAlpha import java.text.DateFormat import java.util.Date @@ -92,7 +99,7 @@ fun MangaScreen( chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, - onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, + onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, @@ -123,10 +130,10 @@ fun MangaScreen( onMultiDeleteClicked: (List) -> Unit, // For chapter swipe - onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, + onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection - onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, + onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) { @@ -225,7 +232,7 @@ private fun MangaScreenSmallImpl( chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, - onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, + onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, @@ -257,16 +264,17 @@ private fun MangaScreenSmallImpl( onMultiDeleteClicked: (List) -> Unit, // For chapter swipe - onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, + onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection - onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, + onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) { val chapterListState = rememberLazyListState() val chapters = remember(state) { state.processedChapters } + val listItem = remember(state) { state.chapterListItems } val isAnySelected by remember { derivedStateOf { @@ -447,7 +455,8 @@ private fun MangaScreenSmallImpl( sharedChapterItems( manga = state.manga, - chapters = chapters, + chapters = listItem, + isAnyChapterSelected = chapters.fastAny { it.selected }, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, chapterSwipeStartAction = chapterSwipeStartAction, @@ -474,7 +483,7 @@ fun MangaScreenLargeImpl( chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onBackClicked: () -> Unit, onChapterClicked: (Chapter) -> Unit, - onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, + onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, onAddToLibraryClicked: () -> Unit, onWebViewClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?, @@ -506,10 +515,10 @@ fun MangaScreenLargeImpl( onMultiDeleteClicked: (List) -> Unit, // For swipe actions - onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, + onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, // Chapter selection - onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, + onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit, onInvertSelection: () -> Unit, ) { @@ -517,6 +526,7 @@ fun MangaScreenLargeImpl( val density = LocalDensity.current val chapters = remember(state) { state.processedChapters } + val listItem = remember(state) { state.chapterListItems } val isAnySelected by remember { derivedStateOf { @@ -688,7 +698,8 @@ fun MangaScreenLargeImpl( sharedChapterItems( manga = state.manga, - chapters = chapters, + chapters = listItem, + isAnyChapterSelected = chapters.fastAny { it.selected }, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, chapterSwipeStartAction = chapterSwipeStartAction, @@ -708,12 +719,12 @@ fun MangaScreenLargeImpl( @Composable private fun SharedMangaBottomActionMenu( - selected: List, + selected: List, modifier: Modifier = Modifier, onMultiBookmarkClicked: (List, bookmarked: Boolean) -> Unit, onMultiMarkAsReadClicked: (List, markAsRead: Boolean) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit, - onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, + onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, onMultiDeleteClicked: (List) -> Unit, fillFraction: Float, ) { @@ -750,92 +761,123 @@ private fun SharedMangaBottomActionMenu( private fun LazyListScope.sharedChapterItems( manga: Manga, - chapters: List, + chapters: List, + isAnyChapterSelected: Boolean, dateRelativeTime: Boolean, dateFormat: DateFormat, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, onChapterClicked: (Chapter) -> Unit, - onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, - onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, - onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, + onDownloadChapter: ((List, ChapterDownloadAction) -> Unit)?, + onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit, + onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit, ) { items( items = chapters, - key = { "chapter-${it.chapter.id}" }, + key = { item -> + when (item) { + is ChapterList.MissingCount -> "missing-count-${item.id}" + is ChapterList.Item -> "chapter-${item.id}" + } + }, contentType = { MangaScreenItem.CHAPTER }, - ) { chapterItem -> + ) { item -> val haptic = LocalHapticFeedback.current val context = LocalContext.current - MangaChapterListItem( - title = if (manga.displayMode == Manga.CHAPTER_DISPLAY_NUMBER) { - stringResource( - R.string.display_mode_chapter, - formatChapterNumber(chapterItem.chapter.chapterNumber), - ) - } else { - chapterItem.chapter.name - }, - date = chapterItem.chapter.dateUpload - .takeIf { it > 0L } - ?.let { - Date(it).toRelativeString( - context, - dateRelativeTime, - dateFormat, + when (item) { + is ChapterList.MissingCount -> { + Row( + modifier = Modifier.padding( + horizontal = MaterialTheme.padding.medium, + vertical = MaterialTheme.padding.small, + ), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium), + ) { + HorizontalDivider(modifier = Modifier.weight(1f)) + Text( + text = pluralStringResource( + id = R.plurals.missing_chapters, + count = item.count, + item.count, + ), + modifier = Modifier.secondaryItemAlpha(), ) - }, - readProgress = chapterItem.chapter.lastPageRead - .takeIf { !chapterItem.chapter.read && it > 0L } - ?.let { - stringResource( - R.string.chapter_progress, - it + 1, - ) - }, - scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() }, - read = chapterItem.chapter.read, - bookmark = chapterItem.chapter.bookmark, - selected = chapterItem.selected, - downloadIndicatorEnabled = chapters.fastAll { !it.selected }, - downloadStateProvider = { chapterItem.downloadState }, - downloadProgressProvider = { chapterItem.downloadProgress }, - chapterSwipeStartAction = chapterSwipeStartAction, - chapterSwipeEndAction = chapterSwipeEndAction, - onLongClick = { - onChapterSelected(chapterItem, !chapterItem.selected, true, true) - haptic.performHapticFeedback(HapticFeedbackType.LongPress) - }, - onClick = { - onChapterItemClick( - chapterItem = chapterItem, - chapters = chapters, - onToggleSelection = { onChapterSelected(chapterItem, !chapterItem.selected, true, false) }, - onChapterClicked = onChapterClicked, + HorizontalDivider(modifier = Modifier.weight(1f)) + } + } + is ChapterList.Item -> { + MangaChapterListItem( + title = if (manga.displayMode == Manga.CHAPTER_DISPLAY_NUMBER) { + stringResource( + R.string.display_mode_chapter, + formatChapterNumber(item.chapter.chapterNumber), + ) + } else { + item.chapter.name + }, + date = item.chapter.dateUpload + .takeIf { it > 0L } + ?.let { + Date(it).toRelativeString( + context, + dateRelativeTime, + dateFormat, + ) + }, + readProgress = item.chapter.lastPageRead + .takeIf { !item.chapter.read && it > 0L } + ?.let { + stringResource( + R.string.chapter_progress, + it + 1, + ) + }, + scanlator = item.chapter.scanlator.takeIf { !it.isNullOrBlank() }, + read = item.chapter.read, + bookmark = item.chapter.bookmark, + selected = item.selected, + downloadIndicatorEnabled = !isAnyChapterSelected, + downloadStateProvider = { item.downloadState }, + downloadProgressProvider = { item.downloadProgress }, + chapterSwipeStartAction = chapterSwipeStartAction, + chapterSwipeEndAction = chapterSwipeEndAction, + onLongClick = { + onChapterSelected(item, !item.selected, true, true) + haptic.performHapticFeedback(HapticFeedbackType.LongPress) + }, + onClick = { + onChapterItemClick( + chapterItem = item, + isAnyChapterSelected = isAnyChapterSelected, + onToggleSelection = { onChapterSelected(item, !item.selected, true, false) }, + onChapterClicked = onChapterClicked, + ) + }, + onDownloadClick = if (onDownloadChapter != null) { + { onDownloadChapter(listOf(item), it) } + } else { + null + }, + onChapterSwipe = { + onChapterSwipe(item, it) + }, ) - }, - onDownloadClick = if (onDownloadChapter != null) { - { onDownloadChapter(listOf(chapterItem), it) } - } else { - null - }, - onChapterSwipe = { - onChapterSwipe(chapterItem, it) - }, - ) + } + } } } private fun onChapterItemClick( - chapterItem: ChapterItem, - chapters: List, + chapterItem: ChapterList.Item, + isAnyChapterSelected: Boolean, onToggleSelection: (Boolean) -> Unit, onChapterClicked: (Chapter) -> Unit, ) { when { chapterItem.selected -> onToggleSelection(false) - chapters.fastAny { it.selected } -> onToggleSelection(true) + isAnyChapterSelected -> onToggleSelection(true) else -> onChapterClicked(chapterItem.chapter) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index a4f6f5d957..4fa4baba5d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -10,6 +10,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.preference.asState import eu.kanade.core.util.addOrRemove +import eu.kanade.core.util.insertSeparators import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.manga.interactor.UpdateManga @@ -61,6 +62,7 @@ import tachiyomi.domain.chapter.interactor.UpdateChapter import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.model.NoChaptersException +import tachiyomi.domain.chapter.service.calculateChapterGap import tachiyomi.domain.chapter.service.getChapterSort import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.library.service.LibraryPreferences @@ -75,6 +77,7 @@ import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import kotlin.math.floor class MangaScreenModel( val context: Context, @@ -117,10 +120,10 @@ class MangaScreenModel( private val isFavorited: Boolean get() = manga?.favorite ?: false - private val allChapters: List? + private val allChapters: List? get() = successState?.chapters - private val filteredChapters: List? + private val filteredChapters: List? get() = successState?.processedChapters val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get() @@ -158,7 +161,7 @@ class MangaScreenModel( updateSuccessState { it.copy( manga = manga, - chapters = chapters.toChapterItems(manga), + chapters = chapters.toChapterListItems(manga), ) } } @@ -169,7 +172,7 @@ class MangaScreenModel( screenModelScope.launchIO { val manga = getMangaAndChapters.awaitManga(mangaId) val chapters = getMangaAndChapters.awaitChapters(mangaId) - .toChapterItems(manga) + .toChapterListItems(manga) if (!manga.favorite) { setMangaDefaultChapterFlags.await(manga) @@ -455,7 +458,7 @@ class MangaScreenModel( private fun updateDownloadState(download: Download) { updateSuccessState { successState -> - val modifiedIndex = successState.chapters.indexOfFirst { it.chapter.id == download.chapter.id } + val modifiedIndex = successState.chapters.indexOfFirst { it.id == download.chapter.id } if (modifiedIndex < 0) return@updateSuccessState successState val newChapters = successState.chapters.toMutableList().apply { @@ -467,7 +470,7 @@ class MangaScreenModel( } } - private fun List.toChapterItems(manga: Manga): List { + private fun List.toChapterListItems(manga: Manga): List { val isLocal = manga.isLocal() return map { chapter -> val activeDownload = if (isLocal) { @@ -486,7 +489,7 @@ class MangaScreenModel( else -> Download.State.NOT_DOWNLOADED } - ChapterItem( + ChapterList.Item( chapter = chapter, downloadState = downloadState, downloadProgress = activeDownload?.progress ?: 0, @@ -534,7 +537,7 @@ class MangaScreenModel( /** * @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled] */ - fun chapterSwipe(chapterItem: ChapterItem, swipeAction: LibraryPreferences.ChapterSwipeAction) { + fun chapterSwipe(chapterItem: ChapterList.Item, swipeAction: LibraryPreferences.ChapterSwipeAction) { screenModelScope.launch { executeChapterSwipeAction(chapterItem, swipeAction) } @@ -544,7 +547,7 @@ class MangaScreenModel( * @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled] */ private fun executeChapterSwipeAction( - chapterItem: ChapterItem, + chapterItem: ChapterList.Item, swipeAction: LibraryPreferences.ChapterSwipeAction, ) { val chapter = chapterItem.chapter @@ -626,7 +629,7 @@ class MangaScreenModel( } fun runChapterDownloadActions( - items: List, + items: List, action: ChapterDownloadAction, ) { when (action) { @@ -641,7 +644,7 @@ class MangaScreenModel( startDownload(listOf(chapter), true) } ChapterDownloadAction.CANCEL -> { - val chapterId = items.singleOrNull()?.chapter?.id ?: return + val chapterId = items.singleOrNull()?.id ?: return cancelDownload(chapterId) } ChapterDownloadAction.DELETE -> { @@ -842,14 +845,14 @@ class MangaScreenModel( } fun toggleSelection( - item: ChapterItem, + item: ChapterList.Item, selected: Boolean, userSelected: Boolean = false, fromLongPress: Boolean = false, ) { updateSuccessState { successState -> val newChapters = successState.processedChapters.toMutableList().apply { - val selectedIndex = successState.processedChapters.indexOfFirst { it.chapter.id == item.chapter.id } + val selectedIndex = successState.processedChapters.indexOfFirst { it.id == item.chapter.id } if (selectedIndex < 0) return@apply val selectedItem = get(selectedIndex) @@ -857,7 +860,7 @@ class MangaScreenModel( val firstSelection = none { it.selected } set(selectedIndex, selectedItem.copy(selected = selected)) - selectedChapterIds.addOrRemove(item.chapter.id, selected) + selectedChapterIds.addOrRemove(item.id, selected) if (selected && userSelected && fromLongPress) { if (firstSelection) { @@ -880,7 +883,7 @@ class MangaScreenModel( range.forEach { val inbetweenItem = get(it) if (!inbetweenItem.selected) { - selectedChapterIds.add(inbetweenItem.chapter.id) + selectedChapterIds.add(inbetweenItem.id) set(it, inbetweenItem.copy(selected = true)) } } @@ -908,7 +911,7 @@ class MangaScreenModel( fun toggleAllSelection(selected: Boolean) { updateSuccessState { successState -> val newChapters = successState.chapters.map { - selectedChapterIds.addOrRemove(it.chapter.id, selected) + selectedChapterIds.addOrRemove(it.id, selected) it.copy(selected = selected) } selectedPositions[0] = -1 @@ -920,7 +923,7 @@ class MangaScreenModel( fun invertSelection() { updateSuccessState { successState -> val newChapters = successState.chapters.map { - selectedChapterIds.addOrRemove(it.chapter.id, !it.selected) + selectedChapterIds.addOrRemove(it.id, !it.selected) it.copy(selected = !it.selected) } selectedPositions[0] = -1 @@ -994,7 +997,7 @@ class MangaScreenModel( val manga: Manga, val source: Source, val isFromSource: Boolean, - val chapters: List, + val chapters: List, val trackItems: List = emptyList(), val isRefreshingData: Boolean = false, val dialog: Dialog? = null, @@ -1005,6 +1008,33 @@ class MangaScreenModel( chapters.applyFilters(manga).toList() } + val chapterListItems by lazy { + processedChapters.insertSeparators { before, after -> + val (lowerChapter, higherChapter) = if (manga.sortDescending()) { + after to before + } else { + before to after + } + if (higherChapter == null) return@insertSeparators null + + if (lowerChapter == null) { + floor(higherChapter.chapter.chapterNumber) + .toInt() + .minus(1) + .coerceAtLeast(0) + } else { + calculateChapterGap(higherChapter.chapter, lowerChapter.chapter) + } + .takeIf { it > 0 } + ?.let { missingCount -> + ChapterList.MissingCount( + id = "${lowerChapter?.id}-${higherChapter.id}", + count = missingCount, + ) + } + } + } + val trackingAvailable: Boolean get() = trackItems.isNotEmpty() @@ -1015,7 +1045,7 @@ class MangaScreenModel( * Applies the view filters to the list of chapters obtained from the database. * @return an observable of the list of chapters filtered and sorted. */ - private fun List.applyFilters(manga: Manga): Sequence { + private fun List.applyFilters(manga: Manga): Sequence { val isLocalManga = manga.isLocal() val unreadFilter = manga.unreadFilter val downloadedFilter = manga.downloadedFilter @@ -1031,11 +1061,21 @@ class MangaScreenModel( } @Immutable -data class ChapterItem( - val chapter: Chapter, - val downloadState: Download.State, - val downloadProgress: Int, - val selected: Boolean = false, -) { - val isDownloaded = downloadState == Download.State.DOWNLOADED +sealed class ChapterList { + @Immutable + data class MissingCount( + val id: String, + val count: Int, + ) : ChapterList() + + @Immutable + data class Item( + val chapter: Chapter, + val downloadState: Download.State, + val downloadProgress: Int, + val selected: Boolean = false, + ) : ChapterList() { + val id = chapter.id + val isDownloaded = downloadState == Download.State.DOWNLOADED + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt index 418220a461..a7accb08e6 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt @@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.util.chapter import eu.kanade.domain.chapter.model.applyFilters import eu.kanade.tachiyomi.data.download.DownloadManager -import eu.kanade.tachiyomi.ui.manga.ChapterItem +import eu.kanade.tachiyomi.ui.manga.ChapterList import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga @@ -22,7 +22,7 @@ fun List.getNextUnread(manga: Manga, downloadManager: DownloadManager): /** * Gets next unread chapter with filters and sorting applied */ -fun List.getNextUnread(manga: Manga): Chapter? { +fun List.getNextUnread(manga: Manga): Chapter? { return applyFilters(manga).let { chapters -> if (manga.sortDescending()) { chapters.findLast { !it.chapter.read }