Updated on 2026-08-14

This commit is contained in:
Tangem 2024-02-12 14:50:26 +01:00
parent 6ba6358b86
commit 67a80fc097
6 changed files with 103 additions and 11 deletions

View file

@ -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()
}
}

View file

@ -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,

View file

@ -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,

View file

@ -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()
}

View file

@ -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()

View file

@ -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)
}