Refactor filters
parent
68e9588f24
commit
f18c182a6a
@ -0,0 +1,30 @@
|
||||
package org.koitharu.kotatsu.list.domain
|
||||
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
|
||||
class AvailableFilters(
|
||||
val sortOrders: Set<SortOrder>,
|
||||
val tags: Set<MangaTag>,
|
||||
) {
|
||||
|
||||
val size: Int
|
||||
get() = sortOrders.size + tags.size
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (javaClass != other?.javaClass) return false
|
||||
other as AvailableFilters
|
||||
if (sortOrders != other.sortOrders) return false
|
||||
if (tags != other.tags) return false
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
var result = sortOrders.hashCode()
|
||||
result = 31 * result + tags.hashCode()
|
||||
return result
|
||||
}
|
||||
|
||||
fun isEmpty(): Boolean = sortOrders.isEmpty() && tags.isEmpty()
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package org.koitharu.kotatsu.list.ui
|
||||
|
||||
import org.koitharu.kotatsu.core.model.MangaFilter
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
|
||||
data class MangaFilterConfig(
|
||||
val sortOrders: List<SortOrder>,
|
||||
val tags: List<MangaTag>,
|
||||
val currentFilter: MangaFilter?
|
||||
)
|
||||
@ -1,94 +0,0 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.core.model.MangaFilter
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
|
||||
class FilterAdapter(
|
||||
private val sortOrders: List<SortOrder> = emptyList(),
|
||||
private val tags: List<MangaTag> = emptyList(),
|
||||
state: MangaFilter?,
|
||||
private val listener: OnFilterChangedListener
|
||||
) : RecyclerView.Adapter<BaseViewHolder<*, Boolean, *>>() {
|
||||
|
||||
private var currentState = state ?: MangaFilter(sortOrders.firstOrNull(), emptySet())
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) {
|
||||
VIEW_TYPE_SORT -> FilterSortHolder(parent).apply {
|
||||
itemView.setOnClickListener {
|
||||
setCheckedSort(requireData())
|
||||
}
|
||||
}
|
||||
VIEW_TYPE_TAG -> FilterTagHolder(parent).apply {
|
||||
itemView.setOnClickListener {
|
||||
setCheckedTag(boundData ?: return@setOnClickListener, !isChecked)
|
||||
}
|
||||
}
|
||||
else -> throw IllegalArgumentException("Unknown viewType $viewType")
|
||||
}
|
||||
|
||||
override fun getItemCount() = sortOrders.size + tags.size
|
||||
|
||||
override fun onBindViewHolder(holder: BaseViewHolder<*, Boolean, *>, position: Int) {
|
||||
when (holder) {
|
||||
is FilterSortHolder -> {
|
||||
val item = sortOrders[position]
|
||||
holder.bind(item, item == currentState.sortOrder)
|
||||
}
|
||||
is FilterTagHolder -> {
|
||||
val item = tags[position - sortOrders.size]
|
||||
holder.bind(item, item in currentState.tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int) = when (position) {
|
||||
in sortOrders.indices -> VIEW_TYPE_SORT
|
||||
else -> VIEW_TYPE_TAG
|
||||
}
|
||||
|
||||
fun setCheckedTag(tag: MangaTag, isChecked: Boolean) {
|
||||
currentState = if (tag in currentState.tags) {
|
||||
if (!isChecked) {
|
||||
currentState.copy(tags = currentState.tags - tag)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
if (isChecked) {
|
||||
currentState.copy(tags = currentState.tags + tag)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
val index = tags.indexOf(tag)
|
||||
if (index in tags.indices) {
|
||||
notifyItemChanged(sortOrders.size + index)
|
||||
}
|
||||
listener.onFilterChanged(currentState)
|
||||
}
|
||||
|
||||
fun setCheckedSort(sort: SortOrder) {
|
||||
if (sort != currentState.sortOrder) {
|
||||
val oldItemPos = sortOrders.indexOf(currentState.sortOrder)
|
||||
val newItemPos = sortOrders.indexOf(sort)
|
||||
currentState = currentState.copy(sortOrder = sort)
|
||||
if (oldItemPos in sortOrders.indices) {
|
||||
notifyItemChanged(oldItemPos)
|
||||
}
|
||||
if (newItemPos in sortOrders.indices) {
|
||||
notifyItemChanged(newItemPos)
|
||||
}
|
||||
listener.onFilterChanged(currentState)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val VIEW_TYPE_SORT = 0
|
||||
const val VIEW_TYPE_TAG = 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
|
||||
|
||||
class FilterAdapter2(
|
||||
listener: OnFilterChangedListener,
|
||||
) : AsyncListDifferDelegationAdapter<FilterItem>(
|
||||
FilterDiffCallback(),
|
||||
filterSortDelegate(listener),
|
||||
filterTagDelegate(listener),
|
||||
filterHeaderDelegate(),
|
||||
)
|
||||
@ -0,0 +1,47 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import com.hannesdorfmann.adapterdelegates4.dsl.adapterDelegateViewBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemCheckableMultipleBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemCheckableSingleBinding
|
||||
import org.koitharu.kotatsu.databinding.ItemFilterHeaderBinding
|
||||
|
||||
fun filterSortDelegate(
|
||||
listener: OnFilterChangedListener,
|
||||
) = adapterDelegateViewBinding<FilterItem.Sort, FilterItem, ItemCheckableSingleBinding>(
|
||||
{ layoutInflater, parent -> ItemCheckableSingleBinding.inflate(layoutInflater, parent, false) }
|
||||
) {
|
||||
|
||||
itemView.setOnClickListener {
|
||||
listener.onSortItemClick(item)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.root.setText(item.order.titleRes)
|
||||
binding.root.isChecked = item.isSelected
|
||||
}
|
||||
}
|
||||
|
||||
fun filterTagDelegate(
|
||||
listener: OnFilterChangedListener,
|
||||
) = adapterDelegateViewBinding<FilterItem.Tag, FilterItem, ItemCheckableMultipleBinding>(
|
||||
{ layoutInflater, parent -> ItemCheckableMultipleBinding.inflate(layoutInflater, parent, false) }
|
||||
) {
|
||||
|
||||
itemView.setOnClickListener {
|
||||
listener.onTagItemClick(item)
|
||||
}
|
||||
|
||||
bind {
|
||||
binding.root.text = item.tag.title
|
||||
binding.root.isChecked = item.isChecked
|
||||
}
|
||||
}
|
||||
|
||||
fun filterHeaderDelegate() = adapterDelegateViewBinding<FilterItem.Header, FilterItem, ItemFilterHeaderBinding>(
|
||||
{ layoutInflater, parent -> ItemFilterHeaderBinding.inflate(layoutInflater, parent, false) }
|
||||
) {
|
||||
|
||||
bind {
|
||||
binding.root.setText(item.titleResId)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
|
||||
class FilterDiffCallback : DiffUtil.ItemCallback<FilterItem>() {
|
||||
|
||||
override fun areItemsTheSame(oldItem: FilterItem, newItem: FilterItem): Boolean {
|
||||
return when {
|
||||
oldItem.javaClass != newItem.javaClass -> false
|
||||
oldItem is FilterItem.Header && newItem is FilterItem.Header -> {
|
||||
oldItem.titleResId == newItem.titleResId
|
||||
}
|
||||
oldItem is FilterItem.Tag && newItem is FilterItem.Tag -> {
|
||||
oldItem.tag == newItem.tag
|
||||
}
|
||||
oldItem is FilterItem.Sort && newItem is FilterItem.Sort -> {
|
||||
oldItem.order == newItem.order
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: FilterItem, newItem: FilterItem): Boolean {
|
||||
return when {
|
||||
oldItem is FilterItem.Header && newItem is FilterItem.Header -> true
|
||||
oldItem is FilterItem.Tag && newItem is FilterItem.Tag -> {
|
||||
oldItem.isChecked == newItem.isChecked
|
||||
}
|
||||
oldItem is FilterItem.Sort && newItem is FilterItem.Sort -> {
|
||||
oldItem.isSelected == newItem.isSelected
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getChangePayload(oldItem: FilterItem, newItem: FilterItem): Any? {
|
||||
val isCheckedChanged = when {
|
||||
oldItem is FilterItem.Tag && newItem is FilterItem.Tag -> {
|
||||
oldItem.isChecked != newItem.isChecked
|
||||
}
|
||||
oldItem is FilterItem.Sort && newItem is FilterItem.Sort -> {
|
||||
oldItem.isSelected != newItem.isSelected
|
||||
}
|
||||
else -> false
|
||||
}
|
||||
return if (isCheckedChanged) Unit else super.getChangePayload(oldItem, newItem)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
|
||||
sealed interface FilterItem {
|
||||
|
||||
class Header(
|
||||
@StringRes val titleResId: Int,
|
||||
) : FilterItem
|
||||
|
||||
class Sort(
|
||||
val order: SortOrder,
|
||||
val isSelected: Boolean,
|
||||
) : FilterItem
|
||||
|
||||
class Tag(
|
||||
val tag: MangaTag,
|
||||
val isChecked: Boolean,
|
||||
) : FilterItem
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.core.model.SortOrder
|
||||
import org.koitharu.kotatsu.databinding.ItemCheckableSingleBinding
|
||||
|
||||
class FilterSortHolder(parent: ViewGroup) :
|
||||
BaseViewHolder<SortOrder, Boolean, ItemCheckableSingleBinding>(
|
||||
ItemCheckableSingleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
) {
|
||||
|
||||
override fun onBind(data: SortOrder, extra: Boolean) {
|
||||
binding.root.setText(data.titleRes)
|
||||
binding.root.isChecked = extra
|
||||
}
|
||||
}
|
||||
@ -1,21 +0,0 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import org.koitharu.kotatsu.base.ui.list.BaseViewHolder
|
||||
import org.koitharu.kotatsu.core.model.MangaTag
|
||||
import org.koitharu.kotatsu.databinding.ItemCheckableMultipleBinding
|
||||
|
||||
class FilterTagHolder(parent: ViewGroup) :
|
||||
BaseViewHolder<MangaTag, Boolean, ItemCheckableMultipleBinding>(
|
||||
ItemCheckableMultipleBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
) {
|
||||
|
||||
val isChecked: Boolean
|
||||
get() = binding.root.isChecked
|
||||
|
||||
override fun onBind(data: MangaTag, extra: Boolean) {
|
||||
binding.root.text = data.title
|
||||
binding.root.isChecked = extra
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
package org.koitharu.kotatsu.list.ui.filter
|
||||
|
||||
import org.koitharu.kotatsu.core.model.MangaFilter
|
||||
interface OnFilterChangedListener {
|
||||
|
||||
fun interface OnFilterChangedListener {
|
||||
fun onSortItemClick(item: FilterItem.Sort)
|
||||
|
||||
fun onFilterChanged(filter: MangaFilter)
|
||||
fun onTagItemClick(item: FilterItem.Tag)
|
||||
}
|
||||
Loading…
Reference in New Issue