From 675e95da2b0a4ef36dbb8aa92aec4379873bd0ac Mon Sep 17 00:00:00 2001 From: Koitharu Date: Sat, 11 Sep 2021 16:01:15 +0300 Subject: [PATCH] Show current filter in list header --- .../kotatsu/base/ui/widgets/ChipsView.kt | 17 +++++++++++++- .../kotatsu/list/ui/MangaListFragment.kt | 10 ++++++-- .../kotatsu/list/ui/MangaListViewModel.kt | 3 +++ .../list/ui/adapter/CurrentFilterAD.kt | 23 +++++++++++++++++++ .../list/ui/adapter/MangaListAdapter.kt | 6 ++++- .../list/ui/model/CurrentFilterModel.kt | 7 ++++++ .../remotelist/ui/RemoteListViewModel.kt | 19 ++++++++++++++- .../main/res/layout/item_current_filter.xml | 15 ++++++++++++ app/src/main/res/values-ru/strings.xml | 1 + app/src/main/res/values/strings.xml | 1 + 10 files changed, 97 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/CurrentFilterAD.kt create mode 100644 app/src/main/java/org/koitharu/kotatsu/list/ui/model/CurrentFilterModel.kt create mode 100644 app/src/main/res/layout/item_current_filter.xml diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/ChipsView.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/ChipsView.kt index 84a34261e..eb867ec0f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/ChipsView.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/widgets/ChipsView.kt @@ -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?) + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt index 7c06f6943..1fa44344c 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListFragment.kt @@ -71,7 +71,13 @@ abstract class MangaListFragment : BaseFragment(), 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(), 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 } } diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt index 0fa365629..3d94d1b65 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/MangaListViewModel.kt @@ -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() diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/CurrentFilterAD.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/CurrentFilterAD.kt new file mode 100644 index 000000000..4848a27b8 --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/CurrentFilterAD.kt @@ -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( + { 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) + } +} \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt index 71b4c2064..cc4c80967 100644 --- a/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/adapter/MangaListAdapter.kt @@ -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, - onRetryClick: (Throwable) -> Unit + onRetryClick: (Throwable) -> Unit, + onTagRemoveClick: (MangaTag) -> Unit, ) : AsyncListDifferDelegationAdapter(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, 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 } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/list/ui/model/CurrentFilterModel.kt b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/CurrentFilterModel.kt new file mode 100644 index 000000000..32cebb25c --- /dev/null +++ b/app/src/main/java/org/koitharu/kotatsu/list/ui/model/CurrentFilterModel.kt @@ -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, +) : ListModel \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index 870480e35..d8df693a3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -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(list.size + 2) + val result = ArrayList(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 { diff --git a/app/src/main/res/layout/item_current_filter.xml b/app/src/main/res/layout/item_current_filter.xml new file mode 100644 index 000000000..1014b0fdd --- /dev/null +++ b/app/src/main/res/layout/item_current_filter.xml @@ -0,0 +1,15 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 35be37286..862eb79f5 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -236,4 +236,5 @@ Авторизация выполнена Авторизация в %s не поддерживается Вы выйдете из всех источников, в которых Вы авторизованы + Жанры \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f82f4b5d9..e09f1ba22 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -239,4 +239,5 @@ Authorization complete Authorization on %s is not supported You will be logged out from all sources that you are authorized in + Genres \ No newline at end of file