Updated on 2026-08-14

This commit is contained in:
Tangem 2026-02-06 18:30:13 +03:00
parent 8deed07408
commit 54d476cadd
14 changed files with 78 additions and 15 deletions

View file

@ -0,0 +1,13 @@
package com.tangem.tap.core.ui
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import javax.inject.Inject
class DefaultHoldToConfirmButtonFeatureToggles @Inject constructor(
featureTogglesManager: FeatureTogglesManager,
) : HoldToConfirmButtonFeatureToggles {
override val isHoldToConfirmEnabled: Boolean = featureTogglesManager.isFeatureEnabled(
"HOLD_TO_CONFIRM_BUTTON_ENABLED",
)
}

View file

@ -1,7 +1,9 @@
package com.tangem.tap.di.core.ui
import com.tangem.core.ui.DesignFeatureToggles
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.tap.core.ui.DefaultDesignFeatureToggles
import com.tangem.tap.core.ui.DefaultHoldToConfirmButtonFeatureToggles
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
@ -13,4 +15,9 @@ interface CoreUiBindsModule {
@Binds
fun bindDesignFeatureToggles(impl: DefaultDesignFeatureToggles): DesignFeatureToggles
@Binds
fun bindHoldToConfirmButtonFeatureToggles(
impl: DefaultHoldToConfirmButtonFeatureToggles,
): HoldToConfirmButtonFeatureToggles
}

View file

@ -82,5 +82,9 @@
{
"name": "EARN_BLOCK_ENABLED",
"version": "undefined"
},
{
"name": "HOLD_TO_CONFIRM_BUTTON_ENABLED",
"version": "undefined"
}
]

View file

@ -0,0 +1,5 @@
package com.tangem.core.ui
interface HoldToConfirmButtonFeatureToggles {
val isHoldToConfirmEnabled: Boolean
}

View file

@ -19,6 +19,7 @@ import com.tangem.core.decompose.model.ParamsContainer
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.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
@ -118,6 +119,7 @@ internal class SendConfirmModel @Inject constructor(
private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase,
private val currenciesRepository: CurrenciesRepository,
private val createAndSendGaslessTransactionUseCase: CreateAndSendGaslessTransactionUseCase,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
) : Model(), SendConfirmClickIntents, FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
@ -597,7 +599,8 @@ internal class SendConfirmModel @Inject constructor(
val confirmUM = uiState.value.confirmUM
val isContent = confirmUM is ConfirmUM.Content
val isReadyToSend = isContent && !confirmUM.isSending
val isHoldToConfirm = userWallet.isHotWallet && isContent
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
userWallet.isHotWallet && isContent
return NavigationButton(
textReference = getPrimaryButtonText(confirmUM, isHoldToConfirm),
iconRes = walletInterationIcon(userWallet),

View file

@ -18,6 +18,7 @@ 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.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
@ -96,6 +97,7 @@ internal class SendWithSwapConfirmModel @Inject constructor(
private val swapAlertFactory: SwapAlertFactory,
private val appRouter: AppRouter,
private val analyticsEventHandler: AnalyticsEventHandler,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
swapTransactionSenderFactory: SwapTransactionSender.Factory,
paramsContainer: ParamsContainer,
) : Model(), FeeSelectorModelCallback, SendNotificationsComponent.ModelCallback {
@ -502,7 +504,8 @@ internal class SendWithSwapConfirmModel @Inject constructor(
val confirmUM = state.confirmUM
val isContent = confirmUM is ConfirmUM.Content
val isReadyToSend = isContent && !confirmUM.isTransactionInProcess
val isHoldToConfirm = params.userWallet.isHotWallet && isContent
val isHoldToConfirm = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
params.userWallet.isHotWallet && isContent
params.callback.onResult(
route = SendWithSwapRoute.Confirm,
sendWithSwapUM = state.copy(

View file

@ -23,6 +23,7 @@ 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.navigation.url.UrlOpener
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.utils.InputNumberFormatter
@ -160,6 +161,7 @@ internal class SwapModel @Inject constructor(
private val getUserWalletsUseCase: GetWalletsUseCase,
private val getTangemPayCustomerIdUseCase: GetTangemPayCustomerIdUseCase,
private val appsFlyerStore: AppsFlyerStore,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Model() {
private val params = paramsContainer.require<SwapComponent.Params>()
@ -192,6 +194,7 @@ internal class SwapModel @Inject constructor(
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
isAccountsModeProvider = Provider { isAccountsMode },
iGaslessFeeSupportedForNetwork = iGaslessFeeSupportedForNetwork,
holdToConfirmButtonFeatureToggles = holdToConfirmButtonFeatureToggles,
)
private val inputNumberFormatter =

View file

@ -24,6 +24,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.domain.promo.models.StoryContent
@ -60,7 +61,7 @@ import kotlin.math.min
/**
* State builder creates a specific states for SwapScreen
*/
@Suppress("LargeClass", "TooManyFunctions")
@Suppress("LargeClass", "TooManyFunctions", "LongParameterList")
internal class StateBuilder(
private val userWalletProvider: Provider<UserWallet>,
private val actions: UiActions,
@ -68,8 +69,12 @@ internal class StateBuilder(
private val appCurrencyProvider: Provider<AppCurrency>,
private val isAccountsModeProvider: Provider<Boolean>,
private val iGaslessFeeSupportedForNetwork: IsGaslessFeeSupportedForNetwork,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) {
private val isHoldToConfirmEnabled: Boolean =
holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled && userWalletProvider().isHotWallet
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
private val tokensDataConverter = TokensDataConverter(
@ -127,7 +132,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
isHoldToConfirm = userWalletProvider().isHotWallet,
isHoldToConfirm = isHoldToConfirmEnabled,
onClick = {},
),
onRefresh = {},
@ -187,7 +192,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
isHoldToConfirm = userWalletProvider().isHotWallet,
isHoldToConfirm = isHoldToConfirmEnabled,
onClick = { },
),
changeCardsButtonState = ChangeCardsButtonState.DISABLED,
@ -253,7 +258,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
isHoldToConfirm = userWalletProvider().isHotWallet,
isHoldToConfirm = isHoldToConfirmEnabled,
onClick = {},
),
providerState = ProviderState.Loading(),
@ -376,7 +381,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = getSwapButtonEnabled(notifications),
isHoldToConfirm = userWalletProvider().isHotWallet,
isHoldToConfirm = isHoldToConfirmEnabled,
onClick = actions.onSwapClick,
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
@ -505,7 +510,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
isHoldToConfirm = userWalletProvider().isHotWallet,
isHoldToConfirm = isHoldToConfirmEnabled,
onClick = actions.onSwapClick,
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),
@ -603,7 +608,7 @@ internal class StateBuilder(
swapButton = SwapButton(
walletInteractionIcon = walletInterationIcon(userWalletProvider()),
isEnabled = false,
isHoldToConfirm = userWalletProvider().isHotWallet,
isHoldToConfirm = isHoldToConfirmEnabled,
onClick = { },
),
changeCardsButtonState = getChangeCardsButtonState(isReverseSwapPossible),

View file

@ -16,6 +16,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionItemUM
import com.tangem.features.walletconnect.transaction.entity.send.WcSendTransactionUM
import com.tangem.features.walletconnect.utils.WcNotificationsFactory
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
@ -26,6 +27,7 @@ internal class WcSendTransactionUMConverter @Inject constructor(
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
private val notificationsFactory: WcNotificationsFactory,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Converter<WcSendTransactionUMConverter.Input, WcSendTransactionUM?> {
override fun convert(value: Input): WcSendTransactionUM? {
@ -63,7 +65,8 @@ internal class WcSendTransactionUMConverter @Inject constructor(
}
},
feeErrorNotification = feeErrorNotification,
isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
value.context.session.wallet.isHotWallet,
),
feeSelectorUM = when (value.feeState) {
WcTransactionFeeState.None -> FeeSelectorUM.Loading

View file

@ -10,6 +10,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
@ -19,6 +20,7 @@ internal class WcSignTransactionUMConverter @Inject constructor(
private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter,
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Converter<WcSignTransactionUMConverter.Input, WcSignTransactionUM> {
override fun convert(value: Input) = WcSignTransactionUM(
@ -36,7 +38,8 @@ internal class WcSignTransactionUMConverter @Inject constructor(
isLoading = value.signState.domainStep == WcSignStep.Signing,
address = WcAddressConverter.convert(value.context.derivationState),
walletInteractionIcon = walletInterationIcon(value.context.session.wallet),
isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
value.context.session.wallet.isHotWallet,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
requestBlockUMConverter.convert(

View file

@ -10,6 +10,7 @@ import com.tangem.features.walletconnect.transaction.entity.common.WcTransaction
import com.tangem.features.walletconnect.transaction.entity.common.WcTransactionRequestInfoUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionItemUM
import com.tangem.features.walletconnect.transaction.entity.sign.WcSignTransactionUM
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.domain.models.wallet.isHotWallet
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.toImmutableList
@ -19,6 +20,7 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
private val appInfoContentUMConverter: WcTransactionAppInfoContentUMConverter,
private val networkInfoUMConverter: WcNetworkInfoUMConverter,
private val requestBlockUMConverter: WcTransactionRequestBlockUMConverter,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Converter<WcSignTypedDataUMConverter.Input, WcSignTransactionUM> {
override fun convert(value: Input): WcSignTransactionUM = WcSignTransactionUM(
@ -36,7 +38,8 @@ internal class WcSignTypedDataUMConverter @Inject constructor(
address = WcAddressConverter.convert(value.context.derivationState),
isLoading = value.signState.domainStep == WcSignStep.Signing,
walletInteractionIcon = walletInterationIcon(value.context.session.wallet),
isHoldToConfirmEnabled = value.context.session.wallet.isHotWallet,
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
value.context.session.wallet.isHotWallet,
),
transactionRequestInfo = WcTransactionRequestInfoUM(
blocks = buildList {

View file

@ -10,6 +10,7 @@ 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.navigation.url.UrlOpener
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.fiat
@ -62,6 +63,7 @@ internal class YieldSupplyApproveModel @Inject constructor(
private val yieldSupplyGetContractAddressUseCase: YieldSupplyGetContractAddressUseCase,
private val yieldSupplyPendingTracker: YieldSupplyPendingTracker,
private val yieldSupplyAlertFactory: YieldSupplyAlertFactory,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
private val params: YieldSupplyApproveComponent.Params = paramsContainer.require()
@ -98,7 +100,8 @@ internal class YieldSupplyApproveModel @Inject constructor(
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
isPrimaryButtonEnabled = false,
isTransactionSending = false,
isHoldToConfirmEnabled = params.userWallet.isHotWallet,
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
params.userWallet.isHotWallet,
),
)

View file

@ -8,6 +8,7 @@ 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
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
@ -69,6 +70,7 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
private val yieldSupplyRepository: YieldSupplyRepository,
private val yieldSupplyPendingTracker: YieldSupplyPendingTracker,
private val appsFlyerStore: AppsFlyerStore,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
private val params: YieldSupplyStartEarningComponent.Params = paramsContainer.require()
@ -313,7 +315,10 @@ internal class YieldSupplyStartEarningModel @Inject constructor(
ifRight = { wallet ->
userWallet = wallet
uiState.update {
it.copy(isHoldToConfirmEnabled = wallet.isHotWallet)
it.copy(
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
wallet.isHotWallet,
)
}
getCurrenciesStatusUpdates()
},

View file

@ -8,6 +8,7 @@ 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.navigation.url.UrlOpener
import com.tangem.core.ui.HoldToConfirmButtonFeatureToggles
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
@ -66,6 +67,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
private val yieldSupplyRepository: YieldSupplyRepository,
private val yieldSupplyPendingTracker: YieldSupplyPendingTracker,
private val appsFlyerStore: AppsFlyerStore,
private val holdToConfirmButtonFeatureToggles: HoldToConfirmButtonFeatureToggles,
) : Model(), YieldSupplyNotificationsComponent.ModelCallback {
private val params: YieldSupplyStopEarningComponent.Params = paramsContainer.require()
@ -102,7 +104,8 @@ internal class YieldSupplyStopEarningModel @Inject constructor(
yieldSupplyFeeUM = YieldSupplyFeeUM.Loading,
isPrimaryButtonEnabled = false,
isTransactionSending = false,
isHoldToConfirmEnabled = params.userWallet.isHotWallet,
isHoldToConfirmEnabled = holdToConfirmButtonFeatureToggles.isHoldToConfirmEnabled &&
params.userWallet.isHotWallet,
),
)