Updated on 2026-08-14
This commit is contained in:
parent
d32b504a80
commit
4021127ae9
19 changed files with 543 additions and 160 deletions
|
|
@ -73,9 +73,9 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
walletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
states = createListOfActions(
|
||||
userWallet,
|
||||
coinStatus,
|
||||
cryptoCurrencyStatus,
|
||||
userWallet = userWallet,
|
||||
coinStatus = coinStatus,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -91,7 +91,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
): List<TokenActionsState.ActionState> {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.MissedDerivation) {
|
||||
return listOf(TokenActionsState.ActionState.HideToken(true))
|
||||
return listOf(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Unreachable) {
|
||||
return getActionsForUnreachableCurrency(cryptoCurrencyStatus)
|
||||
|
|
@ -102,52 +102,61 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
|
||||
// copy address
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.CopyAddress(true))
|
||||
activeList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
|
||||
// receive
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.Receive(true))
|
||||
activeList.add(TokenActionsState.ActionState.Receive(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
|
||||
// send
|
||||
if (
|
||||
isSendDisabled(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
) {
|
||||
disabledList.add(TokenActionsState.ActionState.Send(false))
|
||||
val sendUnavailabilityReason = getSendUnavailabilityReason(
|
||||
userWalletId = userWallet.walletId,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
if (sendUnavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(TokenActionsState.ActionState.Send(sendUnavailabilityReason))
|
||||
} else {
|
||||
activeList.add(TokenActionsState.ActionState.Send(true))
|
||||
disabledList.add(TokenActionsState.ActionState.Send(sendUnavailabilityReason))
|
||||
}
|
||||
|
||||
// swap
|
||||
if (userWallet.isMultiCurrency) {
|
||||
if (marketCryptoCurrencyRepository.isExchangeable(userWallet.walletId, cryptoCurrency)) {
|
||||
activeList.add(TokenActionsState.ActionState.Swap(true))
|
||||
activeList.add(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Swap(false))
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.NotExchangeable(cryptoCurrency.name),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// buy
|
||||
if (rampManager.availableForBuy(cryptoCurrency)) {
|
||||
activeList.add(TokenActionsState.ActionState.Buy(true))
|
||||
activeList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Buy(false))
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.BuyUnavailable(cryptoCurrency.symbol)),
|
||||
)
|
||||
}
|
||||
|
||||
// sell
|
||||
if (rampManager.availableForSell(cryptoCurrency)) {
|
||||
activeList.add(TokenActionsState.ActionState.Sell(true))
|
||||
activeList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Sell(false))
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Sell(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.SellUnavailable(cryptoCurrency.name),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
// hide
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(true))
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
|
||||
return activeList + disabledList
|
||||
}
|
||||
|
|
@ -155,63 +164,140 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
private fun getActionsForUnreachableCurrency(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
): List<TokenActionsState.ActionState> {
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val disabledList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val actionsList = mutableListOf<TokenActionsState.ActionState>()
|
||||
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.CopyAddress(true))
|
||||
actionsList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
if (rampManager.availableForBuy(cryptoCurrencyStatus.currency)) {
|
||||
activeList.add(TokenActionsState.ActionState.Buy(true))
|
||||
actionsList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Buy(false))
|
||||
actionsList.add(
|
||||
TokenActionsState.ActionState.Buy(
|
||||
ScenarioUnavailabilityReason.BuyUnavailable(
|
||||
cryptoCurrencyName = cryptoCurrencyStatus.currency.name,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
disabledList.add(TokenActionsState.ActionState.Send(false))
|
||||
disabledList.add(TokenActionsState.ActionState.Swap(false))
|
||||
disabledList.add(TokenActionsState.ActionState.Sell(false))
|
||||
actionsList.add(TokenActionsState.ActionState.Send(ScenarioUnavailabilityReason.NoQuotes))
|
||||
actionsList.add(TokenActionsState.ActionState.Swap(ScenarioUnavailabilityReason.NoQuotes))
|
||||
actionsList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.NoQuotes))
|
||||
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
activeList.add(TokenActionsState.ActionState.Receive(true))
|
||||
actionsList.add(TokenActionsState.ActionState.Receive(ScenarioUnavailabilityReason.None))
|
||||
}
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(true))
|
||||
return activeList + disabledList
|
||||
actionsList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
return actionsList
|
||||
}
|
||||
|
||||
private suspend fun isSendDisabled(
|
||||
private suspend fun getSendUnavailabilityReason(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
val feePaidCurrency = currenciesRepository.getFeePaidCurrency(userWalletId, cryptoCurrencyStatus.currency)
|
||||
val notEnoughBalanceForFee = isNotEnoughBalanceForFee(
|
||||
feePaidCurrency = feePaidCurrency,
|
||||
): ScenarioUnavailabilityReason {
|
||||
val insufficientFundsForFee = insufficientFundsForFee(
|
||||
userWalletId = userWalletId,
|
||||
tokenStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
return cryptoCurrencyStatus.value.amount.isNullOrZero() ||
|
||||
notEnoughBalanceForFee ||
|
||||
|
||||
return when {
|
||||
cryptoCurrencyStatus.value.amount.isNullOrZero() -> {
|
||||
ScenarioUnavailabilityReason.EmptyBalance
|
||||
}
|
||||
insufficientFundsForFee != null -> {
|
||||
ScenarioUnavailabilityReason.InsufficientFundsForFee(
|
||||
currencyName = insufficientFundsForFee.currencyName,
|
||||
networkName = insufficientFundsForFee.networkName,
|
||||
feeCurrencyName = insufficientFundsForFee.feeCurrencyName,
|
||||
feeCurrencySymbol = insufficientFundsForFee.feeCurrencySymbol,
|
||||
)
|
||||
}
|
||||
currenciesRepository.hasPendingTransactions(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
coinStatus = coinStatus,
|
||||
)
|
||||
) -> {
|
||||
ScenarioUnavailabilityReason.PendingTransaction(coinStatus?.currency?.symbol.orEmpty())
|
||||
}
|
||||
else -> {
|
||||
ScenarioUnavailabilityReason.None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isNotEnoughBalanceForFee(
|
||||
feePaidCurrency: FeePaidCurrency,
|
||||
private suspend fun insufficientFundsForFee(
|
||||
userWalletId: UserWalletId,
|
||||
tokenStatus: CryptoCurrencyStatus,
|
||||
coinStatus: CryptoCurrencyStatus?,
|
||||
): Boolean {
|
||||
return if (sendFeatureToggles.isRedesignedSendEnabled) {
|
||||
tokenStatus.value.amount.isZero()
|
||||
} else {
|
||||
when (feePaidCurrency) {
|
||||
FeePaidCurrency.Coin -> !tokenStatus.value.amount.isZero() && coinStatus?.value?.amount.isZero()
|
||||
FeePaidCurrency.SameCurrency -> tokenStatus.value.amount.isZero()
|
||||
is FeePaidCurrency.Token -> {
|
||||
val feePaidTokenBalance = feePaidCurrency.balance
|
||||
!tokenStatus.value.amount.isZero() && feePaidTokenBalance.isZero()
|
||||
): FeeInfo? {
|
||||
val feePaidCurrency = currenciesRepository.getFeePaidCurrency(userWalletId, tokenStatus.currency)
|
||||
coinStatus ?: return null
|
||||
return when {
|
||||
sendFeatureToggles.isRedesignedSendEnabled && !tokenStatus.value.amount.isZero() -> {
|
||||
null
|
||||
}
|
||||
feePaidCurrency is FeePaidCurrency.Coin &&
|
||||
!tokenStatus.value.amount.isZero() &&
|
||||
coinStatus.value.amount.isZero() -> {
|
||||
FeeInfo(
|
||||
currencyName = tokenStatus.currency.name,
|
||||
networkName = coinStatus.currency.network.name,
|
||||
feeCurrencyName = coinStatus.currency.name,
|
||||
feeCurrencySymbol = coinStatus.currency.symbol,
|
||||
)
|
||||
}
|
||||
feePaidCurrency is FeePaidCurrency.SameCurrency && !tokenStatus.value.amount.isZero() -> {
|
||||
FeeInfo(
|
||||
currencyName = tokenStatus.currency.name,
|
||||
networkName = coinStatus.currency.network.name,
|
||||
feeCurrencyName = coinStatus.currency.name,
|
||||
feeCurrencySymbol = coinStatus.currency.symbol,
|
||||
)
|
||||
}
|
||||
feePaidCurrency is FeePaidCurrency.Token -> {
|
||||
val feePaidTokenBalance = feePaidCurrency.balance
|
||||
val amount = tokenStatus.value.amount ?: return null
|
||||
if (!amount.isZero() && feePaidTokenBalance.isZero()) {
|
||||
constructTokenBalanceNotEnoughWarning(
|
||||
userWalletId = userWalletId,
|
||||
tokenStatus = tokenStatus,
|
||||
feePaidToken = feePaidCurrency,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun constructTokenBalanceNotEnoughWarning(
|
||||
userWalletId: UserWalletId,
|
||||
tokenStatus: CryptoCurrencyStatus,
|
||||
feePaidToken: FeePaidCurrency.Token,
|
||||
): FeeInfo {
|
||||
val token = currenciesRepository
|
||||
.getMultiCurrencyWalletCurrenciesSync(userWalletId)
|
||||
.find {
|
||||
it is CryptoCurrency.Token &&
|
||||
it.contractAddress.equals(feePaidToken.contractAddress, ignoreCase = true) &&
|
||||
it.network.derivationPath == tokenStatus.currency.network.derivationPath
|
||||
}
|
||||
return if (token != null) {
|
||||
FeeInfo(
|
||||
currencyName = tokenStatus.currency.name,
|
||||
networkName = token.network.name,
|
||||
feeCurrencyName = feePaidToken.name,
|
||||
feeCurrencySymbol = feePaidToken.symbol,
|
||||
)
|
||||
} else {
|
||||
FeeInfo(
|
||||
currencyName = tokenStatus.currency.name,
|
||||
networkName = tokenStatus.currency.network.name,
|
||||
feeCurrencyName = feePaidToken.name,
|
||||
feeCurrencySymbol = feePaidToken.symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -222,4 +308,11 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
private fun BigDecimal?.isZero(): Boolean {
|
||||
return this?.signum() == 0
|
||||
}
|
||||
|
||||
data class FeeInfo(
|
||||
val currencyName: String,
|
||||
val networkName: String,
|
||||
val feeCurrencyName: String,
|
||||
val feeCurrencySymbol: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
sealed class ScenarioUnavailabilityReason {
|
||||
data object None : ScenarioUnavailabilityReason()
|
||||
|
||||
// send-specific
|
||||
data class PendingTransaction(val cryptoCurrencySymbol: String) : ScenarioUnavailabilityReason()
|
||||
data object EmptyBalance : ScenarioUnavailabilityReason()
|
||||
data class InsufficientFundsForFee(
|
||||
val currencyName: String,
|
||||
val networkName: String,
|
||||
val feeCurrencyName: String,
|
||||
val feeCurrencySymbol: String,
|
||||
) : ScenarioUnavailabilityReason()
|
||||
|
||||
// buy-specific
|
||||
data class BuyUnavailable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
// swap-specific
|
||||
data class NotExchangeable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
// sell-specific
|
||||
data class SellUnavailable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
data object NoQuotes : ScenarioUnavailabilityReason()
|
||||
}
|
||||
|
|
@ -10,20 +10,20 @@ data class TokenActionsState(
|
|||
|
||||
sealed class ActionState {
|
||||
|
||||
abstract val enabled: Boolean
|
||||
abstract val unavailabilityReason: ScenarioUnavailabilityReason
|
||||
|
||||
data class Buy(override val enabled: Boolean) : ActionState()
|
||||
data class Buy(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
|
||||
data class CopyAddress(override val enabled: Boolean) : ActionState()
|
||||
data class CopyAddress(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
|
||||
data class Sell(override val enabled: Boolean) : ActionState()
|
||||
data class Sell(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
|
||||
data class Receive(override val enabled: Boolean) : ActionState()
|
||||
data class Receive(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
|
||||
data class Swap(override val enabled: Boolean) : ActionState()
|
||||
data class Swap(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
|
||||
data class Send(override val enabled: Boolean) : ActionState()
|
||||
data class Send(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
|
||||
data class HideToken(override val enabled: Boolean) : ActionState()
|
||||
data class HideToken(override val unavailabilityReason: ScenarioUnavailabilityReason) : ActionState()
|
||||
}
|
||||
}
|
||||
|
|
@ -72,10 +72,10 @@ internal object TokenDetailsPreviewData {
|
|||
)
|
||||
|
||||
private val actionButtons = persistentListOf(
|
||||
TokenDetailsActionButton.Buy(enabled = true, onClick = {}),
|
||||
TokenDetailsActionButton.Send(enabled = true, onClick = {}),
|
||||
TokenDetailsActionButton.Buy(dimContent = false, onClick = {}),
|
||||
TokenDetailsActionButton.Send(dimContent = false, onClick = {}),
|
||||
TokenDetailsActionButton.Receive(onClick = {}),
|
||||
TokenDetailsActionButton.Swap(enabled = true, onClick = {}),
|
||||
TokenDetailsActionButton.Swap(dimContent = false, onClick = {}),
|
||||
)
|
||||
|
||||
val balanceLoading = TokenDetailsBalanceBlockState.Loading(actionButtons = actionButtons)
|
||||
|
|
|
|||
|
|
@ -14,36 +14,35 @@ internal sealed class TokenDetailsActionButton(val config: ActionButtonConfig) {
|
|||
/**
|
||||
* Buy
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Buy button is clicked
|
||||
*/
|
||||
data class Buy(val enabled: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
data class Buy(val dimContent: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_buy),
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Send
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Send button is clicked
|
||||
*/
|
||||
data class Send(val enabled: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
data class Send(val dimContent: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_send),
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Receive
|
||||
*
|
||||
* @property onClick lambda be invoked when Receive button is clicked
|
||||
*/
|
||||
data class Receive(override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
|
|
@ -58,30 +57,30 @@ internal sealed class TokenDetailsActionButton(val config: ActionButtonConfig) {
|
|||
/**
|
||||
* Sell
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Sell button is clicked
|
||||
*/
|
||||
data class Sell(val enabled: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
data class Sell(val dimContent: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_sell),
|
||||
iconResId = R.drawable.ic_currency_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Swap
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Swap button is clicked
|
||||
*/
|
||||
data class Swap(val enabled: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
data class Swap(val dimContent: Boolean, override val onClick: () -> Unit) : TokenDetailsActionButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.swapping_swap_action),
|
||||
iconResId = R.drawable.ic_exchange_vertical_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ internal data class TokenDetailsDialogConfig(
|
|||
|
||||
sealed class DialogContentConfig {
|
||||
|
||||
abstract val title: TextReference
|
||||
abstract val title: TextReference?
|
||||
abstract val message: TextReference
|
||||
abstract val confirmButtonConfig: ButtonConfig
|
||||
abstract val cancelButtonConfig: ButtonConfig?
|
||||
|
|
@ -77,5 +77,22 @@ internal data class TokenDetailsDialogConfig(
|
|||
onClick = onConfirmClick,
|
||||
)
|
||||
}
|
||||
|
||||
data class DisabledButtonReasonDialogConfig(
|
||||
val text: TextReference,
|
||||
val onConfirmClick: () -> Unit,
|
||||
) : DialogContentConfig() {
|
||||
|
||||
override val title = null
|
||||
|
||||
override val message: TextReference = text
|
||||
|
||||
override val cancelButtonConfig = null
|
||||
|
||||
override val confirmButtonConfig: ButtonConfig = ButtonConfig(
|
||||
text = TextReference.Res(R.string.common_ok),
|
||||
onClick = onConfirmClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
|
||||
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenDetailsState
|
||||
import com.tangem.feature.tokendetails.presentation.tokendetails.state.components.TokenDetailsActionButton
|
||||
|
|
@ -26,19 +27,33 @@ internal class TokenDetailsActionButtonsConverter(
|
|||
.mapNotNull { action ->
|
||||
when (action) {
|
||||
is TokenActionsState.ActionState.Buy -> {
|
||||
TokenDetailsActionButton.Buy(enabled = action.enabled, onClick = clickIntents::onBuyClick)
|
||||
TokenDetailsActionButton.Buy(
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = { clickIntents.onBuyClick(action.unavailabilityReason) },
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Receive -> {
|
||||
TokenDetailsActionButton.Receive(onClick = clickIntents::onReceiveClick)
|
||||
TokenDetailsActionButton.Receive(
|
||||
onClick = { clickIntents.onReceiveClick(action.unavailabilityReason) },
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Sell -> {
|
||||
TokenDetailsActionButton.Sell(enabled = action.enabled, onClick = clickIntents::onSellClick)
|
||||
TokenDetailsActionButton.Sell(
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = { clickIntents.onSellClick(action.unavailabilityReason) },
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Send -> {
|
||||
TokenDetailsActionButton.Send(enabled = action.enabled, onClick = clickIntents::onSendClick)
|
||||
TokenDetailsActionButton.Send(
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = { clickIntents.onSendClick(action.unavailabilityReason) },
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Swap -> {
|
||||
TokenDetailsActionButton.Swap(enabled = action.enabled, onClick = clickIntents::onSwapClick)
|
||||
TokenDetailsActionButton.Swap(
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = { clickIntents.onSwapClick(action.unavailabilityReason) },
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
|
|
|
|||
|
|
@ -84,11 +84,11 @@ internal class TokenDetailsSkeletonStateConverter(
|
|||
|
||||
private fun createButtons(): ImmutableList<TokenDetailsActionButton> {
|
||||
return persistentListOf(
|
||||
TokenDetailsActionButton.Buy(enabled = false, onClick = {}),
|
||||
TokenDetailsActionButton.Send(enabled = false, onClick = {}),
|
||||
TokenDetailsActionButton.Buy(dimContent = false, onClick = {}),
|
||||
TokenDetailsActionButton.Send(dimContent = false, onClick = {}),
|
||||
TokenDetailsActionButton.Receive(onClick = {}),
|
||||
TokenDetailsActionButton.Sell(enabled = false, onClick = {}),
|
||||
TokenDetailsActionButton.Swap(enabled = false, onClick = {}),
|
||||
TokenDetailsActionButton.Sell(dimContent = false, onClick = {}),
|
||||
TokenDetailsActionButton.Swap(dimContent = false, onClick = {}),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,16 +10,12 @@ import com.tangem.core.ui.components.transactions.state.TransactionState
|
|||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.event.consumedEvent
|
||||
import com.tangem.core.ui.event.triggeredEvent
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryListError
|
||||
|
|
@ -39,8 +35,9 @@ import com.tangem.utils.Provider
|
|||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import java.lang.IllegalArgumentException
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
@Suppress("TooManyFunctions", "LargeClass")
|
||||
internal class TokenDetailsStateFactory(
|
||||
private val currentStateProvider: Provider<TokenDetailsState>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
|
|
@ -177,6 +174,19 @@ internal class TokenDetailsStateFactory(
|
|||
)
|
||||
}
|
||||
|
||||
fun getStateWithActionButtonErrorDialog(unavailabilityReason: ScenarioUnavailabilityReason): TokenDetailsState {
|
||||
return currentStateProvider().copy(
|
||||
dialogConfig = TokenDetailsDialogConfig(
|
||||
isShow = true,
|
||||
onDismissRequest = clickIntents::onDismissDialog,
|
||||
content = TokenDetailsDialogConfig.DialogContentConfig.DisabledButtonReasonDialogConfig(
|
||||
text = getUnavailabilityReasonText(unavailabilityReason),
|
||||
onConfirmClick = clickIntents::onDismissDialog,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun getRefreshingState(): TokenDetailsState {
|
||||
return refreshStateConverter.convert(true)
|
||||
}
|
||||
|
|
@ -321,4 +331,60 @@ internal class TokenDetailsStateFactory(
|
|||
}.toImmutableList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
|
||||
return when (unavailabilityReason) {
|
||||
// send
|
||||
is ScenarioUnavailabilityReason.PendingTransaction -> {
|
||||
resourceReference(
|
||||
id = R.string.warning_send_blocked_pending_transactions_message,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencySymbol),
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.EmptyBalance -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_empty_balance,
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.InsufficientFundsForFee -> {
|
||||
resourceReference(
|
||||
id = R.string.warning_send_blocked_funds_for_fee_message,
|
||||
formatArgs = wrappedList(
|
||||
unavailabilityReason.currencyName,
|
||||
unavailabilityReason.networkName,
|
||||
unavailabilityReason.currencyName,
|
||||
unavailabilityReason.feeCurrencyName,
|
||||
unavailabilityReason.feeCurrencySymbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.BuyUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_buy_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.NotExchangeable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_not_exchangeable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.SellUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_sell_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.NoQuotes -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_no_quotes,
|
||||
)
|
||||
}
|
||||
|
||||
ScenarioUnavailabilityReason.None -> {
|
||||
throw IllegalArgumentException("The unavailability reason must be other than None")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ private fun TokenDetailsDialog(config: TokenDetailsDialogConfig) {
|
|||
onClick = config.content.confirmButtonConfig.onClick,
|
||||
),
|
||||
onDismissDialog = config.onDismissRequest,
|
||||
title = config.content.title.resolveReference(),
|
||||
title = config.content.title?.resolveReference(),
|
||||
dismissButton = config.content.cancelButtonConfig?.let { cancelButtonConfig ->
|
||||
DialogButton(
|
||||
title = cancelButtonConfig.text.resolveReference(),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels
|
||||
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
@Suppress("TooManyFunctions")
|
||||
|
|
@ -8,13 +9,15 @@ interface TokenDetailsClickIntents {
|
|||
|
||||
fun onBackClick()
|
||||
|
||||
fun onSendClick()
|
||||
fun onReceiveClick(unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onReceiveClick()
|
||||
fun onSendClick(unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onSellClick()
|
||||
fun onSwapClick(unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onSwapClick()
|
||||
fun onBuyClick(unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onSellClick(unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onDismissDialog()
|
||||
|
||||
|
|
@ -24,8 +27,6 @@ interface TokenDetailsClickIntents {
|
|||
|
||||
fun onRefreshSwipe()
|
||||
|
||||
fun onBuyClick()
|
||||
|
||||
fun onBuyCoinClick(cryptoCurrency: CryptoCurrency)
|
||||
|
||||
fun onReloadClick()
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
|||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
|
|
@ -388,9 +389,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
router.popBackStack()
|
||||
}
|
||||
|
||||
override fun onBuyClick() {
|
||||
override fun onBuyClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrency.symbol))
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
showErrorIfDemoModeOrElse {
|
||||
val status = cryptoCurrencyStatus ?: return@showErrorIfDemoModeOrElse
|
||||
|
||||
|
|
@ -417,9 +420,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
updateTxHistory(refresh = true, showItemsLoading = true)
|
||||
}
|
||||
|
||||
override fun onSendClick() {
|
||||
override fun onSendClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrency.symbol))
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
sendCurrency(status = cryptoCurrencyStatus ?: return)
|
||||
}
|
||||
|
||||
|
|
@ -485,9 +490,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onReceiveClick() {
|
||||
override fun onReceiveClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened)
|
||||
|
|
@ -523,9 +530,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSellClick() {
|
||||
override fun onSellClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrency.symbol))
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
showErrorIfDemoModeOrElse {
|
||||
val status = cryptoCurrencyStatus ?: return@showErrorIfDemoModeOrElse
|
||||
|
||||
|
|
@ -538,9 +547,11 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSwapClick() {
|
||||
override fun onSwapClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrency.symbol))
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrency))
|
||||
}
|
||||
|
||||
|
|
@ -697,7 +708,15 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
shouldShowSwapPromoTokenUseCase.neverToShow()
|
||||
analyticsEventsHandler.send(TokenSwapPromoAnalyticsEvent.Exchange(cryptoCurrency.symbol))
|
||||
}
|
||||
onSwapClick()
|
||||
onSwapClick(ScenarioUnavailabilityReason.None)
|
||||
}
|
||||
|
||||
private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean {
|
||||
if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false
|
||||
|
||||
uiState = stateFactory.getStateWithActionButtonErrorDialog(unavailabilityReason)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ internal sealed interface WalletAlertState {
|
|||
}
|
||||
|
||||
data class DefaultAlert(
|
||||
override val title: TextReference,
|
||||
override val title: TextReference?,
|
||||
override val message: TextReference,
|
||||
override val onConfirmClick: (() -> Unit)?,
|
||||
) : Basic()
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
/** Is click enabled */
|
||||
abstract val enabled: Boolean
|
||||
|
||||
/** Whether to dim content */
|
||||
abstract val dimContent: Boolean
|
||||
|
||||
/** Lambda be invoked when manage button is clicked */
|
||||
abstract val onClick: () -> Unit
|
||||
|
||||
|
|
@ -25,29 +28,40 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
* Buy
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Buy button is clicked
|
||||
*/
|
||||
data class Buy(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_buy),
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
),
|
||||
)
|
||||
data class Buy(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) :
|
||||
WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_buy),
|
||||
iconResId = R.drawable.ic_plus_24,
|
||||
onClick = onClick,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
/**
|
||||
* Send
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Send button is clicked
|
||||
*/
|
||||
data class Send(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
|
||||
data class Send(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_send),
|
||||
iconResId = R.drawable.ic_arrow_up_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -56,12 +70,16 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
*
|
||||
* @property onClick lambda be invoked when Receive button is clicked
|
||||
*/
|
||||
data class Receive(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
|
||||
data class Receive(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_receive),
|
||||
iconResId = R.drawable.ic_arrow_down_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -69,14 +87,19 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
* Sell
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Sell button is clicked
|
||||
*/
|
||||
data class Sell(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
|
||||
data class Sell(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.common_sell),
|
||||
iconResId = R.drawable.ic_currency_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -84,14 +107,19 @@ internal sealed class WalletManageButton(val config: ActionButtonConfig) {
|
|||
* Swap
|
||||
*
|
||||
* @property enabled button click availability
|
||||
* @property dimContent determines whether the button content will be dimmed
|
||||
* @property onClick lambda be invoked when Swap button is clicked
|
||||
*/
|
||||
data class Swap(override val enabled: Boolean, override val onClick: () -> Unit) : WalletManageButton(
|
||||
data class Swap(
|
||||
override val enabled: Boolean,
|
||||
override val dimContent: Boolean,
|
||||
override val onClick: () -> Unit,
|
||||
) : WalletManageButton(
|
||||
config = ActionButtonConfig(
|
||||
text = TextReference.Res(id = R.string.swapping_swap_action),
|
||||
iconResId = R.drawable.ic_exchange_vertical_24,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
dimContent = dimContent,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -85,10 +85,10 @@ internal class InitializeWalletsTransformer(
|
|||
|
||||
private fun createDisabledButtons(): PersistentList<WalletManageButton> {
|
||||
return persistentListOf(
|
||||
WalletManageButton.Buy(enabled = false, onClick = {}),
|
||||
WalletManageButton.Send(enabled = false, onClick = {}),
|
||||
WalletManageButton.Receive(enabled = false, onClick = {}),
|
||||
WalletManageButton.Sell(enabled = false, onClick = {}),
|
||||
WalletManageButton.Receive(enabled = false, dimContent = false, onClick = {}),
|
||||
WalletManageButton.Send(enabled = false, dimContent = false, onClick = {}),
|
||||
WalletManageButton.Buy(enabled = false, dimContent = false, onClick = {}),
|
||||
WalletManageButton.Sell(enabled = false, dimContent = false, onClick = {}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.state.transformers
|
||||
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.model.WalletManageButton
|
||||
|
|
@ -43,26 +44,47 @@ internal class SetCryptoCurrencyActionsTransformer(
|
|||
when (action) {
|
||||
is TokenActionsState.ActionState.Buy -> {
|
||||
WalletManageButton.Buy(
|
||||
enabled = action.enabled,
|
||||
onClick = { clickIntents.onBuyClick(cryptoCurrencyStatus) },
|
||||
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onBuyClick(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
unavailabilityReason = action.unavailabilityReason,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Receive -> {
|
||||
WalletManageButton.Receive(
|
||||
enabled = action.enabled,
|
||||
onClick = { clickIntents.onReceiveClick(cryptoCurrencyStatus) },
|
||||
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onReceiveClick(cryptoCurrencyStatus = cryptoCurrencyStatus)
|
||||
},
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Sell -> {
|
||||
WalletManageButton.Sell(
|
||||
enabled = action.enabled,
|
||||
onClick = { clickIntents.onSellClick(cryptoCurrencyStatus) },
|
||||
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onSellClick(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
unavailabilityReason = action.unavailabilityReason,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
is TokenActionsState.ActionState.Send -> {
|
||||
WalletManageButton.Send(
|
||||
enabled = action.enabled,
|
||||
onClick = { clickIntents.onSendClick(cryptoCurrencyStatus) },
|
||||
enabled = action.unavailabilityReason == ScenarioUnavailabilityReason.None,
|
||||
dimContent = action.unavailabilityReason != ScenarioUnavailabilityReason.None,
|
||||
onClick = {
|
||||
clickIntents.onSendClick(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
unavailabilityReason = action.unavailabilityReason,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.transformers.convert
|
|||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenActionsState
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -51,7 +52,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.Buy -> {
|
||||
title = resourceReference(R.string.common_buy)
|
||||
icon = R.drawable.ic_plus_24
|
||||
action = { clickIntents.onBuyClick(cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onBuyClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Receive -> {
|
||||
title = resourceReference(R.string.common_receive)
|
||||
|
|
@ -61,17 +62,17 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
is TokenActionsState.ActionState.Sell -> {
|
||||
title = resourceReference(R.string.common_sell)
|
||||
icon = R.drawable.ic_currency_24
|
||||
action = { clickIntents.onSellClick(cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onSellClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Send -> {
|
||||
title = resourceReference(R.string.common_send)
|
||||
icon = R.drawable.ic_arrow_up_24
|
||||
action = { clickIntents.onSendClick(cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onSendClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
|
||||
}
|
||||
is TokenActionsState.ActionState.Swap -> {
|
||||
title = resourceReference(R.string.swapping_swap_action)
|
||||
icon = R.drawable.ic_exchange_horizontal_24
|
||||
action = { clickIntents.onSwapClick(cryptoCurrencyStatus) }
|
||||
action = { clickIntents.onSwapClick(cryptoCurrencyStatus, ScenarioUnavailabilityReason.None) }
|
||||
}
|
||||
is TokenActionsState.ActionState.CopyAddress -> {
|
||||
title = resourceReference(R.string.common_copy_address)
|
||||
|
|
@ -90,7 +91,7 @@ internal class MultiWalletCurrencyActionsConverter(
|
|||
iconResId = icon,
|
||||
onClick = action,
|
||||
isWarning = actionsState is TokenActionsState.ActionState.HideToken,
|
||||
enabled = actionsState.enabled,
|
||||
enabled = actionsState.unavailabilityReason == ScenarioUnavailabilityReason.None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -45,7 +45,7 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
|
|||
walletCardState = userWallet.toLoadingWalletCardState(),
|
||||
warnings = persistentListOf(),
|
||||
bottomSheetConfig = null,
|
||||
buttons = createDisabledButtons(),
|
||||
buttons = createDimmedButtons(),
|
||||
marketPriceBlockState = MarketPriceBlockState.Loading(currencySymbol = currencySymbol),
|
||||
txHistoryState = TxHistoryState.Content(
|
||||
contentItems = MutableStateFlow(
|
||||
|
|
@ -86,12 +86,12 @@ internal class WalletLoadingStateFactory(private val clickIntents: WalletClickIn
|
|||
)
|
||||
}
|
||||
|
||||
private fun createDisabledButtons(): PersistentList<WalletManageButton> {
|
||||
private fun createDimmedButtons(): PersistentList<WalletManageButton> {
|
||||
return persistentListOf(
|
||||
WalletManageButton.Buy(enabled = false, onClick = {}),
|
||||
WalletManageButton.Send(enabled = false, onClick = {}),
|
||||
WalletManageButton.Receive(enabled = false, onClick = {}),
|
||||
WalletManageButton.Sell(enabled = false, onClick = {}),
|
||||
WalletManageButton.Receive(enabled = true, dimContent = true, onClick = {}),
|
||||
WalletManageButton.Send(enabled = true, dimContent = true, onClick = {}),
|
||||
WalletManageButton.Buy(enabled = true, dimContent = true, onClick = {}),
|
||||
WalletManageButton.Sell(enabled = true, dimContent = true, onClick = {}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,7 @@ import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBot
|
|||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
|
||||
import com.tangem.core.ui.extensions.WrappedList
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.extenstions.unwrap
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
|
|
@ -19,6 +18,7 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
|
|
@ -39,11 +39,18 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.launch
|
||||
import java.lang.IllegalArgumentException
|
||||
import javax.inject.Inject
|
||||
|
||||
interface WalletCurrencyActionsClickIntents {
|
||||
|
||||
fun onSendClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
fun onSendClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus, unavailabilityReason: ScenarioUnavailabilityReason)
|
||||
|
||||
fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
|
|
@ -53,12 +60,6 @@ interface WalletCurrencyActionsClickIntents {
|
|||
|
||||
fun onPerformHideToken(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus)
|
||||
|
||||
fun onExploreClick()
|
||||
}
|
||||
|
||||
|
|
@ -82,13 +83,18 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
) : BaseWalletClickIntents(), WalletCurrencyActionsClickIntents {
|
||||
|
||||
override fun onSendClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onSendClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWallet.walletId))
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
|
|
@ -274,11 +280,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSellClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onSellClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
showErrorIfDemoModeOrElse {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
|
|
@ -291,13 +302,18 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onBuyClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onBuyClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
showErrorIfDemoModeOrElse {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
|
|
@ -311,11 +327,16 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
override fun onSwapClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
override fun onSwapClick(
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency))
|
||||
}
|
||||
|
||||
|
|
@ -402,4 +423,79 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
action()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean {
|
||||
if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false
|
||||
|
||||
val unavailabilityReasonText = getUnavailabilityReasonText(unavailabilityReason)
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
walletEventSender.send(
|
||||
event = WalletEvent.ShowAlert(
|
||||
state = WalletAlertState.DefaultAlert(
|
||||
title = null,
|
||||
message = unavailabilityReasonText,
|
||||
onConfirmClick = null,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun getUnavailabilityReasonText(unavailabilityReason: ScenarioUnavailabilityReason): TextReference {
|
||||
return when (unavailabilityReason) {
|
||||
// send
|
||||
is ScenarioUnavailabilityReason.PendingTransaction -> {
|
||||
resourceReference(
|
||||
id = R.string.warning_send_blocked_pending_transactions_message,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencySymbol),
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.EmptyBalance -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_empty_balance,
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.InsufficientFundsForFee -> {
|
||||
resourceReference(
|
||||
id = R.string.warning_send_blocked_funds_for_fee_message,
|
||||
formatArgs = wrappedList(
|
||||
unavailabilityReason.currencyName,
|
||||
unavailabilityReason.networkName,
|
||||
unavailabilityReason.currencyName,
|
||||
unavailabilityReason.feeCurrencyName,
|
||||
unavailabilityReason.feeCurrencySymbol,
|
||||
),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.BuyUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_buy_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.NotExchangeable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_not_exchangeable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.SellUnavailable -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_sell_unavailable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.NoQuotes -> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_no_quotes,
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.None -> {
|
||||
throw IllegalArgumentException("The unavailability reason must be other than None")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue