diff --git a/features/staking/impl/build.gradle.kts b/features/staking/impl/build.gradle.kts index 92180a4384..72ddfd69ee 100644 --- a/features/staking/impl/build.gradle.kts +++ b/features/staking/impl/build.gradle.kts @@ -81,6 +81,10 @@ dependencies { /** Feature modules */ implementation(projects.features.staking.api) implementation(projects.features.txhistory.api) + implementation(projects.features.approval.api) + + /** Decompose */ + implementation(deps.decompose.ext.compose) /** DI */ implementation(deps.hilt.android) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/DefaultStakingComponent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/DefaultStakingComponent.kt index 3f40fea39d..f4f55c926f 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/DefaultStakingComponent.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/DefaultStakingComponent.kt @@ -4,8 +4,12 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.arkivanov.decompose.extensions.compose.subscribeAsState +import com.arkivanov.decompose.router.slot.childSlot import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.features.approval.api.GiveApprovalComponent import com.tangem.features.staking.api.StakingComponent import com.tangem.features.staking.impl.presentation.model.StakingModel import com.tangem.features.staking.impl.presentation.ui.StakingScreen @@ -16,14 +20,34 @@ import dagger.assisted.AssistedInject internal class DefaultStakingComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, @Assisted params: StakingComponent.Params, + private val giveApprovalComponentFactory: GiveApprovalComponent.Factory, ) : StakingComponent, AppComponentContext by appComponentContext { private val model: StakingModel = getOrCreateModel(params) + private val approvalSlot = childSlot( + key = "stakingApprovalSlot", + source = model.approvalSlotNavigation, + serializer = null, + handleBackButton = true, + childFactory = { _, factoryContext -> + val approvalParams = model.getApprovalParams() + ?: error("Approval params are not available") + giveApprovalComponentFactory.create( + context = childByContext(factoryContext), + params = approvalParams, + ) + }, + ) + @Composable override fun Content(modifier: Modifier) { val currentState by model.uiState.collectAsStateWithLifecycle() + val approvalSlotState by approvalSlot.subscribeAsState() + StakingScreen(currentState) + + approvalSlotState.child?.instance?.BottomSheet() } @AssistedFactory diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 9a5c3044f1..d94650898d 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -10,8 +10,13 @@ import com.tangem.common.routing.AppRouter import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary +import com.arkivanov.decompose.router.slot.SlotNavigation +import com.arkivanov.decompose.router.slot.activate +import com.arkivanov.decompose.router.slot.dismiss import com.tangem.common.ui.bottomsheet.permission.state.ApproveType import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig +import com.tangem.features.approval.api.GiveApprovalComponent +import com.tangem.features.approval.api.GiveApprovalFeatureToggles import com.tangem.common.ui.notifications.NotificationUM import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.api.ParamsInterceptorHolder @@ -22,6 +27,7 @@ import com.tangem.core.decompose.ui.UiMessageSender 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.format.bigdecimal.crypto import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.haptic.TangemHapticEffect @@ -146,12 +152,15 @@ internal class StakingModel @Inject constructor( @DelayedWork private val coroutineScope: CoroutineScope, private val innerRouter: InnerStakingRouter, private val messageSender: UiMessageSender, + private val giveApprovalFeatureToggles: GiveApprovalFeatureToggles, appRouter: AppRouter, ) : Model(), StakingClickIntents { val uiState: StateFlow = stateController.uiState val value: StakingUiState get() = uiState.value + val approvalSlotNavigation = SlotNavigation() + private val params = paramsContainer.require() private val stakingStateRouter: StakingStateRouter = StakingStateRouter( @@ -254,6 +263,31 @@ internal class StakingModel @Inject constructor( analyticsEventHandler = analyticsEventHandler, ) + private val approvalCallback = object : GiveApprovalComponent.Callback { + override fun onApproveClick() { + } + + override fun onApproveDone() { + approvalSlotNavigation.dismiss() + stakingAnalyticSender.sendTransactionApprovalAnalytics( + cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return, + ) + stateController.update(SetApprovalInProgressTransformer) + awaitForAllowance() + } + + override fun onApproveFailed() { + approvalSlotNavigation.dismiss() + stateController.update( + SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus), + ) + } + + override fun onCancelClick() { + approvalSlotNavigation.dismiss() + } + } + private var stakingApproval: StakingApproval = StakingApproval.Empty private var stakingAllowance: BigDecimal = BigDecimal.ZERO private var isAmountSubtractAvailable: Boolean = false @@ -748,16 +782,20 @@ internal class StakingModel @Inject constructor( } override fun showApprovalBottomSheet() { - stateController.update( - ShowApprovalBottomSheetTransformer( - userWallet = userWallet, - appCurrencyProvider = Provider { appCurrency }, - cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, - feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, - ) { - stateController.update(DismissBottomSheetStateTransformer) - }, - ) + if (giveApprovalFeatureToggles.isGaslessApprovalEnabled) { + approvalSlotNavigation.activate(Unit) + } else { + stateController.update( + ShowApprovalBottomSheetTransformer( + userWallet = userWallet, + appCurrencyProvider = Provider { appCurrency }, + cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, + ) { + stateController.update(DismissBottomSheetStateTransformer) + }, + ) + } } override fun onApproveTypeChange(approveType: ApproveType) { @@ -1439,6 +1477,26 @@ internal class StakingModel @Inject constructor( isTonHeatupCase } + fun getApprovalParams(): GiveApprovalComponent.Params? { + val amountState = value.amountState as? AmountState.Data ?: return null + val validatorState = value.validatorState as? StakingStates.ValidatorState.Data ?: return null + val targetAddress = validatorState.chosenTarget.address + val feeCurrencyStatus = feeCryptoCurrencyStatus ?: return null + + return GiveApprovalComponent.Params( + userWalletId = userWallet.walletId, + cryptoCurrencyStatus = cryptoCurrencyStatus, + feeCryptoCurrencyStatus = feeCurrencyStatus, + amount = amountState.amountTextField.value, + spenderAddress = targetAddress, + subtitle = resourceReference( + id = R.string.give_permission_staking_subtitle, + formatArgs = wrappedList(cryptoCurrencyStatus.currency.symbol), + ), + callback = approvalCallback, + ) + } + private companion object { const val WHAT_IS_STAKING_ARTICLE_URL = "https://tangem.com/en/blog/post/how-to-stake-cryptocurrency/" const val ALLOWANCE_UPDATE_DELAY = 10_000L