Updated on 2026-08-14

This commit is contained in:
Tangem 2025-06-05 16:18:11 +03:00
commit c8c64e882b
1244 changed files with 26701 additions and 16971 deletions

View file

@ -4,8 +4,5 @@ import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.send.v2.api.SendFeatureToggles
internal class DefaultSendFeatureToggles(
private val featureToggles: FeatureTogglesManager,
) : SendFeatureToggles {
override val isSendV2Enabled: Boolean
get() = featureToggles.isFeatureEnabled("SEND_V2_ENABLED")
}
@Suppress("UnusedPrivateMember") private val featureToggles: FeatureTogglesManager,
) : SendFeatureToggles

View file

@ -1,12 +1,11 @@
package com.tangem.features.send.v2.common
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.nft.RefreshAllNFTUseCase
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.txhistory.TxHistoryFeatureToggles
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
import com.tangem.utils.coroutines.DelayedWork
import dagger.assisted.Assisted
@ -19,9 +18,8 @@ internal class SendBalanceUpdater @AssistedInject constructor(
private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase,
private val updateDelayedNetworkStatusUseCase: UpdateDelayedNetworkStatusUseCase,
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
private val txHistoryFeatureToggles: TxHistoryFeatureToggles,
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
private val refreshAllNFTUseCase: RefreshAllNFTUseCase,
@DelayedWork private val coroutineScope: CoroutineScope,
@Assisted private val userWallet: UserWallet,
@Assisted private val cryptoCurrency: CryptoCurrency,
@ -33,7 +31,7 @@ internal class SendBalanceUpdater @AssistedInject constructor(
async {
fetchPendingTransactionsUseCase(
userWalletId = userWallet.walletId,
networks = setOf(cryptoCurrency.network),
network = cryptoCurrency.network,
)
},
// we should update tx history and network for new balances
@ -43,16 +41,25 @@ internal class SendBalanceUpdater @AssistedInject constructor(
async {
updateNetworkStatuses()
},
async {
updateNFT()
},
).awaitAll()
}
}
private suspend fun updateNFT() {
delay(BALANCE_UPDATE_DELAY)
refreshAllNFTUseCase(
userWalletId = userWallet.walletId,
)
}
private suspend fun updateNetworkStatuses(delay: Long = BALANCE_UPDATE_DELAY) {
updateDelayedNetworkStatusUseCase(
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
delayMillis = delay,
refresh = true,
)
}
@ -64,15 +71,7 @@ internal class SendBalanceUpdater @AssistedInject constructor(
)
txHistoryItemsCountEither.onRight {
if (txHistoryFeatureToggles.isFeatureEnabled) {
txHistoryContentUpdateEmitter.triggerUpdate()
} else {
getTxHistoryItemsUseCase(
userWalletId = userWallet.walletId,
currency = cryptoCurrency,
refresh = true,
)
}
txHistoryContentUpdateEmitter.triggerUpdate()
}
}

View file

@ -0,0 +1,51 @@
package com.tangem.features.send.v2.common.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.Keyboard
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.res.TangemTheme
@Composable
internal fun SendingText(footerText: TextReference, modifier: Modifier = Modifier) {
var isVisibleProxy by remember { mutableStateOf(footerText != TextReference.EMPTY) }
val keyboard by keyboardAsState()
// the text should appear when the keyboard is closed
LaunchedEffect(footerText != TextReference.EMPTY, keyboard) {
if (footerText != TextReference.EMPTY && keyboard is Keyboard.Opened) {
return@LaunchedEffect
}
isVisibleProxy = footerText != TextReference.EMPTY
}
AnimatedVisibility(
visible = isVisibleProxy,
modifier = modifier,
enter = slideInVertically(initialOffsetY = { it / 2 }) + fadeIn(),
exit = fadeOut(tween(durationMillis = 300)),
label = "Animate show sending state text",
) {
Text(
text = footerText.resolveAnnotatedReference(),
textAlign = TextAlign.Center,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
)
}
}

View file

@ -12,6 +12,7 @@ internal sealed class ConfirmUM {
data class Content(
override val isPrimaryButtonEnabled: Boolean = false,
val walletName: TextReference,
val isSending: Boolean,
val showTapHelp: Boolean,
val sendingFooter: TextReference,

View file

@ -0,0 +1,66 @@
package com.tangem.features.send.v2.common.utils
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.transaction.Fee
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.domain.appcurrency.model.AppCurrency
import com.tangem.features.send.v2.impl.R
import com.tangem.utils.StringsSigns.COMA_SIGN
internal fun getTronTokenFeeSendingText(fee: Fee.Tron, fiatFee: String, fiatSending: TextReference): TextReference {
val suffix = when {
fee.remainingEnergy == 0L -> {
resourceReference(
R.string.send_summary_transaction_description_suffix_including,
wrappedList(fiatFee),
)
}
fee.feeEnergy <= fee.remainingEnergy -> {
resourceReference(
R.string.send_summary_transaction_description_suffix_fee_covered,
wrappedList(fee.feeEnergy),
)
}
else -> {
resourceReference(
R.string.send_summary_transaction_description_suffix_fee_reduced,
wrappedList(fee.remainingEnergy),
)
}
}
val prefix = resourceReference(
R.string.send_summary_transaction_description_prefix,
wrappedList(fiatSending),
)
return combinedReference(prefix, stringReference("$COMA_SIGN "), suffix)
}
internal fun formatFooterFiatFee(
amount: Amount?,
isFeeConvertibleToFiat: Boolean,
isFeeApproximate: Boolean,
appCurrency: AppCurrency,
): String {
return if (isFeeConvertibleToFiat) {
amount?.value.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
}
} else {
amount?.value.format {
crypto(
decimals = amount?.decimals ?: 0,
symbol = amount?.currencySymbol.orEmpty(),
).fee(
canBeLower = isFeeApproximate,
)
}
}
}

View file

@ -0,0 +1,87 @@
package com.tangem.features.send.v2.deeplink
import arrow.core.getOrElse
import com.tangem.common.routing.AppRoute
import com.tangem.common.routing.AppRouter
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
import com.tangem.features.send.v2.api.deeplink.SellDeepLinkHandler
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import timber.log.Timber
@Suppress("ComplexCondition")
internal class DefaultSellDeepLinkHandler @AssistedInject constructor(
@Assisted scope: CoroutineScope,
@Assisted queryParams: Map<String, String>,
appRouter: AppRouter,
getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase,
getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
) : SellDeepLinkHandler {
init {
val currencyId = queryParams[CURRENCY_ID_KEY]
val transactionId = queryParams[TRANSACTION_ID_KEY]
val amount = queryParams[AMOUNT_KEY]
val destinationAddress = queryParams[DESTINATION_ADDRESS_KEY]
val memo = queryParams[MEMO_KEY]
// It is okay here, we are navigating from outside, and there is no other way to getting UserWallet
getSelectedWalletSyncUseCase()
.fold(
ifLeft = {
Timber.e("Error on getting user wallet: $it")
},
ifRight = { userWallet ->
if (currencyId.isNullOrEmpty() || transactionId.isNullOrEmpty() ||
amount.isNullOrEmpty() || destinationAddress.isNullOrEmpty()
) {
Timber.e(
"""
Invalid parameters for SELL deeplink
|- Params: $queryParams
""".trimIndent(),
)
return@fold
}
scope.launch {
val cryptoCurrency = getCryptoCurrencyUseCase(userWallet, currencyId).getOrElse {
Timber.e("Error on getting cryptoCurrency: $it")
return@launch
}
appRouter.push(
AppRoute.Send(
currency = cryptoCurrency,
userWalletId = userWallet.walletId,
transactionId = transactionId,
destinationAddress = destinationAddress,
amount = amount,
tag = memo,
),
)
}
},
)
}
@AssistedFactory
interface Factory : SellDeepLinkHandler.Factory {
override fun create(
coroutineScope: CoroutineScope,
queryParams: Map<String, String>,
): DefaultSellDeepLinkHandler
}
private companion object {
const val TRANSACTION_ID_KEY = "transactionId"
const val CURRENCY_ID_KEY = "currency_id"
const val AMOUNT_KEY = "baseCurrencyAmount"
const val DESTINATION_ADDRESS_KEY = "depositWalletAddress"
const val MEMO_KEY = "depositWalletAddressTag"
}
}

View file

@ -0,0 +1,18 @@
package com.tangem.features.send.v2.deeplink.di
import com.tangem.features.send.v2.api.deeplink.SellDeepLinkHandler
import com.tangem.features.send.v2.deeplink.DefaultSellDeepLinkHandler
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
internal interface SendDeepLinkModule {
@Binds
@Singleton
fun bindFactory(impl: DefaultSellDeepLinkHandler.Factory): SellDeepLinkHandler.Factory
}

View file

@ -4,7 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.send.ui.state.SendUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationTextFieldUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM

View file

@ -15,21 +15,23 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
import com.tangem.domain.settings.NeverShowTapHelpUseCase
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
@ -255,7 +257,7 @@ internal class SendConfirmModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage,
blockchainId = cryptoCurrency.network.id.value,
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
destinationAddress = confirmData.enteredDestination.orEmpty(),
tokenSymbol = if (amount?.type is AmountType.Token) {
@ -269,7 +271,8 @@ internal class SendConfirmModel @Inject constructor(
),
)
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrNull() ?: return
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY]
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
@ -286,6 +289,7 @@ internal class SendConfirmModel @Inject constructor(
it.copy(
confirmUM = SendConfirmInitialStateTransformer(
isShowTapHelp = isShowTapHelp,
walletName = stringReference(userWallet.name),
).transform(uiState.value.confirmUM),
)
}

View file

@ -7,9 +7,11 @@ import kotlinx.collections.immutable.persistentListOf
internal class SendConfirmInitialStateTransformer(
private val isShowTapHelp: Boolean,
private val walletName: TextReference,
) : Transformer<ConfirmUM> {
override fun transform(prevState: ConfirmUM): ConfirmUM {
return ConfirmUM.Content(
walletName = walletName,
isSending = false,
showTapHelp = isShowTapHelp,
sendingFooter = TextReference.EMPTY,

View file

@ -1,21 +1,23 @@
package com.tangem.features.send.v2.send.confirm.model.transformers
import com.tangem.blockchain.common.Amount
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.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
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.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.utils.formatFooterFiatFee
import com.tangem.features.send.v2.common.utils.getTronTokenFeeSendingText
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
@ -92,17 +94,18 @@ internal class SendConfirmationNotificationsTransformer(
fiatCurrencySymbol = appCurrency.symbol,
)
}
val fiatFee = formatFiatFee(
val fiatFee = formatFooterFiatFee(
amount = fee.amount.copy(value = fiatFeeValue),
isFeeConvertibleToFiat = feeUM.isFeeConvertibleToFiat,
isFeeApproximate = feeUM.isFeeApproximate,
appCurrency = appCurrency,
)
return if (feeUM.isTronToken && fee is Fee.Tron) {
getTokenFeeSendingText(
getTronTokenFeeSendingText(
fee = fee,
fiatFee = fiatFee,
fiatSending = fiatSending,
fiatSending = stringReference(fiatSending),
)
} else {
resourceReference(
@ -115,57 +118,4 @@ internal class SendConfirmationNotificationsTransformer(
)
}
}
private fun formatFiatFee(amount: Amount?, isFeeConvertibleToFiat: Boolean, isFeeApproximate: Boolean): String {
return if (isFeeConvertibleToFiat) {
amount?.value.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
}
} else {
amount?.value.format {
crypto(
decimals = amount?.decimals ?: 0,
symbol = amount?.currencySymbol.orEmpty(),
).fee(
canBeLower = isFeeApproximate,
)
}
}
}
private fun getTokenFeeSendingText(fee: Fee.Tron, fiatFee: String, fiatSending: String): TextReference {
val suffix = when {
fee.remainingEnergy == 0L -> {
resourceReference(
R.string.send_summary_transaction_description_suffix_including,
wrappedList(fiatFee),
)
}
fee.feeEnergy <= fee.remainingEnergy -> {
resourceReference(
R.string.send_summary_transaction_description_suffix_fee_covered,
wrappedList(fee.feeEnergy),
)
}
else -> {
resourceReference(
R.string.send_summary_transaction_description_suffix_fee_reduced,
wrappedList(fee.remainingEnergy),
)
}
}
val prefix = resourceReference(
R.string.send_summary_transaction_description_prefix,
wrappedList(fiatSending),
)
return combinedReference(prefix, COMMA_SEPARATOR, suffix)
}
companion object {
private val COMMA_SEPARATOR = stringReference(", ")
}
}

View file

@ -1,33 +1,24 @@
package com.tangem.features.send.v2.send.confirm.ui
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.Keyboard
import com.tangem.core.ui.components.SpacerHMax
import com.tangem.core.ui.components.keyboardAsState
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resolveAnnotatedReference
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.DateTimeFormatters
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.features.send.v2.common.ui.SendingText
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.tapHelp
import com.tangem.features.send.v2.impl.R
@ -82,38 +73,6 @@ internal fun SendConfirmContent(
}
}
@Composable
private fun SendingText(footerText: TextReference, modifier: Modifier = Modifier) {
var isVisibleProxy by remember { mutableStateOf(footerText != TextReference.EMPTY) }
val keyboard by keyboardAsState()
// the text should appear when the keyboard is closed
LaunchedEffect(footerText != TextReference.EMPTY, keyboard) {
if (footerText != TextReference.EMPTY && keyboard is Keyboard.Opened) {
return@LaunchedEffect
}
isVisibleProxy = footerText != TextReference.EMPTY
}
AnimatedVisibility(
visible = isVisibleProxy,
modifier = modifier,
enter = slideInVertically() + fadeIn(),
exit = fadeOut(tween(durationMillis = 300)),
label = "Animate show sending state text",
) {
Text(
text = footerText.resolveAnnotatedReference(),
textAlign = TextAlign.Center,
style = TangemTheme.typography.caption2,
color = TangemTheme.colors.text.primary1,
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
)
}
}
private fun LazyListScope.blocks(
uiState: SendUM,
destinationBlockComponent: SendDestinationBlockComponent,

View file

@ -24,9 +24,8 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
@ -34,6 +33,8 @@ import com.tangem.domain.transaction.usecase.CreateTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.utils.convertToSdkAmount
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.SendComponent
import com.tangem.features.send.v2.common.CommonSendRoute
@ -73,8 +74,7 @@ internal class SendModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val router: Router,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
@ -219,7 +219,8 @@ internal class SendModel @Inject constructor(
ifRight = { wallet ->
userWallet = wallet
val isSingleWalletWithToken = wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
val isSingleWalletWithToken = wallet is UserWallet.Cold &&
wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
val isMultiCurrency = wallet.isMultiCurrency
getCurrenciesStatusUpdates(
isSingleWalletWithToken = isSingleWalletWithToken,
@ -275,13 +276,13 @@ internal class SendModel @Inject constructor(
isMultiCurrency: Boolean,
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
return if (isMultiCurrency) {
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = isSingleWalletWithToken,
)
} else {
getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWalletId)
getSingleCryptoCurrencyStatusUseCase.invokeSingleWallet(userWalletId = userWalletId)
}
}
@ -335,7 +336,7 @@ internal class SendModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage.orEmpty(),
blockchainId = cryptoCurrency.network.id.value,
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
destinationAddress = "",
tokenSymbol = "",
@ -344,7 +345,8 @@ internal class SendModel @Inject constructor(
),
)
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrNull() ?: return
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY]
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))

View file

@ -85,11 +85,13 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
) { stack ->
componentScope.launch {
when (val activeComponent = stack.active.instance) {
is NFTSendConfirmComponent -> if (currentRouteFlow.value.isEditMode) {
is NFTSendConfirmComponent -> {
analyticsEventHandler.send(
CommonSendAnalyticEvents.ConfirmationScreenOpened(categoryName = analyticsCategoryName),
)
activeComponent.updateState(model.uiState.value)
if (currentRouteFlow.value.isEditMode) {
activeComponent.updateState(model.uiState.value)
}
}
is SendDestinationComponent -> {
analyticsEventHandler.send(

View file

@ -4,7 +4,7 @@ import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.Basic
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationTextFieldUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM

View file

@ -12,7 +12,7 @@ import com.tangem.core.decompose.navigation.Router
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.extensions.stringReference
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.feedback.GetCardInfoUseCase
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
@ -24,6 +24,8 @@ import com.tangem.domain.settings.NeverShowTapHelpUseCase
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.features.nft.entity.NFTSendSuccessTrigger
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
import com.tangem.features.send.v2.common.SendConfirmAlertFactory
@ -78,6 +80,7 @@ internal class NFTSendConfirmModel @Inject constructor(
private val shareManager: ShareManager,
private val analyticsEventHandler: AnalyticsEventHandler,
private val nftSendAnalyticHelper: NFTSendAnalyticHelper,
private val nftSendSuccessTrigger: NFTSendSuccessTrigger,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), NFTSendConfirmClickIntents {
@ -201,7 +204,7 @@ internal class NFTSendConfirmModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage,
blockchainId = cryptoCurrency.network.id.value,
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
destinationAddress = confirmData.enteredDestination.orEmpty(),
tokenSymbol = null,
@ -210,7 +213,8 @@ internal class NFTSendConfirmModel @Inject constructor(
),
)
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrNull() ?: return
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY]
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
@ -228,6 +232,7 @@ internal class NFTSendConfirmModel @Inject constructor(
it.copy(
confirmUM = NFTSendConfirmInitialStateTransformer(
isShowTapHelp = isShowTapHelp,
walletName = stringReference(userWallet.name),
).transform(uiState.value.confirmUM),
)
}
@ -357,6 +362,7 @@ internal class NFTSendConfirmModel @Inject constructor(
analyticsEventHandler = analyticsEventHandler,
cryptoCurrency = cryptoCurrencyStatus.currency,
analyticsCategoryName = analyticsCategoryName,
appCurrency = params.appCurrency,
).transform(uiState.value.confirmUM),
)
}
@ -369,15 +375,13 @@ internal class NFTSendConfirmModel @Inject constructor(
transform = { state, route -> state to route },
).onEach { (state, _) ->
val confirmUM = state.confirmUM
val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending
val confirmUMContent = confirmUM as? ConfirmUM.Content
val isReadyToSend = confirmUMContent != null && !confirmUM.isSending
params.callback.onResult(
state.copy(
navigationUM = NavigationUM.Content(
title = resourceReference(
id = R.string.send_summary_title,
formatArgs = wrappedList(params.cryptoCurrencyStatus.currency.name),
),
subtitle = null,
title = resourceReference(R.string.nft_send),
subtitle = confirmUMContent?.walletName,
backIconRes = R.drawable.ic_close_24,
backIconClick = {
analyticsEventHandler.send(
@ -404,15 +408,7 @@ internal class NFTSendConfirmModel @Inject constructor(
isEnabled = confirmUM.isPrimaryButtonEnabled,
isHapticClick = isReadyToSend,
onClick = {
when (confirmUM) {
is ConfirmUM.Success -> appRouter.pop()
is ConfirmUM.Content -> if (confirmUM.isSending) {
return@PrimaryButtonUM
} else {
onSendClick()
}
else -> return@PrimaryButtonUM
}
onNextClick(confirmUM)
},
),
prevButton = null,
@ -430,6 +426,23 @@ internal class NFTSendConfirmModel @Inject constructor(
}.launchIn(modelScope)
}
private fun onNextClick(confirmUM: ConfirmUM) {
when (confirmUM) {
is ConfirmUM.Success -> {
modelScope.launch {
nftSendSuccessTrigger.triggerSuccessNFTSend()
}
appRouter.pop()
}
is ConfirmUM.Content -> if (confirmUM.isSending) {
return
} else {
onSendClick()
}
else -> return
}
}
private companion object {
const val CHECK_FEE_UPDATE_DELAY = 10_000L
}

View file

@ -7,12 +7,14 @@ import kotlinx.collections.immutable.persistentListOf
internal class NFTSendConfirmInitialStateTransformer(
private val isShowTapHelp: Boolean,
private val walletName: TextReference,
) : Transformer<ConfirmUM> {
override fun transform(prevState: ConfirmUM): ConfirmUM {
return ConfirmUM.Content(
isSending = false,
showTapHelp = isShowTapHelp,
sendingFooter = TextReference.EMPTY,
walletName = walletName,
notifications = persistentListOf(),
)
}

View file

@ -1,12 +1,20 @@
package com.tangem.features.send.v2.sendnft.confirm.model.transformers
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.utils.parseToBigDecimal
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.utils.formatFooterFiatFee
import com.tangem.features.send.v2.common.utils.getTronTokenFeeSendingText
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
@ -19,11 +27,13 @@ internal class NFTSendConfirmationNotificationsTransformer(
private val analyticsEventHandler: AnalyticsEventHandler,
private val cryptoCurrency: CryptoCurrency,
private val analyticsCategoryName: String,
private val appCurrency: AppCurrency,
) : Transformer<ConfirmUM> {
override fun transform(prevState: ConfirmUM): ConfirmUM {
val state = prevState as? ConfirmUM.Content ?: return prevState
val feeUM = feeUM as? FeeUM.Content ?: return prevState
return state.copy(
sendingFooter = getSendingFooterText(),
notifications = buildList {
addTooHighNotification(feeUM.feeSelectorUM)
addTooLowNotification(feeUM)
@ -48,6 +58,38 @@ internal class NFTSendConfirmationNotificationsTransformer(
}
}
private fun getSendingFooterText(): TextReference {
val feeUM = feeUM as? FeeUM.Content
val fee = (feeUM?.feeSelectorUM as? FeeSelectorUM.Content)?.selectedFee ?: return TextReference.EMPTY
val fiatFee = formatFooterFiatFee(
amount = fee.amount,
isFeeConvertibleToFiat = feeUM.isFeeConvertibleToFiat,
isFeeApproximate = feeUM.isFeeApproximate,
appCurrency = appCurrency,
)
return if (feeUM.isTronToken && fee is Fee.Tron) {
getTronTokenFeeSendingText(
fee = fee,
fiatFee = fiatFee,
fiatSending = resourceReference(R.string.common_nft),
)
} else {
resourceReference(
id = if (feeUM.isFeeConvertibleToFiat) {
R.string.send_summary_transaction_description
} else {
R.string.send_summary_transaction_description_no_fiat_fee
},
formatArgs = wrappedList(
resourceReference(R.string.common_nft),
fiatFee,
),
)
}
}
private fun MutableList<NotificationUM>.addTooHighNotification(feeSelectorUM: FeeSelectorUM) {
if (feeSelectorUM !is FeeSelectorUM.Content) return

View file

@ -11,13 +11,16 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.ui.components.SpacerHMax
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
import com.tangem.core.ui.extensions.TextReference
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.DateTimeFormatters
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.features.nft.component.NFTDetailsBlockComponent
import com.tangem.features.send.v2.common.ui.SendingText
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.tapHelp
import com.tangem.features.send.v2.impl.R
@ -66,6 +69,8 @@ internal fun NFTSendConfirmContent(
)
}
}
SpacerHMax()
SendingText(footerText = confirmUM?.sendingFooter ?: TextReference.EMPTY)
}
}

View file

@ -18,15 +18,16 @@ import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
import com.tangem.domain.feedback.models.BlockchainErrorInfo
import com.tangem.domain.feedback.models.FeedbackEmailType
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.CreateNFTTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.requireColdWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.NFTSendComponent
import com.tangem.features.send.v2.common.CommonSendRoute
@ -60,7 +61,7 @@ internal class NFTSendModel @Inject constructor(
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
@ -151,7 +152,8 @@ internal class NFTSendModel @Inject constructor(
?: return@launch
getCurrenciesStatusUpdates(
isSingleWalletWithToken = wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
isSingleWalletWithToken = wallet is UserWallet.Cold &&
wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
)
},
ifLeft = {
@ -166,7 +168,7 @@ internal class NFTSendModel @Inject constructor(
saveBlockchainErrorUseCase(
error = BlockchainErrorInfo(
errorMessage = errorMessage.orEmpty(),
blockchainId = cryptoCurrency.network.id.value,
blockchainId = cryptoCurrency.network.rawId,
derivationPath = cryptoCurrency.network.derivationPath.value,
destinationAddress = "",
tokenSymbol = null,
@ -175,7 +177,8 @@ internal class NFTSendModel @Inject constructor(
),
)
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrNull() ?: return
val cardInfo =
getCardInfoUseCase(userWallet.requireColdWallet().scanResponse).getOrNull() ?: return // TODO [REDACTED_TASK_KEY]
modelScope.launch {
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
@ -183,7 +186,7 @@ internal class NFTSendModel @Inject constructor(
}
private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean) {
getCurrencyStatusUpdatesUseCase(
getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet(
userWalletId = userWalletId,
currencyId = cryptoCurrency.id,
isSingleWalletWithTokens = isSingleWalletWithToken,

View file

@ -1,7 +1,7 @@
package com.tangem.features.send.v2.sendnft.ui.state
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM

View file

@ -1,7 +1,7 @@
package com.tangem.features.send.v2.subcomponents.destination
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues

View file

@ -5,6 +5,7 @@ internal enum class EnterAddressSource {
PasteButton,
RecentAddress,
InputField,
MyWallets,
;
val isPasted: Boolean

View file

@ -9,17 +9,19 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.models.network.CryptoCurrencyAddress
import com.tangem.domain.qrscanning.models.SourceType
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
import com.tangem.domain.tokens.GetNetworkAddressesUseCase
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
import com.tangem.domain.transaction.usecase.IsUtxoConsolidationAvailableUseCase
import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.isLocked
import com.tangem.domain.wallets.models.isMultiCurrency
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents
@ -142,8 +144,10 @@ internal class SendDestinationModel @Inject constructor(
private fun initSenderAddress() {
modelScope.launch {
senderAddresses.value = getNetworkAddressesUseCase.invokeSync(userWalletId, cryptoCurrency.network)
.filter { cryptoCurrency.id == it.cryptoCurrency.id }
senderAddresses.value = getNetworkAddressesUseCase.invokeSync(
userWalletId = userWalletId,
networkRawId = cryptoCurrency.network.id.rawId,
).filter { cryptoCurrency.id == it.cryptoCurrency.id }
}
senderAddresses.onEach {
getWalletsAndRecent()
@ -212,13 +216,19 @@ internal class SendDestinationModel @Inject constructor(
val addresses = if (!wallet.isMultiCurrency) {
getCryptoCurrencyUseCase(wallet.walletId).getOrNull()?.let {
if (it.network.id == cryptoCurrencyNetwork.id) {
getNetworkAddressesUseCase.invokeSync(wallet.walletId, it.network)
getNetworkAddressesUseCase.invokeSync(
userWalletId = wallet.walletId,
networkRawId = it.network.id.rawId,
)
} else {
null
}
}
} else {
getNetworkAddressesUseCase.invokeSync(wallet.walletId, cryptoCurrencyNetwork)
getNetworkAddressesUseCase.invokeSync(
userWalletId = wallet.walletId,
networkRawId = cryptoCurrencyNetwork.id.rawId,
)
}
wallet to addresses
}

View file

@ -9,13 +9,13 @@ import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.utils.DateTimeFormatters
import com.tangem.core.ui.utils.toDateFormatWithTodayYesterday
import com.tangem.core.ui.utils.toTimeFormat
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.TxInfo
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationRecipientListUM
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.RECENT_DEFAULT_COUNT
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.RECENT_KEY_TAG
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.emptyListState
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationRecipientListUM
import com.tangem.utils.converter.Converter
import com.tangem.utils.extensions.isZero
import kotlinx.collections.immutable.ImmutableList
@ -23,21 +23,21 @@ import kotlinx.collections.immutable.toPersistentList
internal class SendRecipientHistoryListConverter(
private val cryptoCurrency: CryptoCurrency,
) : Converter<List<TxHistoryItem>, ImmutableList<DestinationRecipientListUM>> {
) : Converter<List<TxInfo>, ImmutableList<DestinationRecipientListUM>> {
override fun convert(value: List<TxHistoryItem>): ImmutableList<DestinationRecipientListUM> {
override fun convert(value: List<TxInfo>): ImmutableList<DestinationRecipientListUM> {
return value.filterRecipients(cryptoCurrency).ifEmpty {
emptyListState(RECENT_KEY_TAG, RECENT_DEFAULT_COUNT)
}
}
private fun List<TxHistoryItem>.filterRecipients(cryptoCurrency: CryptoCurrency) = this.filter { item ->
val isTransfer = item.type == TxHistoryItem.TransactionType.Transfer
val isNotContract = item.interactionAddressType is TxHistoryItem.InteractionAddressType.User
private fun List<TxInfo>.filterRecipients(cryptoCurrency: CryptoCurrency) = this.filter { item ->
val isTransfer = item.type == TxInfo.TransactionType.Transfer
val isNotContract = item.interactionAddressType is TxInfo.InteractionAddressType.User
val isSingleAddress = if (item.isOutgoing) {
item.destinationType is TxHistoryItem.DestinationType.Single
item.destinationType is TxInfo.DestinationType.Single
} else {
item.sourceType is TxHistoryItem.SourceType.Single
item.sourceType is TxInfo.SourceType.Single
}
val notZero = !item.amount.isZero()
isTransfer && isSingleAddress && isNotContract && item.isOutgoing && notZero
@ -54,31 +54,31 @@ internal class SendRecipientHistoryListConverter(
)
}.toPersistentList()
private fun TxHistoryItem.extractAddress(): TextReference = if (isOutgoing) {
private fun TxInfo.extractAddress(): TextReference = if (isOutgoing) {
when (val destination = destinationType) {
is TxHistoryItem.DestinationType.Multiple -> resourceReference(
is TxInfo.DestinationType.Multiple -> resourceReference(
R.string.transaction_history_multiple_addresses,
)
is TxHistoryItem.DestinationType.Single -> stringReference(destination.addressType.address)
is TxInfo.DestinationType.Single -> stringReference(destination.addressType.address)
}
} else {
when (val source = sourceType) {
is TxHistoryItem.SourceType.Multiple -> resourceReference(R.string.transaction_history_multiple_addresses)
is TxHistoryItem.SourceType.Single -> stringReference(source.address)
is TxInfo.SourceType.Multiple -> resourceReference(R.string.transaction_history_multiple_addresses)
is TxInfo.SourceType.Single -> stringReference(source.address)
}
}
private fun TxHistoryItem.extractIconRes() = if (isOutgoing) {
private fun TxInfo.extractIconRes() = if (isOutgoing) {
R.drawable.ic_arrow_up_24
} else {
R.drawable.ic_arrow_down_24
}
private fun TxHistoryItem.getAmount(cryptoCurrency: CryptoCurrency): String {
private fun TxInfo.getAmount(cryptoCurrency: CryptoCurrency): String {
return amount.format { crypto(cryptoCurrency) }
}
private fun TxHistoryItem.extractTimestamp(): TextReference {
private fun TxInfo.extractTimestamp(): TextReference {
val date = timestampInMillis.toDateFormatWithTodayYesterday(
formatter = DateTimeFormatters.dateDDMMYYYY,
)

View file

@ -1,11 +1,11 @@
package com.tangem.features.send.v2.subcomponents.destination.model.converters
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationRecipientListUM
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.WALLET_DEFAULT_COUNT
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.WALLET_KEY_TAG
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.emptyListState
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationRecipientListUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationWalletUM
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.PersistentList

View file

@ -4,8 +4,8 @@ import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.Network
import com.tangem.features.send.v2.impl.R
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationTextFieldUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM

View file

@ -1,10 +1,10 @@
package com.tangem.features.send.v2.subcomponents.destination.model.transformers
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.network.TxInfo
import com.tangem.features.send.v2.subcomponents.destination.model.converters.SendRecipientHistoryListConverter
import com.tangem.features.send.v2.subcomponents.destination.model.converters.SendRecipientWalletListConverter
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationWalletUM
import com.tangem.utils.transformer.Transformer
@ -13,7 +13,7 @@ internal class SendDestinationRecentListTransformer(
private val cryptoCurrency: CryptoCurrency,
private val isUtxoConsolidationAvailable: Boolean,
private val destinationWalletList: List<DestinationWalletUM>,
private val txHistoryList: List<TxHistoryItem>,
private val txHistoryList: List<TxInfo>,
) : Transformer<DestinationUM> {
override fun transform(prevState: DestinationUM): DestinationUM {
val state = prevState as? DestinationUM.Content ?: return prevState

View file

@ -72,9 +72,14 @@ internal fun SendDestinationContent(
)
listItem(
list = wallets,
clickIntents = clickIntents,
isLast = recipients.any { !it.isVisible },
isBalanceHidden = isBalanceHidden,
onClick = { title ->
clickIntents.onRecipientAddressValueChange(
title,
EnterAddressSource.MyWallets,
)
},
)
listHeaderItem(
titleRes = R.string.send_recent_transactions,
@ -83,9 +88,14 @@ internal fun SendDestinationContent(
)
listItem(
list = recipients,
clickIntents = clickIntents,
isLast = true,
isBalanceHidden = isBalanceHidden,
onClick = { title ->
clickIntents.onRecipientAddressValueChange(
title,
EnterAddressSource.RecentAddress,
)
},
)
}
}
@ -182,9 +192,9 @@ private fun LazyListScope.listHeaderItem(@StringRes titleRes: Int, isVisible: Bo
private fun LazyListScope.listItem(
list: ImmutableList<DestinationRecipientListUM>,
clickIntents: SendDestinationClickIntents,
isLast: Boolean,
isBalanceHidden: Boolean,
onClick: (String) -> Unit,
) {
items(
count = list.size,
@ -201,12 +211,7 @@ private fun LazyListScope.listItem(
info = item.timestamp?.resolveReference(),
subtitleEndOffset = item.subtitleEndOffset,
subtitleIconRes = item.subtitleIconRes,
onClick = {
clickIntents.onRecipientAddressValueChange(
title,
EnterAddressSource.RecentAddress,
)
},
onClick = { onClick(title) },
isLoading = item.isLoading,
modifier = Modifier
.animateItem()

View file

@ -3,7 +3,7 @@ package com.tangem.features.send.v2.subcomponents.destination.ui.state
import androidx.annotation.DrawableRes
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.models.network.Network
import com.tangem.domain.wallets.models.UserWalletId
@Immutable

View file

@ -1,7 +1,7 @@
package com.tangem.features.send.v2.subcomponents.destination.ui.state
import androidx.compose.runtime.Immutable
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.wallets.models.UserWalletId
/**

View file

@ -30,7 +30,7 @@ internal class BitcoinCustomFeeConverter(
) : CustomFeeConverter<Fee.Bitcoin> {
private val currencyStatus = feeCryptoCurrencyStatus.value
private val network = feeCryptoCurrencyStatus.currency.network.id.value
private val network = feeCryptoCurrencyStatus.currency.network.rawId
override fun convert(value: Fee.Bitcoin): ImmutableList<CustomFeeFieldUM> {
val feeValue = value.amount.value

View file

@ -1,7 +1,7 @@
package com.tangem.features.send.v2.subcomponents.fee.model.transformers
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
@ -28,7 +28,7 @@ internal class SendFeeInitialStateTransformer(
isCustomSelected = false,
isFeeConvertibleToFiat = feeCryptoCurrencyStatus.currency.network.hasFiatFeeRate,
isTronToken = cryptoCurrency is CryptoCurrency.Token &&
isTron(cryptoCurrency.network.id.value),
isTron(cryptoCurrency.network.rawId),
)
}
}

View file

@ -25,12 +25,12 @@ 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.ui.extensions.resourceReference
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.notifications.GetTronFeeNotificationShowCountUseCase
import com.tangem.domain.notifications.IncrementNotificationsShowCountUseCase
import com.tangem.domain.tokens.GetBalanceNotEnoughForFeeWarningUseCase
import com.tangem.domain.tokens.GetCurrencyCheckUseCase
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyCheck
import com.tangem.domain.transaction.usecase.ValidateTransactionUseCase
@ -255,7 +255,7 @@ internal class NotificationsModel @Inject constructor(
)
},
)
if (!BlockchainUtils.isCardano(currency.network.id.value)) {
if (!BlockchainUtils.isCardano(currency.network.rawId)) {
addDustWarningNotification(
dustValue = currencyCheck.dustValue,
feeValue = feeValue,
@ -375,7 +375,7 @@ internal class NotificationsModel @Inject constructor(
private suspend fun MutableList<NotificationUM>.addTronNetworkFeesNotification() {
val cryptoCurrency = cryptoCurrencyStatus.currency
val isTronToken = cryptoCurrency is CryptoCurrency.Token &&
isTron(cryptoCurrency.network.id.value)
isTron(cryptoCurrency.network.rawId)
if (isTronToken && getTronFeeNotificationShowCountUseCase() <= TRON_FEE_NOTIFICATION_MAX_SHOW_COUNT) {
add(