diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt index ddd14e256..c409bc1ac 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/domain/FavouritesRepository.kt @@ -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> { return db.favouriteCategoriesDao.observeAll().mapItems { it.toFavouriteCategory() - } + }.distinctUntilChanged() } fun observeCategories(mangaId: Long): Flow> { return db.favouritesDao.observe(mangaId).map { entity -> entity?.categories?.map { it.toFavouriteCategory() }.orEmpty() - } + }.distinctUntilChanged() } fun observeCategoriesIds(mangaId: Long): Flow> { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt index 8a8b740c2..41ef1f802 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesContainerFragment.kt @@ -26,6 +26,7 @@ class FavouritesContainerFragment : BaseFragment(), 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(), 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(), } private fun onCategoriesChanged(categories: List) { - val data = ArrayList(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(), viewModel.createCategory(name) } + private fun wrapCategories(categories: List): List { + val data = ArrayList(categories.size + 1) + data += FavouriteCategory(0L, getString(R.string.all_favourites), -1, Date()) + data += categories + return data + } + companion object { fun newInstance() = FavouritesContainerFragment() diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt index 8da63e64c..5fa02d548 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/FavouritesPagerAdapter.kt @@ -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() + 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) { - 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() { + + 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 + } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt index 5efac4e2e..f28231c98 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesActivity.kt @@ -93,8 +93,6 @@ class CategoriesActivity : BaseActivity(), } private fun onCategoriesChanged(categories: List) { - // TODO check if not moved - adapter.items = categories binding.textViewHolder.isVisible = categories.isEmpty() } @@ -124,14 +122,22 @@ class CategoriesActivity : BaseActivity(), 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 { diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt index aa6d262b0..5bc88aa05 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/CategoriesAdapter.kt @@ -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 @@ -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 } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt index 428bf4864..8c648a47b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/categories/FavouritesCategoriesViewModel.kt @@ -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) } } diff --git a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt index b88e37974..bdbd4991a 100644 --- a/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/favourites/ui/list/FavouritesListViewModel.kt @@ -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))