Updated on 2026-08-14
This commit is contained in:
parent
1166f7e8bf
commit
84cf236d5f
20 changed files with 258 additions and 10 deletions
|
|
@ -16,6 +16,9 @@ import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
|||
import com.tangem.domain.wallets.repository.WalletNamesMigrationRepository
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.IsWalletNFTEnabledSyncUseCase
|
||||
import com.tangem.feature.wallet.presentation.wallet.domain.WalletNameMigrationUseCase
|
||||
import com.tangem.features.hotwallet.HotWalletFeatureToggles
|
||||
|
|
@ -448,4 +451,24 @@ internal object WalletsDomainModule {
|
|||
userWalletUseCase = getUserWalletUseCase,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideYieldSupplyApyFlowUseCase(
|
||||
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
|
||||
): YieldSupplyApyFlowUseCase {
|
||||
return YieldSupplyApyFlowUseCase(
|
||||
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
|
||||
)
|
||||
}
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideYieldSupplyApyUpdateUseCase(
|
||||
yieldSupplyMarketRepository: YieldSupplyMarketRepository,
|
||||
): YieldSupplyApyUpdateUseCase {
|
||||
return YieldSupplyApyUpdateUseCase(
|
||||
yieldSupplyMarketRepository = yieldSupplyMarketRepository,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -218,6 +218,13 @@ class TokenItemStateConverter(
|
|||
text = status.getFormattedFiatAmount(appCurrency = appCurrency),
|
||||
isFlickering = status.value.isFlickering(),
|
||||
icons = buildList {
|
||||
if (status.value.yieldSupplyStatus?.isActive == true &&
|
||||
status.value.yieldSupplyStatus?.isAllowedToSpend == false) {
|
||||
TokenItemState.FiatAmountState.Content.IconUM(
|
||||
iconRes = R.drawable.ic_alert_triangle_20,
|
||||
tint = IconTint.Warning,
|
||||
).let(::add)
|
||||
}
|
||||
if (!status.getStakedBalance().isZero()) {
|
||||
add(
|
||||
TokenItemState.FiatAmountState.Content.IconUM(
|
||||
|
|
|
|||
|
|
@ -8,9 +8,9 @@ import retrofit2.http.Path
|
|||
|
||||
interface YieldSupplyApi {
|
||||
|
||||
@GET("v1/yield/markets")
|
||||
@GET("api/v1/yield/markets")
|
||||
suspend fun getYieldMarkets(): ApiResponse<YieldMarketsResponse>
|
||||
|
||||
@GET("v1/yield/token/{tokenAddress}")
|
||||
@GET("api/v1/yield/token/{tokenAddress}")
|
||||
suspend fun getYieldTokenStatus(@Path("tokenAddress") tokenAddress: String): ApiResponse<YieldTokenStatusResponse>
|
||||
}
|
||||
|
|
@ -53,6 +53,20 @@ internal object ApiConfigsModule {
|
|||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideYieldSupplyConfig(
|
||||
environmentConfigStorage: EnvironmentConfigStorage,
|
||||
appVersionProvider: AppVersionProvider,
|
||||
authProvider: AuthProvider,
|
||||
appInfoProvider: AppInfoProvider,
|
||||
): ApiConfig = YieldSupply(
|
||||
environmentConfigStorage = environmentConfigStorage,
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = authProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@IntoSet
|
||||
fun provideTangemVisaConfig(appVersionProvider: AppVersionProvider): ApiConfig = TangemPay(appVersionProvider)
|
||||
|
|
|
|||
|
|
@ -37,6 +37,14 @@ class ApiConfigTest {
|
|||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.YieldSupply -> {
|
||||
YieldSupply(
|
||||
environmentConfigStorage = mockk(),
|
||||
appVersionProvider = mockk(),
|
||||
authProvider = mockk(),
|
||||
appInfoProvider = mockk(),
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.TangemTech -> {
|
||||
TangemTech(
|
||||
environmentConfigStorage = mockk(),
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ internal class MockEnvironmentConfigStorage : EnvironmentConfigStorage {
|
|||
express = ExpressModel(apiKey = EXPRESS_API_KEY, signVerifierPublicKey = "vocibus"),
|
||||
devExpress = ExpressModel(apiKey = EXPRESS_DEV_API_KEY, signVerifierPublicKey = "pellentesque"),
|
||||
blockAidApiKey = BLOCK_AID_API_KEY,
|
||||
tangemApiKey = TANGEM_API_KEY,
|
||||
tangemApiKeyDev = TANGEM_API_KEY_DEV,
|
||||
tangemApiKeyStage = TANGEM_API_KEY_STAGE,
|
||||
)
|
||||
|
||||
override suspend fun initialize() = environmentConfig
|
||||
|
|
@ -26,5 +29,8 @@ internal class MockEnvironmentConfigStorage : EnvironmentConfigStorage {
|
|||
const val EXPRESS_API_KEY = "express_api_key"
|
||||
const val EXPRESS_DEV_API_KEY = "express_dev_api_key"
|
||||
const val BLOCK_AID_API_KEY = "block_aid_api_key"
|
||||
const val TANGEM_API_KEY = "tangem_api_key"
|
||||
const val TANGEM_API_KEY_DEV = "tangem_api_key_dev"
|
||||
const val TANGEM_API_KEY_STAGE = "tangem_api_key_stage"
|
||||
}
|
||||
}
|
||||
|
|
@ -84,6 +84,14 @@ internal class ProdApiConfigsManagerTest {
|
|||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.YieldSupply -> {
|
||||
YieldSupply(
|
||||
environmentConfigStorage = environmentConfigStorage,
|
||||
appVersionProvider = appVersionProvider,
|
||||
authProvider = appAuthProvider,
|
||||
appInfoProvider = appInfoProvider,
|
||||
)
|
||||
}
|
||||
ApiConfig.ID.TangemTech -> {
|
||||
TangemTech(
|
||||
environmentConfigStorage = environmentConfigStorage,
|
||||
|
|
@ -102,6 +110,7 @@ internal class ProdApiConfigsManagerTest {
|
|||
private fun provideTestModels() = ApiConfig.ID.entries.map {
|
||||
when (it) {
|
||||
ApiConfig.ID.Express -> createExpressModel()
|
||||
ApiConfig.ID.YieldSupply -> createYieldSupplyModel()
|
||||
ApiConfig.ID.TangemTech -> createTangemTechModel()
|
||||
ApiConfig.ID.StakeKit -> createStakeKitModel()
|
||||
ApiConfig.ID.TangemPay -> createTangemPayModel()
|
||||
|
|
@ -166,6 +175,30 @@ internal class ProdApiConfigsManagerTest {
|
|||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://api.tangem.org/",
|
||||
headers = mapOf(
|
||||
"api-key" to ProviderSuspend { MockEnvironmentConfigStorage.TANGEM_API_KEY },
|
||||
"card_id" to ProviderSuspend { APP_CARD_ID },
|
||||
"card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
"platform" to ProviderSuspend { "android" },
|
||||
"system_version" to ProviderSuspend { "Android 16" },
|
||||
"language" to ProviderSuspend { Locale.getDefault().language.checkHeaderValueOrEmpty() },
|
||||
"timezone" to ProviderSuspend {
|
||||
TimeZone.getDefault().getDisplayName(false, TimeZone.SHORT).checkHeaderValueOrEmpty()
|
||||
},
|
||||
"device" to ProviderSuspend { "${Build.MANUFACTURER} ${Build.MODEL}".checkHeaderValueOrEmpty() },
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createYieldSupplyModel(): TestModel {
|
||||
return TestModel(
|
||||
id = ApiConfig.ID.YieldSupply,
|
||||
expected = ApiEnvironmentConfig(
|
||||
environment = ApiEnvironment.PROD,
|
||||
baseUrl = "https://yield.tangem.org/",
|
||||
headers = mapOf(
|
||||
"api-key" to ProviderSuspend { MockEnvironmentConfigStorage.TANGEM_API_KEY },
|
||||
"card_id" to ProviderSuspend { APP_CARD_ID },
|
||||
"card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY },
|
||||
"version" to ProviderSuspend { VERSION_NAME },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.yield.supply.usecase
|
||||
|
||||
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
/**
|
||||
* Emits a map of APY values per token.
|
||||
*
|
||||
* Return map:
|
||||
* - key: token contract address
|
||||
* - value: APY as string
|
||||
*/
|
||||
class YieldSupplyApyFlowUseCase(
|
||||
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
|
||||
) {
|
||||
|
||||
operator fun invoke(): Flow<Map<String, String>> {
|
||||
return yieldSupplyMarketRepository.getMarketsFlow()
|
||||
.map { yieldMarketTokenList ->
|
||||
yieldMarketTokenList.filter { it.isActive }.associate {
|
||||
it.tokenAddress to it.apy.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package com.tangem.domain.yield.supply.usecase
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.yield.supply.YieldSupplyMarketRepository
|
||||
import kotlin.collections.filter
|
||||
|
||||
/**
|
||||
* Updates and returns a map of APY values per token.
|
||||
*
|
||||
* Return map:
|
||||
* - key: token contract address
|
||||
* - value: APY as string
|
||||
*/
|
||||
class YieldSupplyApyUpdateUseCase(
|
||||
private val yieldSupplyMarketRepository: YieldSupplyMarketRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(): Either<Throwable, Map<String, String>> = Either.catch {
|
||||
yieldSupplyMarketRepository.updateMarkets()
|
||||
.filter { it.isActive }
|
||||
.associate {
|
||||
it.tokenAddress to it.apy.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -98,6 +98,8 @@ dependencies {
|
|||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.notifications)
|
||||
implementation(projects.domain.transaction)
|
||||
implementation(projects.domain.yieldSupply)
|
||||
implementation(projects.domain.yieldSupply.models)
|
||||
|
||||
/** Feature Apis */
|
||||
implementation(projects.features.details.api)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import com.tangem.domain.pay.usecase.TangemPayMainScreenCustomerInfoUseCase
|
|||
import com.tangem.domain.settings.*
|
||||
import com.tangem.domain.tokens.RefreshMultiCurrencyWalletQuotesUseCase
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyUpdateUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
|
|
@ -43,6 +44,7 @@ import com.tangem.features.pushnotifications.api.PushNotificationsModelCallbacks
|
|||
import com.tangem.features.pushnotifications.api.utils.PUSH_PERMISSION
|
||||
import com.tangem.features.tangempay.TangemPayFeatureToggles
|
||||
import com.tangem.features.wallet.deeplink.WalletDeepLinkActionListener
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyFeatureToggles
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.*
|
||||
import kotlinx.coroutines.*
|
||||
|
|
@ -92,6 +94,8 @@ internal class WalletModel @Inject constructor(
|
|||
private val tangemPayMainScreenCustomerInfoUseCase: TangemPayMainScreenCustomerInfoUseCase,
|
||||
private val tangemPayIssueOrderUseCase: TangemPayIssueOrderUseCase,
|
||||
private val tangemPayFeatureToggles: TangemPayFeatureToggles,
|
||||
private val yieldSupplyApyUpdateUseCase: YieldSupplyApyUpdateUseCase,
|
||||
private val yieldSupplyFeatureToggles: YieldSupplyFeatureToggles,
|
||||
val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
val innerWalletRouter: InnerWalletRouter,
|
||||
) : Model() {
|
||||
|
|
@ -116,6 +120,7 @@ internal class WalletModel @Inject constructor(
|
|||
|
||||
maybeMigrateNames()
|
||||
maybeSetWalletFirstTimeUsage()
|
||||
updateYieldSupplyApy()
|
||||
subscribeToUserWalletsUpdates()
|
||||
subscribeOnBalanceHiding()
|
||||
subscribeOnSelectedWalletFlow()
|
||||
|
|
@ -127,6 +132,14 @@ internal class WalletModel @Inject constructor(
|
|||
clickIntents.initialize(innerWalletRouter, modelScope)
|
||||
}
|
||||
|
||||
private fun updateYieldSupplyApy() {
|
||||
if (yieldSupplyFeatureToggles.isYieldSupplyFeatureEnabled) {
|
||||
modelScope.launch(dispatchers.default) {
|
||||
yieldSupplyApyUpdateUseCase()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun maybeMigrateNames() {
|
||||
modelScope.launch {
|
||||
walletNameMigrationUseCase()
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -42,6 +43,7 @@ internal class MultiWalletContentLoader(
|
|||
private val walletsRepository: WalletsRepository,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
|
|
@ -57,6 +59,7 @@ internal class MultiWalletContentLoader(
|
|||
applyTokenListSortingUseCase = applyTokenListSortingUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
).let(::add)
|
||||
|
||||
WalletNFTListSubscriber(
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.repository.WalletsRepository
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -40,6 +41,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase,
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet, clickIntents: WalletClickIntents): WalletContentLoader {
|
||||
|
|
@ -62,6 +64,7 @@ internal class MultiWalletContentLoaderFactory @Inject constructor(
|
|||
getNFTCollectionsUseCase = getNFTCollectionsUseCase,
|
||||
currenciesRepository = currenciesRepository,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -5,6 +5,7 @@ import com.tangem.domain.promo.GetStoryContentUseCase
|
|||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -32,6 +33,7 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
) : WalletContentLoader(id = userWallet.walletId) {
|
||||
|
||||
override fun create(): List<WalletSubscriber> {
|
||||
|
|
@ -46,6 +48,7 @@ internal class SingleWalletWithTokenContentLoader(
|
|||
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
|
||||
runPolkadotAccountHealthCheckUseCase = runPolkadotAccountHealthCheckUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
).let(::add)
|
||||
MultiWalletWarningsSubscriber(
|
||||
userWallet = userWallet,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.promo.GetStoryContentUseCase
|
|||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.WalletWarningsAnalyticsSender
|
||||
|
|
@ -33,6 +34,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
private val shouldSaveUserWalletsUseCase: ShouldSaveUserWalletsUseCase,
|
||||
private val getStoryContentUseCase: GetStoryContentUseCase,
|
||||
private val accountDependencies: AccountDependencies,
|
||||
private val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
) {
|
||||
|
||||
fun create(userWallet: UserWallet.Cold, clickIntents: WalletClickIntents): SingleWalletWithTokenContentLoader {
|
||||
|
|
@ -51,6 +53,7 @@ internal class SingleWalletWithTokenContentLoaderFactory @Inject constructor(
|
|||
getStoryContentUseCase = getStoryContentUseCase,
|
||||
shouldSaveUserWalletsUseCase = shouldSaveUserWalletsUseCase,
|
||||
accountDependencies = accountDependencies,
|
||||
yieldSupplyApyFlowUseCase = yieldSupplyApyFlowUseCase,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -16,6 +16,7 @@ internal class SetTokenListTransformer(
|
|||
private val userWallet: UserWallet,
|
||||
private val appCurrency: AppCurrency,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val yieldSupplyApyMap: Map<String, String> = emptyMap(),
|
||||
) : WalletStateTransformer(userWallet.walletId) {
|
||||
|
||||
override fun transform(prevState: WalletState): WalletState {
|
||||
|
|
@ -58,6 +59,7 @@ internal class SetTokenListTransformer(
|
|||
selectedWallet = userWallet,
|
||||
appCurrency = appCurrency,
|
||||
clickIntents = clickIntents,
|
||||
apyMap = yieldSupplyApyMap,
|
||||
).convert(value = this)
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
|
|||
|
||||
import com.tangem.common.ui.account.AccountCryptoPortfolioItemStateConverter
|
||||
import com.tangem.common.ui.tokens.TokenItemStateConverter
|
||||
import com.tangem.core.ui.components.token.state.TokenItemState
|
||||
import com.tangem.core.ui.components.tokenlist.state.PortfolioTokensListItemUM
|
||||
import com.tangem.core.ui.components.tokenlist.state.TokensListItemUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -13,6 +14,7 @@ import com.tangem.domain.models.TotalFiatBalance
|
|||
import com.tangem.domain.models.account.Account
|
||||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountStatus
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.currency.CryptoCurrencyStatus
|
||||
import com.tangem.domain.models.tokenlist.TokenList
|
||||
import com.tangem.domain.models.tokenlist.TokenList.GroupedByNetwork.NetworkGroup
|
||||
|
|
@ -34,6 +36,7 @@ internal class TokenListStateConverter(
|
|||
private val params: TokenConverterParams,
|
||||
private val selectedWallet: UserWallet,
|
||||
private val clickIntents: WalletClickIntents,
|
||||
private val apyMap: Map<String, String> = emptyMap(),
|
||||
) : Converter<WalletTokensListState, WalletTokensListState> {
|
||||
|
||||
private val onTokenClick: (accountId: AccountId?, currencyStatus: CryptoCurrencyStatus) -> Unit =
|
||||
|
|
@ -165,12 +168,54 @@ internal class TokenListStateConverter(
|
|||
tokenConverter: TokenItemStateConverter,
|
||||
token: CryptoCurrencyStatus,
|
||||
): List<TokensListItemUM> {
|
||||
val tokenItemState = tokenConverter.convert(token)
|
||||
val tokenItemState = tokenConverter.convert(token).withEarnApyBadge(token)
|
||||
|
||||
add(TokensListItemUM.Token(tokenItemState))
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun TokenItemState.withEarnApyBadge(cryptoCurrencyStatus: CryptoCurrencyStatus): TokenItemState {
|
||||
val apy: String? = resolveEarnApy(cryptoCurrencyStatus)
|
||||
|
||||
val shouldApply = apy != null && this.titleState is TokenItemState.TitleState.Content
|
||||
|
||||
return if (!shouldApply) {
|
||||
this
|
||||
} else {
|
||||
val contentTitle = this.titleState as TokenItemState.TitleState.Content
|
||||
val newTitle = contentTitle.copy(
|
||||
earnApy = resourceReference(
|
||||
R.string.yield_module_earn_badge,
|
||||
wrappedList(apy),
|
||||
),
|
||||
)
|
||||
|
||||
when (this) {
|
||||
is TokenItemState.Content -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Unreachable -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.NoAddress -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Loading -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Draggable -> this.copy(titleState = newTitle)
|
||||
is TokenItemState.Locked -> this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveEarnApy(cryptoCurrencyStatus: CryptoCurrencyStatus): String? {
|
||||
if (apyMap.isEmpty()) return null
|
||||
|
||||
val isYieldSupplyActive = (cryptoCurrencyStatus.value as? CryptoCurrencyStatus.Loaded)
|
||||
?.yieldSupplyStatus?.isActive == true
|
||||
if (isYieldSupplyActive) return null
|
||||
|
||||
val contract = (cryptoCurrencyStatus.currency as? CryptoCurrency.Token)
|
||||
?.contractAddress
|
||||
?.lowercase() ?: return null
|
||||
|
||||
return apyMap[contract]
|
||||
}
|
||||
|
||||
private fun getOrganizeTokensButtonState(tokenList: TokenList): WalletOrganizeTokensButtonConfig? {
|
||||
val currenciesSize = when (tokenList) {
|
||||
TokenList.Empty -> return null
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.models.tokenlist.TokenList
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -39,6 +40,7 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
protected abstract val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase
|
||||
protected abstract val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase
|
||||
protected abstract val accountDependencies: AccountDependencies
|
||||
protected abstract val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase
|
||||
|
||||
private val sendAnalyticsJobHolder = JobHolder()
|
||||
private val onTokenListReceivedJobHolder = JobHolder()
|
||||
|
|
@ -73,8 +75,14 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
coroutineScope.launch { startCheck(maybeTokenList) }
|
||||
},
|
||||
flow2 = appCurrencyFlow(),
|
||||
transform = { maybeTokenList, appCurrency ->
|
||||
singleAccountTransform(maybeTokenList, appCurrency, PortfolioId(userWallet.walletId))
|
||||
flow3 = yieldSupplyApyFlow(),
|
||||
transform = { maybeTokenList, appCurrency, yieldSupplyApyMap ->
|
||||
singleAccountTransform(
|
||||
maybeTokenList = maybeTokenList,
|
||||
appCurrency = appCurrency,
|
||||
portfolioId = PortfolioId(userWallet.walletId),
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -83,6 +91,7 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
maybeTokenList: Lce<TokenListError, TokenList>,
|
||||
appCurrency: AppCurrency,
|
||||
portfolioId: PortfolioId,
|
||||
yieldSupplyApyMap: Map<String, String>,
|
||||
) {
|
||||
val tokenList = maybeTokenList.getOrElse(
|
||||
ifLoading = { maybeContent ->
|
||||
|
|
@ -106,7 +115,12 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
return
|
||||
},
|
||||
)
|
||||
updateContent(TokenConverterParams.Wallet(portfolioId, tokenList), appCurrency)
|
||||
updateContent(
|
||||
params = TokenConverterParams.Wallet(portfolioId, tokenList),
|
||||
appCurrency = appCurrency,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
)
|
||||
|
||||
walletWithFundsChecker.check(tokenList)
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +157,8 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
flow2 = appCurrencyFlow(),
|
||||
flow3 = accountDependencies.expandedAccountsHolder.expandedAccounts(userWallet),
|
||||
flow4 = accountDependencies.isAccountsModeEnabledUseCase(),
|
||||
transform = { accountList, appCurrency, expandedAccounts, isAccountMode ->
|
||||
flow5 = yieldSupplyApyFlow(),
|
||||
transform = { accountList, appCurrency, expandedAccounts, isAccountMode, yieldSupplyApyMap ->
|
||||
val accountFlattenTokensList = accountList.flattenTokens()
|
||||
val accountFlattenCurrencies = accountFlattenTokensList
|
||||
.map { it.flattenCurrencies() }
|
||||
|
|
@ -153,7 +168,12 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
}
|
||||
|
||||
suspend fun singleAccountTransform(maybeTokenList: Lce<TokenListError, TokenList>) =
|
||||
this.singleAccountTransform(maybeTokenList, appCurrency, PortfolioId(mainAccount.account.accountId))
|
||||
this.singleAccountTransform(
|
||||
maybeTokenList,
|
||||
appCurrency,
|
||||
PortfolioId(mainAccount.account.accountId),
|
||||
yieldSupplyApyMap,
|
||||
)
|
||||
|
||||
when {
|
||||
!isAccountMode -> when (mainAccount.tokenList.flattenCurrencies().isEmpty()) {
|
||||
|
|
@ -171,7 +191,7 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
)
|
||||
false -> {
|
||||
val convertParams = TokenConverterParams.Account(accountList, expandedAccounts)
|
||||
updateContent(convertParams, appCurrency)
|
||||
updateContent(convertParams, appCurrency, yieldSupplyApyMap)
|
||||
accountFlattenTokensList
|
||||
.map { tokenList -> coroutineScope.launch { walletWithFundsChecker.check(tokenList) } }
|
||||
.joinAll()
|
||||
|
|
@ -197,13 +217,18 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
)
|
||||
}
|
||||
|
||||
private fun updateContent(params: TokenConverterParams, appCurrency: AppCurrency) {
|
||||
private fun updateContent(
|
||||
params: TokenConverterParams,
|
||||
appCurrency: AppCurrency,
|
||||
yieldSupplyApyMap: Map<String, String>,
|
||||
) {
|
||||
stateHolder.update(
|
||||
SetTokenListTransformer(
|
||||
params = params,
|
||||
userWallet = userWallet,
|
||||
appCurrency = appCurrency,
|
||||
clickIntents = clickIntents,
|
||||
yieldSupplyApyMap = yieldSupplyApyMap,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -216,4 +241,7 @@ internal abstract class BasicTokenListSubscriber : WalletSubscriber() {
|
|||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
|
||||
private fun yieldSupplyApyFlow(): Flow<Map<String, String>> = yieldSupplyApyFlowUseCase()
|
||||
.distinctUntilChanged()
|
||||
}
|
||||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.models.wallet.UserWallet
|
|||
import com.tangem.domain.tokens.ApplyTokenListSortingUseCase
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -34,6 +35,7 @@ internal class MultiWalletTokenListSubscriber(
|
|||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
) : BasicTokenListSubscriber() {
|
||||
|
||||
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import com.tangem.domain.models.tokenlist.TokenList
|
|||
import com.tangem.domain.models.wallet.UserWallet
|
||||
import com.tangem.domain.tokens.RunPolkadotAccountHealthCheckUseCase
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.yield.supply.usecase.YieldSupplyApyFlowUseCase
|
||||
import com.tangem.feature.wallet.child.wallet.model.intents.WalletClickIntents
|
||||
import com.tangem.feature.wallet.presentation.account.AccountDependencies
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.TokenListAnalyticsSender
|
||||
|
|
@ -29,6 +30,7 @@ internal class SingleWalletWithTokenListSubscriber(
|
|||
override val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
override val runPolkadotAccountHealthCheckUseCase: RunPolkadotAccountHealthCheckUseCase,
|
||||
override val accountDependencies: AccountDependencies,
|
||||
override val yieldSupplyApyFlowUseCase: YieldSupplyApyFlowUseCase,
|
||||
) : BasicTokenListSubscriber() {
|
||||
|
||||
override fun tokenListFlow(coroutineScope: CoroutineScope): LceFlow<TokenListError, TokenList> {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue