Updated on 2026-08-14
This commit is contained in:
parent
6369a3f33a
commit
97f63586b2
16 changed files with 229 additions and 135 deletions
|
|
@ -26,11 +26,18 @@ import com.tangem.core.ui.res.TangemThemePreview
|
|||
* @param isSelected Whether the radio button is selected
|
||||
* @param onClick Called when the user clicks the button
|
||||
* @param modifier Modifier to be applied to the button
|
||||
* @param isEnabled Whether the button click is enabled
|
||||
*/
|
||||
@Composable
|
||||
fun TangemRadioButton(isSelected: Boolean, onClick: () -> Unit, modifier: Modifier = Modifier) {
|
||||
fun TangemRadioButton(
|
||||
isSelected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
isEnabled: Boolean = true,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier.clickable(
|
||||
enabled = isEnabled,
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(bounded = false, radius = TangemTheme.dimens.size16),
|
||||
onClick = onClick,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,9 @@ internal class StakingStateRouter(
|
|||
fun onNextClick() {
|
||||
when (stateController.uiState.value.currentStep) {
|
||||
StakingStep.InitialInfo -> showAmount()
|
||||
StakingStep.Amount -> showConfirm()
|
||||
StakingStep.Validators,
|
||||
StakingStep.Amount,
|
||||
-> showConfirm()
|
||||
StakingStep.Confirm -> showSuccess()
|
||||
StakingStep.Success -> onBackClick()
|
||||
}
|
||||
|
|
@ -51,6 +53,10 @@ internal class StakingStateRouter(
|
|||
stateController.update { it.copy(currentStep = StakingStep.Amount) }
|
||||
}
|
||||
|
||||
fun showValidators() {
|
||||
stateController.update { it.copy(currentStep = StakingStep.Validators) }
|
||||
}
|
||||
|
||||
fun showConfirm() {
|
||||
stateController.update { it.copy(currentStep = StakingStep.Confirm) }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ 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
|
||||
|
|
@ -85,16 +84,12 @@ internal sealed class StakingStates {
|
|||
val appCurrency: AppCurrency,
|
||||
val isFeeApproximate: Boolean,
|
||||
)
|
||||
|
||||
data class ValidatorState(
|
||||
val validatorState: InnerValidatorState,
|
||||
val availableValidators: List<Yield.Validator>,
|
||||
)
|
||||
}
|
||||
|
||||
enum class StakingStep {
|
||||
InitialInfo,
|
||||
Amount,
|
||||
Validators,
|
||||
Confirm,
|
||||
Success,
|
||||
}
|
||||
|
|
@ -4,13 +4,14 @@ import androidx.compose.runtime.Immutable
|
|||
import com.tangem.domain.staking.model.Yield
|
||||
|
||||
@Immutable
|
||||
internal sealed class InnerValidatorState {
|
||||
internal sealed class ValidatorState {
|
||||
|
||||
data class Content(
|
||||
val chosenValidator: Yield.Validator,
|
||||
) : InnerValidatorState()
|
||||
val availableValidators: List<Yield.Validator>,
|
||||
) : ValidatorState()
|
||||
|
||||
data object Loading : InnerValidatorState()
|
||||
data object Loading : ValidatorState()
|
||||
|
||||
data object Error : InnerValidatorState()
|
||||
data object Error : ValidatorState()
|
||||
}
|
||||
|
|
@ -7,9 +7,9 @@ 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 com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -73,10 +73,8 @@ internal object ConfirmStakingStatePreviewData {
|
|||
isFeeApproximate = false,
|
||||
isFeeConvertibleToFiat = true,
|
||||
),
|
||||
validatorState = StakingStates.ValidatorState(
|
||||
validatorState = InnerValidatorState.Content(
|
||||
chosenValidator = validatorList[0],
|
||||
),
|
||||
validatorState = ValidatorState.Content(
|
||||
chosenValidator = validatorList[0],
|
||||
availableValidators = validatorList,
|
||||
),
|
||||
footerText = "You stake \$715.11 and will be receiving ~\$35 monthly",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.stub
|
||||
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
|
||||
object StakingClickIntentsStub : StakingClickIntents {
|
||||
|
|
@ -19,4 +20,8 @@ object StakingClickIntentsStub : StakingClickIntents {
|
|||
override fun onCurrencyChangeClick(isFiat: Boolean) {}
|
||||
|
||||
override fun onAmountNext() {}
|
||||
|
||||
override fun openValidators() {}
|
||||
|
||||
override fun onValidatorSelect(validator: Yield.Validator) {}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.stub
|
||||
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
|
||||
internal object StakingValidatorStateStub {
|
||||
private val validator = Yield.Validator(
|
||||
address = "address",
|
||||
status = "status",
|
||||
name = "Binance",
|
||||
image = null,
|
||||
website = null,
|
||||
apr = "0.0354".toBigDecimal(),
|
||||
commission = null,
|
||||
stakedBalance = null,
|
||||
votingPower = null,
|
||||
preferred = false,
|
||||
)
|
||||
|
||||
val state = StakingStates.ValidatorState.Data(
|
||||
isPrimaryButtonEnabled = false,
|
||||
validators = listOf(
|
||||
validator,
|
||||
validator.copy(
|
||||
address = "TRONLink",
|
||||
name = "TRONLink",
|
||||
apr = "0.0506".toBigDecimal(),
|
||||
),
|
||||
validator.copy(
|
||||
address = "1inch",
|
||||
name = "1inch",
|
||||
apr = "0.0306".toBigDecimal(),
|
||||
),
|
||||
),
|
||||
selectedValidator = validator,
|
||||
)
|
||||
}
|
||||
|
|
@ -14,8 +14,10 @@ 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.StakingUiState
|
||||
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.previewdata.ConfirmStakingStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
|
@ -47,6 +49,7 @@ internal class SetInitialDataStateTransformer(
|
|||
currentStep = StakingStep.InitialInfo,
|
||||
initialInfoState = createInitialInfoState(),
|
||||
amountState = createInitialAmountState(),
|
||||
confirmStakingState = createInitialConfirmationState(),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -75,6 +78,15 @@ internal class SetInitialDataStateTransformer(
|
|||
return amountStateConverter.convert("")
|
||||
}
|
||||
|
||||
private fun createInitialConfirmationState(): StakingStates.ConfirmStakingState {
|
||||
return ConfirmStakingStatePreviewData.confirmStakingState.copy(
|
||||
validatorState = ValidatorState.Content(
|
||||
chosenValidator = yield.validators.first(),
|
||||
availableValidators = yield.validators,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getAprRange(): TextReference {
|
||||
val aprValues = yield.validators.mapNotNull { it.apr }
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers.validator
|
||||
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class ValidatorSelectChangeTransformer(
|
||||
private val selectedValidator: Yield.Validator,
|
||||
) : Transformer<StakingUiState> {
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val confirmState = prevState.confirmStakingState as? StakingStates.ConfirmStakingState.Data ?: return prevState
|
||||
val validatorState = confirmState.validatorState as? ValidatorState.Content ?: return prevState
|
||||
|
||||
return prevState.copy(
|
||||
confirmStakingState = confirmState.copy(
|
||||
validatorState = validatorState.copy(
|
||||
chosenValidator = selectedValidator,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -18,11 +18,18 @@ 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.state.stub.StakingClickIntentsStub
|
||||
import com.tangem.features.staking.impl.presentation.ui.block.NotificationsBlock
|
||||
import com.tangem.features.staking.impl.presentation.ui.block.StakingFeeBlock
|
||||
import com.tangem.features.staking.impl.presentation.ui.block.ValidatorBlock
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
|
||||
@Composable
|
||||
internal fun StakingConfirmContent(amountState: AmountState, state: StakingStates.ConfirmStakingState) {
|
||||
internal fun StakingConfirmContent(
|
||||
amountState: AmountState,
|
||||
state: StakingStates.ConfirmStakingState,
|
||||
clickIntents: StakingClickIntents,
|
||||
) {
|
||||
if (state !is StakingStates.ConfirmStakingState.Data) return
|
||||
|
||||
Column(
|
||||
|
|
@ -39,6 +46,7 @@ internal fun StakingConfirmContent(amountState: AmountState, state: StakingState
|
|||
isEditingDisabled = true,
|
||||
onClick = {},
|
||||
)
|
||||
ValidatorBlock(validatorState = state.validatorState, onClick = clickIntents::openValidators)
|
||||
StakingFeeBlock(feeState = state.feeState)
|
||||
NotificationsBlock(notifications = state.notifications)
|
||||
SpacerHMax()
|
||||
|
|
@ -66,6 +74,7 @@ private fun Preview_StakingConfirmContent() {
|
|||
StakingConfirmContent(
|
||||
amountState = AmountStatePreviewData.amountState,
|
||||
state = ConfirmStakingStatePreviewData.confirmStakingState,
|
||||
clickIntents = StakingClickIntentsStub,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.features.staking.impl.presentation.ui
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
import androidx.compose.animation.shrinkHorizontally
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
|
|
@ -9,7 +11,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
|
|
@ -24,8 +26,8 @@ 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.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStep
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
|
||||
@Composable
|
||||
internal fun StakingNavigationButtons(uiState: StakingUiState, modifier: Modifier = Modifier) {
|
||||
|
|
@ -105,7 +107,8 @@ private fun getButtonData(currentState: StakingUiState): Pair<Int, () -> Unit> {
|
|||
StakingStep.Amount,
|
||||
StakingStep.Confirm,
|
||||
StakingStep.Success,
|
||||
-> R.string.common_next to { currentState.clickIntents.onNextClick() }
|
||||
-> R.string.common_next to currentState.clickIntents::onNextClick
|
||||
StakingStep.Validators -> R.string.common_continue to currentState.clickIntents::onNextClick
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -113,6 +116,7 @@ private fun isButtonEnabled(uiState: StakingUiState): Boolean {
|
|||
return when (uiState.currentStep) {
|
||||
StakingStep.InitialInfo -> uiState.initialInfoState.isPrimaryButtonEnabled
|
||||
StakingStep.Amount -> uiState.amountState.isPrimaryButtonEnabled
|
||||
StakingStep.Validators -> uiState.confirmStakingState.isPrimaryButtonEnabled
|
||||
StakingStep.Confirm -> uiState.confirmStakingState.isPrimaryButtonEnabled
|
||||
StakingStep.Success -> true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.common.ui.amountScreen.AmountScreenContent
|
|||
import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
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 kotlinx.coroutines.delay
|
||||
|
|
@ -50,13 +51,16 @@ internal fun StakingScreen(uiState: StakingUiState) {
|
|||
@Composable
|
||||
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.Confirm -> stringResource(id = R.string.common_stake)
|
||||
StakingStep.InitialInfo,
|
||||
StakingStep.Validators,
|
||||
StakingStep.Confirm,
|
||||
-> stringResource(id = R.string.common_stake)
|
||||
StakingStep.Success -> ""
|
||||
}
|
||||
val backIcon = when (uiState.currentStep) {
|
||||
StakingStep.Amount,
|
||||
StakingStep.Validators,
|
||||
StakingStep.Confirm,
|
||||
StakingStep.Success,
|
||||
-> {
|
||||
|
|
@ -130,7 +134,16 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
|
|||
StakingStep.Confirm -> StakingConfirmContent(
|
||||
amountState = uiState.amountState,
|
||||
state = uiState.confirmStakingState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
StakingStep.Validators -> {
|
||||
val confirmState = uiState.confirmStakingState
|
||||
if (confirmState !is StakingStates.ConfirmStakingState.Data) return@AnimatedContent
|
||||
StakingValidatorListContent(
|
||||
state = confirmState.validatorState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
}
|
||||
else -> TODO()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ 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.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import com.tangem.features.staking.impl.presentation.state.previewdata.ConfirmStakingStatePreviewData
|
||||
import com.tangem.features.staking.impl.presentation.state.stub.StakingClickIntentsStub
|
||||
import com.tangem.features.staking.impl.presentation.state.stub.StakingValidatorStateStub
|
||||
import com.tangem.features.staking.impl.presentation.viewmodel.StakingClickIntents
|
||||
import java.math.BigDecimal
|
||||
|
||||
|
|
@ -37,11 +37,10 @@ import java.math.BigDecimal
|
|||
*/
|
||||
@Composable
|
||||
internal fun StakingValidatorListContent(
|
||||
state: StakingStates.ValidatorState,
|
||||
state: ValidatorState,
|
||||
clickIntents: StakingClickIntents,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
if (state !is StakingStates.ValidatorState.Data) return
|
||||
val bottomBarHeight = with(LocalDensity.current) { WindowInsets.systemBars.getBottom(this).toDp() }
|
||||
|
||||
LazyColumn(
|
||||
|
|
@ -65,41 +64,44 @@ internal fun StakingValidatorListContent(
|
|||
),
|
||||
)
|
||||
}
|
||||
items(
|
||||
count = state.validators.size,
|
||||
key = { state.validators[it] },
|
||||
contentType = { state.validators[it]::class.java },
|
||||
) { index ->
|
||||
val item = state.validators[index]
|
||||
if (state is ValidatorState.Content) {
|
||||
val validators = state.availableValidators
|
||||
items(
|
||||
count = validators.size,
|
||||
key = { validators[it].address },
|
||||
contentType = { validators[it]::class.java },
|
||||
) { index ->
|
||||
val item = validators[index]
|
||||
|
||||
InputRowImageSelector(
|
||||
subtitle = stringReference(item.name),
|
||||
caption = combinedReference(
|
||||
resourceReference(R.string.staking_details_apr),
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(" ")
|
||||
withStyle(style = SpanStyle(color = TangemTheme.colors.text.accent)) {
|
||||
append(
|
||||
BigDecimalFormatter.formatPercent(item.apr ?: BigDecimal.ZERO, true),
|
||||
)
|
||||
}
|
||||
},
|
||||
InputRowImageSelector(
|
||||
subtitle = stringReference(item.name),
|
||||
caption = combinedReference(
|
||||
resourceReference(R.string.staking_details_apr),
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(" ")
|
||||
withStyle(style = SpanStyle(color = TangemTheme.colors.text.accent)) {
|
||||
append(
|
||||
BigDecimalFormatter.formatPercent(item.apr ?: BigDecimal.ZERO, true),
|
||||
)
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
imageUrl = item.image.orEmpty(),
|
||||
isSelected = item == state.selectedValidator,
|
||||
onSelect = { clickIntents.onValidatorSelect(item) },
|
||||
modifier = Modifier
|
||||
.clip(
|
||||
if (index == state.validators.lastIndex) {
|
||||
CornersToRound.BOTTOM_2
|
||||
} else {
|
||||
CornersToRound.ZERO
|
||||
}.getShape(),
|
||||
)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
imageUrl = item.image.orEmpty(),
|
||||
isSelected = item == state.chosenValidator,
|
||||
onSelect = { clickIntents.onValidatorSelect(item) },
|
||||
modifier = Modifier
|
||||
.clip(
|
||||
if (index == validators.lastIndex) {
|
||||
CornersToRound.BOTTOM_2
|
||||
} else {
|
||||
CornersToRound.ZERO
|
||||
}.getShape(),
|
||||
)
|
||||
.background(TangemTheme.colors.background.action),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +112,7 @@ internal fun StakingValidatorListContent(
|
|||
@Composable
|
||||
private fun StakingValidatorListContent_Preview(
|
||||
@PreviewParameter(StakingValidatorListContentPreviewProvider::class)
|
||||
data: StakingStates.ValidatorState.Data,
|
||||
data: ValidatorState,
|
||||
) {
|
||||
TangemThemePreview {
|
||||
StakingValidatorListContent(
|
||||
|
|
@ -120,8 +122,8 @@ private fun StakingValidatorListContent_Preview(
|
|||
}
|
||||
}
|
||||
|
||||
private class StakingValidatorListContentPreviewProvider : PreviewParameterProvider<StakingStates.ValidatorState.Data> {
|
||||
override val values: Sequence<StakingStates.ValidatorState.Data>
|
||||
get() = sequenceOf(StakingValidatorStateStub.state)
|
||||
private class StakingValidatorListContentPreviewProvider : PreviewParameterProvider<ValidatorState> {
|
||||
override val values: Sequence<ValidatorState>
|
||||
get() = sequenceOf(ConfirmStakingStatePreviewData.confirmStakingState.validatorState)
|
||||
}
|
||||
// endregion
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
package com.tangem.features.staking.impl.presentation.ui.block
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import com.tangem.core.ui.components.inputrow.InputRowImageChevron
|
||||
import com.tangem.core.ui.extensions.annotatedReference
|
||||
import com.tangem.core.ui.extensions.combinedReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.features.staking.impl.R
|
||||
import com.tangem.features.staking.impl.presentation.state.ValidatorState
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Composable
|
||||
internal fun ValidatorBlock(validatorState: ValidatorState, onClick: () -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(TangemTheme.colors.background.action)
|
||||
.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = rememberRipple(),
|
||||
onClick = onClick,
|
||||
)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.staking_validator),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
modifier = Modifier.padding(bottom = TangemTheme.dimens.spacing6),
|
||||
)
|
||||
if (validatorState is ValidatorState.Content) {
|
||||
InputRowImageChevron(
|
||||
subtitle = stringReference(validatorState.chosenValidator.name),
|
||||
caption = combinedReference(
|
||||
resourceReference(R.string.staking_details_apr),
|
||||
annotatedReference(
|
||||
buildAnnotatedString {
|
||||
append(" ")
|
||||
withStyle(SpanStyle(color = TangemTheme.colors.text.accent)) {
|
||||
val apr = validatorState.chosenValidator.apr ?: BigDecimal.ZERO
|
||||
append(BigDecimalFormatter.formatPercent(apr, true))
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
imageUrl = validatorState.chosenValidator.image.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.features.staking.impl.presentation.viewmodel
|
||||
|
||||
import com.tangem.common.ui.amountScreen.AmountScreenClickIntents
|
||||
import com.tangem.domain.staking.model.Yield
|
||||
|
||||
internal interface StakingClickIntents : AmountScreenClickIntents {
|
||||
|
||||
|
|
@ -11,4 +12,8 @@ internal interface StakingClickIntents : AmountScreenClickIntents {
|
|||
fun onPrevClick()
|
||||
|
||||
override fun onAmountNext() = onNextClick()
|
||||
|
||||
fun openValidators()
|
||||
|
||||
fun onValidatorSelect(validator: Yield.Validator)
|
||||
}
|
||||
|
|
@ -15,22 +15,20 @@ 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
|
||||
import com.tangem.features.staking.impl.presentation.state.transformers.amount.AmountMaxValueStateTransformer
|
||||
import com.tangem.features.staking.impl.presentation.state.transformers.amount.AmountPasteDismissStateTransformer
|
||||
import com.tangem.features.staking.impl.presentation.state.transformers.validator.ValidatorSelectChangeTransformer
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -48,7 +46,6 @@ 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 {
|
||||
|
||||
|
|
@ -58,18 +55,17 @@ internal class StakingViewModel @Inject constructor(
|
|||
var stakingStateRouter: StakingStateRouter by Delegates.notNull()
|
||||
private set
|
||||
|
||||
private val cryptoCurrencyId: CryptoCurrency.ID = savedStateHandle.get<Bundle>(
|
||||
AppRoute.Staking.CRYPTO_CURRENCY_ID_KEY,
|
||||
)
|
||||
?.let { it.unbundle(CryptoCurrency.ID.serializer()) }
|
||||
?: error("This screen can't be opened without `CryptoCurrency.ID`")
|
||||
private val cryptoCurrencyId: CryptoCurrency.ID =
|
||||
savedStateHandle.get<Bundle>(AppRoute.Staking.CRYPTO_CURRENCY_ID_KEY)
|
||||
?.unbundle(CryptoCurrency.ID.serializer())
|
||||
?: error("This screen can't be opened without `CryptoCurrency.ID`")
|
||||
|
||||
private val userWalletId: UserWalletId = savedStateHandle.get<Bundle>(AppRoute.Staking.USER_WALLET_ID_KEY)
|
||||
?.let { it.unbundle(UserWalletId.serializer()) }
|
||||
?.unbundle(UserWalletId.serializer())
|
||||
?: error("This screen can't be opened without `UserWalletId`")
|
||||
|
||||
private val yield: Yield = savedStateHandle.get<Bundle>(AppRoute.Staking.YIELD_KEY)
|
||||
?.let { it.unbundle(Yield.serializer()) }
|
||||
?.unbundle(Yield.serializer())
|
||||
?: error("This screen can't be opened without `Yield`")
|
||||
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
|
|
@ -90,27 +86,6 @@ 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() {
|
||||
|
|
@ -133,6 +108,12 @@ internal class StakingViewModel @Inject constructor(
|
|||
stateController.update(AmountCurrencyChangeStateTransformer(cryptoCurrencyStatus, isFiat))
|
||||
}
|
||||
|
||||
override fun openValidators() = stakingStateRouter.showValidators()
|
||||
|
||||
override fun onValidatorSelect(validator: Yield.Validator) {
|
||||
stateController.update(ValidatorSelectChangeTransformer(validator))
|
||||
}
|
||||
|
||||
fun setRouter(router: InnerStakingRouter, stateRouter: StakingStateRouter) {
|
||||
innerRouter = router
|
||||
this.stakingStateRouter = stateRouter
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue