Merge pull request #7181 from thundernest/add_content_loading_error_view

Add content loading error view
This commit is contained in:
Wolf-Martell Montwé 2023-09-21 15:57:00 +00:00 committed by GitHub
commit 8d8450bcfe
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 277 additions and 68 deletions

View file

@ -0,0 +1,31 @@
package app.k9mail.ui.catalog.ui.common.list
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.designsystem.atom.text.TextCaption
import app.k9mail.core.ui.compose.theme.MainTheme
fun LazyGridScope.sectionInfoItem(
text: String,
) {
item(span = { GridItemSpan(maxLineSpan) }) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
start = MainTheme.spacings.double,
end = MainTheme.spacings.double,
),
horizontalAlignment = Alignment.CenterHorizontally,
) {
TextCaption(
text = text,
)
}
}
}

View file

@ -1,10 +1,23 @@
package app.k9mail.ui.catalog.ui.molecule.items
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.grid.LazyGridScope
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import app.k9mail.core.ui.compose.designsystem.atom.text.TextSubtitle1
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorState
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
import app.k9mail.ui.catalog.ui.common.list.ItemOutlined
import app.k9mail.ui.catalog.ui.common.list.sectionHeaderItem
import app.k9mail.ui.catalog.ui.common.list.sectionInfoItem
import app.k9mail.ui.catalog.ui.common.list.sectionSubtitleItem
fun LazyGridScope.stateItems() {
@ -33,4 +46,52 @@ fun LazyGridScope.stateItems() {
)
}
}
sectionHeaderItem(text = "ContentLoadingErrorView")
sectionInfoItem(text = "Click below to change state")
item {
Column {
ItemOutlined {
StatefulContentLoadingErrorState()
}
}
}
}
@Composable
private fun StatefulContentLoadingErrorState() {
val state = remember {
mutableStateOf(ContentLoadingErrorState.Loading)
}
ContentLoadingErrorView(
state = state.value,
modifier = Modifier
.clickable {
when (state.value) {
ContentLoadingErrorState.Loading -> {
state.value = ContentLoadingErrorState.Content
}
ContentLoadingErrorState.Content -> {
state.value = ContentLoadingErrorState.Error
}
ContentLoadingErrorState.Error -> {
state.value = ContentLoadingErrorState.Loading
}
}
}
.height(200.dp)
.fillMaxSize(),
error = {
TextSubtitle1(text = "Error")
},
loading = {
TextSubtitle1(text = "Loading...")
},
content = {
TextSubtitle1(text = "Content")
},
)
}

View file

@ -0,0 +1,86 @@
package app.k9mail.core.ui.compose.designsystem.molecule
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.designsystem.atom.text.TextSubtitle1
import app.k9mail.core.ui.compose.theme.PreviewWithThemes
@OptIn(ExperimentalAnimationApi::class)
@Composable
fun ContentLoadingErrorView(
state: ContentLoadingErrorState,
loading: @Composable () -> Unit,
error: @Composable () -> Unit,
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.Center,
content: @Composable () -> Unit,
) {
Box(
modifier = modifier,
contentAlignment = contentAlignment,
) {
AnimatedContent(
targetState = state,
label = "ContentLoadingErrorView",
) { targetState ->
when (targetState) {
ContentLoadingErrorState.Loading -> loading()
ContentLoadingErrorState.Content -> content()
ContentLoadingErrorState.Error -> error()
}
}
}
}
enum class ContentLoadingErrorState {
Loading, Content, Error
}
@Composable
@Preview(showBackground = true)
internal fun ContentLoadingErrorViewPreview() {
PreviewWithThemes {
val state = remember {
mutableStateOf(ContentLoadingErrorState.Loading)
}
ContentLoadingErrorView(
state = state.value,
modifier = Modifier
.clickable {
when (state.value) {
ContentLoadingErrorState.Loading -> {
state.value = ContentLoadingErrorState.Content
}
ContentLoadingErrorState.Content -> {
state.value = ContentLoadingErrorState.Error
}
ContentLoadingErrorState.Error -> {
state.value = ContentLoadingErrorState.Loading
}
}
}
.fillMaxSize(),
error = {
TextSubtitle1(text = "Error")
},
loading = {
TextSubtitle1(text = "Loading...")
},
content = {
TextSubtitle1(text = "Content")
},
)
}
}

View file

@ -1,6 +1,7 @@
package app.k9mail.core.ui.compose.designsystem.molecule
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -26,49 +27,56 @@ fun ErrorView(
modifier: Modifier = Modifier,
message: String? = null,
onRetry: () -> Unit = { },
contentAlignment: Alignment = Alignment.Center,
) {
Column(
Box(
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = MainTheme.spacings.default,
horizontal = MainTheme.spacings.double,
)
.then(modifier),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
contentAlignment = contentAlignment,
) {
Icon(
imageVector = Icons.Filled.error,
contentDescription = null,
tint = MainTheme.colors.error,
modifier = Modifier.padding(top = MainTheme.spacings.default),
)
TextSubtitle1(
text = title,
modifier = Modifier.padding(bottom = MainTheme.spacings.default),
)
if (message != null) {
TextBody2(
text = message,
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = MainTheme.spacings.default,
horizontal = MainTheme.spacings.double,
),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
Icon(
imageVector = Icons.Filled.error,
contentDescription = null,
tint = MainTheme.colors.error,
modifier = Modifier.padding(top = MainTheme.spacings.default),
)
TextSubtitle1(
text = title,
modifier = Modifier.padding(bottom = MainTheme.spacings.default),
)
if (message != null) {
TextBody2(
text = message,
modifier = Modifier
.fillMaxWidth(),
)
}
Row(
modifier = Modifier
.fillMaxWidth(),
)
}
Row(
modifier = Modifier
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
) {
ButtonText(
text = stringResource(id = R.string.designsystem_molecule_error_view_button_retry),
onClick = onRetry,
contentPadding = buttonContentPadding(
start = MainTheme.spacings.double,
end = MainTheme.spacings.double,
),
)
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End,
) {
ButtonText(
text = stringResource(id = R.string.designsystem_molecule_error_view_button_retry),
onClick = onRetry,
contentPadding = buttonContentPadding(
start = MainTheme.spacings.double,
end = MainTheme.spacings.double,
),
)
}
}
}
}

View file

@ -1,5 +1,7 @@
package app.k9mail.core.ui.compose.designsystem.molecule
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -18,22 +20,31 @@ import app.k9mail.core.ui.compose.theme.PreviewWithThemes
fun LoadingView(
modifier: Modifier = Modifier,
message: String? = null,
contentAlignment: Alignment = Alignment.Center,
) {
Column(
Box(
modifier = Modifier
.fillMaxWidth()
.padding(MainTheme.spacings.default)
.then(modifier),
horizontalAlignment = Alignment.CenterHorizontally,
contentAlignment = contentAlignment,
) {
if (message != null) {
TextSubtitle1(text = message)
}
Row(
modifier = Modifier.height(MainTheme.sizes.larger),
verticalAlignment = Alignment.CenterVertically,
Column(
modifier = Modifier
.fillMaxWidth()
.padding(MainTheme.spacings.default)
.then(modifier),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
CircularProgressIndicator()
if (message != null) {
TextSubtitle1(text = message)
}
Row(
modifier = Modifier.height(MainTheme.sizes.larger),
verticalAlignment = Alignment.CenterVertically,
) {
CircularProgressIndicator()
}
}
}
}

View file

@ -8,18 +8,21 @@ import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import app.k9mail.core.ui.compose.common.DevicePreviews
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorState
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
import app.k9mail.core.ui.compose.theme.K9Theme
import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.ThunderbirdTheme
import app.k9mail.feature.account.common.ui.item.ErrorItem
import app.k9mail.feature.account.common.ui.item.LoadingItem
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
import app.k9mail.feature.account.oauth.ui.preview.PreviewAccountOAuthViewModel
import app.k9mail.feature.account.setup.R
@ -43,28 +46,37 @@ internal fun AccountAutoDiscoveryContent(
.then(modifier),
) {
val resources = LocalContext.current.resources
val viewState = remember(key1 = state.isLoading, key2 = state.error) {
when {
state.isLoading -> ContentLoadingErrorState.Loading
state.error != null -> ContentLoadingErrorState.Error
else -> ContentLoadingErrorState.Content
}
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.imePadding(),
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double, Alignment.CenterVertically),
ContentLoadingErrorView(
state = viewState,
loading = {
LoadingView(
message = stringResource(id = R.string.account_setup_auto_discovery_loading_message),
modifier = Modifier.fillMaxSize(),
)
},
error = {
ErrorView(
title = stringResource(id = R.string.account_setup_auto_discovery_loading_error),
message = state.error?.toResourceString(resources),
onRetry = { onEvent(Event.OnRetryClicked) },
modifier = Modifier.fillMaxSize(),
)
},
) {
if (state.isLoading) {
item(key = "loading") {
LoadingItem(
message = stringResource(id = R.string.account_setup_auto_discovery_loading_message),
)
}
} else if (state.error != null) {
item(key = "error") {
ErrorItem(
title = stringResource(id = R.string.account_setup_auto_discovery_loading_error),
message = state.error.toResourceString(resources),
onRetry = { onEvent(Event.OnRetryClicked) },
)
}
} else {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.imePadding(),
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.double, Alignment.CenterVertically),
) {
contentItems(
state = state,
onEvent = onEvent,