Experiment with stashing

This commit is contained in:
Rahul Patel
2024-06-29 02:53:18 +05:30
committed by Aayush Gupta
parent 2325b6e4a3
commit 6f425fe641
6 changed files with 123 additions and 57 deletions

View File

@@ -0,0 +1,9 @@
package com.aurora.store
import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.contracts.StreamContract
import com.aurora.gplayapi.helpers.contracts.TopChartsContract
typealias TopChartStash = Map<TopChartsContract.Type, Map<TopChartsContract.Chart, StreamCluster>>
typealias HomeStash = MutableMap<StreamContract.Category, StreamBundle>

View File

@@ -21,6 +21,10 @@ package com.aurora.store.data.model
sealed class ViewState {
inline fun <reified T> ViewState.getDataAs(): T {
return (this as? Success<*>)?.data as T
}
data object Loading : ViewState()
data object Empty : ViewState()
data class Error(val error: String?) : ViewState()
@@ -34,7 +38,7 @@ sealed class AuthState {
data object SignedIn : AuthState()
data object SignedOut : AuthState()
data object Valid : AuthState()
data object Fetching: AuthState()
data object Verifying: AuthState()
data object Fetching : AuthState()
data object Verifying : AuthState()
data class Failed(val status: String) : AuthState()
}

View File

@@ -28,8 +28,10 @@ import com.aurora.gplayapi.data.models.StreamBundle
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.contracts.StreamContract.Category
import com.aurora.gplayapi.helpers.contracts.StreamContract.Type
import com.aurora.store.HomeStash
import com.aurora.store.R
import com.aurora.store.data.model.ViewState
import com.aurora.store.data.ViewState.Loading.getDataAs
import com.aurora.store.databinding.FragmentForYouBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.controller.GenericCarouselController
@@ -43,10 +45,10 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
private var _binding: FragmentForYouBinding? = null
private val binding get() = _binding!!
private var category: Category = Category.APPLICATION
private val viewModel: StreamViewModel by activityViewModels()
private lateinit var streamBundle: StreamBundle
private var category: Category = Category.APPLICATION
private var streamBundle: StreamBundle? = StreamBundle()
companion object {
@JvmStatic
@@ -84,6 +86,13 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
}
binding.recycler.setController(genericCarouselController)
binding.recycler.addOnScrollListener(
object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
viewModel.observe(category, Type.HOME)
}
}
)
viewModel.liveData.observe(viewLifecycleOwner) {
when (it) {
@@ -92,17 +101,9 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
}
is ViewState.Success<*> -> {
if (!::streamBundle.isInitialized) {
binding.recycler.addOnScrollListener(
object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
viewModel.observe(category, Type.HOME)
}
}
)
}
val stash = it.getDataAs<HomeStash>()
streamBundle = stash[category]
streamBundle = it.data as StreamBundle
genericCarouselController.setData(streamBundle)
}

View File

@@ -21,12 +21,15 @@ package com.aurora.store.view.ui.commons
import android.os.Bundle
import android.view.View
import androidx.fragment.app.viewModels
import androidx.fragment.app.activityViewModels
import com.aurora.Constants
import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Chart
import com.aurora.gplayapi.helpers.contracts.TopChartsContract.Type
import com.aurora.store.R
import com.aurora.store.TopChartStash
import com.aurora.store.data.ViewState
import com.aurora.store.data.ViewState.Empty.getDataAs
import com.aurora.store.databinding.FragmentTopContainerBinding
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
import com.aurora.store.view.epoxy.views.AppProgressViewModel_
@@ -41,7 +44,9 @@ class TopChartFragment : BaseFragment(R.layout.fragment_top_container) {
private var _binding: FragmentTopContainerBinding? = null
private val binding get() = _binding!!
private val viewModel: TopChartViewModel by viewModels()
private val viewModel: TopChartViewModel by activityViewModels()
private var streamCluster: StreamCluster? = StreamCluster()
companion object {
@JvmStatic
@@ -59,42 +64,52 @@ class TopChartFragment : BaseFragment(R.layout.fragment_top_container) {
super.onViewCreated(view, savedInstanceState)
_binding = FragmentTopContainerBinding.bind(view)
var chartType = 0
var chartCategory = 0
var type = 0
var category = 0
val bundle = arguments
if (bundle != null) {
chartType = bundle.getInt(Constants.TOP_CHART_TYPE, 0)
chartCategory = bundle.getInt(Constants.TOP_CHART_CATEGORY, 0)
type = bundle.getInt(Constants.TOP_CHART_TYPE, 0)
category = bundle.getInt(Constants.TOP_CHART_CATEGORY, 0)
}
when (chartType) {
0 -> when (chartCategory) {
0 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.TOP_SELLING_FREE)
1 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.TOP_GROSSING)
2 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.MOVERS_SHAKERS)
3 -> viewModel.getStreamCluster(Type.APPLICATION, Chart.TOP_SELLING_PAID)
}
1 -> when (chartCategory) {
0 -> viewModel.getStreamCluster(Type.GAME, Chart.TOP_SELLING_FREE)
1 -> viewModel.getStreamCluster(Type.GAME, Chart.TOP_GROSSING)
2 -> viewModel.getStreamCluster(Type.GAME, Chart.MOVERS_SHAKERS)
3 -> viewModel.getStreamCluster(Type.GAME, Chart.TOP_SELLING_PAID)
}
val chartType = when (type) {
1 -> Type.GAME
else -> Type.APPLICATION
}
viewModel.liveData.observe(viewLifecycleOwner) {
updateController(it)
val chartCategory = when (category) {
1 -> Chart.TOP_GROSSING
2 -> Chart.MOVERS_SHAKERS
3 -> Chart.TOP_SELLING_PAID
else -> Chart.TOP_SELLING_FREE
}
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
viewModel.nextCluster()
viewModel.nextCluster(chartType, chartCategory)
}
})
updateController(null)
viewModel.getStreamCluster(chartType, chartCategory)
viewModel.liveData.observe(viewLifecycleOwner) {
when (it) {
is ViewState.Loading, is ViewState.Error -> {
updateController(null)
}
is ViewState.Success<*> -> {
val stash = it.getDataAs<TopChartStash>()
streamCluster = stash[chartType]?.get(chartCategory)
updateController(streamCluster)
}
else -> {}
}
}
}
override fun onDestroyView() {

View File

@@ -30,6 +30,7 @@ import com.aurora.gplayapi.helpers.StreamHelper
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.HomeStash
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import com.aurora.store.util.Log
@@ -55,7 +56,7 @@ class StreamViewModel @Inject constructor(
val liveData: MutableLiveData<ViewState> = MutableLiveData()
private var streamBundleMap: MutableMap<StreamContract.Category, StreamBundle> = mutableMapOf(
private var stash: HomeStash = mutableMapOf(
StreamContract.Category.APPLICATION to StreamBundle(),
StreamContract.Category.GAME to StreamBundle()
)
@@ -77,7 +78,7 @@ class StreamViewModel @Inject constructor(
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
if (targetBundle(category).streamClusters.isNotEmpty()) {
liveData.postValue(ViewState.Success(targetBundle(category)))
liveData.postValue(ViewState.Success(stash))
}
try {
@@ -100,7 +101,7 @@ class StreamViewModel @Inject constructor(
}
//Post updated to UI
liveData.postValue(ViewState.Success(targetBundle(category)))
liveData.postValue(ViewState.Success(stash))
} else {
Log.i("End of Bundle")
}
@@ -119,7 +120,7 @@ class StreamViewModel @Inject constructor(
val newCluster =
contract().nextStreamCluster(streamCluster.clusterNextPageUrl)
updateCluster(category, streamCluster.id, newCluster)
liveData.postValue(ViewState.Success(targetBundle(category)))
liveData.postValue(ViewState.Success(stash))
} else {
Log.i("End of cluster")
streamCluster.clusterNextPageUrl = String()
@@ -143,8 +144,9 @@ class StreamViewModel @Inject constructor(
}
private fun targetBundle(category: StreamContract.Category): StreamBundle {
val streamBundle = streamBundleMap[category]
// stash is initialized with empty StreamBundle so this will never return null or throw an exception
val streamBundle = stash.getValue(category)
return streamBundle!!
return streamBundle
}
}

View File

@@ -28,6 +28,8 @@ import com.aurora.gplayapi.data.models.StreamCluster
import com.aurora.gplayapi.helpers.TopChartsHelper
import com.aurora.gplayapi.helpers.contracts.TopChartsContract
import com.aurora.gplayapi.helpers.web.WebTopChartsHelper
import com.aurora.store.TopChartStash
import com.aurora.store.data.ViewState
import com.aurora.store.data.network.HttpClient
import com.aurora.store.data.providers.AuthProvider
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -50,11 +52,22 @@ class TopChartViewModel @Inject constructor(
private val webTopChartsHelper: TopChartsContract = WebTopChartsHelper()
.using(HttpClient.getPreferredClient(context))
val liveData: MutableLiveData<StreamCluster> = MutableLiveData()
var streamCluster: StreamCluster = StreamCluster()
private val dummyChart = mapOf(
TopChartsContract.Chart.TOP_GROSSING to StreamCluster(),
TopChartsContract.Chart.TOP_SELLING_FREE to StreamCluster(),
TopChartsContract.Chart.TOP_SELLING_PAID to StreamCluster(),
TopChartsContract.Chart.MOVERS_SHAKERS to StreamCluster(),
)
private var stash: TopChartStash = mapOf(
TopChartsContract.Type.APPLICATION to dummyChart,
TopChartsContract.Type.GAME to dummyChart
)
val liveData: MutableLiveData<ViewState> = MutableLiveData()
private fun contract(): TopChartsContract {
return if(authProvider.isAnonymous){
return if (authProvider.isAnonymous) {
webTopChartsHelper
} else {
topChartsHelper
@@ -63,34 +76,56 @@ class TopChartViewModel @Inject constructor(
fun getStreamCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
viewModelScope.launch(Dispatchers.IO) {
if (targetCluster(type, chart).clusterAppList.isNotEmpty()) {
liveData.postValue(ViewState.Success(stash))
}
try {
streamCluster = contract().getCluster(type.value, chart.value)
liveData.postValue(streamCluster)
val cluster = contract().getCluster(type.value, chart.value)
updateCluster(type, chart, cluster)
liveData.postValue(ViewState.Success(stash))
} catch (_: Exception) {
}
}
}
fun nextCluster() {
fun nextCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
if (streamCluster.hasNext()) {
val target = targetCluster(type, chart)
if (target.hasNext()) {
val newCluster = topChartsHelper.getNextStreamCluster(
streamCluster.clusterNextPageUrl
target.clusterNextPageUrl
)
streamCluster.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
}
updateCluster(type, chart, newCluster)
liveData.postValue(streamCluster)
} else {
liveData.postValue(ViewState.Success(stash))
}
} catch (_: Exception) {
}
}
}
}
private fun updateCluster(
type: TopChartsContract.Type,
chart: TopChartsContract.Chart,
newCluster: StreamCluster
) {
targetCluster(type, chart).apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
}
}
private fun targetCluster(
type: TopChartsContract.Type,
chart: TopChartsContract.Chart
): StreamCluster {
// Stash is initialized with empty clusters so this will never return null or throw an exception
val cluster = stash.getValue(type).getValue(chart)
return cluster
}
}