Add stashing to SubCategory Streams
This commit is contained in:
committed by
Aayush Gupta
parent
6f425fe641
commit
8a12ffcd96
@@ -21,15 +21,16 @@ package com.aurora.store.view.ui.commons
|
|||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.fragment.app.viewModels
|
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.navigation.fragment.navArgs
|
import androidx.navigation.fragment.navArgs
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
|
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.data.model.ViewState
|
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.databinding.ActivityGenericRecyclerBinding
|
||||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||||
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
|
import com.aurora.store.view.epoxy.controller.CategoryCarouselController
|
||||||
@@ -46,16 +47,20 @@ class CategoryBrowseFragment : BaseFragment(R.layout.activity_generic_recycler),
|
|||||||
get() = _binding!!
|
get() = _binding!!
|
||||||
|
|
||||||
private val args: CategoryBrowseFragmentArgs by navArgs()
|
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 category: StreamContract.Category
|
||||||
private lateinit var endlessRecyclerOnScrollListener: EndlessRecyclerOnScrollListener
|
private var streamBundle: StreamBundle? = StreamBundle()
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
_binding = ActivityGenericRecyclerBinding.bind(view)
|
_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
|
// Toolbar
|
||||||
binding.layoutToolbarAction.apply {
|
binding.layoutToolbarAction.apply {
|
||||||
@@ -65,39 +70,30 @@ class CategoryBrowseFragment : BaseFragment(R.layout.activity_generic_recycler),
|
|||||||
|
|
||||||
// RecyclerView
|
// RecyclerView
|
||||||
binding.recycler.setController(genericCarouselController)
|
binding.recycler.setController(genericCarouselController)
|
||||||
|
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
||||||
|
override fun onLoadMore(currentPage: Int) {
|
||||||
|
viewModel.observe(category)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ViewState.Empty -> {
|
|
||||||
}
|
|
||||||
|
|
||||||
is ViewState.Loading -> {
|
is ViewState.Loading -> {
|
||||||
updateController(null)
|
genericCarouselController.setData(null)
|
||||||
}
|
|
||||||
|
|
||||||
is ViewState.Error -> {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is ViewState.Success<*> -> {
|
is ViewState.Success<*> -> {
|
||||||
updateController(it.data as StreamBundle)
|
val stash = it.getDataAs<Map<String, StreamBundle>>()
|
||||||
|
streamBundle = stash[category.value]
|
||||||
|
|
||||||
|
genericCarouselController.setData(streamBundle)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
endlessRecyclerOnScrollListener = object : EndlessRecyclerOnScrollListener() {
|
viewModel.observe(category)
|
||||||
override fun onLoadMore(currentPage: Int) {
|
|
||||||
viewModel.observe()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
endlessRecyclerOnScrollListener.disable()
|
|
||||||
binding.recycler.addOnScrollListener(endlessRecyclerOnScrollListener)
|
|
||||||
|
|
||||||
viewModel.observeCategory(args.browseUrl)
|
|
||||||
updateController(null)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
override fun onDestroyView() {
|
||||||
@@ -105,25 +101,12 @@ class CategoryBrowseFragment : BaseFragment(R.layout.activity_generic_recycler),
|
|||||||
_binding = null
|
_binding = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateController(streamBundle: StreamBundle?) {
|
|
||||||
if (streamBundle != null) endlessRecyclerOnScrollListener.enable()
|
|
||||||
genericCarouselController.setData(streamBundle)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
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) {
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
viewModel.observeCluster(streamCluster)
|
viewModel.observeCluster(category, streamCluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAppClick(app: App) {
|
override fun onAppClick(app: App) {
|
||||||
|
|||||||
@@ -118,8 +118,7 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
override fun onHeaderClicked(streamCluster: StreamCluster) {
|
||||||
if (streamCluster.clusterBrowseUrl.isNotEmpty())
|
|
||||||
openStreamBrowseFragment(streamCluster.clusterBrowseUrl, streamCluster.clusterTitle)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
|
|||||||
@@ -77,25 +77,26 @@ class StreamViewModel @Inject constructor(
|
|||||||
fun observe(category: StreamContract.Category, type: StreamContract.Type) {
|
fun observe(category: StreamContract.Category, type: StreamContract.Type) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
if (targetBundle(category).streamClusters.isNotEmpty()) {
|
val bundle = targetBundle(category)
|
||||||
|
if (bundle.streamClusters.isNotEmpty()) {
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!targetBundle(category).hasCluster() || targetBundle(category).hasNext()) {
|
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||||
|
|
||||||
//Fetch new stream bundle
|
//Fetch new stream bundle
|
||||||
val newBundle = if (targetBundle(category).streamClusters.isEmpty()) {
|
val newBundle = if (bundle.streamClusters.isEmpty()) {
|
||||||
contract().fetch(type, category)
|
contract().fetch(type, category)
|
||||||
} else {
|
} else {
|
||||||
contract().nextStreamBundle(
|
contract().nextStreamBundle(
|
||||||
category,
|
category,
|
||||||
targetBundle((category)).streamNextPageUrl
|
bundle.streamNextPageUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
targetBundle(category).apply {
|
bundle.apply {
|
||||||
streamClusters.putAll(newBundle.streamClusters)
|
streamClusters.putAll(newBundle.streamClusters)
|
||||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import com.aurora.store.data.model.ViewState
|
|||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
||||||
import com.aurora.gplayapi.helpers.web.WebStreamHelper
|
import com.aurora.gplayapi.helpers.web.WebStreamHelper
|
||||||
import com.aurora.store.data.network.HttpClient
|
import com.aurora.store.data.network.HttpClient
|
||||||
import com.aurora.store.data.providers.AuthProvider
|
|
||||||
import com.aurora.store.util.Log
|
import com.aurora.store.util.Log
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
@@ -50,76 +49,64 @@ class SubCategoryClusterViewModel @Inject constructor(
|
|||||||
.using(HttpClient.getPreferredClient(context))
|
.using(HttpClient.getPreferredClient(context))
|
||||||
|
|
||||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||||
var streamBundle: StreamBundle = StreamBundle()
|
|
||||||
|
|
||||||
private lateinit var homeUrl: String
|
private var stash: MutableMap<String, StreamBundle> = mutableMapOf()
|
||||||
private lateinit var type: StreamContract.Type
|
|
||||||
private lateinit var category: StreamContract.Category
|
|
||||||
|
|
||||||
init {
|
|
||||||
liveData.postValue(ViewState.Loading)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getCategoryStreamBundle(
|
private fun getCategoryStreamBundle(
|
||||||
|
category: StreamContract.Category,
|
||||||
nextPageUrl: String
|
nextPageUrl: String
|
||||||
): StreamBundle {
|
): StreamBundle {
|
||||||
return if (streamBundle.streamClusters.isEmpty())
|
return if (targetBundle(category).streamClusters.isEmpty())
|
||||||
contract.fetch(type, category)
|
contract.fetch(StreamContract.Type.HOME, category)
|
||||||
else
|
else
|
||||||
contract.nextStreamBundle(category, nextPageUrl)
|
contract.nextStreamBundle(category, nextPageUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCategory(homeUrl: String) {
|
fun observe(category: StreamContract.Category) {
|
||||||
this.homeUrl = homeUrl
|
liveData.postValue(ViewState.Loading)
|
||||||
|
|
||||||
val rawCategory = homeUrl.split("/").last()
|
|
||||||
|
|
||||||
type = StreamContract.Type.HOME
|
|
||||||
category = StreamContract.Category.NONE.apply {
|
|
||||||
value = rawCategory
|
|
||||||
}
|
|
||||||
|
|
||||||
observe()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun observe() {
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
|
val bundle = targetBundle(category)
|
||||||
|
|
||||||
|
if (bundle.streamClusters.isNotEmpty()) {
|
||||||
|
liveData.postValue(ViewState.Success(stash))
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!streamBundle.hasCluster() || streamBundle.hasNext()) {
|
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||||
//Fetch new stream bundle
|
//Fetch new stream bundle
|
||||||
val newBundle = getCategoryStreamBundle(
|
val newBundle = getCategoryStreamBundle(
|
||||||
streamBundle.streamNextPageUrl
|
category,
|
||||||
|
bundle.streamNextPageUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
streamBundle.apply {
|
bundle.apply {
|
||||||
streamClusters.putAll(newBundle.streamClusters)
|
streamClusters.putAll(newBundle.streamClusters)
|
||||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||||
}
|
}
|
||||||
|
|
||||||
//Post updated to UI
|
//Post updated to UI
|
||||||
liveData.postValue(ViewState.Success(streamBundle))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of Bundle")
|
Log.i("End of Bundle")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
e.printStackTrace()
|
|
||||||
liveData.postValue(ViewState.Error(e.message))
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCluster(streamCluster: StreamCluster) {
|
fun observeCluster(category: StreamContract.Category, streamCluster: StreamCluster) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
if (streamCluster.hasNext()) {
|
||||||
val newCluster =
|
val newCluster =
|
||||||
contract.nextStreamCluster(streamCluster.clusterNextPageUrl)
|
contract.nextStreamCluster(streamCluster.clusterNextPageUrl)
|
||||||
updateCluster(newCluster)
|
updateCluster(category, streamCluster.id, newCluster)
|
||||||
liveData.postValue(ViewState.Success(streamBundle))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i("End of cluster")
|
Log.i("End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
streamCluster.clusterNextPageUrl = String()
|
||||||
@@ -131,10 +118,22 @@ class SubCategoryClusterViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCluster(newCluster: StreamCluster) {
|
private fun updateCluster(
|
||||||
streamBundle.streamClusters[newCluster.id]?.apply {
|
category: StreamContract.Category,
|
||||||
|
clusterID: Int,
|
||||||
|
newCluster: StreamCluster
|
||||||
|
) {
|
||||||
|
targetBundle(category).streamClusters[clusterID]?.apply {
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
clusterAppList.addAll(newCluster.clusterAppList)
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
||||||
|
val streamBundle = stash.getOrPut(category.value) {
|
||||||
|
StreamBundle()
|
||||||
|
}
|
||||||
|
|
||||||
|
return streamBundle
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user