From 3c6f0f1697ccab055ee7af47da84b2161d406f0c Mon Sep 17 00:00:00 2001 From: NarwhalHorns Date: Sat, 21 Feb 2026 10:14:55 +0000 Subject: [PATCH] Add option for bookmarked chapters to download dropdown (#2891) --- CHANGELOG.md | 1 + .../java/eu/kanade/domain/DomainModule.kt | 2 + .../components/DownloadDropdownMenu.kt | 6 +-- .../manga/MangaScreenConstants.kt | 1 + .../ui/library/LibraryScreenModel.kt | 40 +++++++++++++++---- .../tachiyomi/ui/manga/MangaScreenModel.kt | 8 ++++ .../GetBookmarkedChaptersByMangaId.kt | 20 ++++++++++ .../moko-resources/base/strings.xml | 1 + 8 files changed, 69 insertions(+), 10 deletions(-) create mode 100644 domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt diff --git a/CHANGELOG.md b/CHANGELOG.md index 70e240fe8..7cf728ffb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - Automatically remove downloads on Suwayomi after reading, configurable via extension settings ([@cpiber](https://github.com/cpiber)) ([#2673](https://github.com/mihonapp/mihon/pull/2673)) - Display author & artist name in MAL search results ([@MajorTanya](https://github.com/MajorTanya)) ([#2833](https://github.com/mihonapp/mihon/pull/2833)) - Add filter options to Updates tab ([@MajorTanya](https://github.com/MajorTanya)) ([#2851](https://github.com/mihonapp/mihon/pull/2851)) +- Add bookmarked chapters to chapter download options ([@NarwhalHorns](https://github.com/NarwhalHorns)) ([#2891](https://github.com/mihonapp/mihon/pull/2891)) - Add `src:` prefix to search the library by source ID ([@MajorTanya](https://github.com/MajorTanya)) ([#2927](https://github.com/mihonapp/mihon/pull/2927)) - Add `src:local` as a way to search for Local Source entries ([@MajorTanya](https://github.com/MajorTanya)) ([#2928](https://github.com/mihonapp/mihon/pull/2928)) diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index 9ecb4b44e..6d3ddc35b 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -57,6 +57,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 @@ -157,6 +158,7 @@ class DomainModule : InjektModule { addSingletonFactory { ChapterRepositoryImpl(get()) } addFactory { GetChapter(get()) } addFactory { GetChaptersByMangaId(get()) } + addFactory { GetBookmarkedChaptersByMangaId(get()) } addFactory { GetChapterByUrlAndMangaId(get()) } addFactory { UpdateChapter(get()) } addFactory { SetReadStatus(get(), get(), get(), get()) } diff --git a/app/src/main/java/eu/kanade/presentation/components/DownloadDropdownMenu.kt b/app/src/main/java/eu/kanade/presentation/components/DownloadDropdownMenu.kt index e3391ec5a..b6cebc932 100644 --- a/app/src/main/java/eu/kanade/presentation/components/DownloadDropdownMenu.kt +++ b/app/src/main/java/eu/kanade/presentation/components/DownloadDropdownMenu.kt @@ -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) -> diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt index 22a6664d0..1a1438053 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreenConstants.kt @@ -6,6 +6,7 @@ enum class DownloadAction { NEXT_10_CHAPTERS, NEXT_25_CHAPTERS, UNREAD_CHAPTERS, + BOOKMARKED_CHAPTERS, } enum class EditCoverAction { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index 5fb8c4f01..0230f2404 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -47,6 +47,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.model.Chapter import tachiyomi.domain.history.interactor.GetNextChapters @@ -73,6 +74,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(), @@ -465,15 +467,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 -> val chapters = getNextChapters.await(manga.id) @@ -494,6 +500,26 @@ class LibraryScreenModel( } } + private fun downloadBookmarkedChapters() { + val mangas = state.value.selectedManga + screenModelScope.launchNonCancellable { + mangas.forEach { manga -> + val chapters = getBookmarkedChaptersByMangaId.await(manga.id) + .fastFilterNot { chapter -> + downloadManager.getQueuedDownloadOrNull(chapter.id) != null || + downloadManager.isChapterDownloaded( + chapter.name, + chapter.scanlator, + chapter.url, + manga.title, + manga.source, + ) + } + downloadManager.downloadChapters(manga, chapters) + } + } + } + /** * Marks mangas' chapters read status. */ 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 410f24bf6..eb3db386e 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 @@ -650,6 +650,13 @@ class MangaScreenModel( return if (manga.sortDescending()) chaptersSorted.reversed() else chaptersSorted } + private fun getBookmarkedChapters(): List { + 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, startNow: Boolean, @@ -712,6 +719,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) diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt new file mode 100644 index 000000000..129528156 --- /dev/null +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetBookmarkedChaptersByMangaId.kt @@ -0,0 +1,20 @@ +package tachiyomi.domain.chapter.interactor + +import logcat.LogPriority +import tachiyomi.core.common.util.system.logcat +import tachiyomi.domain.chapter.model.Chapter +import tachiyomi.domain.chapter.repository.ChapterRepository + +class GetBookmarkedChaptersByMangaId( + private val chapterRepository: ChapterRepository, +) { + + suspend fun await(mangaId: Long): List { + return try { + chapterRepository.getBookmarkedChaptersByMangaId(mangaId) + } catch (e: Exception) { + logcat(LogPriority.ERROR, e) + emptyList() + } + } +} diff --git a/i18n/src/commonMain/moko-resources/base/strings.xml b/i18n/src/commonMain/moko-resources/base/strings.xml index 8bc7ca486..aea867ed5 100644 --- a/i18n/src/commonMain/moko-resources/base/strings.xml +++ b/i18n/src/commonMain/moko-resources/base/strings.xml @@ -759,6 +759,7 @@ By upload date Download Unread + Bookmarked Custom cover Cover Cover saved