Add DisplayOptionsScreen
This commit is contained in:
parent
b9a7d17bba
commit
6dc1405616
27 changed files with 815 additions and 14 deletions
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package app.k9mail.feature.account.common.domain.entity
|
||||
|
||||
data class AccountDisplayOptions(
|
||||
val accountName: String,
|
||||
val displayName: String,
|
||||
val emailSignature: String?,
|
||||
)
|
|
@ -10,4 +10,5 @@ data class AccountState(
|
|||
val authorizationState: AuthorizationState? = null,
|
||||
val specialFolderSettings: SpecialFolderSettings? = null,
|
||||
val options: AccountOptions? = null,
|
||||
val displayOptions: AccountDisplayOptions? = null,
|
||||
)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<SpecialFoldersViewModel>(),
|
||||
)
|
||||
}
|
||||
|
||||
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<DisplayOptionsViewModel>(),
|
||||
)
|
||||
}
|
||||
|
||||
composable(route = NESTED_NAVIGATION_SYNC_OPTIONS) {
|
||||
AccountOptionsScreen(
|
||||
onNext = { navController.navigate(NESTED_NAVIGATION_CREATE_ACCOUNT) },
|
||||
onBack = { navController.popBackStack() },
|
||||
|
|
|
@ -73,7 +73,7 @@ internal fun AccountOptionsScreenK9Preview() {
|
|||
onNext = {},
|
||||
onBack = {},
|
||||
viewModel = CreateAccountViewModel(
|
||||
createAccount = { _, _, _, _, _, _ -> AccountCreatorResult.Success("irrelevant") },
|
||||
createAccount = { _, _, _, _, _, _, _ -> AccountCreatorResult.Success("irrelevant") },
|
||||
accountStateRepository = InMemoryAccountStateRepository(),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -40,6 +40,7 @@ class CreateAccountViewModel(
|
|||
authorizationState = accountState.authorizationState?.state,
|
||||
specialFolderSettings = accountState.specialFolderSettings,
|
||||
options = accountState.options!!,
|
||||
displayOptions = accountState.displayOptions!!,
|
||||
)
|
||||
|
||||
when (result) {
|
||||
|
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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<State, Event, Effect>
|
||||
|
||||
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
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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() },
|
||||
)
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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<State, Event, Effect>(
|
||||
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)
|
||||
}
|
|
@ -44,8 +44,8 @@
|
|||
<string name="account_setup_options_section_display_options">Display options</string>
|
||||
<string name="account_setup_options_account_name_label">Account name</string>
|
||||
<string name="account_setup_options_account_name_error_blank">Account name can\'t be blank.</string>
|
||||
<string name="account_setup_options_display_name_label">Display name</string>
|
||||
<string name="account_setup_options_display_name_error_required">Display name is required.</string>
|
||||
<string name="account_setup_options_display_name_label">Your name</string>
|
||||
<string name="account_setup_options_display_name_error_required">Your name is required.</string>
|
||||
<string name="account_setup_options_email_signature_label">Email signature</string>
|
||||
<string name="account_setup_options_email_signature_error_blank">Email signature name can\'t be blank.</string>
|
||||
<string name="account_setup_options_section_sync_options_">Sync options</string>
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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(),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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<State, Event, Effect>(initialState), ViewModel {
|
||||
|
||||
val events = mutableListOf<Event>()
|
||||
|
||||
override fun event(event: Event) {
|
||||
events.add(event)
|
||||
}
|
||||
|
||||
fun effect(effect: Effect) {
|
||||
emitEffect(effect)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue