Improve filter

This commit is contained in:
Koitharu
2024-10-07 20:02:34 +03:00
parent 9ea1122ca0
commit 1e22e8de45
6 changed files with 98 additions and 14 deletions

View File

@@ -332,6 +332,15 @@ class FilterCoordinator @Inject constructor(
}
}
fun toggleDemographic(value: Demographic, isSelected: Boolean) {
currentListFilter.update { oldValue ->
oldValue.copy(
demographics = if (isSelected) oldValue.demographics + value else oldValue.demographics - value,
query = oldValue.takeQueryIfSupported(),
)
}
}
fun toggleContentType(value: ContentType, isSelected: Boolean) {
currentListFilter.update { oldValue ->
oldValue.copy(

View File

@@ -8,6 +8,7 @@ import android.view.View
import android.widget.RelativeLayout
import android.widget.TextView
import androidx.annotation.AttrRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes
import androidx.core.view.isInvisible
@@ -68,6 +69,10 @@ class FilterFieldLayout @JvmOverloads constructor(
}
}
fun setTitle(@StringRes titleResId: Int) {
binding.textViewTitle.setText(titleResId)
}
fun setError(errorMessage: String?) {
if (errorMessage == null && errorView == null) {
return

View File

@@ -16,7 +16,13 @@ import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.FragmentFilterHeaderBinding
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
import org.koitharu.kotatsu.filter.ui.tags.TagsCatalogSheet
import org.koitharu.kotatsu.parsers.model.ContentRating
import org.koitharu.kotatsu.parsers.model.ContentType
import org.koitharu.kotatsu.parsers.model.Demographic
import org.koitharu.kotatsu.parsers.model.MangaState
import org.koitharu.kotatsu.parsers.model.MangaTag
import org.koitharu.kotatsu.parsers.model.YEAR_UNKNOWN
import java.util.Locale
import javax.inject.Inject
@AndroidEntryPoint
@@ -55,6 +61,13 @@ class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsV
override fun onChipCloseClick(chip: Chip, data: Any?) {
when (data) {
is String -> filter.setQuery(null)
is ContentRating -> filter.toggleContentRating(data, false)
is Demographic -> filter.toggleDemographic(data, false)
is ContentType -> filter.toggleContentType(data, false)
is MangaState -> filter.toggleState(data, false)
is Locale -> filter.setLocale(null)
is Int -> filter.setYear(YEAR_UNKNOWN)
is IntRange -> filter.setYearRange(YEAR_UNKNOWN, YEAR_UNKNOWN)
}
}

View File

@@ -3,12 +3,15 @@ package org.koitharu.kotatsu.filter.ui
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.titleResId
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.MangaListFilter
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.parsers.util.toTitleCase
import org.koitharu.kotatsu.search.domain.MangaSearchRepository
import javax.inject.Inject
import com.google.android.material.R as materialR
@@ -18,15 +21,14 @@ class FilterHeaderProducer @Inject constructor(
) {
fun observeHeader(filterCoordinator: FilterCoordinator): Flow<FilterHeaderModel> {
return combine(filterCoordinator.tags, filterCoordinator.query) { tags, query ->
createChipsList(
return combine(filterCoordinator.tags, filterCoordinator.observe()) { tags, snapshot ->
val chipList = createChipsList(
source = filterCoordinator.mangaSource,
capabilities = filterCoordinator.capabilities,
property = tags,
query = query,
tagsProperty = tags,
snapshot = snapshot.listFilter,
limit = 8,
)
}.combine(filterCoordinator.observe()) { chipList, snapshot ->
FilterHeaderModel(
chips = chipList,
sortOrder = snapshot.sortOrder,
@@ -38,20 +40,20 @@ class FilterHeaderProducer @Inject constructor(
private suspend fun createChipsList(
source: MangaSource,
capabilities: MangaListFilterCapabilities,
property: FilterProperty<MangaTag>,
query: String?,
tagsProperty: FilterProperty<MangaTag>,
snapshot: MangaListFilter,
limit: Int,
): List<ChipsView.ChipModel> {
val result = ArrayDeque<ChipsView.ChipModel>(limit + 3)
if (query.isNullOrEmpty() || capabilities.isSearchWithFiltersSupported) {
val selectedTags = property.selectedItems.toMutableSet()
if (snapshot.query.isNullOrEmpty() || capabilities.isSearchWithFiltersSupported) {
val selectedTags = tagsProperty.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)
tags = tags + tagsProperty.availableItems.take(limit - tags.size)
}
if (tags.isEmpty() && selectedTags.isEmpty()) {
return emptyList()
@@ -77,13 +79,59 @@ class FilterHeaderProducer @Inject constructor(
result.addFirst(model)
}
}
if (!query.isNullOrEmpty()) {
snapshot.locale?.let {
result.addFirst(
ChipsView.ChipModel(
title = query,
title = it.getDisplayName(it).toTitleCase(it),
icon = R.drawable.ic_language,
isCloseable = true,
data = it,
),
)
}
snapshot.types.forEach {
result.addFirst(
ChipsView.ChipModel(
titleResId = it.titleResId,
isCloseable = true,
data = it,
),
)
}
snapshot.demographics.forEach {
result.addFirst(
ChipsView.ChipModel(
titleResId = it.titleResId,
isCloseable = true,
data = it,
),
)
}
snapshot.contentRating.forEach {
result.addFirst(
ChipsView.ChipModel(
titleResId = it.titleResId,
isCloseable = true,
data = it,
),
)
}
snapshot.states.forEach {
result.addFirst(
ChipsView.ChipModel(
titleResId = it.titleResId,
isCloseable = true,
data = it,
),
)
}
if (!snapshot.query.isNullOrEmpty()) {
result.addFirst(
ChipsView.ChipModel(
title = snapshot.query,
icon = materialR.drawable.abc_ic_search_api_material,
isCloseable = true,
data = query,
data = snapshot.query,
),
)
}
@@ -97,6 +145,5 @@ class FilterHeaderProducer @Inject constructor(
private fun moreTagsChip() = ChipsView.ChipModel(
titleResId = R.string.more,
isDropdown = true,
// icon = materialR.drawable.abc_ic_menu_overflow_material,
)
}

View File

@@ -68,12 +68,20 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
filter.year.observe(viewLifecycleOwner, this::onYearChanged)
filter.yearRange.observe(viewLifecycleOwner, this::onYearRangeChanged)
binding.layoutGenres.setTitle(
if (filter.capabilities.isMultipleTagsSupported) {
R.string.genres
} else {
R.string.genre
},
)
binding.spinnerLocale.onItemSelectedListener = this
binding.spinnerOriginalLocale.onItemSelectedListener = this
binding.spinnerOrder.onItemSelectedListener = this
binding.chipsState.onChipClickListener = this
binding.chipsTypes.onChipClickListener = this
binding.chipsContentRating.onChipClickListener = this
binding.chipsDemographics.onChipClickListener = this
binding.chipsGenres.onChipClickListener = this
binding.chipsGenresExclude.onChipClickListener = this
binding.sliderYear.addOnChangeListener(this::onSliderValueChange)
@@ -143,6 +151,7 @@ class FilterSheetFragment : BaseAdaptiveSheet<SheetFilterBinding>(),
is ContentType -> filter.toggleContentType(data, !chip.isChecked)
is ContentRating -> filter.toggleContentRating(data, !chip.isChecked)
is Demographic -> filter.toggleDemographic(data, !chip.isChecked)
null -> TagsCatalogSheet.show(getChildFragmentManager(), chip.parentView?.id == R.id.chips_genresExclude)
}
}

View File

@@ -741,4 +741,5 @@
<string name="start_download">Start download</string>
<string name="save_manga_confirm">Save selected manga? This may consume traffic and disk space</string>
<string name="save_manga">Save manga</string>
<string name="genre">Genre</string>
</resources>