Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 13:56:56 +01:00
parent 75dc487a7a
commit 39c29d175b
6317 changed files with 388324 additions and 2 deletions

View file

@ -0,0 +1,25 @@
plugins {
id(ThunderbirdPlugins.Library.androidCompose)
}
android {
namespace = "app.k9mail.feature.account.edit"
resourcePrefix = "account_edit_"
}
dependencies {
implementation(projects.core.common)
implementation(projects.core.ui.compose.designsystem)
implementation(projects.core.ui.compose.navigation)
implementation(projects.mail.common)
implementation(projects.feature.account.common)
implementation(projects.feature.account.oauth)
implementation(projects.feature.account.server.settings)
implementation(projects.feature.account.server.certificate)
implementation(projects.feature.account.server.validation)
testImplementation(projects.core.ui.compose.testing)
testImplementation(projects.mail.protocols.imap)
}

View file

@ -0,0 +1,51 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.designsystem.PreviewWithThemes
@Composable
@Preview(showBackground = true)
internal fun SaveServerSettingsContentPreview() {
PreviewWithThemes {
SaveServerSettingsContent(
state = SaveServerSettingsContract.State(
isLoading = false,
error = null,
),
contentPadding = PaddingValues(),
)
}
}
@Composable
@Preview(showBackground = true)
internal fun SaveServerSettingsContentLoadingPreview() {
PreviewWithThemes {
SaveServerSettingsContent(
state = SaveServerSettingsContract.State(
isLoading = true,
error = null,
),
contentPadding = PaddingValues(),
)
}
}
@Composable
@Preview(showBackground = true)
internal fun SaveServerSettingsContentErrorPreview() {
PreviewWithThemes {
SaveServerSettingsContent(
state = SaveServerSettingsContract.State(
isLoading = false,
error = SaveServerSettingsContract.Failure.SaveServerSettingsFailed(
message = "Error",
),
),
contentPadding = PaddingValues(),
)
}
}

View file

@ -0,0 +1,21 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import androidx.compose.runtime.Composable
import androidx.compose.ui.tooling.preview.Preview
import app.k9mail.core.ui.compose.designsystem.PreviewWithTheme
import app.k9mail.feature.account.edit.ui.server.settings.save.fake.FakeSaveServerSettingsViewModel
@Composable
@Preview(showBackground = true)
internal fun SaveServerSettingsScreenK9Preview() {
PreviewWithTheme {
SaveServerSettingsScreen(
title = "Incoming server settings",
onNext = {},
onBack = {},
viewModel = FakeSaveServerSettingsViewModel(
isIncoming = true,
),
)
}
}

View file

@ -0,0 +1,23 @@
package app.k9mail.feature.account.edit.ui.server.settings.save.fake
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Effect
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Event
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.State
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.ViewModel
class FakeSaveServerSettingsViewModel(
override val isIncoming: Boolean,
initialState: State = State(),
) : BaseViewModel<State, Event, Effect>(initialState), ViewModel {
val events = mutableListOf<Event>()
override fun event(event: Event) {
events.add(event)
}
fun effect(effect: Effect) {
emitEffect(effect)
}
}

View file

@ -0,0 +1,26 @@
package app.k9mail.feature.account.edit
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import com.fsck.k9.mail.ServerSettings
interface AccountEditExternalContract {
sealed interface AccountUpdaterResult {
data class Success(val accountUuid: String) : AccountUpdaterResult
data class Failure(val error: AccountUpdaterFailure) : AccountUpdaterResult
}
sealed interface AccountUpdaterFailure {
data class AccountNotFound(val accountUuid: String) : AccountUpdaterFailure
data class UnknownError(val error: Exception) : AccountUpdaterFailure
}
fun interface AccountServerSettingsUpdater {
suspend fun updateServerSettings(
accountUuid: String,
isIncoming: Boolean,
serverSettings: ServerSettings,
authorizationState: AuthorizationState?,
): AccountUpdaterResult
}
}

View file

@ -0,0 +1,83 @@
package app.k9mail.feature.account.edit
import app.k9mail.feature.account.common.featureAccountCommonModule
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
import app.k9mail.feature.account.edit.domain.usecase.GetAccountState
import app.k9mail.feature.account.edit.domain.usecase.LoadAccountState
import app.k9mail.feature.account.edit.domain.usecase.SaveServerSettings
import app.k9mail.feature.account.edit.navigation.AccountEditNavigation
import app.k9mail.feature.account.edit.navigation.DefaultAccountEditNavigation
import app.k9mail.feature.account.edit.ui.server.settings.modify.ModifyIncomingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.modify.ModifyOutgoingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveIncomingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveOutgoingServerSettingsViewModel
import app.k9mail.feature.account.oauth.featureAccountOAuthModule
import app.k9mail.feature.account.server.certificate.featureAccountServerCertificateModule
import app.k9mail.feature.account.server.settings.featureAccountServerSettingsModule
import app.k9mail.feature.account.server.validation.featureAccountServerValidationModule
import org.koin.core.module.dsl.viewModel
import org.koin.dsl.module
val featureAccountEditModule = module {
includes(
featureAccountCommonModule,
featureAccountOAuthModule,
featureAccountServerCertificateModule,
featureAccountServerSettingsModule,
featureAccountServerValidationModule,
)
single<AccountEditNavigation> { DefaultAccountEditNavigation() }
factory<AccountEditDomainContract.UseCase.LoadAccountState> {
LoadAccountState(
accountStateLoader = get(),
accountStateRepository = get(),
)
}
factory<AccountEditDomainContract.UseCase.GetAccountState> {
GetAccountState(
accountStateRepository = get(),
)
}
factory<AccountEditDomainContract.UseCase.SaveServerSettings> {
SaveServerSettings(
getAccountState = get(),
serverSettingsUpdater = get(),
)
}
viewModel { (accountUuid: String) ->
ModifyIncomingServerSettingsViewModel(
accountUuid = accountUuid,
accountStateLoader = get(),
validator = get(),
accountStateRepository = get(),
)
}
viewModel { (accountUuid: String) ->
ModifyOutgoingServerSettingsViewModel(
accountUuid = accountUuid,
accountStateLoader = get(),
validator = get(),
accountStateRepository = get(),
)
}
viewModel { (accountUuid: String) ->
SaveIncomingServerSettingsViewModel(
accountUuid = accountUuid,
saveServerSettings = get(),
)
}
viewModel { (accountUuid: String) ->
SaveOutgoingServerSettingsViewModel(
accountUuid = accountUuid,
saveServerSettings = get(),
)
}
}

View file

@ -0,0 +1,21 @@
package app.k9mail.feature.account.edit.domain
import app.k9mail.feature.account.common.domain.entity.AccountState
interface AccountEditDomainContract {
interface UseCase {
fun interface LoadAccountState {
suspend fun execute(accountUuid: String): AccountState
}
fun interface GetAccountState {
suspend fun execute(accountUuid: String): AccountState
}
fun interface SaveServerSettings {
suspend fun execute(accountUuid: String, isIncoming: Boolean)
}
}
}

View file

@ -0,0 +1,18 @@
package app.k9mail.feature.account.edit.domain.usecase
import app.k9mail.feature.account.common.domain.AccountDomainContract
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract.UseCase
class GetAccountState(
private val accountStateRepository: AccountDomainContract.AccountStateRepository,
) : UseCase.GetAccountState {
override suspend fun execute(accountUuid: String): AccountState {
val accountState = accountStateRepository.getState()
return if (accountState.uuid == accountUuid) {
accountState
} else {
error("Account state for $accountUuid not found")
}
}
}

View file

@ -0,0 +1,23 @@
package app.k9mail.feature.account.edit.domain.usecase
import app.k9mail.feature.account.common.AccountCommonExternalContract
import app.k9mail.feature.account.common.domain.AccountDomainContract
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
class LoadAccountState(
private val accountStateLoader: AccountCommonExternalContract.AccountStateLoader,
private val accountStateRepository: AccountDomainContract.AccountStateRepository,
) : AccountEditDomainContract.UseCase.LoadAccountState {
override suspend fun execute(accountUuid: String): AccountState {
val accountState = accountStateLoader.loadAccountState(accountUuid)
if (accountState != null) {
accountStateRepository.setState(accountState)
} else {
error("Account state for $accountUuid not found")
}
return accountState
}
}

View file

@ -0,0 +1,48 @@
package app.k9mail.feature.account.edit.domain.usecase
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountServerSettingsUpdater
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterResult
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract.UseCase
import com.fsck.k9.mail.ServerSettings
class SaveServerSettings(
private val getAccountState: UseCase.GetAccountState,
private val serverSettingsUpdater: AccountServerSettingsUpdater,
) : UseCase.SaveServerSettings {
override suspend fun execute(accountUuid: String, isIncoming: Boolean) {
val accountState = getAccountState.execute(accountUuid)
val serverSettings = accountState.getServerSettings(isIncoming)
val authorizationState = accountState.authorizationState
if (serverSettings != null) {
updateServerSettings(accountUuid, isIncoming, serverSettings, authorizationState)
} else {
error("Server settings not found")
}
}
private suspend fun updateServerSettings(
accountUuid: String,
isIncoming: Boolean,
serverSettings: ServerSettings,
authorizationState: AuthorizationState?,
) {
val result = serverSettingsUpdater.updateServerSettings(
accountUuid = accountUuid,
isIncoming = isIncoming,
serverSettings = serverSettings,
authorizationState = authorizationState,
)
if (result is AccountUpdaterResult.Failure) {
error("Server settings update failed")
}
}
private fun AccountState.getServerSettings(isIncoming: Boolean): ServerSettings? {
return if (isIncoming) incomingServerSettings else outgoingServerSettings
}
}

View file

@ -0,0 +1,5 @@
package app.k9mail.feature.account.edit.navigation
import app.k9mail.core.ui.compose.navigation.Navigation
interface AccountEditNavigation : Navigation<AccountEditRoute>

View file

@ -0,0 +1,35 @@
package app.k9mail.feature.account.edit.navigation
import app.k9mail.core.ui.compose.navigation.Route
import kotlinx.serialization.Serializable
sealed interface AccountEditRoute : Route {
@Serializable
data class IncomingServerSettings(
val accountId: String,
) : AccountEditRoute {
override val basePath: String = BASE_PATH
override fun route(): String = "$basePath/$accountId"
companion object {
const val BASE_PATH = "$ACCOUNT_EDIT_BASE_PATH/incoming"
}
}
@Serializable
data class OutgoingServerSettings(val accountId: String) : AccountEditRoute {
override val basePath: String = BASE_PATH
override fun route(): String = "$basePath/$accountId"
companion object {
const val BASE_PATH = "$ACCOUNT_EDIT_BASE_PATH/outgoing"
}
}
companion object {
const val ACCOUNT_EDIT_BASE_PATH = "app://account/edit"
}
}

View file

@ -0,0 +1,42 @@
package app.k9mail.feature.account.edit.navigation
import androidx.navigation.NavGraphBuilder
import androidx.navigation.toRoute
import app.k9mail.core.ui.compose.navigation.deepLinkComposable
import app.k9mail.feature.account.edit.navigation.AccountEditRoute.IncomingServerSettings
import app.k9mail.feature.account.edit.navigation.AccountEditRoute.OutgoingServerSettings
import app.k9mail.feature.account.edit.ui.server.settings.EditIncomingServerSettingsNavHost
import app.k9mail.feature.account.edit.ui.server.settings.EditOutgoingServerSettingsNavHost
class DefaultAccountEditNavigation : AccountEditNavigation {
override fun registerRoutes(
navGraphBuilder: NavGraphBuilder,
onBack: () -> Unit,
onFinish: (AccountEditRoute) -> Unit,
) = with(navGraphBuilder) {
deepLinkComposable<IncomingServerSettings>(
basePath = IncomingServerSettings.BASE_PATH,
) { backStackEntry ->
val incomingServerSettingsRoute = backStackEntry.toRoute<IncomingServerSettings>()
EditIncomingServerSettingsNavHost(
accountUuid = incomingServerSettingsRoute.accountId,
onBack = onBack,
onFinish = onFinish,
)
}
deepLinkComposable<OutgoingServerSettings>(
basePath = OutgoingServerSettings.BASE_PATH,
) { backStackEntry ->
val outgoingServerSettingsRoute = backStackEntry.toRoute<OutgoingServerSettings>()
EditOutgoingServerSettingsNavHost(
accountUuid = outgoingServerSettingsRoute.accountId,
onBack = onBack,
onFinish = onFinish,
)
}
}
}

View file

@ -0,0 +1,76 @@
package app.k9mail.feature.account.edit.ui.server.settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import app.k9mail.feature.account.edit.navigation.AccountEditRoute
import app.k9mail.feature.account.edit.ui.server.settings.modify.ModifyIncomingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveIncomingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsScreen
import app.k9mail.feature.account.server.settings.R
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsScreen
import app.k9mail.feature.account.server.validation.ui.IncomingServerValidationViewModel
import app.k9mail.feature.account.server.validation.ui.ServerValidationScreen
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
import org.koin.core.parameter.parametersOf
private const val NESTED_NAVIGATION_ROUTE_MODIFY = "modify"
private const val NESTED_NAVIGATION_ROUTE_VALIDATE = "validate"
private const val NESTED_NAVIGATION_ROUTE_SAVE = "save"
private fun NavController.navigateToValidate() {
navigate(NESTED_NAVIGATION_ROUTE_VALIDATE)
}
private fun NavController.navigateToSave() {
navigate(NESTED_NAVIGATION_ROUTE_SAVE)
}
@Composable
fun EditIncomingServerSettingsNavHost(
accountUuid: String,
onFinish: (AccountEditRoute) -> Unit,
onBack: () -> Unit,
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = NESTED_NAVIGATION_ROUTE_MODIFY,
) {
composable(route = NESTED_NAVIGATION_ROUTE_MODIFY) {
IncomingServerSettingsScreen(
onBack = onBack,
onNext = { navController.navigateToValidate() },
viewModel = koinViewModel<ModifyIncomingServerSettingsViewModel> {
parametersOf(accountUuid)
},
)
}
composable(route = NESTED_NAVIGATION_ROUTE_VALIDATE) {
ServerValidationScreen(
title = stringResource(id = R.string.account_server_settings_incoming_top_bar_title),
onBack = { navController.popBackStack() },
onNext = { navController.navigateToSave() },
viewModel = koinViewModel<IncomingServerValidationViewModel> {
parametersOf(accountUuid)
},
brandNameProvider = koinInject(),
)
}
composable(route = NESTED_NAVIGATION_ROUTE_SAVE) {
SaveServerSettingsScreen(
title = stringResource(id = R.string.account_server_settings_incoming_top_bar_title),
onNext = { onFinish(AccountEditRoute.IncomingServerSettings(accountUuid)) },
onBack = { navController.popBackStack(route = NESTED_NAVIGATION_ROUTE_MODIFY, inclusive = false) },
viewModel = koinViewModel<SaveIncomingServerSettingsViewModel> {
parametersOf(accountUuid)
},
)
}
}
}

View file

@ -0,0 +1,76 @@
package app.k9mail.feature.account.edit.ui.server.settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import androidx.navigation.NavController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import app.k9mail.feature.account.edit.navigation.AccountEditRoute
import app.k9mail.feature.account.edit.ui.server.settings.modify.ModifyOutgoingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveOutgoingServerSettingsViewModel
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsScreen
import app.k9mail.feature.account.server.settings.R
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsScreen
import app.k9mail.feature.account.server.validation.ui.OutgoingServerValidationViewModel
import app.k9mail.feature.account.server.validation.ui.ServerValidationScreen
import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
import org.koin.core.parameter.parametersOf
private const val NESTED_NAVIGATION_ROUTE_MODIFY = "modify"
private const val NESTED_NAVIGATION_ROUTE_VALIDATE = "validate"
private const val NESTED_NAVIGATION_ROUTE_SAVE = "save"
private fun NavController.navigateToValidate() {
navigate(NESTED_NAVIGATION_ROUTE_VALIDATE)
}
private fun NavController.navigateToSave() {
navigate(NESTED_NAVIGATION_ROUTE_SAVE)
}
@Composable
fun EditOutgoingServerSettingsNavHost(
accountUuid: String,
onFinish: (AccountEditRoute) -> Unit,
onBack: () -> Unit,
) {
val navController = rememberNavController()
NavHost(
navController = navController,
startDestination = NESTED_NAVIGATION_ROUTE_MODIFY,
) {
composable(route = NESTED_NAVIGATION_ROUTE_MODIFY) {
OutgoingServerSettingsScreen(
onBack = onBack,
onNext = { navController.navigateToValidate() },
viewModel = koinViewModel<ModifyOutgoingServerSettingsViewModel> {
parametersOf(accountUuid)
},
)
}
composable(route = NESTED_NAVIGATION_ROUTE_VALIDATE) {
ServerValidationScreen(
title = stringResource(id = R.string.account_server_settings_outgoing_top_bar_title),
onBack = { navController.popBackStack() },
onNext = { navController.navigateToSave() },
viewModel = koinViewModel<OutgoingServerValidationViewModel> {
parametersOf(accountUuid)
},
brandNameProvider = koinInject(),
)
}
composable(route = NESTED_NAVIGATION_ROUTE_SAVE) {
SaveServerSettingsScreen(
title = stringResource(id = R.string.account_server_settings_outgoing_top_bar_title),
onNext = { onFinish(AccountEditRoute.OutgoingServerSettings(accountUuid)) },
onBack = { navController.popBackStack(route = NESTED_NAVIGATION_ROUTE_MODIFY, inclusive = false) },
viewModel = koinViewModel<SaveOutgoingServerSettingsViewModel> {
parametersOf(accountUuid)
},
)
}
}
}

View file

@ -0,0 +1,34 @@
package app.k9mail.feature.account.edit.ui.server.settings.modify
import androidx.lifecycle.viewModelScope
import app.k9mail.feature.account.common.domain.AccountDomainContract
import app.k9mail.feature.account.common.domain.entity.InteractionMode
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsViewModel
import app.k9mail.feature.account.server.settings.ui.incoming.toIncomingServerSettingsState
import kotlinx.coroutines.launch
class ModifyIncomingServerSettingsViewModel(
val accountUuid: String,
private val accountStateLoader: AccountEditDomainContract.UseCase.LoadAccountState,
validator: IncomingServerSettingsContract.Validator,
accountStateRepository: AccountDomainContract.AccountStateRepository,
initialState: IncomingServerSettingsContract.State = IncomingServerSettingsContract.State(),
) : IncomingServerSettingsViewModel(
mode = InteractionMode.Edit,
validator = validator,
accountStateRepository = accountStateRepository,
initialState = initialState,
) {
override fun loadAccountState() {
viewModelScope.launch {
val state = accountStateLoader.execute(accountUuid)
updateState {
state.toIncomingServerSettingsState()
}
}
}
}

View file

@ -0,0 +1,33 @@
package app.k9mail.feature.account.edit.ui.server.settings.modify
import androidx.lifecycle.viewModelScope
import app.k9mail.feature.account.common.domain.AccountDomainContract
import app.k9mail.feature.account.common.domain.entity.InteractionMode
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsViewModel
import app.k9mail.feature.account.server.settings.ui.outgoing.toOutgoingServerSettingsState
import kotlinx.coroutines.launch
class ModifyOutgoingServerSettingsViewModel(
val accountUuid: String,
private val accountStateLoader: AccountEditDomainContract.UseCase.LoadAccountState,
validator: OutgoingServerSettingsContract.Validator,
accountStateRepository: AccountDomainContract.AccountStateRepository,
initialState: OutgoingServerSettingsContract.State = OutgoingServerSettingsContract.State(),
) : OutgoingServerSettingsViewModel(
mode = InteractionMode.Edit,
validator = validator,
accountStateRepository = accountStateRepository,
initialState = initialState,
) {
override fun loadAccountState() {
viewModelScope.launch {
val state = accountStateLoader.execute(accountUuid)
updateState {
state.toOutgoingServerSettingsState()
}
}
}
}

View file

@ -0,0 +1,76 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import androidx.lifecycle.viewModelScope
import app.k9mail.core.ui.compose.common.mvi.BaseViewModel
import app.k9mail.feature.account.common.ui.WizardConstants
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Effect
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Event
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Failure
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.State
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.ViewModel
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
abstract class BaseSaveServerSettingsViewModel(
val accountUuid: String,
override val isIncoming: Boolean,
private val saveServerSettings: AccountEditDomainContract.UseCase.SaveServerSettings,
initialState: State = State(),
) : BaseViewModel<State, Event, Effect>(initialState),
ViewModel {
override fun event(event: Event) {
when (event) {
Event.SaveServerSettings -> handleOneTimeEvent(event, ::onSaveServerSettings)
Event.OnBackClicked -> navigateBack()
}
}
@Suppress("TooGenericExceptionCaught")
private fun onSaveServerSettings() {
viewModelScope.launch {
try {
saveServerSettings.execute(accountUuid, isIncoming)
updateSuccess()
} catch (e: Exception) {
updateFailure(Failure.SaveServerSettingsFailed(e.message ?: "Unknown error"))
}
}
}
private fun updateSuccess() {
updateState {
it.copy(
isLoading = false,
)
}
viewModelScope.launch {
delay(WizardConstants.CONTINUE_NEXT_DELAY)
navigateNext()
}
}
private fun updateFailure(failure: Failure) {
updateState {
it.copy(
error = failure,
isLoading = false,
)
}
}
private fun navigateNext() {
viewModelScope.coroutineContext.cancelChildren()
emitEffect(Effect.NavigateNext)
}
private fun navigateBack() {
if (state.value.isLoading || state.value.error == null) return
viewModelScope.coroutineContext.cancelChildren()
emitEffect(Effect.NavigateBack)
}
}

View file

@ -0,0 +1,15 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract.UseCase
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.State
class SaveIncomingServerSettingsViewModel(
accountUuid: String,
saveServerSettings: UseCase.SaveServerSettings,
initialState: State = State(),
) : BaseSaveServerSettingsViewModel(
accountUuid = accountUuid,
isIncoming = true,
saveServerSettings = saveServerSettings,
initialState = initialState,
)

View file

@ -0,0 +1,15 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import app.k9mail.feature.account.edit.domain.AccountEditDomainContract.UseCase
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.State
class SaveOutgoingServerSettingsViewModel(
accountUuid: String,
saveServerSettings: UseCase.SaveServerSettings,
initialState: State = State(),
) : BaseSaveServerSettingsViewModel(
accountUuid = accountUuid,
isIncoming = false,
saveServerSettings = saveServerSettings,
initialState = initialState,
)

View file

@ -0,0 +1,48 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
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.edit.R
import net.thunderbird.core.ui.compose.common.modifier.testTagAsResourceId
@Composable
fun SaveServerSettingsContent(
state: SaveServerSettingsContract.State,
contentPadding: PaddingValues,
modifier: Modifier = Modifier,
) {
ResponsiveWidthContainer(
modifier = Modifier
.testTagAsResourceId("SaveServerSettingsContent")
.padding(contentPadding)
.then(modifier),
) { contentPadding ->
ContentLoadingErrorView(
state = state,
loading = {
LoadingView(
message = stringResource(id = R.string.account_edit_save_server_settings_loading_message),
)
},
error = {
ErrorView(
title = stringResource(id = R.string.account_edit_save_server_settings_error_message),
)
},
content = {
LoadingView(
message = stringResource(id = R.string.account_edit_save_server_settings_success_message),
)
},
modifier = Modifier.fillMaxSize().padding(contentPadding),
)
}
}

View file

@ -0,0 +1,32 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel
import app.k9mail.core.ui.compose.designsystem.molecule.LoadingErrorState
interface SaveServerSettingsContract {
interface ViewModel : UnidirectionalViewModel<State, Event, Effect> {
val isIncoming: Boolean
}
data class State(
override val error: Failure? = null,
override val isLoading: Boolean = true,
) : LoadingErrorState<Failure>
sealed interface Event {
data object SaveServerSettings : Event
data object OnBackClicked : Event
}
sealed interface Effect {
data object NavigateNext : Effect
data object NavigateBack : Effect
}
sealed interface Failure {
data class SaveServerSettingsFailed(
val message: String,
) : Failure
}
}

View file

@ -0,0 +1,67 @@
package app.k9mail.feature.account.edit.ui.server.settings.save
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import app.k9mail.core.ui.compose.common.mvi.observe
import app.k9mail.core.ui.compose.designsystem.organism.TopAppBarWithBackButton
import app.k9mail.core.ui.compose.designsystem.template.Scaffold
import app.k9mail.feature.account.common.ui.WizardNavigationBar
import app.k9mail.feature.account.common.ui.WizardNavigationBarState
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Effect
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.Event
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract.ViewModel
@Composable
fun SaveServerSettingsScreen(
title: String,
onNext: () -> Unit,
onBack: () -> Unit,
viewModel: ViewModel,
modifier: Modifier = Modifier,
) {
val (state, dispatch) = viewModel.observe { effect ->
when (effect) {
is Effect.NavigateNext -> onNext()
Effect.NavigateBack -> onBack()
}
}
LaunchedEffect(key1 = Unit) {
dispatch(Event.SaveServerSettings)
}
BackHandler {
dispatch(Event.OnBackClicked)
}
Scaffold(
topBar = {
TopAppBarWithBackButton(
title = title,
onBackClick = {
dispatch(Event.OnBackClicked)
},
)
},
bottomBar = {
WizardNavigationBar(
onNextClick = {},
onBackClick = {
dispatch(Event.OnBackClicked)
},
state = WizardNavigationBarState(
showNext = false,
isBackEnabled = state.value.error != null,
),
)
},
modifier = modifier,
) { innerPadding ->
SaveServerSettingsContent(
state = state.value,
contentPadding = innerPadding,
)
}
}

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">يتم الآن حفظ إعدادات الخادم…</string>
<string name="account_edit_save_server_settings_error_message">تعذر حفظ إعدادات الخادم</string>
<string name="account_edit_save_server_settings_success_message">تم حفظ إعدادات الخادم</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Захаванне налад сервера…</string>
<string name="account_edit_save_server_settings_error_message">Не ўдалося захаваць налады сервера</string>
<string name="account_edit_save_server_settings_success_message">Налады сервера захаваныя</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Запазване настройките на сървъра…</string>
<string name="account_edit_save_server_settings_error_message">Неуспешно запазване на сървърните настройки</string>
<string name="account_edit_save_server_settings_success_message">Сървърните настройки запазени</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">সার্ভার পছন্দসমূহ সংরক্ষণ ব্যর্থ হয়েছে</string>
<string name="account_edit_save_server_settings_success_message">সার্ভার পছন্দসমূহ সংরক্ষিত</string>
<string name="account_edit_save_server_settings_loading_message">সার্ভার পছন্দসমূহ সংরক্ষণ হচ্ছে…</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Oc\'h enrollañ arventennoù an dafariad…</string>
<string name="account_edit_save_server_settings_error_message">Fazi oc\'h enrollañ arventennoù an dafariad</string>
<string name="account_edit_save_server_settings_success_message">Enrollet eo bet arventennoù an dafariad</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">S\'està desant la configuració del servidor…</string>
<string name="account_edit_save_server_settings_error_message">No s\'ha pogut desar la configuració del servidor</string>
<string name="account_edit_save_server_settings_success_message">S\'ha desat la configuració del servidor</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Arregistramentu di i parametri di u servitore…</string>
<string name="account_edit_save_server_settings_error_message">Fiascu à larregistramentu di i parametri di u servitore</string>
<string name="account_edit_save_server_settings_success_message">Parametri di u servitore arregistrati</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">Nepodařilo se uložit nastavení serveru</string>
<string name="account_edit_save_server_settings_loading_message">Ukládání nastavení serveru…</string>
<string name="account_edit_save_server_settings_success_message">Nastavení serveru uložena</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Yn cadw gosodiadau gweinydd…</string>
<string name="account_edit_save_server_settings_error_message">Wedi methu cadw gosodiadau gweinydd</string>
<string name="account_edit_save_server_settings_success_message">Gosodiadau gweinydd wedi\'u cadw</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Servereinstellungen werden gespeichert…</string>
<string name="account_edit_save_server_settings_success_message">Servereinstellungen gespeichert</string>
<string name="account_edit_save_server_settings_error_message">Servereinstellungen konnten nicht gespeichert werden</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Αποθήκευση ρυθμίσεων διακομιστή…</string>
<string name="account_edit_save_server_settings_error_message">Αποτυχία αποθήκευσης ρυθμίσεων διακομιστή</string>
<string name="account_edit_save_server_settings_success_message">Οι ρυθμίσεις διακομιστή αποθηκεύτηκαν</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Saving server settings…</string>
<string name="account_edit_save_server_settings_error_message">Failed to save server settings</string>
<string name="account_edit_save_server_settings_success_message">Server settings saved</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">Konservado de la servilaj agordoj fiaskis</string>
<string name="account_edit_save_server_settings_success_message">Servilaj agordoj ekkonservitaj</string>
<string name="account_edit_save_server_settings_loading_message">Konservante servilajn agordojn…</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Guardando los ajustes del servidor…</string>
<string name="account_edit_save_server_settings_error_message">Error al guardar la configuración del servidor</string>
<string name="account_edit_save_server_settings_success_message">Configuración del servidor guardada</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">Serveri seadistuste salvestamine ei õnnestunud</string>
<string name="account_edit_save_server_settings_loading_message">Salvestame serveri seadistusi…</string>
<string name="account_edit_save_server_settings_success_message">Serveri seadistused on salvestatud</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Zerbitzariaren ezarpenak gordetzen…</string>
<string name="account_edit_save_server_settings_error_message">Akatsa zerbitzariaren ezarpenak gordetzean</string>
<string name="account_edit_save_server_settings_success_message">Zerbitzariaren ezarpenak gordeta</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">ذخیره کردن تنظیمات کارساز…</string>
<string name="account_edit_save_server_settings_error_message">شکست در ذخیرهٔ تنظیمات کارساز</string>
<string name="account_edit_save_server_settings_success_message">تنظیمات کارساز ذخیره شد</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Tallennetaan palvelimen asetuksia…</string>
<string name="account_edit_save_server_settings_error_message">Palvelimen asetusten tallentaminen epäonnistui</string>
<string name="account_edit_save_server_settings_success_message">Palvelimen asetukset tallennettu</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Enregistrement des paramètres du serveur…</string>
<string name="account_edit_save_server_settings_error_message">Échec denregistrement des paramètres du serveur</string>
<string name="account_edit_save_server_settings_success_message">Les paramètres du serveur ont été enregistrés</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Serverynstellingen bewarje…</string>
<string name="account_edit_save_server_settings_error_message">Serverynstellingen net bewarre</string>
<string name="account_edit_save_server_settings_success_message">Serverynstellingen bewarre</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Socruithe freastalaí á sábháil…</string>
<string name="account_edit_save_server_settings_error_message">Theip ar shábháil socruithe an fhreastalaí</string>
<string name="account_edit_save_server_settings_success_message">Sábháladh socruithe an fhreastalaí</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">Cha b urrainn dhuinn roghainnean an fhrithealaiche a shàbhaladh</string>
<string name="account_edit_save_server_settings_loading_message">A sàbhaladh roghainnean an fhrithealaiche…</string>
<string name="account_edit_save_server_settings_success_message">Chaidh roghainnean an fhrithealaiche a shàbhaladh</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">Nije uspjelo spremanje postavki poslužitelja</string>
<string name="account_edit_save_server_settings_success_message">Postavke poslužitelja spremljene</string>
<string name="account_edit_save_server_settings_loading_message">Spremanje postavki poslužitelja…</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Kiszolgáló beállítások mentése…</string>
<string name="account_edit_save_server_settings_error_message">Nem sikerült menteni a kiszolgáló beállításait</string>
<string name="account_edit_save_server_settings_success_message">Kiszolgáló beállítások mentése</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Menyimpan pengaturan peladen…</string>
<string name="account_edit_save_server_settings_error_message">Gagal menyimpan pengaturan peladen</string>
<string name="account_edit_save_server_settings_success_message">Pengaturan peladen disimpan</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Vista stillingar póstþjóns…</string>
<string name="account_edit_save_server_settings_error_message">Mistókst að vista stillingar póstþjóns</string>
<string name="account_edit_save_server_settings_success_message">Stillingar póstþjóns vistaðar</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Salvataggio impostazioni server…</string>
<string name="account_edit_save_server_settings_error_message">Salvataggio impostazioni server fallito</string>
<string name="account_edit_save_server_settings_success_message">Impostazioni server salvate</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">שומר הגדרות שרת…</string>
<string name="account_edit_save_server_settings_error_message">נכשל בשמירת הגדרות השרת</string>
<string name="account_edit_save_server_settings_success_message">הגדרות שרת נשמרו</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">サーバー設定の保存中…</string>
<string name="account_edit_save_server_settings_error_message">サーバー設定を保存できませんでした</string>
<string name="account_edit_save_server_settings_success_message">サーバー設定を保存しました</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Asekles n iɣewwaṛen n useqdac…</string>
</resources>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Сервер баптаулары сақталуда…</string>
<string name="account_edit_save_server_settings_success_message">Сервер баптаулары сақталды</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_success_message">Įrašyti serverio nustatymai</string>
<string name="account_edit_save_server_settings_loading_message">Įrašoma serverio nustatymai…</string>
<string name="account_edit_save_server_settings_error_message">Nepavyko įrašyti serverio nustatymų.</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Lagrer serverinnstillinger…</string>
<string name="account_edit_save_server_settings_error_message">Feil ved lagring av tjenerinnstillinger</string>
<string name="account_edit_save_server_settings_success_message">Tjenerinnstillingene er lagret</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Serverinstellingen opslaan…</string>
<string name="account_edit_save_server_settings_error_message">Serverinstellingen niet opgeslagen</string>
<string name="account_edit_save_server_settings_success_message">Serverinstellingen opgeslagen</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Lagrar tenarinnstillingar…</string>
<string name="account_edit_save_server_settings_error_message">Klarte ikkje lagre tenarinnstillingar</string>
<string name="account_edit_save_server_settings_success_message">Tenarinnstillingar lagra</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Zapisywanie ustawień serwera…</string>
<string name="account_edit_save_server_settings_error_message">Nie udało się zapisać ustawień serwera</string>
<string name="account_edit_save_server_settings_success_message">Zapisano ustawienia serwera</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Salvando configurações do servidor…</string>
<string name="account_edit_save_server_settings_error_message">Falha ao salvar configurações do servidor</string>
<string name="account_edit_save_server_settings_success_message">Configurações do servidor salvas</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">A guardar as configurações do servidor…</string>
<string name="account_edit_save_server_settings_error_message">Falha ao guardar as definições do servidor</string>
<string name="account_edit_save_server_settings_success_message">Configurações do servidor guardadas</string>
</resources>

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<resources></resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">Salvarea configurației serverului a eșuat</string>
<string name="account_edit_save_server_settings_loading_message">Se salvează configurației serverului…</string>
<string name="account_edit_save_server_settings_success_message">Configurația serverului a fost salvată</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Сохранение настроек сервера…</string>
<string name="account_edit_save_server_settings_error_message">Невозможно сохранить настройки сервера</string>
<string name="account_edit_save_server_settings_success_message">Настройки сервера сохранены</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Ukladám nastavenia servera…</string>
<string name="account_edit_save_server_settings_error_message">Zlyhalo uloženie nastavení servera</string>
<string name="account_edit_save_server_settings_success_message">Nastavenia servera boli uložené</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Shranjevanje nastavitev strežnika …</string>
<string name="account_edit_save_server_settings_error_message">Shranjevanje nastavitev strežnika je spodletelo.</string>
<string name="account_edit_save_server_settings_success_message">Nastavitve strežnika so bile shranjene.</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Po ruhen rregullime shërbyesi…</string>
<string name="account_edit_save_server_settings_error_message">Su arrit të ruhen rregullime shërbyesi</string>
<string name="account_edit_save_server_settings_success_message">Rregullimet e shërbyesit u ruajtën</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Чување подешавања сервера…</string>
<string name="account_edit_save_server_settings_error_message">Неуспешно чување подешавања сервера</string>
<string name="account_edit_save_server_settings_success_message">Подешавања сервера су сачувана</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Sparar serverinställningar…</string>
<string name="account_edit_save_server_settings_error_message">Det gick inte att spara serverinställningarna</string>
<string name="account_edit_save_server_settings_success_message">Serverinställningar sparade</string>
</resources>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_success_message">சேவையக அமைப்புகள் சேமிக்கப்பட்டன</string>
<string name="account_edit_save_server_settings_loading_message">சேவையக அமைப்புகளைச் சேமிக்கிறது…</string>
<string name="account_edit_save_server_settings_error_message">சேவையக அமைப்புகளை சேமிப்பதில் தோல்வி</string>
</resources>

View file

@ -0,0 +1,3 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Sunucu ayarları kaydediliyor…</string>
<string name="account_edit_save_server_settings_error_message">Sunucu ayarları kaydedilemedi</string>
<string name="account_edit_save_server_settings_success_message">Sunucu ayarları kaydedildi</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Збереження налаштувань сервера…</string>
<string name="account_edit_save_server_settings_error_message">Не вдалося зберегти налаштування сервера</string>
<string name="account_edit_save_server_settings_success_message">Налаштування сервера збережено</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Đang lưu các cài đặt của máy chủ…</string>
<string name="account_edit_save_server_settings_error_message">Lưu các cài đặt của máy chủ thất bại</string>
<string name="account_edit_save_server_settings_success_message">Đã lưu các cài đặt của máy chủ</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">正在保存服务器设置…</string>
<string name="account_edit_save_server_settings_error_message">保存服务器设置失败</string>
<string name="account_edit_save_server_settings_success_message">服务器设置已保存</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_error_message">無法儲存伺服器設定</string>
<string name="account_edit_save_server_settings_success_message">已儲存伺服器設定</string>
<string name="account_edit_save_server_settings_loading_message">正在儲存伺服器設定…</string>
</resources>

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="account_edit_save_server_settings_loading_message">Saving server settings…</string>
<string name="account_edit_save_server_settings_error_message">Failed to save server settings</string>
<string name="account_edit_save_server_settings_success_message">Server settings saved</string>
</resources>

View file

@ -0,0 +1,34 @@
package app.k9mail.feature.account.edit
import android.content.Context
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.InteractionMode
import app.k9mail.feature.account.edit.ui.server.settings.save.SaveServerSettingsContract
import app.k9mail.feature.account.server.certificate.ui.ServerCertificateErrorContract
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
import app.k9mail.feature.account.server.validation.ui.ServerValidationContract
import org.junit.Test
import org.koin.test.KoinTest
import org.koin.test.verify.verify
class AccountEditModuleKtTest : KoinTest {
@Test
fun `should have a valid di module`() {
featureAccountEditModule.verify(
extraTypes = listOf(
Context::class,
AccountState::class,
Class.forName("net.openid.appauth.AppAuthConfiguration").kotlin,
ServerValidationContract.State::class,
ServerCertificateErrorContract.State::class,
IncomingServerSettingsContract.State::class,
OutgoingServerSettingsContract.State::class,
SaveServerSettingsContract.State::class,
AccountEditExternalContract.AccountServerSettingsUpdater::class,
InteractionMode::class,
),
)
}
}

View file

@ -0,0 +1,104 @@
package app.k9mail.feature.account.edit.domain.usecase
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions
import app.k9mail.feature.account.common.domain.entity.AccountOptions
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import kotlinx.coroutines.test.runTest
import org.junit.Test
class GetAccountStateTest {
@Test
fun `should get account state from repository`() = runTest {
val testSubject = GetAccountState(
accountStateRepository = InMemoryAccountStateRepository(state = ACCOUNT_STATE),
)
val result = testSubject.execute(ACCOUNT_UUID)
assertThat(result).isEqualTo(ACCOUNT_STATE)
}
@Test
fun `should throw exception WHEN account state repository contains state for different account uuid`() = runTest {
val testSubject = GetAccountState(
accountStateRepository = InMemoryAccountStateRepository(
state = ACCOUNT_STATE.copy(uuid = "differentAccountUuid"),
),
)
assertFailure {
testSubject.execute(ACCOUNT_UUID)
}.isInstanceOf<IllegalStateException>()
.hasMessage("Account state for $ACCOUNT_UUID not found")
}
private companion object {
const val ACCOUNT_UUID = "accountUuid"
const val EMAIL_ADDRESS = "test@example.com"
val INCOMING_SERVER_SETTINGS = ServerSettings(
type = "imap",
host = "imap.example.com",
port = 993,
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "user",
password = "password",
clientCertificateAlias = null,
)
val OUTGOING_SERVER_SETTINGS = ServerSettings(
type = "smtp",
host = "smtp.example.com",
port = 465,
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "user",
password = "password",
clientCertificateAlias = null,
)
val AUTHORIZATION_STATE = AuthorizationState("authorization state")
val OPTIONS = AccountOptions(
accountName = "accountName",
displayName = "displayName",
emailSignature = null,
checkFrequencyInMinutes = 15,
messageDisplayCount = 25,
showNotification = true,
)
val DISPLAY_OPTIONS = AccountDisplayOptions(
accountName = "accountName",
displayName = "displayName",
emailSignature = null,
)
val SYNC_OPTIONS = AccountSyncOptions(
checkFrequencyInMinutes = 15,
messageDisplayCount = 25,
showNotification = true,
)
val ACCOUNT_STATE = AccountState(
uuid = ACCOUNT_UUID,
emailAddress = EMAIL_ADDRESS,
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
authorizationState = AUTHORIZATION_STATE,
displayOptions = DISPLAY_OPTIONS,
syncOptions = SYNC_OPTIONS,
)
}
}

View file

@ -0,0 +1,98 @@
package app.k9mail.feature.account.edit.domain.usecase
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import kotlinx.coroutines.test.runTest
import org.junit.Test
class LoadAccountStateTest {
@Test
fun `should load account state and update account state repository`() = runTest {
val accountStateRepository = InMemoryAccountStateRepository()
val testSubject = LoadAccountState(
accountStateLoader = { _ ->
ACCOUNT_STATE
},
accountStateRepository = accountStateRepository,
)
val result = testSubject.execute(ACCOUNT_UUID)
assertThat(result).isEqualTo(ACCOUNT_STATE)
assertThat(accountStateRepository.getState()).isEqualTo(ACCOUNT_STATE)
}
@Test
fun `should throw exception WHEN account loader returns null`() = runTest {
val testSubject = LoadAccountState(
accountStateLoader = { null },
accountStateRepository = InMemoryAccountStateRepository(),
)
assertFailure {
testSubject.execute(ACCOUNT_UUID)
}.isInstanceOf<IllegalStateException>()
.hasMessage("Account state for $ACCOUNT_UUID not found")
}
private companion object {
const val ACCOUNT_UUID = "accountUuid"
const val EMAIL_ADDRESS = "test@example.com"
val INCOMING_SERVER_SETTINGS = ServerSettings(
type = "imap",
host = "imap.example.com",
port = 993,
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "user",
password = "password",
clientCertificateAlias = null,
)
val OUTGOING_SERVER_SETTINGS = ServerSettings(
type = "smtp",
host = "smtp.example.com",
port = 465,
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "user",
password = "password",
clientCertificateAlias = null,
)
val AUTHORIZATION_STATE = AuthorizationState("authorization state")
val DISPLAY_OPTIONS = AccountDisplayOptions(
accountName = "accountName",
displayName = "displayName",
emailSignature = null,
)
val SYNC_OPTIONS = AccountSyncOptions(
checkFrequencyInMinutes = 15,
messageDisplayCount = 25,
showNotification = true,
)
val ACCOUNT_STATE = AccountState(
uuid = ACCOUNT_UUID,
emailAddress = EMAIL_ADDRESS,
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
authorizationState = AUTHORIZATION_STATE,
displayOptions = DISPLAY_OPTIONS,
syncOptions = SYNC_OPTIONS,
)
}
}

View file

@ -0,0 +1,169 @@
package app.k9mail.feature.account.edit.domain.usecase
import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions
import app.k9mail.feature.account.common.domain.entity.AuthorizationState
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterFailure
import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterResult
import assertk.assertFailure
import assertk.assertThat
import assertk.assertions.hasMessage
import assertk.assertions.isEqualTo
import assertk.assertions.isInstanceOf
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import kotlinx.coroutines.test.runTest
import org.junit.Test
class SaveServerSettingsTest {
@Test
fun `should get account state and update incoming server settings`() = runTest {
var recordedAccountUuid: String? = null
var recordedIsIncoming: Boolean? = null
var recordedServerSettings: ServerSettings? = null
var recordedAuthorizationState: AuthorizationState? = null
val testSubject = SaveServerSettings(
getAccountState = { _ -> ACCOUNT_STATE },
serverSettingsUpdater = { accountUuid, isIncoming, serverSettings, authorizationState ->
recordedAccountUuid = accountUuid
recordedIsIncoming = isIncoming
recordedServerSettings = serverSettings
recordedAuthorizationState = authorizationState
AccountUpdaterResult.Success(accountUuid)
},
)
testSubject.execute(ACCOUNT_UUID, isIncoming = true)
assertThat(recordedAccountUuid).isEqualTo(ACCOUNT_UUID)
assertThat(recordedIsIncoming).isEqualTo(true)
assertThat(recordedServerSettings).isEqualTo(INCOMING_SERVER_SETTINGS)
assertThat(recordedAuthorizationState).isEqualTo(AUTHORIZATION_STATE)
}
@Test
fun `should throw exception WHEN no incoming server settings present`() = runTest {
val testSubject = SaveServerSettings(
getAccountState = { _ -> ACCOUNT_STATE.copy(incomingServerSettings = null) },
serverSettingsUpdater = { accountUuid, _, _, _ ->
AccountUpdaterResult.Success(accountUuid)
},
)
assertFailure {
testSubject.execute(ACCOUNT_UUID, isIncoming = true)
}.isInstanceOf<IllegalStateException>()
.hasMessage("Server settings not found")
}
@Test
fun `should get account state and update outgoing server settings`() = runTest {
var recordedAccountUuid: String? = null
var recordedIsIncoming: Boolean? = null
var recordedServerSettings: ServerSettings? = null
var recordedAuthorizationState: AuthorizationState? = null
val testSubject = SaveServerSettings(
getAccountState = { _ -> ACCOUNT_STATE },
serverSettingsUpdater = { accountUuid, isIncoming, serverSettings, authorizationState ->
recordedAccountUuid = accountUuid
recordedIsIncoming = isIncoming
recordedServerSettings = serverSettings
recordedAuthorizationState = authorizationState
AccountUpdaterResult.Success(accountUuid)
},
)
testSubject.execute(ACCOUNT_UUID, isIncoming = false)
assertThat(recordedAccountUuid).isEqualTo(ACCOUNT_UUID)
assertThat(recordedIsIncoming).isEqualTo(false)
assertThat(recordedServerSettings).isEqualTo(OUTGOING_SERVER_SETTINGS)
assertThat(recordedAuthorizationState).isEqualTo(AUTHORIZATION_STATE)
}
@Test
fun `should throw exception WHEN no outgoing server settings present`() = runTest {
val testSubject = SaveServerSettings(
getAccountState = { _ -> ACCOUNT_STATE.copy(outgoingServerSettings = null) },
serverSettingsUpdater = { accountUuid, _, _, _ ->
AccountUpdaterResult.Success(accountUuid)
},
)
assertFailure {
testSubject.execute(ACCOUNT_UUID, isIncoming = false)
}.isInstanceOf<IllegalStateException>()
.hasMessage("Server settings not found")
}
@Test
fun `should throw exception WHEN update failed`() = runTest {
val testSubject = SaveServerSettings(
getAccountState = { _ -> ACCOUNT_STATE },
serverSettingsUpdater = { _, _, _, _ ->
AccountUpdaterResult.Failure(
AccountUpdaterFailure.AccountNotFound(ACCOUNT_UUID),
)
},
)
assertFailure {
testSubject.execute(ACCOUNT_UUID, isIncoming = true)
}.isInstanceOf<IllegalStateException>()
.hasMessage("Server settings update failed")
}
private companion object {
const val ACCOUNT_UUID = "accountUuid"
const val EMAIL_ADDRESS = "test@example.com"
val INCOMING_SERVER_SETTINGS = ServerSettings(
type = "imap",
host = "imap.example.com",
port = 993,
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "user",
password = "password",
clientCertificateAlias = null,
)
val OUTGOING_SERVER_SETTINGS = ServerSettings(
type = "smtp",
host = "smtp.example.com",
port = 465,
connectionSecurity = MailConnectionSecurity.SSL_TLS_REQUIRED,
authenticationType = AuthType.PLAIN,
username = "user",
password = "password",
clientCertificateAlias = null,
)
val AUTHORIZATION_STATE = AuthorizationState("authorization state")
val DISPLAY_OPTIONS = AccountDisplayOptions(
accountName = "accountName",
displayName = "displayName",
emailSignature = null,
)
val SYNC_OPTIONS = AccountSyncOptions(
checkFrequencyInMinutes = 15,
messageDisplayCount = 25,
showNotification = true,
)
val ACCOUNT_STATE = AccountState(
uuid = ACCOUNT_UUID,
emailAddress = EMAIL_ADDRESS,
incomingServerSettings = INCOMING_SERVER_SETTINGS,
outgoingServerSettings = OUTGOING_SERVER_SETTINGS,
authorizationState = AUTHORIZATION_STATE,
displayOptions = DISPLAY_OPTIONS,
syncOptions = SYNC_OPTIONS,
)
}
}

View file

@ -0,0 +1,18 @@
package app.k9mail.feature.account.edit.ui.server.settings.modify
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract
import net.thunderbird.core.common.domain.usecase.validation.ValidationResult
class FakeIncomingServerSettingsValidator(
private val serverAnswer: ValidationResult = ValidationResult.Success,
private val portAnswer: ValidationResult = ValidationResult.Success,
private val usernameAnswer: ValidationResult = ValidationResult.Success,
private val passwordAnswer: ValidationResult = ValidationResult.Success,
private val imapPrefixAnswer: ValidationResult = ValidationResult.Success,
) : IncomingServerSettingsContract.Validator {
override fun validateServer(server: String): ValidationResult = serverAnswer
override fun validatePort(port: Long?): ValidationResult = portAnswer
override fun validateUsername(username: String): ValidationResult = usernameAnswer
override fun validatePassword(password: String): ValidationResult = passwordAnswer
override fun validateImapPrefix(imapPrefix: String): ValidationResult = imapPrefixAnswer
}

View file

@ -0,0 +1,16 @@
package app.k9mail.feature.account.edit.ui.server.settings.modify
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract
import net.thunderbird.core.common.domain.usecase.validation.ValidationResult
class FakeOutgoingServerSettingsValidator(
private val serverAnswer: ValidationResult = ValidationResult.Success,
private val portAnswer: ValidationResult = ValidationResult.Success,
private val usernameAnswer: ValidationResult = ValidationResult.Success,
private val passwordAnswer: ValidationResult = ValidationResult.Success,
) : OutgoingServerSettingsContract.Validator {
override fun validateServer(server: String): ValidationResult = serverAnswer
override fun validatePort(port: Long?): ValidationResult = portAnswer
override fun validateUsername(username: String): ValidationResult = usernameAnswer
override fun validatePassword(password: String): ValidationResult = passwordAnswer
}

View file

@ -0,0 +1,87 @@
package app.k9mail.feature.account.edit.ui.server.settings.modify
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.runMviTest
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AuthenticationType
import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import app.k9mail.feature.account.common.domain.input.NumberInputField
import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.Event
import app.k9mail.feature.account.server.settings.ui.incoming.IncomingServerSettingsContract.State
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import com.fsck.k9.mail.store.imap.ImapStoreSettings
import kotlinx.coroutines.delay
import net.thunderbird.core.testing.coroutines.MainDispatcherRule
import org.junit.Rule
import org.junit.Test
class ModifyIncomingServerSettingsViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
@Test
fun `should load account state from use case`() = runMviTest {
val accountUuid = "accountUuid"
val accountState = AccountState(
uuid = "accountUuid",
emailAddress = "test@example.com",
incomingServerSettings = ServerSettings(
"imap",
"imap.example.com",
123,
MailConnectionSecurity.SSL_TLS_REQUIRED,
AuthType.PLAIN,
"username",
"password",
clientCertificateAlias = null,
extra = ImapStoreSettings.createExtra(
autoDetectNamespace = true,
pathPrefix = null,
useCompression = true,
sendClientInfo = true,
),
),
)
val testSubject = ModifyIncomingServerSettingsViewModel(
accountUuid = accountUuid,
accountStateLoader = { _ ->
delay(50)
accountState
},
validator = FakeIncomingServerSettingsValidator(),
accountStateRepository = InMemoryAccountStateRepository(),
initialState = State(),
)
val turbines = turbinesWithInitialStateCheck(testSubject, State())
testSubject.event(Event.LoadAccountState)
assertThatAndMviTurbinesConsumed(
actual = turbines.awaitStateItem(),
turbines = turbines,
) {
isEqualTo(
State(
server = StringInputField(value = "imap.example.com"),
security = ConnectionSecurity.TLS,
port = NumberInputField(value = 123L),
authenticationType = AuthenticationType.PasswordCleartext,
username = StringInputField(value = "username"),
password = StringInputField(value = "password"),
imapAutodetectNamespaceEnabled = true,
imapPrefix = StringInputField(value = ""),
imapUseCompression = true,
imapSendClientInfo = true,
),
)
}
}
}

View file

@ -0,0 +1,76 @@
package app.k9mail.feature.account.edit.ui.server.settings.modify
import app.k9mail.core.ui.compose.testing.mvi.assertThatAndMviTurbinesConsumed
import app.k9mail.core.ui.compose.testing.mvi.runMviTest
import app.k9mail.core.ui.compose.testing.mvi.turbinesWithInitialStateCheck
import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository
import app.k9mail.feature.account.common.domain.entity.AccountState
import app.k9mail.feature.account.common.domain.entity.AuthenticationType
import app.k9mail.feature.account.common.domain.entity.ConnectionSecurity
import app.k9mail.feature.account.common.domain.entity.MailConnectionSecurity
import app.k9mail.feature.account.common.domain.input.NumberInputField
import app.k9mail.feature.account.common.domain.input.StringInputField
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.Event
import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSettingsContract.State
import assertk.assertions.isEqualTo
import com.fsck.k9.mail.AuthType
import com.fsck.k9.mail.ServerSettings
import kotlinx.coroutines.delay
import net.thunderbird.core.testing.coroutines.MainDispatcherRule
import org.junit.Rule
import org.junit.Test
class ModifyOutgoingServerSettingsViewModelTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
@Test
fun `should load account state from use case`() = runMviTest {
val accountUuid = "accountUuid"
val accountState = AccountState(
uuid = "accountUuid",
emailAddress = "test@example.com",
outgoingServerSettings = ServerSettings(
"smtp",
"smtp.example.com",
123,
MailConnectionSecurity.SSL_TLS_REQUIRED,
AuthType.PLAIN,
"username",
"password",
clientCertificateAlias = null,
extra = emptyMap(),
),
)
val testSubject = ModifyOutgoingServerSettingsViewModel(
accountUuid = accountUuid,
accountStateLoader = { _ ->
delay(50)
accountState
},
validator = FakeOutgoingServerSettingsValidator(),
accountStateRepository = InMemoryAccountStateRepository(),
initialState = State(),
)
val turbines = turbinesWithInitialStateCheck(testSubject, State())
testSubject.event(Event.LoadAccountState)
assertThatAndMviTurbinesConsumed(
actual = turbines.awaitStateItem(),
turbines = turbines,
) {
isEqualTo(
State(
server = StringInputField(value = "smtp.example.com"),
security = ConnectionSecurity.TLS,
port = NumberInputField(value = 123L),
authenticationType = AuthenticationType.PasswordCleartext,
username = StringInputField(value = "username"),
password = StringInputField(value = "password"),
),
)
}
}
}

Some files were not shown because too many files have changed in this diff Show more