mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 06:10:31 +05:30
Merge pull request #5250 from Bnyro/master
refactor: simplify channel page and tabs logic
This commit is contained in:
commit
3ac85a75ea
@ -31,11 +31,11 @@ import com.github.libretube.ui.adapters.VideosAdapter
|
||||
import com.github.libretube.ui.dialogs.ShareDialog
|
||||
import com.github.libretube.ui.extensions.setupSubscriptionButton
|
||||
import com.github.libretube.util.deArrow
|
||||
import java.io.IOException
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
class ChannelFragment : Fragment() {
|
||||
private var _binding: FragmentChannelBinding? = null
|
||||
@ -43,19 +43,19 @@ class ChannelFragment : Fragment() {
|
||||
|
||||
private var channelId: String? = null
|
||||
private var channelName: String? = null
|
||||
private var nextPage: String? = null
|
||||
private var channelAdapter: VideosAdapter? = null
|
||||
private var isLoading = true
|
||||
private var isSubscribed: Boolean? = false
|
||||
|
||||
private var onScrollEnd: () -> Unit = {}
|
||||
|
||||
private val possibleTabs = listOf(
|
||||
ChannelTabs.Channels,
|
||||
ChannelTabs.Playlists,
|
||||
private val possibleTabs = arrayOf(
|
||||
ChannelTabs.Shorts,
|
||||
ChannelTabs.Livestreams,
|
||||
ChannelTabs.Shorts
|
||||
ChannelTabs.Playlists,
|
||||
ChannelTabs.Channels
|
||||
)
|
||||
private var channelTabs: List<ChannelTab> = emptyList()
|
||||
private var nextPages = Array<String?>(5) { null }
|
||||
private var searchAdapter: SearchAdapter? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -81,26 +81,19 @@ class ChannelFragment : Fragment() {
|
||||
|
||||
binding.channelRecView.layoutManager = LinearLayoutManager(context)
|
||||
|
||||
val refreshChannel = {
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
binding.channelRefresh.setOnRefreshListener {
|
||||
fetchChannel()
|
||||
}
|
||||
|
||||
refreshChannel()
|
||||
|
||||
binding.channelRefresh.setOnRefreshListener {
|
||||
refreshChannel()
|
||||
}
|
||||
|
||||
binding.channelScrollView.viewTreeObserver.addOnScrollChangedListener {
|
||||
if (_binding?.channelScrollView?.canScrollVertically(1) == false) {
|
||||
try {
|
||||
onScrollEnd()
|
||||
} catch (e: Exception) {
|
||||
Log.e("tabs failed", e.toString())
|
||||
}
|
||||
}
|
||||
val binding = _binding ?: return@addOnScrollChangedListener
|
||||
|
||||
if (binding.channelScrollView.canScrollVertically(1) || isLoading) return@addOnScrollChangedListener
|
||||
|
||||
loadNextPage()
|
||||
}
|
||||
|
||||
fetchChannel()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
@ -108,112 +101,139 @@ class ChannelFragment : Fragment() {
|
||||
_binding = null
|
||||
}
|
||||
|
||||
private fun fetchChannel() {
|
||||
lifecycleScope.launch {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (channelId != null) {
|
||||
RetrofitInstance.api.getChannel(channelId!!)
|
||||
} else {
|
||||
RetrofitInstance.api.getChannelByName(channelName!!)
|
||||
}.apply {
|
||||
relatedStreams = relatedStreams.deArrow()
|
||||
}
|
||||
private fun loadNextPage() = lifecycleScope.launch {
|
||||
val binding = _binding ?: return@launch
|
||||
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
isLoading = true
|
||||
|
||||
try {
|
||||
if (binding.tabChips.checkedChipId == binding.videos.id) {
|
||||
fetchChannelNextPage(nextPages[0] ?: return@launch)?.let {
|
||||
nextPages[0] = it
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
_binding?.channelRefresh?.isRefreshing = false
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launch
|
||||
} catch (e: HttpException) {
|
||||
_binding?.channelRefresh?.isRefreshing = false
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
return@launch
|
||||
}
|
||||
val binding = _binding ?: return@launch
|
||||
|
||||
// needed if the channel gets loaded by the ID
|
||||
channelId = response.id
|
||||
channelName = response.name
|
||||
val shareData = ShareData(currentChannel = response.name)
|
||||
|
||||
onScrollEnd = {
|
||||
fetchChannelNextPage()
|
||||
}
|
||||
|
||||
val channelId = channelId ?: return@launch
|
||||
// fetch and update the subscription status
|
||||
isSubscribed = SubscriptionHelper.isSubscribed(channelId) ?: false
|
||||
|
||||
binding.channelSubscribe.setupSubscriptionButton(
|
||||
channelId,
|
||||
channelName,
|
||||
binding.notificationBell
|
||||
)
|
||||
|
||||
binding.channelShare.setOnClickListener {
|
||||
val bundle = bundleOf(
|
||||
IntentData.id to channelId.toID(),
|
||||
IntentData.shareObjectType to ShareObjectType.CHANNEL,
|
||||
IntentData.shareData to shareData
|
||||
)
|
||||
val newShareDialog = ShareDialog()
|
||||
newShareDialog.arguments = bundle
|
||||
newShareDialog.show(childFragmentManager, ShareDialog::class.java.name)
|
||||
}
|
||||
|
||||
nextPage = response.nextpage
|
||||
isLoading = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
|
||||
binding.channelScrollView.isVisible = true
|
||||
binding.channelName.text = response.name
|
||||
if (response.verified) {
|
||||
binding.channelName.setCompoundDrawablesWithIntrinsicBounds(
|
||||
0,
|
||||
0,
|
||||
R.drawable.ic_verified,
|
||||
0
|
||||
)
|
||||
}
|
||||
binding.channelSubs.text = resources.getString(
|
||||
R.string.subscribers,
|
||||
response.subscriberCount.formatShort()
|
||||
)
|
||||
if (response.description.orEmpty().isBlank()) {
|
||||
binding.channelDescription.isGone = true
|
||||
} else {
|
||||
binding.channelDescription.text = response.description.orEmpty().trim()
|
||||
val currentTabIndex = binding.tabChips.children.indexOfFirst {
|
||||
it.id == binding.tabChips.checkedChipId
|
||||
}
|
||||
val channelTab = channelTabs.first { tab ->
|
||||
tab.name == possibleTabs[currentTabIndex - 1].identifierName
|
||||
}
|
||||
val nextPage = nextPages[currentTabIndex] ?: return@launch
|
||||
fetchTabNextPage(nextPage, channelTab)?.let {
|
||||
nextPages[currentTabIndex] = it
|
||||
}
|
||||
}
|
||||
|
||||
ImageHelper.loadImage(response.bannerUrl, binding.channelBanner)
|
||||
ImageHelper.loadImage(response.avatarUrl, binding.channelImage)
|
||||
|
||||
binding.channelImage.setOnClickListener {
|
||||
NavigationHelper.openImagePreview(
|
||||
requireContext(),
|
||||
response.avatarUrl ?: return@setOnClickListener
|
||||
)
|
||||
}
|
||||
|
||||
binding.channelBanner.setOnClickListener {
|
||||
NavigationHelper.openImagePreview(
|
||||
requireContext(),
|
||||
response.bannerUrl ?: return@setOnClickListener
|
||||
)
|
||||
}
|
||||
|
||||
// recyclerview of the videos by the channel
|
||||
channelAdapter = VideosAdapter(
|
||||
response.relatedStreams.toMutableList(),
|
||||
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
|
||||
)
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
|
||||
setupTabs(response.tabs)
|
||||
} catch (e: Exception) {
|
||||
Log.e("error fetching tabs", e.toString())
|
||||
}
|
||||
}.invokeOnCompletion {
|
||||
_binding?.channelRefresh?.isRefreshing = false
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
private fun fetchChannel() = lifecycleScope.launch {
|
||||
isLoading = true
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (channelId != null) {
|
||||
RetrofitInstance.api.getChannel(channelId!!)
|
||||
} else {
|
||||
RetrofitInstance.api.getChannelByName(channelName!!)
|
||||
}.apply {
|
||||
relatedStreams = relatedStreams.deArrow()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launch
|
||||
} catch (e: HttpException) {
|
||||
Log.e(TAG(), "HttpException, unexpected response")
|
||||
return@launch
|
||||
} finally {
|
||||
_binding?.channelRefresh?.isRefreshing = false
|
||||
isLoading = false
|
||||
}
|
||||
val binding = _binding ?: return@launch
|
||||
|
||||
// needed if the channel gets loaded by the ID
|
||||
channelId = response.id
|
||||
channelName = response.name
|
||||
val shareData = ShareData(currentChannel = response.name)
|
||||
|
||||
val channelId = channelId ?: return@launch
|
||||
// fetch and update the subscription status
|
||||
isSubscribed = SubscriptionHelper.isSubscribed(channelId) ?: false
|
||||
|
||||
binding.channelSubscribe.setupSubscriptionButton(
|
||||
channelId,
|
||||
channelName,
|
||||
binding.notificationBell
|
||||
)
|
||||
|
||||
binding.channelShare.setOnClickListener {
|
||||
val bundle = bundleOf(
|
||||
IntentData.id to channelId.toID(),
|
||||
IntentData.shareObjectType to ShareObjectType.CHANNEL,
|
||||
IntentData.shareData to shareData
|
||||
)
|
||||
val newShareDialog = ShareDialog()
|
||||
newShareDialog.arguments = bundle
|
||||
newShareDialog.show(childFragmentManager, ShareDialog::class.java.name)
|
||||
}
|
||||
|
||||
nextPages[0] = response.nextpage
|
||||
isLoading = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
|
||||
binding.channelScrollView.isVisible = true
|
||||
binding.channelName.text = response.name
|
||||
if (response.verified) {
|
||||
binding.channelName
|
||||
.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.ic_verified, 0)
|
||||
}
|
||||
binding.channelSubs.text = resources.getString(
|
||||
R.string.subscribers,
|
||||
response.subscriberCount.formatShort()
|
||||
)
|
||||
if (response.description.orEmpty().isBlank()) {
|
||||
binding.channelDescription.isGone = true
|
||||
} else {
|
||||
binding.channelDescription.text = response.description.orEmpty().trim()
|
||||
}
|
||||
|
||||
ImageHelper.loadImage(response.bannerUrl, binding.channelBanner)
|
||||
ImageHelper.loadImage(response.avatarUrl, binding.channelImage)
|
||||
|
||||
binding.channelImage.setOnClickListener {
|
||||
NavigationHelper.openImagePreview(
|
||||
requireContext(),
|
||||
response.avatarUrl ?: return@setOnClickListener
|
||||
)
|
||||
}
|
||||
|
||||
binding.channelBanner.setOnClickListener {
|
||||
NavigationHelper.openImagePreview(
|
||||
requireContext(),
|
||||
response.bannerUrl ?: return@setOnClickListener
|
||||
)
|
||||
}
|
||||
|
||||
// recyclerview of the videos by the channel
|
||||
channelAdapter = VideosAdapter(
|
||||
response.relatedStreams.toMutableList(),
|
||||
forceMode = VideosAdapter.Companion.ForceMode.CHANNEL
|
||||
)
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
|
||||
setupTabs(response.tabs)
|
||||
}
|
||||
|
||||
private fun setupTabs(tabs: List<ChannelTab>) {
|
||||
this.channelTabs = tabs
|
||||
|
||||
binding.tabChips.children.forEach { chip ->
|
||||
val resourceTab = possibleTabs.firstOrNull { it.chipId == chip.id }
|
||||
resourceTab?.let { resTab ->
|
||||
@ -225,15 +245,12 @@ class ChannelFragment : Fragment() {
|
||||
when (binding.tabChips.checkedChipId) {
|
||||
binding.videos.id -> {
|
||||
binding.channelRecView.adapter = channelAdapter
|
||||
onScrollEnd = {
|
||||
fetchChannelNextPage()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
possibleTabs.first { binding.tabChips.checkedChipId == it.chipId }.let {
|
||||
val tab = tabs.first { tab -> tab.name == it.identifierName }
|
||||
loadTab(tab)
|
||||
loadChannelTab(tab)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -242,89 +259,58 @@ class ChannelFragment : Fragment() {
|
||||
// Load selected chip content if it's not videos tab.
|
||||
possibleTabs.firstOrNull { binding.tabChips.checkedChipId == it.chipId }?.let {
|
||||
val tab = tabs.first { tab -> tab.name == it.identifierName }
|
||||
loadTab(tab)
|
||||
loadChannelTab(tab)
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadTab(tab: ChannelTab) {
|
||||
lifecycleScope.launch {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelTab(tab.data)
|
||||
}.apply {
|
||||
content = content.deArrow()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
return@launch
|
||||
}
|
||||
val binding = _binding ?: return@launch
|
||||
|
||||
val adapter = SearchAdapter(true)
|
||||
binding.channelRecView.adapter = adapter
|
||||
adapter.submitList(response.content)
|
||||
|
||||
var tabNextPage = response.nextpage
|
||||
onScrollEnd = {
|
||||
tabNextPage?.let {
|
||||
fetchTabNextPage(it, tab, adapter) { nextPage ->
|
||||
tabNextPage = nextPage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchChannelNextPage() {
|
||||
if (nextPage == null || isLoading) return
|
||||
isLoading = true
|
||||
private fun loadChannelTab(tab: ChannelTab) = lifecycleScope.launch {
|
||||
binding.channelRefresh.isRefreshing = true
|
||||
isLoading = true
|
||||
|
||||
lifecycleScope.launch {
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage!!).apply {
|
||||
relatedStreams = relatedStreams.deArrow()
|
||||
}
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
_binding?.channelRefresh?.isRefreshing = false
|
||||
Log.e(TAG(), "IOException, you might not have internet connection")
|
||||
return@launch
|
||||
} catch (e: HttpException) {
|
||||
_binding?.channelRefresh?.isRefreshing = false
|
||||
Log.e(TAG(), "HttpException, unexpected response," + e.response())
|
||||
return@launch
|
||||
val response = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelTab(tab.data)
|
||||
}.apply {
|
||||
content = content.deArrow()
|
||||
}
|
||||
val binding = _binding ?: return@launch
|
||||
|
||||
nextPage = response.nextpage
|
||||
channelAdapter?.insertItems(response.relatedStreams)
|
||||
isLoading = false
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
} catch (e: Exception) {
|
||||
return@launch
|
||||
}
|
||||
nextPages[channelTabs.indexOf(tab) + 1] = response.nextpage
|
||||
|
||||
val binding = _binding ?: return@launch
|
||||
|
||||
searchAdapter = SearchAdapter(true)
|
||||
binding.channelRecView.adapter = searchAdapter
|
||||
searchAdapter?.submitList(response.content)
|
||||
|
||||
binding.channelRefresh.isRefreshing = false
|
||||
isLoading = false
|
||||
}
|
||||
|
||||
private fun fetchTabNextPage(
|
||||
nextPage: String,
|
||||
tab: ChannelTab,
|
||||
adapter: SearchAdapter,
|
||||
onNewNextPage: (String?) -> Unit
|
||||
) {
|
||||
lifecycleScope.launch {
|
||||
val newContent = try {
|
||||
withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelTab(tab.data, nextPage)
|
||||
}.apply {
|
||||
content = content.deArrow()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), "Exception: $e")
|
||||
null
|
||||
}
|
||||
onNewNextPage(newContent?.nextpage)
|
||||
newContent?.content?.let {
|
||||
adapter.submitList(adapter.currentList + it)
|
||||
private suspend fun fetchChannelNextPage(nextPage: String): String? {
|
||||
val response = withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelNextPage(channelId!!, nextPage).apply {
|
||||
relatedStreams = relatedStreams.deArrow()
|
||||
}
|
||||
}
|
||||
|
||||
channelAdapter?.insertItems(response.relatedStreams)
|
||||
|
||||
return response.nextpage
|
||||
}
|
||||
|
||||
private suspend fun fetchTabNextPage(nextPage: String, tab: ChannelTab): String? {
|
||||
val newContent = withContext(Dispatchers.IO) {
|
||||
RetrofitInstance.api.getChannelTab(tab.data, nextPage)
|
||||
}.apply {
|
||||
content = content.deArrow()
|
||||
}
|
||||
|
||||
searchAdapter?.let {
|
||||
it.submitList(it.currentList + newContent.content)
|
||||
}
|
||||
|
||||
return newContent.nextpage
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user