Improve filter

master
Koitharu 2 years ago
parent 9ea1122ca0
commit 1e22e8de45
Signed by: Koitharu
GPG Key ID: 676DEE768C17A9D7

@ -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) { fun toggleContentType(value: ContentType, isSelected: Boolean) {
currentListFilter.update { oldValue -> currentListFilter.update { oldValue ->
oldValue.copy( oldValue.copy(

@ -8,6 +8,7 @@ import android.view.View
import android.widget.RelativeLayout import android.widget.RelativeLayout
import android.widget.TextView import android.widget.TextView
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.StringRes
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.content.withStyledAttributes import androidx.core.content.withStyledAttributes
import androidx.core.view.isInvisible 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?) { fun setError(errorMessage: String?) {
if (errorMessage == null && errorView == null) { if (errorMessage == null && errorView == null) {
return return

@ -16,7 +16,13 @@ import org.koitharu.kotatsu.core.util.ext.observe
import org.koitharu.kotatsu.databinding.FragmentFilterHeaderBinding import org.koitharu.kotatsu.databinding.FragmentFilterHeaderBinding
import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel import org.koitharu.kotatsu.filter.ui.model.FilterHeaderModel
import org.koitharu.kotatsu.filter.ui.tags.TagsCatalogSheet 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.MangaTag
import org.koitharu.kotatsu.parsers.model.YEAR_UNKNOWN
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
@ -55,6 +61,13 @@ class FilterHeaderFragment : BaseFragment<FragmentFilterHeaderBinding>(), ChipsV
override fun onChipCloseClick(chip: Chip, data: Any?) { override fun onChipCloseClick(chip: Chip, data: Any?) {
when (data) { when (data) {
is String -> filter.setQuery(null) 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)
} }
} }

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

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

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

Loading…
Cancel
Save