Updated on 2026-08-14
This commit is contained in:
parent
2f2a8b04b5
commit
37a224169c
13 changed files with 75 additions and 33 deletions
|
|
@ -64,6 +64,8 @@ fun AmountTextField(
|
|||
keyboardActions: KeyboardActions = KeyboardActions.Default,
|
||||
isEnabled: Boolean = true,
|
||||
isAutoResize: Boolean = false,
|
||||
isValuePasted: Boolean = false,
|
||||
onValuePastedTriggerDismiss: () -> Unit = {},
|
||||
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
|
||||
reduceFactor: Double = 0.9,
|
||||
) {
|
||||
|
|
@ -101,6 +103,8 @@ fun AmountTextField(
|
|||
fontSize = fontSize,
|
||||
textDirection = TextDirection.ContentOrLtr,
|
||||
),
|
||||
isValuePasted = isValuePasted,
|
||||
onValuePastedTriggerDismiss = onValuePastedTriggerDismiss,
|
||||
color = textColor,
|
||||
keyboardOptions = keyboardOptions,
|
||||
keyboardActions = keyboardActions,
|
||||
|
|
|
|||
|
|
@ -39,15 +39,13 @@ fun SimpleTextField(
|
|||
color: Color = TangemTheme.colors.text.primary1,
|
||||
textStyle: TextStyle = TangemTheme.typography.body2.copy(color = color),
|
||||
readOnly: Boolean = false,
|
||||
isValuePasted: Boolean = false,
|
||||
onValuePastedTriggerDismiss: () -> Unit = {},
|
||||
decorationBox: (@Composable (innerTextField: @Composable () -> Unit) -> Unit)? = null,
|
||||
) {
|
||||
val proxyValue by remember(value) { derivedStateOf { value } }
|
||||
var textFieldValueState by remember {
|
||||
mutableStateOf(
|
||||
TextFieldValue(
|
||||
text = value,
|
||||
selection = getValueRange(value),
|
||||
),
|
||||
)
|
||||
mutableStateOf(TextFieldValue(text = value))
|
||||
}
|
||||
val focusRequester = remember { FocusRequester.Default }
|
||||
val customTextSelectionColors = TextSelectionColors(
|
||||
|
|
@ -55,36 +53,28 @@ fun SimpleTextField(
|
|||
backgroundColor = TangemTheme.colors.text.accent.copy(alpha = 0.3f),
|
||||
)
|
||||
|
||||
val textFieldValue = textFieldValueState.copy(text = value)
|
||||
|
||||
val isSelectionChanged by remember {
|
||||
derivedStateOf {
|
||||
textFieldValue.selection != textFieldValueState.selection ||
|
||||
textFieldValue.composition != textFieldValueState.composition ||
|
||||
textFieldValue.text != textFieldValueState.text
|
||||
}
|
||||
var lastTextValue by remember(proxyValue, isValuePasted) {
|
||||
textFieldValueState = textFieldValueState.copy(
|
||||
text = proxyValue,
|
||||
selection = if (isValuePasted) {
|
||||
TextRange(proxyValue.length, proxyValue.length)
|
||||
} else {
|
||||
textFieldValueState.selection
|
||||
},
|
||||
)
|
||||
mutableStateOf(proxyValue)
|
||||
}
|
||||
|
||||
LaunchedEffect(key1 = isSelectionChanged) {
|
||||
if (isSelectionChanged) {
|
||||
textFieldValueState = textFieldValue
|
||||
// resets paste value cursor trigger
|
||||
LaunchedEffect(key1 = isValuePasted) {
|
||||
if (isValuePasted) {
|
||||
onValuePastedTriggerDismiss()
|
||||
}
|
||||
}
|
||||
|
||||
var lastTextValue by remember(value) {
|
||||
val isSelectionLastIndex = textFieldValueState.selection.end == textFieldValueState.text.lastIndex
|
||||
if (textFieldValueState.text.isBlank() || isSelectionLastIndex) {
|
||||
textFieldValueState = textFieldValueState.copy(
|
||||
text = value,
|
||||
selection = getValueRange(value),
|
||||
)
|
||||
}
|
||||
mutableStateOf(value)
|
||||
}
|
||||
|
||||
CompositionLocalProvider(LocalTextSelectionColors provides customTextSelectionColors) {
|
||||
BasicTextField(
|
||||
value = textFieldValue,
|
||||
value = textFieldValueState,
|
||||
onValueChange = { newTextFieldValueState ->
|
||||
textFieldValueState = newTextFieldValueState
|
||||
|
||||
|
|
@ -114,10 +104,12 @@ fun SimpleTextField(
|
|||
}
|
||||
}
|
||||
|
||||
private fun getValueRange(value: String) = when {
|
||||
value.isEmpty() -> TextRange.Zero
|
||||
else -> TextRange(value.length, value.length)
|
||||
}
|
||||
// private fun TextRange.getValueRange(oldValue: String, newValue: String) = when {
|
||||
// newValue.isEmpty() -> TextRange.Zero
|
||||
// newValue.length - oldValue.length > 1 -> TextRange(newValue.length, newValue.length)
|
||||
// newValue.length - oldValue.length > 0 -> TextRange(this.start.inc(), this.end.inc())
|
||||
// else -> this
|
||||
// }
|
||||
|
||||
@Composable
|
||||
private fun SimpleTextPlaceholder(
|
||||
|
|
|
|||
|
|
@ -33,6 +33,11 @@ internal class AmountStateFactory(
|
|||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
)
|
||||
}
|
||||
private val amountPasteConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
SendAmountPastedTriggerDismissConverter(
|
||||
currentStateProvider = currentStateProvider,
|
||||
)
|
||||
}
|
||||
|
||||
fun getOnAmountValueChange(value: String) = amountFieldChangeConverter.convert(value)
|
||||
|
||||
|
|
@ -41,4 +46,6 @@ internal class AmountStateFactory(
|
|||
}
|
||||
|
||||
fun getOnCurrencyChangedState(isFiat: Boolean) = amountCurrencyConverter.convert(isFiat)
|
||||
|
||||
fun getOnAmountPastedTriggerDismiss() = amountPasteConverter.convert(false)
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ internal class SendAmountCurrencyConverter(
|
|||
amountState = amountState.copy(
|
||||
amountTextField = amountTextField.copy(
|
||||
isFiatValue = value,
|
||||
isValuePasted = true,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
package com.tangem.features.send.impl.presentation.state.amount
|
||||
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class SendAmountPastedTriggerDismissConverter(
|
||||
private val currentStateProvider: Provider<SendUiState>,
|
||||
) : Converter<Boolean, SendUiState> {
|
||||
override fun convert(value: Boolean): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
val amountState = state.amountState ?: return state
|
||||
return state.copy(
|
||||
amountState = amountState.copy(
|
||||
amountTextField = amountState.amountTextField.copy(
|
||||
isValuePasted = false,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -55,6 +55,8 @@ internal class SendAmountFieldConverter(
|
|||
isError = false,
|
||||
error = TextReference.Res(R.string.swapping_insufficient_funds),
|
||||
isFiatUnavailable = fiatRate == null,
|
||||
isValuePasted = false,
|
||||
onValuePastedTriggerDismiss = clickIntents::onAmountPasteTriggerDismiss,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ internal class SendAmountFieldMaxAmountConverter(
|
|||
amountState = amountState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
amountTextField = amountTextField.copy(
|
||||
isValuePasted = true,
|
||||
value = cryptoValue,
|
||||
fiatValue = fiatValue,
|
||||
isError = false,
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ internal sealed class SendTextField {
|
|||
val isFiatValue: Boolean,
|
||||
val fiatValue: String,
|
||||
val isFiatUnavailable: Boolean,
|
||||
val isValuePasted: Boolean,
|
||||
val onValuePastedTriggerDismiss: () -> Unit,
|
||||
val isError: Boolean,
|
||||
val error: TextReference,
|
||||
) : SendTextField()
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ internal object AmountStatePreviewData {
|
|||
isFiatUnavailable = false,
|
||||
isError = false,
|
||||
error = TextReference.EMPTY,
|
||||
isValuePasted = false,
|
||||
onValuePastedTriggerDismiss = {},
|
||||
),
|
||||
isSegmentedButtonsEnabled = true,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ internal object SendClickIntentsStub : SendClickIntents {
|
|||
|
||||
override fun onMaxValueClick() {}
|
||||
|
||||
override fun onAmountPasteTriggerDismiss() {}
|
||||
|
||||
override fun onRecipientAddressValueChange(value: String, type: EnterAddressSource?) {}
|
||||
|
||||
override fun onRecipientMemoValueChange(value: String) {}
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ internal fun AmountField(sendField: SendTextField.AmountField, appCurrencyCode:
|
|||
textAlign = TextAlign.Center,
|
||||
),
|
||||
isAutoResize = true,
|
||||
isValuePasted = sendField.isValuePasted,
|
||||
onValuePastedTriggerDismiss = sendField.onValuePastedTriggerDismiss,
|
||||
modifier = Modifier
|
||||
.focusRequester(requester)
|
||||
.padding(
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ internal interface SendClickIntents {
|
|||
fun onCurrencyChangeClick(isFiat: Boolean)
|
||||
|
||||
fun onMaxValueClick()
|
||||
|
||||
fun onAmountPasteTriggerDismiss()
|
||||
// endregion
|
||||
|
||||
// region Recipient
|
||||
|
|
|
|||
|
|
@ -551,6 +551,10 @@ internal class SendViewModel @Inject constructor(
|
|||
uiState = amountStateFactory.getOnMaxAmountClick()
|
||||
analyticsEventHandler.send(SendAnalyticEvents.MaxAmountButtonClicked)
|
||||
}
|
||||
|
||||
override fun onAmountPasteTriggerDismiss() {
|
||||
uiState = amountStateFactory.getOnAmountPastedTriggerDismiss()
|
||||
}
|
||||
// endregion
|
||||
|
||||
// region recipient state clicks
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue