Updated on 2026-08-14
This commit is contained in:
parent
920d664fb6
commit
8f48ebb4bb
11 changed files with 100 additions and 24 deletions
|
|
@ -2,11 +2,13 @@ package com.tangem.common.ui.navigationButtons
|
|||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
|
|
@ -14,14 +16,18 @@ import androidx.compose.ui.draw.clip
|
|||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.graphics.vector.rememberVectorPainter
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
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.navigationButtons.preview.NavigationButtonsPreview
|
||||
import com.tangem.core.ui.components.Keyboard
|
||||
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.components.keyboardAsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.isNullOrEmpty
|
||||
import com.tangem.core.ui.extensions.rememberHapticFeedback
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
|
|
@ -29,12 +35,17 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
@Composable
|
||||
fun NavigationButtonsBlock(buttonState: NavigationButtonsState, modifier: Modifier = Modifier) {
|
||||
fun NavigationButtonsBlock(
|
||||
buttonState: NavigationButtonsState,
|
||||
modifier: Modifier = Modifier,
|
||||
footerText: TextReference? = null,
|
||||
) {
|
||||
val state = buttonState as? NavigationButtonsState.Data
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
) {
|
||||
InfoText(footerText)
|
||||
ExtraButtons(state?.extraButtons, state?.txUrl)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
|
@ -134,6 +145,39 @@ private fun ExtraButtons(extraButtons: ImmutableList<NavigationButton>?, txUrl:
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoText(footerText: TextReference?, modifier: Modifier = Modifier) {
|
||||
var isVisibleProxy by remember { mutableStateOf(!footerText.isNullOrEmpty()) }
|
||||
val keyboard by keyboardAsState()
|
||||
|
||||
// the text should appear when the keyboard is closed
|
||||
LaunchedEffect(footerText, keyboard) {
|
||||
if (footerText.isNullOrEmpty() && keyboard is Keyboard.Opened) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
isVisibleProxy = !footerText.isNullOrEmpty()
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isVisibleProxy,
|
||||
modifier = modifier,
|
||||
enter = slideInVertically() + fadeIn(),
|
||||
exit = fadeOut(tween(durationMillis = 300)),
|
||||
label = "Animate footer text appearance",
|
||||
) {
|
||||
val text = remember(this) { requireNotNull(footerText) }
|
||||
Text(
|
||||
text = text.resolveReference(),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.caption2,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(bottom = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberNavigationButton(button: NavigationButton?): MutableState<NavigationButton?> {
|
||||
return remember(
|
||||
|
|
|
|||
|
|
@ -696,6 +696,7 @@
|
|||
<string name="staking_rewards">Rewards</string>
|
||||
<string name="staking_stake_locked">Stake locked</string>
|
||||
<string name="staking_stake_more">Stake more</string>
|
||||
<string name="staking_summary_description_text">You stake %1$s and will be receiving ~%2$s yearly</string>
|
||||
<string name="staking_title_stake">Stake %s</string>
|
||||
<string name="staking_title_unstake">Unstake %s</string>
|
||||
<string name="staking_unbonding">Unbonding</string>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ internal sealed class StakingStates {
|
|||
val validatorState: ValidatorState,
|
||||
val pendingAction: PendingAction?,
|
||||
val notifications: ImmutableList<NotificationUM>,
|
||||
val footerText: String,
|
||||
val footerText: TextReference,
|
||||
val transactionDoneState: TransactionDoneState,
|
||||
val isApprovalNeeded: Boolean,
|
||||
val reduceAmountBy: BigDecimal?,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Amount
|
|||
import com.tangem.blockchain.common.AmountType.Coin
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.extensions.wrappedList
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
|
|
@ -77,7 +78,7 @@ internal object ConfirmationStatePreviewData {
|
|||
chosenValidator = validatorList[0],
|
||||
availableValidators = validatorList,
|
||||
),
|
||||
footerText = "You stake \$715.11 and will be receiving ~\$35 monthly",
|
||||
footerText = stringReference("You stake \$715.11 and will be receiving ~\$35 monthly"),
|
||||
notifications = persistentListOf(
|
||||
StakingNotification.Info.EarnRewards(
|
||||
subtitleText = resourceReference(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
|
|
@ -37,6 +38,7 @@ internal class SetConfirmationStateCompletedTransformer(
|
|||
validatorState = validatorState.copySealed(
|
||||
isClickable = false,
|
||||
),
|
||||
footerText = TextReference.EMPTY,
|
||||
notifications = persistentListOf(),
|
||||
transactionDoneState = TransactionDoneState.Content(
|
||||
timestamp = System.currentTimeMillis(),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
|
|
@ -19,6 +20,7 @@ internal class SetConfirmationStateInProgressTransformer : Transformer<StakingUi
|
|||
isPrimaryButtonEnabled = false,
|
||||
innerState = InnerConfirmationStakingState.IN_PROGRESS,
|
||||
validatorState = validatorState.copySealed(isClickable = false),
|
||||
footerText = TextReference.EMPTY,
|
||||
)
|
||||
} else {
|
||||
this
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
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.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionCommonType
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SetConfirmationStateLoadingTransformer(
|
||||
private val yield: Yield,
|
||||
private val appCurrency: AppCurrency,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
|
|
@ -25,7 +35,7 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
availableValidators = yield.validators,
|
||||
),
|
||||
notifications = persistentListOf(),
|
||||
footerText = "",
|
||||
footerText = getFooter(prevState),
|
||||
transactionDoneState = TransactionDoneState.Empty,
|
||||
pendingAction = possibleConfirmationState?.pendingAction,
|
||||
isApprovalNeeded = false,
|
||||
|
|
@ -33,4 +43,35 @@ internal class SetConfirmationStateLoadingTransformer(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getFooter(state: StakingUiState): TextReference {
|
||||
val amountState = state.amountState as? AmountState.Data
|
||||
val confirmationState = state.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
val validatorState = confirmationState?.validatorState as? ValidatorState.Content
|
||||
|
||||
val isEnterAction = state.actionType == StakingActionCommonType.ENTER
|
||||
|
||||
val apr = validatorState?.chosenValidator?.apr.orZero()
|
||||
val amountDecimal = amountState?.amountTextField?.fiatAmount?.value
|
||||
val potentialReward = amountDecimal?.multiply(apr)
|
||||
|
||||
val amountValue = BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = amountDecimal,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
val potentialRewardValue = BigDecimalFormatter.formatFiatAmount(
|
||||
fiatAmount = potentialReward,
|
||||
fiatCurrencyCode = appCurrency.code,
|
||||
fiatCurrencySymbol = appCurrency.symbol,
|
||||
)
|
||||
return if (isEnterAction && amountDecimal != null && potentialReward != null) {
|
||||
resourceReference(
|
||||
id = R.string.staking_summary_description_text,
|
||||
formatArgs = wrappedList(amountValue, potentialRewardValue),
|
||||
)
|
||||
} else {
|
||||
TextReference.EMPTY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -211,7 +211,7 @@ internal class SetInitialDataStateTransformer(
|
|||
feeState = FeeState.Loading,
|
||||
validatorState = ValidatorState.Loading,
|
||||
notifications = persistentListOf(),
|
||||
footerText = "",
|
||||
footerText = TextReference.EMPTY,
|
||||
transactionDoneState = TransactionDoneState.Empty,
|
||||
pendingAction = null,
|
||||
isApprovalNeeded = isApprovalNeeded,
|
||||
|
|
|
|||
|
|
@ -5,20 +5,16 @@ import androidx.compose.animation.AnimatedVisibility
|
|||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
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.components.transactions.TransactionDoneTitle
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.res.TangemThemePreview
|
||||
|
|
@ -71,22 +67,9 @@ internal fun StakingConfirmationContent(
|
|||
}
|
||||
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
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import kotlinx.coroutines.flow.withIndex
|
|||
@Composable
|
||||
internal fun StakingScreen(uiState: StakingUiState) {
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
val confirmationState = uiState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
|
||||
BackHandler(onBack = uiState.clickIntents::onPrevClick)
|
||||
Column(
|
||||
|
|
@ -55,6 +56,7 @@ internal fun StakingScreen(uiState: StakingUiState) {
|
|||
)
|
||||
NavigationButtonsBlock(
|
||||
buttonState = uiState.buttonsState,
|
||||
footerText = confirmationState?.footerText.takeIf { uiState.currentStep == StakingStep.Confirmation },
|
||||
modifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
}
|
||||
stakingStateRouter.onNextClick()
|
||||
when {
|
||||
isInitState() -> stateController.update(SetConfirmationStateLoadingTransformer(yield))
|
||||
isInitState() -> stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency))
|
||||
isAssentState() -> getFee(pendingAction)
|
||||
}
|
||||
}
|
||||
|
|
@ -183,7 +183,7 @@ internal class StakingViewModel @Inject constructor(
|
|||
|
||||
override fun getFee(pendingAction: PendingAction?) {
|
||||
viewModelScope.launch {
|
||||
stateController.update(SetConfirmationStateLoadingTransformer(yield))
|
||||
stateController.update(SetConfirmationStateLoadingTransformer(yield, appCurrency))
|
||||
val cryptoCurrencyValue = cryptoCurrencyStatus.value
|
||||
val confirmationState = value.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
?: error("No confirmation state")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue