From 6dc140561689316930dc0ca4585111f2a694d31d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Tue, 9 Jan 2024 14:50:58 +0100 Subject: [PATCH 1/6] Add DisplayOptionsScreen --- .../data/InMemoryAccountStateRepository.kt | 5 + .../common/domain/AccountDomainContract.kt | 3 + .../domain/entity/AccountDisplayOptions.kt | 7 + .../common/domain/entity/AccountState.kt | 1 + .../preview/PreviewAccountStateRepository.kt | 3 + .../account/setup/domain/DomainContract.kt | 2 + .../setup/domain/usecase/CreateAccount.kt | 15 +- .../setup/navigation/AccountSetupNavHost.kt | 19 ++- .../ui/createaccount/CreateAccountScreen.kt | 2 +- .../createaccount/CreateAccountViewModel.kt | 1 + .../options/display/DisplayOptionsContent.kt | 128 ++++++++++++++ .../options/display/DisplayOptionsContract.kt | 38 +++++ .../options/display/DisplayOptionsScreen.kt | 93 +++++++++++ .../display/DisplayOptionsStateMapper.kt | 31 ++++ .../display/DisplayOptionsStringMapper.kt | 38 +++++ .../display/DisplayOptionsValidator.kt | 25 +++ .../display/DisplayOptionsViewModel.kt | 82 +++++++++ .../setup/src/main/res/values/strings.xml | 4 +- .../setup/domain/usecase/CreateAccountTest.kt | 20 ++- .../CreateAccountViewModelTest.kt | 8 + .../ui/createaccount/FakeCreateAccount.kt | 4 + .../display/DisplayOptionsScreenKtTest.kt | 45 +++++ .../DisplayOptionsStateMapperKtTest.kt | 39 +++++ .../display/DisplayOptionsStateTest.kt | 23 +++ .../display/DisplayOptionsViewModelTest.kt | 157 ++++++++++++++++++ .../display/FakeDisplayOptionsValidator.kt | 14 ++ .../display/FakeDisplayOptionsViewModel.kt | 22 +++ 27 files changed, 815 insertions(+), 14 deletions(-) create mode 100644 feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountDisplayOptions.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContent.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContract.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreen.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStringMapper.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsValidator.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreenKtTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapperKtTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModelTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsValidator.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsViewModel.kt diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt index c55d1fbbad3..9f4f8440e0b 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt @@ -1,6 +1,7 @@ package app.k9mail.feature.account.common.data import app.k9mail.feature.account.common.domain.AccountDomainContract +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.AuthorizationState @@ -45,6 +46,10 @@ class InMemoryAccountStateRepository( state = state.copy(options = options) } + override fun setDisplayOptions(displayOptions: AccountDisplayOptions) { + state = state.copy(displayOptions = displayOptions) + } + override fun clear() { state = AccountState() } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt index ddf3aa60e80..ed58add77de 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt @@ -1,5 +1,6 @@ package app.k9mail.feature.account.common.domain +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.AuthorizationState @@ -25,6 +26,8 @@ interface AccountDomainContract { fun setOptions(options: AccountOptions) + fun setDisplayOptions(displayOptions: AccountDisplayOptions) + fun clear() } } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountDisplayOptions.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountDisplayOptions.kt new file mode 100644 index 00000000000..7384e3387ac --- /dev/null +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountDisplayOptions.kt @@ -0,0 +1,7 @@ +package app.k9mail.feature.account.common.domain.entity + +data class AccountDisplayOptions( + val accountName: String, + val displayName: String, + val emailSignature: String?, +) diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt index e4632a1f4ef..1259bdc59b4 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt @@ -10,4 +10,5 @@ data class AccountState( val authorizationState: AuthorizationState? = null, val specialFolderSettings: SpecialFolderSettings? = null, val options: AccountOptions? = null, + val displayOptions: AccountDisplayOptions? = null, ) diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt index 0ecfa100d54..c119bfba53b 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt @@ -1,6 +1,7 @@ package app.k9mail.feature.account.common.ui.preview import app.k9mail.feature.account.common.domain.AccountDomainContract +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.AuthorizationState @@ -49,5 +50,7 @@ class PreviewAccountStateRepository : AccountDomainContract.AccountStateReposito override fun setOptions(options: AccountOptions) = Unit + override fun setDisplayOptions(displayOptions: AccountDisplayOptions) = Unit + override fun clear() = Unit } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt index c3076a1dc71..dd60fd79ef5 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt @@ -3,6 +3,7 @@ package app.k9mail.feature.account.setup.domain import app.k9mail.autodiscovery.api.AutoDiscoveryResult import app.k9mail.core.common.domain.usecase.validation.ValidationError import app.k9mail.core.common.domain.usecase.validation.ValidationResult +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.SpecialFolderOptions import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings @@ -24,6 +25,7 @@ interface DomainContract { authorizationState: String?, specialFolderSettings: SpecialFolderSettings?, options: AccountOptions, + displayOptions: AccountDisplayOptions, ): AccountCreatorResult } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt index 0a61246a978..06121fd4f41 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt @@ -1,6 +1,7 @@ package app.k9mail.feature.account.setup.domain.usecase import app.k9mail.feature.account.common.domain.entity.Account +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.SpecialFolderSettings import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator @@ -20,6 +21,7 @@ class CreateAccount( authorizationState: String?, specialFolderSettings: SpecialFolderSettings?, options: AccountOptions, + displayOptions: AccountDisplayOptions, ): AccountCreatorResult { val account = Account( uuid = uuidGenerator(), @@ -28,9 +30,20 @@ class CreateAccount( outgoingServerSettings = outgoingServerSettings, authorizationState = authorizationState, specialFolderSettings = specialFolderSettings, - options = options, + options = mapOptions(options, displayOptions), ) return accountCreator.createAccount(account) } + + private fun mapOptions( + options: AccountOptions, + displayOptions: AccountDisplayOptions, + ): AccountOptions { + return options.copy( + accountName = displayOptions.accountName, + displayName = displayOptions.displayName, + emailSignature = displayOptions.emailSignature, + ) + } } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt index 7cc82a680d9..3a356615b1b 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt @@ -22,6 +22,8 @@ import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountScreen import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountViewModel import app.k9mail.feature.account.setup.ui.options.AccountOptionsScreen import app.k9mail.feature.account.setup.ui.options.AccountOptionsViewModel +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsScreen +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsViewModel import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersScreen import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersViewModel import org.koin.androidx.compose.koinViewModel @@ -32,7 +34,8 @@ private const val NESTED_NAVIGATION_INCOMING_SERVER_VALIDATION = "incoming-serve private const val NESTED_NAVIGATION_OUTGOING_SERVER_CONFIG = "outgoing-server/config" private const val NESTED_NAVIGATION_OUTGOING_SERVER_VALIDATION = "outgoing-server/validation" private const val NESTED_NAVIGATION_SPECIAL_FOLDERS = "special-folders" -private const val NESTED_NAVIGATION_ACCOUNT_OPTIONS = "account-options" +private const val NESTED_NAVIGATION_DISPLAY_OPTIONS = "display-options" +private const val NESTED_NAVIGATION_SYNC_OPTIONS = "sync-options" private const val NESTED_NAVIGATION_CREATE_ACCOUNT = "create-account" @Suppress("LongMethod") @@ -109,7 +112,7 @@ fun AccountSetupNavHost( if (hasSpecialFolders) { NESTED_NAVIGATION_SPECIAL_FOLDERS } else { - NESTED_NAVIGATION_ACCOUNT_OPTIONS + NESTED_NAVIGATION_DISPLAY_OPTIONS }, ) { if (isAutomaticConfig) { @@ -127,14 +130,22 @@ fun AccountSetupNavHost( composable(route = NESTED_NAVIGATION_SPECIAL_FOLDERS) { SpecialFoldersScreen( onNext = { - navController.navigate(NESTED_NAVIGATION_ACCOUNT_OPTIONS) + navController.navigate(NESTED_NAVIGATION_DISPLAY_OPTIONS) }, onBack = { navController.popBackStack() }, viewModel = koinViewModel(), ) } - composable(route = NESTED_NAVIGATION_ACCOUNT_OPTIONS) { + composable(route = NESTED_NAVIGATION_DISPLAY_OPTIONS) { + DisplayOptionsScreen( + onNext = { navController.navigate(NESTED_NAVIGATION_SYNC_OPTIONS) }, + onBack = { navController.popBackStack() }, + viewModel = koinViewModel(), + ) + } + + composable(route = NESTED_NAVIGATION_SYNC_OPTIONS) { AccountOptionsScreen( onNext = { navController.navigate(NESTED_NAVIGATION_CREATE_ACCOUNT) }, onBack = { navController.popBackStack() }, diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt index 049041643d6..e859a34b2af 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt @@ -73,7 +73,7 @@ internal fun AccountOptionsScreenK9Preview() { onNext = {}, onBack = {}, viewModel = CreateAccountViewModel( - createAccount = { _, _, _, _, _, _ -> AccountCreatorResult.Success("irrelevant") }, + createAccount = { _, _, _, _, _, _, _ -> AccountCreatorResult.Success("irrelevant") }, accountStateRepository = InMemoryAccountStateRepository(), ), ) diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt index 37dd6ef56a9..ad31f07c366 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt @@ -40,6 +40,7 @@ class CreateAccountViewModel( authorizationState = accountState.authorizationState?.state, specialFolderSettings = accountState.specialFolderSettings, options = accountState.options!!, + displayOptions = accountState.displayOptions!!, ) when (result) { diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContent.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContent.kt new file mode 100644 index 00000000000..0f22ae5e696 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContent.kt @@ -0,0 +1,128 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.ui.compose.designsystem.atom.text.TextOverline +import app.k9mail.core.ui.compose.designsystem.molecule.input.TextInput +import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.core.ui.compose.theme.MainTheme +import app.k9mail.core.ui.compose.theme.ThunderbirdTheme +import app.k9mail.feature.account.common.ui.item.defaultHeadlineItemPadding +import app.k9mail.feature.account.common.ui.item.defaultItemPadding +import app.k9mail.feature.account.setup.R +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State + +@OptIn(ExperimentalLayoutApi::class) +@Suppress("LongMethod") +@Composable +internal fun DisplayOptionsContent( + state: State, + onEvent: (Event) -> Unit, + contentPadding: PaddingValues, + modifier: Modifier = Modifier, +) { + val resources = LocalContext.current.resources + + ResponsiveWidthContainer( + modifier = Modifier + .testTag("DisplayOptionsContent") + .consumeWindowInsets(contentPadding) + .padding(contentPadding) + .then(modifier), + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .imePadding(), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default), + ) { + item { + TextOverline( + text = stringResource(id = R.string.account_setup_options_section_display_options), + modifier = Modifier + .fillMaxWidth() + .padding(defaultHeadlineItemPadding()), + ) + } + + item { + TextInput( + text = state.accountName.value, + errorMessage = state.accountName.error?.toResourceString(resources), + onTextChange = { onEvent(Event.OnAccountNameChanged(it)) }, + label = stringResource(id = R.string.account_setup_options_account_name_label), + contentPadding = defaultItemPadding(), + ) + } + + item { + TextInput( + text = state.displayName.value, + errorMessage = state.displayName.error?.toResourceString(resources), + onTextChange = { onEvent(Event.OnDisplayNameChanged(it)) }, + label = stringResource(id = R.string.account_setup_options_display_name_label), + contentPadding = defaultItemPadding(), + isRequired = true, + ) + } + + item { + TextInput( + text = state.emailSignature.value, + errorMessage = state.emailSignature.error?.toResourceString(resources), + onTextChange = { onEvent(Event.OnEmailSignatureChanged(it)) }, + label = stringResource(id = R.string.account_setup_options_email_signature_label), + contentPadding = defaultItemPadding(), + isSingleLine = false, + ) + } + + item { + Spacer(modifier = Modifier.requiredHeight(MainTheme.sizes.smaller)) + } + } + } +} + +@Composable +@Preview(showBackground = true) +internal fun DisplayOptionsContentK9Preview() { + K9Theme { + DisplayOptionsContent( + state = State(), + onEvent = {}, + contentPadding = PaddingValues(), + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun DisplayOptionsContentThunderbirdPreview() { + ThunderbirdTheme { + DisplayOptionsContent( + state = State(), + onEvent = {}, + contentPadding = PaddingValues(), + ) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContract.kt new file mode 100644 index 00000000000..822b75a9ffe --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsContract.kt @@ -0,0 +1,38 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.core.common.domain.usecase.validation.ValidationResult +import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel +import app.k9mail.feature.account.common.domain.input.StringInputField + +interface DisplayOptionsContract { + + interface ViewModel : UnidirectionalViewModel + + data class State( + val accountName: StringInputField = StringInputField(), + val displayName: StringInputField = StringInputField(), + val emailSignature: StringInputField = StringInputField(), + ) + + sealed interface Event { + data class OnAccountNameChanged(val accountName: String) : Event + data class OnDisplayNameChanged(val displayName: String) : Event + data class OnEmailSignatureChanged(val emailSignature: String) : Event + + data object LoadAccountState : Event + + data object OnNextClicked : Event + data object OnBackClicked : Event + } + + sealed interface Effect { + data object NavigateNext : Effect + data object NavigateBack : Effect + } + + interface Validator { + fun validateAccountName(accountName: String): ValidationResult + fun validateDisplayName(displayName: String): ValidationResult + fun validateEmailSignature(emailSignature: String): ValidationResult + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreen.kt new file mode 100644 index 00000000000..4aaa9c33dea --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreen.kt @@ -0,0 +1,93 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.ui.compose.common.mvi.observe +import app.k9mail.core.ui.compose.designsystem.template.Scaffold +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.core.ui.compose.theme.ThunderbirdTheme +import app.k9mail.feature.account.common.ui.AccountTopAppBar +import app.k9mail.feature.account.common.ui.WizardNavigationBar +import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository +import app.k9mail.feature.account.setup.R.string +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.ViewModel + +@Composable +internal fun DisplayOptionsScreen( + onNext: () -> Unit, + onBack: () -> Unit, + viewModel: ViewModel, + modifier: Modifier = Modifier, +) { + val (state, dispatch) = viewModel.observe { effect -> + when (effect) { + Effect.NavigateBack -> onBack() + Effect.NavigateNext -> onNext() + } + } + + LaunchedEffect(key1 = Unit) { + dispatch(Event.LoadAccountState) + } + + BackHandler { + dispatch(Event.OnBackClicked) + } + + Scaffold( + topBar = { + AccountTopAppBar( + title = stringResource(id = string.account_setup_options_section_display_options), + ) + }, + bottomBar = { + WizardNavigationBar( + onNextClick = { dispatch(Event.OnNextClicked) }, + onBackClick = { dispatch(Event.OnBackClicked) }, + ) + }, + modifier = modifier, + ) { innerPadding -> + DisplayOptionsContent( + state = state.value, + onEvent = { dispatch(it) }, + contentPadding = innerPadding, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun DisplayOptionsScreenK9Preview() { + K9Theme { + DisplayOptionsScreen( + onNext = {}, + onBack = {}, + viewModel = DisplayOptionsViewModel( + validator = DisplayOptionsValidator(), + accountStateRepository = PreviewAccountStateRepository(), + ), + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun DisplayOptionsScreenThunderbirdPreview() { + ThunderbirdTheme { + DisplayOptionsScreen( + onNext = {}, + onBack = {}, + viewModel = DisplayOptionsViewModel( + validator = DisplayOptionsValidator(), + accountStateRepository = PreviewAccountStateRepository(), + ), + ) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt new file mode 100644 index 00000000000..5025ba9bef7 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt @@ -0,0 +1,31 @@ +package app.k9mail.feature.account.setup.ui.options.display + +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.input.StringInputField +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State + +internal fun AccountState.toAccountOptionsState(): State { + val options = options + return if (options == null) { + State( + accountName = StringInputField(emailAddress ?: ""), + // displayName = StringInputField(""), + // TODO: get display name from: preferences.defaultAccount?.senderName ?: "" + ) + } else { + State( + accountName = StringInputField(options.accountName), + displayName = StringInputField(options.displayName), + emailSignature = StringInputField(options.emailSignature ?: ""), + ) + } +} + +internal fun State.toAccountDisplayOptions(): AccountDisplayOptions { + return AccountDisplayOptions( + accountName = accountName.value, + displayName = displayName.value, + emailSignature = emailSignature.value.takeIf { it.isNotEmpty() }, + ) +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStringMapper.kt new file mode 100644 index 00000000000..43e06229997 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStringMapper.kt @@ -0,0 +1,38 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import android.content.res.Resources +import app.k9mail.core.common.domain.usecase.validation.ValidationError +import app.k9mail.feature.account.setup.R +import app.k9mail.feature.account.setup.domain.usecase.ValidateAccountName.ValidateAccountNameError +import app.k9mail.feature.account.setup.domain.usecase.ValidateAccountName.ValidateAccountNameError.BlankAccountName +import app.k9mail.feature.account.setup.domain.usecase.ValidateDisplayName.ValidateDisplayNameError +import app.k9mail.feature.account.setup.domain.usecase.ValidateDisplayName.ValidateDisplayNameError.EmptyDisplayName +import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailSignature.ValidateEmailSignatureError +import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailSignature.ValidateEmailSignatureError.BlankEmailSignature + +internal fun ValidationError.toResourceString(resources: Resources): String { + return when (this) { + is ValidateAccountNameError -> toAccountNameErrorString(resources) + is ValidateDisplayNameError -> toDisplayNameErrorString(resources) + is ValidateEmailSignatureError -> toEmailSignatureErrorString(resources) + else -> throw IllegalArgumentException("Unknown error: $this") + } +} + +private fun ValidateAccountNameError.toAccountNameErrorString(resources: Resources): String { + return when (this) { + is BlankAccountName -> resources.getString(R.string.account_setup_options_account_name_error_blank) + } +} + +private fun ValidateDisplayNameError.toDisplayNameErrorString(resources: Resources): String { + return when (this) { + is EmptyDisplayName -> resources.getString(R.string.account_setup_options_display_name_error_required) + } +} + +private fun ValidateEmailSignatureError.toEmailSignatureErrorString(resources: Resources): String { + return when (this) { + is BlankEmailSignature -> resources.getString(R.string.account_setup_options_email_signature_error_blank) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsValidator.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsValidator.kt new file mode 100644 index 00000000000..7f0b1f8ac82 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsValidator.kt @@ -0,0 +1,25 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.core.common.domain.usecase.validation.ValidationResult +import app.k9mail.feature.account.setup.domain.usecase.ValidateAccountName +import app.k9mail.feature.account.setup.domain.usecase.ValidateDisplayName +import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailSignature +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Validator + +internal class DisplayOptionsValidator( + private val accountNameValidator: ValidateAccountName = ValidateAccountName(), + private val displayNameValidator: ValidateDisplayName = ValidateDisplayName(), + private val emailSignatureValidator: ValidateEmailSignature = ValidateEmailSignature(), +) : Validator { + override fun validateAccountName(accountName: String): ValidationResult { + return accountNameValidator.execute(accountName) + } + + override fun validateDisplayName(displayName: String): ValidationResult { + return displayNameValidator.execute(displayName) + } + + override fun validateEmailSignature(emailSignature: String): ValidationResult { + return emailSignatureValidator.execute(emailSignature) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt new file mode 100644 index 00000000000..ea8ad92eda1 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt @@ -0,0 +1,82 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.core.common.domain.usecase.validation.ValidationResult +import app.k9mail.core.ui.compose.common.mvi.BaseViewModel +import app.k9mail.feature.account.common.domain.AccountDomainContract +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Validator +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.ViewModel + +internal class DisplayOptionsViewModel( + private val validator: Validator, + private val accountStateRepository: AccountDomainContract.AccountStateRepository, + initialState: State? = null, +) : BaseViewModel( + initialState = initialState ?: accountStateRepository.getState().toAccountOptionsState(), +), + ViewModel { + + override fun event(event: Event) { + when (event) { + Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState) + + is Event.OnAccountNameChanged -> updateState { state -> + state.copy( + accountName = state.accountName.updateValue(event.accountName), + ) + } + + is Event.OnDisplayNameChanged -> updateState { + it.copy( + displayName = it.displayName.updateValue(event.displayName), + ) + } + + is Event.OnEmailSignatureChanged -> updateState { + it.copy( + emailSignature = it.emailSignature.updateValue(event.emailSignature), + ) + } + + Event.OnNextClicked -> submit() + Event.OnBackClicked -> navigateBack() + } + } + + private fun loadAccountState() { + updateState { + accountStateRepository.getState().toAccountOptionsState() + } + } + + private fun submit() = with(state.value) { + val accountNameResult = validator.validateAccountName(accountName.value) + val displayNameResult = validator.validateDisplayName(displayName.value) + val emailSignatureResult = validator.validateEmailSignature(emailSignature.value) + + val hasError = listOf( + accountNameResult, + displayNameResult, + emailSignatureResult, + ).any { it is ValidationResult.Failure } + + updateState { + it.copy( + accountName = it.accountName.updateFromValidationResult(accountNameResult), + displayName = it.displayName.updateFromValidationResult(displayNameResult), + emailSignature = it.emailSignature.updateFromValidationResult(emailSignatureResult), + ) + } + + if (!hasError) { + accountStateRepository.setDisplayOptions(state.value.toAccountDisplayOptions()) + navigateNext() + } + } + + private fun navigateBack() = emitEffect(Effect.NavigateBack) + + private fun navigateNext() = emitEffect(Effect.NavigateNext) +} diff --git a/feature/account/setup/src/main/res/values/strings.xml b/feature/account/setup/src/main/res/values/strings.xml index 41e591f0941..e39bf586401 100644 --- a/feature/account/setup/src/main/res/values/strings.xml +++ b/feature/account/setup/src/main/res/values/strings.xml @@ -44,8 +44,8 @@ Display options Account name Account name can\'t be blank. - Display name - Display name is required. + Your name + Your name is required. Email signature Email signature name can\'t be blank. Sync options diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt index 038aa5679ea..773c475689e 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt @@ -1,6 +1,7 @@ package app.k9mail.feature.account.setup.domain.usecase import app.k9mail.feature.account.common.domain.entity.Account +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.MailConnectionSecurity import app.k9mail.feature.account.common.domain.entity.SpecialFolderOption @@ -30,12 +31,13 @@ class CreateAccountTest { ) val result = createAccount.execute( - EMAIL_ADDRESS, - INCOMING_SETTINGS, - OUTGOING_SETTINGS, - AUTHORIZATION_STATE, - SPECIAL_FOLDER_SETTINGS, - OPTIONS, + emailAddress = EMAIL_ADDRESS, + incomingServerSettings = INCOMING_SETTINGS, + outgoingServerSettings = OUTGOING_SETTINGS, + authorizationState = AUTHORIZATION_STATE, + specialFolderSettings = SPECIAL_FOLDER_SETTINGS, + options = OPTIONS, + displayOptions = DISPLAY_OPTIONS, ) assertThat(result).isEqualTo(AccountCreatorResult.Success("uuid")) @@ -105,5 +107,11 @@ class CreateAccountTest { messageDisplayCount = 25, showNotification = true, ) + + val DISPLAY_OPTIONS = AccountDisplayOptions( + accountName = "accountName", + displayName = "displayName", + emailSignature = null, + ) } } diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt index c663d601827..0a03f40335d 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt @@ -4,6 +4,7 @@ import app.cash.turbine.testIn import app.k9mail.core.ui.compose.testing.MainDispatcherRule import app.k9mail.core.ui.compose.testing.mvi.eventStateTest 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.AuthorizationState @@ -67,6 +68,7 @@ class CreateAccountViewModelTest { authorizationState = AUTHORIZATION_STATE.state, specialFolderSettings = SPECIAL_FOLDER_SETTINGS, options = ACCOUNT_OPTIONS, + displayOptions = ACCOUNT_DISPLAY_OPTIONS, ), ) @@ -193,6 +195,12 @@ class CreateAccountViewModelTest { showNotification = false, ) + val ACCOUNT_DISPLAY_OPTIONS = AccountDisplayOptions( + accountName = "account name", + displayName = "display name", + emailSignature = null, + ) + val ACCOUNT_STATE = AccountState( emailAddress = EMAIL_ADDRESS, incomingServerSettings = INCOMING_SERVER_SETTINGS, diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt index 3711cd9394c..bd0ab7cad90 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt @@ -1,5 +1,6 @@ package app.k9mail.feature.account.setup.ui.createaccount +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.SpecialFolderSettings import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult @@ -18,6 +19,7 @@ class FakeCreateAccount : CreateAccount { authorizationState: String?, specialFolderSettings: SpecialFolderSettings?, options: AccountOptions, + displayOptions: AccountDisplayOptions, ): AccountCreatorResult { recordedInvocations.add( CreateAccountArguments( @@ -27,6 +29,7 @@ class FakeCreateAccount : CreateAccount { authorizationState, specialFolderSettings, options, + displayOptions, ), ) @@ -41,4 +44,5 @@ data class CreateAccountArguments( val authorizationState: String?, val specialFolderSettings: SpecialFolderSettings?, val options: AccountOptions, + val displayOptions: AccountDisplayOptions, ) diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreenKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreenKtTest.kt new file mode 100644 index 00000000000..0680d9fe7bc --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsScreenKtTest.kt @@ -0,0 +1,45 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.core.ui.compose.testing.ComposeTest +import app.k9mail.core.ui.compose.testing.setContent +import app.k9mail.core.ui.compose.theme.ThunderbirdTheme +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State +import assertk.assertThat +import assertk.assertions.isEqualTo +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class DisplayOptionsScreenKtTest : ComposeTest() { + + @Test + fun `should delegate navigation effects`() = runTest { + val initialState = State() + val viewModel = FakeDisplayOptionsViewModel(initialState) + var onNextCounter = 0 + var onBackCounter = 0 + + setContent { + ThunderbirdTheme { + DisplayOptionsScreen( + onNext = { onNextCounter++ }, + onBack = { onBackCounter++ }, + viewModel = viewModel, + ) + } + } + + assertThat(onNextCounter).isEqualTo(0) + assertThat(onBackCounter).isEqualTo(0) + + viewModel.effect(Effect.NavigateNext) + + assertThat(onNextCounter).isEqualTo(1) + assertThat(onBackCounter).isEqualTo(0) + + viewModel.effect(Effect.NavigateBack) + + assertThat(onNextCounter).isEqualTo(1) + assertThat(onBackCounter).isEqualTo(1) + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapperKtTest.kt new file mode 100644 index 00000000000..99414379f3a --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapperKtTest.kt @@ -0,0 +1,39 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.feature.account.common.domain.entity.AccountDisplayOptions +import app.k9mail.feature.account.common.domain.input.StringInputField +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isNull +import org.junit.Test + +class DisplayOptionsStateMapperKtTest { + + @Test + fun `should map state to account options`() { + val state = DisplayOptionsContract.State( + accountName = StringInputField("accountName"), + displayName = StringInputField("displayName"), + emailSignature = StringInputField("emailSignature"), + ) + + val result = state.toAccountDisplayOptions() + + assertThat(result).isEqualTo( + AccountDisplayOptions( + accountName = "accountName", + displayName = "displayName", + emailSignature = "emailSignature", + ), + ) + } + + @Test + fun `empty signature should map to null`() { + val state = DisplayOptionsContract.State(emailSignature = StringInputField("")) + + val result = state.toAccountDisplayOptions() + + assertThat(result.emailSignature).isNull() + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateTest.kt new file mode 100644 index 00000000000..ed306f2912c --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateTest.kt @@ -0,0 +1,23 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.feature.account.common.domain.input.StringInputField +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.junit.Test + +class DisplayOptionsStateTest { + + @Test + fun `should set default values`() { + val state = State() + + assertThat(state).isEqualTo( + State( + accountName = StringInputField(), + displayName = StringInputField(), + emailSignature = StringInputField(), + ), + ) + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModelTest.kt new file mode 100644 index 00000000000..a9deea0cd15 --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModelTest.kt @@ -0,0 +1,157 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.cash.turbine.testIn +import app.k9mail.core.common.domain.usecase.validation.ValidationError +import app.k9mail.core.common.domain.usecase.validation.ValidationResult +import app.k9mail.core.ui.compose.testing.MainDispatcherRule +import app.k9mail.core.ui.compose.testing.mvi.eventStateTest +import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository +import app.k9mail.feature.account.common.domain.input.StringInputField +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State +import assertk.assertThat +import assertk.assertions.assertThatAndTurbinesConsumed +import assertk.assertions.isEqualTo +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class DisplayOptionsViewModelTest { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private val testSubject = DisplayOptionsViewModel( + validator = FakeDisplayOptionsValidator(), + accountStateRepository = InMemoryAccountStateRepository(), + ) + + @Test + fun `should change state when OnAccountNameChanged event is received`() = runTest { + eventStateTest( + viewModel = testSubject, + initialState = State(), + event = Event.OnAccountNameChanged("accountName"), + expectedState = State(accountName = StringInputField(value = "accountName")), + coroutineScope = backgroundScope, + ) + } + + @Test + fun `should change state when OnDisplayNameChanged event is received`() = runTest { + eventStateTest( + viewModel = testSubject, + initialState = State(), + event = Event.OnDisplayNameChanged("displayName"), + expectedState = State(displayName = StringInputField(value = "displayName")), + coroutineScope = backgroundScope, + ) + } + + @Test + fun `should change state when OnEmailSignatureChanged event is received`() = runTest { + eventStateTest( + viewModel = testSubject, + initialState = State(), + event = Event.OnEmailSignatureChanged("emailSignature"), + expectedState = State(emailSignature = StringInputField(value = "emailSignature")), + coroutineScope = backgroundScope, + ) + } + + @Test + fun `should change state and emit NavigateNext effect when OnNextClicked event received and input valid`() = + runTest { + val viewModel = testSubject + val stateTurbine = viewModel.state.testIn(backgroundScope) + val effectTurbine = viewModel.effect.testIn(backgroundScope) + val turbines = listOf(stateTurbine, effectTurbine) + + assertThatAndTurbinesConsumed( + actual = stateTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(State()) + } + + viewModel.event(Event.OnNextClicked) + + assertThat(stateTurbine.awaitItem()).isEqualTo( + State( + accountName = StringInputField(value = "", isValid = true), + displayName = StringInputField(value = "", isValid = true), + emailSignature = StringInputField(value = "", isValid = true), + ), + ) + + assertThatAndTurbinesConsumed( + actual = effectTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(Effect.NavigateNext) + } + } + + @Test + fun `should change state and not emit effect when OnNextClicked event received and input invalid`() = + runTest { + val viewModel = DisplayOptionsViewModel( + validator = FakeDisplayOptionsValidator( + accountNameAnswer = ValidationResult.Failure(TestError), + ), + accountStateRepository = InMemoryAccountStateRepository(), + ) + val stateTurbine = viewModel.state.testIn(backgroundScope) + val effectTurbine = viewModel.effect.testIn(backgroundScope) + val turbines = listOf(stateTurbine, effectTurbine) + + assertThatAndTurbinesConsumed( + actual = stateTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(State()) + } + + viewModel.event(Event.OnNextClicked) + + assertThatAndTurbinesConsumed( + actual = stateTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo( + State( + accountName = StringInputField(value = "", error = TestError, isValid = false), + displayName = StringInputField(value = "", isValid = true), + emailSignature = StringInputField(value = "", isValid = true), + ), + ) + } + } + + @Test + fun `should emit NavigateBack effect when OnBackClicked event received`() = runTest { + val viewModel = testSubject + val stateTurbine = viewModel.state.testIn(backgroundScope) + val effectTurbine = viewModel.effect.testIn(backgroundScope) + val turbines = listOf(stateTurbine, effectTurbine) + + assertThatAndTurbinesConsumed( + actual = stateTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(State()) + } + + viewModel.event(Event.OnBackClicked) + + assertThatAndTurbinesConsumed( + actual = effectTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(Effect.NavigateBack) + } + } + + private object TestError : ValidationError +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsValidator.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsValidator.kt new file mode 100644 index 00000000000..5cf624745b5 --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsValidator.kt @@ -0,0 +1,14 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.core.common.domain.usecase.validation.ValidationResult +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Validator + +internal class FakeDisplayOptionsValidator( + private val accountNameAnswer: ValidationResult = ValidationResult.Success, + private val displayNameAnswer: ValidationResult = ValidationResult.Success, + private val emailSignatureAnswer: ValidationResult = ValidationResult.Success, +) : Validator { + override fun validateAccountName(accountName: String): ValidationResult = accountNameAnswer + override fun validateDisplayName(displayName: String): ValidationResult = displayNameAnswer + override fun validateEmailSignature(emailSignature: String): ValidationResult = emailSignatureAnswer +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsViewModel.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsViewModel.kt new file mode 100644 index 00000000000..52d65d6c902 --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/display/FakeDisplayOptionsViewModel.kt @@ -0,0 +1,22 @@ +package app.k9mail.feature.account.setup.ui.options.display + +import app.k9mail.core.ui.compose.common.mvi.BaseViewModel +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.ViewModel + +class FakeDisplayOptionsViewModel( + initialState: State = State(), +) : BaseViewModel(initialState), ViewModel { + + val events = mutableListOf() + + override fun event(event: Event) { + events.add(event) + } + + fun effect(effect: Effect) { + emitEffect(effect) + } +} From a6b4633dbc329643a18cf4dd89f7bde1d5f866f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Tue, 9 Jan 2024 15:22:55 +0100 Subject: [PATCH 2/6] Rename string --- .../feature/account/setup/ui/options/AccountOptionsContent.kt | 2 +- feature/account/setup/src/main/res/values-bg/strings.xml | 2 +- feature/account/setup/src/main/res/values-ca/strings.xml | 2 +- feature/account/setup/src/main/res/values-cs/strings.xml | 2 +- feature/account/setup/src/main/res/values-da/strings.xml | 2 +- feature/account/setup/src/main/res/values-de/strings.xml | 2 +- feature/account/setup/src/main/res/values-el/strings.xml | 2 +- feature/account/setup/src/main/res/values-en-rGB/strings.xml | 2 +- feature/account/setup/src/main/res/values-es/strings.xml | 2 +- feature/account/setup/src/main/res/values-et/strings.xml | 2 +- feature/account/setup/src/main/res/values-eu/strings.xml | 2 +- feature/account/setup/src/main/res/values-fa/strings.xml | 2 +- feature/account/setup/src/main/res/values-fi/strings.xml | 2 +- feature/account/setup/src/main/res/values-fr/strings.xml | 2 +- feature/account/setup/src/main/res/values-fy/strings.xml | 2 +- feature/account/setup/src/main/res/values-hi/strings.xml | 2 +- feature/account/setup/src/main/res/values-hu/strings.xml | 2 +- feature/account/setup/src/main/res/values-is/strings.xml | 2 +- feature/account/setup/src/main/res/values-it/strings.xml | 2 +- feature/account/setup/src/main/res/values-ja/strings.xml | 2 +- feature/account/setup/src/main/res/values-nb-rNO/strings.xml | 2 +- feature/account/setup/src/main/res/values-nl/strings.xml | 2 +- feature/account/setup/src/main/res/values-pl/strings.xml | 2 +- feature/account/setup/src/main/res/values-pt-rBR/strings.xml | 2 +- feature/account/setup/src/main/res/values-pt-rPT/strings.xml | 2 +- feature/account/setup/src/main/res/values-ro/strings.xml | 2 +- feature/account/setup/src/main/res/values-sq/strings.xml | 2 +- feature/account/setup/src/main/res/values-sv/strings.xml | 2 +- feature/account/setup/src/main/res/values-tr/strings.xml | 2 +- feature/account/setup/src/main/res/values-vi/strings.xml | 2 +- feature/account/setup/src/main/res/values-zh-rCN/strings.xml | 2 +- feature/account/setup/src/main/res/values-zh-rTW/strings.xml | 2 +- feature/account/setup/src/main/res/values/strings.xml | 2 +- 33 files changed, 33 insertions(+), 33 deletions(-) diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt index 7e7e3ad97be..c4d1f9bbef7 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt @@ -102,7 +102,7 @@ internal fun AccountOptionsContent( item { TextOverline( - text = stringResource(id = R.string.account_setup_options_section_sync_options_), + text = stringResource(id = R.string.account_setup_options_section_sync_options), modifier = Modifier .fillMaxWidth() .padding(defaultHeadlineItemPadding()), diff --git a/feature/account/setup/src/main/res/values-bg/strings.xml b/feature/account/setup/src/main/res/values-bg/strings.xml index f586296751a..885c21e874e 100644 --- a/feature/account/setup/src/main/res/values-bg/strings.xml +++ b/feature/account/setup/src/main/res/values-bg/strings.xml @@ -34,7 +34,7 @@ Името за визуализация е задължително. Възникна грешка при създаването на акаунта Никога - Настройки за синхронизация + Настройки за синхронизация Показване на известия Настройки за визуализация \ No newline at end of file diff --git a/feature/account/setup/src/main/res/values-ca/strings.xml b/feature/account/setup/src/main/res/values-ca/strings.xml index 0d92791018a..e4abea048be 100644 --- a/feature/account/setup/src/main/res/values-ca/strings.xml +++ b/feature/account/setup/src/main/res/values-ca/strings.xml @@ -29,7 +29,7 @@ L\'adreça de correu electrònic no es reconeix com a vàlida. Mai Cercant els detalls del correu electrònic - Opcions de sincronització + Opcions de sincronització Mostra les notificacions Opcions de visualització Aquesta direcció de correu electrònic no està permesa. diff --git a/feature/account/setup/src/main/res/values-cs/strings.xml b/feature/account/setup/src/main/res/values-cs/strings.xml index 80f2c2afeb7..0217b36992c 100644 --- a/feature/account/setup/src/main/res/values-cs/strings.xml +++ b/feature/account/setup/src/main/res/values-cs/strings.xml @@ -28,7 +28,7 @@ Toto není rozeznáno jako platná e-mailová adresa. Nikdy Zjišťování podrobností o e-mailu - Nastavení synchronizace + Nastavení synchronizace Zobrazovat oznámení Nastavení zobrazení K-9 Mail diff --git a/feature/account/setup/src/main/res/values-da/strings.xml b/feature/account/setup/src/main/res/values-da/strings.xml index 98bbcde3f8d..ee06be54767 100644 --- a/feature/account/setup/src/main/res/values-da/strings.xml +++ b/feature/account/setup/src/main/res/values-da/strings.xml @@ -18,7 +18,7 @@ Netværk Email adresse er ugyldig. Aldrig - Synkroniserings indstillinger + Synkroniserings indstillinger Vis notifikationer Visningsindstillinger K-9 Mail diff --git a/feature/account/setup/src/main/res/values-de/strings.xml b/feature/account/setup/src/main/res/values-de/strings.xml index b4532ced2a4..44fffa838ee 100644 --- a/feature/account/setup/src/main/res/values-de/strings.xml +++ b/feature/account/setup/src/main/res/values-de/strings.xml @@ -28,7 +28,7 @@ Diese Adresse wird nicht als gültige E-Mail-Adresse erkannt. Niemals E-Mail-Details finden - Synchronisierungsoptionen + Synchronisierungsoptionen Benachrichtigungen anzeigen Anzeigeoptionen K-9 Mail diff --git a/feature/account/setup/src/main/res/values-el/strings.xml b/feature/account/setup/src/main/res/values-el/strings.xml index de29bb330a0..94dc7346599 100644 --- a/feature/account/setup/src/main/res/values-el/strings.xml +++ b/feature/account/setup/src/main/res/values-el/strings.xml @@ -32,7 +32,7 @@ Η διεύθυνση email δεν αναγνωρίζεται ως έγκυρη. Ποτέ Εύερεση λεπτομεριών email - Επιλογές συγχρονισμού + Επιλογές συγχρονισμού Εμφάνιση ειδοποιήσεων Επιλογές εμφάνισης K-9 Mail diff --git a/feature/account/setup/src/main/res/values-en-rGB/strings.xml b/feature/account/setup/src/main/res/values-en-rGB/strings.xml index 1acf0e5599f..0783b126782 100644 --- a/feature/account/setup/src/main/res/values-en-rGB/strings.xml +++ b/feature/account/setup/src/main/res/values-en-rGB/strings.xml @@ -33,7 +33,7 @@ This is not recognised as a valid email address. Never Finding email details - Sync options + Sync options Show notifications Display options K-9 Mail diff --git a/feature/account/setup/src/main/res/values-es/strings.xml b/feature/account/setup/src/main/res/values-es/strings.xml index 99fd34489cd..6d1617d2588 100644 --- a/feature/account/setup/src/main/res/values-es/strings.xml +++ b/feature/account/setup/src/main/res/values-es/strings.xml @@ -28,7 +28,7 @@ No se reconoce como una dirección de correo electrónico válida. Nunca Buscando los ajustes oficiales del servicio de forma automática - Ajustes de sincronización + Ajustes de sincronización Mostrar notificaciones Ajustes de pantalla K-9 Mail diff --git a/feature/account/setup/src/main/res/values-et/strings.xml b/feature/account/setup/src/main/res/values-et/strings.xml index da296657ac9..af7681f1e0a 100644 --- a/feature/account/setup/src/main/res/values-et/strings.xml +++ b/feature/account/setup/src/main/res/values-et/strings.xml @@ -29,7 +29,7 @@ Kuvatav nimi Kuvatav nimi peab olema sisestatud. Mitte kunagi - Sünkroniseerimisvalikud + Sünkroniseerimisvalikud Näita teavitusi Kuvatavad valikud Sellise e-posti aadressi kasutamine pole lubatud. diff --git a/feature/account/setup/src/main/res/values-eu/strings.xml b/feature/account/setup/src/main/res/values-eu/strings.xml index 46fdd48acd3..07bf4335745 100644 --- a/feature/account/setup/src/main/res/values-eu/strings.xml +++ b/feature/account/setup/src/main/res/values-eu/strings.xml @@ -28,7 +28,7 @@ Helbide hau ez da helbide elektroniko zuzentzat jotzen. Inoiz ere ez E-mail xehetasunak bilatzen - Sinkronizazio ezarpenak + Sinkronizazio ezarpenak Erakutsi jakinarazpenak Pantaila ezarpenak K-9 Mail diff --git a/feature/account/setup/src/main/res/values-fa/strings.xml b/feature/account/setup/src/main/res/values-fa/strings.xml index 6bbdc40a231..03454590938 100644 --- a/feature/account/setup/src/main/res/values-fa/strings.xml +++ b/feature/account/setup/src/main/res/values-fa/strings.xml @@ -46,7 +46,7 @@ پوشه هرزنامه هرگز پیدا کردن جزییات رایانامه - گزینه‌های همگام‌سازی + گزینه‌های همگام‌سازی نمایش اعلان‌ها گزینه‌های نمایش تنظیم \"خودکار\" تغییراتی که توسط سرور به طور خودکار انجام می شود را اعمال می‌کند. مقدار فعلی در داخل پرانتز نمایش داده می‌شود. diff --git a/feature/account/setup/src/main/res/values-fi/strings.xml b/feature/account/setup/src/main/res/values-fi/strings.xml index 9daa6876362..60447e67270 100644 --- a/feature/account/setup/src/main/res/values-fi/strings.xml +++ b/feature/account/setup/src/main/res/values-fi/strings.xml @@ -27,7 +27,7 @@ Tätä ei tunnistettu kelvolliseksi sähköpostiosoitteeksi. Ei koskaan Etsitään sähköpostin tietoja - Synkronointiasetukset + Synkronointiasetukset Näytä ilmoitukset Näkymäasetukset K-9 Mail diff --git a/feature/account/setup/src/main/res/values-fr/strings.xml b/feature/account/setup/src/main/res/values-fr/strings.xml index 7483cb4c700..43197466dd7 100644 --- a/feature/account/setup/src/main/res/values-fr/strings.xml +++ b/feature/account/setup/src/main/res/values-fr/strings.xml @@ -28,7 +28,7 @@ Cette adresse courriel n’est pas reconnue comme valide. Jamais Recherche des détails du compte de courriel - Options de synchronisation + Options de synchronisation Afficher les notifications Options d’affichage Courriel K-9 Mail diff --git a/feature/account/setup/src/main/res/values-fy/strings.xml b/feature/account/setup/src/main/res/values-fy/strings.xml index 8c31bbc4158..a5f9726823b 100644 --- a/feature/account/setup/src/main/res/values-fy/strings.xml +++ b/feature/account/setup/src/main/res/values-fy/strings.xml @@ -28,7 +28,7 @@ Dit is net as in jildich e-mailadres werkend. Nea E-maildetails sykje - Syngronisaasjeopsjes + Syngronisaasjeopsjes Meldingen toane Werjefteopsjes K-9 Mail diff --git a/feature/account/setup/src/main/res/values-hi/strings.xml b/feature/account/setup/src/main/res/values-hi/strings.xml index 9d6752c45e5..627cac3b1b2 100644 --- a/feature/account/setup/src/main/res/values-hi/strings.xml +++ b/feature/account/setup/src/main/res/values-hi/strings.xml @@ -28,7 +28,7 @@ ईमेल पता गलत है। कभी नहीं ईमेल की जानकारी खोज रहे - सिंक ऑप्शन + सिंक ऑप्शन नोटिफिकेशन दिखाएं डिस्प्ले ऑप्शन K-9 मेल diff --git a/feature/account/setup/src/main/res/values-hu/strings.xml b/feature/account/setup/src/main/res/values-hu/strings.xml index 21646644be4..5a9bd393e20 100644 --- a/feature/account/setup/src/main/res/values-hu/strings.xml +++ b/feature/account/setup/src/main/res/values-hu/strings.xml @@ -28,7 +28,7 @@ Az email cím nem tekinthető érvényes email címnek. Soha E-mail adatok keresése - Szinkronizálási opciók + Szinkronizálási opciók Értesítések megjelenítése Megjelenítési opciók K-9 Mail diff --git a/feature/account/setup/src/main/res/values-is/strings.xml b/feature/account/setup/src/main/res/values-is/strings.xml index 5f8742fe020..06db82712af 100644 --- a/feature/account/setup/src/main/res/values-is/strings.xml +++ b/feature/account/setup/src/main/res/values-is/strings.xml @@ -28,7 +28,7 @@ Þetta telst ekki vera gilt tölvupóstfang. Aldrei Finna nánar um tölvupóst - Valkostir samstillinga + Valkostir samstillinga Birta tilkynningar Valkostir birtingar Við höfum tekið við uppsetningunni fyrir póstþjóninn um tengingu sem ekki er eins örugg og við myndum vilja. Þetta þýðir að það eru einhverjar líkur á að einhver hafi komist í upplýsingarnar og breytt þeim. Geturðu yfirfarið þessar upplýsingar og gengið úr skugga um að allt sé eins og það á að vera\? diff --git a/feature/account/setup/src/main/res/values-it/strings.xml b/feature/account/setup/src/main/res/values-it/strings.xml index f9dbd5b49cf..5076cc1e2fb 100644 --- a/feature/account/setup/src/main/res/values-it/strings.xml +++ b/feature/account/setup/src/main/res/values-it/strings.xml @@ -28,7 +28,7 @@ Non è riconosciuto come indirizzo email valido Mai Ricerca configurazione email - Opzioni di sincronizzazione + Opzioni di sincronizzazione Mostra notifiche Opzioni di visualizzazione K-9 Mail diff --git a/feature/account/setup/src/main/res/values-ja/strings.xml b/feature/account/setup/src/main/res/values-ja/strings.xml index b9dbc32af29..5b4fa2feb55 100644 --- a/feature/account/setup/src/main/res/values-ja/strings.xml +++ b/feature/account/setup/src/main/res/values-ja/strings.xml @@ -28,7 +28,7 @@ 有効なメールアドレスとして認識されませんでした。 確認しない メールの詳細を確認中 - 同期設定 + 同期設定 通知を表示する 表示設定 K-9 Mail diff --git a/feature/account/setup/src/main/res/values-nb-rNO/strings.xml b/feature/account/setup/src/main/res/values-nb-rNO/strings.xml index 1af6b044b87..37d000d26c6 100644 --- a/feature/account/setup/src/main/res/values-nb-rNO/strings.xml +++ b/feature/account/setup/src/main/res/values-nb-rNO/strings.xml @@ -28,7 +28,7 @@ E-postadressen er ugyldig. Aldri Finner e-postdetaljer … - Synkroniseringsalternativer + Synkroniseringsalternativer Vis merknader Visningsalternativer K-9 Mail diff --git a/feature/account/setup/src/main/res/values-nl/strings.xml b/feature/account/setup/src/main/res/values-nl/strings.xml index 6b6cb3f1065..5f21e532b83 100644 --- a/feature/account/setup/src/main/res/values-nl/strings.xml +++ b/feature/account/setup/src/main/res/values-nl/strings.xml @@ -28,7 +28,7 @@ Dit is niet als een geldig e-mailadres herkend. Nooit E-maildetails zoeken - Synchronisatieopties + Synchronisatieopties Meldingen tonen Weergaveopties K-9 Mail diff --git a/feature/account/setup/src/main/res/values-pl/strings.xml b/feature/account/setup/src/main/res/values-pl/strings.xml index f7f9794da05..550b294cd26 100644 --- a/feature/account/setup/src/main/res/values-pl/strings.xml +++ b/feature/account/setup/src/main/res/values-pl/strings.xml @@ -28,7 +28,7 @@ Nie jest rozpoznawany jako prawidłowy adres e-mail. Nigdy Znajdowanie szczegółów poczty e-mail - Opcje synchronizacji + Opcje synchronizacji Pokaż powiadomienia Opcje wyświetlania K-9 Mail diff --git a/feature/account/setup/src/main/res/values-pt-rBR/strings.xml b/feature/account/setup/src/main/res/values-pt-rBR/strings.xml index 9a1b051e873..b3ebee23eef 100644 --- a/feature/account/setup/src/main/res/values-pt-rBR/strings.xml +++ b/feature/account/setup/src/main/res/values-pt-rBR/strings.xml @@ -28,7 +28,7 @@ Este endereço eletrônico é inválido. Nunca Encontrando configuração do email - Opções de sincronização + Opções de sincronização Mostrar notificações Opções de visualização K-9 Mail diff --git a/feature/account/setup/src/main/res/values-pt-rPT/strings.xml b/feature/account/setup/src/main/res/values-pt-rPT/strings.xml index 3c8301fd28b..0cb6613aabe 100644 --- a/feature/account/setup/src/main/res/values-pt-rPT/strings.xml +++ b/feature/account/setup/src/main/res/values-pt-rPT/strings.xml @@ -33,7 +33,7 @@ Não reconhecido como um endereço de email válido. Nunca A procurar detalhes do email - Opções de sincronização + Opções de sincronização Mostrar notificações Opções de visualização K-9 Mail diff --git a/feature/account/setup/src/main/res/values-ro/strings.xml b/feature/account/setup/src/main/res/values-ro/strings.xml index 22570914907..f5fd243291c 100644 --- a/feature/account/setup/src/main/res/values-ro/strings.xml +++ b/feature/account/setup/src/main/res/values-ro/strings.xml @@ -28,7 +28,7 @@ Aceasta nu este recunoscută ca o adresă de e-mail validă. Niciodată Găsirea detaliilor de e-mail - Opțiuni de sincronizare + Opțiuni de sincronizare Afișare notificări Opțiuni de afișare K-9 Mail diff --git a/feature/account/setup/src/main/res/values-sq/strings.xml b/feature/account/setup/src/main/res/values-sq/strings.xml index aed661965bf..2bd9cd9bea8 100644 --- a/feature/account/setup/src/main/res/values-sq/strings.xml +++ b/feature/account/setup/src/main/res/values-sq/strings.xml @@ -29,7 +29,7 @@ Kjo nuk njihet si adresë email e vlefshme. Kurrë Po gjenden hollësi email-i - Mundësi njëkohësimi + Mundësi njëkohësimi Shfaq njoftime Mundësi shfaqjeje Kjo adresë email nuk lejohet. diff --git a/feature/account/setup/src/main/res/values-sv/strings.xml b/feature/account/setup/src/main/res/values-sv/strings.xml index 424f7c082e4..2859f2a16b4 100644 --- a/feature/account/setup/src/main/res/values-sv/strings.xml +++ b/feature/account/setup/src/main/res/values-sv/strings.xml @@ -25,7 +25,7 @@ Nätverk Aldrig Hitta e-postdetaljer - Synkroniseringsinställningar + Synkroniseringsinställningar Visa notiser Visningsinställningar StartTLS diff --git a/feature/account/setup/src/main/res/values-tr/strings.xml b/feature/account/setup/src/main/res/values-tr/strings.xml index ff12d47ea9d..2bea22e9d54 100644 --- a/feature/account/setup/src/main/res/values-tr/strings.xml +++ b/feature/account/setup/src/main/res/values-tr/strings.xml @@ -25,7 +25,7 @@ Bu geçerli bir e-posta adresi olarak tanınmıyor. E-posta bilgileri bulunuyor - Eşzamanlama seçenekleri + Eşzamanlama seçenekleri Görüntüleme seçenekleri K-9 Posta Denetleme sıklığı diff --git a/feature/account/setup/src/main/res/values-vi/strings.xml b/feature/account/setup/src/main/res/values-vi/strings.xml index 40b542d083b..9f6797ebc7d 100644 --- a/feature/account/setup/src/main/res/values-vi/strings.xml +++ b/feature/account/setup/src/main/res/values-vi/strings.xml @@ -30,7 +30,7 @@ Đây không được công nhận là địa chỉ email hợp lệ. Không bao giờ Tìm chi tiết e-mail - Tùy chọn đồng bộ hóa + Tùy chọn đồng bộ hóa Hiển thị thông báo Tùy chọn hiển thị Thư K-9 diff --git a/feature/account/setup/src/main/res/values-zh-rCN/strings.xml b/feature/account/setup/src/main/res/values-zh-rCN/strings.xml index 1c5d47157a3..48009f85688 100644 --- a/feature/account/setup/src/main/res/values-zh-rCN/strings.xml +++ b/feature/account/setup/src/main/res/values-zh-rCN/strings.xml @@ -28,7 +28,7 @@ 输入值未被识别为有效的邮箱地址。 从不 寻找电子邮件详情中 - 同步选项 + 同步选项 显示通知 显示选项 K-9 Mail diff --git a/feature/account/setup/src/main/res/values-zh-rTW/strings.xml b/feature/account/setup/src/main/res/values-zh-rTW/strings.xml index 9fc24868e9a..2b88250fd5e 100644 --- a/feature/account/setup/src/main/res/values-zh-rTW/strings.xml +++ b/feature/account/setup/src/main/res/values-zh-rTW/strings.xml @@ -28,7 +28,7 @@ 輸入值未被識別為有效的電子郵件地址。 從不 正在尋找電子郵件詳細資料 - 同步選項 + 同步選項 顯示通知 顯示選項 K-9 Mail diff --git a/feature/account/setup/src/main/res/values/strings.xml b/feature/account/setup/src/main/res/values/strings.xml index e39bf586401..34e3f657026 100644 --- a/feature/account/setup/src/main/res/values/strings.xml +++ b/feature/account/setup/src/main/res/values/strings.xml @@ -48,7 +48,7 @@ Your name is required. Email signature Email signature name can\'t be blank. - Sync options + Sync options Check frequency Never Number of messages to display From 63d834d18cea5ed7e1500fdca10002fc07e25501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 10 Jan 2024 13:28:10 +0100 Subject: [PATCH 3/6] Add SyncOptionsScreen --- .../data/InMemoryAccountStateRepository.kt | 5 + .../common/domain/AccountDomainContract.kt | 4 + .../common/domain/entity/AccountState.kt | 1 + .../domain/entity/AccountSyncOptions.kt | 7 + .../preview/PreviewAccountStateRepository.kt | 4 + .../setup/navigation/AccountSetupNavHost.kt | 8 +- .../ui/options/sync/SyncOptionsContent.kt | 130 ++++++++++++++++++ .../ui/options/sync/SyncOptionsContract.kt | 32 +++++ .../ui/options/sync/SyncOptionsScreen.kt | 91 ++++++++++++ .../ui/options/sync/SyncOptionsStateMapper.kt | 28 ++++ .../options/sync/SyncOptionsStringMapper.kt | 31 +++++ .../ui/options/sync/SyncOptionsViewModel.kt | 59 ++++++++ .../options/sync/FakeSyncOptionsViewModel.kt | 22 +++ .../options/sync/SyncOptionsScreenKtTest.kt | 45 ++++++ .../sync/SyncOptionsStateMapperKtTest.kt | 30 ++++ .../ui/options/sync/SyncOptionsStateTest.kt | 24 ++++ .../options/sync/SyncOptionsViewModelTest.kt | 123 +++++++++++++++++ 17 files changed, 640 insertions(+), 4 deletions(-) create mode 100644 feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountSyncOptions.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContent.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContract.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreen.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStringMapper.kt create mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/FakeSyncOptionsViewModel.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreenKtTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapperKtTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateTest.kt create mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModelTest.kt diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt index 9f4f8440e0b..aa4c4bb1dc8 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt @@ -4,6 +4,7 @@ import app.k9mail.feature.account.common.domain.AccountDomainContract 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.SpecialFolderSettings import com.fsck.k9.mail.ServerSettings @@ -50,6 +51,10 @@ class InMemoryAccountStateRepository( state = state.copy(displayOptions = displayOptions) } + override fun setSyncOptions(syncOptions: AccountSyncOptions) { + state = state.copy(syncOptions = syncOptions) + } + override fun clear() { state = AccountState() } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt index ed58add77de..6579dd3e809 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt @@ -3,12 +3,14 @@ package app.k9mail.feature.account.common.domain 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.SpecialFolderSettings import com.fsck.k9.mail.ServerSettings interface AccountDomainContract { + @Suppress("TooManyFunctions") interface AccountStateRepository { fun getState(): AccountState @@ -28,6 +30,8 @@ interface AccountDomainContract { fun setDisplayOptions(displayOptions: AccountDisplayOptions) + fun setSyncOptions(syncOptions: AccountSyncOptions) + fun clear() } } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt index 1259bdc59b4..8a9dd9ca634 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt @@ -11,4 +11,5 @@ data class AccountState( val specialFolderSettings: SpecialFolderSettings? = null, val options: AccountOptions? = null, val displayOptions: AccountDisplayOptions? = null, + val syncOptions: AccountSyncOptions? = null, ) diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountSyncOptions.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountSyncOptions.kt new file mode 100644 index 00000000000..6d79a4dfecc --- /dev/null +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountSyncOptions.kt @@ -0,0 +1,7 @@ +package app.k9mail.feature.account.common.domain.entity + +data class AccountSyncOptions( + val checkFrequencyInMinutes: Int, + val messageDisplayCount: Int, + val showNotification: Boolean, +) diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt index c119bfba53b..a0bc0f1ab3f 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt @@ -4,12 +4,14 @@ import app.k9mail.feature.account.common.domain.AccountDomainContract 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 app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings import com.fsck.k9.mail.AuthType import com.fsck.k9.mail.ServerSettings +@Suppress("TooManyFunctions") class PreviewAccountStateRepository : AccountDomainContract.AccountStateRepository { override fun getState(): AccountState = AccountState( @@ -52,5 +54,7 @@ class PreviewAccountStateRepository : AccountDomainContract.AccountStateReposito override fun setDisplayOptions(displayOptions: AccountDisplayOptions) = Unit + override fun setSyncOptions(syncOptions: AccountSyncOptions) = Unit + override fun clear() = Unit } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt index 3a356615b1b..dc1a996e48e 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/navigation/AccountSetupNavHost.kt @@ -20,10 +20,10 @@ import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryScr import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryViewModel import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountScreen import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountViewModel -import app.k9mail.feature.account.setup.ui.options.AccountOptionsScreen -import app.k9mail.feature.account.setup.ui.options.AccountOptionsViewModel import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsScreen import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsViewModel +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsScreen +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsViewModel import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersScreen import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersViewModel import org.koin.androidx.compose.koinViewModel @@ -146,10 +146,10 @@ fun AccountSetupNavHost( } composable(route = NESTED_NAVIGATION_SYNC_OPTIONS) { - AccountOptionsScreen( + SyncOptionsScreen( onNext = { navController.navigate(NESTED_NAVIGATION_CREATE_ACCOUNT) }, onBack = { navController.popBackStack() }, - viewModel = koinViewModel(), + viewModel = koinViewModel(), ) } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContent.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContent.kt new file mode 100644 index 00000000000..ea404137d6a --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContent.kt @@ -0,0 +1,130 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.consumeWindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.imePadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredHeight +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.ui.compose.designsystem.atom.text.TextOverline +import app.k9mail.core.ui.compose.designsystem.molecule.input.SelectInput +import app.k9mail.core.ui.compose.designsystem.molecule.input.SwitchInput +import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.core.ui.compose.theme.MainTheme +import app.k9mail.core.ui.compose.theme.ThunderbirdTheme +import app.k9mail.feature.account.common.ui.item.defaultHeadlineItemPadding +import app.k9mail.feature.account.common.ui.item.defaultItemPadding +import app.k9mail.feature.account.setup.R +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State + +@OptIn(ExperimentalLayoutApi::class) +@Suppress("LongMethod") +@Composable +internal fun SyncOptionsContent( + state: State, + onEvent: (Event) -> Unit, + contentPadding: PaddingValues, + modifier: Modifier = Modifier, +) { + val resources = LocalContext.current.resources + + ResponsiveWidthContainer( + modifier = Modifier + .testTag("SyncOptionsContent") + .consumeWindowInsets(contentPadding) + .padding(contentPadding) + .then(modifier), + ) { + LazyColumn( + modifier = Modifier + .fillMaxSize() + .imePadding(), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default), + ) { + item { + TextOverline( + text = stringResource(id = R.string.account_setup_options_section_sync_options), + modifier = Modifier + .fillMaxWidth() + .padding(defaultHeadlineItemPadding()), + ) + } + + item { + SelectInput( + options = EmailCheckFrequency.all(), + optionToStringTransformation = { it.toResourceString(resources) }, + selectedOption = state.checkFrequency, + onOptionChange = { onEvent(Event.OnCheckFrequencyChanged(it)) }, + label = stringResource(id = R.string.account_setup_options_account_check_frequency_label), + contentPadding = defaultItemPadding(), + ) + } + + item { + SelectInput( + options = EmailDisplayCount.all(), + optionToStringTransformation = { it.toResourceString(resources) }, + selectedOption = state.messageDisplayCount, + onOptionChange = { onEvent(Event.OnMessageDisplayCountChanged(it)) }, + label = stringResource(id = R.string.account_setup_options_email_display_count_label), + contentPadding = defaultItemPadding(), + ) + } + + item { + SwitchInput( + text = stringResource(id = R.string.account_setup_options_show_notifications_label), + checked = state.showNotification, + onCheckedChange = { onEvent(Event.OnShowNotificationChanged(it)) }, + contentPadding = defaultItemPadding(), + ) + } + + item { + Spacer(modifier = Modifier.requiredHeight(MainTheme.sizes.smaller)) + } + } + } +} + +@Composable +@Preview(showBackground = true) +internal fun SyncOptionsContentK9Preview() { + K9Theme { + SyncOptionsContent( + state = State(), + onEvent = {}, + contentPadding = PaddingValues(), + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun SyncOptionsContentThunderbirdPreview() { + ThunderbirdTheme { + SyncOptionsContent( + state = State(), + onEvent = {}, + contentPadding = PaddingValues(), + ) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContract.kt new file mode 100644 index 00000000000..25eb6a9b047 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsContract.kt @@ -0,0 +1,32 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount + +interface SyncOptionsContract { + + interface ViewModel : UnidirectionalViewModel + + data class State( + val checkFrequency: EmailCheckFrequency = EmailCheckFrequency.DEFAULT, + val messageDisplayCount: EmailDisplayCount = EmailDisplayCount.DEFAULT, + val showNotification: Boolean = false, + ) + + sealed interface Event { + data class OnCheckFrequencyChanged(val checkFrequency: EmailCheckFrequency) : Event + data class OnMessageDisplayCountChanged(val messageDisplayCount: EmailDisplayCount) : Event + data class OnShowNotificationChanged(val showNotification: Boolean) : Event + + data object LoadAccountState : Event + + data object OnNextClicked : Event + data object OnBackClicked : Event + } + + sealed interface Effect { + object NavigateNext : Effect + object NavigateBack : Effect + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreen.kt new file mode 100644 index 00000000000..f8162366f86 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreen.kt @@ -0,0 +1,91 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.tooling.preview.Preview +import app.k9mail.core.ui.compose.common.mvi.observe +import app.k9mail.core.ui.compose.designsystem.template.Scaffold +import app.k9mail.core.ui.compose.theme.K9Theme +import app.k9mail.core.ui.compose.theme.ThunderbirdTheme +import app.k9mail.feature.account.common.ui.AccountTopAppBar +import app.k9mail.feature.account.common.ui.WizardNavigationBar +import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository +import app.k9mail.feature.account.setup.R.string +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.ViewModel + +@Composable +internal fun SyncOptionsScreen( + onNext: () -> Unit, + onBack: () -> Unit, + viewModel: ViewModel, + modifier: Modifier = Modifier, +) { + val (state, dispatch) = viewModel.observe { effect -> + when (effect) { + Effect.NavigateBack -> onBack() + Effect.NavigateNext -> onNext() + } + } + + LaunchedEffect(key1 = Unit) { + dispatch(Event.LoadAccountState) + } + + BackHandler { + dispatch(Event.OnBackClicked) + } + + Scaffold( + topBar = { + AccountTopAppBar( + title = stringResource(id = string.account_setup_options_section_sync_options), + ) + }, + bottomBar = { + WizardNavigationBar( + onNextClick = { dispatch(Event.OnNextClicked) }, + onBackClick = { dispatch(Event.OnBackClicked) }, + ) + }, + modifier = modifier, + ) { innerPadding -> + SyncOptionsContent( + state = state.value, + onEvent = { dispatch(it) }, + contentPadding = innerPadding, + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun SyncOptionsScreenK9Preview() { + K9Theme { + SyncOptionsScreen( + onNext = {}, + onBack = {}, + viewModel = SyncOptionsViewModel( + accountStateRepository = PreviewAccountStateRepository(), + ), + ) + } +} + +@Composable +@Preview(showBackground = true) +internal fun SyncOptionsScreenThunderbirdPreview() { + ThunderbirdTheme { + SyncOptionsScreen( + onNext = {}, + onBack = {}, + viewModel = SyncOptionsViewModel( + accountStateRepository = PreviewAccountStateRepository(), + ), + ) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt new file mode 100644 index 00000000000..d6628d44ecd --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt @@ -0,0 +1,28 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.feature.account.common.domain.entity.AccountState +import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State + +internal fun AccountState.toAccountOptionsState(): State { + val options = options + return if (options == null) { + State() + } else { + State( + checkFrequency = EmailCheckFrequency.fromMinutes(options.checkFrequencyInMinutes), + messageDisplayCount = EmailDisplayCount.fromCount(options.messageDisplayCount), + showNotification = options.showNotification, + ) + } +} + +internal fun State.toAccountSyncOptions(): AccountSyncOptions { + return AccountSyncOptions( + checkFrequencyInMinutes = checkFrequency.minutes, + messageDisplayCount = messageDisplayCount.count, + showNotification = showNotification, + ) +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStringMapper.kt new file mode 100644 index 00000000000..e7d420671d0 --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStringMapper.kt @@ -0,0 +1,31 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import android.content.res.Resources +import app.k9mail.feature.account.setup.R +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount + +internal fun EmailDisplayCount.toResourceString(resources: Resources) = resources.getQuantityString( + R.plurals.account_setup_options_email_display_count_messages, + count, + count, +) + +@Suppress("MagicNumber") +internal fun EmailCheckFrequency.toResourceString(resources: Resources): String { + return when (minutes) { + -1 -> resources.getString(R.string.account_setup_options_email_check_frequency_never) + + in 1..59 -> resources.getQuantityString( + R.plurals.account_setup_options_email_check_frequency_minutes, + minutes, + minutes, + ) + + else -> resources.getQuantityString( + R.plurals.account_setup_options_email_check_frequency_hours, + (minutes / 60), + (minutes / 60), + ) + } +} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt new file mode 100644 index 00000000000..35c1673a44c --- /dev/null +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt @@ -0,0 +1,59 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.core.ui.compose.common.mvi.BaseViewModel +import app.k9mail.feature.account.common.domain.AccountDomainContract +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.ViewModel + +internal class SyncOptionsViewModel( + private val accountStateRepository: AccountDomainContract.AccountStateRepository, + initialState: State? = null, +) : BaseViewModel( + initialState = initialState ?: accountStateRepository.getState().toAccountOptionsState(), +), + ViewModel { + + override fun event(event: Event) { + when (event) { + Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState) + + is Event.OnCheckFrequencyChanged -> updateState { + it.copy( + checkFrequency = event.checkFrequency, + ) + } + + is Event.OnMessageDisplayCountChanged -> updateState { state -> + state.copy( + messageDisplayCount = event.messageDisplayCount, + ) + } + + is Event.OnShowNotificationChanged -> updateState { state -> + state.copy( + showNotification = event.showNotification, + ) + } + + Event.OnNextClicked -> submit() + Event.OnBackClicked -> navigateBack() + } + } + + private fun loadAccountState() { + updateState { + accountStateRepository.getState().toAccountOptionsState() + } + } + + private fun submit() { + accountStateRepository.setSyncOptions(state.value.toAccountSyncOptions()) + navigateNext() + } + + private fun navigateBack() = emitEffect(Effect.NavigateBack) + + private fun navigateNext() = emitEffect(Effect.NavigateNext) +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/FakeSyncOptionsViewModel.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/FakeSyncOptionsViewModel.kt new file mode 100644 index 00000000000..4ad539597a0 --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/FakeSyncOptionsViewModel.kt @@ -0,0 +1,22 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.core.ui.compose.common.mvi.BaseViewModel +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.ViewModel + +class FakeSyncOptionsViewModel( + initialState: State = State(), +) : BaseViewModel(initialState), ViewModel { + + val events = mutableListOf() + + override fun event(event: Event) { + events.add(event) + } + + fun effect(effect: Effect) { + emitEffect(effect) + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreenKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreenKtTest.kt new file mode 100644 index 00000000000..92c9698d4ae --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsScreenKtTest.kt @@ -0,0 +1,45 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.core.ui.compose.testing.ComposeTest +import app.k9mail.core.ui.compose.testing.setContent +import app.k9mail.core.ui.compose.theme.ThunderbirdTheme +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State +import assertk.assertThat +import assertk.assertions.isEqualTo +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class SyncOptionsScreenKtTest : ComposeTest() { + + @Test + fun `should delegate navigation effects`() = runTest { + val initialState = State() + val viewModel = FakeSyncOptionsViewModel(initialState) + var onNextCounter = 0 + var onBackCounter = 0 + + setContent { + ThunderbirdTheme { + SyncOptionsScreen( + onNext = { onNextCounter++ }, + onBack = { onBackCounter++ }, + viewModel = viewModel, + ) + } + } + + assertThat(onNextCounter).isEqualTo(0) + assertThat(onBackCounter).isEqualTo(0) + + viewModel.effect(Effect.NavigateNext) + + assertThat(onNextCounter).isEqualTo(1) + assertThat(onBackCounter).isEqualTo(0) + + viewModel.effect(Effect.NavigateBack) + + assertThat(onNextCounter).isEqualTo(1) + assertThat(onBackCounter).isEqualTo(1) + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapperKtTest.kt new file mode 100644 index 00000000000..eff8cd9d60f --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapperKtTest.kt @@ -0,0 +1,30 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.junit.Test + +class SyncOptionsStateMapperKtTest { + + @Test + fun `should map state to account options`() { + val state = SyncOptionsContract.State( + checkFrequency = EmailCheckFrequency.EVERY_2_HOURS, + messageDisplayCount = EmailDisplayCount.MESSAGES_100, + showNotification = true, + ) + + val result = state.toAccountSyncOptions() + + assertThat(result).isEqualTo( + AccountSyncOptions( + checkFrequencyInMinutes = 120, + messageDisplayCount = 100, + showNotification = true, + ), + ) + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateTest.kt new file mode 100644 index 00000000000..35a61e1cc65 --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateTest.kt @@ -0,0 +1,24 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.junit.Test + +class SyncOptionsStateTest { + + @Test + fun `should set default values`() { + val state = State() + + assertThat(state).isEqualTo( + State( + checkFrequency = EmailCheckFrequency.DEFAULT, + messageDisplayCount = EmailDisplayCount.DEFAULT, + showNotification = false, + ), + ) + } +} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModelTest.kt new file mode 100644 index 00000000000..7ecae08e645 --- /dev/null +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModelTest.kt @@ -0,0 +1,123 @@ +package app.k9mail.feature.account.setup.ui.options.sync + +import app.cash.turbine.testIn +import app.k9mail.core.ui.compose.testing.MainDispatcherRule +import app.k9mail.core.ui.compose.testing.mvi.assertThatAndEffectTurbineConsumed +import app.k9mail.core.ui.compose.testing.mvi.eventStateTest +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.AccountSyncOptions +import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency +import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Effect +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.Event +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State +import assertk.assertThat +import assertk.assertions.assertThatAndTurbinesConsumed +import assertk.assertions.isEqualTo +import kotlinx.coroutines.test.runTest +import org.junit.Rule +import org.junit.Test + +class SyncOptionsViewModelTest { + + @get:Rule + val mainDispatcherRule = MainDispatcherRule() + + private val testSubject = SyncOptionsViewModel( + accountStateRepository = InMemoryAccountStateRepository(), + ) + + @Test + fun `should change state when OnCheckFrequencyChanged event is received`() = runTest { + eventStateTest( + viewModel = testSubject, + initialState = State(), + event = Event.OnCheckFrequencyChanged(EmailCheckFrequency.EVERY_12_HOURS), + expectedState = State(checkFrequency = EmailCheckFrequency.EVERY_12_HOURS), + coroutineScope = backgroundScope, + ) + } + + @Test + fun `should change state when OnMessageDisplayCountChanged event is received`() = runTest { + eventStateTest( + viewModel = testSubject, + initialState = State(), + event = Event.OnMessageDisplayCountChanged(EmailDisplayCount.MESSAGES_1000), + expectedState = State(messageDisplayCount = EmailDisplayCount.MESSAGES_1000), + coroutineScope = backgroundScope, + ) + } + + @Test + fun `should change state when OnShowNotificationChanged event is received`() = runTest { + eventStateTest( + viewModel = testSubject, + initialState = State(), + event = Event.OnShowNotificationChanged(true), + expectedState = State(showNotification = true), + coroutineScope = backgroundScope, + ) + } + + @Test + fun `should store state and emit NavigateNext effect when OnNextClicked event received and input valid`() = + runTest { + val accountStateRepository = InMemoryAccountStateRepository() + val initialState = State( + checkFrequency = EmailCheckFrequency.EVERY_HOUR, + messageDisplayCount = EmailDisplayCount.MESSAGES_1000, + showNotification = true, + ) + val viewModel = SyncOptionsViewModel( + accountStateRepository = accountStateRepository, + initialState = initialState, + ) + val turbines = turbinesWithInitialStateCheck( + viewModel = viewModel, + initialState = initialState, + ) + + viewModel.event(Event.OnNextClicked) + + turbines.assertThatAndEffectTurbineConsumed { + isEqualTo(Effect.NavigateNext) + } + + assertThat(accountStateRepository.getState()).isEqualTo( + AccountState( + syncOptions = AccountSyncOptions( + checkFrequencyInMinutes = 60, + messageDisplayCount = 1000, + showNotification = true, + ), + ), + ) + } + + @Test + fun `should emit NavigateBack effect when OnBackClicked event received`() = runTest { + val viewModel = testSubject + val stateTurbine = viewModel.state.testIn(backgroundScope) + val effectTurbine = viewModel.effect.testIn(backgroundScope) + val turbines = listOf(stateTurbine, effectTurbine) + + assertThatAndTurbinesConsumed( + actual = stateTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(State()) + } + + viewModel.event(Event.OnBackClicked) + + assertThatAndTurbinesConsumed( + actual = effectTurbine.awaitItem(), + turbines = turbines, + ) { + isEqualTo(Effect.NavigateBack) + } + } +} From c50314ffbe8ad671faa575966bfb7841cc0bbe73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 10 Jan 2024 13:32:18 +0100 Subject: [PATCH 4/6] Rename `AuthorizationState` property to value --- .../k9mail/feature/preview/account/InMemoryAccountStore.kt | 4 ++-- .../com/fsck/k9/account/AccountServerSettingsUpdater.kt | 2 +- .../com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt | 6 +++--- .../account/common/data/InMemoryAccountStateRepository.kt | 2 +- .../account/common/domain/entity/AuthorizationState.kt | 2 +- .../account/common/domain/entity/AuthorizationStateTest.kt | 2 +- .../k9mail/feature/account/oauth/data/AuthStateExtension.kt | 4 ++-- .../feature/account/oauth/ui/AccountOAuthViewModelTest.kt | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt index 462b75e1987..e0d4f0a724e 100644 --- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt +++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt @@ -39,12 +39,12 @@ class InMemoryAccountStore( accountMap[account.uuid] = if (isIncoming) { account.copy( incomingServerSettings = serverSettings, - authorizationState = authorizationState?.state, + authorizationState = authorizationState?.value, ) } else { account.copy( outgoingServerSettings = serverSettings, - authorizationState = authorizationState?.state, + authorizationState = authorizationState?.value, ) } diff --git a/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt b/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt index 71e0163d902..a2ec8845244 100644 --- a/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt +++ b/app/k9mail/src/main/java/com/fsck/k9/account/AccountServerSettingsUpdater.kt @@ -67,7 +67,7 @@ class AccountServerSettingsUpdater( account.outgoingServerSettings = serverSettings } - account.oAuthState = authorizationState?.state + account.oAuthState = authorizationState?.value accountManager.saveAccount(account) diff --git a/app/k9mail/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt b/app/k9mail/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt index 861a64b214e..6255ea3ab2e 100644 --- a/app/k9mail/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt +++ b/app/k9mail/src/test/java/com/fsck/k9/account/AccountServerSettingsUpdaterTest.kt @@ -53,7 +53,7 @@ class AccountServerSettingsUpdaterTest { assertThat(k9Account).isNotNull().all { prop(K9Account::incomingServerSettings).isEqualTo(updatedIncomingServerSettings) prop(K9Account::outgoingServerSettings).isEqualTo(OUTGOING_SERVER_SETTINGS) - prop(K9Account::oAuthState).isEqualTo(updatedAuthorizationState.state) + prop(K9Account::oAuthState).isEqualTo(updatedAuthorizationState.value) } } @@ -77,7 +77,7 @@ class AccountServerSettingsUpdaterTest { assertThat(k9Account).isNotNull().all { prop(K9Account::incomingServerSettings).isEqualTo(INCOMING_SERVER_SETTINGS) prop(K9Account::outgoingServerSettings).isEqualTo(updatedOutgoingServerSettings) - prop(K9Account::oAuthState).isEqualTo(updatedAuthorizationState.state) + prop(K9Account::oAuthState).isEqualTo(updatedAuthorizationState.value) } } @@ -136,7 +136,7 @@ class AccountServerSettingsUpdaterTest { ).apply { incomingServerSettings = INCOMING_SERVER_SETTINGS outgoingServerSettings = OUTGOING_SERVER_SETTINGS - oAuthState = AUTHORIZATION_STATE.state + oAuthState = AUTHORIZATION_STATE.value } } } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt index aa4c4bb1dc8..35e62920b7a 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt @@ -60,7 +60,7 @@ class InMemoryAccountStateRepository( } override fun getAuthorizationState(): String? { - return state.authorizationState?.state + return state.authorizationState?.value } override fun updateAuthorizationState(authorizationState: String?) { diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt index 5d4e46fef52..74e410136bd 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationState.kt @@ -1,5 +1,5 @@ package app.k9mail.feature.account.common.domain.entity data class AuthorizationState( - val state: String? = null, + val value: String? = null, ) diff --git a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt index 1311b660768..0ccdc696b29 100644 --- a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt +++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AuthorizationStateTest.kt @@ -10,6 +10,6 @@ class AuthorizationStateTest { fun `should default to null state`() { val authorizationState = AuthorizationState() - assertThat(authorizationState.state).isNull() + assertThat(authorizationState.value).isNull() } } diff --git a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt index daba4ec5788..b02846f111c 100644 --- a/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt +++ b/feature/account/oauth/src/main/kotlin/app/k9mail/feature/account/oauth/data/AuthStateExtension.kt @@ -7,7 +7,7 @@ import timber.log.Timber fun AuthState.toAuthorizationState(): AuthorizationState { return try { - AuthorizationState(state = jsonSerializeString()) + AuthorizationState(value = jsonSerializeString()) } catch (e: JSONException) { Timber.e(e, "Error serializing AuthorizationState") AuthorizationState() @@ -16,7 +16,7 @@ fun AuthState.toAuthorizationState(): AuthorizationState { fun AuthorizationState.toAuthState(): AuthState { return try { - state?.let { AuthState.jsonDeserialize(it) } ?: AuthState() + value?.let { AuthState.jsonDeserialize(it) } ?: AuthState() } catch (e: JSONException) { Timber.e(e, "Error deserializing AuthorizationState") AuthState() diff --git a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt index a7769480b0d..daf97c5a617 100644 --- a/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt +++ b/feature/account/oauth/src/test/kotlin/app/k9mail/feature/account/oauth/ui/AccountOAuthViewModelTest.kt @@ -169,7 +169,7 @@ class AccountOAuthViewModelTest { @Test fun `should finish OAuth sign in when onOAuthResult received with success`() = runTest { val initialState = defaultState - val authorizationState = AuthorizationState(state = "state") + val authorizationState = AuthorizationState(value = "state") val testSubject = createTestSubject( authorizationResult = AuthorizationResult.Success(authorizationState), initialState = initialState, From 38a1be4f07cd7062d9eca1454bfdb44473fffca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 10 Jan 2024 14:00:06 +0100 Subject: [PATCH 5/6] Change `AccountState` to use display and sync options --- .../preview/account/InMemoryAccountStore.kt | 22 +++++++++- .../data/InMemoryAccountStateRepository.kt | 5 --- .../common/domain/AccountDomainContract.kt | 3 -- .../common/domain/entity/AccountState.kt | 1 - .../preview/PreviewAccountStateRepository.kt | 3 -- .../InMemoryAccountStateRepositoryTest.kt | 40 ++++++++++++++----- .../common/domain/entity/AccountStateTest.kt | 4 +- .../domain/usecase/GetAccountStateTest.kt | 17 +++++++- .../domain/usecase/LoadAccountStateTest.kt | 11 +++-- .../domain/usecase/SaveServerSettingsTest.kt | 11 +++-- .../account/setup/domain/DomainContract.kt | 15 +------ .../setup/domain/usecase/CreateAccount.kt | 33 +++++++-------- .../AccountAutoDiscoveryStateMapper.kt | 3 +- .../ui/createaccount/CreateAccountScreen.kt | 2 +- .../createaccount/CreateAccountViewModel.kt | 12 +----- .../display/DisplayOptionsStateMapper.kt | 4 +- .../display/DisplayOptionsViewModel.kt | 4 +- .../ui/options/sync/SyncOptionsStateMapper.kt | 4 +- .../ui/options/sync/SyncOptionsViewModel.kt | 4 +- .../setup/domain/usecase/CreateAccountTest.kt | 29 +++++++++----- .../AccountAutoDiscoveryStateMapperKtTest.kt | 3 +- .../AccountAutoDiscoveryViewModelTest.kt | 3 +- .../CreateAccountViewModelTest.kt | 22 +++++----- .../ui/createaccount/FakeCreateAccount.kt | 37 ++--------------- 24 files changed, 151 insertions(+), 141 deletions(-) diff --git a/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt b/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt index e0d4f0a724e..22d416bbb7f 100644 --- a/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt +++ b/app-feature-preview/src/main/java/app/k9mail/feature/preview/account/InMemoryAccountStore.kt @@ -2,7 +2,10 @@ package app.k9mail.feature.preview.account import app.k9mail.feature.account.common.AccountCommonExternalContract.AccountStateLoader import app.k9mail.feature.account.common.domain.entity.Account +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.edit.AccountEditExternalContract.AccountServerSettingsUpdater import app.k9mail.feature.account.edit.AccountEditExternalContract.AccountUpdaterFailure @@ -59,7 +62,24 @@ class InMemoryAccountStore( incomingServerSettings = account.incomingServerSettings, outgoingServerSettings = account.outgoingServerSettings, authorizationState = account.authorizationState?.let { AuthorizationState(it) }, - options = account.options, + displayOptions = mapToDisplayOptions(account.options), + syncOptions = mapToSyncOptions(account.options), + ) + } + + private fun mapToDisplayOptions(options: AccountOptions): AccountDisplayOptions { + return AccountDisplayOptions( + accountName = options.accountName, + displayName = options.displayName, + emailSignature = options.emailSignature, + ) + } + + private fun mapToSyncOptions(options: AccountOptions): AccountSyncOptions { + return AccountSyncOptions( + checkFrequencyInMinutes = options.checkFrequencyInMinutes, + messageDisplayCount = options.messageDisplayCount, + showNotification = options.showNotification, ) } } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt index 35e62920b7a..be713c17ff1 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepository.kt @@ -2,7 +2,6 @@ package app.k9mail.feature.account.common.data import app.k9mail.feature.account.common.domain.AccountDomainContract 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 @@ -43,10 +42,6 @@ class InMemoryAccountStateRepository( state = state.copy(specialFolderSettings = specialFolderSettings) } - override fun setOptions(options: AccountOptions) { - state = state.copy(options = options) - } - override fun setDisplayOptions(displayOptions: AccountDisplayOptions) { state = state.copy(displayOptions = displayOptions) } diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt index 6579dd3e809..a5820f2113e 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/AccountDomainContract.kt @@ -1,7 +1,6 @@ package app.k9mail.feature.account.common.domain 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 @@ -26,8 +25,6 @@ interface AccountDomainContract { fun setSpecialFolderSettings(specialFolderSettings: SpecialFolderSettings) - fun setOptions(options: AccountOptions) - fun setDisplayOptions(displayOptions: AccountDisplayOptions) fun setSyncOptions(syncOptions: AccountSyncOptions) diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt index 8a9dd9ca634..90ae5ceafe3 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/domain/entity/AccountState.kt @@ -9,7 +9,6 @@ data class AccountState( val outgoingServerSettings: ServerSettings? = null, val authorizationState: AuthorizationState? = null, val specialFolderSettings: SpecialFolderSettings? = null, - val options: AccountOptions? = null, val displayOptions: AccountDisplayOptions? = null, val syncOptions: AccountSyncOptions? = null, ) diff --git a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt index a0bc0f1ab3f..bcb1dae0579 100644 --- a/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt +++ b/feature/account/common/src/main/kotlin/app/k9mail/feature/account/common/ui/preview/PreviewAccountStateRepository.kt @@ -2,7 +2,6 @@ package app.k9mail.feature.account.common.ui.preview import app.k9mail.feature.account.common.domain.AccountDomainContract 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 @@ -50,8 +49,6 @@ class PreviewAccountStateRepository : AccountDomainContract.AccountStateReposito override fun setSpecialFolderSettings(specialFolderSettings: SpecialFolderSettings) = Unit - override fun setOptions(options: AccountOptions) = Unit - override fun setDisplayOptions(displayOptions: AccountDisplayOptions) = Unit override fun setSyncOptions(syncOptions: AccountSyncOptions) = Unit diff --git a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt index 5d52ec49352..b8c6a2d1ccf 100644 --- a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt +++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/data/InMemoryAccountStateRepositoryTest.kt @@ -1,7 +1,8 @@ package app.k9mail.feature.account.common.data -import app.k9mail.feature.account.common.domain.entity.AccountOptions +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 assertk.assertThat import assertk.assertions.isEqualTo @@ -25,7 +26,8 @@ class InMemoryAccountStateRepositoryTest { incomingServerSettings = null, outgoingServerSettings = null, authorizationState = null, - options = null, + displayOptions = null, + syncOptions = null, ), ) } @@ -39,7 +41,8 @@ class InMemoryAccountStateRepositoryTest { incomingServerSettings = INCOMING_SERVER_SETTINGS, outgoingServerSettings = OUTGOING_SERVER_SETTINGS, authorizationState = AuthorizationState("authorizationState"), - options = OPTIONS, + displayOptions = DISPLAY_OPTIONS, + syncOptions = SYNC_OPTIONS, ), ) val newState = AccountState( @@ -48,10 +51,12 @@ class InMemoryAccountStateRepositoryTest { incomingServerSettings = INCOMING_SERVER_SETTINGS.copy(host = "imap2.example.org"), outgoingServerSettings = OUTGOING_SERVER_SETTINGS.copy(host = "smtp2.example.org"), authorizationState = AuthorizationState("authorizationState2"), - options = OPTIONS.copy( + displayOptions = DISPLAY_OPTIONS.copy( accountName = "accountName2", displayName = "displayName2", emailSignature = "emailSignature2", + ), + syncOptions = SYNC_OPTIONS.copy( checkFrequencyInMinutes = 50, messageDisplayCount = 60, showNotification = false, @@ -104,13 +109,21 @@ class InMemoryAccountStateRepositoryTest { } @Test - fun `should set options`() { + fun `should set display options`() { val testSubject = InMemoryAccountStateRepository() - testSubject.setOptions(OPTIONS) + testSubject.setDisplayOptions(DISPLAY_OPTIONS) - assertThat(testSubject.getState().options) - .isEqualTo(OPTIONS) + assertThat(testSubject.getState().displayOptions).isEqualTo(DISPLAY_OPTIONS) + } + + @Test + fun `should set sync options`() { + val testSubject = InMemoryAccountStateRepository() + + testSubject.setSyncOptions(SYNC_OPTIONS) + + assertThat(testSubject.getState().syncOptions).isEqualTo(SYNC_OPTIONS) } @Test @@ -122,7 +135,8 @@ class InMemoryAccountStateRepositoryTest { incomingServerSettings = INCOMING_SERVER_SETTINGS, outgoingServerSettings = OUTGOING_SERVER_SETTINGS, authorizationState = AuthorizationState("authorizationState"), - options = OPTIONS, + displayOptions = DISPLAY_OPTIONS, + syncOptions = SYNC_OPTIONS, ), ) @@ -135,7 +149,8 @@ class InMemoryAccountStateRepositoryTest { incomingServerSettings = null, outgoingServerSettings = null, authorizationState = null, - options = null, + displayOptions = null, + syncOptions = null, ), ) } @@ -163,10 +178,13 @@ class InMemoryAccountStateRepositoryTest { null, ) - val OPTIONS = AccountOptions( + val DISPLAY_OPTIONS = AccountDisplayOptions( accountName = "accountName", displayName = "displayName", emailSignature = "emailSignature", + ) + + val SYNC_OPTIONS = AccountSyncOptions( checkFrequencyInMinutes = 10, messageDisplayCount = 20, showNotification = true, diff --git a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt index dd3c0c4e975..6c0405f1839 100644 --- a/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt +++ b/feature/account/common/src/test/kotlin/app/k9mail/feature/account/common/domain/entity/AccountStateTest.kt @@ -17,7 +17,9 @@ class AccountStateTest { prop(AccountState::incomingServerSettings).isNull() prop(AccountState::outgoingServerSettings).isNull() prop(AccountState::authorizationState).isNull() - prop(AccountState::options).isNull() + prop(AccountState::specialFolderSettings).isNull() + prop(AccountState::displayOptions).isNull() + prop(AccountState::syncOptions).isNull() } } } diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/GetAccountStateTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/GetAccountStateTest.kt index e210e26c19b..e063fac102e 100644 --- a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/GetAccountStateTest.kt +++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/GetAccountStateTest.kt @@ -1,8 +1,10 @@ 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 @@ -77,13 +79,26 @@ class GetAccountStateTest { 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, - options = OPTIONS, + displayOptions = DISPLAY_OPTIONS, + syncOptions = SYNC_OPTIONS, ) } } diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt index 0d6f66397e3..77a77864bca 100644 --- a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt +++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/LoadAccountStateTest.kt @@ -1,8 +1,9 @@ package app.k9mail.feature.account.edit.domain.usecase import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository -import app.k9mail.feature.account.common.domain.entity.AccountOptions +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 @@ -72,10 +73,13 @@ class LoadAccountStateTest { val AUTHORIZATION_STATE = AuthorizationState("authorization state") - val OPTIONS = AccountOptions( + val DISPLAY_OPTIONS = AccountDisplayOptions( accountName = "accountName", displayName = "displayName", emailSignature = null, + ) + + val SYNC_OPTIONS = AccountSyncOptions( checkFrequencyInMinutes = 15, messageDisplayCount = 25, showNotification = true, @@ -87,7 +91,8 @@ class LoadAccountStateTest { incomingServerSettings = INCOMING_SERVER_SETTINGS, outgoingServerSettings = OUTGOING_SERVER_SETTINGS, authorizationState = AUTHORIZATION_STATE, - options = OPTIONS, + displayOptions = DISPLAY_OPTIONS, + syncOptions = SYNC_OPTIONS, ) } } diff --git a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/SaveServerSettingsTest.kt b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/SaveServerSettingsTest.kt index da6250c8c9e..ca414a0bea7 100644 --- a/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/SaveServerSettingsTest.kt +++ b/feature/account/edit/src/test/kotlin/app/k9mail/feature/account/edit/domain/usecase/SaveServerSettingsTest.kt @@ -1,7 +1,8 @@ package app.k9mail.feature.account.edit.domain.usecase -import app.k9mail.feature.account.common.domain.entity.AccountOptions +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 @@ -143,10 +144,13 @@ class SaveServerSettingsTest { val AUTHORIZATION_STATE = AuthorizationState("authorization state") - val OPTIONS = AccountOptions( + val DISPLAY_OPTIONS = AccountDisplayOptions( accountName = "accountName", displayName = "displayName", emailSignature = null, + ) + + val SYNC_OPTIONS = AccountSyncOptions( checkFrequencyInMinutes = 15, messageDisplayCount = 25, showNotification = true, @@ -158,7 +162,8 @@ class SaveServerSettingsTest { incomingServerSettings = INCOMING_SERVER_SETTINGS, outgoingServerSettings = OUTGOING_SERVER_SETTINGS, authorizationState = AUTHORIZATION_STATE, - options = OPTIONS, + displayOptions = DISPLAY_OPTIONS, + syncOptions = SYNC_OPTIONS, ) } } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt index dd60fd79ef5..3dbe24d6904 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/DomainContract.kt @@ -3,12 +3,9 @@ package app.k9mail.feature.account.setup.domain import app.k9mail.autodiscovery.api.AutoDiscoveryResult import app.k9mail.core.common.domain.usecase.validation.ValidationError import app.k9mail.core.common.domain.usecase.validation.ValidationResult -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.SpecialFolderOptions -import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult -import com.fsck.k9.mail.ServerSettings interface DomainContract { @@ -18,15 +15,7 @@ interface DomainContract { } fun interface CreateAccount { - suspend fun execute( - emailAddress: String, - incomingServerSettings: ServerSettings, - outgoingServerSettings: ServerSettings, - authorizationState: String?, - specialFolderSettings: SpecialFolderSettings?, - options: AccountOptions, - displayOptions: AccountDisplayOptions, - ): AccountCreatorResult + suspend fun execute(accountState: AccountState): AccountCreatorResult } fun interface ValidateEmailAddress { diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt index 06121fd4f41..ce373124e60 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccount.kt @@ -3,47 +3,42 @@ package app.k9mail.feature.account.setup.domain.usecase import app.k9mail.feature.account.common.domain.entity.Account 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.SpecialFolderSettings +import app.k9mail.feature.account.common.domain.entity.AccountState +import app.k9mail.feature.account.common.domain.entity.AccountSyncOptions import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult import app.k9mail.feature.account.setup.domain.DomainContract.UseCase -import com.fsck.k9.mail.ServerSettings import java.util.UUID class CreateAccount( private val accountCreator: AccountCreator, private val uuidGenerator: () -> String = { UUID.randomUUID().toString() }, ) : UseCase.CreateAccount { - override suspend fun execute( - emailAddress: String, - incomingServerSettings: ServerSettings, - outgoingServerSettings: ServerSettings, - authorizationState: String?, - specialFolderSettings: SpecialFolderSettings?, - options: AccountOptions, - displayOptions: AccountDisplayOptions, - ): AccountCreatorResult { + override suspend fun execute(accountState: AccountState): AccountCreatorResult { val account = Account( uuid = uuidGenerator(), - emailAddress = emailAddress, - incomingServerSettings = incomingServerSettings, - outgoingServerSettings = outgoingServerSettings, - authorizationState = authorizationState, - specialFolderSettings = specialFolderSettings, - options = mapOptions(options, displayOptions), + emailAddress = accountState.emailAddress!!, + incomingServerSettings = accountState.incomingServerSettings!!.copy(), + outgoingServerSettings = accountState.outgoingServerSettings!!.copy(), + authorizationState = accountState.authorizationState?.value, + specialFolderSettings = accountState.specialFolderSettings, + options = mapOptions(accountState.displayOptions!!, accountState.syncOptions!!), ) return accountCreator.createAccount(account) } private fun mapOptions( - options: AccountOptions, displayOptions: AccountDisplayOptions, + syncOptions: AccountSyncOptions, ): AccountOptions { - return options.copy( + return AccountOptions( accountName = displayOptions.accountName, displayName = displayOptions.displayName, emailSignature = displayOptions.emailSignature, + checkFrequencyInMinutes = syncOptions.checkFrequencyInMinutes, + messageDisplayCount = syncOptions.messageDisplayCount, + showNotification = syncOptions.showNotification, ) } } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt index 3cf564de9e4..722bf343ef8 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt @@ -19,7 +19,8 @@ internal fun AccountAutoDiscoveryContract.State.toAccountState(): AccountState { incomingServerSettings = autoDiscoverySettings?.incomingServerSettings?.toServerSettings(password.value), outgoingServerSettings = autoDiscoverySettings?.outgoingServerSettings?.toServerSettings(password.value), authorizationState = authorizationState, - options = null, + displayOptions = null, + syncOptions = null, ) } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt index e859a34b2af..e55a2bcc997 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountScreen.kt @@ -73,7 +73,7 @@ internal fun AccountOptionsScreenK9Preview() { onNext = {}, onBack = {}, viewModel = CreateAccountViewModel( - createAccount = { _, _, _, _, _, _, _ -> AccountCreatorResult.Success("irrelevant") }, + createAccount = { AccountCreatorResult.Success("irrelevant") }, accountStateRepository = InMemoryAccountStateRepository(), ), ) diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt index ad31f07c366..5215c6f875a 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModel.kt @@ -33,17 +33,7 @@ class CreateAccountViewModel( val accountState = accountStateRepository.getState() viewModelScope.launch { - val result = createAccount.execute( - emailAddress = accountState.emailAddress ?: "", - incomingServerSettings = accountState.incomingServerSettings!!, - outgoingServerSettings = accountState.outgoingServerSettings!!, - authorizationState = accountState.authorizationState?.state, - specialFolderSettings = accountState.specialFolderSettings, - options = accountState.options!!, - displayOptions = accountState.displayOptions!!, - ) - - when (result) { + when (val result = createAccount.execute(accountState)) { is AccountCreatorResult.Success -> showSuccess(AccountUuid(result.accountUuid)) is AccountCreatorResult.Error -> showError(result) } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt index 5025ba9bef7..46011dad357 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsStateMapper.kt @@ -5,8 +5,8 @@ import app.k9mail.feature.account.common.domain.entity.AccountState import app.k9mail.feature.account.common.domain.input.StringInputField import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract.State -internal fun AccountState.toAccountOptionsState(): State { - val options = options +internal fun AccountState.toDisplayOptionsState(): State { + val options = displayOptions return if (options == null) { State( accountName = StringInputField(emailAddress ?: ""), diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt index ea8ad92eda1..65d00e3b543 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/display/DisplayOptionsViewModel.kt @@ -14,7 +14,7 @@ internal class DisplayOptionsViewModel( private val accountStateRepository: AccountDomainContract.AccountStateRepository, initialState: State? = null, ) : BaseViewModel( - initialState = initialState ?: accountStateRepository.getState().toAccountOptionsState(), + initialState = initialState ?: accountStateRepository.getState().toDisplayOptionsState(), ), ViewModel { @@ -47,7 +47,7 @@ internal class DisplayOptionsViewModel( private fun loadAccountState() { updateState { - accountStateRepository.getState().toAccountOptionsState() + accountStateRepository.getState().toDisplayOptionsState() } } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt index d6628d44ecd..c76ec4523a2 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsStateMapper.kt @@ -6,8 +6,8 @@ import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract.State -internal fun AccountState.toAccountOptionsState(): State { - val options = options +internal fun AccountState.toSyncOptionsState(): State { + val options = syncOptions return if (options == null) { State() } else { diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt index 35c1673a44c..109e403fd6c 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/sync/SyncOptionsViewModel.kt @@ -11,7 +11,7 @@ internal class SyncOptionsViewModel( private val accountStateRepository: AccountDomainContract.AccountStateRepository, initialState: State? = null, ) : BaseViewModel( - initialState = initialState ?: accountStateRepository.getState().toAccountOptionsState(), + initialState = initialState ?: accountStateRepository.getState().toSyncOptionsState(), ), ViewModel { @@ -44,7 +44,7 @@ internal class SyncOptionsViewModel( private fun loadAccountState() { updateState { - accountStateRepository.getState().toAccountOptionsState() + accountStateRepository.getState().toSyncOptionsState() } } diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt index 773c475689e..1928ca6c713 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/domain/usecase/CreateAccountTest.kt @@ -3,6 +3,9 @@ package app.k9mail.feature.account.setup.domain.usecase import app.k9mail.feature.account.common.domain.entity.Account 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 app.k9mail.feature.account.common.domain.entity.SpecialFolderOption import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings @@ -31,13 +34,15 @@ class CreateAccountTest { ) val result = createAccount.execute( - emailAddress = EMAIL_ADDRESS, - incomingServerSettings = INCOMING_SETTINGS, - outgoingServerSettings = OUTGOING_SETTINGS, - authorizationState = AUTHORIZATION_STATE, - specialFolderSettings = SPECIAL_FOLDER_SETTINGS, - options = OPTIONS, - displayOptions = DISPLAY_OPTIONS, + AccountState( + emailAddress = EMAIL_ADDRESS, + incomingServerSettings = INCOMING_SETTINGS, + outgoingServerSettings = OUTGOING_SETTINGS, + authorizationState = AUTHORIZATION_STATE, + specialFolderSettings = SPECIAL_FOLDER_SETTINGS, + displayOptions = DISPLAY_OPTIONS, + syncOptions = SYNC_OPTIONS, + ), ) assertThat(result).isEqualTo(AccountCreatorResult.Success("uuid")) @@ -47,7 +52,7 @@ class CreateAccountTest { emailAddress = EMAIL_ADDRESS, incomingServerSettings = INCOMING_SETTINGS, outgoingServerSettings = OUTGOING_SETTINGS, - authorizationState = AUTHORIZATION_STATE, + authorizationState = AUTHORIZATION_STATE.value, specialFolderSettings = SPECIAL_FOLDER_SETTINGS, options = OPTIONS, ), @@ -79,7 +84,7 @@ class CreateAccountTest { clientCertificateAlias = null, ) - const val AUTHORIZATION_STATE = "authorization state" + val AUTHORIZATION_STATE = AuthorizationState("authorization state") val SPECIAL_FOLDER_SETTINGS = SpecialFolderSettings( archiveSpecialFolderOption = SpecialFolderOption.Special( @@ -113,5 +118,11 @@ class CreateAccountTest { displayName = "displayName", emailSignature = null, ) + + val SYNC_OPTIONS = AccountSyncOptions( + checkFrequencyInMinutes = 15, + messageDisplayCount = 25, + showNotification = true, + ) } } diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt index 802cfe0e66e..d01dd195036 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt @@ -32,7 +32,8 @@ class AccountAutoDiscoveryStateMapperKtTest { incomingServerSettings = null, outgoingServerSettings = null, authorizationState = null, - options = null, + displayOptions = null, + syncOptions = null, ), ) } diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt index 0d0da78cf3d..3aa896a8c5d 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryViewModelTest.kt @@ -297,7 +297,8 @@ class AccountAutoDiscoveryViewModelTest { incomingServerSettings = null, outgoingServerSettings = null, authorizationState = null, - options = null, + displayOptions = null, + syncOptions = null, ), ) } diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt index 0a03f40335d..6b7b4682bd1 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/CreateAccountViewModelTest.kt @@ -5,8 +5,8 @@ import app.k9mail.core.ui.compose.testing.MainDispatcherRule import app.k9mail.core.ui.compose.testing.mvi.eventStateTest 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.SpecialFolderOption import app.k9mail.feature.account.common.domain.entity.SpecialFolderSettings @@ -61,14 +61,14 @@ class CreateAccountViewModelTest { ) assertThat(fakeCreateAccount.recordedInvocations).containsExactly( - CreateAccountArguments( + AccountState( emailAddress = EMAIL_ADDRESS, incomingServerSettings = INCOMING_SERVER_SETTINGS, outgoingServerSettings = OUTGOING_SERVER_SETTINGS, - authorizationState = AUTHORIZATION_STATE.state, + authorizationState = AUTHORIZATION_STATE, specialFolderSettings = SPECIAL_FOLDER_SETTINGS, - options = ACCOUNT_OPTIONS, displayOptions = ACCOUNT_DISPLAY_OPTIONS, + syncOptions = ACCOUNT_SYNC_OPTIONS, ), ) @@ -186,28 +186,26 @@ class CreateAccountViewModelTest { ), ) - val ACCOUNT_OPTIONS = AccountOptions( + val ACCOUNT_DISPLAY_OPTIONS = AccountDisplayOptions( accountName = "account name", displayName = "display name", emailSignature = null, + ) + + val ACCOUNT_SYNC_OPTIONS = AccountSyncOptions( checkFrequencyInMinutes = 0, messageDisplayCount = 50, showNotification = false, ) - val ACCOUNT_DISPLAY_OPTIONS = AccountDisplayOptions( - accountName = "account name", - displayName = "display name", - emailSignature = null, - ) - val ACCOUNT_STATE = AccountState( emailAddress = EMAIL_ADDRESS, incomingServerSettings = INCOMING_SERVER_SETTINGS, outgoingServerSettings = OUTGOING_SERVER_SETTINGS, authorizationState = AUTHORIZATION_STATE, specialFolderSettings = SPECIAL_FOLDER_SETTINGS, - options = ACCOUNT_OPTIONS, + displayOptions = ACCOUNT_DISPLAY_OPTIONS, + syncOptions = ACCOUNT_SYNC_OPTIONS, ) } } diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt index bd0ab7cad90..d8c0fefcd30 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/createaccount/FakeCreateAccount.kt @@ -1,48 +1,19 @@ package app.k9mail.feature.account.setup.ui.createaccount -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.SpecialFolderSettings +import app.k9mail.feature.account.common.domain.entity.AccountState import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult import app.k9mail.feature.account.setup.domain.DomainContract.UseCase.CreateAccount -import com.fsck.k9.mail.ServerSettings class FakeCreateAccount : CreateAccount { - val recordedInvocations = mutableListOf() + val recordedInvocations = mutableListOf() var result: AccountCreatorResult = AccountCreatorResult.Success("default result") override suspend fun execute( - emailAddress: String, - incomingServerSettings: ServerSettings, - outgoingServerSettings: ServerSettings, - authorizationState: String?, - specialFolderSettings: SpecialFolderSettings?, - options: AccountOptions, - displayOptions: AccountDisplayOptions, + accountState: AccountState, ): AccountCreatorResult { - recordedInvocations.add( - CreateAccountArguments( - emailAddress, - incomingServerSettings, - outgoingServerSettings, - authorizationState, - specialFolderSettings, - options, - displayOptions, - ), - ) + recordedInvocations.add(accountState) return result } } - -data class CreateAccountArguments( - val emailAddress: String, - val incomingServerSettings: ServerSettings, - val outgoingServerSettings: ServerSettings, - val authorizationState: String?, - val specialFolderSettings: SpecialFolderSettings?, - val options: AccountOptions, - val displayOptions: AccountDisplayOptions, -) From 34d0da026bbf2573bd4b1a8bf15280b4968013ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolf-Martell=20Montw=C3=A9?= Date: Wed, 10 Jan 2024 13:39:26 +0100 Subject: [PATCH 6/6] Remove AccountOptions --- .../account/setup/AccountSetupModule.kt | 17 +- .../AccountAutoDiscoveryStateMapper.kt | 6 +- .../setup/ui/options/AccountOptionsContent.kt | 172 ---------------- .../ui/options/AccountOptionsContract.kt | 46 ----- .../setup/ui/options/AccountOptionsScreen.kt | 93 --------- .../ui/options/AccountOptionsStateMapper.kt | 39 ---- .../ui/options/AccountOptionsStringMapper.kt | 65 ------ .../ui/options/AccountOptionsValidator.kt | 25 --- .../ui/options/AccountOptionsViewModel.kt | 100 --------- .../account/setup/AccountSetupModuleKtTest.kt | 6 +- .../AccountAutoDiscoveryStateMapperKtTest.kt | 6 +- .../ui/options/AccountOptionsScreenKtTest.kt | 45 ---- .../AccountOptionsStateMapperKtTest.kt | 47 ----- .../ui/options/AccountOptionsStateTest.kt | 28 --- .../ui/options/AccountOptionsViewModelTest.kt | 192 ------------------ .../ui/options/FakeAccountOptionsValidator.kt | 14 -- .../ui/options/FakeAccountOptionsViewModel.kt | 22 -- 17 files changed, 22 insertions(+), 901 deletions(-) delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStringMapper.kt delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsValidator.kt delete mode 100644 feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt delete mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreenKtTest.kt delete mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt delete mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt delete mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt delete mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsValidator.kt delete mode 100644 feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsViewModel.kt diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt index ac5a6b50b02..5c80689340e 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/AccountSetupModule.kt @@ -15,9 +15,10 @@ import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryCon import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryValidator import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryViewModel import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountViewModel -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract -import app.k9mail.feature.account.setup.ui.options.AccountOptionsValidator -import app.k9mail.feature.account.setup.ui.options.AccountOptionsViewModel +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsValidator +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsViewModel +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsViewModel import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersContract import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersFormUiModel import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersViewModel @@ -61,7 +62,7 @@ val featureAccountSetupModule: Module = module { } factory { AccountAutoDiscoveryValidator() } - factory { AccountOptionsValidator() } + factory { DisplayOptionsValidator() } viewModel { AccountAutoDiscoveryViewModel( @@ -107,12 +108,18 @@ val featureAccountSetupModule: Module = module { } viewModel { - AccountOptionsViewModel( + DisplayOptionsViewModel( validator = get(), accountStateRepository = get(), ) } + viewModel { + SyncOptionsViewModel( + accountStateRepository = get(), + ) + } + viewModel { CreateAccountViewModel( createAccount = get(), diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt index 722bf343ef8..64b25767c8b 100644 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt +++ b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapper.kt @@ -11,7 +11,7 @@ import app.k9mail.feature.account.setup.domain.entity.toAuthenticationType import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity import app.k9mail.feature.account.setup.domain.entity.toIncomingProtocolType import app.k9mail.feature.account.setup.domain.toServerSettings -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract internal fun AccountAutoDiscoveryContract.State.toAccountState(): AccountState { return AccountState( @@ -67,8 +67,8 @@ internal fun AccountAutoDiscoveryContract.State.toOutgoingConfigState(): Outgoin } } -internal fun AccountAutoDiscoveryContract.State.toOptionsState(): AccountOptionsContract.State { - return AccountOptionsContract.State( +internal fun AccountAutoDiscoveryContract.State.toOptionsState(): DisplayOptionsContract.State { + return DisplayOptionsContract.State( accountName = StringInputField(value = emailAddress.value), ) } diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt deleted file mode 100644 index c4d1f9bbef7..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContent.kt +++ /dev/null @@ -1,172 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.ExperimentalLayoutApi -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.consumeWindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.imePadding -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredHeight -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.testTag -import androidx.compose.ui.res.stringResource -import app.k9mail.core.ui.compose.common.PreviewDevices -import app.k9mail.core.ui.compose.designsystem.atom.text.TextOverline -import app.k9mail.core.ui.compose.designsystem.molecule.input.SelectInput -import app.k9mail.core.ui.compose.designsystem.molecule.input.SwitchInput -import app.k9mail.core.ui.compose.designsystem.molecule.input.TextInput -import app.k9mail.core.ui.compose.designsystem.template.ResponsiveWidthContainer -import app.k9mail.core.ui.compose.theme.K9Theme -import app.k9mail.core.ui.compose.theme.MainTheme -import app.k9mail.core.ui.compose.theme.ThunderbirdTheme -import app.k9mail.feature.account.common.ui.item.defaultHeadlineItemPadding -import app.k9mail.feature.account.common.ui.item.defaultItemPadding -import app.k9mail.feature.account.setup.R -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State - -@OptIn(ExperimentalLayoutApi::class) -@Suppress("LongMethod") -@Composable -internal fun AccountOptionsContent( - state: State, - onEvent: (Event) -> Unit, - contentPadding: PaddingValues, - modifier: Modifier = Modifier, -) { - val resources = LocalContext.current.resources - - ResponsiveWidthContainer( - modifier = Modifier - .testTag("AccountOptionsContent") - .consumeWindowInsets(contentPadding) - .padding(contentPadding) - .then(modifier), - ) { - LazyColumn( - modifier = Modifier - .fillMaxSize() - .imePadding(), - horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(MainTheme.spacings.default), - ) { - item { - TextOverline( - text = stringResource(id = R.string.account_setup_options_section_display_options), - modifier = Modifier - .fillMaxWidth() - .padding(defaultHeadlineItemPadding()), - ) - } - - item { - TextInput( - text = state.accountName.value, - errorMessage = state.accountName.error?.toResourceString(resources), - onTextChange = { onEvent(Event.OnAccountNameChanged(it)) }, - label = stringResource(id = R.string.account_setup_options_account_name_label), - contentPadding = defaultItemPadding(), - ) - } - - item { - TextInput( - text = state.displayName.value, - errorMessage = state.displayName.error?.toResourceString(resources), - onTextChange = { onEvent(Event.OnDisplayNameChanged(it)) }, - label = stringResource(id = R.string.account_setup_options_display_name_label), - contentPadding = defaultItemPadding(), - isRequired = true, - ) - } - - item { - TextInput( - text = state.emailSignature.value, - errorMessage = state.emailSignature.error?.toResourceString(resources), - onTextChange = { onEvent(Event.OnEmailSignatureChanged(it)) }, - label = stringResource(id = R.string.account_setup_options_email_signature_label), - contentPadding = defaultItemPadding(), - isSingleLine = false, - ) - } - - item { - TextOverline( - text = stringResource(id = R.string.account_setup_options_section_sync_options), - modifier = Modifier - .fillMaxWidth() - .padding(defaultHeadlineItemPadding()), - ) - } - - item { - SelectInput( - options = EmailCheckFrequency.all(), - optionToStringTransformation = { it.toResourceString(resources) }, - selectedOption = state.checkFrequency, - onOptionChange = { onEvent(Event.OnCheckFrequencyChanged(it)) }, - label = stringResource(id = R.string.account_setup_options_account_check_frequency_label), - contentPadding = defaultItemPadding(), - ) - } - - item { - SelectInput( - options = EmailDisplayCount.all(), - optionToStringTransformation = { it.toResourceString(resources) }, - selectedOption = state.messageDisplayCount, - onOptionChange = { onEvent(Event.OnMessageDisplayCountChanged(it)) }, - label = stringResource(id = R.string.account_setup_options_email_display_count_label), - contentPadding = defaultItemPadding(), - ) - } - - item { - SwitchInput( - text = stringResource(id = R.string.account_setup_options_show_notifications_label), - checked = state.showNotification, - onCheckedChange = { onEvent(Event.OnShowNotificationChanged(it)) }, - contentPadding = defaultItemPadding(), - ) - } - - item { - Spacer(modifier = Modifier.requiredHeight(MainTheme.sizes.smaller)) - } - } - } -} - -@Composable -@PreviewDevices -internal fun AccountOptionsContentK9Preview() { - K9Theme { - AccountOptionsContent( - state = State(), - onEvent = {}, - contentPadding = PaddingValues(), - ) - } -} - -@Composable -@PreviewDevices -internal fun AccountOptionsContentThunderbirdPreview() { - ThunderbirdTheme { - AccountOptionsContent( - state = State(), - onEvent = {}, - contentPadding = PaddingValues(), - ) - } -} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt deleted file mode 100644 index 0b536d7110a..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsContract.kt +++ /dev/null @@ -1,46 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.core.common.domain.usecase.validation.ValidationResult -import app.k9mail.core.ui.compose.common.mvi.UnidirectionalViewModel -import app.k9mail.feature.account.common.domain.input.StringInputField -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount - -interface AccountOptionsContract { - - interface ViewModel : UnidirectionalViewModel - - data class State( - val accountName: StringInputField = StringInputField(), - val displayName: StringInputField = StringInputField(), - val emailSignature: StringInputField = StringInputField(), - val checkFrequency: EmailCheckFrequency = EmailCheckFrequency.DEFAULT, - val messageDisplayCount: EmailDisplayCount = EmailDisplayCount.DEFAULT, - val showNotification: Boolean = false, - ) - - sealed interface Event { - data class OnAccountNameChanged(val accountName: String) : Event - data class OnDisplayNameChanged(val displayName: String) : Event - data class OnEmailSignatureChanged(val emailSignature: String) : Event - data class OnCheckFrequencyChanged(val checkFrequency: EmailCheckFrequency) : Event - data class OnMessageDisplayCountChanged(val messageDisplayCount: EmailDisplayCount) : Event - data class OnShowNotificationChanged(val showNotification: Boolean) : Event - - object LoadAccountState : Event - - object OnNextClicked : Event - object OnBackClicked : Event - } - - sealed interface Effect { - object NavigateNext : Effect - object NavigateBack : Effect - } - - interface Validator { - fun validateAccountName(accountName: String): ValidationResult - fun validateDisplayName(displayName: String): ValidationResult - fun validateEmailSignature(emailSignature: String): ValidationResult - } -} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt deleted file mode 100644 index f54c5982827..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreen.kt +++ /dev/null @@ -1,93 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import androidx.activity.compose.BackHandler -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource -import app.k9mail.core.ui.compose.common.PreviewDevices -import app.k9mail.core.ui.compose.common.mvi.observe -import app.k9mail.core.ui.compose.designsystem.template.Scaffold -import app.k9mail.core.ui.compose.theme.K9Theme -import app.k9mail.core.ui.compose.theme.ThunderbirdTheme -import app.k9mail.feature.account.common.ui.AccountTopAppBar -import app.k9mail.feature.account.common.ui.WizardNavigationBar -import app.k9mail.feature.account.common.ui.preview.PreviewAccountStateRepository -import app.k9mail.feature.account.setup.R.string -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.ViewModel - -@Composable -internal fun AccountOptionsScreen( - onNext: () -> Unit, - onBack: () -> Unit, - viewModel: ViewModel, - modifier: Modifier = Modifier, -) { - val (state, dispatch) = viewModel.observe { effect -> - when (effect) { - Effect.NavigateBack -> onBack() - Effect.NavigateNext -> onNext() - } - } - - LaunchedEffect(key1 = Unit) { - dispatch(Event.LoadAccountState) - } - - BackHandler { - dispatch(Event.OnBackClicked) - } - - Scaffold( - topBar = { - AccountTopAppBar( - title = stringResource(id = string.account_setup_options_top_bar_title), - ) - }, - bottomBar = { - WizardNavigationBar( - onNextClick = { dispatch(Event.OnNextClicked) }, - onBackClick = { dispatch(Event.OnBackClicked) }, - ) - }, - modifier = modifier, - ) { innerPadding -> - AccountOptionsContent( - state = state.value, - onEvent = { dispatch(it) }, - contentPadding = innerPadding, - ) - } -} - -@Composable -@PreviewDevices -internal fun AccountOptionsScreenK9Preview() { - K9Theme { - AccountOptionsScreen( - onNext = {}, - onBack = {}, - viewModel = AccountOptionsViewModel( - validator = AccountOptionsValidator(), - accountStateRepository = PreviewAccountStateRepository(), - ), - ) - } -} - -@Composable -@PreviewDevices -internal fun AccountOptionsScreenThunderbirdPreview() { - ThunderbirdTheme { - AccountOptionsScreen( - onNext = {}, - onBack = {}, - viewModel = AccountOptionsViewModel( - validator = AccountOptionsValidator(), - accountStateRepository = PreviewAccountStateRepository(), - ), - ) - } -} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt deleted file mode 100644 index 3616cd5bebe..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapper.kt +++ /dev/null @@ -1,39 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -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.input.StringInputField -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State - -internal fun AccountState.toAccountOptionsState(): State { - val options = options - return if (options == null) { - State( - accountName = StringInputField(emailAddress ?: ""), - // displayName = StringInputField(""), - // TODO: get display name from: preferences.defaultAccount?.senderName ?: "" - ) - } else { - State( - accountName = StringInputField(options.accountName), - displayName = StringInputField(options.displayName), - emailSignature = StringInputField(options.emailSignature ?: ""), - checkFrequency = EmailCheckFrequency.fromMinutes(options.checkFrequencyInMinutes), - messageDisplayCount = EmailDisplayCount.fromCount(options.messageDisplayCount), - showNotification = options.showNotification, - ) - } -} - -internal fun State.toAccountOptions(): AccountOptions { - return AccountOptions( - accountName = accountName.value, - displayName = displayName.value, - emailSignature = emailSignature.value.takeIf { it.isNotEmpty() }, - checkFrequencyInMinutes = checkFrequency.minutes, - messageDisplayCount = messageDisplayCount.count, - showNotification = showNotification, - ) -} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStringMapper.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStringMapper.kt deleted file mode 100644 index eae82030ce2..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStringMapper.kt +++ /dev/null @@ -1,65 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import android.content.res.Resources -import app.k9mail.core.common.domain.usecase.validation.ValidationError -import app.k9mail.feature.account.setup.R -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount -import app.k9mail.feature.account.setup.domain.usecase.ValidateAccountName.ValidateAccountNameError -import app.k9mail.feature.account.setup.domain.usecase.ValidateAccountName.ValidateAccountNameError.BlankAccountName -import app.k9mail.feature.account.setup.domain.usecase.ValidateDisplayName.ValidateDisplayNameError -import app.k9mail.feature.account.setup.domain.usecase.ValidateDisplayName.ValidateDisplayNameError.EmptyDisplayName -import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailSignature.ValidateEmailSignatureError -import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailSignature.ValidateEmailSignatureError.BlankEmailSignature - -internal fun EmailDisplayCount.toResourceString(resources: Resources) = resources.getQuantityString( - R.plurals.account_setup_options_email_display_count_messages, - count, - count, -) - -@Suppress("MagicNumber") -internal fun EmailCheckFrequency.toResourceString(resources: Resources): String { - return when (minutes) { - -1 -> resources.getString(R.string.account_setup_options_email_check_frequency_never) - - in 1..59 -> resources.getQuantityString( - R.plurals.account_setup_options_email_check_frequency_minutes, - minutes, - minutes, - ) - - else -> resources.getQuantityString( - R.plurals.account_setup_options_email_check_frequency_hours, - (minutes / 60), - (minutes / 60), - ) - } -} - -internal fun ValidationError.toResourceString(resources: Resources): String { - return when (this) { - is ValidateAccountNameError -> toAccountNameErrorString(resources) - is ValidateDisplayNameError -> toDisplayNameErrorString(resources) - is ValidateEmailSignatureError -> toEmailSignatureErrorString(resources) - else -> throw IllegalArgumentException("Unknown error: $this") - } -} - -private fun ValidateAccountNameError.toAccountNameErrorString(resources: Resources): String { - return when (this) { - is BlankAccountName -> resources.getString(R.string.account_setup_options_account_name_error_blank) - } -} - -private fun ValidateDisplayNameError.toDisplayNameErrorString(resources: Resources): String { - return when (this) { - is EmptyDisplayName -> resources.getString(R.string.account_setup_options_display_name_error_required) - } -} - -private fun ValidateEmailSignatureError.toEmailSignatureErrorString(resources: Resources): String { - return when (this) { - is BlankEmailSignature -> resources.getString(R.string.account_setup_options_email_signature_error_blank) - } -} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsValidator.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsValidator.kt deleted file mode 100644 index 4a167b311d5..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsValidator.kt +++ /dev/null @@ -1,25 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.core.common.domain.usecase.validation.ValidationResult -import app.k9mail.feature.account.setup.domain.usecase.ValidateAccountName -import app.k9mail.feature.account.setup.domain.usecase.ValidateDisplayName -import app.k9mail.feature.account.setup.domain.usecase.ValidateEmailSignature -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Validator - -internal class AccountOptionsValidator( - private val accountNameValidator: ValidateAccountName = ValidateAccountName(), - private val displayNameValidator: ValidateDisplayName = ValidateDisplayName(), - private val emailSignatureValidator: ValidateEmailSignature = ValidateEmailSignature(), -) : Validator { - override fun validateAccountName(accountName: String): ValidationResult { - return accountNameValidator.execute(accountName) - } - - override fun validateDisplayName(displayName: String): ValidationResult { - return displayNameValidator.execute(displayName) - } - - override fun validateEmailSignature(emailSignature: String): ValidationResult { - return emailSignatureValidator.execute(emailSignature) - } -} diff --git a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt b/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt deleted file mode 100644 index 7d02b09a11e..00000000000 --- a/feature/account/setup/src/main/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModel.kt +++ /dev/null @@ -1,100 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.core.common.domain.usecase.validation.ValidationResult -import app.k9mail.core.ui.compose.common.mvi.BaseViewModel -import app.k9mail.feature.account.common.domain.AccountDomainContract -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Validator -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.ViewModel - -internal class AccountOptionsViewModel( - private val validator: Validator, - private val accountStateRepository: AccountDomainContract.AccountStateRepository, - initialState: State? = null, -) : BaseViewModel( - initialState = initialState ?: accountStateRepository.getState().toAccountOptionsState(), -), - ViewModel { - - override fun event(event: Event) { - when (event) { - Event.LoadAccountState -> handleOneTimeEvent(event, ::loadAccountState) - - is Event.OnAccountNameChanged -> updateState { state -> - state.copy( - accountName = state.accountName.updateValue(event.accountName), - ) - } - - is Event.OnDisplayNameChanged -> updateState { - it.copy( - displayName = it.displayName.updateValue(event.displayName), - ) - } - - is Event.OnEmailSignatureChanged -> updateState { - it.copy( - emailSignature = it.emailSignature.updateValue(event.emailSignature), - ) - } - - is Event.OnCheckFrequencyChanged -> updateState { - it.copy( - checkFrequency = event.checkFrequency, - ) - } - - is Event.OnMessageDisplayCountChanged -> updateState { state -> - state.copy( - messageDisplayCount = event.messageDisplayCount, - ) - } - - is Event.OnShowNotificationChanged -> updateState { state -> - state.copy( - showNotification = event.showNotification, - ) - } - - Event.OnNextClicked -> submit() - Event.OnBackClicked -> navigateBack() - } - } - - private fun loadAccountState() { - updateState { - accountStateRepository.getState().toAccountOptionsState() - } - } - - private fun submit() = with(state.value) { - val accountNameResult = validator.validateAccountName(accountName.value) - val displayNameResult = validator.validateDisplayName(displayName.value) - val emailSignatureResult = validator.validateEmailSignature(emailSignature.value) - - val hasError = listOf( - accountNameResult, - displayNameResult, - emailSignatureResult, - ).any { it is ValidationResult.Failure } - - updateState { - it.copy( - accountName = it.accountName.updateFromValidationResult(accountNameResult), - displayName = it.displayName.updateFromValidationResult(displayNameResult), - emailSignature = it.emailSignature.updateFromValidationResult(emailSignatureResult), - ) - } - - if (!hasError) { - accountStateRepository.setOptions(state.value.toAccountOptions()) - navigateNext() - } - } - - private fun navigateBack() = emitEffect(Effect.NavigateBack) - - private fun navigateNext() = emitEffect(Effect.NavigateNext) -} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt index 4a129f6b4f8..119c5d7140b 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/AccountSetupModuleKtTest.kt @@ -14,7 +14,8 @@ import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCrea import app.k9mail.feature.account.setup.AccountSetupExternalContract.AccountCreator.AccountCreatorResult import app.k9mail.feature.account.setup.ui.autodiscovery.AccountAutoDiscoveryContract import app.k9mail.feature.account.setup.ui.createaccount.CreateAccountContract -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract +import app.k9mail.feature.account.setup.ui.options.sync.SyncOptionsContract import app.k9mail.feature.account.setup.ui.specialfolders.SpecialFoldersContract import com.fsck.k9.mail.oauth.AuthStateStorage import com.fsck.k9.mail.oauth.OAuth2TokenProvider @@ -71,7 +72,8 @@ class AccountSetupModuleKtTest : KoinTest { ServerValidationContract.State::class, IncomingServerSettingsContract.State::class, OutgoingServerSettingsContract.State::class, - AccountOptionsContract.State::class, + DisplayOptionsContract.State::class, + SyncOptionsContract.State::class, AccountState::class, ServerCertificateErrorContract.State::class, AuthStateStorage::class, diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt index d01dd195036..c66fa1936e4 100644 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt +++ b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/autodiscovery/AccountAutoDiscoveryStateMapperKtTest.kt @@ -15,7 +15,7 @@ import app.k9mail.feature.account.server.settings.ui.outgoing.OutgoingServerSett import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryAuthenticationType import app.k9mail.feature.account.setup.domain.entity.AutoDiscoveryConnectionSecurity import app.k9mail.feature.account.setup.domain.entity.toConnectionSecurity -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract +import app.k9mail.feature.account.setup.ui.options.display.DisplayOptionsContract import assertk.assertThat import assertk.assertions.isEqualTo import org.junit.Test @@ -146,7 +146,7 @@ class AccountAutoDiscoveryStateMapperKtTest { fun `should map to OptionsState when empty`() { val optionsState = EMPTY_STATE.toOptionsState() - assertThat(optionsState).isEqualTo(AccountOptionsContract.State()) + assertThat(optionsState).isEqualTo(DisplayOptionsContract.State()) } @Test @@ -154,7 +154,7 @@ class AccountAutoDiscoveryStateMapperKtTest { val optionsState = EMAIL_PASSWORD_STATE.toOptionsState() assertThat(optionsState).isEqualTo( - AccountOptionsContract.State( + DisplayOptionsContract.State( accountName = StringInputField(value = EMAIL_ADDRESS), ), ) diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreenKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreenKtTest.kt deleted file mode 100644 index e8048736122..00000000000 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsScreenKtTest.kt +++ /dev/null @@ -1,45 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.core.ui.compose.testing.ComposeTest -import app.k9mail.core.ui.compose.testing.setContent -import app.k9mail.core.ui.compose.theme.ThunderbirdTheme -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State -import assertk.assertThat -import assertk.assertions.isEqualTo -import kotlinx.coroutines.test.runTest -import org.junit.Test - -class AccountOptionsScreenKtTest : ComposeTest() { - - @Test - fun `should delegate navigation effects`() = runTest { - val initialState = State() - val viewModel = FakeAccountOptionsViewModel(initialState) - var onNextCounter = 0 - var onBackCounter = 0 - - setContent { - ThunderbirdTheme { - AccountOptionsScreen( - onNext = { onNextCounter++ }, - onBack = { onBackCounter++ }, - viewModel = viewModel, - ) - } - } - - assertThat(onNextCounter).isEqualTo(0) - assertThat(onBackCounter).isEqualTo(0) - - viewModel.effect(Effect.NavigateNext) - - assertThat(onNextCounter).isEqualTo(1) - assertThat(onBackCounter).isEqualTo(0) - - viewModel.effect(Effect.NavigateBack) - - assertThat(onNextCounter).isEqualTo(1) - assertThat(onBackCounter).isEqualTo(1) - } -} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt deleted file mode 100644 index cfaa6c1e052..00000000000 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateMapperKtTest.kt +++ /dev/null @@ -1,47 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.feature.account.common.domain.entity.AccountOptions -import app.k9mail.feature.account.common.domain.input.StringInputField -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount -import assertk.assertThat -import assertk.assertions.isEqualTo -import assertk.assertions.isNull -import org.junit.Test - -class AccountOptionsStateMapperKtTest { - - @Test - fun `should map state to account options`() { - val state = AccountOptionsContract.State( - accountName = StringInputField("accountName"), - displayName = StringInputField("displayName"), - emailSignature = StringInputField("emailSignature"), - checkFrequency = EmailCheckFrequency.EVERY_2_HOURS, - messageDisplayCount = EmailDisplayCount.MESSAGES_100, - showNotification = true, - ) - - val result = state.toAccountOptions() - - assertThat(result).isEqualTo( - AccountOptions( - accountName = "accountName", - displayName = "displayName", - emailSignature = "emailSignature", - checkFrequencyInMinutes = 120, - messageDisplayCount = 100, - showNotification = true, - ), - ) - } - - @Test - fun `empty signature should map to null`() { - val state = AccountOptionsContract.State(emailSignature = StringInputField("")) - - val result = state.toAccountOptions() - - assertThat(result.emailSignature).isNull() - } -} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt deleted file mode 100644 index 40526d70134..00000000000 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsStateTest.kt +++ /dev/null @@ -1,28 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.feature.account.common.domain.input.StringInputField -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State -import assertk.assertThat -import assertk.assertions.isEqualTo -import org.junit.Test - -class AccountOptionsStateTest { - - @Test - fun `should set default values`() { - val state = State() - - assertThat(state).isEqualTo( - State( - accountName = StringInputField(), - displayName = StringInputField(), - emailSignature = StringInputField(), - checkFrequency = EmailCheckFrequency.DEFAULT, - messageDisplayCount = EmailDisplayCount.DEFAULT, - showNotification = false, - ), - ) - } -} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt deleted file mode 100644 index f610c024940..00000000000 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/AccountOptionsViewModelTest.kt +++ /dev/null @@ -1,192 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.cash.turbine.testIn -import app.k9mail.core.common.domain.usecase.validation.ValidationError -import app.k9mail.core.common.domain.usecase.validation.ValidationResult -import app.k9mail.core.ui.compose.testing.MainDispatcherRule -import app.k9mail.core.ui.compose.testing.mvi.eventStateTest -import app.k9mail.feature.account.common.data.InMemoryAccountStateRepository -import app.k9mail.feature.account.common.domain.input.StringInputField -import app.k9mail.feature.account.setup.domain.entity.EmailCheckFrequency -import app.k9mail.feature.account.setup.domain.entity.EmailDisplayCount -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State -import assertk.assertThat -import assertk.assertions.assertThatAndTurbinesConsumed -import assertk.assertions.isEqualTo -import kotlinx.coroutines.test.runTest -import org.junit.Rule -import org.junit.Test - -class AccountOptionsViewModelTest { - - @get:Rule - val mainDispatcherRule = MainDispatcherRule() - - private val testSubject = AccountOptionsViewModel( - validator = FakeAccountOptionsValidator(), - accountStateRepository = InMemoryAccountStateRepository(), - ) - - @Test - fun `should change state when OnAccountNameChanged event is received`() = runTest { - eventStateTest( - viewModel = testSubject, - initialState = State(), - event = Event.OnAccountNameChanged("accountName"), - expectedState = State(accountName = StringInputField(value = "accountName")), - coroutineScope = backgroundScope, - ) - } - - @Test - fun `should change state when OnDisplayNameChanged event is received`() = runTest { - eventStateTest( - viewModel = testSubject, - initialState = State(), - event = Event.OnDisplayNameChanged("displayName"), - expectedState = State(displayName = StringInputField(value = "displayName")), - coroutineScope = backgroundScope, - ) - } - - @Test - fun `should change state when OnEmailSignatureChanged event is received`() = runTest { - eventStateTest( - viewModel = testSubject, - initialState = State(), - event = Event.OnEmailSignatureChanged("emailSignature"), - expectedState = State(emailSignature = StringInputField(value = "emailSignature")), - coroutineScope = backgroundScope, - ) - } - - @Test - fun `should change state when OnCheckFrequencyChanged event is received`() = runTest { - eventStateTest( - viewModel = testSubject, - initialState = State(), - event = Event.OnCheckFrequencyChanged(EmailCheckFrequency.EVERY_12_HOURS), - expectedState = State(checkFrequency = EmailCheckFrequency.EVERY_12_HOURS), - coroutineScope = backgroundScope, - ) - } - - @Test - fun `should change state when OnMessageDisplayCountChanged event is received`() = runTest { - eventStateTest( - viewModel = testSubject, - initialState = State(), - event = Event.OnMessageDisplayCountChanged(EmailDisplayCount.MESSAGES_1000), - expectedState = State(messageDisplayCount = EmailDisplayCount.MESSAGES_1000), - coroutineScope = backgroundScope, - ) - } - - @Test - fun `should change state when OnShowNotificationChanged event is received`() = runTest { - eventStateTest( - viewModel = testSubject, - initialState = State(), - event = Event.OnShowNotificationChanged(true), - expectedState = State(showNotification = true), - coroutineScope = backgroundScope, - ) - } - - @Test - fun `should change state and emit NavigateNext effect when OnNextClicked event received and input valid`() = - runTest { - val viewModel = testSubject - val stateTurbine = viewModel.state.testIn(backgroundScope) - val effectTurbine = viewModel.effect.testIn(backgroundScope) - val turbines = listOf(stateTurbine, effectTurbine) - - assertThatAndTurbinesConsumed( - actual = stateTurbine.awaitItem(), - turbines = turbines, - ) { - isEqualTo(State()) - } - - viewModel.event(Event.OnNextClicked) - - assertThat(stateTurbine.awaitItem()).isEqualTo( - State( - accountName = StringInputField(value = "", isValid = true), - displayName = StringInputField(value = "", isValid = true), - emailSignature = StringInputField(value = "", isValid = true), - ), - ) - - assertThatAndTurbinesConsumed( - actual = effectTurbine.awaitItem(), - turbines = turbines, - ) { - isEqualTo(Effect.NavigateNext) - } - } - - @Test - fun `should change state and not emit effect when OnNextClicked event received and input invalid`() = - runTest { - val viewModel = AccountOptionsViewModel( - validator = FakeAccountOptionsValidator( - accountNameAnswer = ValidationResult.Failure(TestError), - ), - accountStateRepository = InMemoryAccountStateRepository(), - ) - val stateTurbine = viewModel.state.testIn(backgroundScope) - val effectTurbine = viewModel.effect.testIn(backgroundScope) - val turbines = listOf(stateTurbine, effectTurbine) - - assertThatAndTurbinesConsumed( - actual = stateTurbine.awaitItem(), - turbines = turbines, - ) { - isEqualTo(State()) - } - - viewModel.event(Event.OnNextClicked) - - assertThatAndTurbinesConsumed( - actual = stateTurbine.awaitItem(), - turbines = turbines, - ) { - isEqualTo( - State( - accountName = StringInputField(value = "", error = TestError, isValid = false), - displayName = StringInputField(value = "", isValid = true), - emailSignature = StringInputField(value = "", isValid = true), - ), - ) - } - } - - @Test - fun `should emit NavigateBack effect when OnBackClicked event received`() = runTest { - val viewModel = testSubject - val stateTurbine = viewModel.state.testIn(backgroundScope) - val effectTurbine = viewModel.effect.testIn(backgroundScope) - val turbines = listOf(stateTurbine, effectTurbine) - - assertThatAndTurbinesConsumed( - actual = stateTurbine.awaitItem(), - turbines = turbines, - ) { - isEqualTo(State()) - } - - viewModel.event(Event.OnBackClicked) - - assertThatAndTurbinesConsumed( - actual = effectTurbine.awaitItem(), - turbines = turbines, - ) { - isEqualTo(Effect.NavigateBack) - } - } - - private object TestError : ValidationError -} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsValidator.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsValidator.kt deleted file mode 100644 index bd65543c2da..00000000000 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsValidator.kt +++ /dev/null @@ -1,14 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.core.common.domain.usecase.validation.ValidationResult -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Validator - -internal class FakeAccountOptionsValidator( - private val accountNameAnswer: ValidationResult = ValidationResult.Success, - private val displayNameAnswer: ValidationResult = ValidationResult.Success, - private val emailSignatureAnswer: ValidationResult = ValidationResult.Success, -) : Validator { - override fun validateAccountName(accountName: String): ValidationResult = accountNameAnswer - override fun validateDisplayName(displayName: String): ValidationResult = displayNameAnswer - override fun validateEmailSignature(emailSignature: String): ValidationResult = emailSignatureAnswer -} diff --git a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsViewModel.kt b/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsViewModel.kt deleted file mode 100644 index e715283ef1a..00000000000 --- a/feature/account/setup/src/test/kotlin/app/k9mail/feature/account/setup/ui/options/FakeAccountOptionsViewModel.kt +++ /dev/null @@ -1,22 +0,0 @@ -package app.k9mail.feature.account.setup.ui.options - -import app.k9mail.core.ui.compose.common.mvi.BaseViewModel -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Effect -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.Event -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.State -import app.k9mail.feature.account.setup.ui.options.AccountOptionsContract.ViewModel - -class FakeAccountOptionsViewModel( - initialState: State = State(), -) : BaseViewModel(initialState), ViewModel { - - val events = mutableListOf() - - override fun event(event: Event) { - events.add(event) - } - - fun effect(effect: Effect) { - emitEffect(effect) - } -}