Add stashing to SubCategory Streams

This commit is contained in:
Rahul Patel
2024-06-29 04:03:01 +05:30
committed by Aayush Gupta
parent 6f425fe641
commit 8a12ffcd96
4 changed files with 65 additions and 83 deletions

View File

@@ -21,15 +21,16 @@ package com.aurora.store.view.ui.commons
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.fragment.app.viewModels
import androidx.fragment.app.activityViewModels
import androidx.navigation.fragment.findNavController
import androidx.navigation.fragment.navArgs
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.contracts.StreamContract
import com.aurora.store.R
import com.aurora.store.data.model.ViewState
import com.aurora.store.data.model.ViewState.Loading.getDataAs
import com.aurora.store.databinding.ActivityGenericRecyclerBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
@@ -46,16 +47,20 @@ class CategoryBrowseFragment : BaseFragment(R.layout.activity_generic_recycler),
get() = _binding!!
private val args: CategoryBrowseFragmentArgs by navArgs()
private val viewModel: SubCategoryClusterViewModel by viewModels()
private val viewModel: SubCategoryClusterViewModel by activityViewModels()
private lateinit var genericCarouselController: GenericCarouselController
private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
private lateinit var category: StreamContract.Category
private var streamBundle: StreamBundle? = StreamBundle()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
_binding = ActivityGenericRecyclerBinding.bind(view)
genericCarouselController = CategoryCarouselController(this)
val rawCategory = args.browseUrl.split("/").last()
category = StreamContract.Category.NONE.apply { value = rawCategory }
val genericCarouselController = CategoryCarouselController(this)
// Toolbar
binding.layoutToolbarAction.apply {
@@ -65,39 +70,30 @@ class CategoryBrowseFragment : BaseFragment(R.layout.activity_generic_recycler),
// RecyclerView
binding.recycler.setController(genericCarouselController)
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
viewModel.observe(category)
}
})
viewModel.liveData.observe(viewLifecycleOwner) {
when (it) {
is ViewState.Empty -> {
}
is ViewState.Loading -> {
updateController(null)
}
is ViewState.Error -> {
genericCarouselController.setData(null)
}
is ViewState.Success<*> -> {
updateController(it.data as StreamBundle)
val stash = it.getDataAs<Map<String, StreamBundle>>()
streamBundle = stash[category.value]
genericCarouselController.setData(streamBundle)
}
else -> {}
}
}
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
viewModel.observe()
}
}
endlessRecyclerOnScrollListener.disable()
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
viewModel.observeCategory(args.browseUrl)
updateController(null)
viewModel.observe(category)
}
override fun onDestroyView() {
@@ -105,25 +101,12 @@ class CategoryBrowseFragment : BaseFragment(R.layout.activity_generic_recycler),
_binding = null
}
private fun updateController(streamBundle: StreamBundle?) {
if (streamBundle != null) endlessRecyclerOnScrollListener.enable()
genericCarouselController.setData(streamBundle)
}
override fun onHeaderClicked(streamCluster: StreamCluster) {
if (streamCluster.clusterBrowseUrl.isNotEmpty())
openStreamBrowseFragment(streamCluster.clusterBrowseUrl)
else
Toast.makeText(
requireContext(),
getString(R.string.toast_page_unavailable),
Toast.LENGTH_SHORT
)
.show()
}
override fun onClusterScrolled(streamCluster: StreamCluster) {
viewModel.observeCluster(streamCluster)
viewModel.observeCluster(category, streamCluster)
}
override fun onAppClick(app: App) {

View File

@@ -118,8 +118,7 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
}
override fun onHeaderClicked(streamCluster: StreamCluster) {
if (streamCluster.clusterBrowseUrl.isNotEmpty())
openStreamBrowseFragment(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
}
override fun onClusterScrolled(streamCluster: StreamCluster) {

View File

@@ -77,25 +77,26 @@ class StreamViewModel @Inject constructor(
fun observe(category: StreamContract.Category, type: StreamContract.Type) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
if (targetBundle(category).streamClusters.isNotEmpty()) {
val bundle = targetBundle(category)
if (bundle.streamClusters.isNotEmpty()) {
liveData.postValue(ViewState.Success(stash))
}
try {
if (!targetBundle(category).hasCluster() || targetBundle(category).hasNext()) {
if (!bundle.hasCluster() || bundle.hasNext()) {
//Fetch new stream bundle
val newBundle = if (targetBundle(category).streamClusters.isEmpty()) {
val newBundle = if (bundle.streamClusters.isEmpty()) {
contract().fetch(type, category)
} else {
contract().nextStreamBundle(
category,
targetBundle((category)).streamNextPageUrl
bundle.streamNextPageUrl
)
}
//Update old bundle
targetBundle(category).apply {
bundle.apply {
streamClusters.putAll(newBundle.streamClusters)
streamNextPageUrl = newBundle.streamNextPageUrl
}

View File

@@ -31,7 +31,6 @@ import com.aurora.store.data.model.ViewState
import com.aurora.gplayapi.helpers.contracts.StreamContract
import com.aurora.gplayapi.helpers.web.WebStreamHelper
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.util.Log
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
@@ -50,76 +49,64 @@ class SubCategoryClusterViewModel @Inject constructor(
.using(HttpClient.getPreferredClient(context))
val liveData: MutableLiveData<ViewState> = MutableLiveData()
var streamBundle: StreamBundle = StreamBundle()
private lateinit var homeUrl: String
private lateinit var type: StreamContract.Type
private lateinit var category: StreamContract.Category
init {
liveData.postValue(ViewState.Loading)
}
private var stash: MutableMap<String, StreamBundle> = mutableMapOf()
private fun getCategoryStreamBundle(
category: StreamContract.Category,
nextPageUrl: String
): StreamBundle {
return if (streamBundle.streamClusters.isEmpty())
contract.fetch(type, category)
return if (targetBundle(category).streamClusters.isEmpty())
contract.fetch(StreamContract.Type.HOME, category)
else
contract.nextStreamBundle(category, nextPageUrl)
}
fun observeCategory(homeUrl: String) {
this.homeUrl = homeUrl
val rawCategory = homeUrl.split("/").last()
type = StreamContract.Type.HOME
category = StreamContract.Category.NONE.apply {
value = rawCategory
}
observe()
}
fun observe() {
fun observe(category: StreamContract.Category) {
liveData.postValue(ViewState.Loading)
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
val bundle = targetBundle(category)
if (bundle.streamClusters.isNotEmpty()) {
liveData.postValue(ViewState.Success(stash))
}
try {
if (!streamBundle.hasCluster() || streamBundle.hasNext()) {
if (!bundle.hasCluster() || bundle.hasNext()) {
//Fetch new stream bundle
val newBundle = getCategoryStreamBundle(
streamBundle.streamNextPageUrl
category,
bundle.streamNextPageUrl
)
//Update old bundle
streamBundle.apply {
bundle.apply {
streamClusters.putAll(newBundle.streamClusters)
streamNextPageUrl = newBundle.streamNextPageUrl
}
//Post updated to UI
liveData.postValue(ViewState.Success(streamBundle))
liveData.postValue(ViewState.Success(stash))
} else {
Log.i("End of Bundle")
}
} catch (e: Exception) {
e.printStackTrace()
liveData.postValue(ViewState.Error(e.message))
}
}
}
}
fun observeCluster(streamCluster: StreamCluster) {
fun observeCluster(category: StreamContract.Category, streamCluster: StreamCluster) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
if (streamCluster.hasNext()) {
val newCluster =
contract.nextStreamCluster(streamCluster.clusterNextPageUrl)
updateCluster(newCluster)
liveData.postValue(ViewState.Success(streamBundle))
updateCluster(category, streamCluster.id, newCluster)
liveData.postValue(ViewState.Success(stash))
} else {
Log.i("End of cluster")
streamCluster.clusterNextPageUrl = String()
@@ -131,10 +118,22 @@ class SubCategoryClusterViewModel @Inject constructor(
}
}
private fun updateCluster(newCluster: StreamCluster) {
streamBundle.streamClusters[newCluster.id]?.apply {
private fun updateCluster(
category: StreamContract.Category,
clusterID: Int,
newCluster: StreamCluster
) {
targetBundle(category).streamClusters[clusterID]?.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
}
}
private fun targetBundle(category: StreamContract.Category): StreamBundle {
val streamBundle = stash.getOrPut(category.value) {
StreamBundle()
}
return streamBundle
}
}