Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit a226a3b1 authored by cketti's avatar cketti Committed by GitHub
Browse files

Merge pull request #8510 from cketti/fix-SpecialFoldersScreen-crash

Fix `SpecialFoldersScreen` crash
parents 7bc83eb8 a36e7ef2
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -130,7 +130,7 @@ private fun StatefulContentLoadingView() {
@Composable
private fun StatefulContentLoadingErrorView() {
    val state = remember {
        mutableStateOf(ContentLoadingErrorState.Loading)
        mutableStateOf<ContentLoadingErrorState>(ContentLoadingErrorState.Loading)
    }

    ContentLoadingErrorView(
+1 −1
Original line number Diff line number Diff line
@@ -45,7 +45,7 @@ internal fun ContentLoadingErrorViewErrorPreview() {
internal fun ContentLoadingErrorViewInteractivePreview() {
    PreviewWithThemes {
        val state = remember {
            mutableStateOf(ContentLoadingErrorState.Loading)
            mutableStateOf<ContentLoadingErrorState>(ContentLoadingErrorState.Loading)
        }

        DefaultContentLoadingErrorView(
+56 −12
Original line number Diff line number Diff line
@@ -6,14 +6,34 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier

/**
 * A container view that can animate between a loading view, an error view, and a content view.
 *
 * @param ERROR The type describing the error.
 * @param STATE The type of the state being passed to this view.
 *
 * @param state The state relevant for displaying the content inside this view.
 * @param loading When `state.isLoading` is `true`, this composable function is displayed.
 * @param error When `state.isLoading` is `false` and `state.error` is not `null`, this composable function is displayed
 *   with `state.error` being passed as the argument.
 * @param content When `state.isLoading` is `false` and `state.error` is `null`, this composable function is displayed
 *   with [state] being passed as the argument.
 *
 * **IMPORTANT**: This is a delicate API whose usage should be carefully reviewed. It is using [AnimatedContent] and
 * inherits its caveats.
 *
 * The [loading], [error] and [content] composable functions should only use the state being passed to them (if any).
 * If you disregard this advice, make sure to read the documentation of [AnimatedContent] to learn when the composable
 * functions are invoked and what that means for the external state a function fetches.
 */
@Composable
fun ContentLoadingErrorView(
    state: ContentLoadingErrorState,
fun <ERROR, STATE : LoadingErrorState<ERROR>> ContentLoadingErrorView(
    state: STATE,
    loading: @Composable () -> Unit,
    error: @Composable () -> Unit,
    error: @Composable (ERROR) -> Unit,
    modifier: Modifier = Modifier,
    contentAlignment: Alignment = Alignment.Center,
    content: @Composable () -> Unit,
    content: @Composable (STATE) -> Unit,
) {
    Box(
        modifier = modifier,
@@ -22,18 +42,42 @@ fun ContentLoadingErrorView(
        AnimatedContent(
            targetState = state,
            label = "ContentLoadingErrorView",
            contentKey = { targetState ->
                ContentKey(isLoading = targetState.isLoading, error = targetState.error)
            },
        ) { targetState ->
            when (targetState) {
                ContentLoadingErrorState.Loading -> loading()
                ContentLoadingErrorState.Content -> content()
                ContentLoadingErrorState.Error -> error()
            val errorValue = targetState.error
            when {
                targetState.isLoading -> loading()
                errorValue != null -> error(errorValue)
                else -> content(targetState)
            }
        }
    }
}

enum class ContentLoadingErrorState {
    Loading,
    Content,
    Error,
/**
 * Signals [ContentLoadingErrorView] which of its composable function parameters to execute/display.
 */
interface LoadingErrorState<ERROR> {
    val isLoading: Boolean
    val error: ERROR?
}

private data class ContentKey<ERROR>(
    override val isLoading: Boolean,
    override val error: ERROR?,
) : LoadingErrorState<ERROR>

/**
 * Helper that can be use as `state` argument for [ContentLoadingErrorView] when none of the composable function
 * parameters need access to any state.
 */
sealed class ContentLoadingErrorState private constructor(
    override val isLoading: Boolean,
    override val error: Unit?,
) : LoadingErrorState<Unit> {
    data object Loading : ContentLoadingErrorState(isLoading = true, error = null)
    data object Content : ContentLoadingErrorState(isLoading = false, error = null)
    data object Error : ContentLoadingErrorState(isLoading = false, error = Unit)
}
+0 −21
Original line number Diff line number Diff line
package app.k9mail.feature.account.common.ui.loadingerror

import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import app.k9mail.core.ui.compose.designsystem.molecule.ContentLoadingErrorState

interface LoadingErrorState<ERROR> {
    val isLoading: Boolean
    val error: ERROR?
}

@Composable
fun <ERROR> rememberContentLoadingErrorViewState(
    state: LoadingErrorState<ERROR>,
) = remember(key1 = state.isLoading, key2 = state.error) {
    when {
        state.isLoading -> ContentLoadingErrorState.Loading
        state.error != null -> ContentLoadingErrorState.Error
        else -> ContentLoadingErrorState.Content
    }
}
+1 −2
Original line number Diff line number Diff line
@@ -11,7 +11,6 @@ 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.feature.account.common.ui.loadingerror.rememberContentLoadingErrorViewState
import app.k9mail.feature.account.edit.R

@Composable
@@ -27,7 +26,7 @@ fun SaveServerSettingsContent(
            .then(modifier),
    ) {
        ContentLoadingErrorView(
            state = rememberContentLoadingErrorViewState(state),
            state = state,
            loading = {
                LoadingView(
                    message = stringResource(id = R.string.account_edit_save_server_settings_loading_message),
Loading