From 35d8c75aa0cab06573e077874f95b21ee6b766a7 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 (cherry picked from commit 6d538db5f2afc45976a65ae5d202a490d2e08352) # Conflicts: # app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt # app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterGetNextUnread.kt --- .../domain/chapter/model/ChapterFilter.kt | 4 +- .../kanade/presentation/manga/MangaScreen.kt | 214 +++++++++++------- .../tachiyomi/ui/manga/MangaScreenModel.kt | 101 ++++++--- .../util/chapter/ChapterGetNextUnread.kt | 4 +- 4 files changed, 202 insertions(+), 121 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 8398b4bc1..b4802c9a4 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 exh.md.utils.MdUtil import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.service.getChapterSort @@ -44,7 +44,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 887f989d5..b60cf527b 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 @@ -73,7 +78,7 @@ import eu.kanade.tachiyomi.source.online.english.EightMuses import eu.kanade.tachiyomi.source.online.english.HBrowse import eu.kanade.tachiyomi.source.online.english.Pururin import eu.kanade.tachiyomi.source.online.english.Tsumino -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.ui.manga.PagePreviewState import eu.kanade.tachiyomi.util.lang.toRelativeString @@ -99,8 +104,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 @@ -116,7 +123,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)?, @@ -157,10 +164,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, ) { @@ -279,7 +286,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)?, @@ -321,16 +328,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 } // SY --> val metadataDescription = metadataDescription(state.source) // SY <-- @@ -574,7 +582,8 @@ private fun MangaScreenSmallImpl( sharedChapterItems( manga = state.manga, - chapters = chapters, + chapters = listItem, + isAnyChapterSelected = chapters.fastAny { it.selected }, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, chapterSwipeStartAction = chapterSwipeStartAction, @@ -604,7 +613,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)?, @@ -646,10 +655,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, ) { @@ -657,6 +666,7 @@ fun MangaScreenLargeImpl( val density = LocalDensity.current val chapters = remember(state) { state.processedChapters } + val listItem = remember(state) { state.chapterListItems } val isAnySelected by remember { derivedStateOf { @@ -872,7 +882,8 @@ fun MangaScreenLargeImpl( sharedChapterItems( manga = state.manga, - chapters = chapters, + chapters = listItem, + isAnyChapterSelected = chapters.fastAny { it.selected }, dateRelativeTime = dateRelativeTime, dateFormat = dateFormat, chapterSwipeStartAction = chapterSwipeStartAction, @@ -895,12 +906,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, ) { @@ -937,7 +948,8 @@ private fun SharedMangaBottomActionMenu( private fun LazyListScope.sharedChapterItems( manga: Manga, - chapters: List, + chapters: List, + isAnyChapterSelected: Boolean, dateRelativeTime: Boolean, dateFormat: DateFormat, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, @@ -946,95 +958,125 @@ private fun LazyListScope.sharedChapterItems( alwaysShowReadingProgress: Boolean, // SY <-- 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 { - // SY --> - if (manga.isEhBasedManga()) { - MetadataUtil.EX_DATE_FORMAT.format(Date(it)) - } else { - Date(it).toRelativeString( - context, - dateRelativeTime, - dateFormat, - ) - } - // SY <-- - }, - readProgress = chapterItem.chapter.lastPageRead - .takeIf { /* SY --> */(!chapterItem.chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0L } - ?.let { - stringResource( - R.string.chapter_progress, - it + 1, + 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(), ) - }, - scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() /* SY --> */ && chapterItem.showScanlator /* SY <-- */ }, - // SY --> - sourceName = chapterItem.sourceName, - // SY <-- - 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 { + // SY --> + if (manga.isEhBasedManga()) { + MetadataUtil.EX_DATE_FORMAT.format(Date(it)) + } else { + Date(it).toRelativeString( + context, + dateRelativeTime, + dateFormat, + ) + } + // SY <-- + }, + readProgress = item.chapter.lastPageRead + .takeIf { /* SY --> */(!item.chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0L } + ?.let { + stringResource( + R.string.chapter_progress, + it + 1, + ) + }, + scanlator = item.chapter.scanlator.takeIf { !it.isNullOrBlank() /* SY --> */ && item.showScanlator /* SY <-- */ }, + // SY --> + sourceName = item.sourceName, + // SY <-- + 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 37a5fdec7..9083ba2e1 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.GetPagePreviews @@ -88,6 +89,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 @@ -121,6 +123,7 @@ import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy +import kotlin.math.floor class MangaScreenModel( val context: Context, @@ -182,10 +185,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() @@ -300,7 +303,7 @@ class MangaScreenModel( .combine(downloadManager.queueState) { state, _ -> state } // SY <-- .collectLatest { (manga, chapters /* SY --> */, flatMetadata, mergedData /* SY <-- */) -> - val chapterItems = chapters.toChapterItems(manga /* SY --> */, mergedData /* SY <-- */) + val chapterItems = chapters.toChapterListItems(manga /* SY --> */, mergedData /* SY <-- */) updateSuccessState { it.copy( manga = manga, @@ -321,7 +324,7 @@ class MangaScreenModel( val manga = getMangaAndChapters.awaitManga(mangaId) // SY --> val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChaptersByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId)) - .toChapterItems(manga, null) + .toChapterListItems(manga, null) val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references -> MergedMangaData( references, @@ -889,7 +892,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 { @@ -901,10 +904,10 @@ class MangaScreenModel( } } - private fun List.toChapterItems( + private fun List.toChapterListItems( manga: Manga, mergedData: MergedMangaData?, - ): List { + ): List { val isLocal = manga.isLocal() // SY --> val isExhManga = manga.isEhBasedManga() @@ -939,7 +942,7 @@ class MangaScreenModel( else -> Download.State.NOT_DOWNLOADED } - ChapterItem( + ChapterList.Item( chapter = chapter, downloadState = downloadState, downloadProgress = activeDownload?.progress ?: 0, @@ -1013,7 +1016,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) } @@ -1023,7 +1026,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 @@ -1110,7 +1113,7 @@ class MangaScreenModel( } fun runChapterDownloadActions( - items: List, + items: List, action: ChapterDownloadAction, ) { when (action) { @@ -1125,7 +1128,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 -> { @@ -1344,14 +1347,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) @@ -1359,7 +1362,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) { @@ -1382,7 +1385,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)) } } @@ -1410,7 +1413,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 @@ -1422,7 +1425,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 @@ -1559,7 +1562,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: MangaScreenModel.Dialog? = null, @@ -1580,6 +1583,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() @@ -1590,7 +1620,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 @@ -1617,18 +1647,27 @@ data class MergedMangaData( ) @Immutable -data class ChapterItem( - val chapter: Chapter, - val downloadState: Download.State, - val downloadProgress: Int, - val selected: Boolean = false, +sealed class ChapterList { + @Immutable + data class MissingCount( + val id: String, + val count: Int, + ) : ChapterList() - // SY --> - val sourceName: String?, - val showScanlator: Boolean, - // SY <-- -) { - val isDownloaded = downloadState == Download.State.DOWNLOADED + @Immutable + data class Item( + val chapter: Chapter, + val downloadState: Download.State, + val downloadProgress: Int, + val selected: Boolean = false, + // SY --> + val sourceName: String?, + val showScanlator: Boolean, + // SY <-- + ) : ChapterList() { + val id = chapter.id + val isDownloaded = downloadState == Download.State.DOWNLOADED + } } // SY --> 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 eed60cc6e..ca3882e6f 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 exh.source.isEhBasedManga import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.Manga @@ -32,7 +32,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 -> // SY --> if (manga.isEhBasedManga()) {