diff --git a/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/common/list/SectionInfoItem.kt b/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/common/list/SectionInfoItem.kt new file mode 100644 index 000000000..dd063ef23 --- /dev/null +++ b/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/common/list/SectionInfoItem.kt @@ -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, + ) + } + } +} diff --git a/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/molecule/items/StateItems.kt b/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/molecule/items/StateItems.kt index 3bd37e2fa..82231091d 100644 --- a/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/molecule/items/StateItems.kt +++ b/app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/molecule/items/StateItems.kt @@ -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") + }, + ) } diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt new file mode 100644 index 000000000..3985665b8 --- /dev/null +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt @@ -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") + }, + ) + } +} diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt index 54f7d38c3..d60281fce 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ErrorView.kt @@ -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, + ), + ) + } } } } diff --git a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt index cb799625b..326639810 100644 --- a/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt +++ b/core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/LoadingView.kt @@ -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() + } } } } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt index 0b515289f..1b1a48b88 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt @@ -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,