mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-28 07:50:31 +05:30
Merge pull request #7169 from Bnyro/master
refactor: move watch history logic to view model
This commit is contained in:
commit
70bd87a002
@ -90,12 +90,10 @@ object DatabaseHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun filterByWatchStatus(
|
suspend fun filterByWatchStatus(
|
||||||
streams: List<WatchHistoryItem>,
|
watchHistoryItem: WatchHistoryItem,
|
||||||
unfinished: Boolean = true
|
unfinished: Boolean = true
|
||||||
): List<WatchHistoryItem> {
|
): Boolean {
|
||||||
return streams.filter {
|
return unfinished xor isVideoWatched(watchHistoryItem.videoId, watchHistoryItem.duration ?: 0)
|
||||||
unfinished xor isVideoWatched(it.videoId, it.duration ?: 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun filterByStatusAndWatchPosition(
|
fun filterByStatusAndWatchPosition(
|
||||||
|
@ -22,30 +22,11 @@ import com.github.libretube.util.TextUtils
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
class WatchHistoryAdapter :
|
class WatchHistoryAdapter :
|
||||||
ListAdapter<WatchHistoryItem, WatchHistoryViewHolder>(DiffUtilItemCallback()) {
|
ListAdapter<WatchHistoryItem, WatchHistoryViewHolder>(DiffUtilItemCallback()) {
|
||||||
|
|
||||||
fun removeFromWatchHistory(position: Int) {
|
|
||||||
val history = getItem(position)
|
|
||||||
runBlocking(Dispatchers.IO) {
|
|
||||||
DatabaseHolder.Database.watchHistoryDao().delete(history)
|
|
||||||
}
|
|
||||||
val updatedList = currentList.toMutableList().also {
|
|
||||||
it.removeAt(position)
|
|
||||||
}
|
|
||||||
submitList(updatedList)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun insertItems(items: List<WatchHistoryItem>) {
|
|
||||||
val updatedList = currentList.toMutableList().also {
|
|
||||||
it.addAll(items)
|
|
||||||
}
|
|
||||||
submitList(updatedList)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {
|
||||||
val layoutInflater = LayoutInflater.from(parent.context)
|
val layoutInflater = LayoutInflater.from(parent.context)
|
||||||
val binding = VideoRowBinding.inflate(layoutInflater, parent, false)
|
val binding = VideoRowBinding.inflate(layoutInflater, parent, false)
|
||||||
|
@ -2,23 +2,19 @@ package com.github.libretube.ui.fragments
|
|||||||
|
|
||||||
import android.content.res.Configuration
|
import android.content.res.Configuration
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
|
||||||
import android.os.Looper
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.core.os.postDelayed
|
|
||||||
import androidx.core.view.isGone
|
import androidx.core.view.isGone
|
||||||
import androidx.core.view.isVisible
|
import androidx.core.view.isVisible
|
||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
|
import androidx.fragment.app.viewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.recyclerview.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.room.withTransaction
|
import androidx.room.withTransaction
|
||||||
import com.github.libretube.R
|
import com.github.libretube.R
|
||||||
import com.github.libretube.constants.PreferenceKeys
|
|
||||||
import com.github.libretube.databinding.FragmentWatchHistoryBinding
|
import com.github.libretube.databinding.FragmentWatchHistoryBinding
|
||||||
import com.github.libretube.db.DatabaseHelper
|
|
||||||
import com.github.libretube.db.DatabaseHolder.Database
|
import com.github.libretube.db.DatabaseHolder.Database
|
||||||
import com.github.libretube.db.obj.WatchHistoryItem
|
import com.github.libretube.db.obj.WatchHistoryItem
|
||||||
import com.github.libretube.extensions.ceilHalf
|
import com.github.libretube.extensions.ceilHalf
|
||||||
@ -26,49 +22,28 @@ import com.github.libretube.extensions.dpToPx
|
|||||||
import com.github.libretube.extensions.setOnDismissListener
|
import com.github.libretube.extensions.setOnDismissListener
|
||||||
import com.github.libretube.helpers.NavBarHelper
|
import com.github.libretube.helpers.NavBarHelper
|
||||||
import com.github.libretube.helpers.NavigationHelper
|
import com.github.libretube.helpers.NavigationHelper
|
||||||
import com.github.libretube.helpers.PreferenceHelper
|
|
||||||
import com.github.libretube.ui.adapters.WatchHistoryAdapter
|
import com.github.libretube.ui.adapters.WatchHistoryAdapter
|
||||||
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
|
import com.github.libretube.ui.base.DynamicLayoutManagerFragment
|
||||||
import com.github.libretube.ui.extensions.addOnBottomReachedListener
|
import com.github.libretube.ui.extensions.addOnBottomReachedListener
|
||||||
import com.github.libretube.ui.extensions.setupFragmentAnimation
|
import com.github.libretube.ui.extensions.setupFragmentAnimation
|
||||||
import com.github.libretube.ui.models.CommonPlayerViewModel
|
import com.github.libretube.ui.models.CommonPlayerViewModel
|
||||||
|
import com.github.libretube.ui.models.WatchHistoryModel
|
||||||
import com.github.libretube.ui.sheets.BaseBottomSheet
|
import com.github.libretube.ui.sheets.BaseBottomSheet
|
||||||
import com.github.libretube.util.PlayingQueue
|
import com.github.libretube.util.PlayingQueue
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import kotlin.math.ceil
|
|
||||||
|
|
||||||
class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watch_history) {
|
class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watch_history) {
|
||||||
private var _binding: FragmentWatchHistoryBinding? = null
|
private var _binding: FragmentWatchHistoryBinding? = null
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
|
||||||
private val commonPlayerViewModel: CommonPlayerViewModel by activityViewModels()
|
private val commonPlayerViewModel: CommonPlayerViewModel by activityViewModels()
|
||||||
private var isLoading = false
|
|
||||||
private var recyclerViewState: Parcelable? = null
|
private var recyclerViewState: Parcelable? = null
|
||||||
|
|
||||||
|
private val viewModel: WatchHistoryModel by viewModels()
|
||||||
private val watchHistoryAdapter = WatchHistoryAdapter()
|
private val watchHistoryAdapter = WatchHistoryAdapter()
|
||||||
|
|
||||||
private var selectedStatusFilter = PreferenceHelper.getInt(
|
|
||||||
PreferenceKeys.SELECTED_HISTORY_STATUS_FILTER,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
set(value) {
|
|
||||||
PreferenceHelper.putInt(PreferenceKeys.SELECTED_HISTORY_STATUS_FILTER, value)
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
private var selectedTypeFilter = PreferenceHelper.getInt(
|
|
||||||
PreferenceKeys.SELECTED_HISTORY_TYPE_FILTER,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
set(value) {
|
|
||||||
PreferenceHelper.putInt(PreferenceKeys.SELECTED_HISTORY_TYPE_FILTER, value)
|
|
||||||
field = value
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun setLayoutManagers(gridItems: Int) {
|
override fun setLayoutManagers(gridItems: Int) {
|
||||||
_binding?.watchHistoryRecView?.layoutManager =
|
_binding?.watchHistoryRecView?.layoutManager =
|
||||||
GridLayoutManager(context, gridItems.ceilHalf())
|
GridLayoutManager(context, gridItems.ceilHalf())
|
||||||
@ -83,7 +58,8 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
|||||||
}
|
}
|
||||||
|
|
||||||
binding.watchHistoryRecView.setOnDismissListener { position ->
|
binding.watchHistoryRecView.setOnDismissListener { position ->
|
||||||
watchHistoryAdapter.removeFromWatchHistory(position)
|
val item = viewModel.filteredWatchHistory.value?.getOrNull(position) ?: return@setOnDismissListener
|
||||||
|
viewModel.removeFromHistory(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
// observe changes to indicate if the history is empty
|
// observe changes to indicate if the history is empty
|
||||||
@ -107,66 +83,81 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
lifecycleScope.launch {
|
binding.filterTypeTV.text =
|
||||||
val history = withContext(Dispatchers.IO) {
|
resources.getStringArray(R.array.filterOptions)[viewModel.selectedTypeFilter]
|
||||||
DatabaseHelper.getWatchHistoryPage(1, HISTORY_PAGE_SIZE)
|
binding.filterStatusTV.text =
|
||||||
}
|
resources.getStringArray(R.array.filterStatusOptions)[viewModel.selectedStatusFilter]
|
||||||
|
|
||||||
if (history.isEmpty()) return@launch
|
val watchPositionItem = arrayOf(getString(R.string.also_clear_watch_positions))
|
||||||
|
val selected = booleanArrayOf(false)
|
||||||
|
|
||||||
binding.filterTypeTV.text =
|
binding.clear.setOnClickListener {
|
||||||
resources.getStringArray(R.array.filterOptions)[selectedTypeFilter]
|
MaterialAlertDialogBuilder(requireContext())
|
||||||
binding.filterStatusTV.text =
|
.setTitle(R.string.clear_history)
|
||||||
resources.getStringArray(R.array.filterStatusOptions)[selectedStatusFilter]
|
.setMultiChoiceItems(watchPositionItem, selected) { _, index, newValue ->
|
||||||
|
selected[index] = newValue
|
||||||
val watchPositionItem = arrayOf(getString(R.string.also_clear_watch_positions))
|
}
|
||||||
val selected = booleanArrayOf(false)
|
.setPositiveButton(R.string.okay) { _, _ ->
|
||||||
|
binding.historyContainer.isGone = true
|
||||||
binding.clear.setOnClickListener {
|
binding.historyEmpty.isVisible = true
|
||||||
MaterialAlertDialogBuilder(requireContext())
|
lifecycleScope.launch(Dispatchers.IO) {
|
||||||
.setTitle(R.string.clear_history)
|
Database.withTransaction {
|
||||||
.setMultiChoiceItems(watchPositionItem, selected) { _, index, newValue ->
|
Database.watchHistoryDao().deleteAll()
|
||||||
selected[index] = newValue
|
if (selected[0]) Database.watchPositionDao().deleteAll()
|
||||||
}
|
|
||||||
.setPositiveButton(R.string.okay) { _, _ ->
|
|
||||||
binding.historyContainer.isGone = true
|
|
||||||
binding.historyEmpty.isVisible = true
|
|
||||||
lifecycleScope.launch(Dispatchers.IO) {
|
|
||||||
Database.withTransaction {
|
|
||||||
Database.watchHistoryDao().deleteAll()
|
|
||||||
if (selected[0]) Database.watchPositionDao().deleteAll()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.setNegativeButton(R.string.cancel, null)
|
}
|
||||||
.show()
|
.setNegativeButton(R.string.cancel, null)
|
||||||
}
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
binding.filterTypeTV.setOnClickListener {
|
binding.filterTypeTV.setOnClickListener {
|
||||||
val filterOptions = resources.getStringArray(R.array.filterOptions)
|
val filterOptions = resources.getStringArray(R.array.filterOptions)
|
||||||
|
|
||||||
BaseBottomSheet().apply {
|
BaseBottomSheet().apply {
|
||||||
setSimpleItems(filterOptions.toList()) { index ->
|
setSimpleItems(filterOptions.toList()) { index ->
|
||||||
binding.filterTypeTV.text = filterOptions[index]
|
binding.filterTypeTV.text = filterOptions[index]
|
||||||
selectedTypeFilter = index
|
viewModel.selectedTypeFilter = index
|
||||||
showWatchHistory(history)
|
}
|
||||||
}
|
}.show(childFragmentManager)
|
||||||
}.show(childFragmentManager)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
binding.filterStatusTV.setOnClickListener {
|
binding.filterStatusTV.setOnClickListener {
|
||||||
val filterOptions = resources.getStringArray(R.array.filterStatusOptions)
|
val filterOptions = resources.getStringArray(R.array.filterStatusOptions)
|
||||||
|
|
||||||
BaseBottomSheet().apply {
|
BaseBottomSheet().apply {
|
||||||
setSimpleItems(filterOptions.toList()) { index ->
|
setSimpleItems(filterOptions.toList()) { index ->
|
||||||
binding.filterStatusTV.text = filterOptions[index]
|
binding.filterStatusTV.text = filterOptions[index]
|
||||||
selectedStatusFilter = index
|
viewModel.selectedStatusFilter = index
|
||||||
showWatchHistory(history)
|
}
|
||||||
}
|
}.show(childFragmentManager)
|
||||||
}.show(childFragmentManager)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
showWatchHistory(history)
|
binding.playAll.setOnClickListener {
|
||||||
|
val history = viewModel.filteredWatchHistory.value.orEmpty()
|
||||||
|
if (history.isEmpty()) return@setOnClickListener
|
||||||
|
|
||||||
|
PlayingQueue.add(
|
||||||
|
*history.reversed().map(WatchHistoryItem::toStreamItem).toTypedArray()
|
||||||
|
)
|
||||||
|
NavigationHelper.navigateVideo(
|
||||||
|
requireContext(),
|
||||||
|
history.last().videoId,
|
||||||
|
keepQueue = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.filteredWatchHistory.observe(viewLifecycleOwner) { history ->
|
||||||
|
binding.historyEmpty.isGone = history.isNotEmpty()
|
||||||
|
binding.historyContainer.isVisible = history.isNotEmpty()
|
||||||
|
|
||||||
|
watchHistoryAdapter.submitList(history)
|
||||||
|
}
|
||||||
|
|
||||||
|
viewModel.fetchNextPage()
|
||||||
|
|
||||||
|
binding.watchHistoryRecView.addOnBottomReachedListener {
|
||||||
|
viewModel.fetchNextPage()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.watchHistoryFragment) {
|
if (NavBarHelper.getStartFragmentId(requireContext()) != R.id.watchHistoryFragment) {
|
||||||
@ -174,69 +165,6 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun showWatchHistory(history: List<WatchHistoryItem>) {
|
|
||||||
val watchHistory = history.filterByStatusAndWatchPosition()
|
|
||||||
|
|
||||||
binding.playAll.setOnClickListener {
|
|
||||||
PlayingQueue.add(
|
|
||||||
*watchHistory.reversed().map(WatchHistoryItem::toStreamItem).toTypedArray()
|
|
||||||
)
|
|
||||||
NavigationHelper.navigateVideo(
|
|
||||||
requireContext(),
|
|
||||||
watchHistory.last().videoId,
|
|
||||||
keepQueue = true
|
|
||||||
)
|
|
||||||
}
|
|
||||||
watchHistoryAdapter.submitList(history)
|
|
||||||
binding.historyEmpty.isGone = true
|
|
||||||
binding.historyContainer.isVisible = true
|
|
||||||
|
|
||||||
// add a listener for scroll end, delay needed to prevent loading new ones the first time
|
|
||||||
handler.postDelayed(200) {
|
|
||||||
if (_binding == null) return@postDelayed
|
|
||||||
|
|
||||||
binding.watchHistoryRecView.addOnBottomReachedListener {
|
|
||||||
if (isLoading) return@addOnBottomReachedListener
|
|
||||||
isLoading = true
|
|
||||||
|
|
||||||
lifecycleScope.launch {
|
|
||||||
val newHistory = withContext(Dispatchers.IO) {
|
|
||||||
val currentPage = ceil(watchHistoryAdapter.itemCount.toFloat() / HISTORY_PAGE_SIZE).toInt()
|
|
||||||
DatabaseHelper.getWatchHistoryPage( currentPage + 1, HISTORY_PAGE_SIZE)
|
|
||||||
}.filterByStatusAndWatchPosition()
|
|
||||||
|
|
||||||
watchHistoryAdapter.insertItems(newHistory)
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun List<WatchHistoryItem>.filterByStatusAndWatchPosition(): List<WatchHistoryItem> {
|
|
||||||
val watchHistoryItem = this.filter {
|
|
||||||
val isLive = (it.duration ?: -1L) < 0L
|
|
||||||
when (selectedTypeFilter) {
|
|
||||||
0 -> true
|
|
||||||
1 -> !it.isShort && !isLive
|
|
||||||
2 -> it.isShort // where is the StreamItem converted to watchHistoryItem?
|
|
||||||
3 -> isLive
|
|
||||||
else -> throw IllegalArgumentException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedStatusFilter == 0) {
|
|
||||||
return watchHistoryItem
|
|
||||||
}
|
|
||||||
|
|
||||||
return runBlocking {
|
|
||||||
when (selectedStatusFilter) {
|
|
||||||
1 -> DatabaseHelper.filterByWatchStatus(watchHistoryItem)
|
|
||||||
2 -> DatabaseHelper.filterByWatchStatus(watchHistoryItem, false)
|
|
||||||
else -> throw IllegalArgumentException()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
@ -247,8 +175,4 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment(R.layout.fragment_watc
|
|||||||
super.onDestroyView()
|
super.onDestroyView()
|
||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val HISTORY_PAGE_SIZE = 10
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,105 @@
|
|||||||
|
package com.github.libretube.ui.models
|
||||||
|
|
||||||
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.asFlow
|
||||||
|
import androidx.lifecycle.asLiveData
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.github.libretube.constants.PreferenceKeys
|
||||||
|
import com.github.libretube.db.DatabaseHelper
|
||||||
|
import com.github.libretube.db.DatabaseHolder
|
||||||
|
import com.github.libretube.db.obj.WatchHistoryItem
|
||||||
|
import com.github.libretube.helpers.PreferenceHelper
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class WatchHistoryModel : ViewModel() {
|
||||||
|
private val watchHistory = MutableLiveData<List<WatchHistoryItem>>()
|
||||||
|
|
||||||
|
private var currentPage = 1
|
||||||
|
private var isLoading = false
|
||||||
|
|
||||||
|
private val selectedStatus = MutableStateFlow(
|
||||||
|
PreferenceHelper.getInt(PreferenceKeys.SELECTED_HISTORY_STATUS_FILTER, 0)
|
||||||
|
)
|
||||||
|
private val selectedType = MutableStateFlow(
|
||||||
|
PreferenceHelper.getInt(PreferenceKeys.SELECTED_HISTORY_TYPE_FILTER, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
val filteredWatchHistory =
|
||||||
|
combine(watchHistory.asFlow(), selectedStatus, selectedType) { history, _, _ -> history }
|
||||||
|
.flowOn(Dispatchers.IO).map { history -> history.filter { it.shouldIncludeByFilters() } }
|
||||||
|
.asLiveData()
|
||||||
|
|
||||||
|
var selectedStatusFilter
|
||||||
|
get() = selectedStatus.value
|
||||||
|
set(value) {
|
||||||
|
PreferenceHelper.putInt(PreferenceKeys.SELECTED_HISTORY_STATUS_FILTER, value)
|
||||||
|
selectedStatus.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedTypeFilter
|
||||||
|
get() = selectedType.value
|
||||||
|
set(value) {
|
||||||
|
PreferenceHelper.putInt(PreferenceKeys.SELECTED_HISTORY_TYPE_FILTER, value)
|
||||||
|
selectedType.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun WatchHistoryItem.shouldIncludeByFilters(): Boolean {
|
||||||
|
val isLive = (duration ?: -1L) < 0L
|
||||||
|
val matchesFilter = when (selectedTypeFilter) {
|
||||||
|
0 -> true
|
||||||
|
1 -> !isShort && !isLive
|
||||||
|
2 -> isShort // where is the StreamItem converted to watchHistoryItem?
|
||||||
|
3 -> isLive
|
||||||
|
else -> throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!matchesFilter) return false
|
||||||
|
|
||||||
|
// no watch position filter
|
||||||
|
if (selectedStatusFilter == 0) return true
|
||||||
|
|
||||||
|
return when (selectedStatusFilter) {
|
||||||
|
1 -> DatabaseHelper.filterByWatchStatus(this)
|
||||||
|
2 -> DatabaseHelper.filterByWatchStatus(this, false)
|
||||||
|
else -> throw IllegalArgumentException()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fetchNextPage() = viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
if (isLoading) return@launch
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
val newHistory = withContext(Dispatchers.IO) {
|
||||||
|
DatabaseHelper.getWatchHistoryPage(currentPage, HISTORY_PAGE_SIZE)
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading = false
|
||||||
|
currentPage++
|
||||||
|
|
||||||
|
watchHistory.postValue(
|
||||||
|
watchHistory.value.orEmpty().toMutableList().apply {
|
||||||
|
addAll(newHistory)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeFromHistory(watchHistoryItem: WatchHistoryItem) =
|
||||||
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
|
DatabaseHolder.Database.watchHistoryDao().delete(watchHistoryItem)
|
||||||
|
|
||||||
|
watchHistory.postValue(
|
||||||
|
watchHistory.value.orEmpty().filter { it != watchHistoryItem }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val HISTORY_PAGE_SIZE = 10
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.github.libretube.ui.models.sources
|
||||||
|
|
||||||
|
import androidx.paging.PagingSource
|
||||||
|
import androidx.paging.PagingState
|
||||||
|
import com.github.libretube.db.DatabaseHelper
|
||||||
|
import com.github.libretube.db.obj.WatchHistoryItem
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
class WatchHistoryPagingSource(
|
||||||
|
private val shouldIncludeItemPredicate: suspend (WatchHistoryItem) -> Boolean
|
||||||
|
): PagingSource<Int, WatchHistoryItem>() {
|
||||||
|
override fun getRefreshKey(state: PagingState<Int, WatchHistoryItem>) = null
|
||||||
|
|
||||||
|
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, WatchHistoryItem> {
|
||||||
|
val newHistory = withContext(Dispatchers.IO) {
|
||||||
|
DatabaseHelper.getWatchHistoryPage( params.key ?: 0, params.loadSize)
|
||||||
|
}.filter { shouldIncludeItemPredicate(it) }
|
||||||
|
|
||||||
|
return LoadResult.Page(newHistory, params.key ?: 0, params.key?.plus(1) ?: 0)
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user