Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add account edit save settings screen #7272

Closed
wants to merge 8 commits into from
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import app.k9mail.feature.account.edit.navigation.accountEditRoute
import app.k9mail.feature.account.edit.navigation.navigateToAccountEditConfigIncoming
import app.k9mail.feature.account.edit.navigation.navigateToAccountEditIncomingServerSettings
import app.k9mail.feature.account.setup.navigation.accountSetupRoute
import app.k9mail.feature.account.setup.navigation.navigateToAccountSetup
import app.k9mail.feature.onboarding.navigation.NAVIGATION_ROUTE_ONBOARDING
Expand All @@ -30,7 +30,7 @@ fun FeatureNavHost(
accountSetupRoute(
onBack = navController::popBackStack,
onFinish = { accountUuid ->
navController.navigateToAccountEditConfigIncoming(accountUuid)
navController.navigateToAccountEditIncomingServerSettings(accountUuid)
},
)
accountEditRoute(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,31 @@ fun LazyGridScope.stateItems() {
ItemOutlined {
ErrorView(
title = "Error",
)
}
}
item {
ItemOutlined {
ErrorView(
title = "Error with message",
message = "Something went wrong",
)
}
}
item {
ItemOutlined {
ErrorView(
title = "Error with retry",
onRetry = {},
)
}
}
item {
ItemOutlined {
ErrorView(
title = "Error with retry and message",
message = "Something went wrong",
onRetry = {},
)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
package com.fsck.k9.account

import app.k9mail.core.common.mail.Protocols
import app.k9mail.feature.account.edit.AccountEditExternalContract
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterFailure
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterResult
import com.fsck.k9.logging.Timber
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.store.imap.ImapStoreSettings
import com.fsck.k9.mail.store.imap.ImapStoreSettings.autoDetectNamespace
import com.fsck.k9.mail.store.imap.ImapStoreSettings.isSendClientId
import com.fsck.k9.mail.store.imap.ImapStoreSettings.isUseCompression
import com.fsck.k9.mail.store.imap.ImapStoreSettings.pathPrefix
import com.fsck.k9.preferences.AccountManager
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -42,7 +48,18 @@ class AccountServerSettingsUpdater(
)

if (isIncoming) {
account.incomingServerSettings = serverSettings
if (serverSettings.type == Protocols.IMAP) {
account.useCompression = serverSettings.isUseCompression
account.isSendClientIdEnabled = serverSettings.isSendClientId
account.incomingServerSettings = serverSettings.copy(
extra = ImapStoreSettings.createExtra(
autoDetectNamespace = serverSettings.autoDetectNamespace,
pathPrefix = serverSettings.pathPrefix,
),
)
} else {
account.incomingServerSettings = serverSettings
}
} else {
account.outgoingServerSettings = serverSettings
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.fsck.k9.account

import app.k9mail.core.common.mail.Protocols
import app.k9mail.feature.account.common.AccountCommonExternalContract
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import com.fsck.k9.Account
import com.fsck.k9.backends.toImapServerSettings
import com.fsck.k9.logging.Timber
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.preferences.AccountManager
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -36,9 +40,15 @@ class AccountStateLoader(
return AccountState(
uuid = account.uuid,
emailAddress = account.email,
incomingServerSettings = account.incomingServerSettings,
incomingServerSettings = account.incomingServerSettingsExtra,
outgoingServerSettings = account.outgoingServerSettings,
authorizationState = AuthorizationState(account.oAuthState),
)
}
}

private val Account.incomingServerSettingsExtra: ServerSettings
get() = when (incomingServerSettings.type) {
Protocols.IMAP -> toImapServerSettings()
else -> incomingServerSettings
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,9 @@ class AccountStateLoaderTest {

private companion object {
val INCOMING_SERVER_SETTINGS = ServerSettings(
type = "imap",
host = "imap.example.org",
port = 143,
type = "pop3",
host = "pop.example.org",
port = 465,
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "username",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,9 @@ class AccountUpdaterTest {
const val ACCOUNT_UUID = "uuid"

val INCOMING_SERVER_SETTINGS = ServerSettings(
type = "imap",
host = "imap.example.org",
port = 143,
type = "pop3",
host = "pop.example.org",
port = 465,
connectionSecurity = ConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "username",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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
Expand All @@ -14,7 +13,6 @@ 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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ 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.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.Icon
import androidx.compose.runtime.Composable
Expand All @@ -26,7 +28,7 @@ fun ErrorView(
title: String,
modifier: Modifier = Modifier,
message: String? = null,
onRetry: () -> Unit = { },
onRetry: (() -> Unit)? = null,
contentAlignment: Alignment = Alignment.Center,
) {
Box(
Expand All @@ -38,44 +40,45 @@ fun ErrorView(
Column(
modifier = Modifier
.fillMaxWidth()
.padding(
vertical = MainTheme.spacings.default,
horizontal = MainTheme.spacings.double,
),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
.padding(MainTheme.spacings.double),
) {
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),
)
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default),
) {
Icon(
imageVector = Icons.Filled.error,
contentDescription = null,
tint = MainTheme.colors.error,
)
TextSubtitle1(
text = title,
)
}

if (message != null) {
Spacer(modifier = Modifier.height(MainTheme.spacings.double))
TextBody2(
text = message,
modifier = Modifier
.fillMaxWidth(),
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,
),
)
if (onRetry != null) {
Spacer(modifier = Modifier.height(MainTheme.spacings.default))
Row(
modifier = Modifier.fillMaxWidth(),
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,
),
)
}
}
}
}
Expand All @@ -101,3 +104,26 @@ internal fun ErrorViewWithMessagePreview() {
)
}
}

@Preview(showBackground = true)
@Composable
internal fun ErrorViewWithRetryPreview() {
PreviewWithThemes {
ErrorView(
title = "Error",
onRetry = {},
)
}
}

@Preview(showBackground = true)
@Composable
internal fun ErrorViewWithRetryAndMessagePreview() {
PreviewWithThemes {
ErrorView(
title = "Error",
message = "Something went wrong.",
onRetry = {},
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ fun LoadingView(
Column(
modifier = Modifier
.fillMaxWidth()
.padding(MainTheme.spacings.default)
.then(modifier),
.padding(MainTheme.spacings.double),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,36 @@ fun <T, STATE, EFFECT> assertThatAndMviTurbinesConsumed(
turbines.effectTurbine.ensureAllEventsConsumed()
}

/**
* The `assertThatAndStateTurbineConsumed` function ensures that the assertion passed and
* all events in the state turbine have been consumed.
*/
suspend fun <STATE, EFFECT> MviTurbines<STATE, EFFECT>.assertThatAndStateTurbineConsumed(
assertion: Assert<STATE>.() -> Unit,
) {
assertThat(stateTurbine.awaitItem()).all {
assertion()
}

stateTurbine.ensureAllEventsConsumed()
effectTurbine.ensureAllEventsConsumed()
}

/**
* The `assertThatAndEffectTurbineConsumed` function ensures that the assertion passed and
* all events in the effect turbine have been consumed.
*/
suspend fun <STATE, EFFECT> MviTurbines<STATE, EFFECT>.assertThatAndEffectTurbineConsumed(
assertion: Assert<EFFECT>.() -> Unit,
) {
assertThat(effectTurbine.awaitItem()).all {
assertion()
}

stateTurbine.ensureAllEventsConsumed()
effectTurbine.ensureAllEventsConsumed()
}

/**
* A container class for the state and effect turbines of an MVI ViewModel.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import app.k9mail.core.ui.compose.theme.MainTheme
import app.k9mail.core.ui.compose.theme.PreviewWithThemes

@Composable
internal fun SuccessView(
fun SuccessView(
message: String,
modifier: Modifier = Modifier,
) {
Expand Down
1 change: 1 addition & 0 deletions feature/account/edit/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ dependencies {
implementation(projects.feature.account.server.validation)

testImplementation(projects.core.ui.compose.testing)
testImplementation(projects.mail.protocols.imap)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import com.fsck.k9.mail.ServerSettings
interface AccountEditExternalContract {

sealed interface AccountUpdaterResult {
data class Success(val message: String) : AccountUpdaterResult
data class Success(val accountUuid: String) : AccountUpdaterResult
data class Failure(val error: AccountUpdaterFailure) : AccountUpdaterResult
}

Expand Down
Loading