diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt index c9e0526b4e..405217433f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIcon.kt @@ -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 = { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIconContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIconContent.kt index 6402068907..229fbe444c 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIconContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/appbar/AppBarWithBackButtonAndIconContent.kt @@ -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 { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt index db255b710f..5a555dfcf7 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/common/TangemButtonsDefaults.kt @@ -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 diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt index fc0ee052f3..fee185d0ab 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/fields/AmountTextField.kt @@ -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 { diff --git a/features/send/impl/build.gradle.kts b/features/send/impl/build.gradle.kts index eb9e87bdf6..db20b4afc2 100644 --- a/features/send/impl/build.gradle.kts +++ b/features/send/impl/build.gradle.kts @@ -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) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt index 9b523c44f2..5f865ed61a 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendStateFactory.kt @@ -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 { diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt index 7572a37ae9..00d7a9dbbe 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/SendUiState.kt @@ -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, diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt new file mode 100644 index 0000000000..4d00bd9b2e --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/amount/SendAmountSubtractConverter.kt @@ -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, + private val cryptoCurrencyStatusProvider: Provider, +) : Converter { + 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), + ), + ), + ) + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt index 37bdcf2eae..332a736cd0 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fields/SendAmountFieldConverter.kt @@ -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), ) diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index 5c0dfc787b..0344343e70 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -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(), - ) } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt index 7cd9b84daf..729c3c9a24 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendScreen.kt @@ -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 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 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" +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt new file mode 100644 index 0000000000..13ff8cab42 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt @@ -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), + ) + } + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt new file mode 100644 index 0000000000..7169e38f49 --- /dev/null +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/RecipientBlock.kt @@ -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), + ) + } +} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt index 6d335ee554..6cd08ba079 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/SendContent.kt @@ -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, modifier: Modifier = Modifier) { items( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt index 0617f6e83b..073fb98d89 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/viewmodel/SendViewModel.kt @@ -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)) } diff --git a/features/send/impl/src/main/res/drawable/send_hint_shape_12.xml b/features/send/impl/src/main/res/drawable/send_hint_shape_12.xml new file mode 100644 index 0000000000..05cc9e9b5b --- /dev/null +++ b/features/send/impl/src/main/res/drawable/send_hint_shape_12.xml @@ -0,0 +1,9 @@ + + +