mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-14 04:58:56 +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:
@@ -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