Updated on 2026-08-14
This commit is contained in:
commit
0a1bb63501
41 changed files with 783 additions and 358 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"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.network.auth
|
||||
|
||||
import com.tangem.common.CardIdRangeDec
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import com.tangem.datasource.local.preferences.utils.getSyncOrDefault
|
||||
|
|
@ -48,7 +49,12 @@ internal class DefaultExpressAuthProvider(
|
|||
}
|
||||
|
||||
private fun isChangeNow(selectedUserWallet: UserWallet): Boolean {
|
||||
return selectedUserWallet.scanResponse.card.batchId == BATCH_ID_CHANGENOW
|
||||
val changeNowRange = CardIdRangeDec(
|
||||
start = "AF99001800554008",
|
||||
end = "AF99001800559994",
|
||||
)
|
||||
val card = selectedUserWallet.scanResponse.card
|
||||
return card.batchId == BATCH_ID_CHANGENOW || changeNowRange?.contains(card.cardId) == true
|
||||
}
|
||||
|
||||
private fun isPartner(selectedUserWallet: UserWallet): Boolean {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -76,10 +78,6 @@ internal class DefaultRampManager(
|
|||
catch = { raise(ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name)) },
|
||||
)
|
||||
|
||||
ensure(condition = sellSupportedByService) {
|
||||
ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name)
|
||||
}
|
||||
|
||||
val reason = getSendUnavailabilityReason(userWalletId, status)
|
||||
|
||||
ensure(condition = reason is ScenarioUnavailabilityReason.None) {
|
||||
|
|
@ -94,14 +92,19 @@ internal class DefaultRampManager(
|
|||
}
|
||||
}
|
||||
|
||||
ensure(condition = sellSupportedByService) {
|
||||
ScenarioUnavailabilityReason.NotSupportedBySellService(status.currency.name)
|
||||
}
|
||||
|
||||
Unit.right()
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
|
|
@ -128,14 +131,29 @@ 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)
|
||||
.firstOrNull()
|
||||
?.getOrNull()
|
||||
?.find { cryptoCurrency.findAssetPredicate(it) }
|
||||
val initializationStatus = expressServiceLoader.getInitializationStatus(userWalletId).firstOrNull()
|
||||
?: return@withContext ExchangeableState.Loading
|
||||
when (initializationStatus) {
|
||||
is Lce.Error -> ExchangeableState.Error
|
||||
is Lce.Loading -> ExchangeableState.Loading
|
||||
is Lce.Content -> {
|
||||
val foundAsset = initializationStatus.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"
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.tangem.datasource.local.token
|
|||
import androidx.datastore.core.DataStore
|
||||
import com.tangem.datasource.api.stakekit.models.response.model.YieldBalanceWrapperDTO
|
||||
import com.tangem.datasource.local.datastore.RuntimeSharedStore
|
||||
import com.tangem.datasource.local.token.StakingBalanceStore.StakingID
|
||||
import com.tangem.datasource.local.token.converter.YieldBalanceConverter
|
||||
import com.tangem.domain.models.StatusSource
|
||||
import com.tangem.domain.staking.model.stakekit.YieldBalance
|
||||
|
|
@ -26,10 +27,16 @@ internal class DefaultStakingBalanceStore(
|
|||
private val runtimeStore: RuntimeSharedStore<YieldBalanceListByWalletId>,
|
||||
) : StakingBalanceStore {
|
||||
|
||||
override fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>> = channelFlow {
|
||||
override fun get(userWalletId: UserWalletId, stakingIds: List<StakingID>): Flow<Set<YieldBalance>> = channelFlow {
|
||||
val cachedBalances = persistenceStore.data
|
||||
.map {
|
||||
val wrappers = it[userWalletId.stringValue].orEmpty()
|
||||
.filter { wrapper ->
|
||||
stakingIds.any { id ->
|
||||
id.address == wrapper.addresses.address && id.integrationId == wrapper.integrationId
|
||||
}
|
||||
}
|
||||
|
||||
YieldBalanceConverter(isCached = true).convertSet(input = wrappers)
|
||||
}
|
||||
.firstOrNull()
|
||||
|
|
@ -40,33 +47,64 @@ internal class DefaultStakingBalanceStore(
|
|||
}
|
||||
|
||||
runtimeStore.get()
|
||||
.map { it[userWalletId].orEmpty() }
|
||||
.map {
|
||||
it[userWalletId].orEmpty().filter { balance ->
|
||||
stakingIds.any { id ->
|
||||
id.address == balance.address && id.integrationId == balance.integrationId
|
||||
}
|
||||
}
|
||||
.toSet()
|
||||
}
|
||||
.onEach {
|
||||
val mergedBalances = mergeYieldBalances(cachedBalances = cachedBalances, runtimeBalances = it)
|
||||
val mergedBalances = mergeYieldBalances(
|
||||
stakingIds = stakingIds,
|
||||
cachedBalances = cachedBalances,
|
||||
runtimeBalances = it,
|
||||
)
|
||||
|
||||
send(mergedBalances)
|
||||
}
|
||||
.launchIn(scope = this)
|
||||
}
|
||||
|
||||
override fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalance?> {
|
||||
return get(userWalletId).map { balances ->
|
||||
balances.getBalance(address = address, integrationId = integrationId)
|
||||
override fun get(userWalletId: UserWalletId, stakingID: StakingID): Flow<YieldBalance?> {
|
||||
return get(userWalletId = userWalletId, stakingIds = listOf(stakingID)).map { balances ->
|
||||
balances.getBalance(stakingID = stakingID)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>? {
|
||||
return runtimeStore.getSyncOrNull()?.getValue(userWalletId)
|
||||
val runtimeBalances = runtimeStore.getSyncOrNull()?.getValue(userWalletId).orEmpty()
|
||||
val cachedBalances = persistenceStore.data.firstOrNull()?.get(userWalletId.stringValue).orEmpty()
|
||||
|
||||
if (runtimeBalances.isEmpty() && cachedBalances.isEmpty()) return null
|
||||
|
||||
return cachedBalances.mapTo(hashSetOf()) {
|
||||
val cached = YieldBalanceConverter(source = StatusSource.ONLY_CACHE).convert(value = it)
|
||||
val runtime = runtimeBalances.getBalance(address = cached.address, integrationId = cached.integrationId)
|
||||
|
||||
if (runtime == null || runtime is YieldBalance.Error) cached else runtime
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(
|
||||
userWalletId: UserWalletId,
|
||||
address: String,
|
||||
integrationId: String,
|
||||
): YieldBalance? {
|
||||
val balances = getSyncOrNull(userWalletId) ?: return null
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingIds: List<StakingID>): Set<YieldBalance>? {
|
||||
val runtime = runtimeStore.getSyncOrNull()?.getValue(userWalletId)
|
||||
val cached = persistenceStore.data.firstOrNull()?.get(userWalletId.stringValue)
|
||||
|
||||
return balances.getBalance(address, integrationId)
|
||||
if (runtime.isNullOrEmpty() && cached.isNullOrEmpty()) return null
|
||||
|
||||
return mergeYieldBalances(
|
||||
cachedBalances = YieldBalanceConverter(source = StatusSource.ONLY_CACHE)
|
||||
.convertSet(input = cached.orEmpty()),
|
||||
runtimeBalances = runtime.orEmpty(),
|
||||
stakingIds = stakingIds,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingID: StakingID): YieldBalance? {
|
||||
val balances = getSyncOrNull(userWalletId = userWalletId, stakingIds = listOf(stakingID)) ?: return null
|
||||
|
||||
return balances.getBalance(stakingID = stakingID)
|
||||
}
|
||||
|
||||
override suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>) {
|
||||
|
|
@ -88,34 +126,52 @@ internal class DefaultStakingBalanceStore(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun refresh(userWalletId: UserWalletId, addressWithIntegrationIdMap: Map<String, String>) {
|
||||
override suspend fun refresh(userWalletId: UserWalletId, stakingIds: List<StakingID>) {
|
||||
updateRuntimeStore(userWalletId = userWalletId) { saved ->
|
||||
saved.mapTo(hashSetOf()) { balance ->
|
||||
val refreshIntegrationId = addressWithIntegrationIdMap[balance.address]
|
||||
saved.mapTo(hashSetOf()) {
|
||||
val yieldBalance = it.takeIf { balance ->
|
||||
stakingIds.any { id -> balance.integrationId == id.integrationId && balance.address == id.address }
|
||||
}
|
||||
|
||||
if (balance.integrationId == refreshIntegrationId) {
|
||||
when (balance) {
|
||||
is YieldBalance.Data -> balance.copy(source = StatusSource.CACHE)
|
||||
is YieldBalance.Empty -> balance.copy(source = StatusSource.CACHE)
|
||||
is YieldBalance.Error -> balance
|
||||
if (yieldBalance != null) {
|
||||
when (yieldBalance) {
|
||||
is YieldBalance.Data -> yieldBalance.copy(source = StatusSource.CACHE)
|
||||
is YieldBalance.Empty -> yieldBalance.copy(source = StatusSource.CACHE)
|
||||
is YieldBalance.Error -> yieldBalance
|
||||
}
|
||||
} else {
|
||||
balance
|
||||
it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun store(
|
||||
userWalletId: UserWalletId,
|
||||
integrationId: String,
|
||||
address: String,
|
||||
item: YieldBalanceWrapperDTO,
|
||||
) {
|
||||
override suspend fun store(userWalletId: UserWalletId, stakingID: StakingID, item: YieldBalanceWrapperDTO) {
|
||||
coroutineScope {
|
||||
launch {
|
||||
storeInRuntimeStore(userWalletId, integrationId, address, item)
|
||||
storeInPersistenceStore(userWalletId, integrationId, address, item)
|
||||
storeInRuntimeStore(
|
||||
userWalletId = userWalletId,
|
||||
integrationId = stakingID.integrationId,
|
||||
address = stakingID.address,
|
||||
item = item,
|
||||
)
|
||||
|
||||
storeInPersistenceStore(
|
||||
userWalletId = userWalletId,
|
||||
integrationId = stakingID.integrationId,
|
||||
address = stakingID.address,
|
||||
item = item,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun storeSingleYieldBalance(userWalletId: UserWalletId, item: YieldBalance) {
|
||||
runtimeStore.update(default = emptyMap()) { saved ->
|
||||
saved.toMutableMap().apply {
|
||||
this[userWalletId] = saved[userWalletId]
|
||||
?.addOrReplace(item) { it.integrationId == item.integrationId && it.address == item.address }
|
||||
?: setOf(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -178,28 +234,22 @@ internal class DefaultStakingBalanceStore(
|
|||
private fun mergeYieldBalances(
|
||||
cachedBalances: Set<YieldBalance>,
|
||||
runtimeBalances: Set<YieldBalance>,
|
||||
stakingIds: List<StakingID>,
|
||||
): Set<YieldBalance> {
|
||||
if (runtimeBalances.isEmpty()) {
|
||||
return cachedBalances.mapNotNullTo(hashSetOf()) {
|
||||
when (it) {
|
||||
is YieldBalance.Data -> it.copy(source = StatusSource.ONLY_CACHE)
|
||||
is YieldBalance.Empty -> it.copy(source = StatusSource.ONLY_CACHE)
|
||||
is YieldBalance.Error -> null
|
||||
}
|
||||
return stakingIds.mapTo(hashSetOf()) { id ->
|
||||
val runtime = runtimeBalances.getBalance(stakingID = id)
|
||||
|
||||
if (runtime == null || runtime is YieldBalance.Error) {
|
||||
getCachedBalanceIfPossible(cachedBalances = cachedBalances, stakingID = id)
|
||||
} else {
|
||||
runtime
|
||||
}
|
||||
}
|
||||
|
||||
return runtimeBalances
|
||||
.map { runtime ->
|
||||
runtime.takeIf { runtime !is YieldBalance.Error }
|
||||
?: getCachedBalanceIfPossible(cachedBalances, runtime)
|
||||
}
|
||||
.toSet()
|
||||
}
|
||||
|
||||
private fun getCachedBalanceIfPossible(cachedBalances: Set<YieldBalance>, runtime: YieldBalance): YieldBalance {
|
||||
val cached = cachedBalances.getBalance(address = runtime.address, integrationId = runtime.integrationId)
|
||||
?: return runtime
|
||||
private fun getCachedBalanceIfPossible(cachedBalances: Set<YieldBalance>, stakingID: StakingID): YieldBalance {
|
||||
val cached = cachedBalances.getBalance(stakingID)
|
||||
?: return YieldBalance.Error(integrationId = stakingID.address, address = stakingID.integrationId)
|
||||
|
||||
val updatedCached = when (cached) {
|
||||
is YieldBalance.Data -> cached.copy(source = StatusSource.ONLY_CACHE)
|
||||
|
|
@ -207,7 +257,11 @@ internal class DefaultStakingBalanceStore(
|
|||
is YieldBalance.Error -> null
|
||||
}
|
||||
|
||||
return updatedCached ?: runtime
|
||||
return updatedCached ?: YieldBalance.Error(integrationId = stakingID.address, address = stakingID.integrationId)
|
||||
}
|
||||
|
||||
private fun Set<YieldBalance>.getBalance(stakingID: StakingID): YieldBalance? {
|
||||
return getBalance(address = stakingID.address, integrationId = stakingID.integrationId)
|
||||
}
|
||||
|
||||
private fun Set<YieldBalance>.getBalance(address: String?, integrationId: String?): YieldBalance? {
|
||||
|
|
|
|||
|
|
@ -9,23 +9,32 @@ import kotlinx.coroutines.flow.Flow
|
|||
/** Staking balance store */
|
||||
interface StakingBalanceStore {
|
||||
|
||||
/** Get flow of [YieldBalanceList] by [userWalletId] */
|
||||
fun get(userWalletId: UserWalletId): Flow<Set<YieldBalance>>
|
||||
/** Get flow of [YieldBalanceList] by [userWalletId] and [stakingIds] */
|
||||
fun get(userWalletId: UserWalletId, stakingIds: List<StakingID>): Flow<Set<YieldBalance>>
|
||||
|
||||
/** Get flow of [YieldBalance] by [userWalletId], [address] and [integrationId] */
|
||||
fun get(userWalletId: UserWalletId, address: String, integrationId: String): Flow<YieldBalance?>
|
||||
/** Get flow of [YieldBalance] by [userWalletId] and [stakingID] */
|
||||
fun get(userWalletId: UserWalletId, stakingID: StakingID): Flow<YieldBalance?>
|
||||
|
||||
/** Get [YieldBalanceList] synchronously or null by [userWalletId] */
|
||||
/** Get all [YieldBalance] synchronously or null by [userWalletId] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId): Set<YieldBalance>?
|
||||
|
||||
/** Get [YieldBalance] synchronously or null by [userWalletId], [address] and [integrationId] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId, address: String, integrationId: String): YieldBalance?
|
||||
/** Get [YieldBalanceList] synchronously or null by [userWalletId] and [stakingIds] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingIds: List<StakingID>): Set<YieldBalance>?
|
||||
|
||||
/** Get [YieldBalance] synchronously or null by [userWalletId] and [stakingID] */
|
||||
suspend fun getSyncOrNull(userWalletId: UserWalletId, stakingID: StakingID): YieldBalance?
|
||||
|
||||
/** Store [items] by [userWalletId] */
|
||||
suspend fun store(userWalletId: UserWalletId, items: Set<YieldBalanceWrapperDTO>)
|
||||
|
||||
/** Store [item] by [userWalletId], [integrationId] and [address] */
|
||||
suspend fun store(userWalletId: UserWalletId, integrationId: String, address: String, item: YieldBalanceWrapperDTO)
|
||||
/** Store [item] by [userWalletId] and [stakingID] */
|
||||
suspend fun store(userWalletId: UserWalletId, stakingID: StakingID, item: YieldBalanceWrapperDTO)
|
||||
|
||||
suspend fun refresh(userWalletId: UserWalletId, addressWithIntegrationIdMap: Map<String, String>)
|
||||
/** Store [item] by [userWalletId] */
|
||||
suspend fun storeSingleYieldBalance(userWalletId: UserWalletId, item: YieldBalance)
|
||||
|
||||
/** Refresh balances of [stakingIds] by [userWalletId] */
|
||||
suspend fun refresh(userWalletId: UserWalletId, stakingIds: List<StakingID>)
|
||||
|
||||
data class StakingID(val integrationId: String, val address: String)
|
||||
}
|
||||
|
|
@ -7,14 +7,18 @@ import com.tangem.domain.staking.model.stakekit.YieldBalance
|
|||
import com.tangem.domain.staking.model.stakekit.YieldBalanceItem
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
internal class YieldBalanceConverter(private val isCached: Boolean) : Converter<YieldBalanceWrapperDTO, YieldBalance> {
|
||||
internal class YieldBalanceConverter(
|
||||
private val source: StatusSource,
|
||||
) : Converter<YieldBalanceWrapperDTO, YieldBalance> {
|
||||
|
||||
constructor(isCached: Boolean) : this(source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL)
|
||||
|
||||
override fun convert(value: YieldBalanceWrapperDTO): YieldBalance {
|
||||
return if (value.balances.isEmpty()) {
|
||||
YieldBalance.Empty(
|
||||
integrationId = value.integrationId,
|
||||
address = value.addresses.address,
|
||||
source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL,
|
||||
source = source,
|
||||
)
|
||||
} else {
|
||||
YieldBalance.Data(
|
||||
|
|
@ -40,7 +44,7 @@ internal class YieldBalanceConverter(private val isCached: Boolean) : Converter<
|
|||
.sortedWith(compareBy({ it.type }, { it.amount })),
|
||||
integrationId = value.integrationId,
|
||||
),
|
||||
source = if (isCached) StatusSource.CACHE else StatusSource.ACTUAL,
|
||||
source = source,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
<string name="action_buttons_service_loading_alert_message">Einige Netzwerke sind derzeit nicht erreichbar. Bitte versuche es später erneut.</string>
|
||||
<string name="action_buttons_service_loading_alert_title">Die Daten wurden noch nicht geladen.</string>
|
||||
<string name="action_buttons_something_wrong_alert_message">Die Aktion ist derzeit nicht verfügbar. Bitte versuche es später erneut oder aktualisiere die Daten, indem Du auf dem Bildschirm nach unten wischst.</string>
|
||||
<string name="action_buttons_something_wrong_alert_title">Schaltfläche ist nicht verfügbar</string>
|
||||
<string name="action_buttons_something_wrong_alert_title">Aktion ist nicht verfügbar</string>
|
||||
<string name="action_buttons_swap_choose_token">Wähle den Token</string>
|
||||
<string name="action_buttons_swap_empty_search_message">Du kannst Deinen Token nicht finden? Gehe zum Bereich „Markt“ auf der Hauptseite und fügen diesen zum tauschen im Portfolio hinzu.</string>
|
||||
<string name="action_buttons_swap_no_available_pair_notification_message">Es gibt keine verfügbaren Token, die mit dem ausgewählten Token getauscht werden können. Bitte wähle einen anderen.</string>
|
||||
|
|
@ -781,6 +781,7 @@
|
|||
<string name="staking_details_estimated_profit">%s geschätzter Profit</string>
|
||||
<string name="staking_details_market_rating">Marktbewertung</string>
|
||||
<string name="staking_details_metrics_block_header">Metriken</string>
|
||||
<string name="staking_details_min_rewards_notification">Gemäß den Netzwerkregeln %1$s sind Ansprüche ab %2$s möglich. Die unten aufgeführten Beträge werden Deinem Konto nach der Aufhebung des Stakings gutgeschrieben.</string>
|
||||
<string name="staking_details_minimum_requirement">Mindestanforderungen</string>
|
||||
<string name="staking_details_no_rewards_to_claim">Keine Belohnungen verfügbar</string>
|
||||
<string name="staking_details_reward_claiming">Belohnungen beanspruchen</string>
|
||||
|
|
@ -827,6 +828,7 @@
|
|||
<string name="staking_notification_restake_rewards_text">Reinvestiert Deine verdienten Prämien in Deinen Einsatzbetrag und erhöht so den potenziellen Gewinn.</string>
|
||||
<string name="staking_notification_restake_text">Mit Restake kannst Du Dein Guthaben von einem Validator zu einem anderen verschieben, ohne dass Du den Stake aufheben musst.</string>
|
||||
<string name="staking_notification_stake_entire_balance_text">Du bist dabei, Dein gesamtes Guthaben zu staken. Wir empfehlen, einen kleinen Betrag übrig zu lassen, um die Netzwerkgebühren für die Aufhebung des Stakes oder das Einfordern von Prämien abzudecken.</string>
|
||||
<string name="staking_notification_ton_activate_account">Um mit dem Staking bei TON zu beginnen, führe zunächst eine ausgehende Transaktion in beliebiger Höhe durch – dadurch wird Deine Wallet aktiviert.</string>
|
||||
<string name="staking_notification_unlock_text">Entsperre dein Geld, um es aus dem Staking-Prozess abzuheben. Das Freischalten nimmt %s.</string>
|
||||
<string name="staking_notification_unstake_cosmos_text">Nach Ablauf der 21-tägigen Bindungsfrist kannst Du über Dein Guthaben verfügen. Die Prämie wird zusammen mit dem ungestaketen Guthaben abgehoben.</string>
|
||||
<string name="staking_notification_unstake_text">Deine Assets stehen Dir nach Ablauf der Frist für die Aufhebung der Bindung %s zur Verfügung.</string>
|
||||
|
|
@ -926,8 +928,10 @@
|
|||
<string name="toast_undo">Rückgängig machen</string>
|
||||
<string name="token_button_unavailability_generic_description">Dieser Vorgang ist derzeit nicht verfügbar. Bitte versuche es später noch einmal.</string>
|
||||
<string name="token_button_unavailability_reason_buy_unavailable">Der Kauf von %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen.</string>
|
||||
<string name="token_button_unavailability_reason_custom_token">Die Aktion ist für benutzerdefinierte Token nicht verfügbar.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">Du hast kein Guthaben um dies zu Verkaufen. Lade dein Konto auf, um Geld von dort zu verkaufen.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">Du hast kein Guthaben zum Versenden. Lade dein Konto auf, um Geld von dort aus senden zu können.</string>
|
||||
<string name="token_button_unavailability_reason_loading">Die Daten wurden noch nicht geladen. Dies kann einige Sekunden dauern. Bitte versuche es später erneut.</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">Das Tauschen von %s wird von aktuellen Anbietern nicht unterstützt, aber wir arbeiten daran, weitere Optionen hinzuzufügen.</string>
|
||||
<string name="token_button_unavailability_reason_out_of_date_balance">Der angezeigte Kontostand kann aufgrund der Zwischenspeicherung veraltet sein.</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">Der Verkauf von Geldmitteln ist möglich, sobald die ausstehende(n) Transaktion(en) im %s -Netzwerk abgeschlossen ist/sind.</string>
|
||||
|
|
@ -976,6 +980,7 @@
|
|||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="twins_recreate_warning">Diese Aktion ist unumkehrbar. Du hast keinen Zugriff mehr auf die alte Wallet.</string>
|
||||
<string name="twins_scan_twin_with_number">Tippe auf die Doppelkarte oder Ring mit der Nummer %s und entferne sie erst am Ende des Vorgangs.</string>
|
||||
<string name="universal_error">Es ist ein Fehler aufgetreten. Fehlercode: %s. Bitte kontaktiere unseren Support.</string>
|
||||
<string name="unlock_wallet_description_full">Verwende %s oder scanne eine Karte oder Ring, um Zugriff auf deine Wallet zu erhalten.</string>
|
||||
<string name="unsupported_wc_version">Verbindung fehlgeschlagen: Diese dApp verwendet Wallet Connect Version 1.0, die nicht unterstützt wird. Bitte stelle sicher, dass die dApp Wallet Connect Version 2.0 unterstützt, um eine erfolgreiche Verbindung herzustellen.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Bleib auf dem Laufenden mit den neuesten Funktionen und Neuigkeiten</string>
|
||||
|
|
|
|||
|
|
@ -920,6 +920,7 @@
|
|||
<string name="token_button_unavailability_reason_buy_unavailable">Comprar %s no está disponible con los proveedores actuales pero estamos trabajando para agregar más opciones.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">No tiene fondos para vender. Recargue su cuenta para poder vender fondos desde ella.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">No tienes fondos para enviar. Recarga tu cuenta para poder enviar fondos desde ella.</string>
|
||||
<string name="token_button_unavailability_reason_loading">Los datos aún no se han cargado. Esto puede tardar unos segundos. Por favor, inténtelo de nuevo más tarde.</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">El servicio de intercambio %s no está compatible con los proveedores actuales pero estamos trabajando para agregar más opciones.</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">La venta de fondos estará disponible una vez que la(s) transacción(es) pendiente(s) en la red %s se complete</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_send">El envío de fondos estará disponible una vez que se completen las transacciones pendientes en la red %s.</string>
|
||||
|
|
@ -939,7 +940,7 @@
|
|||
<string name="token_swap_changelly_promotion_message">Cambie este token por otro por una tarifa de servicio de %1$s del %2$s al %3$s de febrero.</string>
|
||||
<string name="token_swap_changelly_promotion_title">Intercambio con Changelly, %s tarifa</string>
|
||||
<string name="token_swap_promotion_button">Intercambie ahora</string>
|
||||
<string name="tokens_list_hot_crypto_header">Crypto-monnaies populaire 🔥</string>
|
||||
<string name="tokens_list_hot_crypto_header">Criptomonedas populares 🔥</string>
|
||||
<string name="tokens_list_unavailable_to_purchase_header">No disponible para compra</string>
|
||||
<string name="tokens_list_unavailable_to_sell_header">No disponible para vender</string>
|
||||
<string name="tokens_list_unavailable_to_swap_header">No disponible para cambio desde %s</string>
|
||||
|
|
|
|||
|
|
@ -922,6 +922,7 @@
|
|||
<string name="token_button_unavailability_reason_buy_unavailable">L\'achat de %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter davantage d\'options.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">Vous n\'avez pas de fonds à vendre. Renflouez votre compte pour pouvoir vendre des fonds à partir de celui-ci.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">Vous n\'avez pas de fonds à envoyer. Renflouez votre compte pour pouvoir envoyer des fonds à partir de celui-ci.</string>
|
||||
<string name="token_button_unavailability_reason_loading">Die Daten wurden noch nicht geladen. Dies kann einige Sekunden dauern. Bitte versuchen Sie es später noch einmal.</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">Le service d\'échange %s n\'est pas pris en charge par les fournisseurs actuels mais nous travaillons à ajouter plus d\'options.</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">La vente de fonds sera disponible une fois que la ou les transactions en attente dans le réseau %s seront terminées</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_send">L\'envoi de fonds sera disponible une fois la ou les transactions en attente dans le réseau %s terminées.</string>
|
||||
|
|
|
|||
|
|
@ -818,6 +818,7 @@
|
|||
<string name="staking_notification_restake_rewards_text">獲得した報酬をステーキングに再投資し、潜在的な収益を増やします。</string>
|
||||
<string name="staking_notification_restake_text">再ステーキングを使うと、ステーキングを解除することなく、あるバリデータから別のバリデータに資金を移動できます。</string>
|
||||
<string name="staking_notification_stake_entire_balance_text">残高のすべてをステーキングしようとしています。ステーキング解除や報酬請求にかかるネットワーク手数料をカバーするために、少額を残しておくことをお勧めします。</string>
|
||||
<string name="staking_notification_ton_activate_account">TONでステーキングを開始するには、まず任意の金額の送金を行ってください。これにより、ウォレットがアクティブになります。</string>
|
||||
<string name="staking_notification_unlock_text">資金をステーキングから引き出すには、ロックを解除してください。ロック解除には%sかかります。</string>
|
||||
<string name="staking_notification_unstake_cosmos_text">資金は、21日間のロック解除期間後に使用可能になります。報酬は、ロック解除後の資金とともに引き出されます。</string>
|
||||
<string name="staking_notification_unstake_text">%sのステーキング解除期間後、資金はすぐ利用可能となります。</string>
|
||||
|
|
@ -920,7 +921,7 @@
|
|||
<string name="token_button_unavailability_reason_custom_token">カスタムトークンでは使用できません。</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">売却できる資金がありません。アカウントに入金して、売却できるようにしてください。</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">送金する資金がありません。アカウントに入金して、送金できるようにしてください。</string>
|
||||
<string name="token_button_unavailability_reason_loading">データはまだ読み込まれていません。数秒かかる場合があります。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="token_button_unavailability_reason_loading">現在データを読み込んでいます。これには数秒かかる場合があります。しばらくしてからもう一度お試しください。</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">%sのスワップは、現在のプロバイダーではサポートされていませんが、より多くのオプションを追加できるよう取り組んでいます。</string>
|
||||
<string name="token_button_unavailability_reason_out_of_date_balance">表示残高はキャッシュにより古くなっている可能性があります。</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">%sネットワーク上の保留中の取引が完了すると、資金の売却が可能になります。</string>
|
||||
|
|
@ -969,6 +970,7 @@
|
|||
<string name="twins_recreate_toolbar">Tangemツイン</string>
|
||||
<string name="twins_recreate_warning">この操作は元に戻せません。古いウォレットにはアクセスできなくなります。</string>
|
||||
<string name="twins_scan_twin_with_number">番号%sのツインカードをタップし、操作が終了するまで取り外さないでください。</string>
|
||||
<string name="universal_error">エラーが発生しました。エラー コード:%s。サポートにお問い合わせください。</string>
|
||||
<string name="unlock_wallet_description_full">%sを使用するか、カード / リングをスキャンしてウォレットにアクセスしてください</string>
|
||||
<string name="unsupported_wc_version">接続に失敗しました:このdAppは、サポートされていないWallet Connectバージョン1.0を使用しています。正常に接続するには、dAppがWallet Connectバージョン2.0をサポートしていることを確認してください。</string>
|
||||
<string name="user_push_notification_agreement_argument_one">最新の機能とニュースをお届けします</string>
|
||||
|
|
|
|||
|
|
@ -841,6 +841,7 @@
|
|||
<string name="staking_notification_restake_rewards_text">Реинвестируйте свои заработанные награды в вашу застейканную сумму, увеличивая потенциальный доход</string>
|
||||
<string name="staking_notification_restake_text">Рестейк позволяет вам переместить средства из одного валидатора в другого без необходимости выхода из стейкинга.</string>
|
||||
<string name="staking_notification_stake_entire_balance_text">Вы собираетесь застейкать весь баланс, рекомендуем оставить небольшую сумму для оплаты комиссии сети при выходе из стейкинга или получении награды.</string>
|
||||
<string name="staking_notification_ton_activate_account">Чтобы начать стейкинг в TON, сначала осуществите исходящую транзакцию на любую сумму — это активирует ваш кошелек.</string>
|
||||
<string name="staking_notification_unlock_text">Разблокируйте свои средства, чтобы вывести их из стейкинга. Разблокировка займёт %s.</string>
|
||||
<string name="staking_notification_unstake_cosmos_text">Ваши средства будут доступны для использования после 21-дневного периода отзыва. Награда будет выведена вместе с вашими выводими средствами.</string>
|
||||
<string name="staking_notification_unstake_text">Ваши средства будут доступны после %s периода отзыва.</string>
|
||||
|
|
@ -941,6 +942,7 @@
|
|||
<string name="token_button_unavailability_reason_buy_unavailable">Покупка %s в данный момент не поддерживается ни одним провайдером. Мы работаем над добавлением новых возможностей. Следите за нашими новостями.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">У вас нет средств для продажи. Пополните счет, чтобы иметь возможность продать с него средства.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">У вас нет средств для отправки. Пополните счет, чтобы иметь возможность отправить с него средства.</string>
|
||||
<string name="token_button_unavailability_reason_loading">Данные в процессе загрузки. Это может занять несколько секунд. Пожалуйста, попробуйте позже.</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">Обмен %s в данный момент не поддерживается ни одним провайдером. Мы работаем над добавлением новых возможностей. Следите за нашими новостями.</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">Продажа средств станет доступной после завершения транзакции(-ий) в сети %s</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_send">Отправка средств станет доступной после завершения транзакции(-ий) в сети %s</string>
|
||||
|
|
@ -988,6 +990,7 @@
|
|||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="twins_recreate_warning">Это действие необратимо. У вас не будет доступа к старому кошельку.</string>
|
||||
<string name="twins_scan_twin_with_number">Приложите twin-карту с номером %s и не убирайте до окончания операции</string>
|
||||
<string name="universal_error">Произошла ошибка. Код ошибки: %s. Попробуйте, пожалуйста, снова. Если проблема будет продолжать возникать — обратитесь в службу поддержки.</string>
|
||||
<string name="unlock_wallet_description_full">Используйте %s или отсканируйте карту/кольцо, чтобы получить доступ к своему кошельку</string>
|
||||
<string name="unsupported_wc_version">Соединение не удалось: это dApp использует Wallet Connect версии 1.0, которая не поддерживается. Убедитесь, что dApp поддерживает Wallet Connect версии 2.0 для успешного подключения.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Будьте в курсе новых функций и новостей</string>
|
||||
|
|
|
|||
|
|
@ -940,6 +940,7 @@
|
|||
<string name="token_button_unavailability_reason_buy_unavailable">Наразі купівля монети %s недоступна. Слідкуйте за нашими оновленнями.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">У вас немає коштів для продажу. Поповніть рахунок, щоб мати змогу продати з нього кошти.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">У вас немає коштів для відправлення. Поповніть рахунок, щоб мати змогу надіслати з нього кошти.</string>
|
||||
<string name="token_button_unavailability_reason_loading">Дані ще не завантажені. Це може зайняти кілька секунд. Будь ласка, спробуйте пізніше.</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">Наразі обмін монети %s недоступний. Слідкуйте за нашими оновленнями.</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">Продаж коштів стане доступним після завершення транзакції(-ій) в мережі %s</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_send">Надсилання коштів стане доступним після завершення транзакції(-ій) в мережі %s</string>
|
||||
|
|
|
|||
|
|
@ -832,6 +832,7 @@
|
|||
<string name="staking_notification_restake_rewards_text">Reinvests your earned rewards in your staked amount, increasing potential earnings.</string>
|
||||
<string name="staking_notification_restake_text">Restake lets you move your funds from one validator to another without the need to unstake</string>
|
||||
<string name="staking_notification_stake_entire_balance_text">You’re about to stake your entire balance. We recommend leaving a small amount to cover network fees for unstaking or claiming rewards.</string>
|
||||
<string name="staking_notification_ton_activate_account">To start staking in TON, first make an outgoing transaction of any amount — this will activate your wallet.</string>
|
||||
<string name="staking_notification_unlock_text">Unlock your money to withdraw it from staking process. Unlocking takes %s.</string>
|
||||
<string name="staking_notification_unstake_cosmos_text">Your funds will be available for use after the 21-day unbonding period. Reward will be withdrawn along with your unstaking funds.</string>
|
||||
<string name="staking_notification_unstake_text">Your funds will be available for use after the %s unbonding period.</string>
|
||||
|
|
@ -934,7 +935,7 @@
|
|||
<string name="token_button_unavailability_reason_custom_token">Action is not available for custom tokens.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_sell">You do not have funds to sell. Top up your account to be able to sell funds from it.</string>
|
||||
<string name="token_button_unavailability_reason_empty_balance_send">You do not have funds to send. Top up your account to be able to send funds from it.</string>
|
||||
<string name="token_button_unavailability_reason_loading">The data has not loaded yet. This may take a few seconds. Please try again later.</string>
|
||||
<string name="token_button_unavailability_reason_loading">The data is currently loading. This may take a few seconds. Please try again later.</string>
|
||||
<string name="token_button_unavailability_reason_not_exchangeable">Swapping %s is not supported by current providers, but we are working to add more options.</string>
|
||||
<string name="token_button_unavailability_reason_out_of_date_balance">The displayed balance might be outdated due to caching.</string>
|
||||
<string name="token_button_unavailability_reason_pending_transaction_sell">Selling funds will be available once the pending transaction(s) on the %s network is complete.</string>
|
||||
|
|
@ -983,6 +984,7 @@
|
|||
<string name="twins_recreate_toolbar">Tangem Twin</string>
|
||||
<string name="twins_recreate_warning">This action is irreversible. You will not have access to the old wallet.</string>
|
||||
<string name="twins_scan_twin_with_number">Tap the twin card with number %s and do not remove until the end of the operation</string>
|
||||
<string name="universal_error">We\'ve encountered an error. Error code: %s. Please contact our support.</string>
|
||||
<string name="unlock_wallet_description_full">Use %s or scan a card/ring to have access to your wallet</string>
|
||||
<string name="unsupported_wc_version">Connection failed: This dApp uses Wallet Connect version 1.0, which is not supported. Please ensure the dApp supports Wallet Connect version 2.0 to connect successfully.</string>
|
||||
<string name="user_push_notification_agreement_argument_one">Stay up to date with the latest features and news</string>
|
||||
|
|
|
|||
|
|
@ -372,20 +372,33 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
|
||||
val requestBody = getBalanceRequestData(address, integrationId)
|
||||
val result = stakeKitApi.getSingleYieldBalance(
|
||||
integrationId = requestBody.integrationId,
|
||||
body = requestBody,
|
||||
).getOrThrow()
|
||||
|
||||
stakingBalanceStore.store(
|
||||
userWalletId = userWalletId,
|
||||
integrationId = requestBody.integrationId,
|
||||
address = address,
|
||||
item = YieldBalanceWrapperDTO(
|
||||
balances = result,
|
||||
integrationId = requestBody.integrationId,
|
||||
addresses = requestBody.addresses,
|
||||
),
|
||||
safeApiCall(
|
||||
call = {
|
||||
val result = stakeKitApi.getSingleYieldBalance(
|
||||
integrationId = requestBody.integrationId,
|
||||
body = requestBody,
|
||||
).bind()
|
||||
|
||||
stakingBalanceStore.store(
|
||||
userWalletId = userWalletId,
|
||||
stakingID = StakingBalanceStore.StakingID(
|
||||
integrationId = requestBody.integrationId,
|
||||
address = address,
|
||||
),
|
||||
item = YieldBalanceWrapperDTO(
|
||||
balances = result,
|
||||
integrationId = requestBody.integrationId,
|
||||
addresses = requestBody.addresses,
|
||||
),
|
||||
)
|
||||
},
|
||||
onError = {
|
||||
stakingBalanceStore.storeSingleYieldBalance(
|
||||
userWalletId = userWalletId,
|
||||
item = YieldBalance.Error(integrationId = requestBody.integrationId, address = address),
|
||||
)
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
|
|
@ -399,7 +412,11 @@ internal class DefaultStakingRepository(
|
|||
val address = walletManagersFacade.getDefaultAddress(userWalletId, cryptoCurrency.network).orEmpty()
|
||||
val integrationId = integrationIdMap[getIntegrationKey(cryptoCurrency.id)]
|
||||
?: error("Could not get integrationId")
|
||||
stakingBalanceStore.get(userWalletId, address, integrationId)
|
||||
|
||||
stakingBalanceStore.get(
|
||||
userWalletId = userWalletId,
|
||||
stakingID = StakingBalanceStore.StakingID(integrationId = integrationId, address = address),
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.collectLatest {
|
||||
if (it != null) {
|
||||
|
|
@ -413,10 +430,7 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
|
||||
withContext(dispatchers.io) {
|
||||
fetchSingleYieldBalance(
|
||||
userWalletId,
|
||||
cryptoCurrency,
|
||||
)
|
||||
fetchSingleYieldBalance(userWalletId = userWalletId, cryptoCurrency = cryptoCurrency)
|
||||
}
|
||||
}.cancellable()
|
||||
|
||||
|
|
@ -431,7 +445,10 @@ internal class DefaultStakingRepository(
|
|||
val integrationId = integrationIdMap[getIntegrationKey(cryptoCurrency.id)]
|
||||
?: error("Could not get integrationId")
|
||||
|
||||
stakingBalanceStore.getSyncOrNull(userWalletId, address, integrationId)
|
||||
stakingBalanceStore.getSyncOrNull(
|
||||
userWalletId = userWalletId,
|
||||
stakingID = StakingBalanceStore.StakingID(integrationId = integrationId, address = address),
|
||||
)
|
||||
?: YieldBalance.Error(integrationId, address)
|
||||
}
|
||||
|
||||
|
|
@ -444,21 +461,7 @@ internal class DefaultStakingRepository(
|
|||
if (refresh) {
|
||||
stakingBalanceStore.refresh(
|
||||
userWalletId = userWalletId,
|
||||
addressWithIntegrationIdMap = cryptoCurrencies
|
||||
.mapNotNull { currency ->
|
||||
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
|
||||
|
||||
if (integrationId != null) {
|
||||
addresses to integrationId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.flatMap { (addresses, integrationId) ->
|
||||
addresses.map { address -> integrationId to address.value }
|
||||
}
|
||||
.toMap(),
|
||||
stakingIds = cryptoCurrencies.mapStakingId(userWalletId),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -523,20 +526,52 @@ internal class DefaultStakingRepository(
|
|||
)
|
||||
}
|
||||
|
||||
private suspend fun List<CryptoCurrency>.mapStakingId(
|
||||
userWalletId: UserWalletId,
|
||||
): List<StakingBalanceStore.StakingID> {
|
||||
return this
|
||||
.mapNotNull { currency ->
|
||||
val addresses = walletManagersFacade.getAddresses(userWalletId, currency.network)
|
||||
val integrationId = integrationIdMap[getIntegrationKey(currency.id)]
|
||||
|
||||
if (integrationId != null) {
|
||||
addresses to integrationId
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
.flatMap { (addresses, integrationId) ->
|
||||
addresses.map { address ->
|
||||
StakingBalanceStore.StakingID(
|
||||
integrationId = integrationId,
|
||||
address = address.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceUpdates(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): Flow<YieldBalanceList> {
|
||||
return stakingBalanceStore.get(userWalletId)
|
||||
.map(YieldBalanceListConverter::convert)
|
||||
.flowOn(dispatchers.io)
|
||||
return flow {
|
||||
stakingBalanceStore.get(
|
||||
userWalletId = userWalletId,
|
||||
stakingIds = cryptoCurrencies.mapStakingId(userWalletId),
|
||||
)
|
||||
.map(YieldBalanceListConverter::convert)
|
||||
.collect { emit(it) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMultiYieldBalanceUpdatesLegacy(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencies: List<CryptoCurrency>,
|
||||
): Flow<YieldBalanceList> = channelFlow {
|
||||
stakingBalanceStore.get(userWalletId)
|
||||
stakingBalanceStore.get(
|
||||
userWalletId = userWalletId,
|
||||
stakingIds = cryptoCurrencies.mapStakingId(userWalletId),
|
||||
)
|
||||
.onEach {
|
||||
val balances = YieldBalanceListConverter.convert(it)
|
||||
send(balances)
|
||||
|
|
@ -554,7 +589,8 @@ internal class DefaultStakingRepository(
|
|||
): YieldBalanceList = withContext(dispatchers.io) {
|
||||
fetchMultiYieldBalance(userWalletId, cryptoCurrencies)
|
||||
|
||||
stakingBalanceStore.getSyncOrNull(userWalletId)?.let(YieldBalanceListConverter::convert)
|
||||
stakingBalanceStore.getSyncOrNull(userWalletId, cryptoCurrencies.mapStakingId(userWalletId))
|
||||
?.let(YieldBalanceListConverter::convert)
|
||||
?: YieldBalanceList.Error
|
||||
}
|
||||
|
||||
|
|
@ -698,6 +734,7 @@ internal class DefaultStakingRepository(
|
|||
}
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
private companion object {
|
||||
const val YIELDS_STORE_KEY = "yields"
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class ValidateTokenFormUseCase(
|
|||
}
|
||||
|
||||
companion object {
|
||||
const val MIN_DECIMALS = 1
|
||||
const val MIN_DECIMALS = 0
|
||||
const val MAX_DECIMALS = 30
|
||||
}
|
||||
}
|
||||
|
|
@ -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,45 +153,43 @@ 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"
|
||||
private const val ASSET_REQUIREMENT = "AssetRequirement"
|
||||
|
||||
@Suppress("CyclomaticComplexMethod")
|
||||
fun ScenarioUnavailabilityReason.toReasonAnalyticsText(): String {
|
||||
return when (this) {
|
||||
is ScenarioUnavailabilityReason.BuyUnavailable,
|
||||
is ScenarioUnavailabilityReason.NotExchangeable,
|
||||
is ScenarioUnavailabilityReason.NotSupportedBySellService,
|
||||
is ScenarioUnavailabilityReason.StakingUnavailable,
|
||||
ScenarioUnavailabilityReason.UnassociatedAsset,
|
||||
-> UNAVAILABLE
|
||||
ScenarioUnavailabilityReason.UnassociatedAsset -> ASSET_REQUIREMENT
|
||||
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,
|
||||
|
|
@ -123,7 +124,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
if (cryptoCurrencyStatus.value.sources.total != StatusSource.ACTUAL) {
|
||||
return getActionsForOutdatedData(userWallet, cryptoCurrencyStatus, requirements)
|
||||
return getActionsForOutdatedData(userWallet, cryptoCurrencyStatus, requirements, isStakingAvailable)
|
||||
}
|
||||
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
|
|
@ -147,27 +148,7 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
// staking
|
||||
if (isStakingAvailable) {
|
||||
val yield = kotlin.runCatching {
|
||||
stakingRepository.getYield(
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
}.getOrNull()
|
||||
activeList.add(
|
||||
TokenActionsState.ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.None,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.StakingUnavailable(cryptoCurrency.name),
|
||||
yield = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
addStakingActions(cryptoCurrency, isStakingAvailable, activeList, disabledList)
|
||||
|
||||
// send
|
||||
val sendUnavailabilityReason = getSendUnavailabilityReason(
|
||||
|
|
@ -181,26 +162,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
|
||||
|
|
@ -231,6 +197,35 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
return activeList + disabledList
|
||||
}
|
||||
|
||||
private suspend fun addStakingActions(
|
||||
cryptoCurrency: CryptoCurrency,
|
||||
isStakingAvailable: Boolean,
|
||||
activeList: MutableList<TokenActionsState.ActionState>,
|
||||
disabledList: MutableList<TokenActionsState.ActionState>,
|
||||
) {
|
||||
if (isStakingAvailable) {
|
||||
val yield = kotlin.runCatching {
|
||||
stakingRepository.getYield(
|
||||
cryptoCurrencyId = cryptoCurrency.id,
|
||||
symbol = cryptoCurrency.symbol,
|
||||
)
|
||||
}.getOrNull()
|
||||
activeList.add(
|
||||
TokenActionsState.ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.None,
|
||||
yield = yield,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Stake(
|
||||
unavailabilityReason = ScenarioUnavailabilityReason.StakingUnavailable(cryptoCurrency.name),
|
||||
yield = null,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getActionsForUnreachableCurrency(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
|
|
@ -270,12 +265,16 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
return actionsList
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
private suspend fun getActionsForOutdatedData(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
requirements: AssetRequirementsCondition?,
|
||||
isStakingAvailable: Boolean,
|
||||
): List<TokenActionsState.ActionState> {
|
||||
val activeList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val disabledList = mutableListOf<TokenActionsState.ActionState>()
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
// copy address
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
|
|
@ -285,30 +284,42 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
// receive
|
||||
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
|
||||
val scenario = getReceiveScenario(requirements)
|
||||
activeList.add(TokenActionsState.ActionState.Receive(scenario))
|
||||
val action = TokenActionsState.ActionState.Receive(scenario)
|
||||
if (scenario == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(action)
|
||||
} else {
|
||||
disabledList.add(action)
|
||||
}
|
||||
}
|
||||
|
||||
// swap
|
||||
val isSwapAvailable = with(cryptoCurrencyStatus.value.sources) {
|
||||
val sources = cryptoCurrencyStatus.value.sources
|
||||
val isSwapAvailable = with(sources) {
|
||||
quoteSource.isActual() && networkSource.isActual()
|
||||
}
|
||||
|
||||
activeList.add(
|
||||
TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = if (isSwapAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
},
|
||||
showBadge = false,
|
||||
),
|
||||
val swapAction = TokenActionsState.ActionState.Swap(
|
||||
unavailabilityReason = if (isSwapAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else if (sources.networkSource == StatusSource.ONLY_CACHE) {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
} else {
|
||||
// CACHE source always when loading
|
||||
ScenarioUnavailabilityReason.DataLoading
|
||||
},
|
||||
showBadge = false,
|
||||
)
|
||||
if (swapAction.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(swapAction)
|
||||
} else {
|
||||
disabledList.add(swapAction)
|
||||
}
|
||||
|
||||
// buy
|
||||
if (rampManager.availableForBuy(userWallet.scanResponse, userWallet.walletId, cryptoCurrencyStatus.currency)) {
|
||||
if (rampManager.availableForBuy(userWallet.scanResponse, userWallet.walletId, cryptoCurrency)) {
|
||||
activeList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
activeList.add(
|
||||
disabledList.add(
|
||||
TokenActionsState.ActionState.Buy(
|
||||
ScenarioUnavailabilityReason.BuyUnavailable(
|
||||
cryptoCurrencyName = cryptoCurrencyStatus.currency.name,
|
||||
|
|
@ -318,29 +329,40 @@ class GetCryptoCurrencyActionsUseCase(
|
|||
}
|
||||
|
||||
// staking
|
||||
activeList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.UsedOutdatedData, null))
|
||||
if (cryptoCurrencyStatus.value.sources.networkSource.isActual()) {
|
||||
addStakingActions(cryptoCurrency, isStakingAvailable, activeList, disabledList)
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Stake(ScenarioUnavailabilityReason.UsedOutdatedData, null))
|
||||
}
|
||||
|
||||
// send
|
||||
val isSendAvailable = cryptoCurrencyStatus.value.sources.networkSource.isActual()
|
||||
|
||||
activeList.add(
|
||||
TokenActionsState.ActionState.Send(
|
||||
unavailabilityReason = if (isSendAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
},
|
||||
),
|
||||
val sendAction = TokenActionsState.ActionState.Send(
|
||||
unavailabilityReason = if (isSendAvailable) {
|
||||
ScenarioUnavailabilityReason.None
|
||||
} else {
|
||||
ScenarioUnavailabilityReason.UsedOutdatedData
|
||||
},
|
||||
)
|
||||
if (sendAction.unavailabilityReason == ScenarioUnavailabilityReason.None) {
|
||||
activeList.add(sendAction)
|
||||
} else {
|
||||
disabledList.add(sendAction)
|
||||
}
|
||||
|
||||
// region sell
|
||||
activeList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.UsedOutdatedData))
|
||||
if (isSendAvailable) {
|
||||
activeList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.None))
|
||||
} else {
|
||||
disabledList.add(TokenActionsState.ActionState.Sell(ScenarioUnavailabilityReason.UsedOutdatedData))
|
||||
}
|
||||
// endregion
|
||||
|
||||
// hide
|
||||
activeList.add(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
|
||||
|
||||
return activeList
|
||||
return activeList + disabledList
|
||||
}
|
||||
|
||||
private fun getReceiveScenario(requirements: AssetRequirementsCondition?): ScenarioUnavailabilityReason {
|
||||
|
|
@ -376,6 +398,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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,7 +112,13 @@ class CachedCurrenciesStatusesOperations(
|
|||
val rawCurrenciesIds = currenciesIds.mapNotNullTo(mutableSetOf()) { it.rawCurrencyId }
|
||||
quotesRepository.fetchQuotes(rawCurrenciesIds)
|
||||
},
|
||||
async { stakingRepository.fetchMultiYieldBalance(userWalletId, currencies) },
|
||||
async {
|
||||
if (currencies.size == 1) {
|
||||
stakingRepository.fetchSingleYieldBalance(userWalletId, currencies.first())
|
||||
} else {
|
||||
stakingRepository.fetchMultiYieldBalance(userWalletId, currencies)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
.map { }
|
||||
|
|
@ -251,6 +257,6 @@ class CachedCurrenciesStatusesOperations(
|
|||
}
|
||||
|
||||
companion object {
|
||||
private const val RETRY_DELAY = 2000L
|
||||
internal const val RETRY_DELAY = 2000L
|
||||
}
|
||||
}
|
||||
|
|
@ -9,10 +9,12 @@ import com.tangem.domain.tokens.model.CryptoCurrency
|
|||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.operations.CachedCurrenciesStatusesOperations.Companion.RETRY_DELAY
|
||||
import com.tangem.domain.tokens.repository.CurrenciesRepository
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
class CurrenciesStatusesOperations(
|
||||
|
|
@ -29,6 +31,7 @@ class CurrenciesStatusesOperations(
|
|||
}
|
||||
.retryWhen { cause, _ ->
|
||||
emit(Error.DataError(cause).left())
|
||||
delay(RETRY_DELAY)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -204,6 +204,7 @@ internal class StakingModel @Inject constructor(
|
|||
|
||||
private val transactionsInProgress: CopyOnWriteArrayList<StakingTransaction> = CopyOnWriteArrayList()
|
||||
|
||||
private var actionsJobHolder: JobHolder = JobHolder()
|
||||
private var approvalJobHolder: JobHolder = JobHolder()
|
||||
private var feeJobHolder: JobHolder = JobHolder()
|
||||
private var sendTransactionJobHolder = JobHolder()
|
||||
|
|
@ -290,6 +291,7 @@ internal class StakingModel @Inject constructor(
|
|||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fee = gasEstimate,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
updateNotifications()
|
||||
|
|
@ -309,6 +311,7 @@ internal class StakingModel @Inject constructor(
|
|||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fee = fee,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
updateNotifications()
|
||||
|
|
@ -328,25 +331,26 @@ internal class StakingModel @Inject constructor(
|
|||
},
|
||||
onConstructError = { error ->
|
||||
stakingEventFactory.createStakingErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus))
|
||||
},
|
||||
onSendSuccess = { txUrl ->
|
||||
stakingAnalyticSender.sendTransactionStakingAnalytics(stateController.value)
|
||||
transactionsInProgress.clear()
|
||||
stateController.update(SetConfirmationStateCompletedTransformer(txUrl))
|
||||
stateController.update(SetConfirmationStateCompletedTransformer(txUrl, cryptoCurrencyStatus))
|
||||
},
|
||||
onSendError = { error ->
|
||||
analyticsEventHandler.send(StakingAnalyticsEvent.TransactionError)
|
||||
stakingEventFactory.createSendTransactionErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus))
|
||||
},
|
||||
onFeeIncreased = { increasedFee ->
|
||||
stateController.updateAll(
|
||||
SetConfirmationStateResetAssentTransformer,
|
||||
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus),
|
||||
SetConfirmationStateAssentTransformer(
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fee = increasedFee,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
stateController.updateEvent(
|
||||
|
|
@ -371,7 +375,9 @@ internal class StakingModel @Inject constructor(
|
|||
}
|
||||
if (!isApprovalInProgress) {
|
||||
stakingStateRouter.onPrevClick()
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(
|
||||
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
|
||||
)
|
||||
}
|
||||
}
|
||||
null,
|
||||
|
|
@ -561,6 +567,7 @@ internal class StakingModel @Inject constructor(
|
|||
stateController.update(SetApprovalBottomSheetTypeChangeTransformer(approveType))
|
||||
}
|
||||
|
||||
@Suppress("LongMethod")
|
||||
override fun onApprovalClick() {
|
||||
modelScope.launch {
|
||||
stateController.update(
|
||||
|
|
@ -597,10 +604,13 @@ internal class StakingModel @Inject constructor(
|
|||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fee = TransactionFee.Single(fee),
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
stakingEventFactory.createGenericErrorAlert(error.message ?: error.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(
|
||||
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
|
||||
)
|
||||
return@launch
|
||||
},
|
||||
ifRight = { it },
|
||||
|
|
@ -619,10 +629,13 @@ internal class StakingModel @Inject constructor(
|
|||
appCurrencyProvider = Provider { appCurrency },
|
||||
feeCryptoCurrencyStatus = feeCryptoCurrencyStatus,
|
||||
fee = TransactionFee.Single(fee),
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
),
|
||||
)
|
||||
stakingEventFactory.createSendTransactionErrorAlert(error)
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(
|
||||
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
|
||||
)
|
||||
},
|
||||
ifRight = {
|
||||
stakingAnalyticSender.sendTransactionApprovalAnalytics(tokenCryptoCurrency)
|
||||
|
|
@ -856,10 +869,16 @@ internal class StakingModel @Inject constructor(
|
|||
},
|
||||
ifLeft = {
|
||||
stakingEventFactory.createGenericErrorAlert(it.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(
|
||||
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
|
||||
)
|
||||
},
|
||||
)
|
||||
getCurrencyStatusUpdatesUseCase(userWalletId, cryptoCurrencyId, false)
|
||||
getCurrencyStatusUpdatesUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = cryptoCurrencyId,
|
||||
isSingleWalletWithTokens = false,
|
||||
)
|
||||
.conflate()
|
||||
.distinctUntilChangedBy { it.getOrNull()?.value?.yieldBalance }
|
||||
.filter { value.currentStep == StakingStep.InitialInfo }
|
||||
|
|
@ -893,12 +912,14 @@ internal class StakingModel @Inject constructor(
|
|||
setupApprovalNeeded()
|
||||
setupIsAnyTokenStaked()
|
||||
checkIfSubtractAvailable()
|
||||
subscribeOnActionsUpdates()
|
||||
subscribeOnStepChanges()
|
||||
subscribeOnActionsUpdates(status)
|
||||
subscribeOnStepChanges(status)
|
||||
},
|
||||
ifLeft = { error ->
|
||||
stakingEventFactory.createGenericErrorAlert(error.toString())
|
||||
stateController.update(SetConfirmationStateResetAssentTransformer)
|
||||
stateController.update(
|
||||
SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
@ -928,13 +949,13 @@ internal class StakingModel @Inject constructor(
|
|||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun subscribeOnStepChanges() {
|
||||
private fun subscribeOnStepChanges(status: CryptoCurrencyStatus) {
|
||||
uiState
|
||||
.distinctUntilChangedBy { it.currentStep }
|
||||
.onEach {
|
||||
when {
|
||||
isInitState() -> {
|
||||
updateInitialData()
|
||||
updateInitialData(status)
|
||||
balanceUpdater.partialUpdate()
|
||||
}
|
||||
isAssentState() -> {
|
||||
|
|
@ -955,32 +976,30 @@ internal class StakingModel @Inject constructor(
|
|||
.saveIn(stepChangesJobHolder)
|
||||
}
|
||||
|
||||
private fun subscribeOnActionsUpdates() {
|
||||
getActionsUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyId = cryptoCurrencyId,
|
||||
)
|
||||
private fun subscribeOnActionsUpdates(status: CryptoCurrencyStatus) {
|
||||
getActionsUseCase(userWalletId = userWalletId, cryptoCurrencyId = cryptoCurrencyId)
|
||||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach { result ->
|
||||
result.getOrNull()?.let { actions ->
|
||||
processingActions = actions
|
||||
if (isInitState()) {
|
||||
updateInitialData()
|
||||
updateInitialData(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(modelScope)
|
||||
.saveIn(actionsJobHolder)
|
||||
}
|
||||
|
||||
private fun updateInitialData() {
|
||||
private fun updateInitialData(status: CryptoCurrencyStatus) {
|
||||
stateController.updateAll(
|
||||
SetInitialDataStateTransformer(
|
||||
clickIntents = this@StakingModel,
|
||||
yield = yield,
|
||||
isAnyTokenStaked = isAnyTokenStaked,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
cryptoCurrencyStatus = status,
|
||||
userWalletProvider = Provider { userWallet },
|
||||
appCurrencyProvider = Provider { appCurrency },
|
||||
balancesToShowProvider = Provider { balancesToShow },
|
||||
|
|
|
|||
|
|
@ -21,13 +21,12 @@ import org.joda.time.DateTime
|
|||
import java.util.Calendar
|
||||
|
||||
internal class BalanceItemConverter(
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val yield: Yield,
|
||||
) : Converter<BalanceItem, BalanceState?> {
|
||||
|
||||
override fun convert(value: BalanceItem): BalanceState? {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val appCurrency = appCurrencyProvider()
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
|
|
|
|||
|
|
@ -19,13 +19,11 @@ import kotlinx.collections.immutable.toPersistentList
|
|||
import java.math.BigDecimal
|
||||
|
||||
internal class RewardsValidatorStateConverter(
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val yield: Yield,
|
||||
) : Converter<Unit, StakingStates.RewardsValidatorsState> {
|
||||
override fun convert(value: Unit): StakingStates.RewardsValidatorsState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance
|
||||
return if (yieldBalance is YieldBalance.Data) {
|
||||
val balances = yieldBalance.balance.items
|
||||
|
|
|
|||
|
|
@ -15,18 +15,17 @@ import com.tangem.utils.converter.Converter
|
|||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class YieldBalancesConverter(
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
||||
private val yield: Yield,
|
||||
) : Converter<Unit, InnerYieldBalanceState> {
|
||||
|
||||
private val balanceItemConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
BalanceItemConverter(cryptoCurrencyStatusProvider, appCurrencyProvider, yield)
|
||||
BalanceItemConverter(cryptoCurrencyStatus, appCurrencyProvider, yield)
|
||||
}
|
||||
|
||||
override fun convert(value: Unit): InnerYieldBalanceState {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val appCurrency = appCurrencyProvider()
|
||||
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
|
@ -68,7 +67,6 @@ internal class YieldBalancesConverter(
|
|||
.toPersistentList()
|
||||
|
||||
private fun getRewardBlockType(): Pair<RewardBlockType, Boolean> {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
val blockchainId = cryptoCurrencyStatus.currency.network.id.value
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val rewards = yieldBalance?.balance?.items
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.helpers
|
||||
|
||||
import com.tangem.domain.staking.FetchStakingYieldBalanceUseCase
|
||||
import com.tangem.domain.staking.FetchActionsUseCase
|
||||
import com.tangem.domain.staking.FetchStakingYieldBalanceUseCase
|
||||
import com.tangem.domain.staking.model.stakekit.Yield
|
||||
import com.tangem.domain.staking.model.stakekit.action.StakingActionStatus
|
||||
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ internal class SetConfirmationStateAssentTransformer(
|
|||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
private val fee: Fee,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
|
|
@ -32,7 +33,9 @@ internal class SetConfirmationStateAssentTransformer(
|
|||
appCurrency = appCurrencyProvider(),
|
||||
isFeeApproximate = false,
|
||||
),
|
||||
isPrimaryButtonEnabled = true,
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
)
|
||||
} else {
|
||||
return this
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.staking.impl.presentation.state.*
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.features.staking.impl.presentation.state.TransactionDoneState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class SetConfirmationStateCompletedTransformer(
|
||||
private val txUrl: String,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
|
|
@ -18,7 +23,9 @@ internal class SetConfirmationStateCompletedTransformer(
|
|||
private fun StakingStates.ConfirmationState.copyWrapped(): StakingStates.ConfirmationState {
|
||||
return if (this is StakingStates.ConfirmationState.Data) {
|
||||
copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
innerState = InnerConfirmationStakingState.COMPLETED,
|
||||
footerText = TextReference.EMPTY,
|
||||
notifications = persistentListOf(),
|
||||
|
|
|
|||
|
|
@ -70,7 +70,9 @@ internal class SetConfirmationStateInitTransformer(
|
|||
actionType = actionType,
|
||||
balanceState = balanceState,
|
||||
confirmationState = StakingStates.ConfirmationState.Data(
|
||||
isPrimaryButtonEnabled = false,
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
feeState = FeeState.Loading,
|
||||
notifications = persistentListOf(),
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
package com.tangem.features.staking.impl.presentation.state.transformers
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.features.staking.impl.presentation.state.InnerConfirmationStakingState
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingStates
|
||||
import com.tangem.features.staking.impl.presentation.state.StakingUiState
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal object SetConfirmationStateResetAssentTransformer : Transformer<StakingUiState> {
|
||||
internal class SetConfirmationStateResetAssentTransformer(
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val confirmationState = prevState.confirmationState
|
||||
return prevState.copy(
|
||||
confirmationState = if (confirmationState is StakingStates.ConfirmationState.Data) {
|
||||
confirmationState.copy(
|
||||
isPrimaryButtonEnabled = true,
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
innerState = InnerConfirmationStakingState.ASSENT,
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ internal class SetInitialDataStateTransformer(
|
|||
private val clickIntents: StakingClickIntents,
|
||||
private val yield: Yield,
|
||||
private val isAnyTokenStaked: Boolean,
|
||||
private val cryptoCurrencyStatusProvider: Provider<CryptoCurrencyStatus>,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
private val userWalletProvider: Provider<UserWallet>,
|
||||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val balancesToShowProvider: Provider<List<BalanceItem>>,
|
||||
|
|
@ -49,20 +49,20 @@ internal class SetInitialDataStateTransformer(
|
|||
private val iconStateConverter by lazy(::CryptoCurrencyToIconStateConverter)
|
||||
|
||||
private val rewardsValidatorStateConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
RewardsValidatorStateConverter(cryptoCurrencyStatusProvider, appCurrencyProvider, yield)
|
||||
RewardsValidatorStateConverter(cryptoCurrencyStatus, appCurrencyProvider, yield)
|
||||
}
|
||||
|
||||
private val yieldBalancesConverter by lazy(LazyThreadSafetyMode.NONE) {
|
||||
YieldBalancesConverter(
|
||||
cryptoCurrencyStatusProvider,
|
||||
appCurrencyProvider,
|
||||
balancesToShowProvider,
|
||||
yield,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
balancesToShowProvider = balancesToShowProvider,
|
||||
yield = yield,
|
||||
)
|
||||
}
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
val cryptoCurrency = cryptoCurrencyStatusProvider().currency
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
return prevState.copy(
|
||||
title = TextReference.EMPTY,
|
||||
cryptoCurrencyName = cryptoCurrency.name,
|
||||
|
|
@ -80,8 +80,12 @@ internal class SetInitialDataStateTransformer(
|
|||
|
||||
private fun createInitialInfoState(): StakingStates.InitialInfoState.Data {
|
||||
val yieldBalance = yieldBalancesConverter.convert(Unit)
|
||||
|
||||
val status = cryptoCurrencyStatus.value
|
||||
return StakingStates.InitialInfoState.Data(
|
||||
isPrimaryButtonEnabled = !cryptoCurrencyStatusProvider().value.amount.isNullOrZero(),
|
||||
isPrimaryButtonEnabled = with(status) {
|
||||
!amount.isNullOrZero() && sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
showBanner = !isAnyTokenStaked && yieldBalance == InnerYieldBalanceState.Empty,
|
||||
aprRange = getAprRange(yield.preferredValidators),
|
||||
infoItems = getInfoItems(),
|
||||
|
|
@ -95,8 +99,6 @@ internal class SetInitialDataStateTransformer(
|
|||
}
|
||||
|
||||
private fun getInfoItems(): PersistentList<RoundedListWithDividersItemData> {
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider()
|
||||
|
||||
return listOfNotNull(
|
||||
createAnnualPercentageRateItem(),
|
||||
createAvailableItem(cryptoCurrencyStatus),
|
||||
|
|
@ -191,7 +193,7 @@ internal class SetInitialDataStateTransformer(
|
|||
private fun createRewardScheduleItem(): RoundedListWithDividersItemData? {
|
||||
val endTextReference = getRewardScheduleText(
|
||||
rewardSchedule = yield.metadata.rewardSchedule,
|
||||
networkId = cryptoCurrencyStatusProvider().currency.network.id.value,
|
||||
networkId = cryptoCurrencyStatus.currency.network.id.value,
|
||||
decapitalize = false,
|
||||
) ?: return null
|
||||
|
||||
|
|
@ -204,7 +206,7 @@ internal class SetInitialDataStateTransformer(
|
|||
}
|
||||
|
||||
private fun createInitialAmountState(): AmountState {
|
||||
val cryptoBalanceValue = cryptoCurrencyStatusProvider().value
|
||||
val cryptoBalanceValue = cryptoCurrencyStatus.value
|
||||
val maxEnterAmount = EnterAmountBoundary(
|
||||
amount = cryptoBalanceValue.amount,
|
||||
fiatAmount = cryptoBalanceValue.fiatAmount,
|
||||
|
|
@ -212,7 +214,7 @@ internal class SetInitialDataStateTransformer(
|
|||
)
|
||||
return AmountStateConverter(
|
||||
clickIntents = clickIntents,
|
||||
cryptoCurrencyStatusProvider = cryptoCurrencyStatusProvider,
|
||||
cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus },
|
||||
appCurrencyProvider = appCurrencyProvider,
|
||||
iconStateConverter = iconStateConverter,
|
||||
maxEnterAmount = maxEnterAmount,
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ internal class SetConfirmationStateAssentApprovalTransformer(
|
|||
private val appCurrencyProvider: Provider<AppCurrency>,
|
||||
private val feeCryptoCurrencyStatus: CryptoCurrencyStatus?,
|
||||
private val fee: TransactionFee,
|
||||
private val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
) : Transformer<StakingUiState> {
|
||||
|
||||
override fun transform(prevState: StakingUiState): StakingUiState {
|
||||
|
|
@ -35,7 +36,9 @@ internal class SetConfirmationStateAssentApprovalTransformer(
|
|||
appCurrency = appCurrencyProvider(),
|
||||
isFeeApproximate = false,
|
||||
),
|
||||
isPrimaryButtonEnabled = true,
|
||||
isPrimaryButtonEnabled = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
},
|
||||
isApprovalNeeded = true,
|
||||
)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -111,6 +111,10 @@ internal class AddStakingNotificationsTransformer(
|
|||
)
|
||||
}.toImmutableList()
|
||||
|
||||
val isActualSources = with(cryptoCurrencyStatus.value) {
|
||||
sources.yieldBalanceSource.isActual() && sources.networkSource.isActual()
|
||||
}
|
||||
|
||||
return prevState.copy(
|
||||
confirmationState = confirmationState.copy(
|
||||
notifications = notifications.toImmutableList(),
|
||||
|
|
@ -119,7 +123,7 @@ internal class AddStakingNotificationsTransformer(
|
|||
it is NotificationUM.Error ||
|
||||
it is NotificationUM.Warning.NetworkFeeUnreachable ||
|
||||
it is StakingNotification.Warning.TransactionInProgress
|
||||
},
|
||||
} && isActualSources,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,10 +44,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
|
||||
|
|
@ -262,6 +259,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
.conflate()
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
sendButtonsEvents(it.states)
|
||||
internalUiState.value = stateFactory.getManageButtonsState(actions = it.states)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
|
|
@ -269,6 +267,21 @@ internal class TokenDetailsModel @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) {
|
||||
modelScope.launch(dispatchers.main) {
|
||||
getCurrencyWarningsUseCase.invoke(
|
||||
|
|
@ -457,14 +470,15 @@ internal class TokenDetailsModel @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
|
||||
}
|
||||
|
||||
|
|
@ -497,7 +511,13 @@ internal class TokenDetailsModel @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)
|
||||
}
|
||||
|
||||
|
|
@ -513,14 +533,15 @@ internal class TokenDetailsModel @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
|
||||
}
|
||||
|
||||
|
|
@ -539,17 +560,19 @@ internal class TokenDetailsModel @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,
|
||||
)
|
||||
) {
|
||||
analyticsEventsHandler.send(
|
||||
TokenScreenAnalyticsEvent.ButtonWithParams.ButtonReceive(
|
||||
token = cryptoCurrency.symbol,
|
||||
blockchain = cryptoCurrency.network.name,
|
||||
status = unavailabilityReason.toReasonAnalyticsText(),
|
||||
),
|
||||
)
|
||||
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
modelScope.launch(dispatchers.main) {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
|
||||
modelScope.launch {
|
||||
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened(cryptoCurrency.symbol))
|
||||
|
||||
internalUiState.value = stateFactory.getStateWithReceiveBottomSheet(
|
||||
|
|
@ -568,12 +591,7 @@ internal class TokenDetailsModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onStakeClick(unavailabilityReason: ScenarioUnavailabilityReason) {
|
||||
if (handleUnavailabilityReason(
|
||||
unavailabilityReason = unavailabilityReason,
|
||||
tokenSymbol = cryptoCurrency.symbol,
|
||||
tokenAction = null,
|
||||
)
|
||||
) {
|
||||
if (handleUnavailabilityReason(unavailabilityReason = unavailabilityReason)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -604,14 +622,15 @@ internal class TokenDetailsModel @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
|
||||
}
|
||||
|
||||
|
|
@ -628,14 +647,15 @@ internal class TokenDetailsModel @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
|
||||
}
|
||||
|
||||
|
|
@ -957,21 +977,8 @@ internal class TokenDetailsModel @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)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,9 +21,14 @@ internal class TokenDetailsBalanceSelectStateConverter(
|
|||
|
||||
override fun convert(value: TokenBalanceSegmentedButtonConfig): TokenDetailsState {
|
||||
return with(currentStateProvider()) {
|
||||
if (stakingBlocksState !is StakingBlockUM.Staked) return this
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatusProvider() ?: return this
|
||||
|
||||
if (stakingBlocksState !is StakingBlockUM.Staked &&
|
||||
stakingBlocksState !is StakingBlockUM.TemporaryUnavailable
|
||||
) {
|
||||
return this
|
||||
}
|
||||
|
||||
val yieldBalance = cryptoCurrencyStatus.value.yieldBalance as? YieldBalance.Data
|
||||
val stakingCryptoAmount = yieldBalance?.getTotalWithRewardsStakingBalance()
|
||||
val stakingFiatAmount = stakingCryptoAmount?.let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) }
|
||||
|
|
|
|||
|
|
@ -40,6 +40,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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
[versions]
|
||||
tangemBlockchainSdk = "develop-977"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "develop-437"
|
||||
tangemCardSdk = "develop-439"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
tangemVico = "2.0.0-alpha.25-tangem-developments8"
|
||||
#tangemVico = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue