feat: add pagination to dearrow in feed

This commit is contained in:
Bnyro 2024-05-06 14:05:50 +02:00
parent ccaedfafbc
commit 800d6704cf
3 changed files with 44 additions and 32 deletions

View File

@ -34,30 +34,14 @@ import com.github.libretube.util.TextUtils
class VideosAdapter( class VideosAdapter(
private val streamItems: MutableList<StreamItem>, private val streamItems: MutableList<StreamItem>,
private val showAllAtOnce: Boolean = true,
private val forceMode: LayoutMode = LayoutMode.RESPECT_PREF private val forceMode: LayoutMode = LayoutMode.RESPECT_PREF
) : RecyclerView.Adapter<VideosViewHolder>() { ) : RecyclerView.Adapter<VideosViewHolder>() {
override fun getItemCount() = streamItems.size
private var visibleCount = minOf(10, streamItems.size)
override fun getItemCount(): Int {
return when {
showAllAtOnce -> streamItems.size
else -> minOf(streamItems.size, visibleCount)
}
}
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {
return if (streamItems[position].type == CAUGHT_UP_STREAM_TYPE) CAUGHT_UP_TYPE else NORMAL_TYPE return if (streamItems[position].type == CAUGHT_UP_STREAM_TYPE) CAUGHT_UP_TYPE else NORMAL_TYPE
} }
fun updateItems() {
val oldSize = visibleCount
visibleCount += minOf(10, streamItems.size - oldSize)
if (visibleCount == oldSize) return
notifyItemRangeInserted(oldSize, visibleCount)
}
fun insertItems(newItems: List<StreamItem>) { fun insertItems(newItems: List<StreamItem>) {
val feedSize = streamItems.size val feedSize = streamItems.size
streamItems.addAll(newItems) streamItems.addAll(newItems)
@ -69,7 +53,7 @@ class VideosAdapter(
it.url?.toID() == videoId it.url?.toID() == videoId
}.takeIf { it > 0 } ?: return }.takeIf { it > 0 } ?: return
streamItems.removeAt(index) streamItems.removeAt(index)
visibleCount--
notifyItemRemoved(index) notifyItemRemoved(index)
notifyItemRangeChanged(index, itemCount) notifyItemRangeChanged(index, itemCount)
} }

View File

@ -45,9 +45,11 @@ import com.github.libretube.ui.sheets.ChannelGroupsSheet
import com.github.libretube.ui.sheets.FilterSortBottomSheet import com.github.libretube.ui.sheets.FilterSortBottomSheet
import com.github.libretube.ui.sheets.FilterSortBottomSheet.Companion.FILTER_SORT_REQUEST_KEY import com.github.libretube.ui.sheets.FilterSortBottomSheet.Companion.FILTER_SORT_REQUEST_KEY
import com.github.libretube.util.PlayingQueue import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.deArrow
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class SubscriptionsFragment : DynamicLayoutManagerFragment() { class SubscriptionsFragment : DynamicLayoutManagerFragment() {
private var _binding: FragmentSubscriptionsBinding? = null private var _binding: FragmentSubscriptionsBinding? = null
@ -60,7 +62,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
private var isCurrentTabSubChannels = false private var isCurrentTabSubChannels = false
private var isAppBarFullyExpanded = true private var isAppBarFullyExpanded = true
var feedAdapter: VideosAdapter? = null private var feedAdapter: VideosAdapter? = null
private val sortedFeed: MutableList<StreamItem> = mutableListOf()
private var channelsAdapter: SubscriptionChannelAdapter? = null private var channelsAdapter: SubscriptionChannelAdapter? = null
private var selectedSortOrder = PreferenceHelper.getInt(PreferenceKeys.FEED_SORT_ORDER, 0) private var selectedSortOrder = PreferenceHelper.getInt(PreferenceKeys.FEED_SORT_ORDER, 0)
set(value) { set(value) {
@ -153,13 +157,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
} }
binding.subFeed.addOnBottomReachedListener { binding.subFeed.addOnBottomReachedListener {
val binding = _binding ?: return@addOnBottomReachedListener loadNextFeedItems()
if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels) {
binding.subRefresh.isRefreshing = true
feedAdapter?.updateItems()
binding.subRefresh.isRefreshing = false
}
} }
// add some extra margin to the subscribed channels while the mini player is visible // add some extra margin to the subscribed channels while the mini player is visible
@ -205,6 +203,30 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
} }
} }
private fun loadNextFeedItems() {
val binding = _binding ?: return
val feedAdapter = feedAdapter ?: return
val hasMore = sortedFeed.size > feedAdapter.itemCount
if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels && !binding.subRefresh.isRefreshing && hasMore) {
binding.subRefresh.isRefreshing = true
lifecycleScope.launch(Dispatchers.IO) {
val toIndex = minOf(feedAdapter.itemCount + 10, sortedFeed.size)
val streamItemsToInsert = sortedFeed
.subList(feedAdapter.itemCount, toIndex)
.deArrow()
withContext(Dispatchers.Main) {
feedAdapter.insertItems(streamItemsToInsert)
binding.subRefresh.isRefreshing = false
}
}
}
}
private fun setupSortAndFilter() { private fun setupSortAndFilter() {
binding.filterSort.setOnClickListener { binding.filterSort.setOnClickListener {
val activityCompat = context as AppCompatActivity val activityCompat = context as AppCompatActivity
@ -332,9 +354,11 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched) DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched)
} }
val sortedFeed = feed val sorted = feed
.sortedBySelectedOrder() .sortedBySelectedOrder()
.toMutableList() .toMutableList()
sortedFeed.clear()
sortedFeed.addAll(sorted)
// add an "all caught up item" // add an "all caught up item"
if (selectedSortOrder == 0) { if (selectedSortOrder == 0) {
@ -355,10 +379,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
binding.subFeed.isGone = notLoaded binding.subFeed.isGone = notLoaded
binding.emptyFeed.isVisible = notLoaded binding.emptyFeed.isVisible = notLoaded
feedAdapter = VideosAdapter( feedAdapter = VideosAdapter(mutableListOf())
sortedFeed.toMutableList(), loadNextFeedItems()
showAllAtOnce = false
)
binding.subFeed.adapter = feedAdapter binding.subFeed.adapter = feedAdapter
binding.toggleSubs.text = getString(R.string.subscriptions) binding.toggleSubs.text = getString(R.string.subscriptions)
@ -401,6 +424,11 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
binding.toggleSubs.text = "${getString(R.string.subscriptions)} ($subCount)" binding.toggleSubs.text = "${getString(R.string.subscriptions)} ($subCount)"
} }
fun removeItem(videoId: String) {
feedAdapter?.removeItemById(videoId)
sortedFeed.removeAll { it.url!!.toID() != videoId }
}
override fun onConfigurationChanged(newConfig: Configuration) { override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig) super.onConfigurationChanged(newConfig)
// manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473 // manually restore the recyclerview state due to https://github.com/material-components/material-components-android/issues/3473

View File

@ -111,7 +111,7 @@ class VideoOptionsBottomSheet : BaseBottomSheet() {
// get the current fragment // get the current fragment
val fragment = navHostFragment?.childFragmentManager?.fragments val fragment = navHostFragment?.childFragmentManager?.fragments
?.firstOrNull() as? SubscriptionsFragment ?.firstOrNull() as? SubscriptionsFragment
fragment?.feedAdapter?.removeItemById(videoId) fragment?.removeItem(videoId)
} }
setFragmentResult(VIDEO_OPTIONS_SHEET_REQUEST_KEY, bundleOf()) setFragmentResult(VIDEO_OPTIONS_SHEET_REQUEST_KEY, bundleOf())
} }