Skip to content

Commit

Permalink
refactor: update text input logic to v2 TextFieldState, part 2 [WPB-8…
Browse files Browse the repository at this point in the history
…779] (#3031)
  • Loading branch information
saleniuk authored May 29, 2024
1 parent a23ff9b commit ac41864
Show file tree
Hide file tree
Showing 27 changed files with 846 additions and 863 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,13 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
Expand All @@ -58,7 +58,6 @@ import com.wire.android.ui.common.WireDialogButtonProperties
import com.wire.android.ui.common.WireDialogButtonType
import com.wire.android.ui.common.progress.WireCircularProgressIndicator
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.textfield.CodeFieldValue
import com.wire.android.ui.common.textfield.CodeTextField
import com.wire.android.ui.common.textfield.WireTextFieldState
import com.wire.android.ui.common.topappbar.WireCenterAlignedTopAppBar
Expand Down Expand Up @@ -90,29 +89,44 @@ fun CreateAccountCodeScreen(

CodeContent(
state = codeState,
onCodeChange = { onCodeChange(it, ::navigateToSummaryScreen) },
textState = codeTextState,
onResendCodePressed = ::resendCode,
onBackPressed = navigator::navigateBack,
onErrorDismiss = ::clearCodeError,
onRemoveDeviceOpen = {
serverConfig = serverConfig
)

(codeState.result as? CreateAccountCodeViewState.Result.Error.DialogError)?.let {
val (title, message) = it.getResources(type = codeState.type)
WireDialog(
title = title,
text = message,
onDismiss = ::clearCodeError,
optionButton1Properties = WireDialogButtonProperties(
onClick = ::clearCodeError,
text = stringResource(id = R.string.label_ok),
type = WireDialogButtonType.Primary,
)
)
}
LaunchedEffect(codeState.result) {
if (codeState.result is CreateAccountCodeViewState.Result.Success) {
navigateToSummaryScreen()
}
if (codeState.result is CreateAccountCodeViewState.Result.Error.TooManyDevicesError) {
clearCodeError()
clearCodeField()
navigator.navigate(NavigationCommand(RemoveDeviceScreenDestination, BackStackMode.CLEAR_WHOLE))
},
serverConfig = serverConfig
)
}
}
}
}

@OptIn(ExperimentalComposeUiApi::class)
@Composable
private fun CodeContent(
state: CreateAccountCodeViewState,
onCodeChange: (CodeFieldValue) -> Unit,
textState: TextFieldState,
onResendCodePressed: () -> Unit,
onBackPressed: () -> Unit,
onErrorDismiss: () -> Unit,
onRemoveDeviceOpen: () -> Unit,
serverConfig: ServerConfig.Links
) {
val focusRequester = remember { FocusRequester() }
Expand Down Expand Up @@ -152,10 +166,10 @@ private fun CodeContent(
Spacer(modifier = Modifier.weight(1f))
Column(horizontalAlignment = Alignment.CenterHorizontally) {
CodeTextField(
value = state.code.text,
onValueChange = onCodeChange,
state = when (state.error) {
is CreateAccountCodeViewState.CodeError.TextFieldError.InvalidActivationCodeError ->
codeLength = state.codeLength,
textState = textState,
state = when (state.result) {
is CreateAccountCodeViewState.Result.Error.TextFieldError.InvalidActivationCodeError ->
WireTextFieldState.Error(stringResource(id = R.string.create_account_code_error))

else -> WireTextFieldState.Default
Expand All @@ -180,51 +194,36 @@ private fun CodeContent(
keyboardController?.show()
}
}
if (state.error is CreateAccountCodeViewState.CodeError.DialogError) {
val (title, message) = state.error.getResources(type = state.type)
WireDialog(
title = title,
text = message,
onDismiss = onErrorDismiss,
optionButton1Properties = WireDialogButtonProperties(
onClick = onErrorDismiss,
text = stringResource(id = R.string.label_ok),
type = WireDialogButtonType.Primary,
)
)
} else if (state.error is CreateAccountCodeViewState.CodeError.TooManyDevicesError) {
onRemoveDeviceOpen()
}
}

@Composable
private fun CreateAccountCodeViewState.CodeError.DialogError.getResources(type: CreateAccountFlowType) = when (this) {
CreateAccountCodeViewState.CodeError.DialogError.AccountAlreadyExistsError -> DialogErrorStrings(
private fun CreateAccountCodeViewState.Result.Error.DialogError.getResources(type: CreateAccountFlowType) = when (this) {
CreateAccountCodeViewState.Result.Error.DialogError.AccountAlreadyExistsError -> DialogErrorStrings(
stringResource(id = R.string.create_account_code_error_title),
stringResource(id = R.string.create_account_email_already_in_use_error)
)

CreateAccountCodeViewState.CodeError.DialogError.BlackListedError -> DialogErrorStrings(
CreateAccountCodeViewState.Result.Error.DialogError.BlackListedError -> DialogErrorStrings(
stringResource(id = R.string.create_account_code_error_title),
stringResource(id = R.string.create_account_email_blacklisted_error)
)

CreateAccountCodeViewState.CodeError.DialogError.EmailDomainBlockedError -> DialogErrorStrings(
CreateAccountCodeViewState.Result.Error.DialogError.EmailDomainBlockedError -> DialogErrorStrings(
stringResource(id = R.string.create_account_code_error_title),
stringResource(id = R.string.create_account_email_domain_blocked_error)
)

CreateAccountCodeViewState.CodeError.DialogError.InvalidEmailError -> DialogErrorStrings(
CreateAccountCodeViewState.Result.Error.DialogError.InvalidEmailError -> DialogErrorStrings(
stringResource(id = R.string.create_account_code_error_title),
stringResource(id = R.string.create_account_email_invalid_error)
)

CreateAccountCodeViewState.CodeError.DialogError.TeamMembersLimitError -> DialogErrorStrings(
CreateAccountCodeViewState.Result.Error.DialogError.TeamMembersLimitError -> DialogErrorStrings(
stringResource(id = R.string.create_account_code_error_title),
stringResource(id = R.string.create_account_code_error_team_members_limit_reached)
)

CreateAccountCodeViewState.CodeError.DialogError.CreationRestrictedError -> DialogErrorStrings(
CreateAccountCodeViewState.Result.Error.DialogError.CreationRestrictedError -> DialogErrorStrings(
stringResource(id = R.string.create_account_code_error_title),
stringResource(
id = when (type) {
Expand All @@ -234,15 +233,21 @@ private fun CreateAccountCodeViewState.CodeError.DialogError.getResources(type:
)
)
// TODO: sync with design about the error message
CreateAccountCodeViewState.CodeError.DialogError.UserAlreadyExists ->
CreateAccountCodeViewState.Result.Error.DialogError.UserAlreadyExistsError ->
DialogErrorStrings("User Already LoggedIn", "UserAlreadyLoggedIn")

is CreateAccountCodeViewState.CodeError.DialogError.GenericError ->
is CreateAccountCodeViewState.Result.Error.DialogError.GenericError ->
this.coreFailure.dialogErrorStrings(LocalContext.current.resources)
}

@Composable
@Preview
fun PreviewCreateAccountCodeScreen() {
CodeContent(CreateAccountCodeViewState(CreateAccountFlowType.CreatePersonalAccount), {}, {}, {}, {}, {}, ServerConfig.DEFAULT)
CodeContent(
textState = TextFieldState(),
state = CreateAccountCodeViewState(CreateAccountFlowType.CreatePersonalAccount),
onResendCodePressed = {},
onBackPressed = {},
serverConfig = ServerConfig.DEFAULT
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@
*/
package com.wire.android.ui.authentication.create.code

import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
Expand All @@ -30,7 +31,7 @@ import com.wire.android.di.ClientScopeProvider
import com.wire.android.di.KaliumCoreLogic
import com.wire.android.ui.authentication.create.common.CreateAccountFlowType
import com.wire.android.ui.authentication.create.common.CreateAccountNavArgs
import com.wire.android.ui.common.textfield.CodeFieldValue
import com.wire.android.ui.common.textfield.textAsFlow
import com.wire.android.ui.navArgs
import com.wire.android.util.WillNeverOccurError
import com.wire.kalium.logic.CoreLogic
Expand All @@ -44,6 +45,7 @@ import com.wire.kalium.logic.feature.register.RegisterParam
import com.wire.kalium.logic.feature.register.RegisterResult
import com.wire.kalium.logic.feature.register.RequestActivationCodeResult
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import javax.inject.Inject

Expand All @@ -61,11 +63,15 @@ class CreateAccountCodeViewModel @Inject constructor(

val serverConfig: ServerConfig.Links = authServerConfigProvider.authServer.value

val codeTextState: TextFieldState = TextFieldState()
var codeState: CreateAccountCodeViewState by mutableStateOf(CreateAccountCodeViewState(createAccountNavArgs.flowType))

fun onCodeChange(newValue: CodeFieldValue, onSuccess: () -> Unit) {
codeState = codeState.copy(code = newValue, error = CreateAccountCodeViewState.CodeError.None)
if (newValue.isFullyFilled) onCodeContinue(onSuccess)
init {
viewModelScope.launch {
codeTextState.textAsFlow().collectLatest {
if (it.length == codeState.codeLength) onCodeContinue()
}
}
}

fun resendCode() {
Expand All @@ -92,17 +98,17 @@ class CreateAccountCodeViewModel @Inject constructor(
}
}

val codeError = authScope.registerScope.requestActivationCode(createAccountNavArgs.userRegistrationInfo.email).toCodeError()
codeState = codeState.copy(loading = false, error = codeError)
val result = authScope.registerScope.requestActivationCode(createAccountNavArgs.userRegistrationInfo.email).toCodeError()
codeState = codeState.copy(loading = false, result = result)
}
}

fun clearCodeError() {
codeState = codeState.copy(error = CreateAccountCodeViewState.CodeError.None)
codeState = codeState.copy(result = CreateAccountCodeViewState.Result.None)
}

fun clearCodeField() {
codeState = codeState.copy(code = CodeFieldValue(text = TextFieldValue(""), isFullyFilled = false))
codeTextState.clearText()
}

private fun registerParamFromType() = when (createAccountNavArgs.flowType) {
Expand All @@ -112,7 +118,7 @@ class CreateAccountCodeViewModel @Inject constructor(
lastName = createAccountNavArgs.userRegistrationInfo.lastName,
password = createAccountNavArgs.userRegistrationInfo.password,
email = createAccountNavArgs.userRegistrationInfo.email,
emailActivationCode = codeState.code.text.text
emailActivationCode = codeTextState.text.toString()
)

CreateAccountFlowType.CreateTeam ->
Expand All @@ -121,14 +127,14 @@ class CreateAccountCodeViewModel @Inject constructor(
lastName = createAccountNavArgs.userRegistrationInfo.lastName,
password = createAccountNavArgs.userRegistrationInfo.password,
email = createAccountNavArgs.userRegistrationInfo.email,
emailActivationCode = codeState.code.text.text,
emailActivationCode = codeTextState.text.toString(),
teamName = createAccountNavArgs.userRegistrationInfo.teamName,
teamIcon = "default"
)
}

@Suppress("ComplexMethod")
private fun onCodeContinue(onSuccess: () -> Unit) {
private fun onCodeContinue() {
codeState = codeState.copy(loading = true)
viewModelScope.launch {
// create account does not support proxy yet
Expand Down Expand Up @@ -188,24 +194,20 @@ class CreateAccountCodeViewModel @Inject constructor(
}

is RegisterClientResult.Success -> {
onSuccess()
codeState = codeState.copy(result = CreateAccountCodeViewState.Result.Success)
}

is RegisterClientResult.E2EICertificateRequired -> {
// TODO
onSuccess()
codeState = codeState.copy(result = CreateAccountCodeViewState.Result.Success)
}
}
}
}
}

private fun updateCodeErrorState(codeError: CreateAccountCodeViewState.CodeError) {
codeState = if (codeError is CreateAccountCodeViewState.CodeError.None) {
codeState.copy(error = codeError)
} else {
codeState.copy(loading = false, error = codeError)
}
private fun updateCodeErrorState(codeError: CreateAccountCodeViewState.Result.Error) {
codeState = codeState.copy(loading = false, result = codeError)
}

private suspend fun registerClient(userId: UserId, password: String) =
Expand All @@ -218,8 +220,8 @@ class CreateAccountCodeViewModel @Inject constructor(
)

private fun RegisterClientResult.Failure.toCodeError() = when (this) {
is RegisterClientResult.Failure.TooManyClients -> CreateAccountCodeViewState.CodeError.TooManyDevicesError
is RegisterClientResult.Failure.Generic -> CreateAccountCodeViewState.CodeError.DialogError.GenericError(this.genericFailure)
is RegisterClientResult.Failure.TooManyClients -> CreateAccountCodeViewState.Result.Error.TooManyDevicesError
is RegisterClientResult.Failure.Generic -> CreateAccountCodeViewState.Result.Error.DialogError.GenericError(this.genericFailure)
is RegisterClientResult.Failure.InvalidCredentials ->
throw WillNeverOccurError("RegisterClient: wrong password when register client after creating a new account")

Expand All @@ -228,29 +230,30 @@ class CreateAccountCodeViewModel @Inject constructor(
}

private fun RegisterResult.Failure.toCodeError() = when (this) {
RegisterResult.Failure.InvalidActivationCode -> CreateAccountCodeViewState.CodeError.TextFieldError.InvalidActivationCodeError
RegisterResult.Failure.AccountAlreadyExists -> CreateAccountCodeViewState.CodeError.DialogError.AccountAlreadyExistsError
RegisterResult.Failure.BlackListed -> CreateAccountCodeViewState.CodeError.DialogError.BlackListedError
RegisterResult.Failure.EmailDomainBlocked -> CreateAccountCodeViewState.CodeError.DialogError.EmailDomainBlockedError
RegisterResult.Failure.InvalidEmail -> CreateAccountCodeViewState.CodeError.DialogError.InvalidEmailError
RegisterResult.Failure.TeamMembersLimitReached -> CreateAccountCodeViewState.CodeError.DialogError.TeamMembersLimitError
RegisterResult.Failure.UserCreationRestricted -> CreateAccountCodeViewState.CodeError.DialogError.CreationRestrictedError
is RegisterResult.Failure.Generic -> CreateAccountCodeViewState.CodeError.DialogError.GenericError(this.failure)
RegisterResult.Failure.InvalidActivationCode -> CreateAccountCodeViewState.Result.Error.TextFieldError.InvalidActivationCodeError
RegisterResult.Failure.AccountAlreadyExists -> CreateAccountCodeViewState.Result.Error.DialogError.AccountAlreadyExistsError
RegisterResult.Failure.BlackListed -> CreateAccountCodeViewState.Result.Error.DialogError.BlackListedError
RegisterResult.Failure.EmailDomainBlocked -> CreateAccountCodeViewState.Result.Error.DialogError.EmailDomainBlockedError
RegisterResult.Failure.InvalidEmail -> CreateAccountCodeViewState.Result.Error.DialogError.InvalidEmailError
RegisterResult.Failure.TeamMembersLimitReached -> CreateAccountCodeViewState.Result.Error.DialogError.TeamMembersLimitError
RegisterResult.Failure.UserCreationRestricted -> CreateAccountCodeViewState.Result.Error.DialogError.CreationRestrictedError
is RegisterResult.Failure.Generic -> CreateAccountCodeViewState.Result.Error.DialogError.GenericError(this.failure)
}

private fun AddAuthenticatedUserUseCase.Result.Failure.toCodeError() = when (this) {
is AddAuthenticatedUserUseCase.Result.Failure.Generic ->
CreateAccountCodeViewState.CodeError.DialogError.GenericError(this.genericFailure)
CreateAccountCodeViewState.Result.Error.DialogError.GenericError(this.genericFailure)

AddAuthenticatedUserUseCase.Result.Failure.UserAlreadyExists -> CreateAccountCodeViewState.CodeError.DialogError.UserAlreadyExists
AddAuthenticatedUserUseCase.Result.Failure.UserAlreadyExists ->
CreateAccountCodeViewState.Result.Error.DialogError.UserAlreadyExistsError
}

private fun RequestActivationCodeResult.toCodeError() = when (this) {
RequestActivationCodeResult.Failure.AlreadyInUse -> CreateAccountCodeViewState.CodeError.DialogError.AccountAlreadyExistsError
RequestActivationCodeResult.Failure.BlacklistedEmail -> CreateAccountCodeViewState.CodeError.DialogError.BlackListedError
RequestActivationCodeResult.Failure.DomainBlocked -> CreateAccountCodeViewState.CodeError.DialogError.EmailDomainBlockedError
RequestActivationCodeResult.Failure.InvalidEmail -> CreateAccountCodeViewState.CodeError.DialogError.InvalidEmailError
is RequestActivationCodeResult.Failure.Generic -> CreateAccountCodeViewState.CodeError.DialogError.GenericError(this.failure)
RequestActivationCodeResult.Success -> CreateAccountCodeViewState.CodeError.None
RequestActivationCodeResult.Failure.AlreadyInUse -> CreateAccountCodeViewState.Result.Error.DialogError.AccountAlreadyExistsError
RequestActivationCodeResult.Failure.BlacklistedEmail -> CreateAccountCodeViewState.Result.Error.DialogError.BlackListedError
RequestActivationCodeResult.Failure.DomainBlocked -> CreateAccountCodeViewState.Result.Error.DialogError.EmailDomainBlockedError
RequestActivationCodeResult.Failure.InvalidEmail -> CreateAccountCodeViewState.Result.Error.DialogError.InvalidEmailError
is RequestActivationCodeResult.Failure.Generic -> CreateAccountCodeViewState.Result.Error.DialogError.GenericError(this.failure)
RequestActivationCodeResult.Success -> CreateAccountCodeViewState.Result.None
}
}
Loading

0 comments on commit ac41864

Please sign in to comment.