compose: details: Expose actual error message when possible

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2025-08-31 12:38:03 +08:00
parent b5733d070e
commit eeff0a65aa
7 changed files with 44 additions and 29 deletions

View File

@@ -5,11 +5,11 @@
package com.aurora.store.compose.composables
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredSize
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
@@ -17,9 +17,11 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import com.aurora.store.R
@@ -35,13 +37,15 @@ import com.aurora.store.R
@Composable
fun ErrorComposable(
modifier: Modifier = Modifier,
@DrawableRes icon: Int,
@StringRes message: Int,
@StringRes actionMessage: Int? = null,
icon: Painter,
message: String,
actionMessage: String? = null,
onAction: () -> Unit = {}
) {
Column(
modifier = modifier.fillMaxSize(),
modifier = modifier
.fillMaxSize()
.padding(dimensionResource(R.dimen.padding_medium)),
verticalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.margin_small),
Alignment.CenterVertically
@@ -49,16 +53,20 @@ fun ErrorComposable(
horizontalAlignment = Alignment.CenterHorizontally
) {
Icon(
painter = painterResource(id = icon),
painter = icon,
contentDescription = null,
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size))
)
Text(text = stringResource(message))
Text(
modifier = Modifier.fillMaxWidth(),
text = message,
textAlign = TextAlign.Center
)
if (actionMessage != null) {
Button(onClick = onAction) {
Text(
text = stringResource(actionMessage),
text = actionMessage,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -71,8 +79,8 @@ fun ErrorComposable(
@Composable
private fun ErrorComposablePreview() {
ErrorComposable(
icon = R.drawable.ic_updates,
message = R.string.details_no_updates,
actionMessage = R.string.check_updates
icon = painterResource(R.drawable.ic_updates),
message = stringResource(R.string.details_no_updates),
actionMessage = stringResource(R.string.check_updates)
)
}

View File

@@ -111,7 +111,12 @@ fun AppDetailsScreen(
when (state) {
is AppState.Loading -> ScreenContentLoading(onNavigateUp = onNavigateUp)
is AppState.Error -> ScreenContentError(onNavigateUp = onNavigateUp)
is AppState.Error -> {
ScreenContentError(
onNavigateUp = onNavigateUp,
message = (state as AppState.Error).message
)
}
else -> {
ScreenContentApp(
@@ -164,14 +169,14 @@ private fun ScreenContentLoading(onNavigateUp: () -> Unit = {}) {
* Composable to display errors related to fetching app details
*/
@Composable
private fun ScreenContentError(onNavigateUp: () -> Unit = {}) {
private fun ScreenContentError(onNavigateUp: () -> Unit = {}, message: String? = null) {
Scaffold(
topBar = { TopAppBarComposable(onNavigateUp = onNavigateUp) }
) { paddingValues ->
ErrorComposable(
modifier = Modifier.padding(paddingValues),
icon = R.drawable.ic_apps_outage,
message = R.string.toast_app_unavailable
icon = painterResource(R.drawable.ic_apps_outage),
message = message ?: stringResource(R.string.toast_app_unavailable)
)
}
}

View File

@@ -114,8 +114,8 @@ private fun ScreenContent(
is LoadState.Error -> {
ErrorComposable(
modifier = Modifier.padding(paddingValues),
icon = R.drawable.ic_disclaimer,
message = R.string.error
icon = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error)
)
}

View File

@@ -16,6 +16,8 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
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.PreviewParameter
import androidx.hilt.navigation.compose.hiltViewModel
@@ -99,8 +101,8 @@ private fun ScreenContent(
is LoadState.Error -> {
ErrorComposable(
modifier = Modifier.padding(paddingValues),
icon = R.drawable.ic_disclaimer,
message = R.string.error
icon = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error)
)
}

View File

@@ -212,8 +212,8 @@ private fun ScreenContent(
is LoadState.Error -> {
ErrorComposable(
modifier = Modifier.padding(paddingValues),
icon = R.drawable.ic_disclaimer,
message = R.string.error
icon = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.error)
)
}
@@ -221,8 +221,8 @@ private fun ScreenContent(
if (isSearching && results.itemCount == 0) {
ErrorComposable(
modifier = Modifier.padding(paddingValues),
icon = R.drawable.ic_disclaimer,
message = R.string.no_apps_available
icon = painterResource(R.drawable.ic_disclaimer),
message = stringResource(R.string.no_apps_available)
)
} else {
LazyColumn {
@@ -261,10 +261,10 @@ private fun ScreenContent(
}
else -> {
if (textFieldState.text.isNotBlank() && results.itemCount > 0) {
if (isSearching && results.itemCount > 0) {
ErrorComposable(
icon = R.drawable.ic_round_search,
message = R.string.select_app_for_details
icon = painterResource(R.drawable.ic_round_search),
message = stringResource(R.string.select_app_for_details)
)
}
}

View File

@@ -56,12 +56,12 @@ sealed class AppState {
) : AppState()
data class Installing(val progress: Float) : AppState()
data class Error(val message: String?) : AppState()
data object Installed : AppState()
data object Archived : AppState()
data object Updatable : AppState()
data object Unavailable : AppState()
data object Loading : AppState()
data object Error : AppState()
/**
* Whether there is some sort of ongoing process related to the app

View File

@@ -153,7 +153,7 @@ class AppDetailsViewModel @Inject constructor(
} catch (exception: Exception) {
Log.e(TAG, "Failed to fetch app details", exception)
_app.value = null
_state.value = AppState.Error
_state.value = AppState.Error(exception.message)
}
}.invokeOnCompletion { throwable ->
// Only proceed if there was no error while fetching the app details