From 272870ddd2eab44fe37518ac4faec17ca6c9c675 Mon Sep 17 00:00:00 2001 From: Rahul Patel Date: Wed, 20 May 2026 16:38:57 +0530 Subject: [PATCH] auth: refresh anonymous session on activity resume Long-backgrounded sessions (especially on aggressive vendors like Huawei) could leave the Play token invalid and OkHttp's connection pool full of dead sockets, producing a perpetual loading spinner with no error. - ComposeActivity#onStart probes the saved token via isSavedAuthDataValid; on failure evicts the OkHttp pool and refreshes anonymous auth. - AuthProvider gains authReady StateFlow, awaitReady(), and a mutex-guarded refreshAnonymousAuth() so concurrent callers don't double-fetch. - Play-backed ViewModels (AppDetails, DevProfile, MoreApps, Reviews, Search, ExpandedStreamBrowse, Category) await refresh completion before issuing helper calls, closing the race between resume-time refresh and screen LaunchedEffect fetches. - AppDetailsScreen rendered Error as Loading when app was null; reorder so Error wins and wire a Retry button through Placeholder. - Stream/Review/Search/DevProfile/CategoryBrowse error placeholders gain Retry actions wired to PagingItems.retry() or the VM's fetch. - CategoryViewModel was logging-only on failure; now emits ViewState.Error with a matching Retry path in CategoriesPage. --- .../java/com/aurora/store/ComposeActivity.kt | 24 +++++++++++ .../store/compose/ui/apps/CategoriesPage.kt | 15 +++++++ .../ui/commons/CategoryBrowseScreen.kt | 4 +- .../ui/commons/ExpandedStreamBrowseScreen.kt | 4 +- .../compose/ui/commons/StreamBrowseScreen.kt | 4 +- .../compose/ui/details/AppDetailsScreen.kt | 17 ++++---- .../store/compose/ui/details/ReviewScreen.kt | 4 +- .../store/compose/ui/dev/DevProfileScreen.kt | 4 +- .../store/compose/ui/search/SearchScreen.kt | 4 +- .../store/data/providers/AuthProvider.kt | 40 +++++++++++++++++++ .../browse/ExpandedStreamBrowseViewModel.kt | 3 ++ .../viewmodel/category/CategoryViewModel.kt | 6 +++ .../viewmodel/details/AppDetailsViewModel.kt | 1 + .../viewmodel/details/DevProfileViewModel.kt | 4 ++ .../store/viewmodel/details/MoreViewModel.kt | 3 ++ .../viewmodel/details/ReviewViewModel.kt | 3 ++ .../store/viewmodel/search/SearchViewModel.kt | 2 + 17 files changed, 129 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/com/aurora/store/ComposeActivity.kt b/app/src/main/java/com/aurora/store/ComposeActivity.kt index da6576e03..b965f21e9 100644 --- a/app/src/main/java/com/aurora/store/ComposeActivity.kt +++ b/app/src/main/java/com/aurora/store/ComposeActivity.kt @@ -15,6 +15,7 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.getValue import androidx.core.content.IntentCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.lifecycleScope import com.aurora.extensions.getPackageName import com.aurora.store.compose.composition.LocalNetworkStatus import com.aurora.store.compose.composition.LocalUI @@ -23,18 +24,27 @@ import com.aurora.store.compose.navigation.NavDisplay import com.aurora.store.compose.navigation.Screen import com.aurora.store.compose.theme.AuroraTheme import com.aurora.store.data.model.NetworkStatus +import com.aurora.store.data.providers.AccountProvider +import com.aurora.store.data.providers.AuthProvider import com.aurora.store.data.providers.NetworkProvider import com.aurora.store.data.receiver.MigrationReceiver import com.aurora.store.util.PackageUtil import com.aurora.store.util.Preferences import dagger.hilt.android.AndroidEntryPoint import javax.inject.Inject +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import okhttp3.OkHttpClient @AndroidEntryPoint class ComposeActivity : ComponentActivity() { @Inject lateinit var networkProvider: NetworkProvider + @Inject lateinit var authProvider: AuthProvider + + @Inject lateinit var okHttpClient: OkHttpClient + override fun onCreate(savedInstanceState: Bundle?) { MigrationReceiver.runMigrationsIfRequired(this) enableEdgeToEdge() @@ -91,4 +101,18 @@ class ComposeActivity : ComponentActivity() { !Preferences.getBoolean(this, Preferences.PREFERENCE_INTRO) -> Screen.Onboarding else -> Screen.Splash } + + override fun onStart() { + super.onStart() + if (!AccountProvider.isLoggedIn(this)) return + lifecycleScope.launch(Dispatchers.IO) { + // Ask Play directly if the saved token still works. If it does, + // sockets are fine too and there's nothing to do. If it doesn't, + // evict the pool (the same backgrounded state that invalidates + // tokens also leaves dead sockets) and refresh anonymous auth. + if (authProvider.isSavedAuthDataValid()) return@launch + okHttpClient.connectionPool.evictAll() + if (authProvider.isAnonymous) authProvider.refreshAnonymousAuth() + } + } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/apps/CategoriesPage.kt b/app/src/main/java/com/aurora/store/compose/ui/apps/CategoriesPage.kt index c07ae64a5..7fb00d14f 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/apps/CategoriesPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/apps/CategoriesPage.kt @@ -12,11 +12,15 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewWrapper import com.aurora.gplayapi.data.models.Category import com.aurora.store.CategoryStash +import com.aurora.store.R import com.aurora.store.compose.composable.CategoryItem +import com.aurora.store.compose.composable.Placeholder import com.aurora.store.compose.composable.ShimmerCategoryRow import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.data.model.ViewState @@ -35,6 +39,17 @@ internal fun CategoriesContent( viewModel.getCategoryList(categoryType) } + if (state is ViewState.Error) { + Placeholder( + modifier = Modifier.fillMaxSize(), + painter = painterResource(R.drawable.ic_disclaimer), + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { viewModel.getCategoryList(categoryType) } + ) + return + } + @Suppress("UNCHECKED_CAST") val categories = (state as? ViewState.Success<*>)?.data as? CategoryStash val list = categories?.get(categoryType) diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt index 93a1c7aa9..c3ca7b275 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/CategoryBrowseScreen.kt @@ -43,7 +43,9 @@ fun CategoryBrowseScreen( Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), - message = stringResource(R.string.error) + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { viewModel.fetchNextPage() } ) } else { val bundle = (uiState as? ViewState.Success<*>)?.data as? StreamBundle diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt index 4d9736c0d..14e39fb07 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/ExpandedStreamBrowseScreen.kt @@ -57,7 +57,9 @@ fun ExpandedStreamBrowseScreen( Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), - message = stringResource(R.string.error) + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { apps.retry() } ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt index 577990f2a..9ca0418ff 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/commons/StreamBrowseScreen.kt @@ -76,7 +76,9 @@ private fun ScreenContent( Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), - message = stringResource(R.string.error) + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { apps.retry() } ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt index b0bd2c83b..dba6279ae 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt @@ -148,14 +148,15 @@ fun AppDetailsScreen( label = "AppDetailsState" ) { currentState -> when { - currentState is AppState.Loading || app == null -> - ScreenContentLoading() - currentState is AppState.Error -> ScreenContentError( - message = currentState.message + message = currentState.message, + onRetry = { viewModel.fetchAppDetails(packageName) } ) + currentState is AppState.Loading || app == null -> + ScreenContentLoading() + else -> { val loadedApp = app!! ScreenContentApp( @@ -199,8 +200,8 @@ fun AppDetailsScreen( } private fun stateKey(state: AppState, app: App?): String = when { - state is AppState.Loading || app == null -> "loading" state is AppState.Error -> "error" + state is AppState.Loading || app == null -> "loading" else -> "content" } @@ -220,14 +221,16 @@ private fun ScreenContentLoading() { * Composable to display errors related to fetching app details */ @Composable -private fun ScreenContentError(message: String? = null) { +private fun ScreenContentError(message: String? = null, onRetry: (() -> Unit)? = null) { Scaffold( topBar = { TopAppBar() } ) { paddingValues -> Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_apps_outage), - message = message ?: stringResource(R.string.toast_app_unavailable) + message = message ?: stringResource(R.string.toast_app_unavailable), + actionLabel = onRetry?.let { stringResource(R.string.action_retry) }, + onAction = onRetry ) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt index f7bf883b7..f48110c67 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/ReviewScreen.kt @@ -117,7 +117,9 @@ private fun ScreenContent( Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), - message = stringResource(R.string.error) + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { reviews.retry() } ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt index ef5e0fb5b..929499b02 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/dev/DevProfileScreen.kt @@ -99,7 +99,9 @@ private fun ScreenContent( Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), - message = stringResource(R.string.error) + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { apps.retry() } ) } diff --git a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt index 9f8302ef5..5924c23f2 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/search/SearchScreen.kt @@ -253,7 +253,9 @@ private fun ScreenContent( Placeholder( modifier = Modifier.padding(paddingValues), painter = painterResource(R.drawable.ic_disclaimer), - message = stringResource(R.string.error) + message = stringResource(R.string.error), + actionLabel = stringResource(R.string.action_retry), + onAction = { results.retry() } ) } diff --git a/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt b/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt index e82a5a882..70cef5d73 100644 --- a/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt +++ b/app/src/main/java/com/aurora/store/data/providers/AuthProvider.kt @@ -36,6 +36,12 @@ import dagger.hilt.android.qualifiers.ApplicationContext import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import kotlinx.serialization.json.Json @@ -67,11 +73,45 @@ class AuthProvider @Inject constructor( val isAnonymous: Boolean get() = AccountProvider.getAccountType(context) == AccountType.ANONYMOUS + private val _authReady = MutableStateFlow(true) + + /** + * Emits false while an auth refresh is in flight so callers can defer + * network work until fresh credentials are persisted. + */ + val authReady: StateFlow = _authReady.asStateFlow() + + private val refreshMutex = Mutex() + /** * Checks whether saved AuthData is valid or not */ fun isSavedAuthDataValid(): Boolean = AuthHelper.isValid(authData!!) + /** + * Suspends until no auth refresh is in flight. ViewModels should call this + * before issuing Play requests so a foreground refresh doesn't race the fetch. + */ + suspend fun awaitReady() { + _authReady.first { it } + } + + /** + * Rebuilds anonymous [AuthData] from a dispenser and persists it. Serialized + * so concurrent callers don't double-fetch. On failure, the previously saved + * auth is left in place so requests can still attempt with stale credentials. + */ + suspend fun refreshAnonymousAuth(): Result = refreshMutex.withLock { + try { + _authReady.value = false + val result = buildAnonymousAuthData() + result.onSuccess { saveAuthData(it) } + result + } finally { + _authReady.value = true + } + } + /** * Builds [AuthData] for login using personal Google account * @param email E-mail ID diff --git a/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt index 9c503fc0b..42718a7bc 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/browse/ExpandedStreamBrowseViewModel.kt @@ -16,6 +16,7 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.helpers.ExpandedBrowseHelper import com.aurora.store.data.PageResult import com.aurora.store.data.paging.GenericPagingSource.Companion.manualPager +import com.aurora.store.data.providers.AuthProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -29,6 +30,7 @@ import kotlinx.coroutines.flow.onEach @HiltViewModel(assistedFactory = ExpandedStreamBrowseViewModel.Factory::class) class ExpandedStreamBrowseViewModel @AssistedInject constructor( @Assisted val browseUrl: String, + private val authProvider: AuthProvider, private val streamHelper: ExpandedBrowseHelper ) : ViewModel() { @@ -53,6 +55,7 @@ class ExpandedStreamBrowseViewModel @AssistedInject constructor( manualPager { page -> val items = try { + authProvider.awaitReady() when (page) { 1 -> { val browseResponse = streamHelper.getBrowseStreamResponse(browseUrl) diff --git a/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt index 9d23d0b49..305f1ab2d 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/category/CategoryViewModel.kt @@ -29,6 +29,7 @@ import com.aurora.gplayapi.helpers.CategoryHelper import com.aurora.gplayapi.helpers.contracts.CategoryContract import com.aurora.store.CategoryStash import com.aurora.store.data.model.ViewState +import com.aurora.store.data.providers.AuthProvider import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.Dispatchers @@ -36,6 +37,7 @@ import kotlinx.coroutines.launch @HiltViewModel class CategoryViewModel @Inject constructor( + private val authProvider: AuthProvider, private val categoryHelper: CategoryHelper ) : ViewModel() { @@ -57,11 +59,15 @@ class CategoryViewModel @Inject constructor( return@launch } + liveData.postValue(ViewState.Loading) + try { + authProvider.awaitReady() stash[type] = contract().getAllCategories(type) liveData.postValue(ViewState.Success(stash)) } catch (exception: Exception) { Log.e(TAG, "Failed fetching list of categories", exception) + liveData.postValue(ViewState.Error(exception.message)) } } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt index e01e362ca..75e9d29bb 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/AppDetailsViewModel.kt @@ -168,6 +168,7 @@ class AppDetailsViewModel @Inject constructor( fun fetchAppDetails(packageName: String) { viewModelScope.launch(Dispatchers.IO) { try { + authProvider.awaitReady() _app.value = appDetailsHelper.getAppByPackageName(packageName).copy( isInstalled = PackageUtil.isInstalled(context, packageName) ) diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt index bf561e26b..47e69114a 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/DevProfileViewModel.kt @@ -31,6 +31,7 @@ import com.aurora.gplayapi.helpers.AppDetailsHelper import com.aurora.gplayapi.helpers.StreamHelper import com.aurora.gplayapi.helpers.contracts.StreamContract import com.aurora.store.data.model.ViewState +import com.aurora.store.data.providers.AuthProvider import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.Dispatchers @@ -39,6 +40,7 @@ import kotlinx.coroutines.supervisorScope @HiltViewModel class DevProfileViewModel @Inject constructor( + private val authProvider: AuthProvider, private val appDetailsHelper: AppDetailsHelper, private val streamHelper: StreamHelper ) : ViewModel() { @@ -54,6 +56,7 @@ class DevProfileViewModel @Inject constructor( viewModelScope.launch(Dispatchers.IO) { supervisorScope { try { + authProvider.awaitReady() devStream = appDetailsHelper.getDeveloperStream(devId) streamBundle = devStream.streamBundle liveData.postValue(ViewState.Success(devStream)) @@ -69,6 +72,7 @@ class DevProfileViewModel @Inject constructor( supervisorScope { try { if (streamCluster.hasNext()) { + authProvider.awaitReady() val newCluster = streamHelper.getNextStreamCluster( streamCluster.clusterNextPageUrl ) diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/MoreViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/MoreViewModel.kt index 1fc6ef1c5..bfe555bc0 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/MoreViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/MoreViewModel.kt @@ -12,6 +12,7 @@ import androidx.lifecycle.viewModelScope import com.aurora.extensions.TAG import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.helpers.AppDetailsHelper +import com.aurora.store.data.providers.AuthProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -24,6 +25,7 @@ import kotlinx.coroutines.launch @HiltViewModel(assistedFactory = MoreViewModel.Factory::class) class MoreViewModel @AssistedInject constructor( @Assisted private val dependencies: List, + private val authProvider: AuthProvider, private val appDetailsHelper: AppDetailsHelper ) : ViewModel() { @@ -42,6 +44,7 @@ class MoreViewModel @AssistedInject constructor( private fun fetchDependencies() { viewModelScope.launch(Dispatchers.IO) { try { + authProvider.awaitReady() _dependentApps.value = appDetailsHelper.getAppByPackageName(dependencies) } catch (exception: Exception) { Log.e(TAG, "Failed to fetch dependencies", exception) diff --git a/app/src/main/java/com/aurora/store/viewmodel/details/ReviewViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/details/ReviewViewModel.kt index f91e3e2ce..618cf5d5a 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/details/ReviewViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/details/ReviewViewModel.kt @@ -16,6 +16,7 @@ import com.aurora.gplayapi.data.models.Review import com.aurora.gplayapi.helpers.ReviewsHelper import com.aurora.store.data.PageResult import com.aurora.store.data.paging.GenericPagingSource.Companion.manualPager +import com.aurora.store.data.providers.AuthProvider import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -29,6 +30,7 @@ import kotlinx.coroutines.flow.onEach @HiltViewModel(assistedFactory = ReviewViewModel.Factory::class) class ReviewViewModel @AssistedInject constructor( @Assisted private val packageName: String, + private val authProvider: AuthProvider, private val reviewsHelper: ReviewsHelper ) : ViewModel() { @@ -49,6 +51,7 @@ class ReviewViewModel @AssistedInject constructor( manualPager { page -> val items = try { + authProvider.awaitReady() when (page) { 1 -> reviewsHelper.getReviews(packageName, filter).also { reviewsNextPageUrl = it.nextPageUrl diff --git a/app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt index ead7a2ef6..505036a3d 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/search/SearchViewModel.kt @@ -83,6 +83,7 @@ class SearchViewModel @Inject constructor( manualPager { page -> val items = try { + authProvider.awaitReady() when (page) { 1 -> contract.searchResults(query) .also { nextBundleUrl = it.streamNextPageUrl } @@ -121,6 +122,7 @@ class SearchViewModel @Inject constructor( fun fetchSuggestions(query: String) { viewModelScope.launch(Dispatchers.IO) { + authProvider.awaitReady() _suggestions.value = contract.searchSuggestions(query) .filter { it.title.isNotBlank() } .take(3)