Updated on 2026-08-14
This commit is contained in:
parent
2d8d9453bd
commit
f916d7394b
21 changed files with 559 additions and 153 deletions
|
|
@ -111,6 +111,7 @@
|
|||
<string name="common_next">Далее</string>
|
||||
<string name="common_no">Нет</string>
|
||||
<string name="common_no_address">Нет адреса</string>
|
||||
<string name="common_now">Сейчас</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">Основная карта</string>
|
||||
<string name="common_passphrase">Парольная фраза</string>
|
||||
|
|
@ -139,6 +140,7 @@
|
|||
<string name="common_support">Поддержка</string>
|
||||
<string name="common_swap">Обмен</string>
|
||||
<string name="common_terms_and_conditions">условия участия</string>
|
||||
<string name="common_today">Сегодня</string>
|
||||
<string name="common_transaction_failed">Ошибка транзакции</string>
|
||||
<string name="common_transactions">Транзакции</string>
|
||||
<string name="common_transfer">Перевод</string>
|
||||
|
|
@ -309,6 +311,8 @@
|
|||
<string name="manage_tokens_unavailable_vote">Голосовать</string>
|
||||
<string name="manage_tokens_wallet_selector_title">Выберите кошелек</string>
|
||||
<string name="manage_tokens_wallet_support_only_one_network_title">Кошелёк не поддерживает более одной сети</string>
|
||||
<string name="markets_token_details_links">Ссылки</string>
|
||||
<string name="markets_token_details_metrics">Метрики</string>
|
||||
<string name="onboarding_access_code_feature_1_description">Вам необходимо установить единый код доступа для защиты всех ваших карт</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Защита</string>
|
||||
<string name="onboarding_access_code_feature_2_description">Позже вы сможете установить индивидуальный код доступа для каждой карты</string>
|
||||
|
|
@ -681,6 +685,7 @@
|
|||
<string name="wallet_connect_sessions_title">Сессии WalletConnect</string>
|
||||
<string name="wallet_connect_subtitle">Подключение к dApps</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_toast_awaiting_session_proposal">Подключение может занять несколько секунд</string>
|
||||
<string name="wallet_marketplace_block_title">Рыночная цена %s</string>
|
||||
<string name="wallet_marketprice_block_update_time">за 24 часа</string>
|
||||
<string name="wallet_network_group_title">Сеть %s</string>
|
||||
|
|
|
|||
|
|
@ -112,6 +112,7 @@
|
|||
<string name="common_next">Next</string>
|
||||
<string name="common_no">No</string>
|
||||
<string name="common_no_address">No address</string>
|
||||
<string name="common_now">Now</string>
|
||||
<string name="common_ok">OK</string>
|
||||
<string name="common_origin_card">Primary Card</string>
|
||||
<string name="common_passphrase">Passphrase</string>
|
||||
|
|
@ -142,6 +143,7 @@
|
|||
<string name="common_support">Support</string>
|
||||
<string name="common_swap">Swap</string>
|
||||
<string name="common_terms_and_conditions">terms and conditions</string>
|
||||
<string name="common_today">Today</string>
|
||||
<string name="common_transaction_failed">Transaction failed</string>
|
||||
<string name="common_transactions">Transactions</string>
|
||||
<string name="common_transfer">Transfer</string>
|
||||
|
|
@ -319,6 +321,11 @@
|
|||
<string name="markets_common_title">Market</string>
|
||||
<string name="markets_select_wallet">Select wallet</string>
|
||||
<string name="markets_sort_by_title">Sort By</string>
|
||||
<string name="markets_token_details_insights">Insights</string>
|
||||
<string name="markets_token_details_links">Links</string>
|
||||
<string name="markets_token_details_metrics">Metrics</string>
|
||||
<string name="markets_token_details_price_performance">Price performance</string>
|
||||
<string name="markets_token_details_security_score">Security score</string>
|
||||
<string name="onboarding_access_code_feature_1_description">You have to set up a single access code to protect all your cards</string>
|
||||
<string name="onboarding_access_code_feature_1_title">Protect</string>
|
||||
<string name="onboarding_access_code_feature_2_description">You can set up an individual access code on each card later</string>
|
||||
|
|
@ -552,6 +559,8 @@
|
|||
<string name="settings_forget_wallet">Forget wallet</string>
|
||||
<string name="settings_forget_wallet_footer">This will remove the wallet from the application. The wallet itself can be added again.</string>
|
||||
<string name="settings_wallet_name_title">Name</string>
|
||||
<string name="staking_active">Active</string>
|
||||
<string name="staking_active_footer">To unstake your assets, click here.</string>
|
||||
<string name="staking_details_apr">APR</string>
|
||||
<string name="staking_details_apy">APY</string>
|
||||
<string name="staking_details_apy_info">The annual percentage return you can earn from participating in staking.</string>
|
||||
|
|
@ -577,6 +586,9 @@
|
|||
<string name="staking_notification_earn_rewards_text" formatted="false">Staking allow you to earn %1s. Your staking rewards arrive every ~%2s days.</string>
|
||||
<string name="staking_notification_earn_rewards_title">Earn staking rewards</string>
|
||||
<string name="staking_rewards">Rewards</string>
|
||||
<string name="staking_stake_more">Stake more</string>
|
||||
<string name="staking_unstaked">Unstacked</string>
|
||||
<string name="staking_unstaked_footer">Check unstaked to claim your assets</string>
|
||||
<string name="staking_validator">Validator</string>
|
||||
<string name="story_awe_description">Store your crypto assets secure while keeping private keys contained in your card</string>
|
||||
<string name="story_awe_title">Revolutionary Hardware Wallet</string>
|
||||
|
|
@ -702,6 +714,7 @@
|
|||
<string name="wallet_connect_sessions_title">WalletConnect Sessions</string>
|
||||
<string name="wallet_connect_subtitle">Connect to dApps</string>
|
||||
<string name="wallet_connect_title">WalletConnect</string>
|
||||
<string name="wallet_connect_toast_awaiting_session_proposal">Connecting may take a few seconds</string>
|
||||
<string name="wallet_marketplace_block_title">%s Market Price</string>
|
||||
<string name="wallet_marketprice_block_update_time">last 24h</string>
|
||||
<string name="wallet_network_group_title">%s network</string>
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
package com.tangem.core.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.core.ui.res.TangemColorPalette
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
||||
@Composable
|
||||
fun BoxWithGradient(
|
||||
modifier: Modifier = Modifier,
|
||||
gradient: Brush = BottomGradient,
|
||||
content: @Composable BoxScope.() -> Unit,
|
||||
) {
|
||||
val bottomInsetsPx = WindowInsets.navigationBars.getBottom(LocalDensity.current)
|
||||
|
||||
Box(modifier = modifier.fillMaxSize()) {
|
||||
content()
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomCenter)
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size164 + bottomInsetsPx.dp)
|
||||
.background(gradient),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private val BottomGradient: Brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
TangemColorPalette.Black.copy(alpha = 0f),
|
||||
TangemColorPalette.Black.copy(alpha = 0.75f),
|
||||
TangemColorPalette.Black.copy(alpha = 0.95f),
|
||||
TangemColorPalette.Black,
|
||||
),
|
||||
)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.send.impl.presentation.ui.common
|
||||
package com.tangem.core.ui.components.containers
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -18,7 +18,7 @@ import com.tangem.core.ui.res.TangemTheme
|
|||
* @param content field content
|
||||
*/
|
||||
@Composable
|
||||
internal fun FooterContainer(
|
||||
fun FooterContainer(
|
||||
modifier: Modifier = Modifier,
|
||||
footer: String? = null,
|
||||
footerTopPadding: Dp = TangemTheme.dimens.spacing8,
|
||||
|
|
@ -4,12 +4,16 @@ import android.content.res.Configuration
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
|
|
@ -20,8 +24,8 @@ import com.tangem.core.ui.R
|
|||
import com.tangem.core.ui.components.inputrow.inner.DividerContainer
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* [InputRowDefault](https://www.figma.com/file/14ISV23YB1yVW1uNVwqrKv/Android?type=design&node-id=2100-807&mode=design&t=86eKp9izWxUvmoCq-4)
|
||||
|
|
@ -64,7 +68,7 @@ fun InputRowDefault(
|
|||
) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = titleColor,
|
||||
)
|
||||
Text(
|
||||
|
|
@ -80,6 +84,7 @@ fun InputRowDefault(
|
|||
contentDescription = null,
|
||||
tint = iconTint,
|
||||
modifier = Modifier
|
||||
.align(CenterVertically)
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing10,
|
||||
bottom = TangemTheme.dimens.spacing10,
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ internal fun InputRowImageBase(
|
|||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = modifier,
|
||||
) {
|
||||
InputRowAsyncImage(
|
||||
|
|
@ -32,11 +33,7 @@ internal fun InputRowImageBase(
|
|||
.size(TangemTheme.dimens.spacing36)
|
||||
.padding(vertical = TangemTheme.dimens.size1),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
text = subtitle.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
|
|
|
|||
|
|
@ -1,13 +1,20 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import androidx.compose.material.Icon
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.components.SpacerWMax
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Input row component with selector
|
||||
|
|
@ -37,10 +44,36 @@ fun InputRowImageChevron(
|
|||
subtitleColor = subtitleColor,
|
||||
captionColor = captionColor,
|
||||
) {
|
||||
SpacerWMax()
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_chevron_right_24),
|
||||
tint = TangemTheme.colors.icon.informative,
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun InputRowImageChevron_Preview() {
|
||||
TangemThemePreview {
|
||||
InputRowImageChevron(
|
||||
subtitle = stringReference("Binance"),
|
||||
caption = combinedReference(
|
||||
resourceReference(R.string.staking_details_apr),
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(" ")
|
||||
withStyle(SpanStyle(TangemTheme.colors.text.accent)) {
|
||||
stringReference("3,54%")
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
imageUrl = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,114 @@
|
|||
package com.tangem.core.ui.components.inputrow
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.atoms.text.EllipsisText
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
||||
/**
|
||||
* Input row component with selector
|
||||
* [Input Row Image](https://www.figma.com/design/14ISV23YB1yVW1uNVwqrKv/Android?node-id=2841-1589&t=u6pOF6lsdpvWLELb-4)
|
||||
*
|
||||
* @param title title text
|
||||
* @param subtitle subtitle text
|
||||
* @param caption caption text
|
||||
* @param infoTitle info title text
|
||||
* @param infoSubtitle info subtitle text
|
||||
* @param modifier modifier
|
||||
* @param imageUrl icon to load
|
||||
* @param subtitleColor subtitle text color
|
||||
* @param captionColor caption text color
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
fun InputRowImageInfo(
|
||||
title: TextReference?,
|
||||
subtitle: TextReference,
|
||||
caption: TextReference,
|
||||
infoTitle: TextReference,
|
||||
infoSubtitle: TextReference,
|
||||
imageUrl: String,
|
||||
modifier: Modifier = Modifier,
|
||||
subtitleColor: Color = TangemTheme.colors.text.primary1,
|
||||
captionColor: Color = TangemTheme.colors.text.tertiary,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6),
|
||||
modifier = modifier
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
if (title != null) {
|
||||
Text(
|
||||
text = title.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
InputRowImageBase(
|
||||
subtitle = subtitle,
|
||||
caption = caption,
|
||||
imageUrl = imageUrl,
|
||||
subtitleColor = subtitleColor,
|
||||
captionColor = captionColor,
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing2),
|
||||
horizontalAlignment = Alignment.End,
|
||||
modifier = Modifier.weight(1f),
|
||||
) {
|
||||
EllipsisText(
|
||||
text = infoTitle.resolveReference(),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
EllipsisText(
|
||||
text = infoSubtitle.resolveReference(),
|
||||
style = TangemTheme.typography.caption2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region Preview
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
@Preview(showBackground = true, widthDp = 360, uiMode = Configuration.UI_MODE_NIGHT_YES)
|
||||
@Composable
|
||||
private fun InputRowImageInfo_Preview() {
|
||||
TangemThemePreview {
|
||||
InputRowImageInfo(
|
||||
title = stringReference("Active"),
|
||||
subtitle = stringReference("Binance"),
|
||||
caption = combinedReference(
|
||||
resourceReference(R.string.staking_details_apr),
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(" ")
|
||||
withStyle(SpanStyle(TangemTheme.colors.text.accent)) {
|
||||
stringReference("3,54%")
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
infoTitle = stringReference("5431231231231231231231232 USD"),
|
||||
infoSubtitle = stringReference("5 SOL"),
|
||||
imageUrl = "",
|
||||
)
|
||||
}
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -53,4 +53,6 @@ fun AnnotatedString.Builder.appendMarkdown(markdownText: String, node: ASTNode):
|
|||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
}
|
||||
|
||||
fun AnnotatedString.Builder.appendSpace() = append(" ")
|
||||
|
|
@ -3,9 +3,8 @@ package com.tangem.data.staking
|
|||
import arrow.core.raise.catch
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchainsdk.utils.toCoinId
|
||||
import com.tangem.data.staking.converters.StakingNetworkTypeConverter
|
||||
import com.tangem.data.staking.converters.TokenConverter
|
||||
import com.tangem.data.staking.converters.YieldConverter
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.converters.*
|
||||
import com.tangem.data.staking.converters.action.ActionStatusConverter
|
||||
import com.tangem.data.staking.converters.action.EnterActionResponseConverter
|
||||
import com.tangem.data.staking.converters.action.StakingActionTypeConverter
|
||||
|
|
@ -18,21 +17,15 @@ import com.tangem.datasource.api.stakekit.StakeKitApi
|
|||
import com.tangem.datasource.api.stakekit.models.request.Address
|
||||
import com.tangem.datasource.api.stakekit.models.request.ConstructTransactionRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.request.EnterActionRequestBody
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.staking.model.StakingEntryInfo
|
||||
import com.tangem.domain.staking.model.Token
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.domain.staking.model.action.EnterAction
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransaction
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.staking.converters.*
|
||||
import com.tangem.datasource.api.stakekit.models.request.YieldBalanceRequestBody
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore
|
||||
import com.tangem.datasource.local.token.StakingYieldsStore
|
||||
import com.tangem.domain.core.lce.LceFlow
|
||||
import com.tangem.domain.core.lce.lceFlow
|
||||
import com.tangem.domain.staking.model.*
|
||||
import com.tangem.domain.staking.model.action.EnterAction
|
||||
import com.tangem.domain.staking.model.transaction.StakingTransaction
|
||||
import com.tangem.domain.staking.repositories.StakingRepository
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyAddress
|
||||
|
|
@ -40,10 +33,6 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.toFormattedString
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ internal class YieldBalanceConverter : Converter<YieldBalanceConverter.Data, Yie
|
|||
amount = item.amount,
|
||||
pricePerShare = item.pricePerShare,
|
||||
rawCurrencyId = item.tokenDTO.coinGeckoId,
|
||||
validatorAddress = item.validatorAddress,
|
||||
)
|
||||
},
|
||||
integrationId = value.integrationId,
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ data class BalanceItem(
|
|||
val amount: BigDecimal,
|
||||
val pricePerShare: BigDecimal,
|
||||
val rawCurrencyId: String?,
|
||||
val validatorAddress: String?,
|
||||
)
|
||||
|
||||
enum class BalanceType {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.core.ui.extensions.resolveReference
|
|||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.ui.common.FooterContainer
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import com.tangem.features.send.impl.presentation.state.SendStates
|
|||
import com.tangem.features.send.impl.presentation.state.fields.SendTextField
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.RecipientStatePreviewData
|
||||
import com.tangem.features.send.impl.presentation.state.previewdata.SendClickIntentsStub
|
||||
import com.tangem.features.send.impl.presentation.ui.common.FooterContainer
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents
|
||||
import com.tangem.utils.Strings.STARS
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import com.tangem.core.ui.components.inputrow.inner.PasteButton
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.impl.presentation.ui.common.FooterContainer
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
|
||||
@Composable
|
||||
internal fun TextFieldWithPaste(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
package com.tangem.features.staking.impl.presentation.state
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.staking.model.BalanceType
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
|
||||
sealed class InnerYieldBalanceState {
|
||||
data class Data(
|
||||
val rewardsCrypto: String,
|
||||
val rewardsFiat: String,
|
||||
val isRewardsToClaim: Boolean,
|
||||
val balance: List<BalanceGroupedState>,
|
||||
) : InnerYieldBalanceState()
|
||||
|
||||
data object Empty : InnerYieldBalanceState()
|
||||
}
|
||||
|
||||
data class BalanceGroupedState(
|
||||
val items: List<BalanceState>,
|
||||
val type: BalanceType,
|
||||
val footer: TextReference?,
|
||||
val title: TextReference,
|
||||
)
|
||||
|
||||
data class BalanceState(
|
||||
val validator: Yield.Validator,
|
||||
val cryptoAmount: TextReference,
|
||||
val fiatAmount: TextReference,
|
||||
val rawCurrencyId: String?,
|
||||
)
|
||||
|
|
@ -53,6 +53,7 @@ internal sealed class StakingStates {
|
|||
val warmupPeriod: String,
|
||||
val rewardSchedule: String,
|
||||
val onInfoClick: (InfoType) -> Unit,
|
||||
val yieldBalance: InnerYieldBalanceState,
|
||||
) : InitialInfoState()
|
||||
|
||||
data class Empty(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.previewdata
|
||||
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.staking.model.BalanceType
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceGroupedState
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
|
||||
internal object InitialStakingStatePreview {
|
||||
val defaultState = StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
available = "15 SOL",
|
||||
onStake = "0 SOL",
|
||||
aprRange = stringReference("2.54-5.12%"),
|
||||
unbondingPeriod = "3d",
|
||||
minimumRequirement = "12 SOL",
|
||||
rewardClaiming = "Auto",
|
||||
warmupPeriod = "Days",
|
||||
rewardSchedule = "Block",
|
||||
onInfoClick = {},
|
||||
yieldBalance = InnerYieldBalanceState.Empty,
|
||||
)
|
||||
|
||||
val stateWithYield = defaultState.copy(
|
||||
yieldBalance = InnerYieldBalanceState.Data(
|
||||
rewardsFiat = "100 $",
|
||||
rewardsCrypto = "100 SOL",
|
||||
isRewardsToClaim = false,
|
||||
balance = listOf(
|
||||
BalanceGroupedState(
|
||||
type = BalanceType.STAKED,
|
||||
title = stringReference("Staked"),
|
||||
footer = null,
|
||||
items = listOf(
|
||||
BalanceState(
|
||||
cryptoAmount = stringReference("100 SOL"),
|
||||
fiatAmount = stringReference("100 $"),
|
||||
rawCurrencyId = null,
|
||||
validator = Yield.Validator(
|
||||
address = "address",
|
||||
status = "status",
|
||||
name = "Binance",
|
||||
image = null,
|
||||
website = null,
|
||||
apr = "5".toBigDecimal(),
|
||||
commission = null,
|
||||
stakedBalance = null,
|
||||
votingPower = null,
|
||||
preferred = false,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -9,17 +9,19 @@ import com.tangem.core.ui.extensions.stringReference
|
|||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.BalanceType
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.domain.staking.model.YieldBalance
|
||||
import com.tangem.domain.staking.model.YieldBalanceItem
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStep
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmStakingStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.isNullOrZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -55,15 +57,20 @@ internal class SetInitialDataStateTransformer(
|
|||
|
||||
private fun createInitialInfoState(): StakingStates.InitialInfoState.Data {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
|
||||
return StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
available = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoCurrencyStatus.value.amount,
|
||||
cryptoAmount = cryptoCurrencyStatus.value.amount,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
onStake = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = (yieldBalance as? YieldBalance.Data)?.getTotalStakingBalance().orZero(),
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency.symbol,
|
||||
decimals = cryptoCurrencyStatus.currency.decimals,
|
||||
),
|
||||
onStake = "0 SOL", // TODO staking add after adding /balances request
|
||||
aprRange = getAprRange(),
|
||||
unbondingPeriod = yield.metadata.cooldownPeriod.days.toString(),
|
||||
minimumRequirement = yield.metadata.minimumStake.toString(),
|
||||
|
|
@ -71,6 +78,7 @@ internal class SetInitialDataStateTransformer(
|
|||
warmupPeriod = yield.metadata.warmupPeriod.days.toString(),
|
||||
rewardSchedule = yield.metadata.rewardSchedule,
|
||||
onInfoClick = clickIntents::onInfoClick,
|
||||
yieldBalance = getStakedBalances(cryptoCurrencyStatus),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -108,6 +116,96 @@ internal class SetInitialDataStateTransformer(
|
|||
return resourceReference(R.string.common_range, wrappedList(formattedMinApr, formattedMaxApr))
|
||||
}
|
||||
|
||||
private fun getStakedBalances(cryptoCurrencyStatus: CryptoCurrencyStatus): InnerYieldBalanceState {
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
return if (yieldBalance is YieldBalance.Data) {
|
||||
val appCurrency = appCurrencyProvider()
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
val cryptoRewardsValue = yieldBalance.getRewardStakingBalance()
|
||||
val fiatRewardsValue = cryptoCurrencyStatus.value.fiatRate?.times(cryptoRewardsValue)
|
||||
val groupedBalances = getGroupedBalance(yieldBalance.balance, cryptoCurrencyStatus, appCurrency)
|
||||
|
||||
InnerYieldBalanceState.Data(
|
||||
rewardsCrypto = BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = cryptoRewardsValue,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
),
|
||||
rewardsFiat = BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = fiatRewardsValue,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
isRewardsToClaim = !cryptoRewardsValue.isNullOrZero(),
|
||||
balance = groupedBalances,
|
||||
)
|
||||
} else {
|
||||
InnerYieldBalanceState.Empty
|
||||
}
|
||||
}
|
||||
|
||||
private fun getGroupedBalance(
|
||||
balance: YieldBalanceItem,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
appCurrency: AppCurrency,
|
||||
) = balance.items
|
||||
.sortedByDescending { it.type }
|
||||
.groupBy { it.type }
|
||||
.mapNotNull { item ->
|
||||
val (title, footer) = getValidatorBalanceInto(item.key)
|
||||
|
||||
val balances = item.value.mapNotNull { balance ->
|
||||
val validator = yield.validators.firstOrNull {
|
||||
balance.validatorAddress?.contains(it.address, ignoreCase = true) == true
|
||||
}
|
||||
val cryptoAmount = balance.amount * balance.pricePerShare
|
||||
val fiatAmount = cryptoCurrencyStatus.value.fiatRate?.times(cryptoAmount)
|
||||
|
||||
validator?.let {
|
||||
BalanceState(
|
||||
validator = validator,
|
||||
cryptoAmount = stringReference(
|
||||
BigDecimalFormatter.formatCryptoAmount(
|
||||
cryptoAmount = balance.amount,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
),
|
||||
),
|
||||
fiatAmount = stringReference(
|
||||
BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = fiatAmount,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
),
|
||||
),
|
||||
rawCurrencyId = balance.rawCurrencyId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
title?.let {
|
||||
BalanceGroupedState(
|
||||
items = balances,
|
||||
type = item.key,
|
||||
footer = footer,
|
||||
title = it,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getValidatorBalanceInto(type: BalanceType) = when (type) {
|
||||
BalanceType.PREPARING,
|
||||
BalanceType.STAKED,
|
||||
-> resourceReference(R.string.staking_active) to resourceReference(R.string.staking_active_footer)
|
||||
BalanceType.UNSTAKING,
|
||||
BalanceType.UNLOCKING,
|
||||
BalanceType.UNSTAKED,
|
||||
-> resourceReference(R.string.staking_unstaked) to resourceReference(R.string.staking_unstaked_footer)
|
||||
BalanceType.AVAILABLE,
|
||||
BalanceType.LOCKED,
|
||||
BalanceType.UNKNOWN,
|
||||
BalanceType.REWARDS,
|
||||
-> null to null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val EQUALITY_THRESHOLD = BigDecimal(1E-10)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.features.staking.impl.presentation.ui
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.AnimatedContent
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
|
|
@ -11,36 +13,64 @@ import androidx.compose.material3.Text
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
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 com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImageInfo
|
||||
import com.tangem.core.ui.components.rows.CornersToRound
|
||||
import com.tangem.core.ui.components.rows.RoundableCornersRow
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.BalanceGroupedState
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.InitialStakingStatePreview
|
||||
import com.tangem.features.staking.impl.presentation.state.transformers.InfoType
|
||||
import com.tangem.utils.Strings.DOT
|
||||
import com.tangem.utils.extensions.orZero
|
||||
|
||||
@Composable
|
||||
internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState) {
|
||||
if (state !is StakingStates.InitialInfoState.Data) return
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier // Do not put fillMaxSize() in here
|
||||
.background(TangemTheme.colors.background.tertiary)
|
||||
.padding(TangemTheme.dimens.spacing16)
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
MetricsBlock(state)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AnimatedVisibility(state.yieldBalance == InnerYieldBalanceState.Empty) {
|
||||
MetricsBlock(state)
|
||||
}
|
||||
StakingDetailsRows(state)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AnimatedContent(targetState = state.yieldBalance, label = "Rewards block visibility animation") {
|
||||
if (it is InnerYieldBalanceState.Data) {
|
||||
StakingRewardBlock(
|
||||
rewardCrypto = it.rewardsCrypto,
|
||||
rewardFiat = it.rewardsFiat,
|
||||
isRewardsToClaim = it.isRewardsToClaim,
|
||||
)
|
||||
}
|
||||
}
|
||||
AnimatedContent(targetState = state.yieldBalance, label = "Rewards block visibility animation") {
|
||||
if (it is InnerYieldBalanceState.Data) {
|
||||
ActiveStakingBlock(it.balance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -52,7 +82,7 @@ private fun MetricsBlock(state: StakingStates.InitialInfoState.Data) {
|
|||
color = TangemTheme.colors.background.primary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
)
|
||||
.padding(16.dp)
|
||||
.padding(TangemTheme.dimens.spacing12)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
|
|
@ -60,7 +90,7 @@ private fun MetricsBlock(state: StakingStates.InitialInfoState.Data) {
|
|||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Spacer(modifier = Modifier.height(TangemTheme.dimens.size8))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
|
@ -108,53 +138,117 @@ private fun MetricsBlock(state: StakingStates.InitialInfoState.Data) {
|
|||
|
||||
@Composable
|
||||
internal fun StakingDetailsRows(state: StakingStates.InitialInfoState.Data) {
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_available),
|
||||
endText = state.available,
|
||||
cornersToRound = CornersToRound.TOP_2,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_on_stake),
|
||||
endText = state.onStake,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_apy),
|
||||
endText = state.aprRange.resolveReference(),
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.APY) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_unbonding_period),
|
||||
endText = state.unbondingPeriod,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.UNBOUNDING_PERIOD) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_minimum_requirement),
|
||||
endText = state.minimumRequirement,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_reward_claiming),
|
||||
endText = state.rewardClaiming,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.REWARD_CLAIMING) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_warmup_period),
|
||||
endText = state.warmupPeriod,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.WARMUP_PERIOD) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_reward_schedule),
|
||||
endText = state.rewardSchedule,
|
||||
cornersToRound = CornersToRound.BOTTOM_2,
|
||||
iconClick = { state.onInfoClick(InfoType.REWARD_SCHEDULE) },
|
||||
Column {
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_available),
|
||||
endText = state.available,
|
||||
cornersToRound = CornersToRound.TOP_2,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_on_stake),
|
||||
endText = state.onStake,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_apy),
|
||||
endText = state.aprRange.resolveReference(),
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.APY) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_unbonding_period),
|
||||
endText = state.unbondingPeriod,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.UNBOUNDING_PERIOD) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_minimum_requirement),
|
||||
endText = state.minimumRequirement,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_reward_claiming),
|
||||
endText = state.rewardClaiming,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.REWARD_CLAIMING) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_warmup_period),
|
||||
endText = state.warmupPeriod,
|
||||
cornersToRound = CornersToRound.ZERO,
|
||||
iconClick = { state.onInfoClick(InfoType.WARMUP_PERIOD) },
|
||||
)
|
||||
InitialInfoContentRow(
|
||||
startText = stringResource(id = R.string.staking_details_reward_schedule),
|
||||
endText = state.rewardSchedule,
|
||||
cornersToRound = CornersToRound.BOTTOM_2,
|
||||
iconClick = { state.onInfoClick(InfoType.REWARD_SCHEDULE) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakingRewardBlock(rewardCrypto: String, rewardFiat: String, isRewardsToClaim: Boolean) {
|
||||
val (text, textColor) = if (isRewardsToClaim) {
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append("+")
|
||||
appendSpace()
|
||||
append(rewardFiat)
|
||||
appendSpace()
|
||||
append(DOT)
|
||||
appendSpace()
|
||||
append(rewardCrypto)
|
||||
},
|
||||
) to TangemTheme.colors.text.primary1
|
||||
} else {
|
||||
resourceReference(R.string.staking_details_no_rewards_to_claim) to TangemTheme.colors.text.tertiary
|
||||
}
|
||||
InputRowDefault(
|
||||
title = resourceReference(R.string.staking_rewards),
|
||||
text = text,
|
||||
iconRes = R.drawable.ic_chevron_right_24,
|
||||
textColor = textColor,
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActiveStakingBlock(groupes: List<BalanceGroupedState>) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
groupes.forEach { group ->
|
||||
FooterContainer(
|
||||
footer = group.footer?.resolveReference(),
|
||||
modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
) {
|
||||
group.items.forEachIndexed { index, balance ->
|
||||
InputRowImageInfo(
|
||||
title = group.title.takeIf { index == 0 },
|
||||
subtitle = stringReference(balance.validator.name),
|
||||
caption = stringReference(
|
||||
BigDecimalFormatter.formatPercent(balance.validator.apr.orZero(), true),
|
||||
),
|
||||
infoTitle = balance.fiatAmount,
|
||||
infoSubtitle = balance.cryptoAmount,
|
||||
imageUrl = balance.validator.image.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InitialInfoContentRow(
|
||||
startText: String,
|
||||
|
|
@ -191,18 +285,8 @@ private fun StakingInitialInfoContent_Preview(
|
|||
private class StakingInitialInfoContentPreviewProvider : PreviewParameterProvider<StakingStates.InitialInfoState.Data> {
|
||||
override val values: Sequence<StakingStates.InitialInfoState.Data>
|
||||
get() = sequenceOf(
|
||||
StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = true,
|
||||
available = "15 SOL",
|
||||
onStake = "0 SOL",
|
||||
aprRange = stringReference("2.54-5.12%"),
|
||||
unbondingPeriod = "3d",
|
||||
minimumRequirement = "12 SOL",
|
||||
rewardClaiming = "Auto",
|
||||
warmupPeriod = "Days",
|
||||
rewardSchedule = "Block",
|
||||
onInfoClick = {},
|
||||
),
|
||||
InitialStakingStatePreview.defaultState,
|
||||
InitialStakingStatePreview.stateWithYield,
|
||||
)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -25,6 +25,7 @@ import com.tangem.core.ui.components.buttons.common.TangemButton
|
|||
import com.tangem.core.ui.components.buttons.common.TangemButtonIconPosition
|
||||
import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerYieldBalanceState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStep
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
|
|
@ -49,12 +50,9 @@ internal fun StakingNavigationButtons(uiState: StakingUiState, modifier: Modifie
|
|||
@Composable
|
||||
private fun StakingNavigationButton(uiState: StakingUiState, modifier: Modifier = Modifier) {
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
val confirmState = uiState.confirmStakingState
|
||||
val isSuccess = (confirmState as? StakingStates.ConfirmStakingState.Data)?.isSuccess ?: false
|
||||
val isStaking = (confirmState as? StakingStates.ConfirmStakingState.Data)?.isStaking ?: false
|
||||
|
||||
val isButtonsVisible = uiState.currentStep != StakingStep.Success
|
||||
val isStakingState = uiState.currentStep == StakingStep.Success && !isSuccess && !isStaking
|
||||
val isButtonsVisible = isPrevButtonVisible(uiState.currentStep)
|
||||
val isStakingState = uiState.currentStep == StakingStep.Confirm
|
||||
|
||||
val (buttonTextId, buttonClick) = getButtonData(
|
||||
currentState = uiState,
|
||||
|
|
@ -103,15 +101,32 @@ private fun StakingNavigationButton(uiState: StakingUiState, modifier: Modifier
|
|||
|
||||
private fun getButtonData(currentState: StakingUiState): Pair<Int, () -> Unit> {
|
||||
return when (currentState.currentStep) {
|
||||
StakingStep.InitialInfo,
|
||||
StakingStep.InitialInfo -> {
|
||||
val initialState = currentState.initialInfoState as? StakingStates.InitialInfoState.Data
|
||||
if (initialState?.yieldBalance is InnerYieldBalanceState.Data) {
|
||||
R.string.staking_stake_more to currentState.clickIntents::onNextClick
|
||||
} else {
|
||||
R.string.common_next to currentState.clickIntents::onNextClick
|
||||
}
|
||||
}
|
||||
StakingStep.Amount,
|
||||
StakingStep.Confirm,
|
||||
StakingStep.Success,
|
||||
-> R.string.common_next to currentState.clickIntents::onNextClick
|
||||
StakingStep.Confirm -> R.string.common_stake to currentState.clickIntents::onNextClick
|
||||
StakingStep.Validators -> R.string.common_continue to currentState.clickIntents::onNextClick
|
||||
StakingStep.Success -> R.string.common_close to currentState.clickIntents::onBackClick
|
||||
}
|
||||
}
|
||||
|
||||
private fun isPrevButtonVisible(step: StakingStep): Boolean = when (step) {
|
||||
StakingStep.InitialInfo,
|
||||
StakingStep.Confirm,
|
||||
StakingStep.Success,
|
||||
-> false
|
||||
StakingStep.Amount,
|
||||
StakingStep.Validators,
|
||||
-> true
|
||||
}
|
||||
|
||||
private fun isButtonEnabled(uiState: StakingUiState): Boolean {
|
||||
return when (uiState.currentStep) {
|
||||
StakingStep.InitialInfo -> uiState.initialInfoState.isPrimaryButtonEnabled
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue