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
This commit is contained in:
AntsyLich 2023-11-02 08:18:19 +06:00 committed by Jobobby04
parent aa2afd1402
commit 35d8c75aa0
4 changed files with 202 additions and 121 deletions

View File

@ -2,7 +2,7 @@ package eu.kanade.domain.chapter.model
import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.domain.manga.model.downloadedFilter
import eu.kanade.tachiyomi.data.download.DownloadManager 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 exh.md.utils.MdUtil
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.service.getChapterSort import tachiyomi.domain.chapter.service.getChapterSort
@ -44,7 +44,7 @@ fun List<Chapter>.applyFilters(manga: Manga, downloadManager: DownloadManager/*
* Applies the view filters to the list of chapters obtained from the database. * Applies the view filters to the list of chapters obtained from the database.
* @return an observable of the list of chapters filtered and sorted. * @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 isLocalManga = manga.isLocal()
val unreadFilter = manga.unreadFilter val unreadFilter = manga.unreadFilter
val downloadedFilter = manga.downloadedFilter val downloadedFilter = manga.downloadedFilter

View File

@ -5,9 +5,11 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues import androidx.compose.foundation.layout.asPaddingValues
@ -26,7 +28,9 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text 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.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastAll
import androidx.compose.ui.util.fastAny 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.HBrowse
import eu.kanade.tachiyomi.source.online.english.Pururin import eu.kanade.tachiyomi.source.online.english.Pururin
import eu.kanade.tachiyomi.source.online.english.Tsumino 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.MangaScreenModel
import eu.kanade.tachiyomi.ui.manga.PagePreviewState import eu.kanade.tachiyomi.ui.manga.PagePreviewState
import eu.kanade.tachiyomi.util.lang.toRelativeString 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.ExtendedFloatingActionButton
import tachiyomi.presentation.core.components.material.PullRefresh import tachiyomi.presentation.core.components.material.PullRefresh
import tachiyomi.presentation.core.components.material.Scaffold 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.isScrolledToEnd
import tachiyomi.presentation.core.util.isScrollingUp import tachiyomi.presentation.core.util.isScrollingUp
import tachiyomi.presentation.core.util.secondaryItemAlpha
import java.text.DateFormat import java.text.DateFormat
import java.util.Date import java.util.Date
@ -116,7 +123,7 @@ fun MangaScreen(
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit, onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
onAddToLibraryClicked: () -> Unit, onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?, onWebViewClicked: (() -> Unit)?,
onWebViewLongClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?,
@ -157,10 +164,10 @@ fun MangaScreen(
onMultiDeleteClicked: (List<Chapter>) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit,
// For chapter swipe // For chapter swipe
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
// Chapter selection // Chapter selection
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
@ -279,7 +286,7 @@ private fun MangaScreenSmallImpl(
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit, onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
onAddToLibraryClicked: () -> Unit, onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?, onWebViewClicked: (() -> Unit)?,
onWebViewLongClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?,
@ -321,16 +328,17 @@ private fun MangaScreenSmallImpl(
onMultiDeleteClicked: (List<Chapter>) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit,
// For chapter swipe // For chapter swipe
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
// Chapter selection // Chapter selection
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
val chapterListState = rememberLazyListState() val chapterListState = rememberLazyListState()
val chapters = remember(state) { state.processedChapters } val chapters = remember(state) { state.processedChapters }
val listItem = remember(state) { state.chapterListItems }
// SY --> // SY -->
val metadataDescription = metadataDescription(state.source) val metadataDescription = metadataDescription(state.source)
// SY <-- // SY <--
@ -574,7 +582,8 @@ private fun MangaScreenSmallImpl(
sharedChapterItems( sharedChapterItems(
manga = state.manga, manga = state.manga,
chapters = chapters, chapters = listItem,
isAnyChapterSelected = chapters.fastAny { it.selected },
dateRelativeTime = dateRelativeTime, dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat, dateFormat = dateFormat,
chapterSwipeStartAction = chapterSwipeStartAction, chapterSwipeStartAction = chapterSwipeStartAction,
@ -604,7 +613,7 @@ fun MangaScreenLargeImpl(
chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeEndAction: LibraryPreferences.ChapterSwipeAction,
onBackClicked: () -> Unit, onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
onAddToLibraryClicked: () -> Unit, onAddToLibraryClicked: () -> Unit,
onWebViewClicked: (() -> Unit)?, onWebViewClicked: (() -> Unit)?,
onWebViewLongClicked: (() -> Unit)?, onWebViewLongClicked: (() -> Unit)?,
@ -646,10 +655,10 @@ fun MangaScreenLargeImpl(
onMultiDeleteClicked: (List<Chapter>) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit,
// For swipe actions // For swipe actions
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
// Chapter selection // Chapter selection
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onAllChapterSelected: (Boolean) -> Unit, onAllChapterSelected: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
) { ) {
@ -657,6 +666,7 @@ fun MangaScreenLargeImpl(
val density = LocalDensity.current val density = LocalDensity.current
val chapters = remember(state) { state.processedChapters } val chapters = remember(state) { state.processedChapters }
val listItem = remember(state) { state.chapterListItems }
val isAnySelected by remember { val isAnySelected by remember {
derivedStateOf { derivedStateOf {
@ -872,7 +882,8 @@ fun MangaScreenLargeImpl(
sharedChapterItems( sharedChapterItems(
manga = state.manga, manga = state.manga,
chapters = chapters, chapters = listItem,
isAnyChapterSelected = chapters.fastAny { it.selected },
dateRelativeTime = dateRelativeTime, dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat, dateFormat = dateFormat,
chapterSwipeStartAction = chapterSwipeStartAction, chapterSwipeStartAction = chapterSwipeStartAction,
@ -895,12 +906,12 @@ fun MangaScreenLargeImpl(
@Composable @Composable
private fun SharedMangaBottomActionMenu( private fun SharedMangaBottomActionMenu(
selected: List<ChapterItem>, selected: List<ChapterList.Item>,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit, onMultiBookmarkClicked: (List<Chapter>, bookmarked: Boolean) -> Unit,
onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit, onMultiMarkAsReadClicked: (List<Chapter>, markAsRead: Boolean) -> Unit,
onMarkPreviousAsReadClicked: (Chapter) -> Unit, onMarkPreviousAsReadClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
onMultiDeleteClicked: (List<Chapter>) -> Unit, onMultiDeleteClicked: (List<Chapter>) -> Unit,
fillFraction: Float, fillFraction: Float,
) { ) {
@ -937,7 +948,8 @@ private fun SharedMangaBottomActionMenu(
private fun LazyListScope.sharedChapterItems( private fun LazyListScope.sharedChapterItems(
manga: Manga, manga: Manga,
chapters: List<ChapterItem>, chapters: List<ChapterList>,
isAnyChapterSelected: Boolean,
dateRelativeTime: Boolean, dateRelativeTime: Boolean,
dateFormat: DateFormat, dateFormat: DateFormat,
chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction, chapterSwipeStartAction: LibraryPreferences.ChapterSwipeAction,
@ -946,95 +958,125 @@ private fun LazyListScope.sharedChapterItems(
alwaysShowReadingProgress: Boolean, alwaysShowReadingProgress: Boolean,
// SY <-- // SY <--
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterList.Item>, ChapterDownloadAction) -> Unit)?,
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterList.Item, Boolean, Boolean, Boolean) -> Unit,
onChapterSwipe: (ChapterItem, LibraryPreferences.ChapterSwipeAction) -> Unit, onChapterSwipe: (ChapterList.Item, LibraryPreferences.ChapterSwipeAction) -> Unit,
) { ) {
items( items(
items = chapters, 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 }, contentType = { MangaScreenItem.CHAPTER },
) { chapterItem -> ) { item ->
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val context = LocalContext.current val context = LocalContext.current
MangaChapterListItem( when (item) {
title = if (manga.displayMode == Manga.CHAPTER_DISPLAY_NUMBER) { is ChapterList.MissingCount -> {
stringResource( Row(
R.string.display_mode_chapter, modifier = Modifier.padding(
formatChapterNumber(chapterItem.chapter.chapterNumber), horizontal = MaterialTheme.padding.medium,
) vertical = MaterialTheme.padding.small,
} else { ),
chapterItem.chapter.name verticalAlignment = Alignment.CenterVertically,
}, horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.medium),
date = chapterItem.chapter.dateUpload ) {
.takeIf { it > 0L } HorizontalDivider(modifier = Modifier.weight(1f))
?.let { Text(
// SY --> text = pluralStringResource(
if (manga.isEhBasedManga()) { id = R.plurals.missing_chapters,
MetadataUtil.EX_DATE_FORMAT.format(Date(it)) count = item.count,
} else { item.count,
Date(it).toRelativeString( ),
context, modifier = Modifier.secondaryItemAlpha(),
dateRelativeTime,
dateFormat,
)
}
// SY <--
},
readProgress = chapterItem.chapter.lastPageRead
.takeIf { /* SY --> */(!chapterItem.chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0L }
?.let {
stringResource(
R.string.chapter_progress,
it + 1,
) )
}, HorizontalDivider(modifier = Modifier.weight(1f))
scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() /* SY --> */ && chapterItem.showScanlator /* SY <-- */ }, }
// SY --> }
sourceName = chapterItem.sourceName, is ChapterList.Item -> {
// SY <-- MangaChapterListItem(
read = chapterItem.chapter.read, title = if (manga.displayMode == Manga.CHAPTER_DISPLAY_NUMBER) {
bookmark = chapterItem.chapter.bookmark, stringResource(
selected = chapterItem.selected, R.string.display_mode_chapter,
downloadIndicatorEnabled = chapters.fastAll { !it.selected }, formatChapterNumber(item.chapter.chapterNumber),
downloadStateProvider = { chapterItem.downloadState }, )
downloadProgressProvider = { chapterItem.downloadProgress }, } else {
chapterSwipeStartAction = chapterSwipeStartAction, item.chapter.name
chapterSwipeEndAction = chapterSwipeEndAction, },
onLongClick = { date = item.chapter.dateUpload
onChapterSelected(chapterItem, !chapterItem.selected, true, true) .takeIf { it > 0L }
haptic.performHapticFeedback(HapticFeedbackType.LongPress) ?.let {
}, // SY -->
onClick = { if (manga.isEhBasedManga()) {
onChapterItemClick( MetadataUtil.EX_DATE_FORMAT.format(Date(it))
chapterItem = chapterItem, } else {
chapters = chapters, Date(it).toRelativeString(
onToggleSelection = { onChapterSelected(chapterItem, !chapterItem.selected, true, false) }, context,
onChapterClicked = onChapterClicked, 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( private fun onChapterItemClick(
chapterItem: ChapterItem, chapterItem: ChapterList.Item,
chapters: List<ChapterItem>, isAnyChapterSelected: Boolean,
onToggleSelection: (Boolean) -> Unit, onToggleSelection: (Boolean) -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
) { ) {
when { when {
chapterItem.selected -> onToggleSelection(false) chapterItem.selected -> onToggleSelection(false)
chapters.fastAny { it.selected } -> onToggleSelection(true) isAnyChapterSelected -> onToggleSelection(true)
else -> onChapterClicked(chapterItem.chapter) else -> onChapterClicked(chapterItem.chapter)
} }
} }

View File

@ -10,6 +10,7 @@ import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.screenModelScope import cafe.adriel.voyager.core.model.screenModelScope
import eu.kanade.core.preference.asState import eu.kanade.core.preference.asState
import eu.kanade.core.util.addOrRemove 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.SetReadStatus
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
import eu.kanade.domain.manga.interactor.GetPagePreviews 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.Chapter
import tachiyomi.domain.chapter.model.ChapterUpdate import tachiyomi.domain.chapter.model.ChapterUpdate
import tachiyomi.domain.chapter.model.NoChaptersException import tachiyomi.domain.chapter.model.NoChaptersException
import tachiyomi.domain.chapter.service.calculateChapterGap
import tachiyomi.domain.chapter.service.getChapterSort import tachiyomi.domain.chapter.service.getChapterSort
import tachiyomi.domain.download.service.DownloadPreferences import tachiyomi.domain.download.service.DownloadPreferences
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
@ -121,6 +123,7 @@ import tachiyomi.source.local.isLocal
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import kotlin.math.floor
class MangaScreenModel( class MangaScreenModel(
val context: Context, val context: Context,
@ -182,10 +185,10 @@ class MangaScreenModel(
private val isFavorited: Boolean private val isFavorited: Boolean
get() = manga?.favorite ?: false get() = manga?.favorite ?: false
private val allChapters: List<ChapterItem>? private val allChapters: List<ChapterList.Item>?
get() = successState?.chapters get() = successState?.chapters
private val filteredChapters: List<ChapterItem>? private val filteredChapters: List<ChapterList.Item>?
get() = successState?.processedChapters get() = successState?.processedChapters
val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get() val chapterSwipeStartAction = libraryPreferences.swipeToEndAction().get()
@ -300,7 +303,7 @@ class MangaScreenModel(
.combine(downloadManager.queueState) { state, _ -> state } .combine(downloadManager.queueState) { state, _ -> state }
// SY <-- // SY <--
.collectLatest { (manga, chapters /* SY --> */, flatMetadata, mergedData /* 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 { updateSuccessState {
it.copy( it.copy(
manga = manga, manga = manga,
@ -321,7 +324,7 @@ class MangaScreenModel(
val manga = getMangaAndChapters.awaitManga(mangaId) val manga = getMangaAndChapters.awaitManga(mangaId)
// SY --> // SY -->
val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChaptersByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId)) 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 -> val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references ->
MergedMangaData( MergedMangaData(
references, references,
@ -889,7 +892,7 @@ class MangaScreenModel(
private fun updateDownloadState(download: Download) { private fun updateDownloadState(download: Download) {
updateSuccessState { successState -> 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 if (modifiedIndex < 0) return@updateSuccessState successState
val newChapters = successState.chapters.toMutableList().apply { val newChapters = successState.chapters.toMutableList().apply {
@ -901,10 +904,10 @@ class MangaScreenModel(
} }
} }
private fun List<Chapter>.toChapterItems( private fun List<Chapter>.toChapterListItems(
manga: Manga, manga: Manga,
mergedData: MergedMangaData?, mergedData: MergedMangaData?,
): List<ChapterItem> { ): List<ChapterList.Item> {
val isLocal = manga.isLocal() val isLocal = manga.isLocal()
// SY --> // SY -->
val isExhManga = manga.isEhBasedManga() val isExhManga = manga.isEhBasedManga()
@ -939,7 +942,7 @@ class MangaScreenModel(
else -> Download.State.NOT_DOWNLOADED else -> Download.State.NOT_DOWNLOADED
} }
ChapterItem( ChapterList.Item(
chapter = chapter, chapter = chapter,
downloadState = downloadState, downloadState = downloadState,
downloadProgress = activeDownload?.progress ?: 0, downloadProgress = activeDownload?.progress ?: 0,
@ -1013,7 +1016,7 @@ class MangaScreenModel(
/** /**
* @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled] * @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 { screenModelScope.launch {
executeChapterSwipeAction(chapterItem, swipeAction) executeChapterSwipeAction(chapterItem, swipeAction)
} }
@ -1023,7 +1026,7 @@ class MangaScreenModel(
* @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled] * @throws IllegalStateException if the swipe action is [LibraryPreferences.ChapterSwipeAction.Disabled]
*/ */
private fun executeChapterSwipeAction( private fun executeChapterSwipeAction(
chapterItem: ChapterItem, chapterItem: ChapterList.Item,
swipeAction: LibraryPreferences.ChapterSwipeAction, swipeAction: LibraryPreferences.ChapterSwipeAction,
) { ) {
val chapter = chapterItem.chapter val chapter = chapterItem.chapter
@ -1110,7 +1113,7 @@ class MangaScreenModel(
} }
fun runChapterDownloadActions( fun runChapterDownloadActions(
items: List<ChapterItem>, items: List<ChapterList.Item>,
action: ChapterDownloadAction, action: ChapterDownloadAction,
) { ) {
when (action) { when (action) {
@ -1125,7 +1128,7 @@ class MangaScreenModel(
startDownload(listOf(chapter), true) startDownload(listOf(chapter), true)
} }
ChapterDownloadAction.CANCEL -> { ChapterDownloadAction.CANCEL -> {
val chapterId = items.singleOrNull()?.chapter?.id ?: return val chapterId = items.singleOrNull()?.id ?: return
cancelDownload(chapterId) cancelDownload(chapterId)
} }
ChapterDownloadAction.DELETE -> { ChapterDownloadAction.DELETE -> {
@ -1344,14 +1347,14 @@ class MangaScreenModel(
} }
fun toggleSelection( fun toggleSelection(
item: ChapterItem, item: ChapterList.Item,
selected: Boolean, selected: Boolean,
userSelected: Boolean = false, userSelected: Boolean = false,
fromLongPress: Boolean = false, fromLongPress: Boolean = false,
) { ) {
updateSuccessState { successState -> updateSuccessState { successState ->
val newChapters = successState.processedChapters.toMutableList().apply { 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 if (selectedIndex < 0) return@apply
val selectedItem = get(selectedIndex) val selectedItem = get(selectedIndex)
@ -1359,7 +1362,7 @@ class MangaScreenModel(
val firstSelection = none { it.selected } val firstSelection = none { it.selected }
set(selectedIndex, selectedItem.copy(selected = selected)) set(selectedIndex, selectedItem.copy(selected = selected))
selectedChapterIds.addOrRemove(item.chapter.id, selected) selectedChapterIds.addOrRemove(item.id, selected)
if (selected && userSelected && fromLongPress) { if (selected && userSelected && fromLongPress) {
if (firstSelection) { if (firstSelection) {
@ -1382,7 +1385,7 @@ class MangaScreenModel(
range.forEach { range.forEach {
val inbetweenItem = get(it) val inbetweenItem = get(it)
if (!inbetweenItem.selected) { if (!inbetweenItem.selected) {
selectedChapterIds.add(inbetweenItem.chapter.id) selectedChapterIds.add(inbetweenItem.id)
set(it, inbetweenItem.copy(selected = true)) set(it, inbetweenItem.copy(selected = true))
} }
} }
@ -1410,7 +1413,7 @@ class MangaScreenModel(
fun toggleAllSelection(selected: Boolean) { fun toggleAllSelection(selected: Boolean) {
updateSuccessState { successState -> updateSuccessState { successState ->
val newChapters = successState.chapters.map { val newChapters = successState.chapters.map {
selectedChapterIds.addOrRemove(it.chapter.id, selected) selectedChapterIds.addOrRemove(it.id, selected)
it.copy(selected = selected) it.copy(selected = selected)
} }
selectedPositions[0] = -1 selectedPositions[0] = -1
@ -1422,7 +1425,7 @@ class MangaScreenModel(
fun invertSelection() { fun invertSelection() {
updateSuccessState { successState -> updateSuccessState { successState ->
val newChapters = successState.chapters.map { val newChapters = successState.chapters.map {
selectedChapterIds.addOrRemove(it.chapter.id, !it.selected) selectedChapterIds.addOrRemove(it.id, !it.selected)
it.copy(selected = !it.selected) it.copy(selected = !it.selected)
} }
selectedPositions[0] = -1 selectedPositions[0] = -1
@ -1559,7 +1562,7 @@ class MangaScreenModel(
val manga: Manga, val manga: Manga,
val source: Source, val source: Source,
val isFromSource: Boolean, val isFromSource: Boolean,
val chapters: List<ChapterItem>, val chapters: List<ChapterList.Item>,
val trackItems: List<TrackItem> = emptyList(), val trackItems: List<TrackItem> = emptyList(),
val isRefreshingData: Boolean = false, val isRefreshingData: Boolean = false,
val dialog: MangaScreenModel.Dialog? = null, val dialog: MangaScreenModel.Dialog? = null,
@ -1580,6 +1583,33 @@ class MangaScreenModel(
chapters.applyFilters(manga).toList() 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 val trackingAvailable: Boolean
get() = trackItems.isNotEmpty() get() = trackItems.isNotEmpty()
@ -1590,7 +1620,7 @@ class MangaScreenModel(
* Applies the view filters to the list of chapters obtained from the database. * Applies the view filters to the list of chapters obtained from the database.
* @return an observable of the list of chapters filtered and sorted. * @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 isLocalManga = manga.isLocal()
val unreadFilter = manga.unreadFilter val unreadFilter = manga.unreadFilter
val downloadedFilter = manga.downloadedFilter val downloadedFilter = manga.downloadedFilter
@ -1617,18 +1647,27 @@ data class MergedMangaData(
) )
@Immutable @Immutable
data class ChapterItem( sealed class ChapterList {
val chapter: Chapter, @Immutable
val downloadState: Download.State, data class MissingCount(
val downloadProgress: Int, val id: String,
val selected: Boolean = false, val count: Int,
) : ChapterList()
// SY --> @Immutable
val sourceName: String?, data class Item(
val showScanlator: Boolean, val chapter: Chapter,
// SY <-- val downloadState: Download.State,
) { val downloadProgress: Int,
val isDownloaded = downloadState == Download.State.DOWNLOADED val selected: Boolean = false,
// SY -->
val sourceName: String?,
val showScanlator: Boolean,
// SY <--
) : ChapterList() {
val id = chapter.id
val isDownloaded = downloadState == Download.State.DOWNLOADED
}
} }
// SY --> // SY -->

View File

@ -2,7 +2,7 @@ package eu.kanade.tachiyomi.util.chapter
import eu.kanade.domain.chapter.model.applyFilters import eu.kanade.domain.chapter.model.applyFilters
import eu.kanade.tachiyomi.data.download.DownloadManager 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 exh.source.isEhBasedManga
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
@ -32,7 +32,7 @@ fun List<Chapter>.getNextUnread(manga: Manga, downloadManager: DownloadManager /
/** /**
* Gets next unread chapter with filters and sorting applied * 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 -> return applyFilters(manga).let { chapters ->
// SY --> // SY -->
if (manga.isEhBasedManga()) { if (manga.isEhBasedManga()) {