From 67a80fc0978aae2b30d09084ba48716b1bb2b6aa Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 12 Feb 2024 14:50:26 +0100 Subject: [PATCH] Updated on 2026-08-14 --- .../addcustomtoken/state/CustomTokenData.kt | 4 ++ .../addcustomtoken/state/TextFieldState.kt | 2 + .../factory/AddCustomTokenStateFactory.kt | 30 ++++++++++++- .../addcustomtoken/ui/AddCustomTokenScreen.kt | 44 ++++++++++++++++++- .../addcustomtoken/ui/TokenTextField.kt | 17 ++++++- .../managetokens/ui/ManageTokensScreen.kt | 17 ++++--- 6 files changed, 103 insertions(+), 11 deletions(-) diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt index ec93de1d4c..b481d6767a 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/CustomTokenData.kt @@ -11,4 +11,8 @@ internal data class CustomTokenData( return contractAddressTextField.isInputValid() && nameTextField.isInputValid() && symbolTextField.isInputValid() && decimalsTextField.isInputValid() } + + fun isNameSymbolDecimalsDisabled(): Boolean { + return nameTextField.isDisabled() && symbolTextField.isDisabled() && decimalsTextField.isDisabled() + } } \ No newline at end of file diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt index 2e7f2b1168..3068a0b6e9 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/TextFieldState.kt @@ -13,6 +13,8 @@ internal sealed class TextFieldState { fun isInputValid(): Boolean = this is Editable && value.isNotBlank() && error == null + fun isDisabled() = this is Editable && !this.isEnabled + fun copySealed( value: String = (this as? Editable)?.value ?: "", isEnabled: Boolean = (this as? Editable)?.isEnabled ?: true, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt index 57ce303a3c..7fe3b1dc8c 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/state/factory/AddCustomTokenStateFactory.kt @@ -176,6 +176,32 @@ internal class AddCustomTokenStateFactory( return currentStateProvider().copy(tokenData = tokenData.copy(contractAddressTextField = contractAddressField)) } + private fun unlockAndClearNameSymbolAndDecimals(state: AddCustomTokenState): AddCustomTokenState { + val currentTokenData = state.tokenData + return state.copy( + tokenData = currentTokenData?.copy( + nameTextField = TextFieldState.Editable( + value = "", + isEnabled = true, + onValueChange = clickIntents::onTokenNameChange, + onFocusExit = clickIntents::onTokenNameFocusExit, + ), + symbolTextField = TextFieldState.Editable( + value = "", + isEnabled = true, + onValueChange = clickIntents::onSymbolChange, + onFocusExit = clickIntents::onSymbolFocusExit, + ), + decimalsTextField = TextFieldState.Editable( + value = "", + isEnabled = true, + onValueChange = clickIntents::onDecimalsChange, + onFocusExit = clickIntents::onDecimalsFocusExit, + ), + ), + ) + } + fun getStateAndTriggerEvent( state: AddCustomTokenState, event: Event, @@ -286,7 +312,7 @@ internal class AddCustomTokenStateFactory( val uiState = currentStateProvider() return when (error) { AddCustomTokenError.INVALID_CONTRACT_ADDRESS -> { - addTokenAddressFieldError(AddCustomTokenWarning.InvalidContractAddress) + removeTokenAddressError().also { unlockAndClearNameSymbolAndDecimals(it) } .copy( addTokenButton = uiState.addTokenButton.copy(isEnabled = false), warnings = uiState.warnings @@ -295,7 +321,7 @@ internal class AddCustomTokenStateFactory( ) } AddCustomTokenError.FIELD_IS_EMPTY -> - removeTokenAddressError() + removeTokenAddressError().also { unlockAndClearNameSymbolAndDecimals(it) } .copy( addTokenButton = uiState.addTokenButton.copy( isEnabled = uiState.tokenData?.isRequiredInformationProvided() == true, diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt index d3291937f0..d335aaca71 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/AddCustomTokenScreen.kt @@ -4,11 +4,16 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.runtime.* +import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.focus.FocusDirection +import androidx.compose.ui.platform.LocalFocusManager import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview @@ -154,8 +159,11 @@ private fun ColumnScope.CustomTokenItemsList(state: AddCustomTokenState, modifie } } +@OptIn(ExperimentalComposeUiApi::class) @Composable private fun TokenFields(state: CustomTokenData, modifier: Modifier = Modifier) { + val focusManager = LocalFocusManager.current + Column( modifier = modifier .fillMaxWidth() @@ -166,21 +174,51 @@ private fun TokenFields(state: CustomTokenData, modifier: Modifier = Modifier) { textFieldState = state.contractAddressTextField, placeholder = "0x0000000000000000000000000000000", title = R.string.custom_token_contract_address_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = if (state.isNameSymbolDecimalsDisabled()) { + ImeAction.Done + } else { + ImeAction.Next + }, + ), + onImeAction = { + if (state.isNameSymbolDecimalsDisabled()) { + focusManager.moveFocus(FocusDirection.Exit) + } else { + focusManager.moveFocus(FocusDirection.Down) + } + }, ) TokenField( textFieldState = state.nameTextField, placeholder = stringResource(id = R.string.custom_token_name_input_placeholder), title = R.string.custom_token_name_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Next, + ), + onImeAction = { focusManager.moveFocus(FocusDirection.Down) }, ) TokenField( textFieldState = state.symbolTextField, placeholder = stringResource(id = R.string.custom_token_token_symbol_input_placeholder), title = R.string.custom_token_token_symbol_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Next, + ), + onImeAction = { focusManager.moveFocus(FocusDirection.Down) }, ) TokenField( textFieldState = state.decimalsTextField, placeholder = "0", title = R.string.custom_token_decimals_input_title, + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done, + ), + onImeAction = { focusManager.moveFocus(FocusDirection.Exit) }, ) } } @@ -190,7 +228,8 @@ private fun TokenField( textFieldState: TextFieldState, placeholder: String, title: Int, - keyboardType: KeyboardType = KeyboardType.Text, + onImeAction: () -> Unit, + keyboardOptions: KeyboardOptions, ) { Column( modifier = Modifier @@ -205,7 +244,8 @@ private fun TokenField( is TextFieldState.Editable -> TokenTextField( state = textFieldState, placeholder = placeholder, - keyboardType = keyboardType, + onImeAction = onImeAction, + keyboardOptions = keyboardOptions, ) is TextFieldState.Loading -> TokenShimmer() } diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt index d794ab5890..63521c6775 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/addcustomtoken/ui/TokenTextField.kt @@ -12,6 +12,7 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.SolidColor +import androidx.compose.ui.input.key.* import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType @@ -22,7 +23,11 @@ import com.tangem.managetokens.presentation.addcustomtoken.state.TextFieldState internal fun TokenTextField( state: TextFieldState.Editable, placeholder: String, - keyboardType: KeyboardType = KeyboardType.Text, + onImeAction: () -> Unit, + keyboardOptions: KeyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Text, + imeAction = ImeAction.Default, + ), ) { val isInitiallyComposed = remember { mutableStateOf(false) } LaunchedEffect(key1 = true) { @@ -32,7 +37,7 @@ internal fun TokenTextField( BasicTextField( value = state.value, onValueChange = state.onValueChange, - keyboardOptions = KeyboardOptions(keyboardType = keyboardType, imeAction = ImeAction.Default), + keyboardOptions = keyboardOptions, singleLine = true, maxLines = 1, textStyle = TangemTheme.typography.subtitle1.copy( @@ -42,6 +47,14 @@ internal fun TokenTextField( cursorBrush = SolidColor(TangemTheme.colors.icon.primary1), modifier = Modifier .fillMaxWidth() + .onKeyEvent { keyEvent -> + if (keyEvent.type == KeyEventType.KeyUp && keyEvent.key == Key.Enter) { + onImeAction() + true + } else { + false + } + } .onFocusChanged { if (!it.isFocused && isInitiallyComposed.value) { state.onFocusExit() diff --git a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt index 77c91a26e3..0f83676b10 100644 --- a/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt +++ b/features/manage-tokens/impl/src/main/java/com/tangem/managetokens/presentation/managetokens/ui/ManageTokensScreen.kt @@ -14,7 +14,9 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import androidx.paging.LoadState import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems +import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.res.TangemTheme import com.tangem.managetokens.presentation.common.state.AlertState import com.tangem.managetokens.presentation.common.state.ChooseWalletState @@ -46,6 +48,7 @@ internal fun ManageTokensScreen(state: ManageTokensState) { @Composable private fun Content(state: ManageTokensState) { + val keyboard by keyboardAsState() Box( modifier = Modifier .fillMaxSize() @@ -88,13 +91,17 @@ private fun Content(state: ManageTokensState) { TokensList(tokens = tokens, addCustomTokenButton = state.addCustomTokenButton) } + state.derivationNotification?.let { - DerivationNotification( - config = it.config, - modifier = Modifier - .align(Alignment.BottomCenter), - ) + if (keyboard is Keyboard.Closed) { + DerivationNotification( + config = it.config, + modifier = Modifier + .align(Alignment.BottomCenter), + ) + } } + state.selectedToken?.let { selectedToken -> ManageTokensBottomSheet(selectedToken = selectedToken, state = state) }