Updated on 2026-08-14
This commit is contained in:
parent
76e17fc21f
commit
f9cc3decfb
12 changed files with 353 additions and 144 deletions
|
|
@ -95,7 +95,7 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
companion object Key {
|
||||
const val BLOCKCHAIN = "blockchain"
|
||||
const val BLOCKCHAIN = "Blockchain"
|
||||
const val TOKEN = "Token"
|
||||
const val SOURCE = "Source"
|
||||
const val BALANCE = "Balance"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import com.tangem.datasource.api.express.models.TangemExpressValues.EMPTY_CONTRA
|
|||
import com.tangem.datasource.api.express.models.response.Asset
|
||||
import com.tangem.datasource.exchangeservice.swap.ExpressServiceLoader
|
||||
import com.tangem.datasource.local.token.ExpressAssetsStore
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.exchange.ExchangeableState
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
|
||||
|
|
@ -97,10 +99,11 @@ internal class DefaultRampManager(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun availableForSwap(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
return runCatching { getExchangeableFlag(userWalletId, cryptoCurrency) && !cryptoCurrency.isCustom }
|
||||
.getOrNull()
|
||||
?: false
|
||||
override suspend fun availableForSwap(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExchangeableState {
|
||||
return runCatching { getExchangeableState(userWalletId, cryptoCurrency) }.getOrNull() ?: ExchangeableState.Error
|
||||
}
|
||||
|
||||
override fun getBuyInitializationStatus(): Flow<ExchangeServiceInitializationStatus> {
|
||||
|
|
@ -127,14 +130,27 @@ internal class DefaultRampManager(
|
|||
return expressServiceLoader.getInitializationStatus(userWalletId)
|
||||
}
|
||||
|
||||
private suspend fun getExchangeableFlag(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean {
|
||||
private suspend fun getExchangeableState(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
): ExchangeableState {
|
||||
return withContext(dispatchers.io) {
|
||||
val asset = expressServiceLoader.getInitializationStatus(userWalletId)
|
||||
.value
|
||||
.getOrNull()
|
||||
?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
when (val asset = expressServiceLoader.getInitializationStatus(userWalletId).value) {
|
||||
is Lce.Error -> ExchangeableState.Error
|
||||
is Lce.Loading -> ExchangeableState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = asset.getOrNull()?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
foundAsset?.exchangeAvailable?.toExchangeableState() ?: ExchangeableState.AssetNotFound
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
asset?.exchangeAvailable ?: false
|
||||
private fun Boolean.toExchangeableState(): ExchangeableState {
|
||||
return if (this) {
|
||||
ExchangeableState.Exchangeable
|
||||
} else {
|
||||
ExchangeableState.NotExchangeable
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -34,7 +34,11 @@ fun ScenarioUnavailabilityReason.getUnavailabilityReasonText(): TextReference {
|
|||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
}
|
||||
ScenarioUnavailabilityReason.Unreachable -> {
|
||||
is ScenarioUnavailabilityReason.TokenNoQuotes,
|
||||
ScenarioUnavailabilityReason.SingleWallet,
|
||||
is ScenarioUnavailabilityReason.ExpressUnreachable,
|
||||
ScenarioUnavailabilityReason.Unreachable,
|
||||
-> {
|
||||
resourceReference(
|
||||
id = R.string.token_button_unavailability_generic_description,
|
||||
)
|
||||
|
|
@ -45,6 +49,21 @@ fun ScenarioUnavailabilityReason.getUnavailabilityReasonText(): TextReference {
|
|||
ScenarioUnavailabilityReason.UsedOutdatedData -> {
|
||||
resourceReference(id = R.string.token_button_unavailability_reason_out_of_date_balance)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.AssetNotFound -> resourceReference(
|
||||
id = R.string.token_button_unavailability_reason_not_exchangeable,
|
||||
formatArgs = wrappedList(unavailabilityReason.cryptoCurrencyName),
|
||||
)
|
||||
is ScenarioUnavailabilityReason.CustomToken -> {
|
||||
resourceReference(
|
||||
id = R.string
|
||||
.token_button_unavailability_reason_custom_token,
|
||||
)
|
||||
}
|
||||
is ScenarioUnavailabilityReason.DataLoading,
|
||||
is ScenarioUnavailabilityReason.ExpressLoading,
|
||||
-> {
|
||||
resourceReference(id = R.string.token_button_unavailability_reason_loading)
|
||||
}
|
||||
ScenarioUnavailabilityReason.None -> {
|
||||
throw IllegalArgumentException("The unavailability reason must be other than None")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,7 +175,7 @@ sealed class AnalyticsParam {
|
|||
}
|
||||
|
||||
companion object Key {
|
||||
const val BLOCKCHAIN = "blockchain"
|
||||
const val BLOCKCHAIN = "Blockchain"
|
||||
const val TOKEN_PARAM = "Token"
|
||||
const val SOURCE = "Source"
|
||||
const val BALANCE = "Balance"
|
||||
|
|
@ -210,5 +210,6 @@ sealed class AnalyticsParam {
|
|||
const val RESIDENCE = "Residence"
|
||||
const val PAYMENT_METHOD = "Payment Method"
|
||||
const val WATCHED = "Watched"
|
||||
const val ACTION = "Action"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.tangem.domain.exchange
|
||||
|
||||
sealed interface ExchangeableState {
|
||||
data object Exchangeable : ExchangeableState
|
||||
data object NotExchangeable : ExchangeableState
|
||||
data object Loading : ExchangeableState
|
||||
data object Error : ExchangeableState
|
||||
data object AssetNotFound : ExchangeableState
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ interface RampStateManager {
|
|||
status: CryptoCurrencyStatus,
|
||||
): Either<ScenarioUnavailabilityReason, Unit>
|
||||
|
||||
suspend fun availableForSwap(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): Boolean
|
||||
suspend fun availableForSwap(userWalletId: UserWalletId, cryptoCurrency: CryptoCurrency): ExchangeableState
|
||||
|
||||
suspend fun fetchBuyServiceData()
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ sealed class ScenarioUnavailabilityReason {
|
|||
|
||||
data object UsedOutdatedData : ScenarioUnavailabilityReason()
|
||||
|
||||
data object DataLoading : ScenarioUnavailabilityReason()
|
||||
|
||||
// staking-specific
|
||||
data class StakingUnavailable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
|
|
@ -19,8 +21,32 @@ sealed class ScenarioUnavailabilityReason {
|
|||
data class BuyUnavailable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
// swap-specific
|
||||
/**
|
||||
* False for this token in asset request
|
||||
*/
|
||||
data class NotExchangeable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
data class CustomToken(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
data class TokenNoQuotes(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
data object SingleWallet : ScenarioUnavailabilityReason()
|
||||
|
||||
/**
|
||||
* Express request failed
|
||||
*/
|
||||
data class ExpressUnreachable(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
/**
|
||||
* Assets still loading
|
||||
*/
|
||||
data class ExpressLoading(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
/**
|
||||
* No token in asset list returned from express
|
||||
*/
|
||||
data class AssetNotFound(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
// sell-specific
|
||||
data class NotSupportedBySellService(val cryptoCurrencyName: String) : ScenarioUnavailabilityReason()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
package com.tangem.domain.tokens.model.analytics
|
||||
|
||||
import com.tangem.core.analytics.models.AnalyticsEvent
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.ACTION
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.BLOCKCHAIN
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.STATUS
|
||||
import com.tangem.core.analytics.models.AnalyticsParam.Key.TOKEN_PARAM
|
||||
import com.tangem.domain.tokens.model.ScenarioUnavailabilityReason
|
||||
|
||||
/**
|
||||
|
|
@ -34,29 +38,89 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
params = mapOf("Token" to token),
|
||||
)
|
||||
|
||||
class ButtonBuy(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Buy",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
sealed class ButtonWithParams(
|
||||
event: String,
|
||||
token: String,
|
||||
blockchain: String,
|
||||
status: String?,
|
||||
) : TokenScreenAnalyticsEvent(
|
||||
event = event,
|
||||
params = buildMap {
|
||||
put(TOKEN_PARAM, token)
|
||||
put(BLOCKCHAIN, blockchain)
|
||||
status?.let { put(STATUS, it) }
|
||||
},
|
||||
) {
|
||||
|
||||
class ButtonSell(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Sell",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
class ButtonBuy(
|
||||
token: String,
|
||||
blockchain: String,
|
||||
status: String?,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Buy",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
|
||||
class ButtonExchange(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Exchange",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
class ButtonSell(
|
||||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Sell",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
|
||||
class ButtonSend(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Send",
|
||||
params = mapOf("Token" to token),
|
||||
)
|
||||
class ButtonExchange(
|
||||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Exchange",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
|
||||
class ButtonReceive(token: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Button - Receive",
|
||||
params = mapOf("Token" to token),
|
||||
class ButtonSend(
|
||||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Send",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
|
||||
class ButtonReceive(
|
||||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
) : ButtonWithParams(
|
||||
event = "Button - Receive",
|
||||
token = token,
|
||||
status = status,
|
||||
blockchain = blockchain,
|
||||
)
|
||||
}
|
||||
|
||||
class ActionButtonDisabled(
|
||||
token: String,
|
||||
status: String,
|
||||
blockchain: String,
|
||||
action: String,
|
||||
) : TokenScreenAnalyticsEvent(
|
||||
event = "Action Button Disabled",
|
||||
params = mapOf(
|
||||
TOKEN_PARAM to token,
|
||||
STATUS to status,
|
||||
BLOCKCHAIN to blockchain,
|
||||
ACTION to action,
|
||||
),
|
||||
)
|
||||
|
||||
class ButtonCopyAddress(token: String) : TokenScreenAnalyticsEvent(
|
||||
|
|
@ -89,30 +153,20 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
params = mapOf("Token" to token),
|
||||
)
|
||||
|
||||
class NoticeActionInactive(token: String, tokenAction: TokenAction, reason: String) : TokenScreenAnalyticsEvent(
|
||||
event = "Notice - Action Inactive",
|
||||
params = buildMap {
|
||||
put("Token", token)
|
||||
put("Action", tokenAction.action)
|
||||
if (reason.isNotEmpty()) {
|
||||
put("Reason", reason)
|
||||
}
|
||||
},
|
||||
) {
|
||||
sealed class TokenAction(val action: String) {
|
||||
data object BuyAction : TokenAction("Buy")
|
||||
data object SellAction : TokenAction("Sell")
|
||||
data object SwapAction : TokenAction("Swap")
|
||||
data object SendAction : TokenAction("Send")
|
||||
data object ReceiveAction : TokenAction("Receive")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val AVAILABLE = "Available"
|
||||
private const val UNAVAILABLE = "Unavailable"
|
||||
private const val EMPTY = "Empty"
|
||||
private const val PENDING = "Pending"
|
||||
private const val CACHING = "Caching"
|
||||
private const val CUSTOM_TOKEN = "Custom Token"
|
||||
private const val BLOCKCHAIN_UNREACHABLE = "Blockchain Unreachable"
|
||||
private const val ASSET_NOT_FOUND = "Asset NotFound"
|
||||
private const val ASSET_LOADING = "Assets Loading"
|
||||
private const val ASSETS_ERROR = "Assets Error"
|
||||
private const val NO_QUOTE = "Quotes Unavailable"
|
||||
private const val SINGLE_WALLET = "Single Wallet"
|
||||
private const val LOADING = "Loading"
|
||||
|
||||
fun ScenarioUnavailabilityReason.toReasonAnalyticsText(): String {
|
||||
return when (this) {
|
||||
|
|
@ -125,9 +179,15 @@ sealed class TokenScreenAnalyticsEvent(
|
|||
is ScenarioUnavailabilityReason.EmptyBalance -> EMPTY
|
||||
is ScenarioUnavailabilityReason.PendingTransaction -> PENDING
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData -> CACHING
|
||||
ScenarioUnavailabilityReason.None,
|
||||
ScenarioUnavailabilityReason.Unreachable,
|
||||
-> ""
|
||||
ScenarioUnavailabilityReason.None -> AVAILABLE
|
||||
ScenarioUnavailabilityReason.Unreachable -> BLOCKCHAIN_UNREACHABLE
|
||||
is ScenarioUnavailabilityReason.AssetNotFound -> ASSET_NOT_FOUND
|
||||
is ScenarioUnavailabilityReason.CustomToken -> CUSTOM_TOKEN
|
||||
is ScenarioUnavailabilityReason.ExpressLoading -> ASSET_LOADING
|
||||
is ScenarioUnavailabilityReason.ExpressUnreachable -> ASSETS_ERROR
|
||||
ScenarioUnavailabilityReason.SingleWallet -> SINGLE_WALLET
|
||||
is ScenarioUnavailabilityReason.TokenNoQuotes -> NO_QUOTE
|
||||
ScenarioUnavailabilityReason.DataLoading -> LOADING
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.exchange.ExchangeableState
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.promo.PromoRepository
|
||||
|
|
@ -24,7 +25,7 @@ import kotlinx.coroutines.withTimeoutOrNull
|
|||
*
|
||||
* @property rampManager Ramp manager to check ramp availability
|
||||
*/
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
class GetCryptoCurrencyActionsUseCase(
|
||||
private val rampManager: RampStateManager,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
|
|
@ -181,26 +182,11 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
// swap
|
||||
if (userWallet.isMultiCurrency) {
|
||||
val isExchangeable = withTimeoutOrNull(REQUEST_EXCHANGE_DATA_TIMEOUT) {
|
||||
rampManager.availableForSwap(userWallet.walletId, cryptoCurrency)
|
||||
} ?: false
|
||||
if (isExchangeable && cryptoCurrencyStatus.value !is CryptoCurrencyStatus.NoQuote) {
|
||||
val swapStoriesEnabled = swapFeatureToggles.isPromoStoriesEnabled
|
||||
activeList.add(
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.None,
|
||||
showBadge = shouldShowSwapStories && swapStoriesEnabled,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.NotExchangeable(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
),
|
||||
)
|
||||
}
|
||||
val swapActionState = getSwapUnavailabilityReason(userWallet, cryptoCurrencyStatus, shouldShowSwapStories)
|
||||
if (swapActionState.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(swapActionState)
|
||||
} else {
|
||||
disabledList.add(swapActionState)
|
||||
}
|
||||
|
||||
// buy
|
||||
|
|
@ -289,7 +275,8 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
// swap
|
||||
val isSwapAvailable = with(cryptoCurrencyStatus.value.sources) {
|
||||
val sources = cryptoCurrencyStatus.value.sources
|
||||
val isSwapAvailable = with(sources) {
|
||||
quoteSource.isActual() && networkSource.isActual()
|
||||
}
|
||||
|
||||
|
|
@ -297,8 +284,11 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = if (isSwapAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else {
|
||||
} else if (sources.networkSource == StatusSource.ONLY_CACHE) {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
} else {
|
||||
// CACHE source always when loading
|
||||
ScenarioUnavailabilityReason.DataLoading
|
||||
},
|
||||
showBadge = false,
|
||||
),
|
||||
|
|
@ -376,6 +366,54 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getSwapUnavailabilityReason(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
shouldShowSwapStories: Boolean,
|
||||
): TokenActionsState.ActionState {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
return if (userWallet.isMultiCurrency) {
|
||||
if (cryptoCurrency.isCustom) {
|
||||
return TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.CustomToken(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.NoQuote) {
|
||||
return TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.TokenNoQuotes(cryptoCurrency.name),
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
val exchangeableState = withTimeoutOrNull(REQUEST_EXCHANGE_DATA_TIMEOUT) {
|
||||
rampManager.availableForSwap(userWallet.walletId, cryptoCurrency)
|
||||
} ?: ExchangeableState.Loading
|
||||
val swapStoriesEnabled = swapFeatureToggles.isPromoStoriesEnabled
|
||||
val currencyName = cryptoCurrency.name
|
||||
|
||||
val reason = when (exchangeableState) {
|
||||
ExchangeableState.Exchangeable -> {
|
||||
ScenarioUnavailabilityReason.None
|
||||
}
|
||||
ExchangeableState.AssetNotFound -> ScenarioUnavailabilityReason.AssetNotFound(currencyName)
|
||||
ExchangeableState.Error -> ScenarioUnavailabilityReason.ExpressUnreachable(currencyName)
|
||||
ExchangeableState.Loading -> ScenarioUnavailabilityReason.ExpressLoading(currencyName)
|
||||
ExchangeableState.NotExchangeable -> ScenarioUnavailabilityReason.NotExchangeable(currencyName)
|
||||
}
|
||||
|
||||
val isShowBadge = reason == ScenarioUnavailabilityReason.None && shouldShowSwapStories && swapStoriesEnabled
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = reason,
|
||||
showBadge = isShowBadge,
|
||||
)
|
||||
} else {
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.SingleWallet,
|
||||
showBadge = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun isAddressAvailable(networkAddress: NetworkAddress?): Boolean {
|
||||
return networkAddress != null && networkAddress.defaultAddress.value.isNotEmpty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.core.lce.Lce
|
||||
import com.tangem.domain.core.utils.getOrElse
|
||||
import com.tangem.domain.exchange.ExchangeableState
|
||||
import com.tangem.domain.exchange.RampStateManager
|
||||
import com.tangem.domain.settings.usercountry.GetUserCountryUseCase
|
||||
import com.tangem.domain.settings.usercountry.models.UserCountry
|
||||
|
|
@ -241,10 +242,21 @@ internal class OnrampTokenListModel @Inject constructor(
|
|||
val isAvailable = rampStateManager.availableForSwap(
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = status.currency,
|
||||
)
|
||||
).isSwapAvailable() && !status.currency.isCustom
|
||||
|
||||
isAvailable && status.value !is CryptoCurrencyStatus.NoQuote
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ExchangeableState.isSwapAvailable(): Boolean {
|
||||
return when (this) {
|
||||
ExchangeableState.AssetNotFound,
|
||||
ExchangeableState.Error,
|
||||
ExchangeableState.Loading,
|
||||
ExchangeableState.NotExchangeable,
|
||||
-> false
|
||||
ExchangeableState.Exchangeable -> true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,10 +45,7 @@ import com.tangem.domain.staking.GetYieldUseCase
|
|||
import com.tangem.domain.staking.model.StakingAvailability
|
||||
import com.tangem.domain.tokens.*
|
||||
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.model.*
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
|
||||
|
|
@ -281,6 +278,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
sendButtonsEvents(it.states)
|
||||
internalUiState.value = stateFactory.getManageButtonsState(actions = it.states)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
|
|
@ -288,6 +286,21 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
.saveIn(buttonsJobHolder)
|
||||
}
|
||||
|
||||
private fun sendButtonsEvents(states: List<TokenActionsState.ActionState>) {
|
||||
// for now send only for swap
|
||||
val swapState = states.firstOrNull { it is TokenActionsState.ActionState.Swap } ?: return
|
||||
if (swapState.unavailabilityReason != ScenarioUnavailabilityReason.None) {
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ActionButtonDisabled(
|
||||
token = cryptoCurrency.symbol,
|
||||
status = swapState.unavailabilityReason.toReasonAnalyticsText(),
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
action = "Swap",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateWarnings(cryptoCurrencyStatus: CryptoCurrencyStatus) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
getCurrencyWarningsUseCase.invoke(
|
||||
|
|
@ -472,14 +485,15 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onBuyClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonBuy(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = TokenScreenAnalyticsEvent.NoticeActionInactive.TokenAction.BuyAction,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -512,7 +526,13 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onBuyCoinClick(cryptoCurrency: CryptoCurrency) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonBuy(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = null,
|
||||
),
|
||||
)
|
||||
router.openTokenDetails(userWalletId = userWalletId, currency = cryptoCurrency)
|
||||
}
|
||||
|
||||
|
|
@ -528,14 +548,15 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSendClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonSend(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = TokenScreenAnalyticsEvent.NoticeActionInactive.TokenAction.SendAction,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -554,17 +575,18 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onReceiveClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return
|
||||
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = TokenScreenAnalyticsEvent.NoticeActionInactive.TokenAction.ReceiveAction,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonReceive(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
|
||||
|
||||
internalUiState.value = stateFactory.getStateWithReceiveBottomSheet(
|
||||
|
|
@ -583,12 +605,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onStakeClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = null,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -619,14 +636,15 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSellClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonSell(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = TokenScreenAnalyticsEvent.NoticeActionInactive.TokenAction.SellAction,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -643,14 +661,15 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSwapClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrency.symbol))
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonExchange(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = TokenScreenAnalyticsEvent.NoticeActionInactive.TokenAction.SwapAction,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -972,21 +991,8 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
internalUiState.value = stateFactory.getStateWithClosedBottomSheet()
|
||||
}
|
||||
|
||||
private fun handleUnavailabilityReason(
|
||||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
tokenSymbol: String,
|
||||
tokenAction: TokenScreenAnalyticsEvent.NoticeActionInactive.TokenAction?,
|
||||
): Boolean {
|
||||
private fun handleUnavailabilityReason(unavailabilityReason: ScenarioUnavailabilityReason): Boolean {
|
||||
if (unavailabilityReason == ScenarioUnavailabilityReason.None) return false
|
||||
if (tokenAction != null) {
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.NoticeActionInactive(
|
||||
token = tokenSymbol,
|
||||
tokenAction = tokenAction,
|
||||
reason = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
internalUiState.value = stateFactory.getStateWithActionButtonErrorDialog(unavailabilityReason)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
|||
import com.tangem.domain.tokens.model.*
|
||||
import com.tangem.domain.tokens.model.analytics.TokenReceiveAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.AVAILABLE
|
||||
import com.tangem.domain.tokens.model.analytics.TokenScreenAnalyticsEvent.Companion.toReasonAnalyticsText
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
|
||||
|
|
@ -133,7 +135,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrencyStatus.currency.symbol),
|
||||
event = TokenScreenAnalyticsEvent.ButtonWithParams.ButtonSend(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
|
@ -151,7 +157,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
val userWalletId = stateHolder.getSelectedWalletId()
|
||||
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrencyStatus.currency.symbol),
|
||||
event = TokenScreenAnalyticsEvent.ButtonWithParams.ButtonReceive(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
status = AVAILABLE,
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
analyticsEventHandler.send(
|
||||
|
|
@ -290,7 +300,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonSell(cryptoCurrencyStatus.currency.symbol),
|
||||
event = TokenScreenAnalyticsEvent.ButtonWithParams.ButtonSell(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
|
@ -314,7 +328,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
val userWallet = getSelectedWalletSyncUseCase.unwrap() ?: return
|
||||
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonBuy(cryptoCurrencyStatus.currency.symbol),
|
||||
event = TokenScreenAnalyticsEvent.ButtonWithParams.ButtonBuy(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
|
@ -349,7 +367,11 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
unavailabilityReason: ScenarioUnavailabilityReason,
|
||||
) {
|
||||
analyticsEventHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrencyStatus.currency.symbol),
|
||||
event = TokenScreenAnalyticsEvent.ButtonWithParams.ButtonExchange(
|
||||
token = cryptoCurrencyStatus.currency.symbol,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
blockchain = cryptoCurrencyStatus.currency.network.name,
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason)) return
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue