Show current filter in list header

This commit is contained in:
Koitharu
2021-09-11 16:01:15 +03:00
parent c1b6cef362
commit 675e95da2b
10 changed files with 97 additions and 5 deletions

View File

@@ -22,12 +22,21 @@ class ChipsView @JvmOverloads constructor(
private var chipOnClickListener = OnClickListener {
onChipClickListener?.onChipClick(it as Chip, it.tag)
}
private var chipOnCloseListener = OnClickListener {
onChipCloseClickListener?.onChipCloseClick(it as Chip, it.tag)
}
var onChipClickListener: OnChipClickListener? = null
set(value) {
field = value
val isChipClickable = value != null
children.forEach { it.isClickable = isChipClickable }
}
var onChipCloseClickListener: OnChipCloseClickListener? = null
set(value) {
field = value
val isCloseIconVisible = value != null
children.forEach { (it as? Chip)?.isCloseIconVisible = isCloseIconVisible }
}
override fun requestLayout() {
if (isLayoutSuppressedCompat) {
@@ -69,7 +78,8 @@ class ChipsView @JvmOverloads constructor(
val drawable = ChipDrawable.createFromAttributes(context, null, 0, R.style.Widget_Kotatsu_Chip)
chip.setChipDrawable(drawable)
chip.setTextColor(ContextCompat.getColor(context, R.color.color_primary))
chip.isCloseIconVisible = false
chip.isCloseIconVisible = onChipCloseClickListener != null
chip.setOnCloseIconClickListener(chipOnCloseListener)
chip.setEnsureMinTouchTargetSize(false)
chip.setOnClickListener(chipOnClickListener)
addView(chip)
@@ -96,4 +106,9 @@ class ChipsView @JvmOverloads constructor(
fun onChipClick(chip: Chip, data: Any?)
}
fun interface OnChipCloseClickListener {
fun onChipCloseClick(chip: Chip, data: Any?)
}
}

View File

@@ -71,7 +71,13 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
super.onViewCreated(view, savedInstanceState)
drawer = binding.root as? DrawerLayout
drawer?.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
listAdapter = MangaListAdapter(get(), viewLifecycleOwner, this, ::resolveException)
listAdapter = MangaListAdapter(
coil = get(),
lifecycleOwner = viewLifecycleOwner,
clickListener = this,
onRetryClick = ::resolveException,
onTagRemoveClick = viewModel::onRemoveFilterTag
)
paginationListener = PaginationScrollListener(4, this)
with(binding.recyclerView) {
setHasFixedSize(true)
@@ -287,7 +293,7 @@ abstract class MangaListFragment : BaseFragment<FragmentListBinding>(),
final override fun getSectionTitle(position: Int): CharSequence? {
return when (binding.recyclerViewFilter.adapter?.getItemViewType(position)) {
FilterAdapter.VIEW_TYPE_SORT -> getString(R.string.sort_order)
FilterAdapter.VIEW_TYPE_TAG -> getString(R.string.genre)
FilterAdapter.VIEW_TYPE_TAG -> getString(R.string.genres)
else -> null
}
}

View File

@@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.prefs.AppSettings
import org.koitharu.kotatsu.core.prefs.ListMode
import org.koitharu.kotatsu.list.ui.model.ListModel
@@ -36,6 +37,8 @@ abstract class MangaListViewModel(
}
}
open fun onRemoveFilterTag(tag: MangaTag) = Unit
abstract fun onRefresh()
abstract fun onRetry()

View File

@@ -0,0 +1,23 @@
package org.koitharu.kotatsu.list.ui.adapter
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.databinding.ItemCurrentFilterBinding
import org.koitharu.kotatsu.list.ui.model.CurrentFilterModel
import org.koitharu.kotatsu.list.ui.model.ListModel
fun currentFilterAD(
onTagRemoveClick: (MangaTag) -> Unit,
) = adapterDelegateViewBinding<CurrentFilterModel, ListModel, ItemCurrentFilterBinding>(
{ inflater, parent -> ItemCurrentFilterBinding.inflate(inflater, parent, false) }
) {
binding.chipsTags.onChipCloseClickListener = ChipsView.OnChipCloseClickListener { chip, data ->
onTagRemoveClick(data as? MangaTag ?: return@OnChipCloseClickListener)
}
bind {
binding.chipsTags.setChips(item.chips)
}
}

View File

@@ -6,6 +6,7 @@ import coil.ImageLoader
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.ui.DateTimeAgo
import org.koitharu.kotatsu.list.ui.model.ListModel
import org.koitharu.kotatsu.list.ui.model.MangaGridModel
@@ -17,7 +18,8 @@ class MangaListAdapter(
coil: ImageLoader,
lifecycleOwner: LifecycleOwner,
clickListener: OnListItemClickListener<Manga>,
onRetryClick: (Throwable) -> Unit
onRetryClick: (Throwable) -> Unit,
onTagRemoveClick: (MangaTag) -> Unit,
) : AsyncListDifferDelegationAdapter<ListModel>(DiffCallback()) {
init {
@@ -38,6 +40,7 @@ class MangaListAdapter(
.addDelegate(ITEM_TYPE_ERROR_FOOTER, errorFooterAD(onRetryClick))
.addDelegate(ITEM_TYPE_EMPTY, emptyStateListAD())
.addDelegate(ITEM_TYPE_HEADER, listHeaderAD())
.addDelegate(ITEM_TYPE_FILTER, currentFilterAD(onTagRemoveClick))
}
fun setItems(list: List<ListModel>, commitCallback: Runnable) {
@@ -79,5 +82,6 @@ class MangaListAdapter(
const val ITEM_TYPE_ERROR_FOOTER = 7
const val ITEM_TYPE_EMPTY = 8
const val ITEM_TYPE_HEADER = 9
const val ITEM_TYPE_FILTER = 10
}
}

View File

@@ -0,0 +1,7 @@
package org.koitharu.kotatsu.list.ui.model
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
data class CurrentFilterModel(
val chips: Collection<ChipsView.ChipModel>,
) : ListModel

View File

@@ -7,8 +7,10 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import org.koitharu.kotatsu.BuildConfig
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.base.ui.widgets.ChipsView
import org.koitharu.kotatsu.core.model.Manga
import org.koitharu.kotatsu.core.model.MangaFilter
import org.koitharu.kotatsu.core.model.MangaTag
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.RemoteMangaRepository
import org.koitharu.kotatsu.core.prefs.AppSettings
@@ -40,8 +42,9 @@ class RemoteListViewModel(
list == null -> listOf(LoadingState)
list.isEmpty() -> listOf(EmptyState(R.drawable.ic_book_cross, R.string.nothing_found, R.string._empty))
else -> {
val result = ArrayList<ListModel>(list.size + 2)
val result = ArrayList<ListModel>(list.size + 3)
result += headerModel
createFilterModel()?.let { result.add(it) }
list.toUi(result, mode)
when {
error != null -> result += error.toErrorFooter()
@@ -65,6 +68,16 @@ class RemoteListViewModel(
loadList(append = !mangaList.value.isNullOrEmpty())
}
override fun onRemoveFilterTag(tag: MangaTag) {
val filter = appliedFilter ?: return
if (tag !in filter.tags) {
return
}
applyFilter(
filter.copy(tags = filter.tags - tag)
)
}
fun loadNextPage() {
if (hasNextPage.value && listError.value == null) {
loadList(append = true)
@@ -108,6 +121,10 @@ class RemoteListViewModel(
}
}
private fun createFilterModel() = appliedFilter?.run {
CurrentFilterModel(tags.map { ChipsView.ChipModel(0, it.title, it) })
}
private fun loadFilter() {
launchJob(Dispatchers.Default) {
try {

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<HorizontalScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<org.koitharu.kotatsu.base.ui.widgets.ChipsView
android:id="@+id/chips_tags"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:closeIconEnabled="true"
app:singleLine="true" />
</HorizontalScrollView>

View File

@@ -236,4 +236,5 @@
<string name="auth_complete">Авторизация выполнена</string>
<string name="auth_not_supported_by">Авторизация в %s не поддерживается</string>
<string name="text_clear_cookies_prompt">Вы выйдете из всех источников, в которых Вы авторизованы</string>
<string name="genres">Жанры</string>
</resources>

View File

@@ -239,4 +239,5 @@
<string name="auth_complete">Authorization complete</string>
<string name="auth_not_supported_by">Authorization on %s is not supported</string>
<string name="text_clear_cookies_prompt">You will be logged out from all sources that you are authorized in</string>
<string name="genres">Genres</string>
</resources>