From 909a184c8d112f12ac7d98c8da398dfb16b6f354 Mon Sep 17 00:00:00 2001 From: saud-97 Date: Fri, 10 Nov 2023 04:14:15 +0300 Subject: [PATCH] Added: Show manga chapters count on search results in Settings>Browse>Global Search --- .../data/preference/PreferenceKeys.kt | 2 + .../data/preference/PreferencesHelper.kt | 2 + .../ui/setting/SettingsBrowseController.kt | 6 +++ .../globalsearch/GlobalSearchController.kt | 1 + .../source/globalsearch/GlobalSearchHolder.kt | 4 ++ .../globalsearch/GlobalSearchMangaHolder.kt | 38 +++++++++++++++++++ .../globalsearch/GlobalSearchPresenter.kt | 26 ++++++++++++- ...rce_global_search_controller_card_item.xml | 29 ++++++++++++++ app/src/main/res/values/strings.xml | 2 + 9 files changed, 108 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt index 5d59f423fdbf..57177a635009 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferenceKeys.kt @@ -151,6 +151,8 @@ object PreferenceKeys { const val onlySearchPinned = "only_search_pinned" + const val fetchMangaChapters = "fetch_manga_chapters" + const val downloadNew = "download_new" const val libraryLayout = "pref_display_library_layout" diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt index 21e7058eb7be..6f4957c151c9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt @@ -419,6 +419,8 @@ class PreferencesHelper(val context: Context) { fun onlySearchPinned() = flowPrefs.getBoolean(Keys.onlySearchPinned, false) + fun fetchMangaChapters() = flowPrefs.getBoolean(Keys.fetchMangaChapters, false) + fun hideInLibraryItems() = flowPrefs.getBoolean("browse_hide_in_library_items", false) // Tutorial preferences diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBrowseController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBrowseController.kt index d1566231800e..fe5225d34849 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBrowseController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsBrowseController.kt @@ -107,6 +107,12 @@ class SettingsBrowseController : SettingsController() { key = PreferenceKeys.onlySearchPinned titleRes = R.string.only_search_pinned_when } + switchPreference { + key = PreferenceKeys.fetchMangaChapters + titleRes = R.string.fetch_manga_chapters + summaryRes = R.string.fetch_manga_chapters_summary + defaultValue = false + } } preferenceCategory { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt index aa2519effd10..eb9998d19987 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchController.kt @@ -304,5 +304,6 @@ open class GlobalSearchController( */ fun onMangaInitialized(source: CatalogueSource, manga: Manga) { getHolder(source)?.setImage(manga) + getHolder(source)?.setChaptersCount(manga) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchHolder.kt index 6c55101d3c47..69977d68d7b3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchHolder.kt @@ -94,6 +94,10 @@ class GlobalSearchHolder(view: View, val adapter: GlobalSearchAdapter) : getHolder(manga)?.setImage(manga) } + fun setChaptersCount(manga: Manga) { + getHolder(manga)?.setChaptersCount(manga) + } + /** * Returns the view holder for the given manga. * diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt index b1e21351191a..d43242c1671f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchMangaHolder.kt @@ -7,14 +7,19 @@ import coil.Coil import coil.dispose import coil.request.CachePolicy import coil.request.ImageRequest +import eu.kanade.tachiyomi.R +import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.image.coil.CoverViewTarget import eu.kanade.tachiyomi.data.image.coil.MangaCoverFetcher +import eu.kanade.tachiyomi.data.preference.PreferencesHelper import eu.kanade.tachiyomi.databinding.SourceGlobalSearchControllerCardItemBinding import eu.kanade.tachiyomi.ui.base.holder.BaseFlexibleViewHolder import eu.kanade.tachiyomi.util.system.dpToPx import eu.kanade.tachiyomi.util.view.makeShapeCorners import eu.kanade.tachiyomi.util.view.setCards +import uy.kohesive.injekt.injectLazy +import java.text.DecimalFormat class GlobalSearchMangaHolder(view: View, adapter: GlobalSearchCardAdapter) : BaseFlexibleViewHolder(view, adapter) { @@ -48,6 +53,7 @@ class GlobalSearchMangaHolder(view: View, adapter: GlobalSearchCardAdapter) : binding.title.text = manga.title binding.favoriteButton.isVisible = manga.favorite setImage(manga) + setChaptersCount(manga) } fun setImage(manga: Manga) { @@ -62,4 +68,36 @@ class GlobalSearchMangaHolder(view: View, adapter: GlobalSearchCardAdapter) : Coil.imageLoader(itemView.context).enqueue(request) } } + + fun setChaptersCount(manga: Manga) { + if (!preferences.fetchMangaChapters().get()) { + return + } + val mangaChapters = db.getChapters(manga).executeAsBlocking() + if (mangaChapters.isEmpty()) { + return + } + + if (!manga.favorite) { + binding.unreadDownloadBadge.badgeView.setChapters(mangaChapters.size) + } + + val latestChapter = mangaChapters.maxOfOrNull { it.chapter_number } ?: -1f + if (latestChapter >= 0f) { + binding.subtitle.text = binding.root.context.getString( + R.string.latest_, + DecimalFormat("#.#").format(latestChapter), + ) + } else { + binding.subtitle.text = binding.root.context.getString( + R.string.latest_, + binding.root.context.getString(R.string.unknown), + ) + } + } + + private companion object { + private val db: DatabaseHelper by injectLazy() + private val preferences: PreferencesHelper by injectLazy() + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchPresenter.kt index 7671ac00950c..8048c82e6d3f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/source/globalsearch/GlobalSearchPresenter.kt @@ -12,16 +12,19 @@ import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.ui.base.presenter.BaseCoroutinePresenter +import eu.kanade.tachiyomi.util.chapter.syncChaptersWithSource import eu.kanade.tachiyomi.util.system.launchIO import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.withUIContext import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit +import timber.log.Timber import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy @@ -226,15 +229,20 @@ open class GlobalSearchPresenter( fetchImageJob?.cancel() fetchImageJob = fetchImageFlow.onEach { (mangaList, source) -> mangaList - .filter { it.thumbnail_url == null && !it.initialized } + .filter { (it.thumbnail_url == null && !it.initialized) || preferences.fetchMangaChapters().get() } .forEach { presenterScope.launchIO { try { val manga = getMangaDetails(it, source) + if (preferences.fetchMangaChapters().get()) { + // to let the database transaction for newly added manga chapters complete before adding the chapters count badge + delay(1250) + } withUIContext { view?.onMangaInitialized(source as CatalogueSource, manga) } - } catch (_: Exception) { + } catch (e: Exception) { + Timber.e(e) withUIContext { view?.onMangaInitialized(source as CatalogueSource, it) } @@ -255,6 +263,9 @@ open class GlobalSearchPresenter( manga.copyFrom(networkManga) manga.initialized = true db.insertManga(manga).executeAsBlocking() + if (preferences.fetchMangaChapters().get()) { + fetchChaptersFromSource(manga, source) + } return manga } @@ -280,4 +291,15 @@ open class GlobalSearchPresenter( } return localManga } + + private suspend fun fetchChaptersFromSource(manga: Manga, source: Source) { + try { + val chapters = source.getChapterList(manga) + syncChaptersWithSource(db, chapters, manga, source) + } catch (e: Exception) { + if (!e.message.isNullOrBlank() && !e.message!!.contains("No chapters found")) { + Timber.e(e) + } + } + } } diff --git a/app/src/main/res/layout/source_global_search_controller_card_item.xml b/app/src/main/res/layout/source_global_search_controller_card_item.xml index b0618ba66ab9..ecb27f964106 100644 --- a/app/src/main/res/layout/source_global_search_controller_card_item.xml +++ b/app/src/main/res/layout/source_global_search_controller_card_item.xml @@ -31,6 +31,14 @@ tools:ignore="ContentDescription" tools:src="@mipmap/ic_launcher" /> + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1f87a004f439..d1970c33404c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -895,6 +895,8 @@ Notify extension has been updated Some extensions may not be auto-updated if they were installed outside this app Only search pinned sources + Show manga chapters count on search results + Automatically fetch latest manga details and chapters and save it in database\nWarning: this feature will greatly impact device performance and increase network traffic to sources Hide entries already in library Match pinned sources Match enabled sources