Favourites container fixes

This commit is contained in:
Koitharu
2023-07-04 13:15:23 +03:00
parent ed672feebe
commit 7908eb1441
6 changed files with 83 additions and 41 deletions

View File

@@ -1,8 +1,11 @@
package org.koitharu.kotatsu.core.ui
import androidx.recyclerview.widget.AsyncDifferConfig
import androidx.recyclerview.widget.AsyncListDiffer.ListListener
import com.hannesdorfmann.adapterdelegates4.AdapterDelegate
import com.hannesdorfmann.adapterdelegates4.AsyncListDifferDelegationAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.flow.FlowCollector
import org.koitharu.kotatsu.core.util.ContinuationResumeRunnable
import org.koitharu.kotatsu.list.ui.ListModelDiffCallback
@@ -11,8 +14,12 @@ import kotlin.coroutines.suspendCoroutine
open class BaseListAdapter(
vararg delegates: AdapterDelegate<List<ListModel>>,
) : AsyncListDifferDelegationAdapter<ListModel>(ListModelDiffCallback, *delegates),
FlowCollector<List<ListModel>> {
) : AsyncListDifferDelegationAdapter<ListModel>(
AsyncDifferConfig.Builder(ListModelDiffCallback)
.setBackgroundThreadExecutor(Dispatchers.Default.limitedParallelism(2).asExecutor())
.build(),
*delegates,
), FlowCollector<List<ListModel>> {
override suspend fun emit(value: List<ListModel>) = suspendCoroutine { cont ->
setItems(value, ContinuationResumeRunnable(cont))

View File

@@ -0,0 +1,29 @@
package org.koitharu.kotatsu.favourites.ui.container
import org.koitharu.kotatsu.list.ui.model.ListModel
class FavouriteTabModel(
val id: Long,
val title: String,
) : ListModel {
override fun areItemsTheSame(other: ListModel): Boolean {
return other is FavouriteTabModel && other.id == id
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as FavouriteTabModel
if (id != other.id) return false
return title == other.title
}
override fun hashCode(): Int {
var result = id.hashCode()
result = 31 * result + title.hashCode()
return result
}
}

View File

@@ -1,25 +1,31 @@
package org.koitharu.kotatsu.favourites.ui.container
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.AdapterListUpdateCallback
import androidx.recyclerview.widget.AsyncDifferConfig
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.TabConfigurationStrategy
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.flow.FlowCollector
import org.koitharu.kotatsu.core.model.FavouriteCategory
import org.koitharu.kotatsu.core.util.ContinuationResumeRunnable
import org.koitharu.kotatsu.favourites.ui.list.FavouritesListFragment
import kotlin.coroutines.suspendCoroutine
class FavouritesContainerAdapter(fragment: Fragment) : FragmentStateAdapter(
fragment.childFragmentManager,
fragment.viewLifecycleOwner.lifecycle,
),
class FavouritesContainerAdapter(fragment: Fragment) :
FragmentStateAdapter(fragment.childFragmentManager, fragment.viewLifecycleOwner.lifecycle),
TabConfigurationStrategy,
FlowCollector<List<FavouriteCategory>> {
FlowCollector<List<FavouriteTabModel>> {
private val differ = AsyncListDiffer(this, DiffCallback())
private val differ = AsyncListDiffer(
AdapterListUpdateCallback(this),
AsyncDifferConfig.Builder(DiffCallback())
.setBackgroundThreadExecutor(Dispatchers.Default.limitedParallelism(2).asExecutor())
.build(),
)
override fun getItemCount(): Int = differ.currentList.size
@@ -27,8 +33,13 @@ class FavouritesContainerAdapter(fragment: Fragment) : FragmentStateAdapter(
return differ.currentList[position].id
}
override fun containsItem(itemId: Long): Boolean {
return differ.currentList.any { x -> x.id == itemId }
}
override fun createFragment(position: Int): Fragment {
return FavouritesListFragment.newInstance(getItemId(position))
val item = differ.currentList[position]
return FavouritesListFragment.newInstance(item.id)
}
override fun onConfigureTab(tab: TabLayout.Tab, position: Int) {
@@ -37,18 +48,18 @@ class FavouritesContainerAdapter(fragment: Fragment) : FragmentStateAdapter(
tab.tag = item
}
override suspend fun emit(value: List<FavouriteCategory>) = suspendCoroutine { cont ->
override suspend fun emit(value: List<FavouriteTabModel>) = suspendCoroutine { cont ->
differ.submitList(value, ContinuationResumeRunnable(cont))
}
private class DiffCallback : DiffUtil.ItemCallback<FavouriteCategory>() {
private class DiffCallback : DiffUtil.ItemCallback<FavouriteTabModel>() {
override fun areItemsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean {
override fun areItemsTheSame(oldItem: FavouriteTabModel, newItem: FavouriteTabModel): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: FavouriteCategory, newItem: FavouriteCategory): Boolean {
return oldItem.title == newItem.title
override fun areContentsTheSame(oldItem: FavouriteTabModel, newItem: FavouriteTabModel): Boolean {
return oldItem == newItem
}
}
}

View File

@@ -1,7 +1,13 @@
package org.koitharu.kotatsu.favourites.ui.container
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.plus
import org.koitharu.kotatsu.core.ui.BaseViewModel
import org.koitharu.kotatsu.core.util.ext.mapItems
import org.koitharu.kotatsu.favourites.domain.FavouritesRepository
import javax.inject.Inject
@@ -11,4 +17,6 @@ class FavouritesContainerViewModel @Inject constructor(
) : BaseViewModel() {
val categories = favouritesRepository.observeCategories()
.mapItems { FavouriteTabModel(it.id, it.title) }
.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, emptyList())
}

View File

@@ -76,9 +76,6 @@ abstract class MangaListFragment :
private var selectionController: ListSelectionController? = null
private var spanResolver: MangaListSpanResolver? = null
private val spanSizeLookup = SpanSizeLookup()
private val listCommitCallback = Runnable {
spanSizeLookup.invalidateCache()
}
open val isSwipeRefreshEnabled = true
protected abstract val viewModel: MangaListViewModel
@@ -166,8 +163,9 @@ abstract class MangaListFragment :
viewModel.onRefresh()
}
private fun onListChanged(list: List<ListModel>) {
listAdapter?.setItems(list, listCommitCallback)
private suspend fun onListChanged(list: List<ListModel>) {
listAdapter?.emit(list)
spanSizeLookup.invalidateCache()
}
private fun resolveException(e: Throwable) {
@@ -341,8 +339,7 @@ abstract class MangaListFragment :
}
override fun getSpanSize(position: Int): Int {
val total =
(requireViewBinding().recyclerView.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
val total = (viewBinding?.recyclerView?.layoutManager as? GridLayoutManager)?.spanCount ?: return 1
return when (listAdapter?.getItemViewType(position)) {
ITEM_TYPE_MANGA_GRID -> 1
else -> total

View File

@@ -1,31 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
<LinearLayout
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="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="0dp"
app:elevation="0dp"
app:liftOnScroll="false">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:tabGravity="start"
app:tabMode="scrollable" />
</com.google.android.material.appbar.AppBarLayout>
app:tabGravity="start"
app:tabMode="scrollable" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior" />
android:layout_height="match_parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>