Updated on 2026-08-14

This commit is contained in:
Tangem 2024-06-05 17:23:23 +02:00
parent 27a864a0a9
commit 0e29df4ce9
45 changed files with 731 additions and 137 deletions

View file

@ -260,7 +260,7 @@ internal object TokenDetailsPreviewData {
bottomSheetConfig = null,
isBalanceHidden = false,
isMarketPriceAvailable = false,
isStakingAvailable = false,
isStakingBlockShown = false,
event = consumedEvent(),
)
@ -296,7 +296,7 @@ internal object TokenDetailsPreviewData {
bottomSheetConfig = null,
isBalanceHidden = false,
isMarketPriceAvailable = true,
isStakingAvailable = true,
isStakingBlockShown = true,
event = consumedEvent(),
)

View file

@ -27,6 +27,6 @@ internal data class TokenDetailsState(
val bottomSheetConfig: TangemBottomSheetConfig?,
val isBalanceHidden: Boolean,
val isMarketPriceAvailable: Boolean,
val isStakingAvailable: Boolean,
val isStakingBlockShown: Boolean,
val event: StateEvent<TextReference>,
)

View file

@ -91,4 +91,19 @@ internal sealed class TokenDetailsActionButton(val config: ActionButtonConfig) {
dimContent = dimContent,
),
)
/**
* Staking
*
* @property dimContent determines whether the button content will be dimmed
* @property onClick lambda be invoked when Swap button is clicked
*/
data class Stake(val dimContent: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_stake),
iconResId = R.drawable.ic_arrow_down_24, // TODO staking
onClick = onClick,
dimContent = dimContent,
),
)
}

View file

@ -38,6 +38,12 @@ internal class TokenDetailsActionButtonsConverter(
onLongClick = clickIntents::onCopyAddress,
)
}
is TokenActionsState.ActionState.Stake -> {
TokenDetailsActionButton.Stake(
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
onClick = { clickIntents.onStakeClick(action.unavailabilityReason) },
)
}
is TokenActionsState.ActionState.Sell -> {
TokenDetailsActionButton.Sell(
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,

View file

@ -7,7 +7,6 @@ import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.networkIconResId
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.staking.model.StakingAvailability
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.feature.tokendetails.presentation.tokendetails.state.*
@ -17,7 +16,6 @@ import com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels.Toke
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
import com.tangem.features.tokendetails.impl.R
import com.tangem.lib.crypto.BlockchainUtils.isBitcoin
import com.tangem.utils.Provider
import com.tangem.utils.converter.Converter
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -27,7 +25,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
internal class TokenDetailsSkeletonStateConverter(
private val clickIntents: TokenDetailsClickIntents,
private val featureToggles: TokenDetailsFeatureToggles,
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
) : Converter<CryptoCurrency, TokenDetailsState> {
private val iconStateConverter by lazy { TokenDetailsIconStateConverter() }
@ -67,7 +64,7 @@ internal class TokenDetailsSkeletonStateConverter(
bottomSheetConfig = null,
isBalanceHidden = true,
isMarketPriceAvailable = value.id.rawCurrencyId != null,
isStakingAvailable = stakingAvailabilityProvider.invoke() is StakingAvailability.Available,
isStakingBlockShown = false,
event = consumedEvent(),
)
}

View file

@ -44,7 +44,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
internal class TokenDetailsStateFactory(
private val currentStateProvider: Provider<TokenDetailsState>,
private val appCurrencyProvider: Provider<AppCurrency>,
private val stakingAvailabilityProvider: Provider<StakingAvailability>,
private val clickIntents: TokenDetailsClickIntents,
private val featureToggles: TokenDetailsFeatureToggles,
symbol: String,
@ -55,7 +54,6 @@ internal class TokenDetailsStateFactory(
TokenDetailsSkeletonStateConverter(
clickIntents = clickIntents,
featureToggles = featureToggles,
stakingAvailabilityProvider = stakingAvailabilityProvider,
)
}
@ -211,6 +209,12 @@ internal class TokenDetailsStateFactory(
)
}
fun getStateWithUpdatedStakingAvailability(stakingAvailability: StakingAvailability): TokenDetailsState {
return currentStateProvider().copy(
isStakingBlockShown = stakingAvailability != StakingAvailability.Unavailable,
)
}
fun getStateWithStaking(stakingEither: Either<Throwable, StakingEntryInfo>): TokenDetailsState {
return currentStateProvider().copy(
stakingBlockState = stakingStateConverter.convert(stakingEither),
@ -369,6 +373,12 @@ internal class TokenDetailsStateFactory(
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
return when (unavailabilityReason) {
is ScenarioUnavailabilityReason.StakingUnavailable -> {
resourceReference(
id = R.string.token_button_unavailability_reason_staking_unavailable,
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
)
}
is ScenarioUnavailabilityReason.PendingTransaction -> {
when (unavailabilityReason.withdrawalScenario) {
ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference(

View file

@ -135,7 +135,7 @@ internal fun TokenDetailsScreen(state: TokenDetailsState) {
)
}
if (state.isStakingAvailable) {
if (state.isStakingBlockShown) {
item(
key = StakingBlockState::class.java,
contentType = StakingBlockState::class.java,

View file

@ -12,6 +12,8 @@ interface TokenDetailsClickIntents {
fun onReceiveClick(unavailabilityReason: ScenarioUnavailabilityReason)
fun onStakeClick(unavailabilityReason: ScenarioUnavailabilityReason)
fun onSendClick(unavailabilityReason: ScenarioUnavailabilityReason)
fun onSwapClick(unavailabilityReason: ScenarioUnavailabilityReason)

View file

@ -142,9 +142,6 @@ internal class TokenDetailsViewModel @Inject constructor(
private val stateFactory = TokenDetailsStateFactory(
currentStateProvider = Provider { uiState },
appCurrencyProvider = Provider(selectedAppCurrencyFlow::value),
stakingAvailabilityProvider = Provider {
getStakingAvailabilityUseCase.invoke(cryptoCurrency.network.id.value)
},
clickIntents = this,
symbol = cryptoCurrency.symbol,
decimals = cryptoCurrency.decimals,
@ -369,7 +366,11 @@ internal class TokenDetailsViewModel @Inject constructor(
private fun updateStakingInfo() {
viewModelScope.launch(dispatchers.io) {
val stakingAvailability = getStakingAvailabilityUseCase(cryptoCurrency.network.id.value)
val stakingAvailability = getStakingAvailabilityUseCase(
cryptoCurrencyId = cryptoCurrency.id,
symbol = cryptoCurrency.symbol,
)
uiState = stateFactory.getStateWithUpdatedStakingAvailability(stakingAvailability)
if (stakingAvailability is StakingAvailability.Available) {
val stakingInfo = getStakingEntryInfoUseCase(stakingAvailability.integrationId)
uiState = stateFactory.getStateWithStaking(stakingInfo)
@ -524,6 +525,10 @@ internal class TokenDetailsViewModel @Inject constructor(
}
}
override fun onStakeClick(unavailabilityReason: ScenarioUnavailabilityReason) {
Timber.e("Not implemented yet")
}
override fun onGenerateExtendedKey() {
viewModelScope.launch(dispatchers.main) {
val extendedKey = getExtendedPublicKeyForCurrencyUseCase(

View file

@ -89,6 +89,19 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
),
)
data class Stake(
override val enabled: Boolean,
override val dimContent: Boolean,
override val onClick: () -> Unit,
) : WalletManageButton(
config = ActionButtonConfig(
text = TextReference.Res(id = R.string.common_stake),
iconResId = R.drawable.ic_arrow_down_24, // TODO staking
onClick = onClick,
dimContent = dimContent,
),
)
/**
* Sell
*

View file

@ -64,6 +64,7 @@ internal class SetRefreshStateTransformer(
is WalletManageButton.Send -> button.copy(enabled = isButtonsEnabled)
is WalletManageButton.Sell -> button.copy(enabled = isButtonsEnabled)
is WalletManageButton.Receive -> button
is WalletManageButton.Stake -> null
is WalletManageButton.Swap -> null
}
}

View file

@ -59,6 +59,11 @@ internal class MultiWalletCurrencyActionsConverter(
icon = R.drawable.ic_arrow_down_24
action = { clickIntents.onReceiveClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Stake -> {
title = resourceReference(R.string.common_stake)
icon = R.drawable.ic_arrow_down_24 // TODO staking replace icon
action = { clickIntents.onStakeClick(cryptoCurrencyStatus) }
}
is TokenActionsState.ActionState.Sell -> {
title = resourceReference(R.string.common_sell)
icon = R.drawable.ic_currency_24

View file

@ -44,6 +44,7 @@ import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
interface WalletCurrencyActionsClickIntents {
@ -58,6 +59,8 @@ interface WalletCurrencyActionsClickIntents {
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
fun onCopyAddressLongClick(cryptoCurrencyStatus: CryptoCurrencyStatus): TextReference?
fun onCopyAddressClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
@ -363,6 +366,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
showErrorIfDemoModeOrElse(action = ::openExplorer)
}
override fun onStakeClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
// TODO staking
Timber.e("Not implemented yet")
}
private fun openExplorer() {
val userWalletId = stateHolder.getSelectedWalletId()
@ -465,6 +473,12 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
return when (unavailabilityReason) {
is ScenarioUnavailabilityReason.StakingUnavailable -> {
resourceReference(
id = R.string.token_button_unavailability_reason_staking_unavailable,
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
)
}
is ScenarioUnavailabilityReason.PendingTransaction -> {
when (unavailabilityReason.withdrawalScenario) {
ScenarioUnavailabilityReason.WithdrawalScenario.SEND -> resourceReference(