mirror of
				https://github.com/mihonapp/mihon.git
				synced 2025-10-31 14:27:57 +01:00 
			
		
		
		
	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
This commit is contained in:
		| @@ -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<Chapter>.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<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> { | ||||
| fun List<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item> { | ||||
|     val isLocalManga = manga.isLocal() | ||||
|     val unreadFilter = manga.unreadFilter | ||||
|     val downloadedFilter = manga.downloadedFilter | ||||
|   | ||||
| @@ -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<ChapterItem>, ChapterDownloadAction) -> Unit)?, | ||||
|     onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, | ||||
|     onAddToLibraryClicked: () -> Unit, | ||||
|     onWebViewClicked: (() -> Unit)?, | ||||
|     onWebViewLongClicked: (() -> Unit)?, | ||||
| @@ -123,10 +130,10 @@ fun MangaScreen( | ||||
|     onMultiDeleteClicked: (List<Chapter>) -> 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<ChapterItem>, ChapterDownloadAction) -> Unit)?, | ||||
|     onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, | ||||
|     onAddToLibraryClicked: () -> Unit, | ||||
|     onWebViewClicked: (() -> Unit)?, | ||||
|     onWebViewLongClicked: (() -> Unit)?, | ||||
| @@ -257,16 +264,17 @@ private fun MangaScreenSmallImpl( | ||||
|     onMultiDeleteClicked: (List<Chapter>) -> 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<ChapterItem>, ChapterDownloadAction) -> Unit)?, | ||||
|     onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, | ||||
|     onAddToLibraryClicked: () -> Unit, | ||||
|     onWebViewClicked: (() -> Unit)?, | ||||
|     onWebViewLongClicked: (() -> Unit)?, | ||||
| @@ -506,10 +515,10 @@ fun MangaScreenLargeImpl( | ||||
|     onMultiDeleteClicked: (List<Chapter>) -> 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<ChapterItem>, | ||||
|     selected: List<ChapterList.Item>, | ||||
|     modifier: Modifier = Modifier, | ||||
|     onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, | ||||
|     onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, | ||||
|     onMarkPreviousAsReadClicked: (Chapter) -> Unit, | ||||
|     onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, | ||||
|     onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?, | ||||
|     onMultiDeleteClicked: (List<Chapter>) -> Unit, | ||||
|     fillFraction: Float, | ||||
| ) { | ||||
| @@ -750,92 +761,123 @@ private fun SharedMangaBottomActionMenu( | ||||
|  | ||||
| private fun LazyListScope.sharedChapterItems( | ||||
|     manga: Manga, | ||||
|     chapters: List<ChapterItem>, | ||||
|     chapters: List<ChapterList>, | ||||
|     isAnyChapterSelected: Boolean, | ||||
|     dateRelativeTime: Boolean, | ||||
|     dateFormat: DateFormat, | ||||
|     chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, | ||||
|     chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, | ||||
|     onChapterClicked: (Chapter) -> Unit, | ||||
|     onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, | ||||
|     onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, | ||||
|     onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, | ||||
|     onDownloadChapter: ((List<ChapterList.Item>, 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>, | ||||
|     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) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<ChapterItem>? | ||||
|     private val allChapters: List<ChapterList.Item>? | ||||
|         get() = successState?.chapters | ||||
|  | ||||
|     private val filteredChapters: List<ChapterItem>? | ||||
|     private val filteredChapters: List<ChapterList.Item>? | ||||
|         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<Chapter>.toChapterItems(manga: Manga): List<ChapterItem> { | ||||
|     private fun List<Chapter>.toChapterListItems(manga: Manga): List<ChapterList.Item> { | ||||
|         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<ChapterItem>, | ||||
|         items: List<ChapterList.Item>, | ||||
|         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<ChapterItem>, | ||||
|             val chapters: List<ChapterList.Item>, | ||||
|             val trackItems: List<TrackItem> = 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<ChapterItem>.applyFilters(manga: Manga): Sequence<ChapterItem> { | ||||
|             private fun List<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item> { | ||||
|                 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 | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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<Chapter>.getNextUnread(manga: Manga, downloadManager: DownloadManager): | ||||
| /** | ||||
|  * Gets next unread chapter with filters and sorting applied | ||||
|  */ | ||||
| fun List<ChapterItem>.getNextUnread(manga: Manga): Chapter? { | ||||
| fun List<ChapterList.Item>.getNextUnread(manga: Manga): Chapter? { | ||||
|     return applyFilters(manga).let { chapters -> | ||||
|         if (manga.sortDescending()) { | ||||
|             chapters.findLast { !it.chapter.read } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user