From 4c8c06416f4ea4014a6457bb7ccff7645ed0de47 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 6 Oct 2025 14:30:09 +0500 Subject: [PATCH] Updated on 2026-08-14 --- .../tap/di/domain/YieldSupplyDomainModule.kt | 20 ++ .../api/common/config/YieldSupply.kt | 31 +-- .../managers/ProdApiConfigsManagerTest.kt | 3 +- core/res/src/main/res/values-ru/strings.xml | 4 + core/res/src/main/res/values/strings.xml | 3 + .../tangem/core/ui/components/label/Label.kt | 9 +- ...TangemLinks.kt => TangemBlogUrlBuilder.kt} | 2 +- .../usecase/YieldSupplyGetApyUseCase.kt | 14 ++ .../usecase/YieldSupplyGetChartUseCase.kt | 16 ++ .../v2/feeselector/model/FeeSelectorModel.kt | 4 +- .../impl/presentation/model/StakingModel.kt | 2 +- .../tangem/feature/swap/model/SwapModel.kt | 2 +- .../impl/apy/YieldSupplyApyComponent.kt | 61 +++++ .../impl/apy/ui/YieldSupplyApyContent.kt | 195 ++++++++++++++++ .../chart/DefaultYieldSupplyChartComponent.kt | 21 +- .../impl/chart/entity/YieldSupplyChartUM.kt | 11 +- .../impl/chart/model/YieldSupplyChartModel.kt | 81 ++++++- .../impl/chart/ui/YieldSupplyChartContent.kt | 212 +++++++++++++++--- .../promo/DefaultYieldSupplyPromoComponent.kt | 12 +- .../approve/model/YieldSupplyApproveModel.kt | 4 +- .../model/YieldSupplyStopEarningModel.kt | 4 +- 21 files changed, 620 insertions(+), 91 deletions(-) rename core/utils/src/main/java/com/tangem/utils/{TangemLinks.kt => TangemBlogUrlBuilder.kt} (95%) create mode 100644 domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetApyUseCase.kt create mode 100644 domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetChartUseCase.kt create mode 100644 features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/YieldSupplyApyComponent.kt create mode 100644 features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/ui/YieldSupplyApyContent.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 4ddbac7a6a..70ce92ea14 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 @@ -88,4 +88,24 @@ internal object YieldSupplyDomainModule { yieldSupplyMarketRepository = yieldSupplyMarketRepository, ) } + + @Provides + @Singleton + fun provideYieldSupplyGetApyUseCase( + yieldSupplyMarketRepository: YieldSupplyMarketRepository, + ): YieldSupplyGetApyUseCase { + return YieldSupplyGetApyUseCase( + yieldSupplyMarketRepository = yieldSupplyMarketRepository, + ) + } + + @Provides + @Singleton + fun provideYieldSupplyGetChartUseCase( + yieldSupplyMarketRepository: YieldSupplyMarketRepository, + ): YieldSupplyGetChartUseCase { + return YieldSupplyGetChartUseCase( + yieldSupplyMarketRepository = yieldSupplyMarketRepository, + ) + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/YieldSupply.kt b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/YieldSupply.kt index 9fee6a798a..0665ec199b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/common/config/YieldSupply.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/common/config/YieldSupply.kt @@ -41,47 +41,32 @@ internal class YieldSupply( private fun createDevEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig( environment = ApiEnvironment.DEV, baseUrl = "[REDACTED_ENV_URL]", - headers = createHeaders(ApiEnvironment.DEV), + headers = createHeaders(), ) private fun createStageEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig( environment = ApiEnvironment.STAGE, baseUrl = "[REDACTED_ENV_URL]", - headers = createHeaders(ApiEnvironment.STAGE), + headers = createHeaders(), ) private fun createMockedEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig( environment = ApiEnvironment.MOCK, baseUrl = "[REDACTED_ENV_URL]", - headers = createHeaders(ApiEnvironment.MOCK), + headers = createHeaders(), ) private fun createProdEnvironment(): ApiEnvironmentConfig = ApiEnvironmentConfig( environment = ApiEnvironment.PROD, baseUrl = "https://yield.tangem.org/", - headers = createHeaders(ApiEnvironment.PROD), + headers = createHeaders(), ) - private fun createHeaders(apiEnvironment: ApiEnvironment) = buildMap { - put( - key = "Authorization", - value = ProviderSuspend { - "Bearer " + environmentConfigStorage.getConfigSync().yieldModuleApiKey - }, - ) - put(key = "api-key", value = ProviderSuspend { getApiKey(apiEnvironment) }) + private fun createHeaders() = buildMap { + put(key = "api-key", value = ProviderSuspend { + environmentConfigStorage.getConfigSync().yieldModuleApiKey.orEmpty() + }) putAll(from = RequestHeader.AppVersionPlatformHeaders(appVersionProvider, appInfoProvider).values) putAll(from = RequestHeader.AuthenticationHeader(authProvider).values) } - - private fun getApiKey(apiEnvironment: ApiEnvironment): String { - return when (apiEnvironment) { - ApiEnvironment.MOCK, - ApiEnvironment.DEV, - ApiEnvironment.DEV_2, - -> environmentConfigStorage.getConfigSync().tangemApiKeyDev - ApiEnvironment.STAGE -> environmentConfigStorage.getConfigSync().tangemApiKeyStage - ApiEnvironment.PROD -> environmentConfigStorage.getConfigSync().tangemApiKey - } ?: error("No tangem tech api config provided") - } } \ No newline at end of file diff --git a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt index 2d7e832808..cb64ca1ea5 100644 --- a/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt +++ b/core/datasource/src/test/kotlin/com/tangem/datasource/api/common/config/managers/ProdApiConfigsManagerTest.kt @@ -198,8 +198,7 @@ internal class ProdApiConfigsManagerTest { environment = ApiEnvironment.PROD, baseUrl = "https://yield.tangem.org/", headers = mapOf( - "Authorization" to ProviderSuspend { "Bearer " + MockEnvironmentConfigStorage.YIELD_MODULE_KEY }, - "api-key" to ProviderSuspend { MockEnvironmentConfigStorage.TANGEM_API_KEY }, + "api-key" to ProviderSuspend { MockEnvironmentConfigStorage.YIELD_MODULE_KEY }, "card_id" to ProviderSuspend { APP_CARD_ID }, "card_public_key" to ProviderSuspend { APP_CARD_PUBLIC_KEY }, "version" to ProviderSuspend { VERSION_NAME }, diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 47b4f344fa..88c15e79d0 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1266,8 +1266,12 @@ Используйте %s или отсканируйте карту/кольцо, чтобы получить доступ к своему кошельку Соединение не удалось: это dApp использует Wallet Connect версии 1.0, которая не поддерживается. Убедитесь, что dApp поддерживает Wallet Connect версии 2.0 для успешного подключения. Будьте в курсе новых функций и новостей + Мгновенные уведомления о транзакциях, обменах и важных обновлениях. + Уведомления о транзакциях Получайте уведомления о входящих транзакциях Узнавайте первым о новых акциях + Ранний доступ к новым функциям и эксклюзивным предложениям. + Новые функции и важные новости Хотите использовать Push-уведомления? Добавить новый кошелек Вы уверены, что хотите забыть этот кошелек? diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index d29df471fb..ffd86a9798 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1743,6 +1743,7 @@ Confirm approval Text about your money in balance [PLACEHOLDER] Your %s is deposited in Aave + Unable to load chart... The amount received, %1$s %2$s was not deposited to Aave. Earn %1$s%% Available @@ -1760,6 +1761,7 @@ Maximum fee Fee policy Network fee is too high right now. Waiting until it falls below your limit. + Historical returns Write description here. In one, two or three lines will be awesome. [PLACEHOLDER] Some token approve needed Check your network connection @@ -1790,6 +1792,7 @@ Stop earning Turning off will withdraw your funds from Aave, return them to %s in your wallet, and stop earning rewards. The network fee will be deducted from the amount you withdraw. + Supply APR APY Make your money work — earn interest on your balance. Earning on your balance diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt b/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt index 86dacb20d6..860dba2c51 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/label/Label.kt @@ -5,9 +5,12 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.animateColorAsState import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon import androidx.compose.material3.Text +import androidx.compose.material3.ripple import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember @@ -82,7 +85,11 @@ fun Label(state: LabelUM, modifier: Modifier = Modifier) { imageVector = ImageVector.vectorResource(wrappedIcon), tint = iconColor, contentDescription = null, - modifier = Modifier.size(16.dp), + modifier = Modifier.size(16.dp).clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = ripple(bounded = false), + onClick = { state.onIconClick?.invoke() }, + ), ) } } diff --git a/core/utils/src/main/java/com/tangem/utils/TangemLinks.kt b/core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt similarity index 95% rename from core/utils/src/main/java/com/tangem/utils/TangemLinks.kt rename to core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt index 285b5859d2..576a078921 100644 --- a/core/utils/src/main/java/com/tangem/utils/TangemLinks.kt +++ b/core/utils/src/main/java/com/tangem/utils/TangemBlogUrlBuilder.kt @@ -2,7 +2,7 @@ package com.tangem.utils import java.util.Locale -object TangemLinks { +object TangemBlogUrlBuilder { private const val RU_LOCALE = "ru" private const val EN_LOCALE = "en" diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetApyUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetApyUseCase.kt new file mode 100644 index 0000000000..6bb5070262 --- /dev/null +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetApyUseCase.kt @@ -0,0 +1,14 @@ +package com.tangem.domain.yield.supply.usecase + +import arrow.core.Either +import com.tangem.domain.yield.supply.YieldSupplyMarketRepository + +class YieldSupplyGetApyUseCase( + private val yieldSupplyMarketRepository: YieldSupplyMarketRepository, +) { + + suspend operator fun invoke(tokenAddress: String): Either = Either.catch { + val apys = yieldSupplyMarketRepository.getCachedMarkets() ?: yieldSupplyMarketRepository.updateMarkets() + apys.first { it.tokenAddress == tokenAddress }.apy.toString() + } +} \ No newline at end of file diff --git a/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetChartUseCase.kt b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetChartUseCase.kt new file mode 100644 index 0000000000..06cb773d1c --- /dev/null +++ b/domain/yield-supply/src/main/java/com/tangem/domain/yield/supply/usecase/YieldSupplyGetChartUseCase.kt @@ -0,0 +1,16 @@ +package com.tangem.domain.yield.supply.usecase + +import arrow.core.Either +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.domain.yield.supply.YieldSupplyMarketRepository +import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData + +class YieldSupplyGetChartUseCase( + private val yieldSupplyMarketRepository: YieldSupplyMarketRepository, +) { + + suspend operator fun invoke(cryptoCurrency: CryptoCurrency.Token): Either = + Either.catch { + yieldSupplyMarketRepository.getTokenChart(cryptoCurrency) + } +} \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt index 664d23a834..ba91d7d75e 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/feeselector/model/FeeSelectorModel.kt @@ -29,7 +29,7 @@ import com.tangem.features.send.v2.api.subcomponents.feeSelector.FeeSelectorRelo import com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics.CommonSendFeeAnalyticEvents import com.tangem.features.send.v2.api.subcomponents.feeSelector.analytics.CommonSendFeeAnalyticEvents.GasPriceInserter import com.tangem.features.send.v2.feeselector.model.transformers.* -import com.tangem.utils.TangemLinks +import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.transformer.update import kotlinx.coroutines.flow.MutableStateFlow @@ -77,7 +77,7 @@ internal class FeeSelectorModel @Inject constructor( } fun onReadMoreClicked() { - urlOpener.openUrl(TangemLinks.FEE_BLOG_LINK) + urlOpener.openUrl(TangemBlogUrlBuilder.FEE_BLOG_LINK) } private fun initAppCurrency() { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index df672cadf7..539f5ee668 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -74,7 +74,7 @@ import com.tangem.features.staking.impl.presentation.state.utils.checkAndCalcula import com.tangem.features.staking.impl.presentation.state.utils.isSingleAction import com.tangem.features.staking.impl.presentation.state.utils.withStubUnstakeAction import com.tangem.utils.Provider -import com.tangem.utils.TangemLinks.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP +import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP import com.tangem.utils.coroutines.* import com.tangem.utils.extensions.isSingleItem import com.tangem.utils.extensions.orZero diff --git a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt index f7cf259a23..4f013dac67 100644 --- a/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt +++ b/features/swap/impl/src/main/java/com/tangem/feature/swap/model/SwapModel.kt @@ -63,7 +63,7 @@ import com.tangem.feature.swap.ui.StateBuilder import com.tangem.feature.swap.utils.formatToUIRepresentation import com.tangem.features.swap.SwapComponent import com.tangem.utils.Provider -import com.tangem.utils.TangemLinks.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP +import com.tangem.utils.TangemBlogUrlBuilder.RESOURCE_TO_LEARN_ABOUT_APPROVING_IN_SWAP import com.tangem.utils.coroutines.* import com.tangem.utils.isNullOrZero import kotlinx.coroutines.NonCancellable diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/YieldSupplyApyComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/YieldSupplyApyComponent.kt new file mode 100644 index 0000000000..7fde37766d --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/YieldSupplyApyComponent.kt @@ -0,0 +1,61 @@ +package com.tangem.features.yield.supply.impl.apy + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.child +import com.tangem.core.ui.decompose.ComposableBottomSheetComponent +import com.tangem.domain.models.currency.CryptoCurrency +import com.tangem.features.yield.supply.impl.apy.ui.YieldSupplyApyContent +import com.tangem.features.yield.supply.impl.chart.DefaultYieldSupplyChartComponent +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.update + +internal class YieldSupplyApyComponent( + private val appComponentContext: AppComponentContext, + private val params: Params, +) : ComposableBottomSheetComponent, AppComponentContext by appComponentContext { + + val loadingState: StateFlow + field = MutableStateFlow(true) + private val chartComponent = DefaultYieldSupplyChartComponent( + appComponentContext = child("chartComponent"), + params = DefaultYieldSupplyChartComponent.Params( + cryptoCurrency = params.cryptoCurrency as CryptoCurrency.Token, + callback = object : DefaultYieldSupplyChartComponent.ModelCallback { + override fun onStartLoading() { + loadingState.update { true } + } + + override fun onSuccessLoad() { + loadingState.update { false } + } + + override fun onLoadFail() { + loadingState.update { false } + } + }, + ), + ) + + override fun dismiss() { + params.onBackClick() + } + + @Composable + override fun BottomSheet() { + val state by loadingState.collectAsState() + YieldSupplyApyContent( + isLoading = state, + onBackClick = params.onBackClick, + chartComponent = chartComponent, + ) + } + + data class Params( + val cryptoCurrency: CryptoCurrency, + val onBackClick: () -> Unit, + ) +} \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/ui/YieldSupplyApyContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/ui/YieldSupplyApyContent.kt new file mode 100644 index 0000000000..8d419ff256 --- /dev/null +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/apy/ui/YieldSupplyApyContent.kt @@ -0,0 +1,195 @@ +package com.tangem.features.yield.supply.impl.apy.ui + +import android.content.res.Configuration +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.text.InlineTextContent +import androidx.compose.foundation.text.appendInlineContent +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.Placeholder +import androidx.compose.ui.text.PlaceholderVerticalAlign +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.tangem.core.ui.components.SecondaryButton +import com.tangem.core.ui.components.SpacerH12 +import com.tangem.core.ui.components.SpacerH16 +import com.tangem.core.ui.components.SpacerH2 +import com.tangem.core.ui.components.SpacerH24 +import com.tangem.core.ui.components.SpacerH8 +import com.tangem.core.ui.components.SpacerW4 +import com.tangem.core.ui.components.TextShimmer +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig +import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet +import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.core.ui.extensions.stringResourceSafe +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.yield.supply.impl.R + +@Suppress("LongMethod") +@Composable +internal fun YieldSupplyApyContent( + isLoading: Boolean, + onBackClick: () -> Unit, + chartComponent: ComposableContentComponent, +) { + TangemModalBottomSheet( + config = TangemBottomSheetConfig( + isShown = true, + onDismissRequest = onBackClick, + content = TangemBottomSheetConfigContent.Empty, + ), + onBack = onBackClick, + containerColor = TangemTheme.colors.background.tertiary, + title = { + TangemModalBottomSheetTitle( + endIconRes = com.tangem.core.ui.R.drawable.ic_close_24, + onEndClick = onBackClick, + ) + }, + content = { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + ) { + Text( + text = stringResourceSafe(R.string.yield_module_rate_info_sheet_title), + style = TangemTheme.typography.h3, + color = TangemTheme.colors.text.primary1, + ) + SpacerH8() + Text( + text = stringResourceSafe(R.string.yield_module_rate_info_sheet_description), + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.secondary, + textAlign = TextAlign.Center, + ) + SpacerH12() + val inlineIconId = "aaveIcon" + Text( + style = TangemTheme.typography.body2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + text = buildAnnotatedString { + append(stringResourceSafe(R.string.yield_module_rate_info_sheet_powered_by)) + append(" ") + appendInlineContent(inlineIconId, "[icon]") + append("Aave") + }, + inlineContent = mapOf( + inlineIconId to InlineTextContent( + Placeholder( + width = 16.sp, + height = 16.sp, + placeholderVerticalAlign = PlaceholderVerticalAlign.Center, + ), + ) { + Image( + painter = painterResource(id = R.drawable.img_aave_22), + contentDescription = null, + ) + }, + ), + ) + SpacerH24() + + ApyChart( + isChartLoading = isLoading, + chartComponent = chartComponent, + modifier = Modifier.padding(horizontal = 12.dp), + ) + + SpacerH16() + + SecondaryButton( + text = stringResourceSafe(R.string.common_got_it), + onClick = onBackClick, + modifier = Modifier + .fillMaxWidth(), + ) + } + }, + ) +} + +@Composable +private fun ApyChart( + isChartLoading: Boolean, + chartComponent: ComposableContentComponent, + modifier: Modifier = Modifier, +) { + Column(modifier = modifier.fillMaxWidth()) { + if (isChartLoading) { + TextShimmer( + text = stringResourceSafe(R.string.yield_module_historical_returns), + style = TangemTheme.typography.subtitle1, + ) + SpacerH2() + TextShimmer( + text = stringResourceSafe(R.string.yield_module_supply_apr), + style = TangemTheme.typography.subtitle2, + ) + } else { + Text( + text = stringResourceSafe(R.string.yield_module_historical_returns), + style = TangemTheme.typography.subtitle1, + color = TangemTheme.colors.text.primary1, + ) + SpacerH2() + Row(verticalAlignment = Alignment.CenterVertically) { + AccentDot() + SpacerW4() + Text( + text = stringResourceSafe(R.string.yield_module_supply_apr), + style = TangemTheme.typography.subtitle2, + color = TangemTheme.colors.text.tertiary, + ) + } + } + SpacerH12() + chartComponent.Content(Modifier) + } +} + +@Composable +private fun AccentDot(modifier: Modifier = Modifier) { + Box( + modifier = modifier + .padding(4.dp) + .size(TangemTheme.dimens.size8) + .background(color = TangemTheme.colors.icon.accent, shape = CircleShape), + ) +} + +// region Preview +@Composable +@Preview(showBackground = true, widthDp = 360) +@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES) +private fun YieldSupplyApyContentContentPreview() { + TangemThemePreview { + YieldSupplyApyContent( + isLoading = false, + onBackClick = {}, + chartComponent = ComposableContentComponent.EMPTY, + ) + } +} +// endregion \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/DefaultYieldSupplyChartComponent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/DefaultYieldSupplyChartComponent.kt index 0fb4d6b26a..c98ec76f41 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/DefaultYieldSupplyChartComponent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/DefaultYieldSupplyChartComponent.kt @@ -6,6 +6,8 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.features.yield.supply.impl.chart.model.YieldSupplyChartModel import com.tangem.features.yield.supply.impl.chart.ui.YieldSupplyChartContent import dagger.assisted.Assisted @@ -14,13 +16,13 @@ import dagger.assisted.AssistedInject internal class DefaultYieldSupplyChartComponent @AssistedInject constructor( @Assisted private val appComponentContext: AppComponentContext, - @Assisted private val params: YieldSupplyChartModel.Params, -) : AppComponentContext by appComponentContext { + @Assisted private val params: Params, +) : ComposableContentComponent, AppComponentContext by appComponentContext { private val model: YieldSupplyChartModel = getOrCreateModel(params = params) @Composable - fun Content(modifier: Modifier = Modifier) { + override fun Content(modifier: Modifier) { val state by model.uiState.collectAsStateWithLifecycle() YieldSupplyChartContent( state = state, @@ -30,6 +32,17 @@ internal class DefaultYieldSupplyChartComponent @AssistedInject constructor( @AssistedFactory internal interface Factory { - fun create(context: AppComponentContext, params: YieldSupplyChartModel.Params): DefaultYieldSupplyChartComponent + fun create(context: AppComponentContext, params: Params): DefaultYieldSupplyChartComponent + } + + internal data class Params( + val cryptoCurrency: CryptoCurrency.Token, + val callback: ModelCallback? = null, + ) + + internal interface ModelCallback { + fun onStartLoading() + fun onSuccessLoad() + fun onLoadFail() } } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/entity/YieldSupplyChartUM.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/entity/YieldSupplyChartUM.kt index a14dd6c6b7..3bba6034ce 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/entity/YieldSupplyChartUM.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/entity/YieldSupplyChartUM.kt @@ -14,7 +14,10 @@ internal sealed class YieldSupplyChartUM { data class Error(val onRetry: () -> Unit) : YieldSupplyChartUM() @Immutable - data class Data(val chartData: YieldSupplyMarketChartDataUM) : YieldSupplyChartUM() + data class Data( + val chartData: YieldSupplyMarketChartDataUM, + val monthLables: ImmutableList, + ) : YieldSupplyChartUM() } @Immutable @@ -22,16 +25,18 @@ internal data class YieldSupplyMarketChartDataUM( val y: ImmutableList, val x: ImmutableList, val avr: Double, + val percentFormat: String, ) { companion object { @Suppress("MagicNumber") fun mock(): YieldSupplyMarketChartDataUM { - // TODO [REDACTED_TASK_KEY] replace mock values with real APY series val y = listOf( 4.6, 4.0, 4.2, 3.3, 2.5, 5.6, 4.2, 6.7, 3.5, 2.5, 4.5, 3.4, + 4.6, 4.0, 4.2, 3.3, 2.5, 5.6, 4.2, 6.7, 3.5, 2.5, 4.5, 3.4, + 4.2, 6.7, 3.5, 2.5, 4.5, 3.4, ).toImmutableList() val x = List(y.size) { 1.0 }.toImmutableList() - return YieldSupplyMarketChartDataUM(y = y, x = x, avr = 5.15) + return YieldSupplyMarketChartDataUM(y = y, x = x, avr = 5.15, "%.1f") } } } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/model/YieldSupplyChartModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/model/YieldSupplyChartModel.kt index e5e0c3992d..8a5df343d6 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/model/YieldSupplyChartModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/model/YieldSupplyChartModel.kt @@ -3,34 +3,95 @@ package com.tangem.features.yield.supply.impl.chart.model import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.domain.yield.supply.models.YieldSupplyMarketChartData +import com.tangem.domain.yield.supply.usecase.YieldSupplyGetChartUseCase +import com.tangem.features.yield.supply.impl.chart.DefaultYieldSupplyChartComponent import com.tangem.features.yield.supply.impl.chart.entity.YieldSupplyChartUM import com.tangem.features.yield.supply.impl.chart.entity.YieldSupplyMarketChartDataUM import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch +import java.text.SimpleDateFormat +import java.util.Calendar +import java.util.Locale import javax.inject.Inject -@Suppress("UnusedPrivateProperty") @ModelScoped internal class YieldSupplyChartModel @Inject constructor( paramsContainer: ParamsContainer, override val dispatchers: CoroutineDispatcherProvider, + private val yieldSupplyGetChartUseCase: YieldSupplyGetChartUseCase, ) : Model() { - data class Params( - val tokenContractAddress: String, - val onRetry: () -> Unit = {}, - ) - - private val params: Params = paramsContainer.require() + private val params: DefaultYieldSupplyChartComponent.Params = paramsContainer.require() val uiState: StateFlow field = MutableStateFlow(YieldSupplyChartUM.Loading) init { - // TODO [REDACTED_TASK_KEY] - val chartData = YieldSupplyMarketChartDataUM.mock() - uiState.update { YieldSupplyChartUM.Data(chartData = chartData) } + loadChart() + } + + private fun loadChart() { + modelScope.launch(dispatchers.default) { + params.callback?.onStartLoading() + yieldSupplyGetChartUseCase(params.cryptoCurrency).onRight { chartData -> + if (chartData.y.isEmpty()) { + uiState.update { + YieldSupplyChartUM.Error({ + uiState.update { YieldSupplyChartUM.Loading } + loadChart() + }) + } + params.callback?.onLoadFail() + } else { + uiState.update { + YieldSupplyChartUM.Data( + chartData = YieldSupplyMarketChartDataUM( + y = chartData.y.toImmutableList(), + x = chartData.x.toImmutableList(), + avr = chartData.avr, + percentFormat = getPercentFormatPattern(chartData), + ), + monthLables = lastMonthLabels().toImmutableList(), + ) + } + params.callback?.onSuccessLoad() + } + }.onLeft { + uiState.update { + YieldSupplyChartUM.Error({ + uiState.update { YieldSupplyChartUM.Loading } + loadChart() + }) + } + params.callback?.onLoadFail() + } + } + } + + @Suppress("MagicNumber") + private fun getPercentFormatPattern(chartData: YieldSupplyMarketChartData): String { + return when { + chartData.y.all { it < 1.0 } -> "%.1f" + chartData.y.all { it < 0.1 } -> "%.2f" + else -> "%.0f" + } + } + private fun lastMonthLabels(n: Int = MONTH_LABELS_COUNT, locale: Locale = Locale.getDefault()): List { + val calendar = Calendar.getInstance() + val formatter = SimpleDateFormat("MMM", locale) + + return (n downTo 1).map { + calendar.add(Calendar.MONTH, -1) + formatter.format(calendar.time) + }.reversed() + } + + companion object { + private const val MONTH_LABELS_COUNT = 5 } } \ No newline at end of file diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/ui/YieldSupplyChartContent.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/ui/YieldSupplyChartContent.kt index 8dca6004ab..f820ca39a4 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/ui/YieldSupplyChartContent.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/chart/ui/YieldSupplyChartContent.kt @@ -1,6 +1,7 @@ package com.tangem.features.yield.supply.impl.chart.ui import android.content.res.Configuration +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -11,25 +12,31 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.size -import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.State +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent import com.patrykandpatrick.vico.compose.cartesian.axis.rememberBottomAxis import com.patrykandpatrick.vico.compose.cartesian.axis.rememberCustomStartAxis import com.patrykandpatrick.vico.compose.cartesian.decoration.rememberHorizontalLine -import com.patrykandpatrick.vico.compose.cartesian.fullWidth +import com.patrykandpatrick.vico.compose.cartesian.layer.grouped import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart import com.patrykandpatrick.vico.compose.cartesian.rememberVicoScrollState import com.patrykandpatrick.vico.compose.cartesian.rememberVicoZoomState import com.patrykandpatrick.vico.compose.cartesian.layer.rememberColumnCartesianLayer +import com.patrykandpatrick.vico.compose.cartesian.segmented import com.patrykandpatrick.vico.compose.common.component.rememberLineComponent import com.patrykandpatrick.vico.compose.common.shape.dashed import com.patrykandpatrick.vico.core.cartesian.HorizontalLayout @@ -40,6 +47,9 @@ import com.patrykandpatrick.vico.core.cartesian.axis.VerticalAxis import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModel import com.patrykandpatrick.vico.core.cartesian.data.ColumnCartesianLayerModel +import com.patrykandpatrick.vico.core.cartesian.decoration.HorizontalLine +import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer +import com.patrykandpatrick.vico.core.cartesian.layer.ColumnCartesianLayer.MergeMode import com.patrykandpatrick.vico.core.common.shape.Shape import com.tangem.core.ui.R import com.tangem.core.ui.components.SpacerH12 @@ -52,6 +62,8 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.res.TangemThemePreview import com.tangem.features.yield.supply.impl.chart.entity.YieldSupplyChartUM import com.tangem.features.yield.supply.impl.chart.entity.YieldSupplyMarketChartDataUM +import kotlinx.collections.immutable.persistentListOf +import java.util.Locale @Composable internal fun YieldSupplyChartContent(state: YieldSupplyChartUM, modifier: Modifier = Modifier) { @@ -122,64 +134,187 @@ private fun YieldSupplyChartLoading(modifier: Modifier = Modifier) { } } } + } +} - CircularProgressIndicator( +@Suppress("MagicNumber") +@Composable +private fun YieldSupplyChartData(state: YieldSupplyChartUM.Data, modifier: Modifier = Modifier) { + val model = rememberChartModel(state.chartData) + + val columnsLayer = rememberColumnsLayer(accentColor = TangemTheme.colors.text.accent) + + val startAxis: VerticalAxis = rememberStartAxis( + labelColor = TangemTheme.colors.text.tertiary, + percentFormat = state.chartData.percentFormat, + ) + + val bottomAxis: HorizontalAxis = rememberBottomAxis( + labelColor = TangemTheme.colors.text.tertiary, + ) + + val referenceLineThickness = 2.dp + val referenceLine = rememberReferenceLine( + average = state.chartData.avr, + color = TangemTheme.colors.icon.primary1, + thickness = referenceLineThickness, + ) + + val chart = rememberCartesianChart( + columnsLayer, + startAxis = startAxis, + bottomAxis = bottomAxis, + decorations = listOf(referenceLine), + horizontalLayout = HorizontalLayout.segmented(), + ) + + val averageLabelTopPadding by rememberAverageLabelTopPadding(state.chartData) + + Box(modifier = modifier) { + MonthLabelsRow(labels = state.monthLables, modifier = Modifier.align(Alignment.BottomEnd)) + CartesianChartHost( + modifier = Modifier.padding(bottom = 4.dp), + chart = chart, + model = model, + zoomState = rememberVicoZoomState(initialZoom = Zoom.Content, zoomEnabled = false), + scrollState = rememberVicoScrollState(scrollEnabled = false), + ) + AverageLabel( modifier = Modifier - .size(TangemTheme.dimens.size16) - .align(Alignment.Center), - color = TangemTheme.colors.text.tertiary, + .padding(start = 36.dp, top = averageLabelTopPadding) + .height(24.dp), + avr = state.chartData.avr.toString(), ) } } @Composable -private fun YieldSupplyChartData(state: YieldSupplyChartUM.Data, modifier: Modifier = Modifier) { - val yValues = state.chartData.y - val model = CartesianChartModel(ColumnCartesianLayerModel.build { series(yValues) }) +private fun rememberChartModel(data: YieldSupplyMarketChartDataUM): CartesianChartModel { + return remember(data.y) { + CartesianChartModel( + ColumnCartesianLayerModel.build { + series(data.y) + }, + ) + } +} - val layer = rememberColumnCartesianLayer() +@Suppress("MagicNumber") +@Composable +private fun rememberColumnsLayer(accentColor: Color): ColumnCartesianLayer { + val column = rememberLineComponent( + color = accentColor, + thickness = 6.dp, + shape = Shape.rounded(40), + ) + return rememberColumnCartesianLayer( + columnProvider = ColumnCartesianLayer.ColumnProvider.series(column), + columnCollectionSpacing = 3.dp, + mergeMode = { MergeMode.grouped() }, + ) +} - val startAxis: VerticalAxis = rememberCustomStartAxis( - label = rememberAxisLabelComponent(color = TangemTheme.colors.text.tertiary), +@Composable +private fun rememberStartAxis(labelColor: Color, percentFormat: String): VerticalAxis { + return rememberCustomStartAxis( + label = rememberAxisLabelComponent(color = labelColor), valueFormatter = CartesianValueFormatter { value, _, _ -> - val pct = value.toInt() + val pct = String.format(Locale.getDefault(), percentFormat, value) "$pct%" }, - ) - - val bottomAxis: HorizontalAxis = rememberBottomAxis( - label = rememberAxisLabelComponent(color = TangemTheme.colors.text.tertiary), guideline = null, tick = null, line = null, ) +} - val average = yValues.map { it.toDouble() }.average() +@Composable +private fun rememberBottomAxis(labelColor: Color): HorizontalAxis { + return rememberBottomAxis( + label = rememberAxisLabelComponent(color = labelColor), + guideline = null, + tick = null, + line = null, + valueFormatter = CartesianValueFormatter { _, _, _ -> + "" + }, + ) +} - val referenceLine = rememberHorizontalLine( +@Composable +private fun rememberReferenceLine(average: Double, color: Color, thickness: Dp): HorizontalLine { + return rememberHorizontalLine( y = { average }, line = rememberLineComponent( - color = TangemTheme.colors.text.tertiary, - thickness = TangemTheme.dimens.size1, - shape = Shape.dashed(Shape.Rectangle, TangemTheme.dimens.size4, TangemTheme.dimens.size4), + color = color, + thickness = thickness, + shape = Shape.dashed(Shape.Pill, 4.dp, 2.dp), ), ) +} - val chart = rememberCartesianChart( - layer, - startAxis = startAxis, - bottomAxis = bottomAxis, - decorations = listOf(referenceLine), - horizontalLayout = HorizontalLayout.fullWidth(), - ) +@Composable +private fun rememberAverageLabelTopPadding(data: YieldSupplyMarketChartDataUM): State { + return remember(data.y, data.avr) { + derivedStateOf { + val chartHeight = 105.dp + val labelHeight = 24.dp + val referenceLineThickness = 2.dp + val desiredGap = 3.dp + val opticalNudge = 1.dp + val extraPaddingAboveLine = 2.dp + val gapAboveLine = desiredGap + referenceLineThickness / 2 + opticalNudge + extraPaddingAboveLine + val chartContentBottomInset = 10.dp - CartesianChartHost( - modifier = modifier, - chart = chart, - model = model, - zoomState = rememberVicoZoomState(initialZoom = Zoom.Content, zoomEnabled = false), - scrollState = rememberVicoScrollState(scrollEnabled = false), - ) + val maxValueInSeries = (data.y.maxOrNull() ?: 0.0).coerceAtLeast(data.avr) + val averageRatio = if (maxValueInSeries == 0.0) { + 0f + } else { + (data.avr / maxValueInSeries).coerceIn(0.0, 1.0).toFloat() + } + val computedTopPadding = (chartHeight - chartContentBottomInset) * (1f - averageRatio) + -labelHeight - gapAboveLine + if (computedTopPadding < 2.dp) 2.dp else computedTopPadding + } + } +} + +@Composable +private fun MonthLabelsRow(labels: kotlinx.collections.immutable.ImmutableList, modifier: Modifier = Modifier) { + Row( + modifier = modifier + .padding(start = 38.dp, top = 4.dp) + .fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(16.dp), + ) { + repeat(labels.size) { index -> + Text( + text = labels[index], + modifier = Modifier + .weight(1f) + .height(16.dp), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + ) + } + } +} + +@Composable +private fun AverageLabel(avr: String, modifier: Modifier = Modifier) { + Box( + modifier = modifier.background( + color = TangemTheme.colors.icon.primary1, + shape = TangemTheme.shapes.roundedCornersSmall2, + ), + ) { + Text( + text = stringResourceSafe(R.string.yield_module_rate_info_sheet_chart_average, avr), + style = TangemTheme.typography.caption1, + color = TangemTheme.colors.text.primary2, + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp), + ) + } } // region Preview @@ -199,7 +334,10 @@ private class YieldSupplyChartPreviewProvider : PreviewParameterProvider EmptyComposableBottomSheetComponent + YieldSupplyPromoConfig.Apy -> YieldSupplyApyComponent( + appComponentContext = childByContext(componentContext), + params = YieldSupplyApyComponent.Params( + cryptoCurrency = params.currency, + onBackClick = { + model.bottomSheetNavigation.dismiss() + }, + ), + ) YieldSupplyPromoConfig.Action -> YieldSupplyStartEarningEntryComponent( appComponentContext = childByContext(componentContext), params = YieldSupplyStartEarningEntryComponent.Params( diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt index 1f2e90bf5c..0e7f24f5a7 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/approve/model/YieldSupplyApproveModel.kt @@ -30,7 +30,7 @@ import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSu import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSupplyNotificationsUpdateTrigger import com.tangem.features.yield.supply.impl.subcomponents.notifications.entity.YieldSupplyNotificationData import com.tangem.utils.StringsSigns.DOT -import com.tangem.utils.TangemLinks +import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.transformer.update import kotlinx.collections.immutable.persistentListOf @@ -101,7 +101,7 @@ internal class YieldSupplyApproveModel @Inject constructor( } fun onReadMoreClick() { - urlOpener.openUrl(TangemLinks.FEE_BLOG_LINK) + urlOpener.openUrl(TangemBlogUrlBuilder.FEE_BLOG_LINK) } override fun onFeeReload() { diff --git a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt index 5ea64abb48..b1bba83d10 100644 --- a/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt +++ b/features/yield-supply/impl/src/main/java/com/tangem/features/yield/supply/impl/subcomponents/stopearning/model/YieldSupplyStopEarningModel.kt @@ -27,7 +27,7 @@ import com.tangem.features.yield.supply.impl.subcomponents.notifications.YieldSu import com.tangem.features.yield.supply.impl.subcomponents.notifications.entity.YieldSupplyNotificationData import com.tangem.features.yield.supply.impl.subcomponents.stopearning.YieldSupplyStopEarningComponent import com.tangem.features.yield.supply.impl.subcomponents.stopearning.model.transformer.YieldSupplyStopEarningFeeContentTransformer -import com.tangem.utils.TangemLinks +import com.tangem.utils.TangemBlogUrlBuilder import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.extensions.orZero import com.tangem.utils.transformer.update @@ -103,7 +103,7 @@ internal class YieldSupplyStopEarningModel @Inject constructor( } fun onReadMoreClick() { - urlOpener.openUrl(TangemLinks.FEE_BLOG_LINK) + urlOpener.openUrl(TangemBlogUrlBuilder.FEE_BLOG_LINK) } fun onClick() {