feat: watch history pagination

This commit is contained in:
Bnyro 2024-08-10 17:42:05 +02:00
parent 0c39a25a4c
commit 6970bf6ee2
5 changed files with 114 additions and 81 deletions

View File

@ -46,12 +46,27 @@ object DatabaseHelper {
} }
// delete the first watch history entry if the limit is reached // delete the first watch history entry if the limit is reached
val watchHistory = Database.watchHistoryDao().getAll() val historySize = Database.watchHistoryDao().getSize()
if (watchHistory.size > maxHistorySize.toInt()) { if (historySize > maxHistorySize.toInt()) {
Database.watchHistoryDao().delete(watchHistory.first()) Database.watchHistoryDao().delete(Database.watchHistoryDao().getOldest())
} }
} }
suspend fun getWatchHistoryPage(page: Int, pageSize: Int): List<WatchHistoryItem> {
val watchHistoryDao = Database.watchHistoryDao()
val historySize = watchHistoryDao.getSize()
if (historySize < pageSize * (page-1)) return emptyList()
val offset = historySize - (pageSize * page)
val limit = if (offset < 0) {
offset + pageSize
} else {
pageSize
}
return watchHistoryDao.getN(limit, maxOf(offset, 0)).reversed()
}
suspend fun addToSearchHistory(searchHistoryItem: SearchHistoryItem) { suspend fun addToSearchHistory(searchHistoryItem: SearchHistoryItem) {
Database.searchHistoryDao().insert(searchHistoryItem) Database.searchHistoryDao().insert(searchHistoryItem)

View File

@ -12,6 +12,12 @@ interface WatchHistoryDao {
@Query("SELECT * FROM watchHistoryItem") @Query("SELECT * FROM watchHistoryItem")
suspend fun getAll(): List<WatchHistoryItem> suspend fun getAll(): List<WatchHistoryItem>
@Query("SELECT * FROM watchHistoryItem LIMIT :limit OFFSET :offset")
suspend fun getN(limit: Int, offset: Int): List<WatchHistoryItem>
@Query("SELECT COUNT(videoId) FROM watchHistoryItem")
suspend fun getSize(): Int
@Query("SELECT * FROM watchHistoryItem WHERE videoId LIKE :videoId LIMIT 1") @Query("SELECT * FROM watchHistoryItem WHERE videoId LIKE :videoId LIMIT 1")
suspend fun findById(videoId: String): WatchHistoryItem? suspend fun findById(videoId: String): WatchHistoryItem?
@ -24,6 +30,9 @@ interface WatchHistoryDao {
@Delete @Delete
suspend fun delete(watchHistoryItem: WatchHistoryItem) suspend fun delete(watchHistoryItem: WatchHistoryItem)
@Query("SELECT * FROM watchHistoryItem LIMIT 1 OFFSET 0")
suspend fun getOldest(): WatchHistoryItem
@Query("DELETE FROM watchHistoryItem WHERE videoId = :id") @Query("DELETE FROM watchHistoryItem WHERE videoId = :id")
suspend fun deleteByVideoId(id: String) suspend fun deleteByVideoId(id: String)

View File

@ -24,9 +24,7 @@ class WatchHistoryAdapter(
) : ) :
RecyclerView.Adapter<WatchHistoryViewHolder>() { RecyclerView.Adapter<WatchHistoryViewHolder>() {
private var visibleCount = minOf(10, watchHistory.size) override fun getItemCount() = watchHistory.size
override fun getItemCount() = visibleCount
fun removeFromWatchHistory(position: Int) { fun removeFromWatchHistory(position: Int) {
val history = watchHistory[position] val history = watchHistory[position]
@ -34,16 +32,14 @@ class WatchHistoryAdapter(
DatabaseHolder.Database.watchHistoryDao().delete(history) DatabaseHolder.Database.watchHistoryDao().delete(history)
} }
watchHistory.removeAt(position) watchHistory.removeAt(position)
visibleCount--
notifyItemRemoved(position) notifyItemRemoved(position)
notifyItemRangeChanged(position, itemCount) notifyItemRangeChanged(position, itemCount)
} }
fun showMoreItems() { fun insertItems(items: List<WatchHistoryItem>) {
val oldSize = visibleCount val oldSize = itemCount
visibleCount += minOf(10, watchHistory.size - oldSize) this.watchHistory.addAll(items)
if (visibleCount == oldSize) return notifyItemRangeInserted(oldSize, itemCount)
notifyItemRangeInserted(oldSize, visibleCount)
} }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WatchHistoryViewHolder {

View File

@ -40,6 +40,8 @@ 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.runBlocking
import kotlinx.coroutines.withContext
import kotlin.math.ceil
class WatchHistoryFragment : DynamicLayoutManagerFragment() { class WatchHistoryFragment : DynamicLayoutManagerFragment() {
private var _binding: FragmentWatchHistoryBinding? = null private var _binding: FragmentWatchHistoryBinding? = null
@ -88,11 +90,12 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
_binding?.watchHistoryRecView?.updatePadding(bottom = if (it) 64f.dpToPx() else 0) _binding?.watchHistoryRecView?.updatePadding(bottom = if (it) 64f.dpToPx() else 0)
} }
val allHistory = runBlocking(Dispatchers.IO) { lifecycleScope.launch {
Database.watchHistoryDao().getAll().reversed() val history = withContext(Dispatchers.IO) {
DatabaseHelper.getWatchHistoryPage(1, HISTORY_PAGE_SIZE)
} }
if (allHistory.isEmpty()) return if (history.isEmpty()) return@launch
binding.filterTypeTV.text = binding.filterTypeTV.text =
resources.getStringArray(R.array.filterOptions)[selectedTypeFilter] resources.getStringArray(R.array.filterOptions)[selectedTypeFilter]
@ -129,7 +132,7 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
setSimpleItems(filterOptions.toList()) { index -> setSimpleItems(filterOptions.toList()) { index ->
binding.filterTypeTV.text = filterOptions[index] binding.filterTypeTV.text = filterOptions[index]
selectedTypeFilter = index selectedTypeFilter = index
showWatchHistory(allHistory) showWatchHistory(history)
} }
}.show(childFragmentManager) }.show(childFragmentManager)
} }
@ -141,7 +144,7 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
setSimpleItems(filterOptions.toList()) { index -> setSimpleItems(filterOptions.toList()) { index ->
binding.filterStatusTV.text = filterOptions[index] binding.filterStatusTV.text = filterOptions[index]
selectedStatusFilter = index selectedStatusFilter = index
showWatchHistory(allHistory) showWatchHistory(history)
} }
}.show(childFragmentManager) }.show(childFragmentManager)
} }
@ -154,20 +157,19 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
} }
}) })
showWatchHistory(allHistory) showWatchHistory(history)
}
} }
private fun showWatchHistory(allHistory: List<WatchHistoryItem>) { private fun showWatchHistory(history: List<WatchHistoryItem>) {
val watchHistory = allHistory.filterByStatusAndWatchPosition() val watchHistory = history.filterByStatusAndWatchPosition()
watchHistory.forEach { watchHistory.forEach {
it.thumbnailUrl = ProxyHelper.rewriteUrl(it.thumbnailUrl) it.thumbnailUrl = ProxyHelper.rewriteUrl(it.thumbnailUrl)
it.uploaderAvatar = ProxyHelper.rewriteUrl(it.uploaderAvatar) it.uploaderAvatar = ProxyHelper.rewriteUrl(it.uploaderAvatar)
} }
val watchHistoryAdapter = WatchHistoryAdapter( val watchHistoryAdapter = WatchHistoryAdapter(watchHistory.toMutableList())
watchHistory.toMutableList()
)
binding.playAll.setOnClickListener { binding.playAll.setOnClickListener {
PlayingQueue.resetToDefaults() PlayingQueue.resetToDefaults()
@ -234,13 +236,20 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
binding.watchHistoryRecView.addOnBottomReachedListener { binding.watchHistoryRecView.addOnBottomReachedListener {
if (isLoading) return@addOnBottomReachedListener if (isLoading) return@addOnBottomReachedListener
isLoading = true isLoading = true
watchHistoryAdapter.showMoreItems()
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 isLoading = false
} }
} }
} }
}
private fun List<WatchHistoryItem>.filterByStatusAndWatchPosition(): List<WatchHistoryItem> { private fun List<WatchHistoryItem>.filterByStatusAndWatchPosition(): List<WatchHistoryItem> {
val watchHistoryItem = this.filter { val watchHistoryItem = this.filter {
@ -277,4 +286,8 @@ class WatchHistoryFragment : DynamicLayoutManagerFragment() {
super.onDestroyView() super.onDestroyView()
_binding = null _binding = null
} }
companion object {
private const val HISTORY_PAGE_SIZE = 10
}
} }

View File

@ -112,10 +112,10 @@ class HomeViewModel : ViewModel() {
} }
private suspend fun loadWatchingFromDB(): List<StreamItem> { private suspend fun loadWatchingFromDB(): List<StreamItem> {
val videos = DatabaseHolder.Database.watchHistoryDao().getAll() val videos = DatabaseHelper.getWatchHistoryPage(1, 50)
return DatabaseHelper return DatabaseHelper
.filterUnwatched(videos.map { it.toStreamItem() }) .filterUnwatched(videos.map { it.toStreamItem() })
.reversed()
.take(20) .take(20)
} }