Updated on 2026-08-14
This commit is contained in:
commit
f08991988d
16 changed files with 722 additions and 8 deletions
|
|
@ -250,6 +250,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
.onEach { currency ->
|
||||
uiState.update { amountUM ->
|
||||
if (amountUM is SwapAmountUM.Content) {
|
||||
initPairs(currency)
|
||||
amountUM.copy(
|
||||
isPrimaryButtonEnabled = false,
|
||||
secondaryAmount = SwapAmountFieldUM.Loading(
|
||||
|
|
@ -260,7 +261,6 @@ internal class SwapAmountModel @Inject constructor(
|
|||
amountUM
|
||||
}
|
||||
}
|
||||
initPairs(currency)
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
|
@ -400,6 +400,7 @@ internal class SwapAmountModel @Inject constructor(
|
|||
swapDirection = swapDirection,
|
||||
allowanceContract = quote.allowanceContract,
|
||||
isApprovalNeeded = checkAllowance(state, quote),
|
||||
fromAmount = fromAmountValue,
|
||||
).convert(
|
||||
SwapQuoteUMConverter.Data(
|
||||
quote = quote,
|
||||
|
|
|
|||
|
|
@ -12,9 +12,13 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
|||
import com.tangem.utils.extensions.isZero
|
||||
import java.math.BigDecimal
|
||||
import java.math.RoundingMode
|
||||
import kotlin.math.min
|
||||
|
||||
internal object SwapAmountQuoteUtils {
|
||||
|
||||
private const val MAX_DECIMALS = 8
|
||||
private const val MIN_DECIMALS = 2
|
||||
|
||||
fun calculatePriceImpact(
|
||||
fromTokenAmount: BigDecimal,
|
||||
toTokenAmount: BigDecimal,
|
||||
|
|
@ -41,6 +45,11 @@ internal object SwapAmountQuoteUtils {
|
|||
}
|
||||
}
|
||||
|
||||
fun calculateRate(fromAmount: BigDecimal, toAmount: BigDecimal, toAmountDecimals: Int): BigDecimal {
|
||||
val rateDecimals = if (toAmountDecimals == 0) MIN_DECIMALS else toAmountDecimals
|
||||
return toAmount.divide(fromAmount, min(rateDecimals, MAX_DECIMALS), RoundingMode.HALF_UP)
|
||||
}
|
||||
|
||||
fun SwapAmountUM.updateAmount(
|
||||
onPrimaryAmount: SwapAmountFieldUM.Content.(CryptoCurrencyStatus) -> SwapAmountFieldUM,
|
||||
onSecondaryAmount: SwapAmountFieldUM.Content.(CryptoCurrencyStatus) -> SwapAmountFieldUM,
|
||||
|
|
|
|||
|
|
@ -1,14 +1,20 @@
|
|||
package com.tangem.features.swap.v2.impl.amount.model.converter
|
||||
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import com.tangem.core.ui.extensions.annotatedReference
|
||||
import com.tangem.core.ui.extensions.appendSpace
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.format.bigdecimal.anyDecimals
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.swap.models.SwapDirection
|
||||
import com.tangem.domain.swap.models.SwapQuoteModel
|
||||
import com.tangem.features.swap.v2.impl.amount.model.SwapAmountQuoteUtils.calculateRate
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM.Content.DifferencePercent
|
||||
import com.tangem.utils.StringsSigns
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -18,10 +24,24 @@ internal class SwapQuoteUMConverter(
|
|||
private val isApprovalNeeded: Boolean,
|
||||
private val primaryCurrency: CryptoCurrency,
|
||||
private val secondaryCurrency: CryptoCurrency,
|
||||
private val fromAmount: BigDecimal,
|
||||
) : Converter<SwapQuoteUMConverter.Data, SwapQuoteUM> {
|
||||
override fun convert(value: Data): SwapQuoteUM {
|
||||
val (quote, provider) = value
|
||||
|
||||
val rate = calculateRate(
|
||||
fromAmount = fromAmount,
|
||||
toAmount = quote.toTokenAmount,
|
||||
toAmountDecimals = secondaryCurrency.decimals,
|
||||
)
|
||||
val rateString = buildAnnotatedString {
|
||||
append(BigDecimal.ONE.format { crypto(symbol = primaryCurrency.symbol, decimals = 0).anyDecimals() })
|
||||
appendSpace()
|
||||
append(StringsSigns.APPROXIMATE)
|
||||
appendSpace()
|
||||
append(rate.format { crypto(secondaryCurrency) })
|
||||
}
|
||||
|
||||
return if (allowanceContract != null) {
|
||||
if (isApprovalNeeded) {
|
||||
SwapQuoteUM.Allowance(
|
||||
|
|
@ -36,6 +56,7 @@ internal class SwapQuoteUMConverter(
|
|||
quoteAmountValue = stringReference(
|
||||
quote.toTokenAmount.toQuoteValue(),
|
||||
),
|
||||
rate = annotatedReference(rateString),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
|
|
@ -46,6 +67,7 @@ internal class SwapQuoteUMConverter(
|
|||
quoteAmountValue = stringReference(
|
||||
quote.toTokenAmount.toQuoteValue(),
|
||||
),
|
||||
rate = annotatedReference(rateString),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import com.tangem.core.ui.components.atoms.text.TextEllipsis
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.express.models.ExpressProviderType
|
||||
import com.tangem.domain.express.models.ExpressRateType
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
|
|
@ -16,10 +18,11 @@ import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountType
|
|||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal data object SwapAmountContentPreview {
|
||||
|
||||
private val cryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||
val cryptoCurrencyStatus = CryptoCurrencyStatus(
|
||||
currency = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID.fromValue("coin⟨BITCOIN⟩bitcoin"),
|
||||
network = Network(
|
||||
|
|
@ -41,12 +44,32 @@ internal data object SwapAmountContentPreview {
|
|||
name = "Bitcoin",
|
||||
symbol = "BTC",
|
||||
decimals = 8,
|
||||
iconUrl = "",
|
||||
iconUrl = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/medium/bitcoin.png",
|
||||
isCustom = false,
|
||||
),
|
||||
value = CryptoCurrencyStatus.Loading,
|
||||
)
|
||||
|
||||
private val provider = ExpressProvider(
|
||||
providerId = "changenow",
|
||||
rateTypes = listOf(ExpressRateType.Float),
|
||||
name = "ChangeNow",
|
||||
type = ExpressProviderType.CEX,
|
||||
imageLarge = "",
|
||||
termsOfUse = "",
|
||||
privacyPolicy = "",
|
||||
isRecommended = true,
|
||||
slippage = BigDecimal.ZERO,
|
||||
)
|
||||
|
||||
private val quote = SwapQuoteUM.Content(
|
||||
provider = provider,
|
||||
quoteAmount = "123".toBigDecimal(),
|
||||
quoteAmountValue = stringReference("123"),
|
||||
rate = stringReference("1 USD ≈ 123.123 POL"),
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Best,
|
||||
)
|
||||
|
||||
val emptyState = SwapAmountUM.Content(
|
||||
isPrimaryButtonEnabled = false,
|
||||
primaryAmount = SwapAmountFieldUM.Empty(
|
||||
|
|
@ -95,7 +118,7 @@ internal data object SwapAmountContentPreview {
|
|||
selectedAmountType = SwapAmountType.From,
|
||||
swapCurrencies = SwapCurrencies.EMPTY,
|
||||
swapQuotes = persistentListOf(),
|
||||
selectedQuote = SwapQuoteUM.Empty,
|
||||
selectedQuote = quote,
|
||||
primaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
secondaryCryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
swapRateType = ExpressRateType.Float,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ internal object SwapChooseProviderContentPreview {
|
|||
provider = provider1,
|
||||
quoteAmount = "123".toBigDecimal(),
|
||||
quoteAmountValue = stringReference("123"),
|
||||
rate = stringReference("1 USD ≈ 123.123 POL"),
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Best,
|
||||
)
|
||||
|
||||
|
|
@ -43,6 +44,7 @@ internal object SwapChooseProviderContentPreview {
|
|||
provider = provider2,
|
||||
quoteAmount = "13.12".toBigDecimal(),
|
||||
quoteAmountValue = stringReference("13.12"),
|
||||
rate = stringReference("1 USD ≈ 12.123 POL"),
|
||||
diffPercent = SwapQuoteUM.Content.DifferencePercent.Empty,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ package com.tangem.features.swap.v2.impl.common.entity
|
|||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.swap.models.SwapDataModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Immutable
|
||||
|
|
@ -18,6 +20,14 @@ internal sealed class ConfirmUM {
|
|||
val notifications: ImmutableList<NotificationUM>,
|
||||
) : ConfirmUM()
|
||||
|
||||
data class Success(
|
||||
override val isPrimaryButtonEnabled: Boolean = true,
|
||||
val transactionDate: Long,
|
||||
val txUrl: String,
|
||||
val swapDataModel: SwapDataModel,
|
||||
val provider: ExpressProvider,
|
||||
) : ConfirmUM()
|
||||
|
||||
data object Empty : ConfirmUM() {
|
||||
override val isPrimaryButtonEnabled: Boolean = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ internal sealed class SwapQuoteUM {
|
|||
val quoteAmount: BigDecimal,
|
||||
val quoteAmountValue: TextReference,
|
||||
val diffPercent: DifferencePercent,
|
||||
val rate: TextReference,
|
||||
) : SwapQuoteUM() {
|
||||
sealed class DifferencePercent {
|
||||
data object Empty : DifferencePercent()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.features.swap.v2.impl.sendviaswap.success
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.common.ui.navigationButtons.NavigationModelCallback
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.SendWithSwapRoute
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.success.model.SendWithSwapSuccessModel
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.success.ui.SendWithSwapSuccessContent
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal class SendWithSwapSuccessComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendWithSwapSuccessModel = getOrCreateModel(params = params)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
SendWithSwapSuccessContent(sendWithSwapUM = state)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val sendWithSwapUMFlow: StateFlow<SendWithSwapUM>,
|
||||
val analyticsCategoryName: String,
|
||||
val currentRoute: Flow<SendWithSwapRoute.Success>,
|
||||
val callback: NavigationModelCallback,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.swap.v2.impl.sendviaswap.success.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.success.model.SendWithSwapSuccessModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface SendWithSwapSuccessModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendWithSwapSuccessModel::class)
|
||||
fun bindSendWithSwapSuccessModel(impl: SendWithSwapSuccessModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
package com.tangem.features.swap.v2.impl.sendviaswap.success.model
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationUM
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.navigation.share.ShareManager
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.success.SendWithSwapSuccessComponent
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class SendWithSwapSuccessModel @Inject constructor(
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
private val appRouter: AppRouter,
|
||||
paramsContainer: ParamsContainer,
|
||||
) : Model() {
|
||||
|
||||
private val params: SendWithSwapSuccessComponent.Params = paramsContainer.require()
|
||||
|
||||
val uiState: StateFlow<SendWithSwapUM> = params.sendWithSwapUMFlow
|
||||
|
||||
val confirmUM = uiState.value.confirmUM as? ConfirmUM.Success
|
||||
|
||||
init {
|
||||
configConfirmSuccessNavigation()
|
||||
}
|
||||
|
||||
private fun configConfirmSuccessNavigation() {
|
||||
params.callback.onNavigationResult(
|
||||
NavigationUM.Content(
|
||||
title = TextReference.EMPTY,
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
backIconClick = appRouter::pop,
|
||||
primaryButton = NavigationButton(
|
||||
textReference = resourceReference(R.string.common_close),
|
||||
iconRes = null,
|
||||
isEnabled = true,
|
||||
isHapticClick = false,
|
||||
onClick = appRouter::pop,
|
||||
),
|
||||
prevButton = null,
|
||||
secondaryPairButtonsUM = NavigationButton(
|
||||
textReference = resourceReference(R.string.common_explore),
|
||||
iconRes = R.drawable.ic_web_24,
|
||||
onClick = ::onExploreClick,
|
||||
) to NavigationButton(
|
||||
textReference = resourceReference(R.string.common_share),
|
||||
iconRes = R.drawable.ic_share_24,
|
||||
onClick = ::onShareClick,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun onExploreClick() {
|
||||
if (confirmUM == null) return
|
||||
|
||||
// analyticsEventHandler.send(CommonSendAnalyticEvents.ExploreButtonClicked(params.analyticsCategoryName))
|
||||
urlOpener.openUrl(confirmUM.txUrl)
|
||||
}
|
||||
|
||||
private fun onShareClick() {
|
||||
if (confirmUM == null) return
|
||||
|
||||
// analyticsEventHandler.send(CommonSendAnalyticEvents.ShareButtonClicked(params.analyticsCategoryName))
|
||||
shareManager.shareText(confirmUM.txUrl)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,441 @@
|
|||
package com.tangem.features.swap.v2.impl.sendviaswap.success.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.amountScreen.utils.getFiatReference
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationUM
|
||||
import com.tangem.core.ui.components.SecondaryButtonIconStart
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.components.currency.icon.CurrencyIcon
|
||||
import com.tangem.core.ui.components.icons.identicon.IdentIcon
|
||||
import com.tangem.core.ui.components.inputrow.InputRowBestRate
|
||||
import com.tangem.core.ui.components.rows.SelectorRowItem
|
||||
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.format.bigdecimal.crypto
|
||||
import com.tangem.core.ui.format.bigdecimal.fee
|
||||
import com.tangem.core.ui.format.bigdecimal.fiat
|
||||
import com.tangem.core.ui.format.bigdecimal.format
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.DateTimeFormatters
|
||||
import com.tangem.core.ui.utils.singleEvent
|
||||
import com.tangem.core.ui.utils.toTimeFormat
|
||||
import com.tangem.domain.express.models.ExpressProvider
|
||||
import com.tangem.domain.express.models.ExpressProviderType
|
||||
import com.tangem.domain.swap.models.SwapDataModel
|
||||
import com.tangem.domain.swap.models.SwapDataTransactionModel
|
||||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.features.send.v2.api.entity.FeeExtraInfo
|
||||
import com.tangem.features.send.v2.api.entity.FeeItem
|
||||
import com.tangem.features.send.v2.api.entity.FeeNonce
|
||||
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationTextFieldUM
|
||||
import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM
|
||||
import com.tangem.features.swap.v2.impl.R
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountFieldUM
|
||||
import com.tangem.features.swap.v2.impl.amount.entity.SwapAmountUM
|
||||
import com.tangem.features.swap.v2.impl.amount.ui.preview.SwapAmountContentPreview
|
||||
import com.tangem.features.swap.v2.impl.common.entity.ConfirmUM
|
||||
import com.tangem.features.swap.v2.impl.common.entity.SwapQuoteUM
|
||||
import com.tangem.features.swap.v2.impl.sendviaswap.entity.SendWithSwapUM
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
private const val GRADIENT_ALPHA = 0.3f
|
||||
|
||||
@Composable
|
||||
internal fun SendWithSwapSuccessContent(sendWithSwapUM: SendWithSwapUM) {
|
||||
if (sendWithSwapUM.navigationUM !is NavigationUM.Content) return
|
||||
|
||||
val confirmUM = sendWithSwapUM.confirmUM as? ConfirmUM.Success ?: return
|
||||
val amountUM = sendWithSwapUM.amountUM as? SwapAmountUM.Content ?: return
|
||||
val quoteUM = amountUM.selectedQuote as? SwapQuoteUM.Content ?: return
|
||||
val destinationUM = sendWithSwapUM.destinationUM as? DestinationUM.Content ?: return
|
||||
val feeSelectorUM = sendWithSwapUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
TransactionDoneTitle(
|
||||
title = resourceReference(R.string.sent_transaction_sent_title),
|
||||
subtitle = resourceReference(
|
||||
id = R.string.send_date_format,
|
||||
formatArgs = wrappedList(
|
||||
confirmUM.transactionDate.toTimeFormat(DateTimeFormatters.dateFormatter),
|
||||
confirmUM.transactionDate.toTimeFormat(),
|
||||
),
|
||||
),
|
||||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
)
|
||||
SwapAmountBlock(amountUM = amountUM)
|
||||
InputRowBestRate(
|
||||
imageUrl = confirmUM.provider.imageLarge,
|
||||
title = stringReference(confirmUM.provider.name),
|
||||
titleExtra = stringReference(confirmUM.provider.type.typeName),
|
||||
subtitle = quoteUM.rate,
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
DestinationBlock(destinationUM.addressTextField)
|
||||
FeeBlock(feeSelectorUM = feeSelectorUM)
|
||||
Spacer(Modifier.height(60.dp))
|
||||
}
|
||||
DoneButtons(
|
||||
pairButtonsUM = sendWithSwapUM.navigationUM.secondaryPairButtonsUM,
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.background(
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
Color.Transparent,
|
||||
TangemTheme.colors.background.tertiary.copy(GRADIENT_ALPHA),
|
||||
TangemTheme.colors.background.tertiary,
|
||||
),
|
||||
),
|
||||
)
|
||||
.padding(top = 24.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwapAmountBlock(amountUM: SwapAmountUM.Content) {
|
||||
AmountBlock(
|
||||
title = resourceReference(
|
||||
id = R.string.send_from_wallet_name,
|
||||
formatArgs = wrappedList(
|
||||
(amountUM.primaryAmount.amountField as? AmountState.Data)?.title
|
||||
?: TextReference.EMPTY,
|
||||
),
|
||||
),
|
||||
amountFieldUM = amountUM.primaryAmount,
|
||||
)
|
||||
AmountBlock(
|
||||
title = resourceReference(R.string.send_with_swap_recipient_amount_success_title),
|
||||
amountFieldUM = amountUM.secondaryAmount,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AmountBlock(title: TextReference, amountFieldUM: SwapAmountFieldUM, modifier: Modifier = Modifier) {
|
||||
val amountFieldData = amountFieldUM.amountField as? AmountState.Data ?: return
|
||||
val cryptoAmount = amountFieldData.amountTextField.cryptoAmount
|
||||
val fiatAmount = amountFieldData.amountTextField.fiatAmount
|
||||
|
||||
Column(
|
||||
modifier = modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = 16.dp),
|
||||
) {
|
||||
CurrencyIcon(
|
||||
state = amountFieldData.tokenIconState,
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
) {
|
||||
Text(
|
||||
text = cryptoAmount.value?.format {
|
||||
crypto(
|
||||
symbol = cryptoAmount.currencySymbol,
|
||||
decimals = cryptoAmount.decimals,
|
||||
)
|
||||
}.orEmpty(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
Text(
|
||||
text = fiatAmount.value.format {
|
||||
fiat(
|
||||
fiatCurrencyCode = amountFieldData.appCurrency.code,
|
||||
fiatCurrencySymbol = amountFieldData.appCurrency.symbol,
|
||||
)
|
||||
},
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeBlock(feeSelectorUM: FeeSelectorUM.Content) {
|
||||
val feeExtraInfo = feeSelectorUM.feeExtraInfo
|
||||
val feeFiatRateUM = feeSelectorUM.feeFiatRateUM
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(com.tangem.common.ui.R.string.common_network_fee_title),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
||||
Box(modifier = Modifier.padding(top = TangemTheme.dimens.spacing8)) {
|
||||
val feeAmount = feeSelectorUM.selectedFeeItem.fee.amount
|
||||
SelectorRowItem(
|
||||
titleRes = com.tangem.common.ui.R.string.common_fee_selector_option_market,
|
||||
iconRes = com.tangem.common.ui.R.drawable.ic_bird_24,
|
||||
preDot = stringReference(
|
||||
feeAmount.value.format {
|
||||
crypto(
|
||||
symbol = feeAmount.currencySymbol,
|
||||
decimals = feeAmount.decimals,
|
||||
).fee(canBeLower = feeExtraInfo.isFeeApproximate)
|
||||
},
|
||||
),
|
||||
postDot = if (feeExtraInfo.isFeeConvertibleToFiat && feeFiatRateUM != null) {
|
||||
getFiatReference(feeAmount.value, feeFiatRateUM.rate, feeFiatRateUM.appCurrency)
|
||||
} else {
|
||||
null
|
||||
},
|
||||
ellipsizeOffset = feeAmount.currencySymbol.length,
|
||||
isSelected = true,
|
||||
showDivider = false,
|
||||
showSelectedAppearance = false,
|
||||
paddingValues = PaddingValues(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DestinationBlock(address: DestinationTextFieldUM.RecipientAddress, modifier: Modifier = Modifier) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.padding(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResourceSafe(R.string.send_recipient),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
||||
modifier = Modifier.padding(top = 8.dp),
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.weight(1f),
|
||||
text = address.value,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
IdentIcon(
|
||||
address = address.value,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size36)
|
||||
.clip(RoundedCornerShape(18.dp))
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO remove [REDACTED_TASK_KEY]
|
||||
@Composable
|
||||
private fun DoneButtons(pairButtonsUM: Pair<NavigationButton, NavigationButton>?, modifier: Modifier = Modifier) {
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = pairButtonsUM != null,
|
||||
modifier = modifier,
|
||||
enter = slideInVertically().plus(fadeIn()),
|
||||
exit = slideOutVertically().plus(fadeOut()),
|
||||
label = "Animate show sent state buttons",
|
||||
) {
|
||||
val (leftButton, rightButton) = remember(this) { requireNotNull(pairButtonsUM) }
|
||||
Row {
|
||||
SecondaryButtonIconStart(
|
||||
text = leftButton.textReference.resolveReference(),
|
||||
iconResId = requireNotNull(leftButton.iconRes),
|
||||
onClick = {
|
||||
singleEvent {
|
||||
leftButton.onClick()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
SpacerW12()
|
||||
SecondaryButtonIconStart(
|
||||
text = rightButton.textReference.resolveReference(),
|
||||
iconResId = requireNotNull(rightButton.iconRes),
|
||||
onClick = {
|
||||
singleEvent {
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.LongPress)
|
||||
rightButton.onClick()
|
||||
}
|
||||
},
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Suppress("LongMethod")
|
||||
@Composable
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 720)
|
||||
@Preview(showBackground = true, widthDp = 360, heightDp = 720, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
private fun SendWithSwapSuccessContent_Preview() {
|
||||
TangemThemePreview {
|
||||
SendWithSwapSuccessContent(
|
||||
sendWithSwapUM = SendWithSwapUM(
|
||||
amountUM = SwapAmountContentPreview.defaultState,
|
||||
destinationUM = DestinationUM.Content(
|
||||
isPrimaryButtonEnabled = false,
|
||||
addressTextField = DestinationTextFieldUM.RecipientAddress(
|
||||
value = "0x391316d97a07027a0702c8A002c8A0C25d8470",
|
||||
keyboardOptions = KeyboardOptions(),
|
||||
placeholder = TextReference.EMPTY,
|
||||
label = resourceReference(R.string.send_recipient),
|
||||
isError = false,
|
||||
error = null,
|
||||
isValuePasted = false,
|
||||
blockchainAddress = "0x391316d97a07027a0702c8A002c8A0C25d8470",
|
||||
),
|
||||
memoTextField = null,
|
||||
recent = persistentListOf(),
|
||||
wallets = persistentListOf(),
|
||||
networkName = "Polygon",
|
||||
isValidating = false,
|
||||
isInitialized = false,
|
||||
isRedesignEnabled = false,
|
||||
),
|
||||
feeSelectorUM = FeeSelectorUM.Content(
|
||||
fees = TransactionFee.Single(
|
||||
normal = Fee.Common(
|
||||
BigDecimal.ONE.convertToSdkAmount(
|
||||
SwapAmountContentPreview.cryptoCurrencyStatus.currency,
|
||||
),
|
||||
),
|
||||
),
|
||||
feeItems = persistentListOf(),
|
||||
selectedFeeItem = FeeItem.Market(
|
||||
Fee.Common(
|
||||
BigDecimal.ONE.convertToSdkAmount(
|
||||
SwapAmountContentPreview.cryptoCurrencyStatus.currency,
|
||||
),
|
||||
),
|
||||
),
|
||||
feeExtraInfo = FeeExtraInfo(
|
||||
isFeeApproximate = false,
|
||||
isFeeConvertibleToFiat = false,
|
||||
isTronToken = false,
|
||||
),
|
||||
feeFiatRateUM = null,
|
||||
feeNonce = FeeNonce.None,
|
||||
),
|
||||
confirmUM = ConfirmUM.Success(
|
||||
isPrimaryButtonEnabled = true,
|
||||
transactionDate = 8960,
|
||||
txUrl = "https://tangem.com",
|
||||
provider = ExpressProvider(
|
||||
providerId = "changelly",
|
||||
rateTypes = listOf(),
|
||||
name = "Changelly",
|
||||
type = ExpressProviderType.CEX,
|
||||
imageLarge = "https://s3.eu-central-1.amazonaws.com/tangem.api/express/changelly-1024.png",
|
||||
termsOfUse = "",
|
||||
privacyPolicy = "",
|
||||
isRecommended = false,
|
||||
slippage = null,
|
||||
),
|
||||
swapDataModel = SwapDataModel(
|
||||
toTokenAmount = BigDecimal.TEN,
|
||||
transaction = SwapDataTransactionModel.CEX(
|
||||
fromAmount = BigDecimal.TEN,
|
||||
toAmount = BigDecimal.TEN,
|
||||
txValue = "cetero",
|
||||
txId = "oporteat",
|
||||
txTo = "graecis",
|
||||
txExtraId = "expetendis",
|
||||
externalTxId = "atomorum",
|
||||
externalTxUrl = "https://tangem.com",
|
||||
txExtraIdName = "Jeffry Blackwell",
|
||||
),
|
||||
),
|
||||
),
|
||||
navigationUM = NavigationUM.Content(
|
||||
title = TextReference.EMPTY,
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
backIconClick = {},
|
||||
additionalIconRes = null,
|
||||
additionalIconClick = null,
|
||||
primaryButton = NavigationButton(
|
||||
textReference = resourceReference(R.string.common_close),
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
secondaryPairButtonsUM = NavigationButton(
|
||||
textReference = resourceReference(R.string.common_explore),
|
||||
iconRes = R.drawable.ic_web_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
) to NavigationButton(
|
||||
textReference = resourceReference(R.string.common_share),
|
||||
iconRes = R.drawable.ic_share_24,
|
||||
isEnabled = true,
|
||||
onClick = {},
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
Loading…
Add table
Add a link
Reference in a new issue