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(
private val streamItems: MutableList<StreamItem>,
private val showAllAtOnce: Boolean = true,
private val forceMode: LayoutMode = LayoutMode.RESPECT_PREF
) : RecyclerView.Adapter<VideosViewHolder>() {
private var visibleCount = minOf(10, streamItems.size)
override fun getItemCount(): Int {
return when {
showAllAtOnce -> streamItems.size
else -> minOf(streamItems.size, visibleCount)
}
}
override fun getItemCount() = streamItems.size
override fun getItemViewType(position: Int): Int {
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>) {
val feedSize = streamItems.size
streamItems.addAll(newItems)
@ -69,7 +53,7 @@ class VideosAdapter(
it.url?.toID() == videoId
}.takeIf { it > 0 } ?: return
streamItems.removeAt(index)
visibleCount--
notifyItemRemoved(index)
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.Companion.FILTER_SORT_REQUEST_KEY
import com.github.libretube.util.PlayingQueue
import com.github.libretube.util.deArrow
import com.google.android.material.chip.Chip
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class SubscriptionsFragment : DynamicLayoutManagerFragment() {
private var _binding: FragmentSubscriptionsBinding? = null
@ -60,7 +62,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
private var isCurrentTabSubChannels = false
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 selectedSortOrder = PreferenceHelper.getInt(PreferenceKeys.FEED_SORT_ORDER, 0)
set(value) {
@ -153,13 +157,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
}
binding.subFeed.addOnBottomReachedListener {
val binding = _binding ?: return@addOnBottomReachedListener
if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels) {
binding.subRefresh.isRefreshing = true
feedAdapter?.updateItems()
binding.subRefresh.isRefreshing = false
}
loadNextFeedItems()
}
// 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() {
binding.filterSort.setOnClickListener {
val activityCompat = context as AppCompatActivity
@ -332,9 +354,11 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched)
}
val sortedFeed = feed
val sorted = feed
.sortedBySelectedOrder()
.toMutableList()
sortedFeed.clear()
sortedFeed.addAll(sorted)
// add an "all caught up item"
if (selectedSortOrder == 0) {
@ -355,10 +379,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
binding.subFeed.isGone = notLoaded
binding.emptyFeed.isVisible = notLoaded
feedAdapter = VideosAdapter(
sortedFeed.toMutableList(),
showAllAtOnce = false
)
feedAdapter = VideosAdapter(mutableListOf())
loadNextFeedItems()
binding.subFeed.adapter = feedAdapter
binding.toggleSubs.text = getString(R.string.subscriptions)
@ -401,6 +424,11 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment() {
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) {
super.onConfigurationChanged(newConfig)
// 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
val fragment = navHostFragment?.childFragmentManager?.fragments
?.firstOrNull() as? SubscriptionsFragment
fragment?.feedAdapter?.removeItemById(videoId)
fragment?.removeItem(videoId)
}
setFragmentResult(VIDEO_OPTIONS_SHEET_REQUEST_KEY, bundleOf())
}