Show current filter in list header
This commit is contained in:
@@ -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?)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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 {
|
||||
|
||||
15
app/src/main/res/layout/item_current_filter.xml
Normal file
15
app/src/main/res/layout/item_current_filter.xml
Normal 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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
Reference in New Issue
Block a user