From fa948bd91afd45b8fe64de160681cc2bd7480706 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 18 Jun 2024 21:26:16 +0300 Subject: [PATCH] Updated on 2026-08-14 --- common/ui/build.gradle.kts | 3 + .../common/ui/amountScreen/ui/AmountBlock.kt | 101 ++++++++++++ .../ui/amountScreen}/utils/FormatterUtils.kt | 8 +- core/res/src/main/res/values-ru/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 11 +- .../state/confirm/SendNotificationFactory.kt | 2 +- .../fee/custom/BitcoinCustomFeeConverter.kt | 2 +- .../fee/custom/EthereumCustomFeeConverter.kt | 2 +- .../presentation/ui/SendNavigationButtons.kt | 5 +- .../ui/fee/SendSpeedSelectorItem.kt | 4 +- .../impl/presentation/ui/send/FeeBlock.kt | 4 +- features/staking/impl/build.gradle.kts | 1 + .../impl/presentation/state/InnerFeeState.kt | 16 ++ .../presentation/state/InnerValidatorState.kt | 16 ++ .../presentation/state/StakingNotification.kt | 54 +++++++ .../state/StakingStateController.kt | 1 - .../presentation/state/StakingStateRouter.kt | 17 +- .../impl/presentation/state/StakingUiState.kt | 50 +++--- .../ConfirmStakingStatePreviewData.kt | 92 +++++++++++ .../SetConfirmStateDataStateTransformer.kt | 35 +++++ .../SetInitialDataStateTransformer.kt | 2 +- .../presentation/ui/StakingConfirmContent.kt | 72 +++++++++ .../ui/StakingNavigationButtons.kt | 8 +- .../impl/presentation/ui/StakingScreen.kt | 11 +- .../ui/StakingValidatorAndFeeContent.kt | 11 -- .../ui/block/NotificationsBlock.kt | 13 ++ .../presentation/ui/block/StakingFeeBlock.kt | 148 ++++++++++++++++++ .../viewmodel/StakingViewModel.kt | 44 ++++-- 28 files changed, 651 insertions(+), 83 deletions(-) create mode 100644 common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlock.kt rename {features/send/impl/src/main/java/com/tangem/features/send/impl/presentation => common/ui/src/main/java/com/tangem/common/ui/amountScreen}/utils/FormatterUtils.kt (79%) create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerFeeState.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerValidatorState.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmStakingStatePreviewData.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmStateDataStateTransformer.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmContent.kt delete mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorAndFeeContent.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt create mode 100644 features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/StakingFeeBlock.kt diff --git a/common/ui/build.gradle.kts b/common/ui/build.gradle.kts index fc0e428770..e57cd07bd1 100644 --- a/common/ui/build.gradle.kts +++ b/common/ui/build.gradle.kts @@ -30,4 +30,7 @@ dependencies { implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) implementation(projects.domain.appCurrency.models) + implementation(deps.tangem.blockchain) { + exclude(module = "joda-time") + } } \ No newline at end of file diff --git a/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlock.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlock.kt new file mode 100644 index 0000000000..942186b435 --- /dev/null +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/ui/AmountBlock.kt @@ -0,0 +1,101 @@ +package com.tangem.common.ui.amountScreen.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +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.draw.clip +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData +import com.tangem.core.ui.components.ResizableText +import com.tangem.core.ui.components.currency.tokenicon.TokenIcon +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.BigDecimalFormatter + +@Composable +fun AmountBlock(amountState: AmountState, isClickDisabled: Boolean, isEditingDisabled: Boolean, onClick: () -> Unit) { + if (amountState !is AmountState.Data) return + val amount = amountState.amountTextField + + val cryptoAmount = BigDecimalFormatter.formatWithSymbol(amount.value, amount.cryptoAmount.currencySymbol) + val fiatAmount = BigDecimalFormatter.formatFiatAmount( + fiatAmount = amount.fiatAmount.value, + fiatCurrencySymbol = amount.fiatAmount.currencySymbol, + fiatCurrencyCode = amountState.appCurrencyCode, + ) + val backgroundColor = if (isEditingDisabled) { + TangemTheme.colors.button.disabled + } else { + TangemTheme.colors.background.action + } + + val (firstAmount, secondAmount) = if (amount.isFiatValue) { + fiatAmount to cryptoAmount + } else { + cryptoAmount to fiatAmount + } + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(backgroundColor) + .clickable(enabled = !isClickDisabled && !isEditingDisabled, onClick = onClick) + .padding(TangemTheme.dimens.spacing16), + ) { + TokenIcon(state = amountState.tokenIconState) + ResizableText( + text = firstAmount, + style = TangemTheme.typography.h2, + color = TangemTheme.colors.text.primary1, + textAlign = TextAlign.Center, + maxLines = 1, + modifier = Modifier + .fillMaxWidth() + .padding(top = TangemTheme.dimens.spacing24), + ) + Text( + text = secondAmount, + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(top = TangemTheme.dimens.spacing8), + ) + } +} + +// region Preview +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun AmountBlockPreview(@PreviewParameter(AmountBlockPreviewProvider::class) value: AmountState) { + TangemThemePreview { + AmountBlock( + amountState = value, + isClickDisabled = false, + isEditingDisabled = false, + onClick = {}, + ) + } +} + +private class AmountBlockPreviewProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + AmountStatePreviewData.amountState, + ) +} +// endregion \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/FormatterUtils.kt similarity index 79% rename from features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt rename to common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/FormatterUtils.kt index 7c8a2becb6..67c6ed8cc9 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/utils/FormatterUtils.kt +++ b/common/ui/src/main/java/com/tangem/common/ui/amountScreen/utils/FormatterUtils.kt @@ -1,4 +1,4 @@ -package com.tangem.features.send.impl.presentation.utils +package com.tangem.common.ui.amountScreen.utils import com.tangem.blockchain.common.Amount import com.tangem.core.ui.extensions.TextReference @@ -11,7 +11,7 @@ import java.math.BigDecimal private const val CRYPTO_FEE_DECIMALS = 6 -internal fun getCryptoReference(amount: Amount?, isFeeApproximate: Boolean): TextReference? { +fun getCryptoReference(amount: Amount?, isFeeApproximate: Boolean): TextReference? { if (amount == null) return null return combinedReference( if (isFeeApproximate) stringReference("${BigDecimalFormatter.CAN_BE_LOWER_SIGN} ") else TextReference.EMPTY, @@ -25,13 +25,13 @@ internal fun getCryptoReference(amount: Amount?, isFeeApproximate: Boolean): Tex ) } -internal fun getFiatReference(value: BigDecimal?, rate: BigDecimal?, appCurrency: AppCurrency): TextReference? { +fun getFiatReference(value: BigDecimal?, rate: BigDecimal?, appCurrency: AppCurrency): TextReference? { if (value == null || rate == null) return null val formattedFiat = getFiatString(value = value, rate = rate, appCurrency = appCurrency) return stringReference(formattedFiat) } -internal fun getFiatString(value: BigDecimal?, rate: BigDecimal?, appCurrency: AppCurrency): String { +fun getFiatString(value: BigDecimal?, rate: BigDecimal?, appCurrency: AppCurrency): String { if (value == null || rate == null) return EMPTY_BALANCE_SIGN val feeValue = value.multiply(rate) return BigDecimalFormatter.formatFiatAmount( diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 9e421c5384..7ad7dd2099 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -543,6 +543,7 @@ Транзакция успешно подписана и отправлена в блокчейн. Баланс будет обновлен через некоторое время Неверный адрес Транзакция отправлена + Подготовьтесь к сканированию карты, которую вы хотите настроить. Забыть кошелек Это приведет к удалению кошелька из приложения. Сам кошелек можно добавить снова. Имя diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 49aad9383e..752c42de5a 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -115,7 +115,7 @@ Primary Card Passphrase Paste - %1$s-%2$s + %1$s-%2$s Read more Receive Reject @@ -310,7 +310,12 @@ Upvote Choose wallet The wallet doesn\'t support more than one network + To start buying, exchanging or receiving this asset, add this token to at least 1 network + This asset is not available + Add to portfolio + My portfolio Market + Select wallet Sort By You have to set up a single access code to protect all your cards Protect @@ -541,6 +546,7 @@ Invalid address %1$s (%2$s) Transaction sent + Prepare to scan card you want to setup. Forget wallet This will remove the wallet from the application. The wallet itself can be added again. Name @@ -559,7 +565,10 @@ Staking %s Unbonding Period Warmup period + Staking allow you to earn %1s. Your staking rewards arrive every ~%2s days. + Earn staking rewards Rewards + Validator Store your crypto assets secure while keeping private keys contained in your card Revolutionary Hardware Wallet Up to 3 physical cards to one wallet diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt index 7d19a4e8f8..3982fbe052 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/confirm/SendNotificationFactory.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchainsdk.utils.fromNetworkId import com.tangem.blockchainsdk.utils.minimalAmount +import com.tangem.common.ui.amountScreen.utils.getFiatString import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.ui.extensions.networkIconResId import com.tangem.core.ui.utils.BigDecimalFormatter @@ -25,7 +26,6 @@ import com.tangem.features.send.impl.presentation.analytics.SendAnalyticEvents import com.tangem.features.send.impl.presentation.state.* import com.tangem.features.send.impl.presentation.state.fee.* import com.tangem.features.send.impl.presentation.state.fields.SendTextField -import com.tangem.features.send.impl.presentation.utils.getFiatString import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.lib.crypto.BlockchainUtils import com.tangem.lib.crypto.BlockchainUtils.isTezos diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/BitcoinCustomFeeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/BitcoinCustomFeeConverter.kt index 62bf1460ad..2d5e5999e3 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/BitcoinCustomFeeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/BitcoinCustomFeeConverter.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.blockchain.common.transaction.Fee +import com.tangem.common.ui.amountScreen.utils.getFiatReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal @@ -14,7 +15,6 @@ import com.tangem.features.send.impl.R import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance import com.tangem.features.send.impl.presentation.state.fields.SendTextField -import com.tangem.features.send.impl.presentation.utils.getFiatReference import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.lib.crypto.BlockchainUtils.isBitcoin import com.tangem.utils.Provider diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt index d64f1c3d47..0c9185cf91 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/state/fee/custom/EthereumCustomFeeConverter.kt @@ -5,6 +5,7 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.KeyboardType import com.tangem.blockchain.common.transaction.Fee +import com.tangem.common.ui.amountScreen.utils.getFiatReference import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.utils.parseBigDecimal import com.tangem.core.ui.utils.parseToBigDecimal @@ -14,7 +15,6 @@ import com.tangem.features.send.impl.R import com.tangem.features.send.impl.presentation.state.StateRouter import com.tangem.features.send.impl.presentation.state.fee.checkExceedBalance import com.tangem.features.send.impl.presentation.state.fields.SendTextField -import com.tangem.features.send.impl.presentation.utils.getFiatReference import com.tangem.features.send.impl.presentation.viewmodel.SendClickIntents import com.tangem.utils.Provider import kotlinx.collections.immutable.ImmutableList diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt index f5247e516a..157a21ad02 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/SendNavigationButtons.kt @@ -21,6 +21,8 @@ import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextAlign +import com.tangem.common.ui.amountScreen.utils.getCryptoReference +import com.tangem.common.ui.amountScreen.utils.getFiatString import com.tangem.core.ui.R import com.tangem.core.ui.components.Keyboard import com.tangem.core.ui.components.SecondaryButtonIconStart @@ -32,12 +34,9 @@ import com.tangem.core.ui.components.keyboardAsState import com.tangem.core.ui.extensions.* import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.domain.tokens.model.Amount import com.tangem.features.send.impl.presentation.state.SendUiCurrentScreen import com.tangem.features.send.impl.presentation.state.SendUiState import com.tangem.features.send.impl.presentation.state.SendUiStateType -import com.tangem.features.send.impl.presentation.utils.getFiatString -import com.tangem.features.send.impl.presentation.utils.getCryptoReference @Composable internal fun SendNavigationButtons( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt index ed1ad443a4..b1cf3f315d 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/fee/SendSpeedSelectorItem.kt @@ -11,6 +11,8 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.common.ui.amountScreen.utils.getCryptoReference +import com.tangem.common.ui.amountScreen.utils.getFiatReference import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.SpacerWMax import com.tangem.core.ui.components.rows.SelectorRowItem @@ -20,8 +22,6 @@ import com.tangem.core.ui.utils.parseToBigDecimal import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType -import com.tangem.features.send.impl.presentation.utils.getCryptoReference -import com.tangem.features.send.impl.presentation.utils.getFiatReference @Composable internal fun SendSpeedSelectorItem( diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt index f2dcd33987..6873ed2b63 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/impl/presentation/ui/send/FeeBlock.kt @@ -14,6 +14,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.tangem.common.ui.amountScreen.utils.getCryptoReference +import com.tangem.common.ui.amountScreen.utils.getFiatReference import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.rows.SelectorRowItem import com.tangem.core.ui.res.TangemThemePreview @@ -24,8 +26,6 @@ import com.tangem.features.send.impl.presentation.state.SendStates import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState import com.tangem.features.send.impl.presentation.state.fee.FeeType import com.tangem.features.send.impl.presentation.state.previewdata.FeeStatePreviewData -import com.tangem.features.send.impl.presentation.utils.getCryptoReference -import com.tangem.features.send.impl.presentation.utils.getFiatReference @Composable internal fun FeeBlock(feeState: SendStates.FeeState, isClickDisabled: Boolean, onClick: () -> Unit) { diff --git a/features/staking/impl/build.gradle.kts b/features/staking/impl/build.gradle.kts index 1547a55bd2..8325def273 100644 --- a/features/staking/impl/build.gradle.kts +++ b/features/staking/impl/build.gradle.kts @@ -60,6 +60,7 @@ dependencies { implementation(projects.domain.appCurrency.models) implementation(projects.domain.legacy) implementation(projects.domain.models) + implementation(projects.domain.transaction) /** Common */ implementation(projects.common.ui) diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerFeeState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerFeeState.kt new file mode 100644 index 0000000000..2511ffa889 --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerFeeState.kt @@ -0,0 +1,16 @@ +package com.tangem.features.staking.impl.presentation.state + +import androidx.compose.runtime.Immutable +import com.tangem.blockchain.common.transaction.TransactionFee + +@Immutable +internal sealed class InnerFeeState { + + data class Content( + val fees: TransactionFee, + ) : InnerFeeState() + + data object Loading : InnerFeeState() + + data object Error : InnerFeeState() +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerValidatorState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerValidatorState.kt new file mode 100644 index 0000000000..8a3c91fa45 --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/InnerValidatorState.kt @@ -0,0 +1,16 @@ +package com.tangem.features.staking.impl.presentation.state + +import androidx.compose.runtime.Immutable +import com.tangem.domain.staking.model.Yield + +@Immutable +internal sealed class InnerValidatorState { + + data class Content( + val chosenValidator: Yield.Validator, + ) : InnerValidatorState() + + data object Loading : InnerValidatorState() + + data object Error : InnerValidatorState() +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt new file mode 100644 index 0000000000..967f681edb --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingNotification.kt @@ -0,0 +1,54 @@ +package com.tangem.features.staking.impl.presentation.state + +import com.tangem.core.ui.components.notifications.NotificationConfig +import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.features.staking.impl.R + +internal sealed class StakingNotification(val config: NotificationConfig) { + + sealed class Error( + title: TextReference, + subtitle: TextReference, + iconResId: Int = R.drawable.ic_alert_24, + buttonState: NotificationConfig.ButtonsState? = null, + onCloseClick: (() -> Unit)? = null, + ) : StakingNotification( + config = NotificationConfig( + title = title, + subtitle = subtitle, + iconResId = iconResId, + buttonsState = buttonState, + onCloseClick = onCloseClick, + ), + ) { + // TODO staking + } + + sealed class Warning( + title: TextReference, + subtitle: TextReference, + buttonsState: NotificationConfig.ButtonsState? = null, + onCloseClick: (() -> Unit)? = null, + ) : StakingNotification( + config = NotificationConfig( + title = title, + subtitle = subtitle, + iconResId = R.drawable.ic_alert_circle_24, + buttonsState = buttonsState, + onCloseClick = onCloseClick, + ), + ) { + data class EarnRewards( + val currencyName: String, + val days: Int, + ) : Warning( + title = resourceReference(R.string.staking_notification_earn_rewards_title), + subtitle = resourceReference( + R.string.staking_notification_earn_rewards_text, + wrappedList(currencyName, days), + ), + ) + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt index 74f5a5bff2..8a61d12bad 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateController.kt @@ -39,7 +39,6 @@ internal class StakingStateController @Inject constructor() { currentStep = StakingStep.InitialInfo, initialInfoState = StakingStates.InitialInfoState.Empty(), amountState = AmountState.Empty(), - validatorState = StakingStates.ValidatorState.Empty(), confirmStakingState = StakingStates.ConfirmStakingState.Empty(), isBalanceHidden = false, event = consumedEvent(), diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt index bc00ead8ca..7d70ebd667 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingStateRouter.kt @@ -1,7 +1,6 @@ package com.tangem.features.staking.impl.presentation.state import androidx.fragment.app.FragmentManager -import kotlinx.coroutines.flow.update import java.lang.ref.WeakReference internal class StakingStateRouter( @@ -22,7 +21,7 @@ internal class StakingStateRouter( isSuccess -> popBackStack() else -> when (type) { StakingStep.Amount -> showInitial() - StakingStep.ValidatorAndFee -> showAmount() + StakingStep.Confirm -> showAmount() else -> popBackStack() } } @@ -31,9 +30,9 @@ internal class StakingStateRouter( fun onNextClick() { when (stateController.uiState.value.currentStep) { StakingStep.InitialInfo -> showAmount() - StakingStep.Amount -> showValidator() - StakingStep.ValidatorAndFee -> showConfirm() - StakingStep.Confirm -> onBackClick() + StakingStep.Amount -> showConfirm() + StakingStep.Confirm -> showSuccess() + StakingStep.Success -> onBackClick() } } @@ -52,13 +51,13 @@ internal class StakingStateRouter( stateController.update { it.copy(currentStep = StakingStep.Amount) } } - fun showValidator() { - stateController.update { it.copy(currentStep = StakingStep.ValidatorAndFee) } - } - fun showConfirm() { stateController.update { it.copy(currentStep = StakingStep.Confirm) } } + fun showSuccess() { + stateController.update { it.copy(currentStep = StakingStep.Success) } + } + private fun getInitialState() = StakingStep.InitialInfo } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt index ebea79fcbc..bd01183407 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/StakingUiState.kt @@ -1,10 +1,15 @@ package com.tangem.features.staking.impl.presentation.state import androidx.compose.runtime.Immutable +import com.tangem.blockchain.common.transaction.Fee import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.core.ui.event.StateEvent import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.staking.model.Yield import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents +import kotlinx.collections.immutable.ImmutableList +import java.math.BigDecimal /** * Ui states of the staking screen @@ -16,7 +21,6 @@ internal data class StakingUiState( val currentStep: StakingStep, val initialInfoState: StakingStates.InitialInfoState, val amountState: AmountState, - val validatorState: StakingStates.ValidatorState, val confirmStakingState: StakingStates.ConfirmStakingState, val isBalanceHidden: Boolean, val event: StateEvent, @@ -25,12 +29,10 @@ internal data class StakingUiState( fun copyWrapped( initialInfoState: StakingStates.InitialInfoState = this.initialInfoState, amountState: AmountState = this.amountState, - validatorState: StakingStates.ValidatorState = this.validatorState, confirmStakingState: StakingStates.ConfirmStakingState = this.confirmStakingState, ): StakingUiState = copy( initialInfoState = initialInfoState, amountState = amountState, - validatorState = validatorState, confirmStakingState = confirmStakingState, ) } @@ -58,32 +60,14 @@ internal sealed class StakingStates { ) : InitialInfoState() } - /** Validator state */ - sealed class ValidatorState : StakingStates() { - data class Data( - override val isPrimaryButtonEnabled: Boolean, - ) : ValidatorState() - - data class Empty( - override val isPrimaryButtonEnabled: Boolean = false, - ) : ValidatorState() - } - - /** Fee state */ - sealed class FeeState : StakingStates() { - data class Data( - override val isPrimaryButtonEnabled: Boolean, - ) : FeeState() - - data class Empty( - override val isPrimaryButtonEnabled: Boolean = false, - ) : FeeState() - } - /** Confirm state */ sealed class ConfirmStakingState : StakingStates() { data class Data( override val isPrimaryButtonEnabled: Boolean, + val feeState: FeeState, + val validatorState: ValidatorState, + val notifications: ImmutableList, + val footerText: String, val isSuccess: Boolean, val isStaking: Boolean, ) : ConfirmStakingState() @@ -92,11 +76,25 @@ internal sealed class StakingStates { override val isPrimaryButtonEnabled: Boolean = false, ) : ConfirmStakingState() } + + data class FeeState( + val innerFeeState: InnerFeeState, + val fee: Fee?, + val rate: BigDecimal?, + val isFeeConvertibleToFiat: Boolean, + val appCurrency: AppCurrency, + val isFeeApproximate: Boolean, + ) + + data class ValidatorState( + val validatorState: InnerValidatorState, + val availableValidators: List, + ) } enum class StakingStep { InitialInfo, Amount, - ValidatorAndFee, Confirm, + Success, } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmStakingStatePreviewData.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmStakingStatePreviewData.kt new file mode 100644 index 0000000000..13d3cc4a1c --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/previewdata/ConfirmStakingStatePreviewData.kt @@ -0,0 +1,92 @@ +package com.tangem.features.staking.impl.presentation.state.previewdata + +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType.Coin +import com.tangem.blockchain.common.transaction.Fee +import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.staking.model.Yield +import com.tangem.features.staking.impl.presentation.state.InnerFeeState +import com.tangem.features.staking.impl.presentation.state.InnerValidatorState +import com.tangem.features.staking.impl.presentation.state.StakingNotification +import com.tangem.features.staking.impl.presentation.state.StakingStates +import kotlinx.collections.immutable.persistentListOf +import java.math.BigDecimal + +internal object ConfirmStakingStatePreviewData { + + private val validatorList = listOf( + Yield.Validator( + address = "0xa6e768fef2d1af36c0cfdb276422e7881a83e951", + status = "active", + name = "Luganodes", + image = "https://assets.stakek.it/validators/luganodes.png", + apr = BigDecimal("0.054823398040640445"), + commission = 0.1, + stakedBalance = "355544384.45009977", + website = "https://luganodes.com/", + votingPower = 0.09778360195377911, + preferred = true, + ), + Yield.Validator( + address = "0x35b1ca0f398905cf752e6fe122b51c88022fca32", + status = "active", + name = "InfStones", + image = "https://assets.stakek.it/validators/infstones.png", + apr = BigDecimal("0.057786472172836965"), + commission = 0.05, + stakedBalance = "12495684.05643019", + website = "https://infstones.com/", + votingPower = 0.0034366257754399774, + preferred = true, + ), + Yield.Validator( + address = "0xd14a87025109013b0a2354a775cb335f926af65a", + status = "active", + name = "Kiln", + image = "https://assets.stakek.it/validators/kiln.png", + apr = BigDecimal("0.057786472172836965"), + commission = 0.05, + stakedBalance = "85400369.96393165", + website = "https://infstones.com/", + votingPower = 0.023487238579718264, + preferred = true, + ), + ) + + private val fee = Fee.Common( + amount = Amount( + currencySymbol = "MATIC", + value = BigDecimal(0.159806), + decimals = 18, + type = Coin, + ), + ) + + val confirmStakingState = StakingStates.ConfirmStakingState.Data( + isPrimaryButtonEnabled = true, + feeState = StakingStates.FeeState( + innerFeeState = InnerFeeState.Content(TransactionFee.Single(fee)), + fee = fee, + rate = BigDecimal.ONE, + appCurrency = AppCurrency.Default, + isFeeApproximate = false, + isFeeConvertibleToFiat = true, + ), + validatorState = StakingStates.ValidatorState( + validatorState = InnerValidatorState.Content( + chosenValidator = validatorList[0], + ), + availableValidators = validatorList, + ), + footerText = "You stake \$715.11 and will be receiving ~\$35 monthly", + notifications = persistentListOf( + StakingNotification.Warning.EarnRewards( + currencyName = "Solana", + days = 2, + ), + ), + isStaking = false, + isSuccess = false, + ) +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmStateDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmStateDataStateTransformer.kt new file mode 100644 index 0000000000..1097794d4b --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetConfirmStateDataStateTransformer.kt @@ -0,0 +1,35 @@ +package com.tangem.features.staking.impl.presentation.state.transformers + +import com.tangem.blockchain.common.transaction.Fee +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.staking.model.Yield +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase +import com.tangem.features.staking.impl.presentation.state.StakingUiState +import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmStakingStatePreviewData +import com.tangem.utils.Provider +import com.tangem.utils.transformer.Transformer + +@Suppress("UnusedPrivateMember") +internal class SetConfirmStateDataStateTransformer( + private val yield: Yield, + private val appCurrencyProvider: Provider, + private val cryptoCurrencyStatusProvider: Provider, + private val isFeeApproximateUseCase: IsFeeApproximateUseCase, +) : Transformer { + + override fun transform(prevState: StakingUiState): StakingUiState { + // TODO staking fill with real data + return prevState.copy( + confirmStakingState = ConfirmStakingStatePreviewData.confirmStakingState, + ) + } + + private fun isFeeApproximate(fee: Fee): Boolean { + val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() + return isFeeApproximateUseCase( + networkId = cryptoCurrencyStatus.currency.network.id, + amountType = fee.amount.type, + ) + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt index dcbe461b4f..c06765c6ed 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/state/transformers/SetInitialDataStateTransformer.kt @@ -93,7 +93,7 @@ internal class SetInitialDataStateTransformer( if (maxApr - minApr < EQUALITY_THRESHOLD) { return stringReference("$formattedMinApr%") } - return resourceReference(R.string.common_percent_range, wrappedList(formattedMinApr, formattedMaxApr)) + return resourceReference(R.string.common_range, wrappedList(formattedMinApr, formattedMaxApr)) } companion object { diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmContent.kt new file mode 100644 index 0000000000..9c78086d8f --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingConfirmContent.kt @@ -0,0 +1,72 @@ +package com.tangem.features.staking.impl.presentation.ui + +import android.content.res.Configuration +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import com.tangem.common.ui.amountScreen.models.AmountState +import com.tangem.common.ui.amountScreen.preview.AmountStatePreviewData +import com.tangem.common.ui.amountScreen.ui.AmountBlock +import com.tangem.core.ui.components.SpacerHMax +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.features.staking.impl.presentation.state.StakingStates +import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmStakingStatePreviewData +import com.tangem.features.staking.impl.presentation.ui.block.NotificationsBlock +import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock + +@Composable +internal fun StakingConfirmContent(amountState: AmountState, state: StakingStates.ConfirmStakingState) { + if (state !is StakingStates.ConfirmStakingState.Data) return + + Column( + modifier = Modifier + .fillMaxSize() + .background(TangemTheme.colors.background.tertiary) + .padding(TangemTheme.dimens.spacing16) + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16), + ) { + AmountBlock( + amountState = amountState, + isClickDisabled = true, + isEditingDisabled = true, + onClick = {}, + ) + StakingFeeBlock(feeState = state.feeState) + NotificationsBlock(notifications = state.notifications) + SpacerHMax() + FooterText(text = state.footerText) + } +} + +@Composable +private fun FooterText(text: String) { + Text( + modifier = Modifier.fillMaxWidth(), + text = text, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.caption2, + textAlign = TextAlign.Center, + ) +} + +@Preview(widthDp = 360, showBackground = true) +@Preview(widthDp = 360, showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun Preview_StakingConfirmContent() { + TangemThemePreview { + Column(Modifier.background(TangemTheme.colors.background.primary)) { + StakingConfirmContent( + amountState = AmountStatePreviewData.amountState, + state = ConfirmStakingStatePreviewData.confirmStakingState, + ) + } + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt index aaceadf9c6..21105e3419 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingNavigationButtons.kt @@ -51,8 +51,8 @@ private fun StakingNavigationButton(uiState: StakingUiState, modifier: Modifier val isSuccess = (confirmState as? StakingStates.ConfirmStakingState.Data)?.isSuccess ?: false val isStaking = (confirmState as? StakingStates.ConfirmStakingState.Data)?.isStaking ?: false - val isButtonsVisible = uiState.currentStep != StakingStep.Confirm - val isStakingState = uiState.currentStep == StakingStep.Confirm && !isSuccess && !isStaking + val isButtonsVisible = uiState.currentStep != StakingStep.Success + val isStakingState = uiState.currentStep == StakingStep.Success && !isSuccess && !isStaking val (buttonTextId, buttonClick) = getButtonData( currentState = uiState, @@ -103,8 +103,8 @@ private fun getButtonData(currentState: StakingUiState): Pair Unit> { return when (currentState.currentStep) { StakingStep.InitialInfo, StakingStep.Amount, - StakingStep.ValidatorAndFee, StakingStep.Confirm, + StakingStep.Success, -> R.string.common_next to { currentState.clickIntents.onNextClick() } } } @@ -113,7 +113,7 @@ private fun isButtonEnabled(uiState: StakingUiState): Boolean { return when (uiState.currentStep) { StakingStep.InitialInfo -> uiState.initialInfoState.isPrimaryButtonEnabled StakingStep.Amount -> uiState.amountState.isPrimaryButtonEnabled - StakingStep.ValidatorAndFee -> uiState.validatorState.isPrimaryButtonEnabled StakingStep.Confirm -> uiState.confirmStakingState.isPrimaryButtonEnabled + StakingStep.Success -> true } } \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt index 51be837ffe..a8f36b6e5e 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingScreen.kt @@ -52,13 +52,13 @@ private fun SendAppBar(uiState: StakingUiState) { val titleRes = when (uiState.currentStep) { StakingStep.InitialInfo -> stringResource(id = R.string.common_stake) StakingStep.Amount -> stringResource(id = R.string.send_amount_label) - StakingStep.ValidatorAndFee -> stringResource(id = R.string.common_stake) - StakingStep.Confirm -> "" + StakingStep.Confirm -> stringResource(id = R.string.common_stake) + StakingStep.Success -> "" } val backIcon = when (uiState.currentStep) { StakingStep.Amount, - StakingStep.ValidatorAndFee, StakingStep.Confirm, + StakingStep.Success, -> { R.drawable.ic_close_24 } @@ -127,8 +127,9 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M isBalanceHiding = uiState.isBalanceHidden, clickIntents = uiState.clickIntents, ) - StakingStep.ValidatorAndFee -> StakingValidatorAndFeeContent( - state = uiState.validatorState, + StakingStep.Confirm -> StakingConfirmContent( + amountState = uiState.amountState, + state = uiState.confirmStakingState, ) else -> TODO() } diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorAndFeeContent.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorAndFeeContent.kt deleted file mode 100644 index a73d0000d4..0000000000 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/StakingValidatorAndFeeContent.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.features.staking.impl.presentation.ui - -import androidx.compose.runtime.Composable -import com.tangem.features.staking.impl.presentation.state.StakingStates - -@Composable -internal fun StakingValidatorAndFeeContent(state: StakingStates.ValidatorState) { - if (state !is StakingStates.ValidatorState.Data) return - - // TODO staking -} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt new file mode 100644 index 0000000000..68d638aaaa --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/NotificationsBlock.kt @@ -0,0 +1,13 @@ +package com.tangem.features.staking.impl.presentation.ui.block + +import androidx.compose.runtime.Composable +import com.tangem.core.ui.components.notifications.Notification +import com.tangem.core.ui.res.TangemTheme +import com.tangem.features.staking.impl.presentation.state.StakingNotification + +@Composable +internal fun NotificationsBlock(notifications: List) { + notifications.forEach { + Notification(config = it.config, iconTint = TangemTheme.colors.icon.accent) + } +} \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/StakingFeeBlock.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/StakingFeeBlock.kt new file mode 100644 index 0000000000..3aa945bd20 --- /dev/null +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/ui/block/StakingFeeBlock.kt @@ -0,0 +1,148 @@ +package com.tangem.features.staking.impl.presentation.ui.block + +import android.content.res.Configuration +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +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.stringResource +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import com.tangem.blockchain.common.Amount +import com.tangem.blockchain.common.AmountType +import com.tangem.blockchain.common.transaction.Fee +import com.tangem.blockchain.common.transaction.TransactionFee +import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.rows.SelectorRowItem +import com.tangem.core.ui.res.TangemThemePreview +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.common.ui.R +import com.tangem.common.ui.amountScreen.utils.getCryptoReference +import com.tangem.common.ui.amountScreen.utils.getFiatReference +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.features.staking.impl.presentation.state.InnerFeeState +import com.tangem.features.staking.impl.presentation.state.StakingStates +import java.math.BigDecimal + +@Composable +internal fun StakingFeeBlock(feeState: StakingStates.FeeState) { + Column( + modifier = Modifier + .fillMaxWidth() + .clip(TangemTheme.shapes.roundedCornersXMedium) + .background(TangemTheme.colors.background.action) + .padding(TangemTheme.dimens.spacing12), + ) { + Text( + text = stringResource(R.string.common_network_fee_title), + style = TangemTheme.typography.caption2, + color = TangemTheme.colors.text.secondary, + ) + + Box( + modifier = Modifier.padding(top = TangemTheme.dimens.spacing8), + ) { + val feeAmount = feeState.fee?.amount + val (title, icon) = R.string.common_fee_selector_option_market to R.drawable.ic_bird_24 + SelectorRowItem( + titleRes = title, + iconRes = icon, + preDot = getCryptoReference(feeAmount, feeState.isFeeApproximate), + postDot = if (feeState.isFeeConvertibleToFiat) { + getFiatReference(feeAmount?.value, feeState.rate, feeState.appCurrency) + } else { + null + }, + ellipsizeOffset = feeAmount?.currencySymbol?.length, + isSelected = true, + showDivider = false, + showSelectedAppearance = false, + paddingValues = PaddingValues(), + ) + FeeLoading(feeState.innerFeeState) + FeeError(feeState.innerFeeState) + } + } +} + +@Composable +private fun BoxScope.FeeLoading(feeSelectorState: InnerFeeState) { + AnimatedContent( + targetState = feeSelectorState, + label = "Fee Loading State Change", + modifier = Modifier.align(Alignment.CenterEnd), + ) { + if (it == InnerFeeState.Loading) { + RectangleShimmer( + radius = TangemTheme.dimens.radius3, + modifier = Modifier.size( + height = TangemTheme.dimens.size12, + width = TangemTheme.dimens.size90, + ), + ) + } + } +} + +@Composable +private fun BoxScope.FeeError(feeSelectorState: InnerFeeState) { + AnimatedContent( + targetState = feeSelectorState, + label = "Fee Error State Change", + modifier = Modifier.align(Alignment.CenterEnd), + ) { + if (it == InnerFeeState.Error) { + Text( + text = BigDecimalFormatter.EMPTY_BALANCE_SIGN, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.body2, + ) + } + } +} + +// region Preview +@Preview +@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES) +@Composable +private fun FeeBlockPreview(@PreviewParameter(FeeBlockPreviewProvider::class) value: StakingStates.FeeState) { + TangemThemePreview { + StakingFeeBlock( + feeState = value, + ) + } +} + +private class FeeBlockPreviewProvider : PreviewParameterProvider { + + override val values: Sequence + get() = sequenceOf( + feeState, + ) + + private val fee = Fee.Common( + amount = Amount( + currencySymbol = "MATIC", + value = BigDecimal(0.159806), + decimals = 18, + type = AmountType.Coin, + ), + ) + + private val feeState = StakingStates.FeeState( + innerFeeState = InnerFeeState.Content(TransactionFee.Single(normal = fee)), + fee = fee, + rate = BigDecimal.ONE, + appCurrency = AppCurrency.Default, + isFeeApproximate = false, + isFeeConvertibleToFiat = true, + ) +} + +// endregion \ No newline at end of file diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt index 107205fa5b..15d7803cc7 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/viewmodel/StakingViewModel.kt @@ -15,14 +15,17 @@ import com.tangem.domain.staking.model.Yield import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.features.staking.impl.navigation.InnerStakingRouter import com.tangem.features.staking.impl.presentation.state.StakingStateController import com.tangem.features.staking.impl.presentation.state.StakingStateRouter +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.transformers.HideBalanceStateTransformer +import com.tangem.features.staking.impl.presentation.state.transformers.SetConfirmStateDataStateTransformer import com.tangem.features.staking.impl.presentation.state.transformers.SetInitialDataStateTransformer import com.tangem.features.staking.impl.presentation.state.transformers.amount.AmountChangeStateTransformer import com.tangem.features.staking.impl.presentation.state.transformers.amount.AmountCurrencyChangeStateTransformer @@ -45,6 +48,7 @@ internal class StakingViewModel @Inject constructor( private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val getUserWalletUseCase: GetUserWalletUseCase, + private val isFeeApproximateUseCase: IsFeeApproximateUseCase, savedStateHandle: SavedStateHandle, ) : ViewModel(), DefaultLifecycleObserver, StakingClickIntents { @@ -72,9 +76,10 @@ internal class StakingViewModel @Inject constructor( private var innerRouter: InnerStakingRouter by Delegates.notNull() private var userWallet: UserWallet by Delegates.notNull() - private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() + private var appCurrency: AppCurrency by Delegates.notNull() init { + subscribeOnSelectedAppCurrency() subscribeOnBalanceHiding() subscribeOnCurrencyStatusUpdates() } @@ -85,6 +90,27 @@ internal class StakingViewModel @Inject constructor( override fun onNextClick() { stakingStateRouter.onNextClick() + when (value.currentStep) { + StakingStep.Confirm -> { + stateController.update( + SetConfirmStateDataStateTransformer( + yield = yield, + appCurrencyProvider = Provider { appCurrency }, + cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, + isFeeApproximateUseCase = isFeeApproximateUseCase, + ), + ) + } + StakingStep.InitialInfo -> { + // TODO staking + } + StakingStep.Amount -> { + // TODO staking + } + StakingStep.Success -> { + // TODO staking + } + } } override fun onPrevClick() { @@ -131,7 +157,7 @@ internal class StakingViewModel @Inject constructor( yield = yield, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, userWalletProvider = Provider { userWallet }, - appCurrencyProvider = Provider { selectedAppCurrencyFlow.value }, + appCurrencyProvider = Provider { appCurrency }, ), ) }, @@ -153,18 +179,14 @@ internal class StakingViewModel @Inject constructor( .launchIn(viewModelScope) } - private fun createSelectedAppCurrencyFlow(): StateFlow { - return getSelectedAppCurrencyUseCase() + private fun subscribeOnSelectedAppCurrency() { + getSelectedAppCurrencyUseCase() .conflate() .distinctUntilChanged() - .map { maybeAppCurrency -> - maybeAppCurrency.getOrElse { AppCurrency.Default } + .onEach { maybeAppCurrency -> + appCurrency = maybeAppCurrency.getOrElse { AppCurrency.Default } } .flowOn(dispatchers.main) - .stateIn( - scope = viewModelScope, - started = SharingStarted.Eagerly, - initialValue = AppCurrency.Default, - ) + .launchIn(viewModelScope) } } \ No newline at end of file