Skip to content

Commit a36e7ef

Browse files
committed
Change ContentLoadingErrorView to pass state
1 parent b4fd053 commit a36e7ef

File tree

14 files changed

+82
-70
lines changed

14 files changed

+82
-70
lines changed

app-ui-catalog/src/main/java/app/k9mail/ui/catalog/ui/molecule/items/StateItems.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ private fun StatefulContentLoadingView() {
130130
@Composable
131131
private fun StatefulContentLoadingErrorView() {
132132
val state = remember {
133-
mutableStateOf(ContentLoadingErrorState.Loading)
133+
mutableStateOf<ContentLoadingErrorState>(ContentLoadingErrorState.Loading)
134134
}
135135

136136
ContentLoadingErrorView(

core/ui/compose/designsystem/src/debug/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorViewPreview.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ internal fun ContentLoadingErrorViewErrorPreview() {
4545
internal fun ContentLoadingErrorViewInteractivePreview() {
4646
PreviewWithThemes {
4747
val state = remember {
48-
mutableStateOf(ContentLoadingErrorState.Loading)
48+
mutableStateOf<ContentLoadingErrorState>(ContentLoadingErrorState.Loading)
4949
}
5050

5151
DefaultContentLoadingErrorView(

core/ui/compose/designsystem/src/main/kotlin/app/k9mail/core/ui/compose/designsystem/molecule/ContentLoadingErrorView.kt

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,34 @@ import androidx.compose.runtime.Composable
66
import androidx.compose.ui.Alignment
77
import androidx.compose.ui.Modifier
88

9+
/**
10+
* A container view that can animate between a loading view, an error view, and a content view.
11+
*
12+
* @param ERROR The type describing the error.
13+
* @param STATE The type of the state being passed to this view.
14+
*
15+
* @param state The state relevant for displaying the content inside this view.
16+
* @param loading When `state.isLoading` is `true`, this composable function is displayed.
17+
* @param error When `state.isLoading` is `false` and `state.error` is not `null`, this composable function is displayed
18+
* with `state.error` being passed as the argument.
19+
* @param content When `state.isLoading` is `false` and `state.error` is `null`, this composable function is displayed
20+
* with [state] being passed as the argument.
21+
*
22+
* **IMPORTANT**: This is a delicate API whose usage should be carefully reviewed. It is using [AnimatedContent] and
23+
* inherits its caveats.
24+
*
25+
* The [loading], [error] and [content] composable functions should only use the state being passed to them (if any).
26+
* If you disregard this advice, make sure to read the documentation of [AnimatedContent] to learn when the composable
27+
* functions are invoked and what that means for the external state a function fetches.
28+
*/
929
@Composable
10-
fun ContentLoadingErrorView(
11-
state: ContentLoadingErrorState,
30+
fun <ERROR, STATE : LoadingErrorState<ERROR>> ContentLoadingErrorView(
31+
state: STATE,
1232
loading: @Composable () -> Unit,
13-
error: @Composable () -> Unit,
33+
error: @Composable (ERROR) -> Unit,
1434
modifier: Modifier = Modifier,
1535
contentAlignment: Alignment = Alignment.Center,
16-
content: @Composable () -> Unit,
36+
content: @Composable (STATE) -> Unit,
1737
) {
1838
Box(
1939
modifier = modifier,
@@ -22,18 +42,42 @@ fun ContentLoadingErrorView(
2242
AnimatedContent(
2343
targetState = state,
2444
label = "ContentLoadingErrorView",
45+
contentKey = { targetState ->
46+
ContentKey(isLoading = targetState.isLoading, error = targetState.error)
47+
},
2548
) { targetState ->
26-
when (targetState) {
27-
ContentLoadingErrorState.Loading -> loading()
28-
ContentLoadingErrorState.Content -> content()
29-
ContentLoadingErrorState.Error -> error()
49+
val errorValue = targetState.error
50+
when {
51+
targetState.isLoading -> loading()
52+
errorValue != null -> error(errorValue)
53+
else -> content(targetState)
3054
}
3155
}
3256
}
3357
}
3458

35-
enum class ContentLoadingErrorState {
36-
Loading,
37-
Content,
38-
Error,
59+
/**
60+
* Signals [ContentLoadingErrorView] which of its composable function parameters to execute/display.
61+
*/
62+
interface LoadingErrorState<ERROR> {
63+
val isLoading: Boolean
64+
val error: ERROR?
65+
}
66+
67+
private data class ContentKey<ERROR>(
68+
override val isLoading: Boolean,
69+
override val error: ERROR?,
70+
) : LoadingErrorState<ERROR>
71+
72+
/**
73+
* Helper that can be use as `state` argument for [ContentLoadingErrorView] when none of the composable function
74+
* parameters need access to any state.
75+
*/
76+
sealed class ContentLoadingErrorState private constructor(
77+
override val isLoading: Boolean,
78+
override val error: Unit?,
79+
) : LoadingErrorState<Unit> {
80+
data object Loading : ContentLoadingErrorState(isLoading = true, error = null)
81+
data object Content : ContentLoadingErrorState(isLoading = false, error = null)
82+
data object Error : ContentLoadingErrorState(isLoading = false, error = Unit)
3983
}

feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/loadingerror/LoadingErrorState.kt

Lines changed: 0 additions & 21 deletions
This file was deleted.

feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/server/settings/save/SaveServerSettingsContent.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
1111
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
1212
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
1313
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
14-
import app.k9mail.feature.account.common.ui.loadingerror.rememberContentLoadingErrorViewState
1514
import app.k9mail.feature.account.edit.R
1615

1716
@Composable
@@ -27,7 +26,7 @@ fun SaveServerSettingsContent(
2726
.then(modifier),
2827
) {
2928
ContentLoadingErrorView(
30-
state = rememberContentLoadingErrorViewState(state),
29+
state = state,
3130
loading = {
3231
LoadingView(
3332
message = stringResource(id = R.string.account_edit_save_server_settings_loading_message),

feature/account/edit/src/main/kotlin/app/k9mail/feature/account/edit/ui/server/settings/save/SaveServerSettingsContract.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package app.k9mail.feature.account.edit.ui.server.settings.save
22

33
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
4-
import app.k9mail.feature.account.common.ui.loadingerror.LoadingErrorState
4+
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingErrorState
55

66
interface SaveServerSettingsContract {
77

feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContent.kt

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import app.k9mail.core.ui.compose.theme2.MainTheme
2525
import app.k9mail.feature.account.common.ui.AppTitleTopHeader
2626
import app.k9mail.feature.account.common.ui.WizardNavigationBar
2727
import app.k9mail.feature.account.common.ui.WizardNavigationBarState
28-
import app.k9mail.feature.account.common.ui.loadingerror.rememberContentLoadingErrorViewState
2928
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
3029
import app.k9mail.feature.account.oauth.ui.AccountOAuthView
3130
import app.k9mail.feature.account.setup.R
@@ -90,24 +89,25 @@ internal fun AutoDiscoveryContent(
9089
val resources = LocalContext.current.resources
9190

9291
ContentLoadingErrorView(
93-
state = rememberContentLoadingErrorViewState(state),
92+
state = state,
9493
loading = {
9594
LoadingView(
9695
message = stringResource(id = R.string.account_setup_auto_discovery_loading_message),
9796
modifier = Modifier.fillMaxSize(),
9897
)
9998
},
100-
error = {
99+
error = { error ->
101100
ErrorView(
102101
title = stringResource(id = R.string.account_setup_auto_discovery_loading_error),
103-
message = state.error?.toAutoDiscoveryErrorString(resources),
102+
message = error.toAutoDiscoveryErrorString(resources),
104103
onRetry = { onEvent(Event.OnRetryClicked) },
105104
modifier = Modifier.fillMaxSize(),
106105
)
107106
},
108-
content = {
107+
content = { contentState ->
108+
@Suppress("ViewModelForwarding")
109109
ContentView(
110-
state = state,
110+
state = contentState,
111111
onEvent = onEvent,
112112
oAuthViewModel = oAuthViewModel,
113113
resources = resources,

feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryContract.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package app.k9mail.feature.account.setup.ui.autodiscovery
33
import app.k9mail.autodiscovery.api.AutoDiscoveryResult
44
import app.k9mail.core.common.domain.usecase.validation.ValidationResult
55
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
6+
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingErrorState
67
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
78
import app.k9mail.feature.account.common.domain.entity.IncomingProtocolType
89
import app.k9mail.feature.account.common.domain.input.BooleanInputField
910
import app.k9mail.feature.account.common.domain.input.StringInputField
10-
import app.k9mail.feature.account.common.ui.loadingerror.LoadingErrorState
1111
import app.k9mail.feature.account.oauth.domain.entity.OAuthResult
1212
import app.k9mail.feature.account.oauth.ui.AccountOAuthContract
1313

feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountContent.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorView
1111
import app.k9mail.core.ui.compose.designsystem.molecule.ErrorView
1212
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingView
1313
import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer
14-
import app.k9mail.feature.account.common.ui.loadingerror.rememberContentLoadingErrorViewState
1514
import app.k9mail.feature.account.setup.R
1615

1716
@Composable
@@ -27,7 +26,7 @@ internal fun CreateAccountContent(
2726
.then(modifier),
2827
) {
2928
ContentLoadingErrorView(
30-
state = rememberContentLoadingErrorViewState(state),
29+
state = state,
3130
loading = {
3231
LoadingView(
3332
message = stringResource(R.string.account_setup_create_account_creating),

feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountContract.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
package app.k9mail.feature.account.setup.ui.createaccount
22

33
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
4-
import app.k9mail.feature.account.common.ui.loadingerror.LoadingErrorState
4+
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingErrorState
55
import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult.Error
66
import app.k9mail.feature.account.setup.domain.entity.AccountUuid
77

0 commit comments

Comments
 (0)