From 448a472832f5534e7162aa4a98113d427d849e8b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 3 Dec 2025 13:42:03 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/YieldSupplyDomainModule.kt | 10 +++ .../ui/tokens/TokenItemStateConverter.kt | 65 +++++++++++------ .../usecase/YieldSupplyEnterStatusUseCase.kt | 24 +++++++ .../intents/WalletContentClickIntents.kt | 70 +++++++++++++++---- .../router/DefaultWalletRouter.kt | 20 ++++++ .../presentation/router/InnerWalletRouter.kt | 6 ++ .../converter/TokenListStateConverter.kt | 9 +-- 7 files changed, 164 insertions(+), 40 deletions(-) create mode 100644 domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEnterStatusUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt index e5d58f7331..a151bfa469 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/YieldSupplyDomainModule.kt @@ -189,4 +189,14 @@ internal object YieldSupplyDomainModule { dispatcherProvider = dispatcherProvider, ) } + + @Provides + @Singleton + fun provideYieldSupplyEnterStatusUseCase( + yieldSupplyRepository: YieldSupplyRepository, + ): YieldSupplyEnterStatusUseCase { + return YieldSupplyEnterStatusUseCase( + yieldSupplyRepository = yieldSupplyRepository, + ) + } } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt index 2d3c6e516a..9817a2f88a 100644 --- a/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/tokens/TokenItemStateConverter.kt @@ -48,11 +48,14 @@ class TokenItemStateConverter( private val iconStateProvider: (CryptoCurrencyStatus) -> CurrencyIconState = { CryptoCurrencyToIconStateConverter().convert(it) }, - private val onApyLabelClick: ((CryptoCurrencyStatus) -> Unit)? = null, - private val titleStateProvider: (CryptoCurrencyStatus) -> TokenItemState.TitleState = { - createTitleState(it, yieldModuleApyMap, stakingApyMap, { - onApyLabelClick?.invoke(it) - }) + private val onApyLabelClick: ((CryptoCurrencyStatus, 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? = { createSubtitleState(it, appCurrency) @@ -105,9 +108,6 @@ class TokenItemStateConverter( onItemLongClick = onItemLongClick?.let { onItemLongClick -> { onItemLongClick(it, this) } }, - onApyLabelClick = onApyLabelClick?.let { onApyLabelClick -> - { onApyLabelClick(this) } - }, ) } @@ -166,7 +166,7 @@ class TokenItemStateConverter( currencyStatus: CryptoCurrencyStatus, yieldModuleApyMap: Map, stakingApyMap: Map>, - onApyLabelClick: () -> Unit, + onApyLabelClick: ((CryptoCurrencyStatus, String) -> Unit)?, ): TokenItemState.TitleState { return when (val value = currencyStatus.value) { is CryptoCurrencyStatus.Loading, @@ -181,7 +181,7 @@ class TokenItemStateConverter( is CryptoCurrencyStatus.NoQuote, is CryptoCurrencyStatus.NoAccount, -> { - val (earnApyText, isActive) = resolveEarnApy( + val apyInfo = resolveEarnApy( cryptoCurrencyStatus = currencyStatus, yieldModuleApyMap = yieldModuleApyMap, stakingApyMap = stakingApyMap, @@ -189,9 +189,13 @@ class TokenItemStateConverter( TokenItemState.TitleState.Content( text = stringReference(currencyStatus.currency.name), hasPending = value.hasCurrentNetworkTransactions, - earnApy = earnApyText, - earnApyIsActive = isActive, - onApyLabelClick = onApyLabelClick, + earnApy = apyInfo?.text, + earnApyIsActive = apyInfo?.isActive == true, + onApyLabelClick = if (apyInfo?.apy != null && onApyLabelClick != null) { + { onApyLabelClick.invoke(currencyStatus, apyInfo.apy) } + } else { + null + }, ) } } @@ -202,7 +206,7 @@ class TokenItemStateConverter( cryptoCurrencyStatus: CryptoCurrencyStatus, yieldModuleApyMap: Map, stakingApyMap: Map>, - ): Pair { + ): EarnApyInfo? { val token = cryptoCurrencyStatus.currency as? CryptoCurrency.Token if (token != null && yieldModuleApyMap.isNotEmpty()) { val yieldSupplyApy = yieldModuleApyMap.entries.firstOrNull { @@ -213,10 +217,14 @@ class TokenItemStateConverter( }?.value if (yieldSupplyApy != null) { val isActive = cryptoCurrencyStatus.value.yieldSupplyStatus?.isActive ?: false - return resourceReference( - R.string.yield_module_earn_badge, - wrappedList(yieldSupplyApy), - ) to isActive + return EarnApyInfo( + text = resourceReference( + R.string.yield_module_earn_badge, + wrappedList(yieldSupplyApy), + ), + isActive = isActive, + apy = yieldSupplyApy, + ) } } @@ -233,14 +241,19 @@ class TokenItemStateConverter( -> R.string.yield_module_earn_badge } if (stakingInfo.rate != null) { - return resourceReference( - rewardTypeRes, - wrappedList(stakingInfo.rate.format { percent(withPercentSign = false) }), - ) to stakingInfo.isActive + val apyString = stakingInfo.rate.format { percent(withPercentSign = false) } + return EarnApyInfo( + text = resourceReference( + rewardTypeRes, + wrappedList(apyString), + ), + isActive = stakingInfo.isActive, + apy = apyString, + ) } } - return null to false + return null } private fun findStakingRate( @@ -412,4 +425,10 @@ class TokenItemStateConverter( val isActive: Boolean, val rewardType: Yield.RewardType?, ) + + private data class EarnApyInfo( + val text: TextReference?, + val isActive: Boolean, + val apy: String?, + ) } \ No newline at end of file diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEnterStatusUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEnterStatusUseCase.kt new file mode 100644 index 0000000000..35de1341bb --- /dev/null +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyEnterStatusUseCase.kt @@ -0,0 +1,24 @@ +package com.tangem.domain.yield.supply.usecase + +import arrow.core.Either +import com.tangem.domain.models.currency.CryptoCurrencyStatus +import com.tangem.domain.models.wallet.UserWalletId +import com.tangem.domain.yield.supply.YieldSupplyRepository +import com.tangem.domain.yield.supply.models.YieldSupplyEnterStatus + +class YieldSupplyEnterStatusUseCase( + private val yieldSupplyRepository: YieldSupplyRepository, +) { + + operator fun invoke( + userWalletId: UserWalletId, + cryptoCurrencyStatus: CryptoCurrencyStatus, + ): Either { + return Either.catch { + yieldSupplyRepository.getTokenProtocolStatus( + userWalletId, + cryptoCurrencyStatus.currency, + ) + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt index 198caf6fd9..8c1278e5b6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/child/wallet/model/intents/WalletContentClickIntents.kt @@ -20,6 +20,7 @@ import com.tangem.domain.tokens.model.TokenActionsState 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.YieldSupplyEnterStatusUseCase import com.tangem.feature.wallet.presentation.account.AccountDependencies import com.tangem.feature.wallet.presentation.wallet.domain.OnrampStatusFactory import com.tangem.feature.wallet.presentation.wallet.domain.unwrap @@ -49,7 +50,7 @@ internal interface WalletContentClickIntents { fun onTokenItemLongClick(userWalletId: UserWalletId, cryptoCurrencyStatus: CryptoCurrencyStatus) - fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) + fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus, apy: String) fun onAccountExpandClick(account: Account) @@ -87,6 +88,7 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( private val analyticsEventHandler: AnalyticsEventHandler, private val hotWalletFeatureToggles: HotWalletFeatureToggles, private val accountDependencies: AccountDependencies, + private val yieldSupplyEnterStatusUseCase: YieldSupplyEnterStatusUseCase, ) : BaseWalletClickIntents(), WalletContentClickIntents { override fun onDetailsClick() { @@ -160,12 +162,65 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( } } - override fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus) { + override fun onApyLabelClick(userWalletId: UserWalletId, currencyStatus: CryptoCurrencyStatus, apy: String) { val navigationAction = if (currencyStatus.currency is CryptoCurrency.Token) { NavigationAction.YieldSupply(currencyStatus.value.yieldSupplyStatus?.isActive == true) } else { NavigationAction.Staking } + + sendApyLabelClickAnalytics(navigationAction, currencyStatus) + + when (navigationAction) { + is NavigationAction.Staking -> router.openTokenDetails(userWalletId, currencyStatus, navigationAction) + is NavigationAction.YieldSupply -> openYieldSupply( + userWalletId = userWalletId, + cryptoCurrencyStatus = currencyStatus, + navigationAction = navigationAction, + apy = apy, + ) + } + } + + override fun onAccountExpandClick(account: Account) { + val userWalletId = stateHolder.getSelectedWalletId() + accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId) + } + + override fun onAccountCollapseClick(account: Account) { + val userWalletId = stateHolder.getSelectedWalletId() + accountDependencies.expandedAccountsHolder.collapseAccount(userWalletId, account.accountId) + } + + private fun openYieldSupply( + userWalletId: UserWalletId, + cryptoCurrencyStatus: CryptoCurrencyStatus, + navigationAction: NavigationAction.YieldSupply, + apy: String, + ) { + modelScope.launch { + val tokenEnterStatus = yieldSupplyEnterStatusUseCase(userWalletId, cryptoCurrencyStatus).getOrNull() + when { + tokenEnterStatus != null -> router.openTokenDetails( + userWalletId = userWalletId, + currencyStatus = cryptoCurrencyStatus, + navigationAction = navigationAction, + ) + navigationAction.isActive -> router.openYieldSupplyActiveScreen( + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrencyStatus.currency, + apy = apy, + ) + else -> router.openYieldSupplyPromoScreen( + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrencyStatus.currency, + apy = apy, + ) + } + } + } + + private fun sendApyLabelClickAnalytics(navigationAction: NavigationAction, currencyStatus: CryptoCurrencyStatus) { val event = when (navigationAction) { is NavigationAction.YieldSupply -> { MainScreenAnalyticsEvent.ApyClicked( @@ -193,17 +248,6 @@ internal class WalletContentClickIntentsImplementor @Inject constructor( } } analyticsEventHandler.send(event) - router.openTokenDetails(userWalletId, currencyStatus, navigationAction) - } - - override fun onAccountExpandClick(account: Account) { - val userWalletId = stateHolder.getSelectedWalletId() - accountDependencies.expandedAccountsHolder.expandAccount(userWalletId, account.accountId) - } - - override fun onAccountCollapseClick(account: Account) { - val userWalletId = stateHolder.getSelectedWalletId() - accountDependencies.expandedAccountsHolder.collapseAccount(userWalletId, account.accountId) } private fun showActionsBottomSheet(tokenActionsState: TokenActionsState, userWallet: UserWallet) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 6de08ad367..5d2fbf72f2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -132,4 +132,24 @@ internal class DefaultWalletRouter @Inject constructor( ), ) } + + override fun openYieldSupplyActiveScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) { + router.push( + AppRoute.YieldSupplyActive( + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrency, + apy = apy, + ), + ) + } + + override fun openYieldSupplyPromoScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) { + router.push( + AppRoute.YieldSupplyPromo( + userWalletId = userWalletId, + cryptoCurrency = cryptoCurrency, + apy = apy, + ), + ) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 9f8d9d42fc..a71880095a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -73,4 +73,10 @@ internal interface InnerWalletRouter { tokenAction: TokenAction, onWarningAcknowledged: (TokenAction) -> Unit, ) + + /** Open yield supply active screen */ + fun openYieldSupplyActiveScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) + + /** Open yield supply promo screen */ + fun openYieldSupplyPromoScreen(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency, apy: String) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt index f88e515f82..f9eefeccd6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/transformers/converter/TokenListStateConverter.kt @@ -49,9 +49,10 @@ internal class TokenListStateConverter( clickIntents.onTokenItemLongClick(selectedWallet.walletId, currencyStatus) } - private val onApyLabelClick: (currencyStatus: CryptoCurrencyStatus) -> Unit = { currencyStatus -> - clickIntents.onApyLabelClick(selectedWallet.walletId, currencyStatus) - } + private val onApyLabelClick: (currencyStatus: CryptoCurrencyStatus, apy: String) -> Unit = + { currencyStatus, apy -> + clickIntents.onApyLabelClick(selectedWallet.walletId, currencyStatus, apy) + } private fun tokenStatusConverter(accountId: AccountId? = null) = TokenItemStateConverter( appCurrency = appCurrency, @@ -59,7 +60,7 @@ internal class TokenListStateConverter( stakingApyMap = stakingApyMap, onItemClick = { _, status -> onTokenClick(accountId, status) }, onItemLongClick = { _, status -> onTokenLongClick(accountId, status) }, - onApyLabelClick = { status -> onApyLabelClick(status) }, + onApyLabelClick = { status, apy -> onApyLabelClick(status, apy) }, ) override fun convert(value: WalletTokensListState): WalletTokensListState {