Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-27 12:16:41 +04:00
parent c62f464b86
commit e047c491cf
11 changed files with 158 additions and 20 deletions

View file

@ -22,6 +22,8 @@ dependencies {
implementation(projects.core.navigation)
implementation(projects.core.ui)
implementation(projects.core.utils)
implementation(projects.core.analytics)
implementation(projects.core.analytics.models)
/** Common */
implementation(projects.common.ui)
@ -33,6 +35,7 @@ dependencies {
/** Domain */
implementation(projects.domain.models)
implementation(projects.domain.wallets)
implementation(projects.domain.wallets.models)
implementation(projects.domain.transaction.models)
implementation(projects.domain.transaction)

View file

@ -4,7 +4,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.child
import com.tangem.core.decompose.model.getOrCreateModel
@ -46,7 +45,7 @@ internal class DefaultGiveApprovalComponent @AssistedInject constructor(
feeDisplaySource = FeeSelectorParams.FeeDisplaySource.Screen,
analyticsCategoryName = CommonSendAnalyticEvents.APPROVE_CATEGORY,
analyticsSendSource = CommonSendAnalyticEvents.CommonSendSource.Approve,
userWalletId = params.userWallet.walletId,
userWalletId = params.userWalletId,
),
onResult = model::onFeeResult,
)
@ -84,7 +83,7 @@ internal class DefaultGiveApprovalComponent @AssistedInject constructor(
approveType = uiState.approveType,
approveItems = uiState.approveItems,
onChangeApproveType = model::onChangeApproveType,
walletInteractionIcon = walletInterationIcon(params.userWallet),
walletInteractionIcon = uiState.walletInteractionIcon,
isApproveEnabled = uiState.isApproveButtonEnabled,
isApproveLoading = uiState.isApproveLoading,
onApproveClick = model::onApproveClick,

View file

@ -2,8 +2,9 @@ package com.tangem.features.approval.impl
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import javax.inject.Inject
internal class DefaultGiveApprovalFeatureToggles(
internal class DefaultGiveApprovalFeatureToggles @Inject constructor(
private val featureToggles: FeatureTogglesManager,
) : GiveApprovalFeatureToggles {

View file

@ -3,7 +3,9 @@ package com.tangem.features.approval.impl.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.approval.api.GiveApprovalComponent
import com.tangem.features.approval.api.GiveApprovalFeatureToggles
import com.tangem.features.approval.impl.DefaultGiveApprovalComponent
import com.tangem.features.approval.impl.DefaultGiveApprovalFeatureToggles
import com.tangem.features.approval.impl.model.GiveApprovalModel
import dagger.Binds
import dagger.Module
@ -17,6 +19,10 @@ import javax.inject.Singleton
@Module
internal interface GiveApprovalFeatureModule {
@Singleton
@Binds
fun bindGiveApprovalFeatureToggle(toggles: DefaultGiveApprovalFeatureToggles): GiveApprovalFeatureToggles
@Binds
@Singleton
fun bindComponentFactory(factory: DefaultGiveApprovalComponent.Factory): GiveApprovalComponent.Factory

View file

@ -8,6 +8,9 @@ import com.tangem.blockchain.common.TransactionData
import com.tangem.blockchain.common.transaction.Fee
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
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.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -28,6 +31,8 @@ import com.tangem.features.approval.api.GiveApprovalComponent
import com.tangem.features.send.v2.api.callbacks.FeeSelectorModelCallback
import com.tangem.features.send.v2.api.entity.FeeSelectorUM
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.common.ui.userwallet.ext.walletInterationIcon
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableStateFlow
@ -52,14 +57,23 @@ internal class GiveApprovalModel @Inject constructor(
private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase,
private val uiMessageSender: UiMessageSender,
private val urlOpener: UrlOpener,
private val getUserWalletUseCase: GetUserWalletUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
) : Model(), FeeSelectorModelCallback {
private val params: GiveApprovalComponent.Params = paramsContainer.require()
private val userWallet by lazy {
requireNotNull(
getUserWalletUseCase(params.userWalletId).getOrNull(),
) { "No wallet found for id: $params.userWalletId" }
}
val uiState: StateFlow<GiveApprovalUM>
field = MutableStateFlow(
GiveApprovalUM(
approveType = ApproveType.LIMITED,
walletInteractionIcon = walletInterationIcon(userWallet),
isApproveButtonEnabled = false,
isApproveLoading = false,
),
@ -73,6 +87,7 @@ internal class GiveApprovalModel @Inject constructor(
}
fun onApproveClick() {
params.callback.onApproveClick()
uiState.update { it.copy(isApproveLoading = true) }
modelScope.launch(dispatchers.main) {
val isSuccess = sendApprovalTransaction()
@ -113,7 +128,7 @@ internal class GiveApprovalModel @Inject constructor(
return createApprovalTransactionUseCase(
cryptoCurrencyStatus = cryptoCurrencyStatus,
userWalletId = params.userWallet.walletId,
userWalletId = params.userWalletId,
amount = getApprovalAmount(),
contractAddress = tokenCurrency.contractAddress,
spenderAddress = params.spenderAddress,
@ -126,7 +141,7 @@ internal class GiveApprovalModel @Inject constructor(
return getFeeUseCase(
transactionData = approvalTransaction,
userWallet = params.userWallet,
userWallet = userWallet,
network = params.cryptoCurrencyStatus.currency.network,
)
}
@ -138,13 +153,13 @@ internal class GiveApprovalModel @Inject constructor(
return if (maybeToken == null) {
getFeeForGaslessUseCase(
transactionData = approvalTransaction,
userWallet = params.userWallet,
userWallet = userWallet,
network = params.cryptoCurrencyStatus.currency.network,
)
} else {
getFeeForTokenUseCase(
transactionData = approvalTransaction,
userWallet = params.userWallet,
userWallet = userWallet,
token = maybeToken.currency,
)
}
@ -162,7 +177,7 @@ internal class GiveApprovalModel @Inject constructor(
val transactionData = createApprovalTransactionUseCase(
cryptoCurrencyStatus = cryptoCurrencyStatus,
userWalletId = params.userWallet.walletId,
userWalletId = params.userWalletId,
amount = getApprovalAmount(),
fee = selectedFee,
contractAddress = tokenCurrency.contractAddress,
@ -174,14 +189,14 @@ internal class GiveApprovalModel @Inject constructor(
return if (isFeeInTokenCurrency) {
createAndSendGaslessTransactionUseCase(
userWallet = params.userWallet,
userWallet = userWallet,
transactionData = transactionData,
fee = feeExtended,
)
} else {
sendTransactionUseCase(
txData = transactionData,
userWallet = params.userWallet,
userWallet = userWallet,
network = tokenCurrency.network,
)
}.fold(
@ -189,7 +204,32 @@ internal class GiveApprovalModel @Inject constructor(
Timber.e("Failed to send approval transaction: $error")
false
},
ifRight = { true },
ifRight = {
sendApproveSuccessAnalytics(feeContent)
true
},
)
}
private fun sendApproveSuccessAnalytics(feeContent: FeeSelectorUM.Content) {
val currency = params.cryptoCurrencyStatus.currency
val feeToken = feeContent.feeExtraInfo.feeCryptoCurrencyStatus.currency.symbol
val permissionType = when (uiState.value.approveType) {
ApproveType.LIMITED -> "Current transaction"
ApproveType.UNLIMITED -> "Unlimited"
}
val event = AnalyticsParam.TxSentFrom.Approve(
blockchain = currency.network.name,
token = currency.symbol,
feeType = feeContent.toAnalyticType(),
feeToken = feeToken,
permissionType = permissionType,
)
analyticsEventHandler.send(
Basic.TransactionSent(
sentFrom = event,
memoType = Basic.TransactionSent.MemoType.Null,
),
)
}

View file

@ -1,5 +1,6 @@
package com.tangem.features.approval.impl.model
import androidx.annotation.DrawableRes
import com.tangem.common.ui.bottomsheet.permission.state.ApproveType
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@ -7,6 +8,7 @@ import kotlinx.collections.immutable.toImmutableList
internal data class GiveApprovalUM(
val approveType: ApproveType,
val approveItems: ImmutableList<ApproveType> = ApproveType.entries.toImmutableList(),
@DrawableRes val walletInteractionIcon: Int?,
val isApproveButtonEnabled: Boolean,
val isApproveLoading: Boolean,
)