Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-25 20:04:51 +05:00
parent d614f69161
commit 4aa74c4b70
21 changed files with 397 additions and 70 deletions

View file

@ -433,6 +433,7 @@ internal class SendConfirmModel @Inject constructor(
).filter { it.second is CommonSendRoute.Confirm }.onEach { (state, _) ->
val amountUM = state.amountUM as? AmountState.Data
val confirmUM = state.confirmUM
val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending
params.callback.onResult(
state.copy(
navigationUM = NavigationUM.Content(
@ -462,9 +463,9 @@ internal class SendConfirmModel @Inject constructor(
}
else -> resourceReference(R.string.common_send)
},
iconResId = R.drawable.ic_tangem_24.takeIf { confirmUM is ConfirmUM.Content },
iconResId = R.drawable.ic_tangem_24.takeIf { isReadyToSend },
isEnabled = confirmUM.isPrimaryButtonEnabled,
isHapticClick = confirmUM is ConfirmUM.Content && !confirmUM.isSending,
isHapticClick = isReadyToSend,
onClick = {
when (confirmUM) {
is ConfirmUM.Success -> appRouter.pop()

View file

@ -2,6 +2,7 @@ package com.tangem.features.send.v2.sendnft.confirm.model
import android.os.SystemClock
import arrow.core.getOrElse
import com.tangem.blockchain.common.TransactionData
import com.tangem.common.routing.AppRouter
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.decompose.di.ModelScoped
@ -12,9 +13,14 @@ 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.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
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.features.send.v2.common.CommonSendRoute
import com.tangem.features.send.v2.common.SendBalanceUpdater
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.impl.R
@ -37,6 +43,7 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import java.math.BigDecimal
import javax.inject.Inject
@ -49,21 +56,32 @@ internal class NFTSendConfirmModel @Inject constructor(
private val appRouter: AppRouter,
private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase,
private val neverShowTapHelpUseCase: NeverShowTapHelpUseCase,
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val sendTransactionUseCase: SendTransactionUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val notificationsUpdateTrigger: NotificationsUpdateTrigger,
private val sendFeeCheckReloadTrigger: SendFeeCheckReloadTrigger,
private val sendFeeCheckReloadListener: SendFeeCheckReloadListener,
private val urlOpener: UrlOpener,
private val shareManager: ShareManager,
private val analyticsEventHandler: AnalyticsEventHandler,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), NFTSendConfirmClickIntents {
private val params: NFTSendConfirmComponent.Params = paramsContainer.require()
private val userWallet = params.userWallet
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
private val cryptoCurrency = cryptoCurrencyStatus.currency
private val _uiState = MutableStateFlow(params.state)
val uiState = _uiState.asStateFlow()
private val sendBalanceUpdater = sendBalanceUpdaterFactory.create(
cryptoCurrency = cryptoCurrency,
userWallet = userWallet,
)
private val destinationUM
get() = uiState.value.destinationUM as? DestinationUM.Content
private val feeUM
@ -191,34 +209,63 @@ internal class NFTSendConfirmModel @Inject constructor(
}
private fun verifyAndSendTransaction() {
// TODO:
val destination = destinationUM?.addressTextField?.value ?: return
val memo = destinationUM?.memoTextField?.value
val fee = feeSelectorUM?.selectedFee ?: return
val ownerAddress = cryptoCurrencyStatus.value.networkAddress?.defaultAddress?.value ?: return
val sdkNFTAsset = NFTSdkAssetConverter.convertBack(params.nftAsset)
modelScope.launch {
createNFTTransferTransactionUseCase(
ownerAddress = ownerAddress,
nftAsset = sdkNFTAsset.second,
fee = fee,
memo = memo,
destinationAddress = destination,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
).fold(
ifLeft = { error ->
Timber.e(error)
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
// alertFactory.getGenericErrorState {
// onFailedTxEmailClick(error.localizedMessage.orEmpty())
// }
},
ifRight = { txData ->
sendTransaction(txData)
},
)
}
}
// private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
// val result = sendTransactionUseCase(
// txData = txData,
// userWallet = userWallet,
// network = cryptoCurrency.network,
// )
//
// _uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
//
// result.fold(
// ifLeft = { error ->
// // alertFactory.getSendTransactionErrorState(
// // error = error,
// // popBack = appRouter::pop,
// // onFailedTxEmailClick = ::onFailedTxEmailClick,
// // )
// analyticsEventHandler.send(SendAnalyticEvents.TransactionError(cryptoCurrency.symbol))
// },
// ifRight = {
// updateTransactionStatus(txData)
// sendBalanceUpdater.scheduleUpdates()
// // sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
// },
// )
// }
private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
val result = sendTransactionUseCase(
txData = txData,
userWallet = params.userWallet,
network = params.cryptoCurrencyStatus.currency.network,
)
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
result.fold(
ifLeft = { error ->
Timber.e(error.toString())
// alertFactory.getSendTransactionErrorState(
// error = error,
// popBack = appRouter::pop,
// onFailedTxEmailClick = ::onFailedTxEmailClick,
// )
// analyticsEventHandler.send(SendAnalyticEvents.TransactionError(cryptoCurrency.symbol))
},
ifRight = {
updateTransactionStatus(txData)
sendBalanceUpdater.scheduleUpdates()
// sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
},
)
}
private fun subscribeOnCheckFeeResultUpdates() {
sendFeeCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
@ -232,13 +279,13 @@ internal class NFTSendConfirmModel @Inject constructor(
}.launchIn(modelScope)
}
// private suspend fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
// val txUrl = getExplorerTransactionUrlUseCase(
// userWalletId = userWallet.walletId,
// network = cryptoCurrency.network,
// ).getOrElse { "" }
// _uiState.update(NFTSendConfirmSentStateTransformer(txData, txUrl))
// }
private fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
val txUrl = getExplorerTransactionUrlUseCase(
txHash = txData.hash.orEmpty(),
networkId = cryptoCurrency.network.id,
).getOrElse { "" }
_uiState.update(NFTSendConfirmSentStateTransformer(txData, txUrl))
}
private fun updateConfirmNotifications() {
modelScope.launch {
@ -272,6 +319,7 @@ 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
params.callback.onResult(
state.copy(
navigationUM = NavigationUM.Content(
@ -301,9 +349,9 @@ internal class NFTSendConfirmModel @Inject constructor(
}
else -> resourceReference(R.string.common_send)
},
iconResId = R.drawable.ic_tangem_24.takeIf { confirmUM is ConfirmUM.Content },
iconResId = R.drawable.ic_tangem_24.takeIf { isReadyToSend },
isEnabled = confirmUM.isPrimaryButtonEnabled,
isHapticClick = confirmUM is ConfirmUM.Content && !confirmUM.isSending,
isHapticClick = isReadyToSend,
onClick = {
when (confirmUM) {
is ConfirmUM.Success -> appRouter.pop()

View file

@ -3,11 +3,13 @@ package com.tangem.features.send.v2.sendnft.model
import androidx.compose.runtime.Stable
import arrow.core.Either
import arrow.core.getOrElse
import arrow.core.left
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
import com.tangem.core.decompose.navigation.Router
import com.tangem.datasource.local.nft.converter.NFTSdkAssetConverter
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.common.util.cardTypesResolver
@ -17,6 +19,8 @@ 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.transaction.usecase.CreateNFTTransferTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.features.send.v2.api.NFTSendComponent
@ -52,6 +56,8 @@ internal class NFTSendModel @Inject constructor(
private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
private val createNFTTransferTransactionUseCase: CreateNFTTransferTransactionUseCase,
private val getFeeUseCase: GetFeeUseCase,
) : Model(), SendNFTComponentCallback {
val params: NFTSendComponent.Params = paramsContainer.require()
@ -92,20 +98,29 @@ internal class NFTSendModel @Inject constructor(
}
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
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 enteredMemo = destinationUM.memoTextField?.value
val nftAsset = NFTSdkAssetConverter.convertBack(params.nftAsset).second
// getNFTTransferFeeUseCase(
// ownerAddress = ownerAddress,
// destinationAddress = enteredDestinationAddress,
// nftAsset = nftAsset,
// userWallet = userWallet,
// cryptoCurrency = cryptoCurrency,
// )
val nftSendTransaction = createNFTTransferTransactionUseCase(
ownerAddress = ownerAddress,
nftAsset = nftAsset,
memo = enteredMemo,
destinationAddress = enteredDestinationAddress,
userWalletId = userWallet.walletId,
network = cryptoCurrency.network,
).getOrElse {
return GetFeeError.DataError(it).left()
}
return getFeeUseCase(
transactionData = nftSendTransaction,
network = cryptoCurrency.network,
userWallet = userWallet,
)
}
private fun initAppCurrency() {