Merge pull request #2509 from Bnyro/master

Fix crash during startup caused by the comments sheet
This commit is contained in:
Bnyro 2022-12-26 15:55:54 +01:00 committed by GitHub
commit 3b4fdc8e7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 79 deletions

View File

@ -4,7 +4,7 @@ import com.fasterxml.jackson.annotation.JsonIgnoreProperties
@JsonIgnoreProperties(ignoreUnknown = true)
data class CommentsPage(
val comments: MutableList<Comment> = arrayListOf(),
var comments: MutableList<Comment> = arrayListOf(),
val disabled: Boolean? = null,
val nextpage: String? = null
)

View File

@ -36,7 +36,6 @@ import com.github.libretube.R
import com.github.libretube.api.CronetHelper
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.ChapterSegment
import com.github.libretube.api.obj.Comment
import com.github.libretube.api.obj.PipedStream
import com.github.libretube.api.obj.Segment
import com.github.libretube.api.obj.SegmentData
@ -76,6 +75,7 @@ import com.github.libretube.ui.extensions.setFormattedHtml
import com.github.libretube.ui.extensions.setInvisible
import com.github.libretube.ui.extensions.setupSubscriptionButton
import com.github.libretube.ui.interfaces.OnlinePlayerOptions
import com.github.libretube.ui.models.CommentsViewModel
import com.github.libretube.ui.models.PlayerViewModel
import com.github.libretube.ui.sheets.BaseBottomSheet
import com.github.libretube.ui.sheets.CommentsSheet
@ -124,6 +124,7 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
private lateinit var doubleTapOverlayBinding: DoubleTapOverlayBinding
private lateinit var playerGestureControlsViewBinding: PlayerGestureControlsViewBinding
private val viewModel: PlayerViewModel by activityViewModels()
private val commentsViewModel: CommentsViewModel by activityViewModels()
/**
* video information
@ -152,8 +153,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
* Chapters and comments
*/
private lateinit var chapters: List<ChapterSegment>
private val comments: MutableList<Comment> = mutableListOf()
private var commentsNextPage: String? = null
/**
* for the player view
@ -330,15 +329,11 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
}
binding.commentsToggle.setOnClickListener {
CommentsSheet(
videoId!!,
comments,
commentsNextPage,
binding.root.height - binding.player.height
) { comments, nextPage ->
this.comments.addAll(comments)
this.commentsNextPage = nextPage
}.show(childFragmentManager)
videoId ?: return@setOnClickListener
// set the max height to not cover the currently playing video
commentsViewModel.maxHeight = binding.root.height - binding.player.height
commentsViewModel.videoId = videoId
CommentsSheet().show(childFragmentManager)
}
playerBinding.queueToggle.visibility = View.VISIBLE
@ -604,6 +599,9 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
playerBinding.exoProgress.clearSegments()
playerBinding.sbToggle.visibility = View.GONE
// reset the comments to become reloaded later
commentsViewModel.reset()
lifecycleScope.launchWhenCreated {
streams = try {
RetrofitInstance.api.getStreams(videoId!!)
@ -754,10 +752,6 @@ class PlayerFragment : BaseFragment(), OnlinePlayerOptions {
if (nextVideoId != null) {
videoId = nextVideoId
// reset the comments to be reloaded later
comments.clear()
commentsNextPage = null
// play the next video
playVideo()
}

View File

@ -0,0 +1,65 @@
package com.github.libretube.ui.models
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.CommentsPage
import com.github.libretube.extensions.TAG
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class CommentsViewModel : ViewModel() {
private val scope = CoroutineScope(Dispatchers.IO)
private var isLoading = false
val commentsPage = MutableLiveData<CommentsPage?>()
private var nextPage: String? = null
var videoId: String? = null
var maxHeight: Int = 0
fun fetchComments() {
videoId ?: return
scope.launch {
isLoading = true
val response = try {
RetrofitInstance.api.getComments(videoId!!)
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launch
}
nextPage = response.nextpage
commentsPage.postValue(response)
isLoading = false
}
}
fun fetchNextComments() {
if (isLoading || nextPage == null || videoId == null) return
scope.launch {
isLoading = true
val response = try {
RetrofitInstance.api.getCommentsNextPage(videoId!!, nextPage!!)
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launch
}
val updatedPage = commentsPage.value?.apply {
comments = comments.plus(response.comments).toMutableList()
}
nextPage = response.nextpage
commentsPage.postValue(updatedPage)
isLoading = false
}
}
fun reset() {
isLoading = false
nextPage = null
commentsPage.value = null
videoId = null
}
}

View File

@ -1,32 +1,22 @@
package com.github.libretube.ui.sheets
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.lifecycle.lifecycleScope
import androidx.fragment.app.activityViewModels
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance
import com.github.libretube.api.obj.Comment
import com.github.libretube.databinding.CommentsSheetBinding
import com.github.libretube.extensions.TAG
import com.github.libretube.ui.adapters.CommentsAdapter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import com.github.libretube.ui.models.CommentsViewModel
class CommentsSheet(
private val videoId: String,
private val comments: List<Comment>,
private var nextPage: String?,
private val maxHeight: Int,
private val onMoreComments: (comments: List<Comment>, nextPage: String?) -> Unit
) : ExpandedBottomSheet() {
class CommentsSheet : ExpandedBottomSheet() {
private lateinit var binding: CommentsSheetBinding
private lateinit var commentsAdapter: CommentsAdapter
private var isLoading = false
private val viewModel: CommentsViewModel by activityViewModels()
override fun onCreateView(
inflater: LayoutInflater,
@ -35,7 +25,7 @@ class CommentsSheet(
): View {
binding = CommentsSheetBinding.inflate(layoutInflater)
// set a fixed maximum height
binding.root.maxHeight = maxHeight
binding.root.maxHeight = viewModel.maxHeight
return binding.root
}
@ -48,62 +38,40 @@ class CommentsSheet(
binding.commentsRV.viewTreeObserver
.addOnScrollChangedListener {
if (!binding.commentsRV.canScrollVertically(1)) {
fetchNextComments()
viewModel.fetchNextComments()
}
}
commentsAdapter = CommentsAdapter(videoId, comments.toMutableList()) {
commentsAdapter = CommentsAdapter(
viewModel.videoId!!,
viewModel.commentsPage.value?.comments.orEmpty().toMutableList()
) {
dialog?.dismiss()
}
binding.commentsRV.adapter = commentsAdapter
if (comments.isEmpty()) fetchComments()
}
private fun fetchComments() {
binding.progress.visibility = View.VISIBLE
lifecycleScope.launchWhenCreated {
isLoading = true
val response = try {
RetrofitInstance.api.getComments(videoId)
} catch (e: Exception) {
return@launchWhenCreated
}
binding.progress.visibility = View.GONE
if (response.disabled == true) {
withContext(Dispatchers.Main) {
binding.errorTV.visibility = View.VISIBLE
}
return@launchWhenCreated
}
if (response.comments.isEmpty()) {
withContext(Dispatchers.Main) {
binding.errorTV.text = getString(R.string.no_comments_available)
binding.errorTV.visibility = View.VISIBLE
}
return@launchWhenCreated
}
commentsAdapter.updateItems(response.comments)
nextPage = response.nextpage
onMoreComments.invoke(response.comments, response.nextpage)
isLoading = false
if (viewModel.commentsPage.value?.comments.orEmpty().isEmpty()) {
binding.progress.visibility = View.VISIBLE
viewModel.fetchComments()
}
}
private fun fetchNextComments() {
if (isLoading || nextPage == null) return
lifecycleScope.launchWhenCreated {
isLoading = true
val response = try {
RetrofitInstance.api.getCommentsNextPage(videoId, nextPage!!)
} catch (e: Exception) {
Log.e(TAG(), e.toString())
return@launchWhenCreated
// listen for new comments to be loaded
viewModel.commentsPage.observe(viewLifecycleOwner) {
it ?: return@observe
binding.progress.visibility = View.GONE
if (it.disabled == true) {
binding.errorTV.visibility = View.VISIBLE
return@observe
}
nextPage = response.nextpage
commentsAdapter.updateItems(response.comments)
onMoreComments.invoke(response.comments, response.nextpage)
isLoading = false
if (it.comments.isEmpty()) {
binding.errorTV.text = getString(R.string.no_comments_available)
binding.errorTV.visibility = View.VISIBLE
return@observe
}
commentsAdapter.updateItems(
// only add the new comments to the recycler view
it.comments.subList(commentsAdapter.itemCount, it.comments.size)
)
}
}
}