Updated on 2026-08-14

This commit is contained in:
Tangem 2024-03-21 12:25:43 +05:00
parent b28720df71
commit 4d74432402
18 changed files with 680 additions and 393 deletions

View file

@ -18,6 +18,7 @@ fun AppBarWithBackButtonAndIcon(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
text: String? = null,
subtitle: String? = null,
@DrawableRes backIconRes: Int? = null,
@DrawableRes iconRes: Int? = null,
onIconClick: (() -> Unit)? = null,
@ -27,6 +28,7 @@ fun AppBarWithBackButtonAndIcon(
onBackClick = onBackClick,
modifier = modifier,
text = text,
subtitle = subtitle,
backIconRes = backIconRes,
backgroundColor = backgroundColor,
iconContent = {

View file

@ -1,10 +1,10 @@
package com.tangem.core.ui.components.appbar
import androidx.annotation.DrawableRes
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
@ -37,6 +37,7 @@ fun AppBarWithBackButtonAndIconContent(
onBackClick: () -> Unit,
modifier: Modifier = Modifier,
text: String? = null,
subtitle: String? = null,
@DrawableRes backIconRes: Int? = null,
backIconTint: Color = TangemTheme.colors.icon.primary1,
backgroundColor: Color = TangemTheme.colors.background.secondary,
@ -46,7 +47,7 @@ fun AppBarWithBackButtonAndIconContent(
modifier = modifier
.background(color = backgroundColor)
.fillMaxWidth()
.padding(all = TangemTheme.dimens.spacing16),
.padding(horizontal = TangemTheme.dimens.spacing16),
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
verticalAlignment = Alignment.CenterVertically,
) {
@ -54,6 +55,7 @@ fun AppBarWithBackButtonAndIconContent(
painter = painterResource(backIconRes ?: R.drawable.ic_back_24),
contentDescription = null,
modifier = Modifier
.padding(vertical = TangemTheme.dimens.spacing16)
.size(size = TangemTheme.dimens.size24)
.clickable(
interactionSource = remember { MutableInteractionSource() },
@ -61,20 +63,37 @@ fun AppBarWithBackButtonAndIconContent(
) { onBackClick() },
tint = backIconTint,
)
AnimatedContent(
targetState = text,
modifier = Modifier.weight(1f),
transitionSpec = { fadeIn().togetherWith(fadeOut()) },
label = "Toolbar title change",
Column(
verticalArrangement = Arrangement.Center,
modifier = Modifier.weight(1f)
.animateContentSize(),
) {
if (!it.isNullOrBlank()) {
AnimatedVisibility(
visible = !text.isNullOrBlank(),
enter = fadeIn(),
exit = fadeOut(),
label = "Toolbar title change",
) {
Text(
text = it,
text = text.orEmpty(),
color = TangemTheme.colors.text.primary1,
maxLines = 1,
style = TangemTheme.typography.subtitle1,
)
}
AnimatedVisibility(
visible = !subtitle.isNullOrBlank(),
enter = fadeIn(),
exit = fadeOut(),
label = "Toolbar subtitle change",
) {
Text(
text = subtitle.orEmpty(),
color = TangemTheme.colors.text.secondary,
maxLines = 1,
style = TangemTheme.typography.caption2,
)
}
}
iconContent()
}
@ -111,6 +130,7 @@ private fun PreviewAppBarWithBackButtonAndIconInDarkTheme() {
TangemTheme(isDark = true) {
AppBarWithBackButtonAndIconContent(
text = "Title",
subtitle = "Subtitle",
onBackClick = {},
iconContent = {
Row {

View file

@ -8,7 +8,7 @@ import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.ui.graphics.Color
import com.tangem.core.ui.res.TangemTheme
internal object TangemButtonsDefaults {
object TangemButtonsDefaults {
val elevation: ButtonElevation
@Composable

View file

@ -1,7 +1,9 @@
package com.tangem.core.ui.components.fields
import androidx.annotation.FloatRange
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
@ -12,7 +14,12 @@ import androidx.compose.ui.Alignment.Companion.TopCenter
import androidx.compose.ui.Alignment.Companion.TopStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.ParagraphIntrinsics
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.createFontFamilyResolver
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDirection
@ -57,51 +64,77 @@ fun AmountTextField(
keyboardType = KeyboardType.Number,
),
keyboardActions: KeyboardActions = KeyboardActions.Default,
isAutoResize: Boolean = false,
@FloatRange(from = 0.0, to = 1.0, fromInclusive = false, toInclusive = false)
reduceFactor: Double = 0.9,
) {
val decimalFormat = rememberDecimalFormat()
val visualTransformation = remember { AmountVisualTransformation(decimals, symbol, decimalFormat) }
val placeholderTextAlign = if (placeholderAlignment == TopCenter) {
TextAlign.Center
} else {
TextAlign.Start
}
SimpleTextField(
value = value,
onValueChange = { newText ->
if (decimalFormat.isValidSymbols(newText)) {
val trimmed = decimalFormat.getValidatedNumberWithFixedDecimals(newText, decimals)
onValueChange(trimmed)
BoxWithConstraints(modifier = modifier) {
var fontSize = textStyle.fontSize
if (isAutoResize) {
val calculateIntrinsics = @Composable {
val transformedText = visualTransformation.filter(AnnotatedString(value)).text.text
ParagraphIntrinsics(
text = transformedText,
style = textStyle.copy(fontSize = fontSize),
density = LocalDensity.current,
fontFamilyResolver = createFontFamilyResolver(LocalContext.current),
)
}
},
modifier = modifier
.background(TangemTheme.colors.background.action),
textStyle = textStyle.copy(textDirection = TextDirection.ContentOrLtr),
color = color,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = true,
visualTransformation = AmountVisualTransformation(decimals, symbol, decimalFormat),
decorationBox = { innerTextField ->
Box {
if (value.isBlank() && showPlaceholder) {
val placeholder = if (symbol != null) {
decimalFormat.defaultFormat().plus(" $symbol")
} else {
decimalFormat.defaultFormat()
}
Text(
text = placeholder,
style = textStyle.copy(textDirection = TextDirection.ContentOrLtr),
color = TangemTheme.colors.text.disabled,
textAlign = placeholderTextAlign,
modifier = Modifier
.align(placeholderAlignment),
)
var intrinsics = calculateIntrinsics()
with(LocalDensity.current) {
while (intrinsics.maxIntrinsicWidth > maxWidth.toPx()) {
fontSize *= reduceFactor
intrinsics = calculateIntrinsics()
}
innerTextField()
}
},
)
}
SimpleTextField(
value = value,
onValueChange = { newText ->
if (decimalFormat.isValidSymbols(newText)) {
val trimmed = decimalFormat.getValidatedNumberWithFixedDecimals(newText, decimals)
onValueChange(trimmed)
}
},
textStyle = textStyle.copy(
fontSize = fontSize,
textDirection = TextDirection.ContentOrLtr,
textAlign = TextAlign.Center,
),
color = color,
keyboardOptions = keyboardOptions,
keyboardActions = keyboardActions,
singleLine = true,
visualTransformation = AmountVisualTransformation(decimals, symbol, decimalFormat),
modifier = Modifier.background(TangemTheme.colors.background.action),
decorationBox = { innerTextField ->
Box {
if (value.isBlank() && showPlaceholder) {
val placeholder = if (symbol != null) {
decimalFormat.defaultFormat().plus(" $symbol")
} else {
decimalFormat.defaultFormat()
}
Text(
text = placeholder,
style = textStyle.copy(textDirection = TextDirection.ContentOrLtr),
color = TangemTheme.colors.text.disabled,
textAlign = placeholderTextAlign,
modifier = Modifier.align(placeholderAlignment),
)
}
innerTextField()
}
},
)
}
}
private fun DecimalFormat.isValidSymbols(text: String): Boolean {

View file

@ -28,6 +28,7 @@ dependencies {
/** Compose */
implementation(deps.compose.accompanist.systemUiController)
implementation(deps.compose.material3)
implementation(deps.compose.material)
implementation(deps.compose.foundation)
implementation(deps.compose.ui)
implementation(deps.compose.ui.tooling)

View file

@ -5,7 +5,6 @@ import com.tangem.blockchain.common.TransactionData
import com.tangem.core.ui.components.currency.tokenicon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.event.consumedEvent
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.txhistory.models.TxHistoryItem
@ -14,6 +13,7 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
import com.tangem.features.send.impl.presentation.state.amount.SendAmountSubtractConverter
import com.tangem.features.send.impl.presentation.state.amount.SendAmountStateConverter
import com.tangem.features.send.impl.presentation.state.fee.SendFeeStateConverter
import com.tangem.features.send.impl.presentation.state.fields.SendAmountFieldConverter
@ -45,7 +45,12 @@ internal class SendStateFactory(
appCurrencyProvider = appCurrencyProvider,
)
}
private val amountSubtractConverter by lazy(LazyThreadSafetyMode.NONE) {
SendAmountSubtractConverter(
currentStateProvider = currentStateProvider,
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
)
}
private val amountStateConverter by lazy(LazyThreadSafetyMode.NONE) {
SendAmountStateConverter(
appCurrencyProvider = appCurrencyProvider,
@ -81,6 +86,7 @@ internal class SendStateFactory(
event = consumedEvent(),
isEditingDisabled = false,
isBalanceHidden = false,
cryptoCurrencySymbol = "",
)
fun getReadyState(): SendUiState {
@ -90,6 +96,7 @@ internal class SendStateFactory(
recipientState = state.recipientState
?: recipientStateConverter.convert(SendRecipientStateConverter.Data("", null)),
feeState = state.feeState ?: feeStateConverter.convert(Unit),
cryptoCurrencySymbol = cryptoCurrencyStatusProvider().currency.symbol,
)
}
@ -101,6 +108,7 @@ internal class SendStateFactory(
?: recipientStateConverter.convert(SendRecipientStateConverter.Data(destinationAddress, memo)),
feeState = state.feeState ?: feeStateConverter.convert(Unit),
isEditingDisabled = true,
cryptoCurrencySymbol = cryptoCurrencyStatusProvider().currency.symbol,
)
}
@ -218,22 +226,12 @@ internal class SendStateFactory(
//endregion
//region send
fun onSubtractSelect(isSubtract: Boolean, isAmountSubtractAvailable: Boolean): SendUiState {
fun onSubtractSelect(isAmountSubtractAvailable: Boolean): SendUiState {
val state = currentStateProvider()
val fee = state.feeState?.fee ?: return state
val amountState = state.amountState ?: return state
val amount = amountState.amountTextField.cryptoAmount
val amountValue = amount.value ?: return state
val amountToSend = if (isSubtract && isAmountSubtractAvailable) {
val feeValue = fee.amount.value ?: return state
amountValue.minus(feeValue)
} else {
amountValue
}
return state.copy(
amountState = amountStateConverter.convert(amountToSend.parseBigDecimal(amount.decimals)),
sendState = state.sendState.copy(isSubtract = isSubtract),
)
if (!isAmountSubtractAvailable) return state
return amountSubtractConverter.convert(Unit)
}
fun getSendingStateUpdate(isSending: Boolean): SendUiState {

View file

@ -25,6 +25,7 @@ import java.math.BigDecimal
internal data class SendUiState(
val clickIntents: SendClickIntents,
val isEditingDisabled: Boolean,
val cryptoCurrencySymbol: String,
val amountState: SendStates.AmountState? = null,
val recipientState: SendStates.RecipientState? = null,
val feeState: SendStates.FeeState? = null,

View file

@ -0,0 +1,45 @@
package com.tangem.features.send.impl.presentation.state.amount
import com.tangem.core.ui.utils.parseBigDecimal
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
internal class SendAmountSubtractConverter(
private val currentStateProvider: Provider<SendUiState>,
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
) : Converter<Unit, SendUiState> {
override fun convert(value: Unit): SendUiState {
val state = currentStateProvider()
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
val amountState = state.amountState ?: return state
val feeValue = state.feeState?.fee?.amount?.value ?: return state
val amountTextField = amountState.amountTextField
val amountValue = amountTextField.cryptoAmount.value ?: return state
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val cryptoDecimals = amountTextField.cryptoAmount.decimals
val fiatDecimals = amountTextField.fiatAmount.decimals
val decimalCryptoValue = amountValue.minus(feeValue)
if (decimalCryptoValue < BigDecimal.ZERO) return state
val decimalFiatValue = decimalCryptoValue.multiply(fiatRate)
val cryptoValue = decimalCryptoValue.parseBigDecimal(cryptoDecimals)
val fiatValue = decimalFiatValue.parseBigDecimal(fiatDecimals)
return state.copy(
sendState = state.sendState.copy(isSubtract = true),
amountState = amountState.copy(
amountTextField = amountTextField.copy(
value = cryptoValue,
fiatValue = fiatValue,
cryptoAmount = amountTextField.cryptoAmount.copy(value = decimalCryptoValue),
fiatAmount = amountTextField.fiatAmount.copy(value = decimalFiatValue),
),
),
)
}
}

View file

@ -32,11 +32,12 @@ internal class SendAmountFieldConverter(
val cryptoDecimal = value.toBigDecimalOrDefault()
val cryptoAmount = cryptoDecimal.convertToAmount(cryptoCurrencyStatus.currency)
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val fiatValue = if (value.isEmpty()) {
""
val (fiatValue, fiatDecimal) = if (value.isEmpty()) {
"" to BigDecimal.ZERO
} else {
val fiatDecimal = fiatRate?.multiply(cryptoDecimal) ?: BigDecimal.ZERO
fiatDecimal.parseBigDecimal(FIAT_DECIMALS)
val fiatValue = fiatDecimal.parseBigDecimal(FIAT_DECIMALS)
fiatValue to fiatDecimal
}
val isDoneActionEnabled = !cryptoDecimal.isZero()
return SendTextField.AmountField(
@ -50,16 +51,16 @@ internal class SendAmountFieldConverter(
keyboardActions = KeyboardActions(onDone = { clickIntents.onNextClick() }),
isFiatValue = false,
cryptoAmount = cryptoAmount,
fiatAmount = getAppCurrencyAmount(appCurrencyProvider()),
fiatAmount = getAppCurrencyAmount(fiatDecimal, appCurrencyProvider()),
isError = false,
error = TextReference.Res(R.string.swapping_insufficient_funds),
isFiatUnavailable = fiatRate == null,
)
}
private fun getAppCurrencyAmount(appCurrency: AppCurrency) = Amount(
private fun getAppCurrencyAmount(fiatValue: BigDecimal, appCurrency: AppCurrency) = Amount(
currencySymbol = appCurrency.symbol,
value = BigDecimal.ZERO,
value = fiatValue,
decimals = FIAT_DECIMALS,
type = AmountType.FiatType(appCurrency.code),
)

View file

@ -1,18 +1,12 @@
package com.tangem.features.send.impl.presentation.ui
import androidx.annotation.StringRes
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandHorizontally
import androidx.compose.animation.shrinkHorizontally
import androidx.compose.animation.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -22,161 +16,185 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import com.tangem.core.ui.R
import com.tangem.core.ui.components.*
import com.tangem.core.ui.extensions.rememberHapticFeedback
import com.tangem.core.ui.components.SecondaryButtonIconStart
import com.tangem.core.ui.components.SpacerW12
import com.tangem.core.ui.components.buttons.common.TangemButton
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
import com.tangem.core.ui.extensions.shareText
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen
import com.tangem.features.send.impl.presentation.state.SendUiState
import com.tangem.features.send.impl.presentation.state.SendUiStateType
@Composable
internal fun SendNavigationButtons(uiState: SendUiState, currentState: SendUiCurrentScreen) {
Row(
val isSuccess = uiState.sendState.isSuccess
val isSendingState = currentState.type == SendUiStateType.Send && !isSuccess
val isSentState = currentState.type == SendUiStateType.Send && isSuccess
Column(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = TangemTheme.dimens.spacing12),
.padding(
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
bottom = TangemTheme.dimens.spacing16,
),
) {
SendSecondaryNavigationButton(
uiState = uiState,
currentState = currentState,
SendingText(uiState = uiState, isVisible = isSendingState)
SendDoneButtons(
txUrl = uiState.sendState.txUrl,
onExploreClick = uiState.clickIntents::onExploreClick,
onShareClick = uiState.clickIntents::onShareClick,
isVisible = isSentState,
)
SendPrimaryNavigationButton(
SendNavigationButton(
uiState = uiState,
currentState = currentState,
modifier = Modifier
.weight(1f)
.padding(horizontal = TangemTheme.dimens.spacing16),
modifier = Modifier,
)
}
}
@Composable
private fun SendSecondaryNavigationButton(uiState: SendUiState, currentState: SendUiCurrentScreen) {
val isEditingDisabled = uiState.isEditingDisabled
val isFromConfirmation = currentState.isFromConfirmation
val isCorrectScreen = currentState.type == SendUiStateType.Amount || currentState.type == SendUiStateType.Fee
AnimatedVisibility(
visible = !isEditingDisabled && isCorrectScreen && !isFromConfirmation,
enter = expandHorizontally(expandFrom = Alignment.End),
exit = shrinkHorizontally(shrinkTowards = Alignment.End),
) {
Icon(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing16)
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
.background(TangemTheme.colors.button.secondary)
.clickable {
uiState.clickIntents.onPrevClick()
}
.padding(TangemTheme.dimens.spacing12),
painter = painterResource(R.drawable.ic_back_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
)
}
}
@Composable
private fun SendPrimaryNavigationButton(
private fun SendNavigationButton(
uiState: SendUiState,
currentState: SendUiCurrentScreen,
modifier: Modifier = Modifier,
) {
val hapticFeedback = LocalHapticFeedback.current
val isEditingDisabled = uiState.isEditingDisabled
val isSuccess = uiState.sendState.isSuccess
val isSending = uiState.sendState.isSending
val txUrl = uiState.sendState.txUrl
val isFromConfirmation = currentState.isFromConfirmation
val isCorrectScreen = currentState.type == SendUiStateType.Amount || currentState.type == SendUiStateType.Fee
val isSendingState = currentState.type == SendUiStateType.Send && !isSuccess
val (buttonTextId, buttonClick) = getButtonData(
currentState = currentState,
isSuccess = isSuccess,
uiState = uiState,
)
val isButtonEnabled = isButtonEnabled(
currentState = currentState,
uiState = uiState,
)
AnimatedContent(
targetState = buttonTextId,
label = "Update send screen state",
val isButtonEnabled = isButtonEnabled(currentState, uiState)
val buttonIcon = if (isSendingState) {
TangemButtonIconPosition.End(R.drawable.ic_tangem_24)
} else {
TangemButtonIconPosition.None
}
Row(
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
modifier = modifier,
) { textId ->
when {
currentState.type == SendUiStateType.Send && !isSuccess -> {
val hapticFeedback = rememberHapticFeedback(state = currentState, onAction = buttonClick)
PrimaryButtonIconEnd(
text = stringResource(textId),
iconResId = R.drawable.ic_tangem_24,
enabled = isButtonEnabled,
onClick = hapticFeedback,
showProgress = isSending,
)
}
currentState.type == SendUiStateType.Send && isSuccess -> {
PrimaryButtonsDone(
textRes = textId,
txUrl = txUrl,
onExploreClick = uiState.clickIntents::onExploreClick,
onShareClick = uiState.clickIntents::onShareClick,
onDoneClick = buttonClick,
modifier = Modifier,
)
}
else -> {
PrimaryButton(
text = stringResource(textId),
enabled = isButtonEnabled,
onClick = buttonClick,
)
}
) {
AnimatedVisibility(
visible = !isEditingDisabled && isCorrectScreen && !isFromConfirmation,
enter = expandHorizontally(expandFrom = Alignment.End),
exit = shrinkHorizontally(shrinkTowards = Alignment.End),
) {
Icon(
painter = painterResource(R.drawable.ic_back_24),
tint = TangemTheme.colors.icon.primary1,
contentDescription = null,
modifier = Modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
.background(TangemTheme.colors.button.secondary)
.clickable { uiState.clickIntents.onPrevClick() }
.padding(TangemTheme.dimens.spacing12),
)
}
TangemButton(
text = stringResource(buttonTextId),
icon = buttonIcon,
enabled = isButtonEnabled,
onClick = {
if (isSendingState) hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
buttonClick()
},
showProgress = uiState.sendState.isSending,
modifier = Modifier.fillMaxWidth(),
colors = TangemButtonsDefaults.primaryButtonColors,
)
}
}
@Composable
private fun SendingText(uiState: SendUiState, isVisible: Boolean, modifier: Modifier = Modifier) {
AnimatedVisibility(
visible = isVisible,
modifier = modifier,
enter = slideInVertically().plus(fadeIn()),
exit = slideOutVertically().plus(fadeOut()),
label = "Animate show sending state text",
) {
val amountState = uiState.amountState
val feeState = uiState.feeState
val fiatRate = feeState?.rate
val fiatAmount = amountState?.amountTextField?.fiatAmount
val feeFiat = feeState?.fee?.amount?.value?.multiply(fiatRate)
val sendingFiat = feeFiat?.let { fiatAmount?.value?.plus(it) }
if (feeFiat != null && sendingFiat != null) {
val sendingValue = BigDecimalFormatter.formatFiatAmount(
fiatAmount = sendingFiat,
fiatCurrencyCode = feeState.appCurrency.code,
fiatCurrencySymbol = feeState.appCurrency.symbol,
)
val feeValue = BigDecimalFormatter.formatFiatAmount(
fiatAmount = feeFiat,
fiatCurrencyCode = feeState.appCurrency.code,
fiatCurrencySymbol = feeState.appCurrency.symbol,
)
Text(
text = stringResource(id = R.string.send_summary_transaction_description, sendingValue, feeValue),
textAlign = TextAlign.Center,
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.tertiary,
modifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing12),
)
}
}
}
@Composable
private fun PrimaryButtonsDone(
@StringRes textRes: Int,
private fun SendDoneButtons(
txUrl: String,
onExploreClick: () -> Unit,
onShareClick: () -> Unit,
onDoneClick: () -> Unit,
isVisible: Boolean,
modifier: Modifier = Modifier,
) {
val hapticFeedback = LocalHapticFeedback.current
val context = LocalContext.current
Column(modifier = modifier) {
if (txUrl.isNotBlank()) {
Row {
SecondaryButtonIconStart(
text = stringResource(id = R.string.common_explore),
iconResId = R.drawable.ic_web_24,
onClick = onExploreClick,
modifier = Modifier.weight(1f),
)
SpacerW12()
SecondaryButtonIconStart(
text = stringResource(id = R.string.common_share),
iconResId = R.drawable.ic_share_24,
onClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
context.shareText(txUrl)
onShareClick()
},
modifier = Modifier.weight(1f),
)
}
SpacerH12()
AnimatedVisibility(
visible = isVisible && txUrl.isNotBlank(),
modifier = modifier,
enter = slideInVertically().plus(fadeIn()),
exit = slideOutVertically().plus(fadeOut()),
label = "Animate show sent state buttons",
) {
Row(modifier = Modifier.padding(TangemTheme.dimens.spacing12)) {
SecondaryButtonIconStart(
text = stringResource(id = R.string.common_explore),
iconResId = R.drawable.ic_web_24,
onClick = onExploreClick,
modifier = Modifier.weight(1f),
)
SpacerW12()
SecondaryButtonIconStart(
text = stringResource(id = R.string.common_share),
iconResId = R.drawable.ic_share_24,
onClick = {
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
context.shareText(txUrl)
onShareClick()
},
modifier = Modifier.weight(1f),
)
}
PrimaryButton(
text = stringResource(id = textRes),
enabled = true,
onClick = onDoneClick,
modifier = Modifier.fillMaxWidth(),
)
}
}

View file

@ -1,20 +1,21 @@
package com.tangem.features.send.impl.presentation.ui
import androidx.activity.compose.BackHandler
import androidx.compose.animation.*
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.foundation.layout.*
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen
@ -40,12 +41,22 @@ internal fun SendScreen(uiState: SendUiState, currentStateFlow: StateFlow<SendUi
horizontalAlignment = Alignment.CenterHorizontally,
) {
val titleRes = when (currentState.value.type) {
SendUiStateType.Amount -> R.string.send_amount_label
SendUiStateType.Recipient -> R.string.send_recipient_label
SendUiStateType.Fee -> R.string.common_fee_selector_title
SendUiStateType.Send -> if (!uiState.sendState.isSuccess) R.string.send_confirm_label else null
SendUiStateType.Amount -> resourceReference(R.string.send_amount_label)
SendUiStateType.Recipient -> resourceReference(R.string.send_recipient_label)
SendUiStateType.Fee -> resourceReference(R.string.common_fee_selector_title)
SendUiStateType.Send -> if (!uiState.sendState.isSuccess) {
resourceReference(R.string.send_summary_title, wrappedList(uiState.cryptoCurrencySymbol))
} else {
null
}
else -> null
}
val isSending = currentState.value.type == SendUiStateType.Send && !uiState.sendState.isSuccess
val subtitleRes = if (isSending) {
uiState.amountState?.walletName
} else {
null
}
val iconRes = if (currentState.value.type == SendUiStateType.Recipient) {
R.drawable.ic_qrcode_scan_24
} else {
@ -53,12 +64,14 @@ internal fun SendScreen(uiState: SendUiState, currentStateFlow: StateFlow<SendUi
}
AppBarWithBackButtonAndIcon(
text = titleRes?.let { stringResource(it) },
text = titleRes?.resolveReference(),
subtitle = subtitleRes,
onBackClick = uiState.clickIntents::popBackStack,
onIconClick = uiState.clickIntents::onQrCodeScanClick,
backIconRes = R.drawable.ic_close_24,
iconRes = iconRes,
backgroundColor = TangemTheme.colors.background.tertiary,
modifier = Modifier.height(TangemTheme.dimens.size56),
)
SendScreenContent(
uiState = uiState,

View file

@ -28,11 +28,7 @@ internal fun AmountFieldContainer(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.fillMaxWidth()
.padding(
top = TangemTheme.dimens.spacing4,
start = TangemTheme.dimens.spacing16,
end = TangemTheme.dimens.spacing16,
)
.padding(horizontal = TangemTheme.dimens.spacing16)
.clip(RoundedCornerShape(TangemTheme.dimens.radius16))
.background(TangemTheme.colors.background.action),
) {

View file

@ -0,0 +1,78 @@
package com.tangem.features.send.impl.presentation.ui.send
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.text.style.TextAlign
import com.tangem.core.ui.components.ResizableText
import com.tangem.core.ui.components.currency.tokenicon.TokenIcon
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.presentation.state.SendStates
@Composable
internal fun AmountBlock(
amountState: SendStates.AmountState,
isSuccess: Boolean,
isEditingDisabled: Boolean,
onClick: () -> Unit,
) {
val amount = amountState.amountTextField
val cryptoAmount = getAmountWithSymbol(amount.value, amount.cryptoAmount.currencySymbol)
val fiatAmount = getAmountWithSymbol(amount.fiatValue, amount.fiatAmount.currencySymbol)
val backgroundColor = if (isEditingDisabled) {
TangemTheme.colors.button.disabled
} else {
TangemTheme.colors.background.action
}
val (firstAmount, secondAmount) = if (amount.isFiatValue) {
fiatAmount to cryptoAmount
} else {
cryptoAmount to fiatAmount
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(backgroundColor)
.clickable(enabled = !isSuccess && !isEditingDisabled, onClick = onClick)
.padding(
vertical = TangemTheme.dimens.spacing14,
horizontal = TangemTheme.dimens.spacing16,
),
) {
TokenIcon(state = amountState.tokenIconState)
ResizableText(
text = firstAmount,
style = TangemTheme.typography.h2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.Center,
maxLines = 1,
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing16),
)
Text(
text = secondAmount,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.tertiary,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(top = TangemTheme.dimens.spacing4),
)
}
}
private fun getAmountWithSymbol(amount: String, symbol: String): String {
return "$amount $symbol"
}

View file

@ -0,0 +1,96 @@
package com.tangem.features.send.impl.presentation.ui.send
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import com.tangem.core.ui.components.atoms.text.EllipsisText
import com.tangem.core.ui.components.atoms.text.TextEllipsis
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
import com.tangem.features.send.impl.presentation.state.fee.FeeType
@Composable
internal fun FeeBlock(feeState: SendStates.FeeState, isSuccess: Boolean, onClick: () -> Unit) {
val fee = feeState.fee ?: return
val feeSelectorState = feeState.feeSelectorState as? FeeSelectorState.Content ?: return
val (title, icon) = when (feeSelectorState.selectedFee) {
FeeType.Slow -> R.string.common_fee_selector_option_slow to R.drawable.ic_tortoise_24
FeeType.Market -> R.string.common_fee_selector_option_market to R.drawable.ic_bird_24
FeeType.Fast -> R.string.common_fee_selector_option_fast to R.drawable.ic_hare_24
FeeType.Custom -> R.string.common_fee_selector_option_custom to R.drawable.ic_edit_24
}
val feeCryptoValue = BigDecimalFormatter.formatCryptoAmount(
cryptoAmount = fee.amount.value,
cryptoCurrency = fee.amount.currencySymbol,
decimals = fee.amount.decimals,
)
val feeFiatValue = BigDecimalFormatter.formatFiatAmount(
fiatAmount = feeState.rate?.let { fee.amount.value?.multiply(it) },
fiatCurrencyCode = feeState.appCurrency.code,
fiatCurrencySymbol = feeState.appCurrency.symbol,
)
Column(
modifier = Modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action)
.clickable(enabled = !isSuccess) { onClick() }
.padding(
vertical = TangemTheme.dimens.spacing12,
horizontal = TangemTheme.dimens.spacing14,
),
) {
Text(
text = stringResource(R.string.common_network_fee_title),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
Row(
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
painter = painterResource(icon),
tint = TangemTheme.colors.icon.accent,
contentDescription = null,
)
Text(
text = stringResource(title),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.padding(start = TangemTheme.dimens.spacing8),
)
EllipsisText(
text = feeCryptoValue,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
textAlign = TextAlign.End,
ellipsis = TextEllipsis.OffsetEnd(fee.amount.currencySymbol.length),
modifier = Modifier
.weight(1f)
.padding(start = TangemTheme.dimens.spacing4),
)
Text(
text = "($feeFiatValue)",
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.padding(start = TangemTheme.dimens.spacing4),
)
}
}
}

View file

@ -0,0 +1,91 @@
package com.tangem.features.send.impl.presentation.ui.send
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.tangem.core.ui.components.icons.identicon.IdentIcon
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
@Composable
internal fun RecipientBlock(
recipientState: SendStates.RecipientState,
isSuccess: Boolean,
isEditingDisabled: Boolean,
onClick: () -> Unit,
) {
val backgroundColor = if (isEditingDisabled) {
TangemTheme.colors.button.disabled
} else {
TangemTheme.colors.background.action
}
Column(
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(backgroundColor)
.clickable(enabled = !isSuccess && !isEditingDisabled, onClick = onClick)
.padding(TangemTheme.dimens.spacing12),
) {
AddressBlock(recipientState.addressTextField)
MemoBlock(recipientState.memoTextField)
}
}
@Composable
private fun AddressBlock(address: SendTextField.RecipientAddress) {
Text(
text = address.label.resolveReference(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
) {
IdentIcon(
address = address.value,
modifier = Modifier
.size(TangemTheme.dimens.size36)
.clip(RoundedCornerShape(TangemTheme.dimens.radius18))
.background(TangemTheme.colors.background.tertiary),
)
Text(
text = address.value,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
)
}
}
@Composable
private fun MemoBlock(memo: SendTextField.RecipientMemo?) {
val showMemo = memo != null && memo.value.isNotBlank()
if (showMemo) {
HorizontalDivider(
color = TangemTheme.colors.stroke.primary,
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
)
Text(
text = memo?.label?.resolveReference().orEmpty(),
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
Text(
text = memo?.value.orEmpty(),
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
)
}
}

View file

@ -1,235 +1,121 @@
package com.tangem.features.send.impl.presentation.ui.send
import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import com.tangem.common.Strings
import com.tangem.core.ui.components.inputrow.InputRowDefault
import com.tangem.core.ui.components.inputrow.InputRowImage
import com.tangem.core.ui.components.inputrow.InputRowRecipientDefault
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.utils.BigDecimalFormatter.formatCryptoAmount
import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount
import com.tangem.domain.tokens.model.AmountType
import com.tangem.features.send.impl.R
import com.tangem.features.send.impl.presentation.state.SendNotification
import com.tangem.features.send.impl.presentation.state.SendStates
import com.tangem.features.send.impl.presentation.state.SendUiState
import kotlinx.collections.immutable.ImmutableList
private const val TAP_HELP_KEY = "TAP_HELP_KEY"
private const val BLOCKS_KEY = "BLOCKS_KEY"
@Suppress("LongMethod")
@Composable
internal fun SendContent(uiState: SendUiState) {
val amountState = uiState.amountState ?: return
val recipientState = uiState.recipientState ?: return
val feeState = uiState.feeState ?: return
val sendState = uiState.sendState
val isSuccess = sendState.isSuccess
val timestamp = sendState.transactionDate
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = TangemTheme.dimens.spacing16),
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
) {
item {
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12)) {
AnimatedVisibility(visible = isSuccess) {
TransactionDoneTitle(
titleRes = R.string.sent_transaction_sent_title,
date = timestamp,
)
}
AnimatedVisibility(visible = !isSuccess) {
FromWallet(
walletName = amountState.walletName,
walletBalance = amountState.walletBalance.resolveReference(),
isBalanceHidden = uiState.isBalanceHidden,
)
}
RecipientBlock(
recipientState = recipientState,
isSuccess = isSuccess,
isEditingDisabled = uiState.isEditingDisabled,
onClick = uiState.clickIntents::showRecipient,
)
AmountBlock(
amountState = amountState,
isSuccess = isSuccess,
isEditingDisabled = uiState.isEditingDisabled,
onClick = uiState.clickIntents::showAmount,
)
FeeBlock(
feeState = feeState,
isSuccess = isSuccess,
onClick = uiState.clickIntents::showFee,
)
}
}
blocks(uiState)
tapHelp(isDisplay = sendState.notifications.isEmpty() && !uiState.sendState.isSuccess)
notifications(sendState.notifications)
}
}
@Composable
private fun FromWallet(walletName: String, walletBalance: String, isBalanceHidden: Boolean) {
Column(
modifier = Modifier
.fillMaxWidth()
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.button.disabled)
.padding(TangemTheme.dimens.spacing12),
) {
Text(
text = buildAnnotatedString {
append(stringResource(R.string.send_from_wallet_android))
append(" ")
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
append(walletName)
}
},
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.secondary,
)
val balance = if (isBalanceHidden) Strings.STARS else walletBalance
AnimatedContent(
targetState = balance,
label = "Hide Balance Animation",
) {
Text(
text = it,
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.padding(
top = TangemTheme.dimens.spacing8,
),
private fun LazyListScope.blocks(uiState: SendUiState) {
val amountState = uiState.amountState ?: return
val recipientState = uiState.recipientState ?: return
val feeState = uiState.feeState ?: return
val sendState = uiState.sendState
val isSuccess = sendState.isSuccess
val timestamp = sendState.transactionDate
item(key = BLOCKS_KEY) {
Column(verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12)) {
AnimatedVisibility(
visible = isSuccess,
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
) {
TransactionDoneTitle(
titleRes = R.string.sent_transaction_sent_title,
date = timestamp,
)
}
RecipientBlock(
recipientState = recipientState,
isSuccess = isSuccess,
isEditingDisabled = uiState.isEditingDisabled,
onClick = uiState.clickIntents::showRecipient,
)
AmountBlock(
amountState = amountState,
isSuccess = isSuccess,
isEditingDisabled = uiState.isEditingDisabled,
onClick = uiState.clickIntents::showAmount,
)
FeeBlock(
feeState = feeState,
isSuccess = isSuccess,
onClick = uiState.clickIntents::showFee,
)
}
}
}
@Composable
private fun AmountBlock(
amountState: SendStates.AmountState,
isSuccess: Boolean,
isEditingDisabled: Boolean,
onClick: () -> Unit,
) {
val amount = amountState.amountTextField
val cryptoAmount = formatCryptoAmount(
cryptoAmount = amount.cryptoAmount.value,
cryptoCurrency = amount.cryptoAmount.currencySymbol,
decimals = amount.cryptoAmount.decimals,
)
val fiatAmount = formatFiatAmount(
fiatAmount = amount.fiatAmount.value,
fiatCurrencyCode = (amount.fiatAmount.type as AmountType.FiatType).code,
fiatCurrencySymbol = amount.fiatAmount.currencySymbol,
)
val backgroundColor = if (isEditingDisabled) {
TangemTheme.colors.button.disabled
} else {
TangemTheme.colors.background.action
}
InputRowImage(
title = TextReference.Res(R.string.send_amount_label),
subtitle = TextReference.Str(cryptoAmount),
caption = TextReference.Str(fiatAmount),
tokenIconState = amountState.tokenIconState,
showNetworkIcon = true,
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(backgroundColor)
.clickable(enabled = !isSuccess && !isEditingDisabled) { onClick() },
)
}
@Composable
private fun RecipientBlock(
recipientState: SendStates.RecipientState,
isSuccess: Boolean,
isEditingDisabled: Boolean,
onClick: () -> Unit,
) {
val address = recipientState.addressTextField
val memo = recipientState.memoTextField
val backgroundColor = if (isEditingDisabled) {
TangemTheme.colors.button.disabled
} else {
TangemTheme.colors.background.action
}
Column(
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(backgroundColor)
.clickable(enabled = !isSuccess && !isEditingDisabled) { onClick() },
) {
val showMemo = memo != null && memo.value.isNotBlank()
InputRowRecipientDefault(
title = TextReference.Res(R.string.send_recipient),
value = address.value,
showDivider = showMemo,
)
if (showMemo) {
InputRowDefault(
title = TextReference.Res(R.string.send_extras_hint_memo),
text = TextReference.Str(memo?.value.orEmpty()),
)
@OptIn(ExperimentalFoundationApi::class)
private fun LazyListScope.tapHelp(isDisplay: Boolean, modifier: Modifier = Modifier) {
if (isDisplay) {
item(key = TAP_HELP_KEY) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = modifier
.fillMaxWidth()
.animateItemPlacement(),
) {
val background = TangemTheme.colors.button.secondary
Icon(
painter = painterResource(id = R.drawable.send_hint_shape_12),
tint = TangemTheme.colors.button.secondary,
contentDescription = null,
modifier = Modifier,
)
Text(
text = stringResource(id = R.string.send_summary_tap_hint),
style = TangemTheme.typography.caption1,
color = TangemTheme.colors.text.secondary,
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(background)
.padding(
horizontal = TangemTheme.dimens.spacing14,
vertical = TangemTheme.dimens.spacing12,
),
)
}
}
}
}
@Composable
private fun FeeBlock(feeState: SendStates.FeeState, isSuccess: Boolean, onClick: () -> Unit) {
val fee = feeState.fee ?: return
val feeCryptoValue = formatCryptoAmount(
cryptoAmount = fee.amount.value,
cryptoCurrency = fee.amount.currencySymbol,
decimals = fee.amount.decimals,
)
val feeFiatValue = formatFiatAmount(
fiatAmount = feeState.rate?.let { fee.amount.value?.multiply(it) },
fiatCurrencyCode = feeState.appCurrency.code,
fiatCurrencySymbol = feeState.appCurrency.symbol,
)
InputRowDefault(
title = resourceReference(R.string.common_network_fee_title),
text = resourceReference(
id = R.string.send_wallet_balance_format,
wrappedList(feeCryptoValue, feeFiatValue),
),
modifier = Modifier
.clip(TangemTheme.shapes.roundedCornersXMedium)
.background(TangemTheme.colors.background.action)
.clickable(enabled = !isSuccess) { onClick() },
)
}
@OptIn(ExperimentalFoundationApi::class)
internal fun LazyListScope.notifications(configs: ImmutableList<SendNotification>, modifier: Modifier = Modifier) {
items(

View file

@ -466,7 +466,6 @@ internal class SendViewModel @Inject constructor(
)
return
} else {
uiState = stateFactory.onSubtractSelect(false, isAmountSubtractAvailable)
analyticsEventHandler.send(SendAnalyticEvents.SubtractFromAmount(false))
}
if (checkIfFeeTooLow(uiState)) {
@ -606,7 +605,7 @@ internal class SendViewModel @Inject constructor(
}
override fun onSubtractSelect() {
uiState = stateFactory.onSubtractSelect(true, isAmountSubtractAvailable)
uiState = stateFactory.onSubtractSelect(isAmountSubtractAvailable)
stateRouter.showSend()
analyticsEventHandler.send(SendAnalyticEvents.SubtractFromAmount(true))
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="12dp"
android:height="7dp"
android:viewportWidth="12"
android:viewportHeight="7">
<path
android:pathData="M4.988,0.785C3.819,3.428 1.89,7 0,7H12C10.115,7 8.269,3.447 7.157,0.806C6.784,-0.08 5.376,-0.094 4.988,0.785Z"
android:fillColor="#EBEBEB"/>
</vector>