Updated on 2026-08-14

This commit is contained in:
Tangem 2026-04-07 12:58:49 +04:00
parent 6298b09399
commit 9869f3059d
10 changed files with 20 additions and 187 deletions

View file

@ -49,16 +49,14 @@ class TokenItemStateConverter(
private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = {
CryptoCurrencyToIconStateConverter().convert(it)
},
private val onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)? = null,
private val onYieldPromoCloseClick: (() -> Unit)? = null,
private val onYieldPromoShown: ((cryptoCurrency: CryptoCurrency) -> Unit)? = null,
private val onYieldPromoClicked: ((cryptoCurrency: CryptoCurrency) -> Unit)? = null,
private val onYieldPromoClicked: ((cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) -> Unit)? = null,
private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = { currencyStatus ->
createTitleState(
currencyStatus = currencyStatus,
yieldModuleApyMap = yieldModuleApyMap,
stakingApyMap = stakingApyMap,
onApyLabelClick = onApyLabelClick,
)
},
private val subtitleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.SubtitleState? = {
@ -75,7 +73,6 @@ class TokenItemStateConverter(
status = status,
yieldModuleApyMap = yieldModuleApyMap,
promoCryptoCurrencyStatus = promoCryptoCurrencyStatus,
onApyLabelClick = onApyLabelClick,
onYieldPromoCloseClick = onYieldPromoCloseClick,
onYieldPromoShown = onYieldPromoShown,
onYieldPromoClicked = onYieldPromoClicked,
@ -159,7 +156,6 @@ class TokenItemStateConverter(
currencyStatus: CryptoCurrencyStatus,
yieldModuleApyMap: Map<String, BigDecimal>,
stakingApyMap: Map<CryptoCurrency, StakingAvailability>,
onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)?,
): TokenItemState.TitleState {
return when (val value = currencyStatus.value) {
is CryptoCurrencyStatus.Loading,
@ -184,11 +180,6 @@ class TokenItemStateConverter(
hasPending = value.hasCurrentNetworkTransactions,
earnApy = apyInfo?.text,
earnApyIsActive = apyInfo?.isActive == true,
onApyLabelClick = if (apyInfo?.apy != null && onApyLabelClick != null) {
{ onApyLabelClick.invoke(currencyStatus, apyInfo.source, apyInfo.apy) }
} else {
null
},
)
}
}
@ -217,7 +208,6 @@ class TokenItemStateConverter(
),
isActive = isActive,
apy = yieldSupplyApy.toString(),
source = ApySource.YIELD_SUPPLY,
)
}
}
@ -243,7 +233,6 @@ class TokenItemStateConverter(
),
isActive = stakingInfo.isActive,
apy = apyString,
source = ApySource.STAKING,
)
}
}
@ -388,10 +377,9 @@ class TokenItemStateConverter(
status: CryptoCurrencyStatus,
yieldModuleApyMap: Map<String, BigDecimal>,
promoCryptoCurrencyStatus: CryptoCurrencyStatus?,
onApyLabelClick: ((CryptoCurrencyStatus, ApySource, String) -> Unit)?,
onYieldPromoCloseClick: (() -> Unit)?,
onYieldPromoShown: ((cryptoCurrency: CryptoCurrency) -> Unit)?,
onYieldPromoClicked: ((cryptoCurrency: CryptoCurrency) -> Unit)?,
onYieldPromoClicked: ((cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) -> Unit)?,
): TokenItemState.PromoBannerState {
val token = status.currency as? CryptoCurrency.Token ?: return TokenItemState.PromoBannerState.Empty
if (status.value !is CryptoCurrencyStatus.Loaded) {
@ -413,8 +401,7 @@ class TokenItemStateConverter(
wrappedList(yieldSupplyApy),
),
onPromoBannerClick = {
onYieldPromoClicked?.invoke(status.currency)
onApyLabelClick?.invoke(status, ApySource.YIELD_SUPPLY, yieldSupplyApy.toString())
onYieldPromoClicked?.invoke(status, yieldSupplyApy.toString())
},
onCloseClick = {
onYieldPromoCloseClick?.invoke()
@ -464,11 +451,5 @@ class TokenItemStateConverter(
val text: TextReference?,
val isActive: Boolean,
val apy: String?,
val source: ApySource,
)
enum class ApySource {
STAKING,
YIELD_SUPPLY,
}
}

View file

@ -2,10 +2,8 @@ package com.tangem.core.analytics.models.event
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACTION
import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN
import com.tangem.core.analytics.models.AnalyticsParam.Key.ERROR_CODE
import com.tangem.core.analytics.models.AnalyticsParam.Key.STATE
import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
/**
@ -124,21 +122,6 @@ sealed class MainScreenAnalyticsEvent(
params = mapOf(ERROR_CODE to errorCode),
)
data class ApyClicked(
val token: String,
val blockchain: String,
val action: String,
val state: String,
) : MainScreenAnalyticsEvent(
event = "APY Clicked",
params = mapOf(
TOKEN_PARAM to token,
BLOCKCHAIN to blockchain,
ACTION to action,
STATE to state,
),
)
data class YieldPromo(
val token: String,
val blockchain: String,

View file

@ -3,7 +3,6 @@ package com.tangem.core.ui.components.token.internal
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@ -68,10 +67,7 @@ private fun ContentTitle(state: TokenTitleState.Content, textStyle: TextStyle, m
apy = state.earnApy,
isActive = state.earnApyIsActive,
modifier = Modifier
.align(alignment = Alignment.CenterVertically)
.clickable {
state.onApyLabelClick?.invoke()
},
.align(alignment = Alignment.CenterVertically),
)
}
}

View file

@ -42,9 +42,6 @@ sealed class TokenItemState {
/** Callback which will be called when an item is long clicked */
abstract val onItemLongClick: ((TokenItemState) -> Unit)?
/** Callback which will be called when an apy label is clicked */
abstract val onApyLabelClick: ((TokenItemState) -> Unit)?
/**
* Loading token state
*
@ -64,7 +61,6 @@ sealed class TokenItemState {
override val promoBannerState: PromoBannerState = PromoBannerState.Empty
override val onItemClick: ((TokenItemState) -> Unit)? = null
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
}
/**
@ -81,7 +77,6 @@ sealed class TokenItemState {
override val promoBannerState: PromoBannerState = PromoBannerState.Empty
override val onItemClick: ((TokenItemState) -> Unit)? = null
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
}
/**
@ -106,7 +101,6 @@ sealed class TokenItemState {
override val promoBannerState: PromoBannerState = PromoBannerState.Empty,
override val onItemClick: ((TokenItemState) -> Unit)?,
override val onItemLongClick: ((TokenItemState) -> Unit)?,
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null,
) : TokenItemState()
/**
@ -128,7 +122,6 @@ sealed class TokenItemState {
override val promoBannerState: PromoBannerState = PromoBannerState.Empty
override val onItemClick: ((TokenItemState) -> Unit)? = null
override val onItemLongClick: ((TokenItemState) -> Unit)? = null
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
}
/**
@ -146,7 +139,6 @@ sealed class TokenItemState {
override val iconState: CurrencyIconState,
override val titleState: TitleState,
override val subtitleState: SubtitleState? = null,
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null,
override val onItemClick: ((TokenItemState) -> Unit)?,
override val onItemLongClick: ((TokenItemState) -> Unit)?,
) : TokenItemState() {
@ -174,7 +166,6 @@ sealed class TokenItemState {
override val fiatAmountState: FiatAmountState? = null
override val subtitle2State: Subtitle2State? = null
override val onItemClick: ((TokenItemState) -> Unit)? = null
override val onApyLabelClick: ((TokenItemState) -> Unit)? = null
override val promoBannerState: PromoBannerState = PromoBannerState.Empty
}
@ -187,7 +178,6 @@ sealed class TokenItemState {
val isAvailable: Boolean = true,
val earnApy: TextReference? = null,
val earnApyIsActive: Boolean = false,
val onApyLabelClick: (() -> Unit)? = null,
) : TitleState()
data object Loading : TitleState()

View file

@ -128,7 +128,6 @@ sealed class TangemTokenRowUM : TangemRowUM {
val hasPending: Boolean = false,
val isAvailable: Boolean = true,
val badge: TangemBadgeUM? = null,
val onBadgeClick: (() -> Unit)? = null,
) : TitleUM()
@Serializable

View file

@ -3,7 +3,6 @@ package com.tangem.core.ui.ds.row.token.internal
import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
@ -23,7 +22,6 @@ import com.tangem.core.ui.components.TextShimmer
import com.tangem.core.ui.ds.badge.TangemBadge
import com.tangem.core.ui.ds.placeholder.TextPlaceholder
import com.tangem.core.ui.ds.row.token.TangemTokenRowUM
import com.tangem.core.ui.extensions.conditional
import com.tangem.core.ui.extensions.resolveAnnotatedReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreviewRedesign
@ -82,10 +80,6 @@ private fun ContentTitle(titleUM: TangemTokenRowUM.TitleUM.Content, modifier: Mo
AnimatedVisibility(
visible = titleUM.badge != null,
modifier = Modifier.conditional(
condition = titleUM.onBadgeClick != null,
modifier = { clickable(onClick = requireNotNull(titleUM.onBadgeClick)) },
),
) {
val wrappedBadge = remember(this) { requireNotNull(titleUM.badge) }
TangemBadge(wrappedBadge)

View file

@ -4,7 +4,6 @@ import androidx.compose.ui.geometry.Offset
import arrow.core.getOrElse
import com.tangem.utils.logging.TangemLogger
import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig
import com.tangem.common.ui.tokens.TokenItemStateConverter.ApySource
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.analytics.models.AnalyticsParam
import com.tangem.core.analytics.models.event.MainScreenAnalyticsEvent
@ -16,13 +15,11 @@ import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountId
import com.tangem.domain.models.currency.CryptoCurrency
import com.tangem.domain.models.currency.CryptoCurrencyStatus
import com.tangem.domain.models.staking.StakingBalance
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.nft.analytics.NFTAnalyticsEvent
import com.tangem.domain.settings.ShouldShowMarketsTooltipUseCase
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
import com.tangem.domain.tokens.model.details.NavigationAction
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
import com.tangem.domain.yield.supply.usecase.YieldSupplySetShouldShowMainPromoUseCase
@ -66,13 +63,11 @@ internal interface WalletContentClickIntents {
tokenRowUM: TangemTokenRowUM,
)
fun onApyLabelClick(accountId: AccountId, currencyStatus: CryptoCurrencyStatus, apySource: ApySource, apy: String)
fun onYieldPromoCloseClick()
fun onYieldPromoShown(cryptoCurrency: CryptoCurrency)
fun onYieldPromoClicked(cryptoCurrency: CryptoCurrency)
fun onYieldPromoClicked(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String)
fun onAccountExpandClick(account: Account)
@ -209,36 +204,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
}
override fun onApyLabelClick(
accountId: AccountId,
currencyStatus: CryptoCurrencyStatus,
apySource: ApySource,
apy: String,
) {
val navigationAction = when (apySource) {
ApySource.STAKING -> NavigationAction.Staking
ApySource.YIELD_SUPPLY -> {
NavigationAction.YieldSupply(currencyStatus.value.yieldSupplyStatus?.isActive == true)
}
}
sendApyLabelClickAnalytics(navigationAction, currencyStatus)
when (navigationAction) {
is NavigationAction.Staking -> router.openTokenDetails(
accountId.userWalletId,
currencyStatus,
navigationAction,
)
is NavigationAction.YieldSupply -> openYieldSupply(
userWalletId = accountId.userWalletId,
cryptoCurrencyStatus = currencyStatus,
apy = apy,
)
is NavigationAction.CloreMigration -> Unit
}
}
override fun onYieldPromoCloseClick() {
modelScope.launch {
yieldSupplySetShouldShowMainPromoUseCase(false)
@ -255,15 +220,18 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
}
}
override fun onYieldPromoClicked(cryptoCurrency: CryptoCurrency) {
modelScope.launch(dispatchers.io) {
analyticsEventHandler.send(
MainScreenAnalyticsEvent.YieldPromoClicked(
token = cryptoCurrency.symbol,
blockchain = cryptoCurrency.network.name,
),
)
}
override fun onYieldPromoClicked(accountId: AccountId, cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) {
analyticsEventHandler.send(
MainScreenAnalyticsEvent.YieldPromoClicked(
token = cryptoCurrencyStatus.currency.symbol,
blockchain = cryptoCurrencyStatus.currency.network.name,
),
)
router.openYieldSupplyEntryScreen(
userWalletId = accountId.userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
apy = apy,
)
}
override fun onAccountExpandClick(account: Account) {
@ -287,45 +255,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor(
)
}
private fun openYieldSupply(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus, apy: String) {
router.openYieldSupplyEntryScreen(
userWalletId = userWalletId,
cryptoCurrency = cryptoCurrencyStatus.currency,
apy = apy,
)
}
private fun sendApyLabelClickAnalytics(navigationAction: NavigationAction, currencyStatus: CryptoCurrencyStatus) {
val event = when (navigationAction) {
is NavigationAction.YieldSupply -> {
MainScreenAnalyticsEvent.ApyClicked(
token = currencyStatus.currency.symbol,
blockchain = currencyStatus.currency.network.name,
action = "Earning",
state = if (currencyStatus.value.yieldSupplyStatus?.isActive == true) {
"Enabled"
} else {
"Disabled"
},
)
}
is NavigationAction.Staking -> {
MainScreenAnalyticsEvent.ApyClicked(
token = currencyStatus.currency.symbol,
blockchain = currencyStatus.currency.network.name,
action = "Staking",
state = if (currencyStatus.value.stakingBalance is StakingBalance.Data) {
"Enabled"
} else {
"Disabled"
},
)
}
is NavigationAction.CloreMigration -> return
}
analyticsEventHandler.send(event)
}
override fun onTransactionClick(txHash: String) {
modelScope.launch(dispatchers.main) {
val currency = singleAccountStatusListSupplier.unwrap(

View file

@ -1,7 +1,6 @@
package com.tangem.feature.wallet.presentation.wallet.state.transformers.converter
import com.tangem.common.ui.R
import com.tangem.common.ui.tokens.TokenItemStateConverter
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.wrappedList
@ -15,7 +14,6 @@ import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.staking.model.StakingOption
import com.tangem.domain.staking.model.common.RewardInfo
import com.tangem.domain.staking.model.common.RewardType
import com.tangem.feature.wallet.presentation.wallet.state.transformers.converter.EarnApyConverter.EarnApyInfo
import com.tangem.lib.crypto.BlockchainUtils
import com.tangem.utils.converter.Converter
import java.math.BigDecimal
@ -23,7 +21,7 @@ import java.math.BigDecimal
internal class EarnApyConverter(
val yieldModuleApyMap: Map<String, BigDecimal>,
val stakingApyMap: Map<CryptoCurrency, StakingAvailability>,
) : Converter<CryptoCurrencyStatus, EarnApyInfo?> {
) : Converter<CryptoCurrencyStatus, EarnApyConverter.EarnApyInfo?> {
override fun convert(value: CryptoCurrencyStatus): EarnApyInfo? {
val token = value.currency as? CryptoCurrency.Token
@ -43,7 +41,6 @@ internal class EarnApyConverter(
),
isActive = isActive,
apy = yieldSupplyApy.toString(),
source = TokenItemStateConverter.ApySource.YIELD_SUPPLY,
)
}
}
@ -69,7 +66,6 @@ internal class EarnApyConverter(
),
isActive = stakingInfo.isActive,
apy = apyString,
source = TokenItemStateConverter.ApySource.STAKING,
)
}
}
@ -134,6 +130,5 @@ internal class EarnApyConverter(
val text: TextReference?,
val isActive: Boolean,
val apy: String?,
val source: TokenItemStateConverter.ApySource,
)
}

View file

@ -58,21 +58,6 @@ internal class TokenListStateConverter(
clickIntents.onTokenItemLongClick(accountId, currencyStatus)
}
private val onApyLabelClick: (
currencyStatus: CryptoCurrencyStatus,
accountId: AccountId,
apySource: TokenItemStateConverter.ApySource,
apy: String,
) -> Unit =
{ currencyStatus, accountId, apySource, apy ->
clickIntents.onApyLabelClick(
accountId = accountId,
currencyStatus = currencyStatus,
apySource = apySource,
apy = apy,
)
}
private fun tokenStatusConverter(accountId: AccountId) = TokenItemStateConverter(
appCurrency = appCurrency,
yieldModuleApyMap = yieldModuleApyMap,
@ -80,10 +65,9 @@ internal class TokenListStateConverter(
stakingApyMap = stakingAvailabilityMap,
onItemClick = { _, status -> onTokenClick(accountId, status) },
onItemLongClick = { _, status -> onTokenLongClick(accountId, status) },
onApyLabelClick = { status, apySource, apy -> onApyLabelClick(status, accountId, apySource, apy) },
onYieldPromoCloseClick = clickIntents::onYieldPromoCloseClick,
onYieldPromoShown = clickIntents::onYieldPromoShown,
onYieldPromoClicked = clickIntents::onYieldPromoClicked,
onYieldPromoClicked = { status, apy -> clickIntents.onYieldPromoClicked(accountId, status, apy) },
)
override fun convert(value: WalletTokensListState): WalletTokensListState {

View file

@ -120,18 +120,6 @@ internal class WalletTokenCurrencyItemConverter(
shape = TangemBadgeShape.Rounded,
size = TangemBadgeSize.X4,
text = earnApyInfo.text,
onClick = if (earnApyInfo.apy != null) {
{
clickIntents.onApyLabelClick(
accountId = accountId,
currencyStatus = currencyStatus,
apySource = earnApyInfo.source,
apy = earnApyInfo.apy,
)
}
} else {
null
},
)
} else {
null
@ -288,13 +276,7 @@ internal class WalletTokenCurrencyItemConverter(
iconRes = R.drawable.ic_yield_mode_mini_12,
type = TangemTokenRowUM.PromoBannerUM.Content.Type.Yield,
onPromoBannerClick = {
clickIntents.onYieldPromoClicked(currency)
clickIntents.onApyLabelClick(
accountId = accountId,
currencyStatus = currencyStatus,
apySource = earnApyInfo.source,
apy = earnApyInfo.apy,
)
clickIntents.onYieldPromoClicked(accountId, currencyStatus, earnApyInfo.apy)
},
onCloseClick = clickIntents::onYieldPromoCloseClick,
onPromoShown = {