Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-21 14:58:24 +05:00
commit b1f71ebb0a
8 changed files with 91 additions and 56 deletions

View file

@ -201,6 +201,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
appCurrency = model.appCurrency,
onLoadFee = model::loadFee,
sendAmount = sendAmount,
destinationAddress = destinationAddress,
callback = model,
@ -240,6 +241,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
appCurrency = model.appCurrency,
callback = model,
predefinedValues = predefinedValues,
onLoadFee = model::loadFee,
),
)
}

View file

@ -4,18 +4,21 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import arrow.core.Either
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.PredefinedValues
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.send.confirm.model.SendConfirmModel
import com.tangem.features.send.v2.send.confirm.ui.SendConfirmContent
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
import com.tangem.features.send.v2.send.ui.state.SendUM
import com.tangem.features.send.v2.subcomponents.amount.SendAmountBlockComponent
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
@ -79,6 +82,7 @@ internal class SendConfirmComponent(
sendAmount = model.confirmData.enteredAmount.orZero(),
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
onLoadFee = params.onLoadFee,
),
onResult = model::onFeeResult,
onClick = model::showEditFee,
@ -144,6 +148,7 @@ internal class SendConfirmComponent(
val currentRoute: Flow<CommonSendRoute>,
val isBalanceHidingFlow: StateFlow<Boolean>,
val predefinedValues: PredefinedValues,
val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
)
interface ModelCallback {

View file

@ -3,6 +3,7 @@ package com.tangem.features.send.v2.send.model
import androidx.compose.runtime.Stable
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.amountScreen.models.AmountState
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
@ -26,6 +27,8 @@ import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.transaction.usecase.GetTransferFeeUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.SendComponent
@ -76,6 +79,7 @@ internal class SendModel @Inject constructor(
private val getCardInfoUseCase: GetCardInfoUseCase,
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getTransferFeeUseCase: GetTransferFeeUseCase,
private val sendAmountUpdateQRTrigger: SendAmountUpdateQRTrigger,
) : Model(), SendComponentCallback {
@ -126,6 +130,20 @@ internal class SendModel @Inject constructor(
_uiState.update { sendUM }
}
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: error("Invalid destination")
val amountUM = uiState.value.amountUM as? AmountState.Data ?: error("Invalid amount")
val enteredDestinationAddress = destinationUM.addressTextField.value
val enteredAmount = amountUM.amountTextField.cryptoAmount.value ?: error("Invalid amount")
return getTransferFeeUseCase(
destination = enteredDestinationAddress,
amount = enteredAmount,
userWallet = userWallet,
cryptoCurrency = params.currency,
)
}
private fun resetPredefinedAmount() {
// reset predefined amount
val internalPredefinedValues = predefinedValues

View file

@ -151,6 +151,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
appCurrency = model.appCurrency,
sendAmount = BigDecimal.ZERO,
onLoadFee = model::loadFee,
destinationAddress = destinationAddress,
callback = model,
onNextClick = ::onChildBack,
@ -176,6 +177,7 @@ internal class DefaultNFTSendComponent @AssistedInject constructor(
callback = model,
currentRoute = currentRouteFlow.filterIsInstance<CommonSendRoute.Confirm>(),
isBalanceHidingFlow = model.isBalanceHiddenFlow,
onLoadFee = model::loadFee,
),
)

View file

@ -4,6 +4,8 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import arrow.core.Either
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
@ -11,6 +13,7 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.nft.models.NFTAsset
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.nft.component.NFTDetailsBlockComponent
import com.tangem.features.send.v2.common.CommonSendRoute
@ -63,6 +66,7 @@ internal class NFTSendConfirmComponent(
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
appCurrency = params.appCurrency,
sendAmount = BigDecimal.ZERO,
onLoadFee = params.onLoadFee,
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
),
@ -139,6 +143,7 @@ internal class NFTSendConfirmComponent(
val callback: ModelCallback,
val currentRoute: Flow<CommonSendRoute.Confirm>,
val isBalanceHidingFlow: StateFlow<Boolean>,
val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
)
interface ModelCallback {

View file

@ -1,7 +1,9 @@
package com.tangem.features.send.v2.sendnft.model
import androidx.compose.runtime.Stable
import arrow.core.Either
import arrow.core.getOrElse
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -14,12 +16,13 @@ import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.NFTSendComponent
import com.tangem.features.send.v2.common.CommonSendRoute
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.sendnft.confirm.NFTSendConfirmComponent
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent
@ -88,6 +91,23 @@ internal class NFTSendModel @Inject constructor(
_uiState.update { it.copy(feeUM = feeUM) }
}
suspend fun loadFee(): Either<GetFeeError, TransactionFee> {
TODO()
// val destinationUM = uiState.value.destinationUM as? DestinationUM.Content ?: error("Invalid destination")
// val ownerAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value
// ?: error("Invalid owner address")
// val enteredDestinationAddress = destinationUM.addressTextField.value
// val nftAsset = null // NFTSdkAssetConverter.convertBack(params.nftAsset).second
// getNFTTransferFeeUseCase(
// ownerAddress = ownerAddress,
// destinationAddress = enteredDestinationAddress,
// nftAsset = nftAsset,
// userWallet = userWallet,
// cryptoCurrency = cryptoCurrency,
// )
}
private fun initAppCurrency() {
modelScope.launch {
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
@ -131,7 +151,9 @@ internal class NFTSendModel @Inject constructor(
cryptoCurrencyStatus = cryptoStatus,
).getOrNull() ?: cryptoStatus
router.replaceAll(CommonSendRoute.Destination(isEditMode = false))
if (uiState.value.destinationUM is DestinationUM.Empty) {
router.replaceAll(CommonSendRoute.Destination(isEditMode = false))
}
},
ifLeft = {
// sendConfirmAlertFactory.getGenericErrorState {

View file

@ -1,7 +1,10 @@
package com.tangem.features.send.v2.subcomponents.fee
import arrow.core.Either
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.transaction.error.GetFeeError
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
@ -19,6 +22,7 @@ internal sealed class SendFeeComponentParams {
abstract val appCurrency: AppCurrency
abstract val sendAmount: BigDecimal
abstract val destinationAddress: String
abstract val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>
data class FeeParams(
override val state: FeeUM,
@ -29,6 +33,7 @@ internal sealed class SendFeeComponentParams {
override val appCurrency: AppCurrency,
override val sendAmount: BigDecimal,
override val destinationAddress: String,
override val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
val currentRoute: Flow<CommonSendRoute.Fee>,
val callback: SendFeeComponent.ModelCallback,
val onNextClick: () -> Unit,
@ -43,6 +48,7 @@ internal sealed class SendFeeComponentParams {
override val appCurrency: AppCurrency,
override val sendAmount: BigDecimal,
override val destinationAddress: String,
override val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
val blockClickEnableFlow: StateFlow<Boolean>,
) : SendFeeComponentParams()
}

View file

@ -8,7 +8,6 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.transaction.usecase.GetTransferFeeUseCase
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
import com.tangem.features.send.v2.common.ui.state.NavigationUM
import com.tangem.features.send.v2.impl.R
@ -29,7 +28,6 @@ import com.tangem.utils.coroutines.saveIn
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.math.BigDecimal
import java.util.Locale
import javax.inject.Inject
@ -40,7 +38,6 @@ internal class SendFeeModel @Inject constructor(
paramsContainer: ParamsContainer,
override val dispatchers: CoroutineDispatcherProvider,
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
private val getTransferFeeUseCase: GetTransferFeeUseCase,
private val urlOpener: UrlOpener,
private val analyticsEventHandler: AnalyticsEventHandler,
private val sendFeeAlertFactory: SendFeeAlertFactory,
@ -74,10 +71,32 @@ internal class SendFeeModel @Inject constructor(
}
override fun feeReload() {
loadFee(
amountValue = params.sendAmount,
destinationAddress = params.destinationAddress,
)
modelScope.launch {
val isShowLoading = (uiState.value as? FeeUM.Content)?.feeSelectorUM !is FeeSelectorUM.Content
if (isShowLoading) {
_uiState.update(SendFeeLoadingTransformer)
}
params.onLoadFee().fold(
ifRight = {
_uiState.update(
SendFeeLoadedTransformer(
fees = it,
clickIntents = this@SendFeeModel,
appCurrency = appCurrency,
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
isFeeApproximate = isFeeApproximate(it.normal.amount.type),
),
)
updateFeeNotifications()
},
ifLeft = { feeError ->
if (isShowLoading) {
_uiState.update(SendFeeFailedTransformer(feeError))
}
updateFeeNotifications()
},
)
}.saveIn(feeJobHolder)
}
override fun onFeeSelectorClick(feeType: FeeType) {
@ -159,12 +178,7 @@ internal class SendFeeModel @Inject constructor(
private fun subscribeOnFeeReloadTriggerUpdates() {
feeReloadListener.reloadTriggerFlow
.onEach { (amount, destination) ->
loadFee(
amountValue = amount ?: params.sendAmount,
destinationAddress = destination ?: params.destinationAddress,
)
}
.onEach { feeReload() }
.launchIn(modelScope)
}
@ -186,48 +200,9 @@ internal class SendFeeModel @Inject constructor(
params.callback.onFeeResult(uiState.value)
}
private fun loadFee(amountValue: BigDecimal, destinationAddress: String) {
modelScope.launch {
val isShowLoading = (uiState.value as? FeeUM.Content)?.feeSelectorUM !is FeeSelectorUM.Content
if (isShowLoading) {
_uiState.update(SendFeeLoadingTransformer)
}
getTransferFeeUseCase.invoke(
amount = amountValue,
destination = destinationAddress,
userWallet = params.userWallet,
cryptoCurrency = cryptoCurrencyStatus.currency,
).fold(
ifRight = {
_uiState.update(
SendFeeLoadedTransformer(
fees = it,
clickIntents = this@SendFeeModel,
appCurrency = appCurrency,
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
isFeeApproximate = isFeeApproximate(it.normal.amount.type),
),
)
updateFeeNotifications()
},
ifLeft = { feeError ->
if (isShowLoading) {
_uiState.update(SendFeeFailedTransformer(feeError))
}
updateFeeNotifications()
},
)
}.saveIn(feeJobHolder)
}
private fun checkLoadFee() {
modelScope.launch {
getTransferFeeUseCase.invoke(
amount = params.sendAmount,
destination = params.destinationAddress,
userWallet = params.userWallet,
cryptoCurrency = cryptoCurrencyStatus.currency,
).fold(
params.onLoadFee().fold(
ifRight = {
sendFeeAlertFactory.getFeeUpdatedAlert(
newFee = it,