POC: Retain streambundle across fragment navigations
This commit is contained in:
committed by
Aayush Gupta
parent
c6850b176e
commit
2325b6e4a3
@@ -21,20 +21,20 @@ package com.aurora.store.data.model
|
||||
|
||||
|
||||
sealed class ViewState {
|
||||
object Loading : ViewState()
|
||||
object Empty : ViewState()
|
||||
data object Loading : ViewState()
|
||||
data object Empty : ViewState()
|
||||
data class Error(val error: String?) : ViewState()
|
||||
data class Status(val status: String?) : ViewState()
|
||||
data class Success<T>(val data: T) : ViewState()
|
||||
}
|
||||
|
||||
sealed class AuthState {
|
||||
object Available : AuthState()
|
||||
object Unavailable : AuthState()
|
||||
object SignedIn : AuthState()
|
||||
object SignedOut : AuthState()
|
||||
object Valid : AuthState()
|
||||
object Fetching: AuthState()
|
||||
object Verifying: AuthState()
|
||||
data object Available : AuthState()
|
||||
data object Unavailable : AuthState()
|
||||
data object SignedIn : AuthState()
|
||||
data object SignedOut : AuthState()
|
||||
data object Valid : AuthState()
|
||||
data object Fetching: AuthState()
|
||||
data object Verifying: AuthState()
|
||||
data class Failed(val status: String) : AuthState()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ 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.App
|
||||
import com.aurora.gplayapi.data.models.StreamBundle
|
||||
@@ -33,7 +33,7 @@ import com.aurora.store.data.model.ViewState
|
||||
import com.aurora.store.databinding.FragmentForYouBinding
|
||||
import com.aurora.store.view.custom.recycler.EndlessRecyclerOnScrollListener
|
||||
import com.aurora.store.view.epoxy.controller.GenericCarouselController
|
||||
import com.aurora.store.viewmodel.homestream.BaseClusterViewModel
|
||||
import com.aurora.store.viewmodel.homestream.StreamViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -43,7 +43,8 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
||||
private var _binding: FragmentForYouBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private val viewModel: BaseClusterViewModel by viewModels()
|
||||
private var category: Category = Category.APPLICATION
|
||||
private val viewModel: StreamViewModel by activityViewModels()
|
||||
|
||||
private lateinit var streamBundle: StreamBundle
|
||||
|
||||
@@ -71,35 +72,31 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
||||
}
|
||||
|
||||
when (pageType) {
|
||||
0 -> viewModel.getStreamBundle(Category.APPLICATION, Type.HOME)
|
||||
1 -> viewModel.getStreamBundle(Category.GAME, Type.HOME)
|
||||
0 -> {
|
||||
category = Category.APPLICATION
|
||||
viewModel.getStreamBundle(category, Type.HOME)
|
||||
}
|
||||
|
||||
1 -> {
|
||||
category = Category.GAME
|
||||
viewModel.getStreamBundle(category, Type.HOME)
|
||||
}
|
||||
}
|
||||
|
||||
binding.recycler.setController(genericCarouselController)
|
||||
|
||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
is ViewState.Empty -> {
|
||||
}
|
||||
|
||||
is ViewState.Loading -> {
|
||||
genericCarouselController.setData(null)
|
||||
}
|
||||
|
||||
is ViewState.Error -> {
|
||||
|
||||
}
|
||||
|
||||
is ViewState.Status -> {
|
||||
|
||||
}
|
||||
|
||||
is ViewState.Success<*> -> {
|
||||
if (!::streamBundle.isInitialized) {
|
||||
binding.recycler.addOnScrollListener(
|
||||
object : EndlessRecyclerOnScrollListener() {
|
||||
override fun onLoadMore(currentPage: Int) {
|
||||
viewModel.observe()
|
||||
viewModel.observe(category, Type.HOME)
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -108,6 +105,8 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
||||
streamBundle = it.data as StreamBundle
|
||||
genericCarouselController.setData(streamBundle)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,7 +122,7 @@ class ForYouFragment : BaseFragment(R.layout.fragment_for_you),
|
||||
}
|
||||
|
||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||
viewModel.observeCluster(streamCluster)
|
||||
viewModel.observeCluster(category, streamCluster)
|
||||
}
|
||||
|
||||
override fun onAppClick(app: App) {
|
||||
|
||||
@@ -42,7 +42,7 @@ import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
@SuppressLint("StaticFieldLeak") // false positive, see https://github.com/google/dagger/issues/3253
|
||||
class BaseClusterViewModel @Inject constructor(
|
||||
class StreamViewModel @Inject constructor(
|
||||
@ApplicationContext private val context: Context,
|
||||
private val authProvider: AuthProvider
|
||||
) : ViewModel() {
|
||||
@@ -54,10 +54,11 @@ class BaseClusterViewModel @Inject constructor(
|
||||
.using(HttpClient.getPreferredClient(context))
|
||||
|
||||
val liveData: MutableLiveData<ViewState> = MutableLiveData()
|
||||
var streamBundle: StreamBundle = StreamBundle()
|
||||
|
||||
lateinit var type: StreamContract.Type
|
||||
lateinit var category: StreamContract.Category
|
||||
private var streamBundleMap: MutableMap<StreamContract.Category, StreamBundle> = mutableMapOf(
|
||||
StreamContract.Category.APPLICATION to StreamBundle(),
|
||||
StreamContract.Category.GAME to StreamBundle()
|
||||
)
|
||||
|
||||
fun contract(): StreamContract {
|
||||
return if (authProvider.isAnonymous) {
|
||||
@@ -68,33 +69,38 @@ class BaseClusterViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun getStreamBundle(category: StreamContract.Category, type: StreamContract.Type) {
|
||||
this.type = type
|
||||
this.category = category
|
||||
liveData.postValue(ViewState.Loading)
|
||||
observe()
|
||||
observe(category, type)
|
||||
}
|
||||
|
||||
fun observe() {
|
||||
fun observe(category: StreamContract.Category, type: StreamContract.Type) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
if (targetBundle(category).streamClusters.isNotEmpty()) {
|
||||
liveData.postValue(ViewState.Success(targetBundle(category)))
|
||||
}
|
||||
|
||||
try {
|
||||
if (!streamBundle.hasCluster() || streamBundle.hasNext()) {
|
||||
if (!targetBundle(category).hasCluster() || targetBundle(category).hasNext()) {
|
||||
|
||||
//Fetch new stream bundle
|
||||
val newBundle = if (streamBundle.streamClusters.isEmpty()) {
|
||||
val newBundle = if (targetBundle(category).streamClusters.isEmpty()) {
|
||||
contract().fetch(type, category)
|
||||
} else {
|
||||
contract().nextStreamBundle(category, streamBundle.streamNextPageUrl)
|
||||
contract().nextStreamBundle(
|
||||
category,
|
||||
targetBundle((category)).streamNextPageUrl
|
||||
)
|
||||
}
|
||||
|
||||
//Update old bundle
|
||||
streamBundle.apply {
|
||||
targetBundle(category).apply {
|
||||
streamClusters.putAll(newBundle.streamClusters)
|
||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||
}
|
||||
|
||||
//Post updated to UI
|
||||
liveData.postValue(ViewState.Success(streamBundle))
|
||||
liveData.postValue(ViewState.Success(targetBundle(category)))
|
||||
} else {
|
||||
Log.i("End of Bundle")
|
||||
}
|
||||
@@ -105,15 +111,15 @@ class BaseClusterViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
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(streamCluster.id, newCluster)
|
||||
liveData.postValue(ViewState.Success(streamBundle))
|
||||
updateCluster(category, streamCluster.id, newCluster)
|
||||
liveData.postValue(ViewState.Success(targetBundle(category)))
|
||||
} else {
|
||||
Log.i("End of cluster")
|
||||
streamCluster.clusterNextPageUrl = String()
|
||||
@@ -125,10 +131,20 @@ class BaseClusterViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCluster(clusterID: Int, newCluster: StreamCluster) {
|
||||
streamBundle.streamClusters[clusterID]?.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 = streamBundleMap[category]
|
||||
|
||||
return streamBundle!!
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user