vm: let paging errors surface so retry() re-runs the load
Stream/ExpandedStream/Review/Search paging VMs were swallowing exceptions inside manualPager and returning emptyList, so the pager never entered LoadState.Error and the error-placeholder retry buttons were dead. Let non-Auth exceptions propagate; keep the AuthException → SessionExpired branch so 401 still hands off to Splash. Also drop supervisorScope wrappers from CategoryStream/DevProfile/TopChart VMs that launch no children — the surrounding try/catch already handles everything.
This commit is contained in:
@@ -45,7 +45,7 @@ fun CategoryBrowseScreen(
|
||||
painter = painterResource(R.drawable.ic_refresh),
|
||||
message = stringResource(R.string.error),
|
||||
actionLabel = stringResource(R.string.action_retry),
|
||||
onAction = { viewModel.fetchNextPage() }
|
||||
onAction = { viewModel.fetch() }
|
||||
)
|
||||
} else {
|
||||
val bundle = (uiState as? ViewState.Success<*>)?.data as? StreamBundle
|
||||
@@ -60,7 +60,7 @@ fun CategoryBrowseScreen(
|
||||
},
|
||||
onAppClick = { onNavigateTo(Destination.AppDetails(it.packageName)) },
|
||||
onClusterScrolled = { viewModel.fetchNextCluster(it) },
|
||||
onScrolledToEnd = { viewModel.fetchNextPage() }
|
||||
onScrolledToEnd = { viewModel.fetch() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,9 +90,6 @@ class ExpandedStreamBrowseViewModel @AssistedInject constructor(
|
||||
Log.w(TAG, "Expanded stream returned ${exception.code}, redirecting to Splash")
|
||||
AuroraApp.events.send(AuthEvent.SessionExpired())
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch apps for page $page", exception)
|
||||
emptyList()
|
||||
}
|
||||
PageResult(items)
|
||||
}.flow.distinctUntilChanged()
|
||||
|
||||
@@ -27,8 +27,11 @@ import androidx.paging.cachedIn
|
||||
import com.aurora.extensions.TAG
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.gplayapi.data.models.StreamCluster
|
||||
import com.aurora.gplayapi.exceptions.GooglePlayException
|
||||
import com.aurora.gplayapi.helpers.web.WebStreamHelper
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.data.PageResult
|
||||
import com.aurora.store.data.event.AuthEvent
|
||||
import com.aurora.store.data.paging.GenericPagingSource.Companion.manualPager
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
@@ -55,10 +58,10 @@ class StreamBrowseViewModel @AssistedInject constructor(
|
||||
val apps = _apps.asStateFlow()
|
||||
|
||||
init {
|
||||
fetchApps()
|
||||
fetch()
|
||||
}
|
||||
|
||||
private fun fetchApps() {
|
||||
fun fetch() {
|
||||
var nextPageUrl: String = streamCluster.clusterNextPageUrl
|
||||
|
||||
manualPager { page ->
|
||||
@@ -79,8 +82,9 @@ class StreamBrowseViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch apps for $page: $nextPageUrl", exception)
|
||||
} catch (exception: GooglePlayException.AuthException) {
|
||||
Log.w(TAG, "Stream returned ${exception.code}, redirecting to Splash")
|
||||
AuroraApp.events.send(AuthEvent.SessionExpired())
|
||||
emptyList()
|
||||
}
|
||||
PageResult(items)
|
||||
|
||||
@@ -38,7 +38,6 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
|
||||
@HiltViewModel
|
||||
class DevProfileViewModel @Inject constructor(
|
||||
@@ -55,7 +54,6 @@ class DevProfileViewModel @Inject constructor(
|
||||
|
||||
fun getStreamBundle(devId: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
devStream = appDetailsHelper.getDeveloperStream(devId)
|
||||
streamBundle = devStream.streamBundle
|
||||
@@ -68,11 +66,9 @@ class DevProfileViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun observeCluster(streamCluster: StreamCluster) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
if (streamCluster.hasNext()) {
|
||||
val newCluster = streamHelper.getNextStreamCluster(
|
||||
@@ -90,7 +86,6 @@ class DevProfileViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCluster(newCluster: StreamCluster) {
|
||||
streamBundle.streamClusters[newCluster.id]?.let { oldCluster ->
|
||||
|
||||
@@ -71,9 +71,6 @@ class ReviewViewModel @AssistedInject constructor(
|
||||
Log.w(TAG, "Reviews fetch returned ${exception.code}, redirecting to Splash")
|
||||
AuroraApp.events.send(AuthEvent.SessionExpired(packageName))
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to fetch reviews for $page: $reviewsNextPageUrl", exception)
|
||||
emptyList()
|
||||
}
|
||||
PageResult(items)
|
||||
}.flow.distinctUntilChanged()
|
||||
|
||||
@@ -113,9 +113,6 @@ class SearchViewModel @Inject constructor(
|
||||
Log.w(TAG, "Search returned ${exception.code}, redirecting to Splash")
|
||||
AuroraApp.events.send(AuthEvent.SessionExpired())
|
||||
emptyList()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to search results for $query", exception)
|
||||
emptyList()
|
||||
}
|
||||
PageResult(items)
|
||||
}.flow.distinctUntilChanged()
|
||||
|
||||
@@ -25,7 +25,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
|
||||
@HiltViewModel(assistedFactory = CategoryStreamViewModel.Factory::class)
|
||||
class CategoryStreamViewModel @AssistedInject constructor(
|
||||
@@ -47,12 +46,11 @@ class CategoryStreamViewModel @AssistedInject constructor(
|
||||
private var streamBundle = StreamBundle.EMPTY
|
||||
|
||||
init {
|
||||
fetchNextPage()
|
||||
fetch()
|
||||
}
|
||||
|
||||
fun fetchNextPage() {
|
||||
fun fetch() {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
if (!streamBundle.hasCluster() || streamBundle.hasNext()) {
|
||||
val newBundle = if (streamBundle.streamClusters.isEmpty()) {
|
||||
@@ -79,11 +77,9 @@ class CategoryStreamViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchNextCluster(streamCluster: StreamCluster) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
if (streamCluster.hasNext()) {
|
||||
val newCluster = categoryStreamContract.nextStreamCluster(
|
||||
@@ -109,5 +105,4 @@ class CategoryStreamViewModel @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.supervisorScope
|
||||
|
||||
@HiltViewModel
|
||||
class TopChartViewModel @Inject constructor(
|
||||
@@ -69,7 +68,6 @@ class TopChartViewModel @Inject constructor(
|
||||
|
||||
fun nextCluster(type: TopChartsContract.Type, chart: TopChartsContract.Chart) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
supervisorScope {
|
||||
try {
|
||||
val target = targetCluster(type, chart)
|
||||
if (target.hasNext()) {
|
||||
@@ -86,7 +84,6 @@ class TopChartViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCluster(
|
||||
type: TopChartsContract.Type,
|
||||
|
||||
Reference in New Issue
Block a user