Updated on 2026-08-14

This commit is contained in:
Tangem 2025-04-04 12:03:02 +03:00
commit 5223d4b417
784 changed files with 24026 additions and 3170 deletions

View file

@ -16,6 +16,8 @@ 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.share.ShareManager
import com.tangem.core.ui.format.bigdecimal.crypto
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.haptic.TangemHapticEffect
import com.tangem.core.ui.haptic.VibratorHapticManager
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -34,6 +36,7 @@ import com.tangem.domain.staking.model.stakekit.*
import com.tangem.domain.staking.model.stakekit.action.StakingAction
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
import com.tangem.domain.staking.model.stakekit.transaction.StakingTransaction
import com.tangem.domain.staking.utils.getValidatorsCount
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
@ -114,6 +117,7 @@ internal class StakingModel @Inject constructor(
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
private val getActionsUseCase: GetActionsUseCase,
private val getYieldUseCase: GetYieldUseCase,
private val checkAccountInitializedUseCase: CheckAccountInitializedUseCase,
private val paramsInterceptorHolder: ParamsInterceptorHolder,
private val shareManager: ShareManager,
@DelayedWork private val coroutineScope: CoroutineScope,
@ -229,49 +233,62 @@ internal class StakingModel @Inject constructor(
}
override fun onNextClick(balanceState: BalanceState?) {
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
val noBalanceState = balanceState == null
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
modelScope.launch {
val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo
val noBalanceState = balanceState == null
val noYieldBalanceData = cryptoCurrencyStatus.value.yieldBalance !is YieldBalance.Data
val isAccountInitialized = checkAccountInitializedUseCase.invoke(
userWalletId = userWalletId,
network = cryptoCurrencyStatus.currency.network,
).getOrElse {
Timber.e(it)
false
}
when {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
stakingEventFactory.createStakingValidatorsUnavailableAlert()
return
}
isInitialInfoStep && noBalanceState -> {
val list = buildList {
SetConfirmationStateInitTransformer(
isEnter = true,
isExplicitExit = false,
balanceState = null,
cryptoCurrencyStatus = cryptoCurrencyStatus,
stakingApproval = stakingApproval,
stakingAllowance = stakingAllowance,
yieldArgs = yield.args,
).let(::add)
if (yield.args.enter.isPartialAmountDisabled) {
ValidatorSelectChangeTransformer(
selectedValidator = yield.preferredValidators.firstOrNull(),
yield = yield,
).let(::add)
SetAmountDataTransformer(
clickIntents = this@StakingModel,
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
userWalletProvider = Provider { userWallet },
appCurrencyProvider = Provider { appCurrency },
).let(::add)
AmountMaxValueStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
minimumTransactionAmount = minimumTransactionAmount,
actionType = uiState.value.actionType,
yield = yield,
).let(::add)
}
when {
isInitialInfoStep && noBalanceState && yield.allValidatorsFull && noYieldBalanceData -> {
stakingEventFactory.createStakingValidatorsUnavailableAlert()
return@launch
}
isInitialInfoStep && noBalanceState && !isAccountInitialized -> {
stakingEventFactory.createInitializeAccountAlert()
return@launch
}
isInitialInfoStep && noBalanceState -> {
val list = buildList {
SetConfirmationStateInitTransformer(
isEnter = true,
isExplicitExit = false,
balanceState = null,
cryptoCurrencyStatus = cryptoCurrencyStatus,
stakingApproval = stakingApproval,
stakingAllowance = stakingAllowance,
yieldArgs = yield.args,
).let(::add)
if (yield.args.enter.isPartialAmountDisabled) {
ValidatorSelectChangeTransformer(
selectedValidator = yield.preferredValidators.firstOrNull(),
yield = yield,
).let(::add)
SetAmountDataTransformer(
clickIntents = this@StakingModel,
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
userWalletProvider = Provider { userWallet },
appCurrencyProvider = Provider { appCurrency },
).let(::add)
AmountMaxValueStateTransformer(
cryptoCurrencyStatus = cryptoCurrencyStatus,
minimumTransactionAmount = minimumTransactionAmount,
actionType = uiState.value.actionType,
yield = yield,
).let(::add)
}
}
stateController.updateAll(*list.toTypedArray())
}
stateController.updateAll(*list.toTypedArray())
}
stakingStateRouter.onNextClick()
}
stakingStateRouter.onNextClick()
}
override fun getFee() {
@ -497,19 +514,34 @@ internal class StakingModel @Inject constructor(
analyticsEventHandler.send(StakingAnalyticsEvent.ButtonRewards)
val rewardsValidators =
stateController.value.rewardsValidatorsState as? StakingStates.RewardsValidatorsState.Data
val rewards = rewardsValidators?.rewards
if (rewards != null && rewards.isSingleItem()) {
onActiveStake(rewards.first())
} else {
analyticsEventHandler.send(
StakingAnalyticsEvent.ButtonValidator(
source = StakeScreenSource.Info,
),
val initialInfoState = stateController.value.initialInfoState as? StakingStates.InitialInfoState.Data
val yieldBalance = initialInfoState?.yieldBalance as? InnerYieldBalanceState.Data
val rewardBlockType = yieldBalance?.reward?.rewardBlockType
val rewardPendingActionConstraints = yieldBalance?.reward?.rewardConstraints
if (rewardBlockType == RewardBlockType.RewardsRequirementsError) {
stakingEventFactory.createStakingRewardsMinimumRequirementsErrorAlert(
cryptoCurrencyName = cryptoCurrencyStatus.currency.name,
cryptoAmountValue = rewardPendingActionConstraints?.amountArg?.minimum?.format {
crypto(cryptoCurrencyStatus.currency)
}.orEmpty(),
)
stateController.update {
value.copy(actionType = StakingActionCommonType.Pending.Rewards)
} else {
val rewards = rewardsValidators?.rewards
if (rewards != null && rewards.isSingleItem()) {
onActiveStake(rewards.first())
} else {
analyticsEventHandler.send(
StakingAnalyticsEvent.ButtonValidator(
source = StakeScreenSource.Info,
),
)
stateController.update {
value.copy(actionType = StakingActionCommonType.Pending.Rewards)
}
stakingStateRouter.showRewardsValidators()
}
stakingStateRouter.showRewardsValidators()
}
}

View file

@ -2,18 +2,14 @@ package com.tangem.features.staking.impl.presentation.state
import androidx.compose.runtime.Immutable
import com.tangem.core.ui.extensions.TextReference
import com.tangem.domain.staking.model.stakekit.BalanceType
import com.tangem.domain.staking.model.stakekit.PendingAction
import com.tangem.domain.staking.model.stakekit.RewardBlockType
import com.tangem.domain.staking.model.stakekit.Yield
import com.tangem.domain.staking.model.stakekit.*
import kotlinx.collections.immutable.ImmutableList
import java.math.BigDecimal
@Immutable
internal sealed class InnerYieldBalanceState {
data class Data(
val rewardsCrypto: String,
val rewardsFiat: String,
val rewardBlockType: RewardBlockType,
val reward: YieldReward,
val isActionable: Boolean,
val balances: ImmutableList<BalanceState>,
) : InnerYieldBalanceState()
@ -37,4 +33,12 @@ internal data class BalanceState(
val validator: Yield.Validator?,
val pendingActions: ImmutableList<PendingAction>,
val isPending: Boolean,
)
@Immutable
internal data class YieldReward(
val rewardsCrypto: String,
val rewardsFiat: String,
val rewardBlockType: RewardBlockType,
val rewardConstraints: PendingActionConstraints?,
)

View file

@ -6,8 +6,11 @@ import com.tangem.core.ui.format.bigdecimal.fiat
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.staking.model.stakekit.*
import com.tangem.domain.staking.model.stakekit.action.StakingActionType
import com.tangem.domain.staking.utils.getRewardStakingBalance
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.features.staking.impl.presentation.state.YieldReward
import com.tangem.lib.crypto.BlockchainUtils.isBSC
import com.tangem.lib.crypto.BlockchainUtils.isSolana
import com.tangem.utils.Provider
@ -29,11 +32,11 @@ internal class YieldBalancesConverter(
val appCurrency = appCurrencyProvider()
val cryptoCurrency = cryptoCurrencyStatus.currency
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val balanceToShowItems = balancesToShowProvider()
return if (yieldBalance is YieldBalance.Data || balanceToShowItems.any { it.isPending }) {
val cryptoRewardsValue = (yieldBalance as? YieldBalance.Data)?.getRewardStakingBalance()
return if (yieldBalance != null || balanceToShowItems.any { it.isPending }) {
val cryptoRewardsValue = yieldBalance?.getRewardStakingBalance()
val fiatRate = cryptoCurrencyStatus.value.fiatRate
val fiatRewardsValue = if (fiatRate != null && cryptoRewardsValue != null) {
@ -41,17 +44,25 @@ internal class YieldBalancesConverter(
} else {
null
}
val (type, isActionable) = getRewardBlockType()
val type = getRewardBlockType()
val pendingRewardsConstraints = yieldBalance?.balance?.items
?.firstOrNull { it.type == BalanceType.REWARDS }
?.pendingActionsConstraints
?.firstOrNull { it.type == StakingActionType.CLAIM_REWARDS }
InnerYieldBalanceState.Data(
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
rewardsFiat = fiatRewardsValue.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
},
rewardBlockType = type,
isActionable = isActionable,
reward = YieldReward(
rewardsCrypto = cryptoRewardsValue.format { crypto(cryptoCurrency) },
rewardsFiat = fiatRewardsValue.format {
fiat(
fiatCurrencyCode = appCurrency.code,
fiatCurrencySymbol = appCurrency.symbol,
)
},
rewardBlockType = type,
rewardConstraints = pendingRewardsConstraints,
),
isActionable = type.isActionable,
balances = balanceToShowItems.mapBalances(),
)
} else {
@ -66,7 +77,7 @@ internal class YieldBalancesConverter(
.sortedBy { it.type.order }
.toPersistentList()
private fun getRewardBlockType(): Pair<RewardBlockType, Boolean> {
private fun getRewardBlockType(): RewardBlockType {
val blockchainId = cryptoCurrencyStatus.currency.network.id.value
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
val rewards = yieldBalance?.balance?.items
@ -76,9 +87,10 @@ internal class YieldBalancesConverter(
val isRewardsClaimable = rewards?.isNotEmpty() == true
return when {
isSolana(blockchainId) || isBSC(blockchainId) -> RewardBlockType.RewardUnavailable to false
isRewardsClaimable -> RewardBlockType.Rewards to isActionable
else -> RewardBlockType.NoRewards to false
isSolana(blockchainId) || isBSC(blockchainId) -> RewardBlockType.RewardUnavailable
isRewardsClaimable && isActionable -> RewardBlockType.Rewards
isRewardsClaimable && !isActionable -> RewardBlockType.RewardsRequirementsError
else -> RewardBlockType.NoRewards
}
}
}

View file

@ -61,4 +61,24 @@ internal sealed class StakingAlertUM : AlertUM {
)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
data object InitializeAccount : StakingAlertUM() {
override val onConfirmClick: (() -> Unit)? = null
override val title: TextReference = resourceReference(id = R.string.staking_error_no_validators_title)
override val message: TextReference = resourceReference(id = R.string.staking_notification_ton_activate_account)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
data class RewardsMinimumRequirementsError(
val cryptoCurrencyName: String,
val cryptoAmountValue: String,
) : StakingAlertUM() {
override val onConfirmClick: (() -> Unit)? = null
override val title: TextReference? = null
override val message: TextReference = resourceReference(
id = R.string.staking_details_min_rewards_notification,
formatArgs = wrappedList(cryptoCurrencyName, cryptoAmountValue),
)
override val confirmButtonText: TextReference = resourceReference(id = R.string.common_ok)
}
}

View file

@ -46,4 +46,20 @@ internal class StakingEventFactory(
val alert = StakingEvent.ShowAlert(alert = StakingAlertUM.ValidatorsUnavailable)
stateController.updateEvent(alert)
}
fun createInitializeAccountAlert() {
val alert = StakingEvent.ShowAlert(alert = StakingAlertUM.InitializeAccount)
stateController.updateEvent(alert)
}
fun createStakingRewardsMinimumRequirementsErrorAlert(cryptoCurrencyName: String, cryptoAmountValue: String) {
stateController.updateEvent(
StakingEvent.ShowAlert(
alert = StakingAlertUM.RewardsMinimumRequirementsError(
cryptoCurrencyName = cryptoCurrencyName,
cryptoAmountValue = cryptoAmountValue,
),
),
)
}
}

View file

@ -1,9 +1,9 @@
package com.tangem.features.staking.impl.presentation.state.previewdata
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.core.ui.components.list.RoundedListWithDividersItemData
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfig
import com.tangem.domain.staking.model.stakekit.BalanceType
import com.tangem.domain.staking.model.stakekit.RewardBlockType
import com.tangem.domain.staking.model.stakekit.Yield
@ -11,6 +11,7 @@ import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.YieldReward
import kotlinx.collections.immutable.persistentListOf
internal object InitialStakingStatePreview {
@ -63,9 +64,12 @@ internal object InitialStakingStatePreview {
val stateWithYield = defaultState.copy(
yieldBalance = InnerYieldBalanceState.Data(
rewardsFiat = "100 $",
rewardsCrypto = "100 SOL",
rewardBlockType = RewardBlockType.RewardUnavailable,
reward = YieldReward(
rewardsFiat = "100 $",
rewardsCrypto = "100 SOL",
rewardBlockType = RewardBlockType.RewardUnavailable,
rewardConstraints = null,
),
isActionable = true,
balances = persistentListOf(
BalanceState(

View file

@ -43,28 +43,7 @@ internal class SetConfirmationStateInitTransformer(
@Suppress("CyclomaticComplexMethod")
override fun transform(prevState: StakingUiState): StakingUiState {
val actionType = when {
isEnter -> StakingActionCommonType.Enter(
skipEnterAmount = yieldArgs.enter.isPartialAmountDisabled,
)
isImplicitExit || isExplicitExit -> StakingActionCommonType.Exit(isPartiallyUnstakeDisabled(prevState))
else -> when (pendingAction?.type) {
StakingActionType.STAKE -> StakingActionCommonType.Pending.Stake(
skipEnterAmount = yieldArgs.enter.isPartialAmountDisabled,
)
StakingActionType.UNSTAKE -> StakingActionCommonType.Exit(
partiallyUnstakeDisabled = isPartiallyUnstakeDisabled(prevState),
)
StakingActionType.CLAIM_REWARDS,
StakingActionType.RESTAKE_REWARDS,
-> StakingActionCommonType.Pending.Rewards
StakingActionType.VOTE_LOCKED,
StakingActionType.RESTAKE,
-> StakingActionCommonType.Pending.Restake
else -> StakingActionCommonType.Pending.Other
}
}
val actionType = getActionType(prevState)
return prevState.copy(
actionType = actionType,
balanceState = balanceState,
@ -86,6 +65,26 @@ internal class SetConfirmationStateInitTransformer(
)
}
private fun getActionType(prevState: StakingUiState): StakingActionCommonType {
val isPartialEnterAmountDisabled = yieldArgs.enter.isPartialAmountDisabled
val isPartialExitAmountDisabled = isPartiallyUnstakeDisabled(prevState)
return when {
isEnter -> StakingActionCommonType.Enter(isPartialEnterAmountDisabled)
isImplicitExit || isExplicitExit -> StakingActionCommonType.Exit(isPartialExitAmountDisabled)
else -> when (pendingAction?.type) {
StakingActionType.STAKE -> StakingActionCommonType.Pending.Stake(isPartialEnterAmountDisabled)
StakingActionType.UNSTAKE -> StakingActionCommonType.Exit(isPartialExitAmountDisabled)
StakingActionType.CLAIM_REWARDS,
StakingActionType.RESTAKE_REWARDS,
-> StakingActionCommonType.Pending.Rewards
StakingActionType.VOTE_LOCKED,
StakingActionType.RESTAKE,
-> StakingActionCommonType.Pending.Restake
else -> StakingActionCommonType.Pending.Other
}
}
}
private fun isPartiallyUnstakeDisabled(state: StakingUiState): Boolean {
val isSolana = BlockchainUtils.isSolana(state.cryptoCurrencyBlockchainId)
val isValidatorPreferred = balanceState?.validator?.preferred == true

View file

@ -8,7 +8,7 @@ internal class AmountPasteDismissStateTransformer : Transformer<StakingUiState>
override fun transform(prevState: StakingUiState): StakingUiState {
return prevState.copy(
amountState = AmountPastedTriggerDismissTransformer().transform(prevState.amountState),
amountState = AmountPastedTriggerDismissTransformer.transform(prevState.amountState),
)
}
}

View file

@ -98,9 +98,7 @@ internal class AddStakingNotificationsTransformer(
onReload = prevState.clickIntents::getFee,
feeValue = feeValue,
)
addStakingErrorNotifications(
stakingError = stakingError,
)
addStakingErrorNotifications(stakingError = stakingError, onReload = prevState.clickIntents::getFee)
// warnings
addWarningNotifications(
prevState = prevState,
@ -136,7 +134,10 @@ internal class AddStakingNotificationsTransformer(
)
}
private fun MutableList<NotificationUM>.addStakingErrorNotifications(stakingError: StakingError?) {
private fun MutableList<NotificationUM>.addStakingErrorNotifications(
stakingError: StakingError?,
onReload: () -> Unit,
) {
when (stakingError) {
is StakingError.StakeKitApiError -> {
if (stakingError.message != StakingErrors.MinimumAmountNotReachedError.message) {
@ -146,9 +147,7 @@ internal class AddStakingNotificationsTransformer(
}
}
null -> Unit
else -> add(
StakingNotification.Error.Common(subtitle = stringReference(stakingError.toString())),
)
else -> add(NotificationUM.Warning.NetworkFeeUnreachable(onReload))
}
}

View file

@ -38,7 +38,7 @@ internal class ValidatorSelectChangeTransformer(
chosenValidator = selectedValidator ?: yield.preferredValidators.first(),
availableValidators = filteredValidators,
isPrimaryButtonEnabled = true,
isClickable = true,
isClickable = yield.preferredValidators.size > 1,
activeValidator = activeValidator,
isVisibleOnConfirmation = isEnter || isRestake || isVoteLocked,
),

View file

@ -31,6 +31,7 @@ import androidx.compose.ui.unit.Density
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
import com.tangem.core.ui.components.SpacerH12
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
import com.tangem.core.ui.components.inputrow.InputRowDefault
import com.tangem.core.ui.components.inputrow.InputRowImageInfo
import com.tangem.core.ui.components.list.roundedListWithDividersItems
@ -38,19 +39,18 @@ import com.tangem.core.ui.decorations.roundedShapeItemDecoration
import com.tangem.core.ui.extensions.*
import com.tangem.core.ui.format.bigdecimal.format
import com.tangem.core.ui.format.bigdecimal.percent
import com.tangem.core.ui.components.containers.pullToRefresh.TangemPullToRefreshContainer
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.staking.model.stakekit.BalanceType
import com.tangem.domain.staking.model.stakekit.RewardBlockType
import com.tangem.features.staking.impl.R
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
import com.tangem.features.staking.impl.presentation.state.BalanceState
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
import com.tangem.features.staking.impl.presentation.state.StakingStates
import com.tangem.features.staking.impl.presentation.state.previewdata.InitialStakingStatePreview
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
import com.tangem.features.staking.impl.presentation.model.StakingClickIntents
import com.tangem.utils.StringsSigns.DOT
import com.tangem.utils.extensions.orZero
@ -214,14 +214,17 @@ private fun StakingRewardBlock(
onRewardsClick: () -> Unit,
isBalanceHidden: Boolean,
) {
val (text, textColor) = when (yieldBalanceState.rewardBlockType) {
RewardBlockType.Rewards -> {
val reward = yieldBalanceState.reward
val (text, textColor) = when (yieldBalanceState.reward.rewardBlockType) {
RewardBlockType.RewardsRequirementsError,
RewardBlockType.Rewards,
-> {
annotatedReference {
append(yieldBalanceState.rewardsFiat.orMaskWithStars(isBalanceHidden))
append(reward.rewardsFiat.orMaskWithStars(isBalanceHidden))
appendSpace()
append(DOT)
appendSpace()
append(yieldBalanceState.rewardsCrypto.orMaskWithStars(isBalanceHidden))
append(reward.rewardsCrypto.orMaskWithStars(isBalanceHidden))
} to TangemTheme.colors.text.primary1
}
RewardBlockType.RewardUnavailable -> {
@ -232,7 +235,7 @@ private fun StakingRewardBlock(
resourceReference(R.string.staking_details_no_rewards_to_claim) to TangemTheme.colors.text.tertiary
}
}
val isShowIcon = yieldBalanceState.rewardBlockType == RewardBlockType.Rewards && yieldBalanceState.isActionable
val isShowIcon = reward.rewardBlockType == RewardBlockType.Rewards && yieldBalanceState.isActionable
InputRowDefault(
title = resourceReference(R.string.staking_rewards),
text = text,

View file

@ -10,9 +10,9 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetTitle
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetTitle
import com.tangem.core.ui.components.inputrow.InputRowDefault
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme

View file

@ -9,9 +9,9 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetTitle
import com.tangem.core.ui.components.bottomsheets.sheet.TangemBottomSheetTitle
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme