fix: don't block ui thread while filtering watched videos

This commit is contained in:
Bnyro 2025-04-23 19:37:41 +02:00
parent 4445df6ede
commit c255d1b1c2
No known key found for this signature in database
4 changed files with 20 additions and 21 deletions

View File

@ -1,7 +1,5 @@
package com.github.libretube.api package com.github.libretube.api
import android.content.Context
import com.github.libretube.R
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.db.obj.SubscriptionsFeedItem import com.github.libretube.db.obj.SubscriptionsFeedItem
import com.github.libretube.helpers.PreferenceHelper import com.github.libretube.helpers.PreferenceHelper
@ -14,8 +12,6 @@ import com.github.libretube.repo.PipedAccountFeedRepository
import com.github.libretube.repo.PipedLocalSubscriptionsRepository import com.github.libretube.repo.PipedLocalSubscriptionsRepository
import com.github.libretube.repo.PipedNoAccountFeedRepository import com.github.libretube.repo.PipedNoAccountFeedRepository
import com.github.libretube.repo.SubscriptionsRepository import com.github.libretube.repo.SubscriptionsRepository
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kotlinx.coroutines.runBlocking
object SubscriptionHelper { object SubscriptionHelper {
/** /**

View File

@ -102,7 +102,7 @@ object DatabaseHelper {
return unfinished xor isVideoWatched(watchHistoryItem.videoId, watchHistoryItem.duration ?: 0) return unfinished xor isVideoWatched(watchHistoryItem.videoId, watchHistoryItem.duration ?: 0)
} }
fun filterByStatusAndWatchPosition( suspend fun filterByStatusAndWatchPosition(
streams: List<StreamItem>, streams: List<StreamItem>,
hideWatched: Boolean hideWatched: Boolean
): List<StreamItem> { ): List<StreamItem> {
@ -116,13 +116,8 @@ object DatabaseHelper {
else -> true else -> true
} }
} }
if (!hideWatched) return streamItems
return if (hideWatched) { return filterUnwatched(streamItems)
runBlocking {
filterUnwatched(streamItems)
}
} else {
streamItems
}
} }
} }

View File

@ -28,6 +28,7 @@ import com.github.libretube.ui.extensions.setupFragmentAnimation
import com.github.libretube.ui.models.HomeViewModel import com.github.libretube.ui.models.HomeViewModel
import com.github.libretube.ui.models.SubscriptionsViewModel import com.github.libretube.ui.models.SubscriptionsViewModel
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.coroutines.runBlocking
class HomeFragment : Fragment(R.layout.fragment_home) { class HomeFragment : Fragment(R.layout.fragment_home) {
@ -150,7 +151,7 @@ class HomeFragment : Fragment(R.layout.fragment_home) {
makeVisible(binding.featuredRV, binding.featuredTV) makeVisible(binding.featuredRV, binding.featuredTV)
val hideWatched = PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false) val hideWatched = PreferenceHelper.getBoolean(PreferenceKeys.HIDE_WATCHED_FROM_FEED, false)
val feedVideos = streamItems val feedVideos = streamItems
.let { DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched) } .let { runBlocking { DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched) } }
.take(20) .take(20)
feedAdapter.submitList(feedVideos) feedAdapter.submitList(feedVideos)

View File

@ -142,7 +142,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
var alreadyShowedFeedOnce = false var alreadyShowedFeedOnce = false
viewModel.videoFeed.observe(viewLifecycleOwner) { viewModel.videoFeed.observe(viewLifecycleOwner) {
if (!viewModel.isCurrentTabSubChannels && it != null) { if (!viewModel.isCurrentTabSubChannels && it != null) {
showFeed(!alreadyShowedFeedOnce) lifecycleScope.launch {
showFeed(!alreadyShowedFeedOnce)
}
alreadyShowedFeedOnce = true alreadyShowedFeedOnce = true
} }
} }
@ -179,7 +181,9 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
binding.subRefresh.isRefreshing = true binding.subRefresh.isRefreshing = true
viewModel.isCurrentTabSubChannels = !viewModel.isCurrentTabSubChannels viewModel.isCurrentTabSubChannels = !viewModel.isCurrentTabSubChannels
if (viewModel.isCurrentTabSubChannels) showSubscriptions() else showFeed() lifecycleScope.launch {
if (viewModel.isCurrentTabSubChannels) showSubscriptions() else showFeed()
}
binding.subChannels.isVisible = viewModel.isCurrentTabSubChannels binding.subChannels.isVisible = viewModel.isCurrentTabSubChannels
binding.subFeed.isGone = viewModel.isCurrentTabSubChannels binding.subFeed.isGone = viewModel.isCurrentTabSubChannels
@ -204,7 +208,10 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
binding.channelGroups.setOnCheckedStateChangeListener { group, _ -> binding.channelGroups.setOnCheckedStateChangeListener { group, _ ->
selectedFilterGroup = group.children.indexOfFirst { it.id == group.checkedChipId } selectedFilterGroup = group.children.indexOfFirst { it.id == group.checkedChipId }
if (viewModel.isCurrentTabSubChannels) showSubscriptions() else showFeed()
lifecycleScope.launch {
if (viewModel.isCurrentTabSubChannels) showSubscriptions() else showFeed()
}
} }
channelGroupsModel.groups.observe(viewLifecycleOwner) { channelGroupsModel.groups.observe(viewLifecycleOwner) {
@ -256,7 +263,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
selectedSortOrder = resultBundle.getInt(IntentData.sortOptions) selectedSortOrder = resultBundle.getInt(IntentData.sortOptions)
hideWatched = resultBundle.getBoolean(IntentData.hideWatched) hideWatched = resultBundle.getBoolean(IntentData.hideWatched)
showUpcoming = resultBundle.getBoolean(IntentData.showUpcoming) showUpcoming = resultBundle.getBoolean(IntentData.showUpcoming)
showFeed() lifecycleScope.launch { showFeed() }
} }
FilterSortBottomSheet() FilterSortBottomSheet()
@ -283,7 +290,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
_binding = null _binding = null
} }
private fun playByGroup(groupIndex: Int) { private suspend fun playByGroup(groupIndex: Int) {
val streams = viewModel.videoFeed.value.orEmpty() val streams = viewModel.videoFeed.value.orEmpty()
.filterByGroup(groupIndex) .filterByGroup(groupIndex)
.let { DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched) } .let { DatabaseHelper.filterByStatusAndWatchPosition(it, hideWatched) }
@ -306,7 +313,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
binding.chipAll.isChecked = selectedFilterGroup == 0 binding.chipAll.isChecked = selectedFilterGroup == 0
binding.chipAll.setOnLongClickListener { binding.chipAll.setOnLongClickListener {
playByGroup(0) lifecycleScope.launch { playByGroup(0) }
true true
} }
@ -321,7 +328,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
text = group.name text = group.name
setOnLongClickListener { setOnLongClickListener {
// the index must be increased by one to skip the "all channels" group button // the index must be increased by one to skip the "all channels" group button
playByGroup(index + 1) lifecycleScope.launch { playByGroup(index + 1) }
true true
} }
} }
@ -360,7 +367,7 @@ class SubscriptionsFragment : DynamicLayoutManagerFragment(R.layout.fragment_sub
else -> this else -> this
} }
private fun showFeed(restoreScrollState: Boolean = true) { private suspend fun showFeed(restoreScrollState: Boolean = true) {
val binding = _binding ?: return val binding = _binding ?: return
val videoFeed = viewModel.videoFeed.value ?: return val videoFeed = viewModel.videoFeed.value ?: return