Updated on 2026-08-14
This commit is contained in:
parent
d84c61dc58
commit
4c8c06416f
21 changed files with 620 additions and 91 deletions
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
@ -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 },
|
||||
|
|
|
|||
|
|
@ -1266,8 +1266,12 @@
|
|||
<string name="unlock_wallet_description_full">Используйте %s или отсканируйте карту/кольцо, чтобы получить доступ к своему кошельку</string>
|
||||
<string name="unsupported_wc_version">Соединение не удалось: это dApp использует Wallet Connect версии 1.0, которая не поддерживается. Убедитесь, что dApp поддерживает Wallet Connect версии 2.0 для успешного подключения.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Будьте в курсе новых функций и новостей</string>
|
||||
<string name="user_push_notification_agreement_argument_one_subtitle">Мгновенные уведомления о транзакциях, обменах и важных обновлениях.</string>
|
||||
<string name="user_push_notification_agreement_argument_one_title">Уведомления о транзакциях</string>
|
||||
<string name="user_push_notification_agreement_argument_three">Получайте уведомления о входящих транзакциях</string>
|
||||
<string name="user_push_notification_agreement_argument_two">Узнавайте первым о новых акциях</string>
|
||||
<string name="user_push_notification_agreement_argument_two_subtitle">Ранний доступ к новым функциям и эксклюзивным предложениям.</string>
|
||||
<string name="user_push_notification_agreement_argument_two_title">Новые функции и важные новости</string>
|
||||
<string name="user_push_notification_agreement_header">Хотите использовать Push-уведомления?</string>
|
||||
<string name="user_wallet_list_add_button">Добавить новый кошелек</string>
|
||||
<string name="user_wallet_list_delete_prompt">Вы уверены, что хотите забыть этот кошелек?</string>
|
||||
|
|
|
|||
|
|
@ -1743,6 +1743,7 @@
|
|||
<string name="yield_module_approve_sheet_title">Confirm approval</string>
|
||||
<string name="yield_module_balance_info_sheet_subtitle">Text about your money in balance [PLACEHOLDER]</string>
|
||||
<string name="yield_module_balance_info_sheet_title">Your %s is deposited in Aave</string>
|
||||
<string name="yield_module_chart_loading_error">Unable to load chart...</string>
|
||||
<string name="yield_module_deposit_error_notification_title">The amount received, %1$s %2$s was not deposited to Aave.</string>
|
||||
<string name="yield_module_earn_badge">Earn %1$s%%</string>
|
||||
<string name="yield_module_earn_sheet_available_title">Available</string>
|
||||
|
|
@ -1760,6 +1761,7 @@
|
|||
<string name="yield_module_fee_policy_sheet_max_fee_title">Maximum fee</string>
|
||||
<string name="yield_module_fee_policy_sheet_title">Fee policy</string>
|
||||
<string name="yield_module_high_fee_error">Network fee is too high right now. Waiting until it falls below your limit.</string>
|
||||
<string name="yield_module_historical_returns">Historical returns</string>
|
||||
<string name="yield_module_main_view_approve_notification_description">Write description here. In one, two or three lines will be awesome. [PLACEHOLDER]</string>
|
||||
<string name="yield_module_main_view_approve_notification_title">Some token approve needed</string>
|
||||
<string name="yield_module_network_fee_unreachable_notification_description">Check your network connection</string>
|
||||
|
|
@ -1790,6 +1792,7 @@
|
|||
<string name="yield_module_stop_earning">Stop earning</string>
|
||||
<string name="yield_module_stop_earning_sheet_description">Turning off will withdraw your funds from Aave, return them to %s in your wallet, and stop earning rewards.</string>
|
||||
<string name="yield_module_stop_earning_sheet_fee_note">The network fee will be deducted from the amount you withdraw.</string>
|
||||
<string name="yield_module_supply_apr">Supply APR</string>
|
||||
<string name="yield_module_token_details_earn_notification_apy">APY</string>
|
||||
<string name="yield_module_token_details_earn_notification_description">Make your money work — earn interest on your balance.</string>
|
||||
<string name="yield_module_token_details_earn_notification_earning_on_your_balance_title">Earning on your balance</string>
|
||||
|
|
|
|||
|
|
@ -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() },
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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<Throwable, String> = Either.catch {
|
||||
val apys = yieldSupplyMarketRepository.getCachedMarkets() ?: yieldSupplyMarketRepository.updateMarkets()
|
||||
apys.first { it.tokenAddress == tokenAddress }.apy.toString()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Throwable, YieldSupplyMarketChartData> =
|
||||
Either.catch {
|
||||
yieldSupplyMarketRepository.getTokenChart(cryptoCurrency)
|
||||
}
|
||||
}
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Boolean>
|
||||
field = MutableStateFlow<Boolean>(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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<TangemBottomSheetConfigContent.Empty>(
|
||||
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
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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<String>,
|
||||
) : YieldSupplyChartUM()
|
||||
}
|
||||
|
||||
@Immutable
|
||||
|
|
@ -22,16 +25,18 @@ internal data class YieldSupplyMarketChartDataUM(
|
|||
val y: ImmutableList<Double>,
|
||||
val x: ImmutableList<Double>,
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<YieldSupplyChartUM>
|
||||
field = MutableStateFlow<YieldSupplyChartUM>(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<String> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
@ -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(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size16)
|
||||
.align(Alignment.Center),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
@Composable
|
||||
private fun YieldSupplyChartData(state: YieldSupplyChartUM.Data, modifier: Modifier = Modifier) {
|
||||
val yValues = state.chartData.y
|
||||
val model = CartesianChartModel(ColumnCartesianLayerModel.build { series(yValues) })
|
||||
val model = rememberChartModel(state.chartData)
|
||||
|
||||
val layer = rememberColumnCartesianLayer()
|
||||
val columnsLayer = rememberColumnsLayer(accentColor = TangemTheme.colors.text.accent)
|
||||
|
||||
val startAxis: VerticalAxis<AxisPosition.Vertical.Start> = rememberCustomStartAxis(
|
||||
label = rememberAxisLabelComponent(color = TangemTheme.colors.text.tertiary),
|
||||
valueFormatter = CartesianValueFormatter { value, _, _ ->
|
||||
val pct = value.toInt()
|
||||
"$pct%"
|
||||
},
|
||||
val startAxis: VerticalAxis<AxisPosition.Vertical.Start> = rememberStartAxis(
|
||||
labelColor = TangemTheme.colors.text.tertiary,
|
||||
percentFormat = state.chartData.percentFormat,
|
||||
)
|
||||
|
||||
val bottomAxis: HorizontalAxis<AxisPosition.Horizontal.Bottom> = rememberBottomAxis(
|
||||
label = rememberAxisLabelComponent(color = TangemTheme.colors.text.tertiary),
|
||||
guideline = null,
|
||||
tick = null,
|
||||
line = null,
|
||||
labelColor = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
|
||||
val average = yValues.map { it.toDouble() }.average()
|
||||
|
||||
val referenceLine = 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),
|
||||
),
|
||||
val referenceLineThickness = 2.dp
|
||||
val referenceLine = rememberReferenceLine(
|
||||
average = state.chartData.avr,
|
||||
color = TangemTheme.colors.icon.primary1,
|
||||
thickness = referenceLineThickness,
|
||||
)
|
||||
|
||||
val chart = rememberCartesianChart(
|
||||
layer,
|
||||
columnsLayer,
|
||||
startAxis = startAxis,
|
||||
bottomAxis = bottomAxis,
|
||||
decorations = listOf(referenceLine),
|
||||
horizontalLayout = HorizontalLayout.fullWidth(),
|
||||
horizontalLayout = HorizontalLayout.segmented(),
|
||||
)
|
||||
|
||||
val averageLabelTopPadding by rememberAverageLabelTopPadding(state.chartData)
|
||||
|
||||
Box(modifier = modifier) {
|
||||
MonthLabelsRow(labels = state.monthLables, modifier = Modifier.align(Alignment.BottomEnd))
|
||||
CartesianChartHost(
|
||||
modifier = modifier,
|
||||
modifier = Modifier.padding(bottom = 4.dp),
|
||||
chart = chart,
|
||||
model = model,
|
||||
zoomState = rememberVicoZoomState(initialZoom = Zoom.Content, zoomEnabled = false),
|
||||
scrollState = rememberVicoScrollState(scrollEnabled = false),
|
||||
)
|
||||
AverageLabel(
|
||||
modifier = Modifier
|
||||
.padding(start = 36.dp, top = averageLabelTopPadding)
|
||||
.height(24.dp),
|
||||
avr = state.chartData.avr.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberChartModel(data: YieldSupplyMarketChartDataUM): CartesianChartModel {
|
||||
return remember(data.y) {
|
||||
CartesianChartModel(
|
||||
ColumnCartesianLayerModel.build {
|
||||
series(data.y)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@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() },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberStartAxis(labelColor: Color, percentFormat: String): VerticalAxis<AxisPosition.Vertical.Start> {
|
||||
return rememberCustomStartAxis(
|
||||
label = rememberAxisLabelComponent(color = labelColor),
|
||||
valueFormatter = CartesianValueFormatter { value, _, _ ->
|
||||
val pct = String.format(Locale.getDefault(), percentFormat, value)
|
||||
"$pct%"
|
||||
},
|
||||
guideline = null,
|
||||
tick = null,
|
||||
line = null,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberBottomAxis(labelColor: Color): HorizontalAxis<AxisPosition.Horizontal.Bottom> {
|
||||
return rememberBottomAxis(
|
||||
label = rememberAxisLabelComponent(color = labelColor),
|
||||
guideline = null,
|
||||
tick = null,
|
||||
line = null,
|
||||
valueFormatter = CartesianValueFormatter { _, _, _ ->
|
||||
""
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberReferenceLine(average: Double, color: Color, thickness: Dp): HorizontalLine {
|
||||
return rememberHorizontalLine(
|
||||
y = { average },
|
||||
line = rememberLineComponent(
|
||||
color = color,
|
||||
thickness = thickness,
|
||||
shape = Shape.dashed(Shape.Pill, 4.dp, 2.dp),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberAverageLabelTopPadding(data: YieldSupplyMarketChartDataUM): State<Dp> {
|
||||
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
|
||||
|
||||
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<String>, 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<YieldSu
|
|||
get() = sequenceOf(
|
||||
YieldSupplyChartUM.Loading,
|
||||
YieldSupplyChartUM.Error(onRetry = {}),
|
||||
YieldSupplyChartUM.Data(chartData = YieldSupplyMarketChartDataUM.mock()),
|
||||
YieldSupplyChartUM.Data(
|
||||
chartData = YieldSupplyMarketChartDataUM.mock(),
|
||||
monthLables = persistentListOf("Jul", "Sep", "Nov", "Feb", "May"),
|
||||
),
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -11,8 +11,8 @@ import com.tangem.core.decompose.context.AppComponentContext
|
|||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
|
||||
import com.tangem.core.ui.decompose.EmptyComposableBottomSheetComponent
|
||||
import com.tangem.features.yield.supply.api.YieldSupplyPromoComponent
|
||||
import com.tangem.features.yield.supply.impl.apy.YieldSupplyApyComponent
|
||||
import com.tangem.features.yield.supply.impl.promo.model.YieldSupplyPromoModel
|
||||
import com.tangem.features.yield.supply.impl.promo.ui.YieldSupplyPromoContent
|
||||
import com.tangem.features.yield.supply.impl.subcomponents.startearning.YieldSupplyStartEarningEntryComponent
|
||||
|
|
@ -52,7 +52,15 @@ internal class DefaultYieldSupplyPromoComponent @AssistedInject constructor(
|
|||
config: YieldSupplyPromoConfig,
|
||||
componentContext: ComponentContext,
|
||||
): ComposableBottomSheetComponent = when (config) {
|
||||
YieldSupplyPromoConfig.Apy -> 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(
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue