From 2191d9c83b14d8ee57794063a8c778270679b565 Mon Sep 17 00:00:00 2001 From: Koitharu Date: Tue, 24 Sep 2024 10:18:10 +0300 Subject: [PATCH] Fix sources catalog content types --- app/build.gradle | 6 +- .../org/koitharu/kotatsu/core/model/Manga.kt | 1 + .../kotatsu/core/model/MangaSource.kt | 1 + .../kotatsu/core/util/ext/Throwable.kt | 2 +- .../kotatsu/filter/ui/FilterHeaderProducer.kt | 65 ++++++++++--------- .../sources/catalog/SourcesCatalogActivity.kt | 14 ++-- .../catalog/SourcesCatalogViewModel.kt | 23 ++++++- app/src/main/res/values/strings.xml | 2 + 8 files changed, 70 insertions(+), 44 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2655e8c83..1f6ae40ba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -16,8 +16,8 @@ android { applicationId 'org.koitharu.kotatsu' minSdk = 21 targetSdk = 35 - versionCode = 670 - versionName = '7.6-a2' + versionCode = 671 + versionName = '7.6-a3' generatedDensities = [] testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner' ksp { @@ -83,7 +83,7 @@ afterEvaluate { } dependencies { //noinspection GradleDependency - implementation('com.github.KotatsuApp:kotatsu-parsers:1.0') { + implementation('com.github.KotatsuApp:kotatsu-parsers:613623fa53') { exclude group: 'org.json', module: 'json' } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt index 78c4bd3da..74bcb3a30 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/Manga.kt @@ -80,6 +80,7 @@ val Demographic.titleResId: Int Demographic.SHOUJO -> R.string.demographic_shoujo Demographic.SEINEN -> R.string.demographic_seinen Demographic.JOSEI -> R.string.demographic_josei + Demographic.KODOMO -> R.string.demographic_kodomo Demographic.NONE -> R.string.none } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt index 84c12973a..9c8ff510d 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/model/MangaSource.kt @@ -61,6 +61,7 @@ val ContentType.titleResId ContentType.MANHWA -> R.string.content_type_manhwa ContentType.MANHUA -> R.string.content_type_manhua ContentType.NOVEL -> R.string.content_type_novel + ContentType.ONE_SHOT -> R.string.content_type_one_shot } tailrec fun MangaSource.unwrap(): MangaSource = if (this is MangaSourceInfo) { diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt index c193e01c9..060703b77 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt @@ -89,7 +89,7 @@ fun Throwable.getDisplayMessage(resources: Resources): String = when (this) { is HttpException -> getHttpDisplayMessage(response.code, resources) is HttpStatusException -> getHttpDisplayMessage(statusCode, resources) - else -> getDisplayMessage(message, resources) ?: localizedMessage + else -> getDisplayMessage(message, resources) ?: message }.ifNullOrEmpty { resources.getString(R.string.error_occurred) } diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderProducer.kt b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderProducer.kt index f43811745..9b6ef87c1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderProducer.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/filter/ui/FilterHeaderProducer.kt @@ -5,6 +5,7 @@ import kotlinx.coroutines.flow.combine import org.koitharu.kotatsu.core.ui.widgets.ChipsView import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel import org.koitharu.kotatsu.filter.ui.model.FilterProperty +import org.koitharu.kotatsu.parsers.model.MangaListFilterCapabilities import org.koitharu.kotatsu.parsers.model.MangaSource import org.koitharu.kotatsu.parsers.model.MangaTag import org.koitharu.kotatsu.search.domain.MangaSearchRepository @@ -19,6 +20,7 @@ class FilterHeaderProducer @Inject constructor( return combine(filterCoordinator.tags, filterCoordinator.query) { tags, query -> createChipsList( source = filterCoordinator.mangaSource, + capabilities = filterCoordinator.capabilities, property = tags, query = query, limit = 8, @@ -34,42 +36,45 @@ class FilterHeaderProducer @Inject constructor( private suspend fun createChipsList( source: MangaSource, + capabilities: MangaListFilterCapabilities, property: FilterProperty, query: String?, limit: Int, ): List { - val selectedTags = property.selectedItems.toMutableSet() - var tags = if (selectedTags.isEmpty()) { - searchRepository.getTagsSuggestion("", limit, source) - } else { - searchRepository.getTagsSuggestion(selectedTags).take(limit) - } - if (tags.size < limit) { - tags = tags + property.availableItems.take(limit - tags.size) - } - if (tags.isEmpty() && selectedTags.isEmpty()) { - return emptyList() - } - val result = ArrayDeque(tags.size + selectedTags.size + 1) - for (tag in tags) { - val model = ChipsView.ChipModel( - title = tag.title, - isChecked = selectedTags.remove(tag), - data = tag, - ) - if (model.isChecked) { - result.addFirst(model) + val result = ArrayDeque(limit + 3) + if (query.isNullOrEmpty() || capabilities.isSearchWithFiltersSupported) { + val selectedTags = property.selectedItems.toMutableSet() + var tags = if (selectedTags.isEmpty()) { + searchRepository.getTagsSuggestion("", limit, source) } else { - result.addLast(model) + searchRepository.getTagsSuggestion(selectedTags).take(limit) + } + if (tags.size < limit) { + tags = tags + property.availableItems.take(limit - tags.size) + } + if (tags.isEmpty() && selectedTags.isEmpty()) { + return emptyList() + } + for (tag in tags) { + val model = ChipsView.ChipModel( + title = tag.title, + isChecked = selectedTags.remove(tag), + data = tag, + ) + if (model.isChecked) { + result.addFirst(model) + } else { + result.addLast(model) + } + } + for (tag in selectedTags) { + val model = ChipsView.ChipModel( + title = tag.title, + isChecked = true, + data = tag, + ) + result.addFirst(model) } - } - for (tag in selectedTags) { - val model = ChipsView.ChipModel( - title = tag.title, - isChecked = true, - data = tag, - ) - result.addFirst(model) } if (!query.isNullOrEmpty()) { result.addFirst( diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt index a655cb410..543a4e707 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogActivity.kt @@ -64,8 +64,8 @@ class SourcesCatalogActivity : BaseActivity(), this, ReversibleActionObserver(viewBinding.recyclerView), ) - combine(viewModel.appliedFilter, viewModel.hasNewSources, ::Pair).observe(this) { - updateFilers(it.first, it.second) + combine(viewModel.appliedFilter, viewModel.hasNewSources, viewModel.contentTypes, ::Triple).observe(this) { + updateFilers(it.first, it.second, it.third) } addMenuProvider(SourcesCatalogMenuProvider(this, viewModel, this)) } @@ -111,8 +111,9 @@ class SourcesCatalogActivity : BaseActivity(), private fun updateFilers( appliedFilter: SourcesCatalogFilter, hasNewSources: Boolean, + contentTypes: List, ) { - val chips = ArrayList(ContentType.entries.size + 2) + val chips = ArrayList(contentTypes.size + 2) chips += ChipModel( title = appliedFilter.locale?.toLocale().getDisplayName(this), icon = R.drawable.ic_language, @@ -126,11 +127,8 @@ class SourcesCatalogActivity : BaseActivity(), data = true, ) } - for (type in ContentType.entries) { - if (type == ContentType.HENTAI && viewModel.isNsfwDisabled) { - continue - } - chips += ChipModel( + contentTypes.mapTo(chips) { type -> + ChipModel( title = getString(type.titleResId), isChecked = type in appliedFilter.types, data = type, diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt index a924dfc5b..0a190cc67 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/settings/sources/catalog/SourcesCatalogViewModel.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.settings.sources.catalog +import androidx.annotation.WorkerThread import androidx.lifecycle.viewModelScope import androidx.room.invalidationTrackerFlow import dagger.hilt.android.lifecycle.HiltViewModel @@ -13,6 +14,7 @@ import kotlinx.coroutines.plus import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.db.MangaDatabase import org.koitharu.kotatsu.core.db.TABLE_SOURCES +import org.koitharu.kotatsu.core.model.isNsfw import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.core.ui.BaseViewModel import org.koitharu.kotatsu.core.ui.util.ReversibleAction @@ -23,7 +25,9 @@ import org.koitharu.kotatsu.explore.data.SourcesSortOrder import org.koitharu.kotatsu.list.ui.model.ListModel import org.koitharu.kotatsu.list.ui.model.LoadingState import org.koitharu.kotatsu.parsers.model.ContentType +import org.koitharu.kotatsu.parsers.model.MangaParserSource import org.koitharu.kotatsu.parsers.model.MangaSource +import java.util.EnumMap import java.util.EnumSet import java.util.Locale import javax.inject.Inject @@ -49,11 +53,11 @@ class SourcesCatalogViewModel @Inject constructor( ), ) - val isNsfwDisabled = settings.isNsfwContentDisabled - val hasNewSources = repository.observeHasNewSources() .stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Lazily, false) + val contentTypes = MutableStateFlow>(emptyList()) + val content: StateFlow> = combine( searchQuery, appliedFilter, @@ -64,6 +68,9 @@ class SourcesCatalogViewModel @Inject constructor( init { repository.clearNewSourcesBadge() + launchJob(Dispatchers.Default) { + contentTypes.value = getContentTypes(settings.isNsfwContentDisabled) + } } fun performSearch(query: String?) { @@ -129,4 +136,16 @@ class SourcesCatalogViewModel @Inject constructor( } } } + + @WorkerThread + private fun getContentTypes(isNsfwDisabled: Boolean): List { + val map = EnumMap(ContentType::class.java) + for (e in MangaParserSource.entries) { + if (isNsfwDisabled && e.isNsfw()) { + continue + } + map[e.contentType] = map.getOrDefault(e.contentType, 0) + 1 + } + return map.entries.sortedByDescending { it.value }.map { it.key } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c49fe7fd5..2076ad1f7 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -727,4 +727,6 @@ Years Any This source does not support search with filters. Your filters have been cleared + Kodomo + One shot