From 481828c0f476428aa8947751e23d151d4729fad9 Mon Sep 17 00:00:00 2001 From: "Thomas W." Date: Mon, 10 Feb 2025 17:54:37 +0100 Subject: [PATCH] fix: multiple recyclerview adapter regressions in SubscriptionsFragment (#7085) * Fix IndexOutOfBoundsException: Inconsistency detected. Invalid view holder adapter positionViewHolder * Fix new videos not being added to feed * Remove obsolete sortedFeed variable * Display items from start --- .../ui/adapters/SubscriptionChannelAdapter.kt | 10 ---- .../ui/fragments/SubscriptionsFragment.kt | 49 ++++++++----------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt b/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt index 33dc6451c..d026670e8 100644 --- a/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt +++ b/app/src/main/java/com/github/libretube/ui/adapters/SubscriptionChannelAdapter.kt @@ -18,9 +18,6 @@ import com.github.libretube.ui.viewholders.SubscriptionChannelViewHolder class SubscriptionChannelAdapter : ListAdapter(DiffUtilItemCallback()) { - private var visibleCount = 20 - - override fun getItemCount() = minOf(visibleCount, currentList.size) override fun onCreateViewHolder( parent: ViewGroup, @@ -31,13 +28,6 @@ class SubscriptionChannelAdapter : return SubscriptionChannelViewHolder(binding) } - fun updateItems() { - val oldSize = visibleCount - visibleCount += minOf(10, currentList.size - oldSize) - if (visibleCount == oldSize) return - notifyItemRangeInserted(oldSize, visibleCount) - } - override fun onBindViewHolder(holder: SubscriptionChannelViewHolder, position: Int) { val subscription = getItem(holder.bindingAdapterPosition) diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt index 48234b7a0..573e205b5 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SubscriptionsFragment.kt @@ -65,8 +65,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub private var isAppBarFullyExpanded = true private var feedAdapter = VideosAdapter() - private val sortedFeed: MutableList = mutableListOf() - private var selectedSortOrder = PreferenceHelper.getInt(PreferenceKeys.FEED_SORT_ORDER, 0) set(value) { PreferenceHelper.putInt(PreferenceKeys.FEED_SORT_ORDER, value) @@ -175,15 +173,10 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub if (viewModel.subscriptions.value != null && isCurrentTabSubChannels) { binding.subRefresh.isRefreshing = true - channelsAdapter.updateItems() binding.subRefresh.isRefreshing = false } } - binding.subFeed.addOnBottomReachedListener { - loadNextFeedItems() - } - // add some extra margin to the subscribed channels while the mini player is visible // otherwise the last channel would be invisible playerModel.isMiniPlayerVisible.observe(viewLifecycleOwner) { @@ -192,8 +185,8 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub } } - binding.channelGroups.setOnCheckedStateChangeListener { group, checkedIds -> - selectedFilterGroup = group.children.indexOfFirst { it.id == checkedIds.first() } + binding.channelGroups.setOnCheckedStateChangeListener { group, _ -> + selectedFilterGroup = group.children.indexOfFirst { it.id == group.checkedChipId } if (isCurrentTabSubChannels) showSubscriptions() else showFeed() } @@ -231,25 +224,22 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub } } - private fun loadNextFeedItems() { + private fun loadFeedItems(sortedFeed: List) { val binding = _binding ?: return - val hasMore = sortedFeed.size > feedAdapter.itemCount - if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels && !binding.subRefresh.isRefreshing && hasMore) { + if (viewModel.videoFeed.value != null && !isCurrentTabSubChannels && !binding.subRefresh.isRefreshing) { binding.subRefresh.isRefreshing = true lifecycleScope.launch { - val toIndex = minOf(feedAdapter.itemCount + 10, sortedFeed.size) - - var streamItemsToInsert = sortedFeed - .subList(feedAdapter.itemCount, toIndex) - .toList() - - withContext(Dispatchers.IO) { - runCatching { streamItemsToInsert = streamItemsToInsert.deArrow() } + val streamItemsToInsert = sortedFeed.let { + withContext(Dispatchers.IO) { + runCatching { it.deArrow() }.getOrDefault(it) + } } - feedAdapter.insertItems(streamItemsToInsert) + feedAdapter.submitList(streamItemsToInsert) { + binding.subFeed.scrollToPosition(0) + } binding.subRefresh.isRefreshing = false } } @@ -383,15 +373,13 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub val sorted = feed .sortedBySelectedOrder() .toMutableList() - sortedFeed.clear() - sortedFeed.addAll(sorted) // add an "all caught up item" if (selectedSortOrder == 0) { val lastCheckedFeedTime = PreferenceHelper.getLastCheckedFeedTime() val caughtUpIndex = feed.indexOfFirst { it.uploaded <= lastCheckedFeedTime && !it.isUpcoming } if (caughtUpIndex > 0) { - sortedFeed.add( + sorted.add( caughtUpIndex, StreamItem(type = VideosAdapter.CAUGHT_UP_STREAM_TYPE) ) @@ -404,13 +392,13 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub val notLoaded = viewModel.videoFeed.value.isNullOrEmpty() binding.subFeed.isGone = notLoaded binding.emptyFeed.isVisible = notLoaded - loadNextFeedItems() + loadFeedItems(sorted) binding.toggleSubs.text = getString(R.string.subscriptions) feed.firstOrNull { !it.isUpcoming }?.uploaded?.let { PreferenceHelper.setLastFeedWatchedTime(it) - }; + } } @SuppressLint("SetTextI18n") @@ -423,9 +411,13 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub ) if (legacySubscriptions) { - legacySubscriptionsAdapter.submitList(subscriptions) + legacySubscriptionsAdapter.submitList(subscriptions) { + binding.subFeed.scrollToPosition(0) + } } else { - channelsAdapter.submitList(subscriptions) + channelsAdapter.submitList(subscriptions) { + binding.subFeed.scrollToPosition(0) + } } binding.subRefresh.isRefreshing = false @@ -442,7 +434,6 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub fun removeItem(videoId: String) { feedAdapter.removeItemById(videoId) - sortedFeed.removeAll { it.url?.toID() != videoId } } override fun onConfigurationChanged(newConfig: Configuration) {