Skip to content

Commit

Permalink
refactor: update text input logic to v2 TextFieldState, part 3 [WPB-8…
Browse files Browse the repository at this point in the history
…779] (#3054)
  • Loading branch information
saleniuk authored May 31, 2024
1 parent d6cdb11 commit dea8a2f
Show file tree
Hide file tree
Showing 22 changed files with 331 additions and 277 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand All @@ -33,8 +35,6 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import com.ramcosta.composedestinations.annotation.Destination
Expand All @@ -53,6 +53,7 @@ import com.wire.android.ui.common.dialogs.CancelLoginDialogContent
import com.wire.android.ui.common.dialogs.CancelLoginDialogState
import com.wire.android.ui.common.error.CoreFailureErrorDialog
import com.wire.android.ui.common.scaffold.WireScaffold
import com.wire.android.ui.common.textfield.DefaultPassword
import com.wire.android.ui.common.textfield.WirePasswordTextField
import com.wire.android.ui.common.textfield.WireTextFieldState
import com.wire.android.ui.common.textfield.clearAutofillTree
Expand All @@ -63,18 +64,21 @@ import com.wire.android.ui.destinations.E2EIEnrollmentScreenDestination
import com.wire.android.ui.destinations.HomeScreenDestination
import com.wire.android.ui.destinations.InitialSyncScreenDestination
import com.wire.android.ui.destinations.RemoveDeviceScreenDestination
import com.wire.android.ui.theme.WireTheme
import com.wire.android.ui.theme.wireDimensions
import com.wire.android.ui.theme.wireTypography
import com.wire.android.util.ui.PreviewMultipleThemes

@RootNavGraph
@Destination(
style = PopUpNavigationAnimation::class,
)
@Composable
fun RegisterDeviceScreen(navigator: Navigator) {
val viewModel: RegisterDeviceViewModel = hiltViewModel()
val clearSessionViewModel: ClearSessionViewModel = hiltViewModel()
val clearSessionState: ClearSessionState = clearSessionViewModel.state
fun RegisterDeviceScreen(
navigator: Navigator,
viewModel: RegisterDeviceViewModel = hiltViewModel(),
clearSessionViewModel: ClearSessionViewModel = hiltViewModel(),
) {
clearAutofillTree()
when (val flowState = viewModel.state.flowState) {
is RegisterDeviceFlowState.Success -> {
Expand All @@ -92,8 +96,8 @@ fun RegisterDeviceScreen(navigator: Navigator) {
else ->
RegisterDeviceContent(
state = viewModel.state,
clearSessionState = clearSessionState,
onPasswordChange = viewModel::onPasswordChange,
passwordTextState = viewModel.passwordTextState,
clearSessionState = clearSessionViewModel.state,
onContinuePressed = viewModel::onContinue,
onErrorDismiss = viewModel::onErrorDismiss,
onBackButtonClicked = clearSessionViewModel::onBackButtonClicked,
Expand All @@ -106,13 +110,14 @@ fun RegisterDeviceScreen(navigator: Navigator) {
@Composable
private fun RegisterDeviceContent(
state: RegisterDeviceState,
passwordTextState: TextFieldState,
clearSessionState: ClearSessionState,
onPasswordChange: (TextFieldValue) -> Unit,
onContinuePressed: () -> Unit,
onErrorDismiss: () -> Unit,
onBackButtonClicked: () -> Unit,
onCancelLoginClicked: () -> Unit,
onProceedLoginClicked: () -> Unit
onProceedLoginClicked: () -> Unit,
modifier: Modifier = Modifier,
) {
BackHandler {
onBackButtonClicked()
Expand All @@ -136,6 +141,7 @@ private fun RegisterDeviceContent(
}

WireScaffold(
modifier = modifier,
topBar = {
WireCenterAlignedTopAppBar(
elevation = 0.dp,
Expand All @@ -161,7 +167,7 @@ private fun RegisterDeviceContent(
)
.testTag("registerText")
)
PasswordTextField(state = state, onPasswordChange = onPasswordChange)
PasswordTextField(state = state, passwordTextState = passwordTextState)
Spacer(modifier = Modifier.weight(1f))
WirePrimaryButton(
text = stringResource(R.string.label_add_device),
Expand All @@ -182,28 +188,31 @@ private fun RegisterDeviceContent(
}

@Composable
private fun PasswordTextField(state: RegisterDeviceState, onPasswordChange: (TextFieldValue) -> Unit) {
private fun PasswordTextField(
state: RegisterDeviceState,
passwordTextState: TextFieldState,
modifier: Modifier = Modifier,
) {
val keyboardController = LocalSoftwareKeyboardController.current
WirePasswordTextField(
value = state.password,
onValueChange = onPasswordChange,
textState = passwordTextState,
state = when (state.flowState) {
is RegisterDeviceFlowState.Error.InvalidCredentialsError ->
WireTextFieldState.Error(stringResource(id = R.string.remove_device_invalid_password))

else -> WireTextFieldState.Default
},
imeAction = ImeAction.Done,
onImeAction = { keyboardController?.hide() },
modifier = Modifier
keyboardOptions = KeyboardOptions.DefaultPassword.copy(imeAction = ImeAction.Done),
onKeyboardAction = { keyboardController?.hide() },
modifier = modifier
.padding(horizontal = MaterialTheme.wireDimensions.spacing16x)
.testTag("password field"),
autofill = true
autoFill = true
)
}

@Composable
@Preview
fun PreviewRegisterDeviceScreen() {
RegisterDeviceContent(RegisterDeviceState(), ClearSessionState(), {}, {}, {}, {}, {}, {})
@PreviewMultipleThemes
fun PreviewRegisterDeviceScreen() = WireTheme {
RegisterDeviceContent(RegisterDeviceState(), TextFieldState(), ClearSessionState(), {}, {}, {}, {}, {})
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,19 @@

package com.wire.android.ui.authentication.devices.register

import androidx.compose.ui.text.input.TextFieldValue
import com.wire.kalium.logic.CoreFailure
import com.wire.kalium.logic.data.conversation.ClientId
import com.wire.kalium.logic.data.user.UserId

data class RegisterDeviceState(
val password: TextFieldValue = TextFieldValue(""),
val continueEnabled: Boolean = false,
val flowState: RegisterDeviceFlowState = RegisterDeviceFlowState.Default
)

sealed class RegisterDeviceFlowState {
object Default : RegisterDeviceFlowState()
object Loading : RegisterDeviceFlowState()
object TooManyDevices : RegisterDeviceFlowState()
data object Default : RegisterDeviceFlowState()
data object Loading : RegisterDeviceFlowState()
data object TooManyDevices : RegisterDeviceFlowState()
data class Success(
val initialSyncCompleted: Boolean,
val isE2EIRequired: Boolean,
Expand All @@ -41,7 +39,7 @@ sealed class RegisterDeviceFlowState {
) : RegisterDeviceFlowState()

sealed class Error : RegisterDeviceFlowState() {
object InvalidCredentialsError : Error()
data object InvalidCredentialsError : Error()
data class GenericError(val coreFailure: CoreFailure) : Error()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,22 @@

package com.wire.android.ui.authentication.devices.register

import androidx.compose.foundation.text.input.TextFieldState
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.ViewModel
import androidx.lifecycle.viewModelScope
import com.wire.android.BuildConfig
import com.wire.android.datastore.UserDataStore
import com.wire.android.ui.common.textfield.textAsFlow
import com.wire.kalium.logic.feature.client.GetOrRegisterClientUseCase
import com.wire.kalium.logic.feature.client.RegisterClientResult
import com.wire.kalium.logic.feature.client.RegisterClientUseCase
import com.wire.kalium.logic.feature.user.IsPasswordRequiredUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
Expand All @@ -43,6 +46,7 @@ class RegisterDeviceViewModel @Inject constructor(
private val userDataStore: UserDataStore,
) : ViewModel() {

val passwordTextState: TextFieldState = TextFieldState()
var state: RegisterDeviceState by mutableStateOf(RegisterDeviceState())
private set

Expand All @@ -62,11 +66,10 @@ class RegisterDeviceViewModel @Inject constructor(
}
}
}
}

fun onPasswordChange(newText: TextFieldValue) {
if (state.password != newText) {
state = state.copy(password = newText, flowState = RegisterDeviceFlowState.Default, continueEnabled = newText.text.isNotEmpty())
viewModelScope.launch {
passwordTextState.textAsFlow().distinctUntilChanged().collectLatest {
state = state.copy(flowState = RegisterDeviceFlowState.Default, continueEnabled = it.isNotEmpty())
}
}
}

Expand Down Expand Up @@ -122,7 +125,7 @@ class RegisterDeviceViewModel @Inject constructor(

fun onContinue() {
viewModelScope.launch {
registerClient(state.password.text)
registerClient(passwordTextState.text.toString())
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
package com.wire.android.ui.authentication.devices.remove

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
Expand All @@ -30,12 +32,12 @@ import androidx.compose.ui.platform.SoftwareKeyboardController
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.TextFieldValue
import com.wire.android.R
import com.wire.android.ui.common.WireDialog
import com.wire.android.ui.common.WireDialogButtonProperties
import com.wire.android.ui.common.WireDialogButtonType
import com.wire.android.ui.common.button.WireButtonState
import com.wire.android.ui.common.textfield.DefaultPassword
import com.wire.android.ui.common.textfield.WirePasswordTextField
import com.wire.android.ui.common.textfield.WireTextFieldState
import com.wire.android.ui.theme.wireDimensions
Expand All @@ -45,16 +47,18 @@ import com.wire.android.util.deviceDateTimeFormat
fun RemoveDeviceDialog(
errorState: RemoveDeviceError,
state: RemoveDeviceDialogState.Visible,
onPasswordChange: (TextFieldValue) -> Unit,
passwordTextState: TextFieldState,
onDialogDismiss: () -> Unit,
onRemoveConfirm: () -> Unit
onRemoveConfirm: () -> Unit,
modifier: Modifier = Modifier,
) {
var keyboardController: SoftwareKeyboardController? = null
val onDialogDismissHideKeyboard: () -> Unit = {
keyboardController?.hide()
onDialogDismiss()
}
WireDialog(
modifier = modifier,
title = stringResource(R.string.remove_device_dialog_title),
text = state.device.name.asString() + "\n" +
stringResource(
Expand Down Expand Up @@ -84,22 +88,21 @@ fun RemoveDeviceDialog(
keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember { FocusRequester() }
WirePasswordTextField(
value = state.password,
onValueChange = onPasswordChange,
textState = passwordTextState,
state = when {
errorState is RemoveDeviceError.InvalidCredentialsError ->
WireTextFieldState.Error(stringResource(id = R.string.remove_device_invalid_password))

state.loading -> WireTextFieldState.Disabled
else -> WireTextFieldState.Default
},
imeAction = ImeAction.Done,
onImeAction = { keyboardController?.hide() },
keyboardOptions = KeyboardOptions.DefaultPassword.copy(imeAction = ImeAction.Done),
onKeyboardAction = { keyboardController?.hide() },
modifier = Modifier
.focusRequester(focusRequester)
.padding(bottom = MaterialTheme.wireDimensions.spacing8x)
.testTag("remove device password field"),
autofill = true
autoFill = true
)
LaunchedEffect(Unit) { // executed only once when showing the dialog
focusRequester.requestFocus()
Expand Down
Loading

0 comments on commit dea8a2f

Please sign in to comment.