Fix favourites categories

This commit is contained in:
Koitharu
2021-01-06 07:57:20 +02:00
parent e674e0f36f
commit d1e17c8ec2
7 changed files with 60 additions and 31 deletions

View File

@@ -4,6 +4,7 @@ import androidx.collection.ArraySet
import androidx.room.withTransaction
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.withContext
import org.koitharu.kotatsu.core.db.MangaDatabase
@@ -61,13 +62,13 @@ class FavouritesRepository(private val db: MangaDatabase) {
fun observeCategories(): Flow<List<FavouriteCategory>> {
return db.favouriteCategoriesDao.observeAll().mapItems {
it.toFavouriteCategory()
}
}.distinctUntilChanged()
}
fun observeCategories(mangaId: Long): Flow<List<FavouriteCategory>> {
return db.favouritesDao.observe(mangaId).map { entity ->
entity?.categories?.map { it.toFavouriteCategory() }.orEmpty()
}
}.distinctUntilChanged()
}
fun observeCategoriesIds(mangaId: Long): Flow<List<Long>> {

View File

@@ -26,6 +26,7 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
private val editDelegate by lazy(LazyThreadSafetyMode.NONE) {
CategoriesEditDelegate(requireContext(), this)
}
private var pagerAdapter: FavouritesPagerAdapter? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -40,13 +41,22 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val adapter = FavouritesPagerAdapter(this, this)
viewModel.categories.value?.let {
adapter.replaceData(wrapCategories(it))
}
binding.pager.adapter = adapter
pagerAdapter = adapter
TabLayoutMediator(binding.tabs, binding.pager, adapter).attach()
viewModel.categories.observe(viewLifecycleOwner, ::onCategoriesChanged)
viewModel.onError.observe(viewLifecycleOwner, ::onError)
}
override fun onDestroyView() {
pagerAdapter = null
super.onDestroyView()
}
override fun onWindowInsetsChanged(insets: Insets) {
binding.tabs.updatePadding(
left = insets.left,
@@ -55,10 +65,7 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
}
private fun onCategoriesChanged(categories: List<FavouriteCategory>) {
val data = ArrayList<FavouriteCategory>(categories.size + 1)
data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date())
data += categories
(binding.pager.adapter as? FavouritesPagerAdapter)?.replaceData(data)
pagerAdapter?.replaceData(wrapCategories(categories))
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
@@ -109,6 +116,13 @@ class FavouritesContainerFragment : BaseFragment<FragmentFavouritesBinding>(),
viewModel.createCategory(name)
}
private fun wrapCategories(categories: List<FavouriteCategory>): List<FavouriteCategory> {
val data = ArrayList<FavouriteCategory>(categories.size + 1)
data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date())
data += categories
return data
}
companion object {
fun newInstance() = FavouritesContainerFragment()

View File

@@ -2,44 +2,55 @@ package org.koitharu.kotatsu.favourites.ui
import android.view.View
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.AsyncListDiffer
import androidx.recyclerview.widget.DiffUtil
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import org.koitharu.kotatsu.base.ui.list.AdapterUpdater
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
import org.koitharu.kotatsu.utils.ext.replaceWith
class FavouritesPagerAdapter(
fragment: Fragment,
private val longClickListener: FavouritesTabLongClickListener
) : FragmentStateAdapter(fragment),
) : FragmentStateAdapter(fragment.childFragmentManager, fragment.viewLifecycleOwner.lifecycle),
TabLayoutMediator.TabConfigurationStrategy, View.OnLongClickListener {
private val dataSet = ArrayList<FavouriteCategory>()
private val differ = AsyncListDiffer(this, DiffCallback())
override fun getItemCount() = dataSet.size
override fun getItemCount() = differ.currentList.size
override fun createFragment(position: Int): Fragment {
val item = dataSet[position]
val item = differ.currentList[position]
return FavouritesListFragment.newInstance(item.id)
}
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
val item = dataSet[position]
val item = differ.currentList[position]
tab.text = item.title
tab.view.tag = item
tab.view.setOnLongClickListener(this)
}
fun replaceData(data: List<FavouriteCategory>) {
val updater = AdapterUpdater(dataSet, data, FavouriteCategory::id)
dataSet.replaceWith(data)
updater(this)
differ.submitList(data)
}
override fun onLongClick(v: View): Boolean {
val item = v.tag as? FavouriteCategory ?: return false
return longClickListener.onTabLongClick(v, item)
}
private class DiffCallback : DiffUtil.ItemCallback<FavouriteCategory>() {
override fun areItemsTheSame(
oldItem: FavouriteCategory,
newItem: FavouriteCategory
): Boolean = oldItem.id == newItem.id
override fun areContentsTheSame(
oldItem: FavouriteCategory,
newItem: FavouriteCategory
): Boolean = oldItem.id == newItem.id && oldItem.title == newItem.title
}
}

View File

@@ -93,8 +93,6 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
}
private fun onCategoriesChanged(categories: List<FavouriteCategory>) {
// TODO check if not moved
adapter.items = categories
binding.textViewHolder.isVisible = categories.isEmpty()
}
@@ -124,14 +122,22 @@ class CategoriesActivity : BaseActivity<ActivityCategoriesBinding>(),
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val oldPos = viewHolder.bindingAdapterPosition
val newPos = target.bindingAdapterPosition
viewModel.reorderCategories(oldPos, newPos)
return true
}
): Boolean = true
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
override fun onMoved(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
fromPos: Int,
target: RecyclerView.ViewHolder,
toPos: Int,
x: Int,
y: Int
) {
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
viewModel.reorderCategories(fromPos, toPos)
}
}
companion object {

View File

@@ -4,7 +4,6 @@ import androidx.recyclerview.widget.DiffUtil
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import org.koitharu.kotatsu.base.ui.list.OnListItemClickListener
import org.koitharu.kotatsu.core.model.FavouriteCategory
import kotlin.jvm.internal.Intrinsics
class CategoriesAdapter(
onItemClickListener: OnListItemClickListener<FavouriteCategory>
@@ -26,7 +25,7 @@ class CategoriesAdapter(
}
override fun areContentsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean {
return Intrinsics.areEqual(oldItem, newItem)
return oldItem.id == newItem.id && oldItem.title == newItem.title
}
}
}

View File

@@ -6,6 +6,8 @@ import kotlinx.coroutines.Job
import org.koitharu.kotatsu.base.ui.BaseViewModel
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import java.util.*
import kotlin.collections.ArrayList
class FavouritesCategoriesViewModel(
private val repository: FavouritesRepository
@@ -40,8 +42,7 @@ class FavouritesCategoriesViewModel(
prevJob?.join()
val items = categories.value ?: error("This should not happen")
val ids = items.mapTo(ArrayList(items.size)) { it.id }
val item = ids.removeAt(oldPos)
ids.add(newPos, item)
Collections.swap(ids, oldPos, newPos)
repository.reorderCategories(ids)
}
}

View File

@@ -14,7 +14,6 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi
import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct
import org.koitharu.kotatsu.utils.ext.onFirst
class FavouritesListViewModel(
private val categoryId: Long,
@@ -38,8 +37,6 @@ class FavouritesListViewModel(
)
else -> list.toUi(mode)
}
}.onFirst {
isLoading.postValue(false)
}.catch {
emit(listOf(it.toErrorState(canRetry = false)))
}.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState))