Updated on 2026-08-14
This commit is contained in:
parent
7dab5f79b2
commit
cc3a64bb05
15 changed files with 144 additions and 21 deletions
|
|
@ -2,6 +2,7 @@ package com.tangem.features.staking.impl.presentation.state
|
|||
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -19,7 +20,9 @@ import javax.inject.Inject
|
|||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
internal class StakingStateController @Inject constructor() {
|
||||
internal class StakingStateController @Inject constructor(
|
||||
private val urlOpener: UrlOpener,
|
||||
) {
|
||||
|
||||
val value: StakingUiState get() = uiState.value
|
||||
|
||||
|
|
@ -27,7 +30,7 @@ internal class StakingStateController @Inject constructor() {
|
|||
|
||||
val uiState: StateFlow<StakingUiState> get() = mutableUiState.asStateFlow()
|
||||
|
||||
private val buttonsTransformer = SetButtonsStateTransformer()
|
||||
private val buttonsTransformer = SetButtonsStateTransformer(urlOpener)
|
||||
private val titleTransformer = SetTitleTransformer
|
||||
|
||||
fun update(function: (StakingUiState) -> StakingUiState) {
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.features.staking.impl.presentation.state.transformers
|
|||
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButton
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -12,7 +13,9 @@ import com.tangem.utils.transformer.Transformer
|
|||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SetButtonsStateTransformer : Transformer<StakingUiState> {
|
||||
internal class SetButtonsStateTransformer(
|
||||
private val urlOpener: UrlOpener,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val confirmState = prevState.confirmationState as? StakingStates.ConfirmationState.Data
|
||||
|
|
@ -23,6 +26,7 @@ internal class SetButtonsStateTransformer : Transformer<StakingUiState> {
|
|||
prevButton = getPrevButton(prevState),
|
||||
extraButtons = getExtraButtons(prevState),
|
||||
txUrl = (confirmState?.transactionDoneState as? TransactionDoneState.Content)?.txUrl,
|
||||
onTextClick = urlOpener::openUrl,
|
||||
)
|
||||
} else {
|
||||
NavigationButtonsState.Empty
|
||||
|
|
|
|||
|
|
@ -28,6 +28,9 @@ import androidx.compose.ui.text.withStyle
|
|||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.Density
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
|
||||
import com.tangem.common.ui.navigationButtons.NavigationPrimaryButton
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.containers.FooterContainer
|
||||
import com.tangem.core.ui.components.inputrow.InputRowDefault
|
||||
|
|
@ -56,14 +59,21 @@ import kotlinx.collections.immutable.ImmutableList
|
|||
private const val BANNER_BLOCK_KEY = "BannerBlock"
|
||||
private const val STAKING_REWARD_BLOCK_KEY = "StakingRewardBlock"
|
||||
private const val ACTIVE_STAKING_BLOCK_KEY = "ActiveStakingBlock"
|
||||
private const val STAKE_PRIMARY_BUTTON_KEY = "StakePrimaryButton"
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, clickIntents: StakingClickIntents) {
|
||||
internal fun StakingInitialInfoContent(
|
||||
state: StakingStates.InitialInfoState,
|
||||
buttonState: NavigationButtonsState,
|
||||
clickIntents: StakingClickIntents,
|
||||
) {
|
||||
if (state !is StakingStates.InitialInfoState.Data) return
|
||||
|
||||
LazyColumn(
|
||||
verticalArrangement = alignLastToBottom(),
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(TangemTheme.colors.background.secondary)
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
|
|
@ -105,6 +115,10 @@ internal fun StakingInitialInfoContent(state: StakingStates.InitialInfoState, cl
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
item(STAKE_PRIMARY_BUTTON_KEY) {
|
||||
StakeButtonBlock(buttonState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -236,6 +250,21 @@ private fun ActiveStakingBlock(groups: ImmutableList<BalanceGroupedState>, onCli
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StakeButtonBlock(buttonState: NavigationButtonsState) {
|
||||
val state = buttonState as? NavigationButtonsState.Data
|
||||
val primaryButton = state?.primaryButton
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
state?.onTextClick?.let { StakingTosText(it) }
|
||||
NavigationPrimaryButton(primaryButton = primaryButton)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun getCaption(balanceType: BalanceType, balance: BalanceState): TextReference {
|
||||
return when (balanceType) {
|
||||
|
|
@ -289,6 +318,24 @@ private val textGradientColors = listOf(
|
|||
Color(0xff8fb4df),
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun alignLastToBottom() = remember {
|
||||
object : Arrangement.Vertical {
|
||||
override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) {
|
||||
var currentOffset = 0
|
||||
|
||||
sizes.forEachIndexed { index, size ->
|
||||
if (index == sizes.lastIndex) {
|
||||
outPositions[index] = totalSize - size
|
||||
} else {
|
||||
outPositions[index] = currentOffset
|
||||
currentOffset += size
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
@Preview(showBackground = true, widthDp = 360)
|
||||
|
|
@ -300,6 +347,7 @@ private fun StakingInitialInfoContent_Preview(
|
|||
TangemThemePreview {
|
||||
StakingInitialInfoContent(
|
||||
state = feeState,
|
||||
buttonState = NavigationButtonsState.Empty,
|
||||
clickIntents = StakingClickIntentsStub,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import com.tangem.common.ui.amountScreen.AmountScreenContent
|
|||
import com.tangem.common.ui.bottomsheet.permission.GiveTxPermissionBottomSheet
|
||||
import com.tangem.common.ui.bottomsheet.permission.state.GiveTxPermissionBottomSheetConfig
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButtonsBlock
|
||||
import com.tangem.common.ui.navigationButtons.NavigationButtonsState
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
|
||||
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
|
|
@ -55,7 +56,8 @@ internal fun StakingScreen(uiState: StakingUiState) {
|
|||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
NavigationButtonsBlock(
|
||||
buttonState = uiState.buttonsState,
|
||||
buttonState = uiState.buttonsState.takeUnless { uiState.currentStep == StakingStep.InitialInfo }
|
||||
?: NavigationButtonsState.Empty,
|
||||
footerText = confirmationState?.footerText.takeIf { uiState.currentStep == StakingStep.Confirmation },
|
||||
modifier = Modifier.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
|
|
@ -153,6 +155,7 @@ private fun StakingScreenContent(uiState: StakingUiState, modifier: Modifier = M
|
|||
when (state) {
|
||||
StakingStep.InitialInfo -> StakingInitialInfoContent(
|
||||
state = uiState.initialInfoState,
|
||||
buttonState = uiState.buttonsState,
|
||||
clickIntents = uiState.clickIntents,
|
||||
)
|
||||
StakingStep.RewardsValidators -> {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.features.staking.impl.presentation.ui
|
||||
|
||||
import androidx.compose.foundation.text.ClickableText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import com.tangem.core.ui.extensions.appendColored
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.staking.impl.R
|
||||
|
||||
private const val TERMS_OF_USE_KEY = "termsOfUse"
|
||||
private const val PRIVACY_POLICY_KEY = "privacyPolicy"
|
||||
|
||||
private const val TERMS_OF_USE_URL = "https://docs.stakek.it/docs/terms-of-use"
|
||||
private const val PRIVACY_POLICY_URL = "https://docs.stakek.it/docs/privacy-policy"
|
||||
|
||||
@Composable
|
||||
internal fun StakingTosText(onTextClick: (String) -> Unit) {
|
||||
val termsOfUse = stringResource(R.string.common_terms_of_use)
|
||||
val privacyPolicy = stringResource(R.string.common_privacy_policy)
|
||||
val tosText = stringResource(R.string.staking_legal, termsOfUse, privacyPolicy)
|
||||
|
||||
val clickableAnnotation = buildAnnotatedString {
|
||||
append(tosText.substringBefore(termsOfUse))
|
||||
|
||||
pushStringAnnotation(TERMS_OF_USE_KEY, "")
|
||||
appendColored(termsOfUse, TangemTheme.colors.text.accent)
|
||||
pop()
|
||||
|
||||
append(tosText.substringAfter(termsOfUse).substringBefore(privacyPolicy))
|
||||
|
||||
pushStringAnnotation(PRIVACY_POLICY_KEY, "")
|
||||
appendColored(privacyPolicy, TangemTheme.colors.text.accent)
|
||||
pop()
|
||||
}
|
||||
|
||||
ClickableText(
|
||||
text = clickableAnnotation,
|
||||
style = TangemTheme.typography.caption2.copy(
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
textAlign = TextAlign.Center,
|
||||
),
|
||||
onClick = { offset ->
|
||||
clickableAnnotation.getStringAnnotations(
|
||||
tag = TERMS_OF_USE_KEY,
|
||||
start = offset,
|
||||
end = offset,
|
||||
).firstOrNull()?.let {
|
||||
onTextClick(TERMS_OF_USE_URL)
|
||||
}
|
||||
|
||||
clickableAnnotation.getStringAnnotations(
|
||||
tag = PRIVACY_POLICY_KEY,
|
||||
start = offset,
|
||||
end = offset,
|
||||
).firstOrNull()?.let {
|
||||
onTextClick(PRIVACY_POLICY_URL)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue