Add option for bookmarked chapters to download dropdown (#2891)

(cherry picked from commit 3c6f0f1697ccab055ee7af47da84b2161d406f0c)

# Conflicts:
#	CHANGELOG.md
#	app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt
This commit is contained in:
NarwhalHorns
2026-02-21 10:14:55 +00:00
committed by Jobobby04
parent 3cfc53bf11
commit 185cd923c0
7 changed files with 109 additions and 10 deletions
@@ -60,6 +60,7 @@ import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.interactor.SetSortModeForCategory
import tachiyomi.domain.category.interactor.UpdateCategory
import tachiyomi.domain.category.repository.CategoryRepository
import tachiyomi.domain.chapter.interactor.GetBookmarkedChaptersByMangaId
import tachiyomi.domain.chapter.interactor.GetChapter
import tachiyomi.domain.chapter.interactor.GetChapterByUrlAndMangaId
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
@@ -156,6 +157,7 @@ class DomainModule : InjektModule {
addSingletonFactory<ChapterRepository> { ChapterRepositoryImpl(get()) }
addFactory { GetChapter(get()) }
addFactory { GetChaptersByMangaId(get()) }
addFactory { GetBookmarkedChaptersByMangaId(get(), get(), get()) }
addFactory { GetChapterByUrlAndMangaId(get()) }
addFactory { UpdateChapter(get()) }
addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
@@ -1,6 +1,5 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -14,11 +13,11 @@ import tachiyomi.presentation.core.i18n.stringResource
@Composable
fun DownloadDropdownMenu(
modifier: Modifier = Modifier,
expanded: Boolean,
onDismissRequest: () -> Unit,
onDownloadClicked: (DownloadAction) -> Unit,
offset: DpOffset? = null,
modifier: Modifier = Modifier,
) {
if (offset != null) {
DropdownMenu(
@@ -49,7 +48,7 @@ fun DownloadDropdownMenu(
}
@Composable
private fun ColumnScope.DownloadDropdownMenuItems(
private fun DownloadDropdownMenuItems(
onDismissRequest: () -> Unit,
onDownloadClicked: (DownloadAction) -> Unit,
) {
@@ -59,6 +58,7 @@ private fun ColumnScope.DownloadDropdownMenuItems(
DownloadAction.NEXT_10_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 10, 10),
DownloadAction.NEXT_25_CHAPTERS to pluralStringResource(MR.plurals.download_amount, 25, 25),
DownloadAction.UNREAD_CHAPTERS to stringResource(MR.strings.download_unread),
DownloadAction.BOOKMARKED_CHAPTERS to stringResource(MR.strings.download_bookmarked),
)
options.map { (downloadAction, string) ->
@@ -6,6 +6,7 @@ enum class DownloadAction {
NEXT_10_CHAPTERS,
NEXT_25_CHAPTERS,
UNREAD_CHAPTERS,
BOOKMARKED_CHAPTERS,
}
enum class EditCoverAction {
@@ -84,6 +84,7 @@ import tachiyomi.core.common.util.lang.launchNonCancellable
import tachiyomi.domain.category.interactor.GetCategories
import tachiyomi.domain.category.interactor.SetMangaCategories
import tachiyomi.domain.category.model.Category
import tachiyomi.domain.chapter.interactor.GetBookmarkedChaptersByMangaId
import tachiyomi.domain.chapter.interactor.GetChaptersByMangaId
import tachiyomi.domain.chapter.interactor.GetMergedChaptersByMangaId
import tachiyomi.domain.chapter.model.Chapter
@@ -122,6 +123,7 @@ class LibraryScreenModel(
private val getTracksPerManga: GetTracksPerManga = Injekt.get(),
private val getNextChapters: GetNextChapters = Injekt.get(),
private val getChaptersByMangaId: GetChaptersByMangaId = Injekt.get(),
private val getBookmarkedChaptersByMangaId: GetBookmarkedChaptersByMangaId = Injekt.get(),
private val setReadStatus: SetReadStatus = Injekt.get(),
private val updateManga: UpdateManga = Injekt.get(),
private val setMangaCategories: SetMangaCategories = Injekt.get(),
@@ -734,15 +736,19 @@ class LibraryScreenModel(
* Queues the amount specified of unread chapters from the list of selected manga
*/
fun performDownloadAction(action: DownloadAction) {
val mangas = state.value.selectedManga
val amount = when (action) {
DownloadAction.NEXT_1_CHAPTER -> 1
DownloadAction.NEXT_5_CHAPTERS -> 5
DownloadAction.NEXT_10_CHAPTERS -> 10
DownloadAction.NEXT_25_CHAPTERS -> 25
DownloadAction.UNREAD_CHAPTERS -> null
when (action) {
DownloadAction.NEXT_1_CHAPTER -> downloadNextChapters(1)
DownloadAction.NEXT_5_CHAPTERS -> downloadNextChapters(5)
DownloadAction.NEXT_10_CHAPTERS -> downloadNextChapters(10)
DownloadAction.NEXT_25_CHAPTERS -> downloadNextChapters(25)
DownloadAction.UNREAD_CHAPTERS -> downloadNextChapters(null)
DownloadAction.BOOKMARKED_CHAPTERS -> downloadBookmarkedChapters()
}
clearSelection()
}
private fun downloadNextChapters(amount: Int?) {
val mangas = state.value.selectedManga
screenModelScope.launchNonCancellable {
mangas.forEach { manga ->
// SY -->
@@ -792,6 +798,54 @@ class LibraryScreenModel(
}
}
private fun downloadBookmarkedChapters() {
val mangas = state.value.selectedManga
screenModelScope.launchNonCancellable {
mangas.forEach { manga ->
// SY -->
if (manga.source == MERGED_SOURCE_ID) {
val mergedMangas = getMergedMangaById.await(manga.id)
.associateBy { it.id }
getBookmarkedChaptersByMangaId.await(manga.id)
.groupBy { it.mangaId }
.forEach ab@{ (mangaId, chapters) ->
val mergedManga = mergedMangas[mangaId] ?: return@ab
val downloadChapters = chapters.fastFilterNot { chapter ->
downloadManager.queueState.value.fastAny { chapter.id == it.chapter.id } ||
downloadManager.isChapterDownloaded(
chapter.name,
chapter.scanlator,
chapter.url,
mergedManga.ogTitle,
mergedManga.source,
)
}
downloadManager.downloadChapters(mergedManga, downloadChapters)
}
return@forEach
}
// SY <--
val chapters = getBookmarkedChaptersByMangaId.await(manga.id)
.fastFilterNot { chapter ->
downloadManager.getQueuedDownloadOrNull(chapter.id) != null ||
downloadManager.isChapterDownloaded(
chapter.name,
chapter.scanlator,
chapter.url,
// SY -->
manga.ogTitle,
// SY <--
manga.source,
)
}
downloadManager.downloadChapters(manga, chapters)
}
}
}
// SY -->
fun cleanTitles() {
state.value.selectedManga.fastFilter {
@@ -1175,6 +1175,13 @@ class MangaScreenModel(
return if (manga.sortDescending()) chaptersSorted.reversed() else chaptersSorted
}
private fun getBookmarkedChapters(): List<Chapter> {
val chapterItems = if (skipFiltered) filteredChapters.orEmpty() else allChapters.orEmpty()
return chapterItems
.filter { (chapter, dlStatus) -> chapter.bookmark && dlStatus == Download.State.NOT_DOWNLOADED }
.map { it.chapter }
}
private fun startDownload(
chapters: List<Chapter>,
startNow: Boolean,
@@ -1237,6 +1244,7 @@ class MangaScreenModel(
DownloadAction.NEXT_10_CHAPTERS -> getUnreadChaptersSorted().take(10)
DownloadAction.NEXT_25_CHAPTERS -> getUnreadChaptersSorted().take(25)
DownloadAction.UNREAD_CHAPTERS -> getUnreadChapters()
DownloadAction.BOOKMARKED_CHAPTERS -> getBookmarkedChapters()
}
if (chaptersToDownload.isNotEmpty()) {
startDownload(chaptersToDownload, false)
@@ -0,0 +1,33 @@
package tachiyomi.domain.chapter.interactor
import exh.source.MERGED_SOURCE_ID
import logcat.LogPriority
import tachiyomi.core.common.util.system.logcat
import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.chapter.repository.ChapterRepository
import tachiyomi.domain.manga.interactor.GetManga
class GetBookmarkedChaptersByMangaId(
private val chapterRepository: ChapterRepository,
// SY -->
private val getManga: GetManga,
private val getMergedChaptersByMangaId: GetMergedChaptersByMangaId,
// SY <--
) {
suspend fun await(mangaId: Long): List<Chapter> {
return try {
// SY -->
val manga = getManga.await(mangaId) ?: return emptyList()
if (manga.source == MERGED_SOURCE_ID) {
return getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
.filter { it.bookmark }
}
// SY <--
chapterRepository.getBookmarkedChaptersByMangaId(mangaId)
} catch (e: Exception) {
logcat(LogPriority.ERROR, e)
emptyList()
}
}
}
@@ -757,6 +757,7 @@
<string name="sort_by_upload_date">By upload date</string>
<string name="manga_download">Download</string>
<string name="download_unread">Unread</string>
<string name="download_bookmarked">Bookmarked</string>
<string name="custom_cover">Custom cover</string>
<string name="manga_cover">Cover</string>
<string name="cover_saved">Cover saved</string>