Updated on 2026-08-14

This commit is contained in:
Tangem 2023-11-30 10:36:39 +04:00
parent 536dcb4f3e
commit ed5d5b1b33
23 changed files with 269 additions and 187 deletions

View file

@ -14,6 +14,7 @@ import com.tangem.domain.common.extensions.toNetworkId
import com.tangem.domain.tokens.legacy.TradeCryptoAction
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.feature.swap.presentation.SwapFragment
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.tap.common.analytics.events.AnalyticsParam
@ -123,7 +124,10 @@ class TradeCryptoMiddleware {
}
private fun proceedNewBuyAction(state: () -> AppState?, action: TradeCryptoAction.New.Buy) {
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress?.defaultAddress ?: return
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress
?.defaultAddress
?.let(NetworkAddress.Address::value)
?: return
val status = action.cryptoCurrencyStatus
val currency = status.currency
@ -206,7 +210,10 @@ class TradeCryptoMiddleware {
}
private fun proceedNewSellAction(action: TradeCryptoAction.New.Sell) {
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress?.defaultAddress ?: return
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress
?.defaultAddress
?.let(NetworkAddress.Address::value)
?: return
val currency = action.cryptoCurrencyStatus.currency
store.state.globalState.exchangeManager.getUrl(

View file

@ -8,6 +8,7 @@ import com.tangem.core.ui.R
import com.tangem.core.ui.components.SimpleSettingsRow
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.res.TangemTheme
@Composable
@ -24,7 +25,7 @@ private fun ChooseAddressBottomSheetContent(content: ChooseAddressBottomSheetCon
) {
content.addressModels.forEach { addressModel ->
SimpleSettingsRow(
title = addressModel.type.name,
title = addressModel.displayName.resolveReference(),
icon = R.drawable.ic_arrow_top_right_24,
onItemsClick = { content.onClick(addressModel) },
)

View file

@ -0,0 +1,43 @@
package com.tangem.core.ui.components.bottomsheets.tokenreceive
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.tokens.model.NetworkAddress
private const val DSC_ADDRESS_NAME = "DSC"
private const val DEL_ADDRESS_NAME = "Main"
private const val LEGACY_ADDRESS_NAME = "Legacy"
private const val DEFAULT_ADDRESS_NAME = "Default"
fun Set<NetworkAddress.Address>.mapToAddressModels(currency: CryptoCurrency): List<AddressModel> = this
.sortedBy { it.type.ordinal }
.map { address ->
AddressModel(
displayName = currency.network.getAddressDisplayName(address.type),
value = address.value,
type = when (address.type) {
NetworkAddress.Address.Type.Primary -> AddressModel.Type.Default
NetworkAddress.Address.Type.Secondary -> AddressModel.Type.Legacy
},
)
}
private fun Network.getAddressDisplayName(addressType: NetworkAddress.Address.Type): TextReference {
return when (id.value) {
"decimal", "decimal/test" -> {
when (addressType) {
NetworkAddress.Address.Type.Primary -> stringReference(value = DEL_ADDRESS_NAME)
NetworkAddress.Address.Type.Secondary -> stringReference(value = DSC_ADDRESS_NAME)
}
}
else -> {
when (addressType) {
NetworkAddress.Address.Type.Primary -> stringReference(value = DEFAULT_ADDRESS_NAME)
NetworkAddress.Address.Type.Secondary -> stringReference(value = LEGACY_ADDRESS_NAME)
}
}
}
}

View file

@ -1,8 +1,11 @@
package com.tangem.core.ui.components.bottomsheets.tokenreceive
import com.tangem.core.ui.extensions.TextReference
data class AddressModel(
val displayName: TextReference,
val value: String,
val type: Type = Type.Default,
val type: Type,
) {
enum class Type {
Legacy, Default

View file

@ -9,7 +9,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@ -26,6 +26,7 @@ import com.tangem.core.ui.components.SecondaryButtonIconStart
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheet
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.rememberQrPainters
import com.tangem.core.ui.extensions.resolveReference
import com.tangem.core.ui.extensions.shareText
import com.tangem.core.ui.res.TangemTheme
@ -181,13 +182,6 @@ private fun getName(content: TokenReceiveBottomSheetConfig, index: Int): String
return if (content.addresses.size < 2) {
content.name
} else {
"${
stringResource(
id = when (content.addresses[index].type) {
AddressModel.Type.Default -> R.string.address_type_default
AddressModel.Type.Legacy -> R.string.address_type_legacy
},
)
} ${content.name}"
"${content.addresses[index].displayName.resolveReference()} ${content.name}"
}
}

View file

@ -1,12 +1,13 @@
package com.tangem.core.ui.components.bottomsheets.tokenreceive
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import kotlinx.collections.immutable.ImmutableList
class TokenReceiveBottomSheetConfig(
val name: String,
val symbol: String,
val network: String,
val addresses: List<AddressModel>,
val addresses: ImmutableList<AddressModel>,
val onCopyClick: () -> Unit,
val onShareClick: () -> Unit,
) : TangemBottomSheetConfigContent

View file

@ -28,7 +28,6 @@ fun getActiveIconRes(blockchainId: String): Int {
"GNO" -> R.drawable.img_gnosis_22
"ETH-Pow", "ETH-Pow/test" -> R.drawable.img_eth_pow_22
"ETH-Fair" -> R.drawable.img_eth_fair_22
"NEAR", "NEAR/test" -> R.drawable.img_near_22
"Polkadot", "Polkadot/test" -> R.drawable.img_polkadot_22
"Kusama" -> R.drawable.img_kusama_22
"OPTIMISM", "OPTIMISM/test" -> R.drawable.img_optimism_22

View file

@ -2,6 +2,7 @@ package com.tangem.data.tokens.utils
import com.tangem.domain.tokens.model.*
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.walletmanager.model.Address
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
@ -20,12 +21,12 @@ internal class NetworkStatusFactory {
is UpdateWalletManagerResult.MissedDerivation -> NetworkStatus.MissedDerivation
is UpdateWalletManagerResult.Unreachable -> NetworkStatus.Unreachable
is UpdateWalletManagerResult.NoAccount -> NetworkStatus.NoAccount(
address = getNetworkAddress(result.defaultAddress, result.addresses),
address = getNetworkAddress(result.selectedAddress, result.addresses),
amountToCreateAccount = result.amountToCreateAccount,
errorMessage = result.errorMessage,
)
is UpdateWalletManagerResult.Verified -> NetworkStatus.Verified(
address = getNetworkAddress(result.defaultAddress, result.addresses),
address = getNetworkAddress(result.selectedAddress, result.addresses),
amounts = formatAmounts(result.currenciesAmounts, currencies),
pendingTransactions = formatTransactions(
transactions = result.currentTransactions,
@ -92,11 +93,26 @@ internal class NetworkStatusFactory {
return transactions.mapTo(hashSetOf()) { it.txHistoryItem }
}
private fun getNetworkAddress(defaultAddress: String, availableAddresses: Set<String>): NetworkAddress {
private fun getNetworkAddress(selectedAddress: String, availableAddresses: Set<Address>): NetworkAddress {
val defaultAddress = availableAddresses
.firstOrNull { it.value == selectedAddress }
?.let(::mapToDomainAddress)
requireNotNull(defaultAddress) { "Selected address must not be null" }
return if (availableAddresses.size != 1) {
NetworkAddress.Selectable(defaultAddress, availableAddresses)
NetworkAddress.Selectable(defaultAddress, availableAddresses.mapTo(hashSetOf(), ::mapToDomainAddress))
} else {
NetworkAddress.Single(defaultAddress)
}
}
private fun mapToDomainAddress(address: Address): NetworkAddress.Address {
val type = when (address.type) {
Address.Type.Primary -> NetworkAddress.Address.Type.Primary
Address.Type.Secondary -> NetworkAddress.Address.Type.Secondary
}
return NetworkAddress.Address(address.value, type)
}
}

View file

@ -195,8 +195,7 @@ fun Blockchain.toCoinId(): String {
Blockchain.Chia, Blockchain.ChiaTestnet -> "chia"
Blockchain.Near -> "near"
Blockchain.NearTestnet -> "near/test"
Blockchain.Decimal -> "decimal"
Blockchain.DecimalTestnet -> "decimal/test"
Blockchain.Decimal, Blockchain.DecimalTestnet -> "decimal"
Blockchain.Unknown -> "unknown"
}
}
@ -224,6 +223,4 @@ private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5
private val excludedBlockchains = listOf(
Blockchain.Unknown,
Blockchain.Ducatus,
Blockchain.Decimal,
Blockchain.DecimalTestnet,
)

View file

@ -116,6 +116,7 @@ interface WalletManagersFacade {
* @param userWalletId selected wallet id
* @param network network of currency
*/
@Deprecated("Use NetworkAddress from CryptoCurrencyStatus")
suspend fun getAddress(userWalletId: UserWalletId, network: Network): List<Address>
/** Returns list of all addresses for all currencies in selected wallet
@ -123,6 +124,7 @@ interface WalletManagersFacade {
* @param userWalletId selected wallet id
* @param network required to create wallet manager
*/
@Deprecated("Use NetworkAddress from CryptoCurrencyStatus")
suspend fun getAddresses(userWalletId: UserWalletId, network: Network): Set<Address>
/**
@ -156,6 +158,7 @@ interface WalletManagersFacade {
* @param userWalletId selected wallet id
* @param network network of currency
*/
@Deprecated("Will be removed in future")
suspend fun getFee(
amount: Amount,
destination: String,
@ -171,6 +174,7 @@ interface WalletManagersFacade {
* @param userWalletId selected wallet id
* @param network network of currency
*/
@Deprecated("Will be removed in future")
suspend fun validateTransaction(
amount: Amount,
fee: Amount?,
@ -189,6 +193,7 @@ interface WalletManagersFacade {
* @param network network of currency
*/
@Suppress("LongParameterList")
@Deprecated("Will be removed in future")
suspend fun createTransaction(
amount: Amount,
fee: Fee,
@ -206,6 +211,7 @@ interface WalletManagersFacade {
* @param userWalletId selected wallet id
* @param network network of currency
*/
@Deprecated("Will be removed in future")
suspend fun sendTransaction(
txData: TransactionData,
signer: CommonSigner,

View file

@ -0,0 +1,11 @@
package com.tangem.domain.walletmanager.model
data class Address(
val value: String,
val type: Type,
) {
enum class Type {
Primary, Secondary,
}
}

View file

@ -9,15 +9,15 @@ sealed class UpdateWalletManagerResult {
object Unreachable : UpdateWalletManagerResult()
data class Verified(
val defaultAddress: String,
val addresses: Set<String>,
val selectedAddress: String,
val addresses: Set<Address>,
val currenciesAmounts: Set<CryptoCurrencyAmount>,
val currentTransactions: Set<CryptoCurrencyTransaction>,
) : UpdateWalletManagerResult()
data class NoAccount(
val defaultAddress: String,
val addresses: Set<String>,
val selectedAddress: String,
val addresses: Set<Address>,
val amountToCreateAccount: BigDecimal,
val errorMessage: String,
) : UpdateWalletManagerResult()

View file

@ -1,15 +1,17 @@
package com.tangem.domain.walletmanager.utils
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.Address
import com.tangem.blockchain.common.address.AddressType
import com.tangem.domain.common.extensions.amountToCreateAccount
import com.tangem.domain.txhistory.models.TxHistoryItem
import com.tangem.domain.walletmanager.model.Address
import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount
import com.tangem.domain.walletmanager.model.CryptoCurrencyTransaction
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
import timber.log.Timber
import java.math.BigDecimal
import java.util.concurrent.TimeUnit
import com.tangem.blockchain.common.address.Address as SdkAddress
internal class UpdateWalletManagerResultFactory {
@ -18,7 +20,7 @@ internal class UpdateWalletManagerResultFactory {
val addresses = getAvailableAddresses(wallet.addresses)
return UpdateWalletManagerResult.Verified(
defaultAddress = wallet.address,
selectedAddress = wallet.address,
addresses = addresses,
currenciesAmounts = getTokensAmounts(wallet.amounts.values.toSet()),
currentTransactions = getCurrentTransactions(addresses, wallet.recentTransactions.toSet()),
@ -30,7 +32,7 @@ internal class UpdateWalletManagerResultFactory {
val addresses = getAvailableAddresses(wallet.addresses)
return UpdateWalletManagerResult.Verified(
defaultAddress = wallet.address,
selectedAddress = wallet.address,
addresses = addresses,
currenciesAmounts = getDemoTokensAmounts(demoAmount, walletManager.cardTokens),
currentTransactions = getCurrentTransactions(addresses, wallet.recentTransactions.toSet()),
@ -48,7 +50,7 @@ internal class UpdateWalletManagerResultFactory {
UpdateWalletManagerResult.Unreachable
} else {
UpdateWalletManagerResult.NoAccount(
defaultAddress = wallet.address,
selectedAddress = wallet.address,
addresses = getAvailableAddresses(wallet.addresses),
amountToCreateAccount = amountToCreateAccount,
errorMessage = customMessage,
@ -70,7 +72,7 @@ internal class UpdateWalletManagerResultFactory {
}
private fun getCurrentTransactions(
walletAddresses: Set<String>,
walletAddresses: Set<Address>,
recentTransactions: Set<TransactionData>,
): Set<CryptoCurrencyTransaction> {
val unconfirmedTransactions = recentTransactions.filter {
@ -95,7 +97,7 @@ internal class UpdateWalletManagerResultFactory {
}
private fun createCurrencyTransaction(
walletAddresses: Set<String>,
walletAddresses: Set<Address>,
data: TransactionData,
): CryptoCurrencyTransaction? {
return when (val type = data.amount.type) {
@ -115,11 +117,11 @@ internal class UpdateWalletManagerResultFactory {
}
}
private fun createTxHistoryItem(walletAddresses: Set<String>, data: TransactionData): TxHistoryItem? {
private fun createTxHistoryItem(walletAddresses: Set<Address>, data: TransactionData): TxHistoryItem? {
val hash = data.hash ?: return null
val millis = data.date?.timeInMillis ?: return null
val amount = getTransactionAmountValue(data.amount) ?: return null
val isOutgoing = data.sourceAddress in walletAddresses
val isOutgoing = data.sourceAddress in walletAddresses.map { it.value }
return TxHistoryItem(
txHash = hash,
@ -141,8 +143,16 @@ internal class UpdateWalletManagerResultFactory {
)
}
private fun getAvailableAddresses(addresses: Set<Address>): Set<String> {
return addresses.mapTo(hashSetOf()) { it.value }
private fun getAvailableAddresses(addresses: Set<SdkAddress>): Set<Address> {
return addresses.mapTo(hashSetOf()) { sdkAddress ->
Address(
value = sdkAddress.value,
type = when (sdkAddress.type) {
AddressType.Default -> Address.Type.Primary
AddressType.Legacy -> Address.Type.Secondary
},
)
}
}
private fun getCurrencyAmountValue(amount: Amount): BigDecimal? {

View file

@ -78,7 +78,7 @@ data class CryptoCurrencyStatus(
override val fiatAmount: BigDecimal?,
override val priceChange: BigDecimal?,
override val fiatRate: BigDecimal?,
override val networkAddress: NetworkAddress?,
override val networkAddress: NetworkAddress,
) : Status(isError = false) {
override val amount: BigDecimal = BigDecimal.ZERO
@ -102,7 +102,7 @@ data class CryptoCurrencyStatus(
override val priceChange: BigDecimal,
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxHistoryItem>,
override val networkAddress: NetworkAddress?,
override val networkAddress: NetworkAddress,
) : Status(isError = false)
/**
@ -123,7 +123,7 @@ data class CryptoCurrencyStatus(
override val priceChange: BigDecimal?,
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxHistoryItem>,
override val networkAddress: NetworkAddress?,
override val networkAddress: NetworkAddress,
) : Status(isError = false)
/**
@ -138,6 +138,6 @@ data class CryptoCurrencyStatus(
override val amount: BigDecimal,
override val hasCurrentNetworkTransactions: Boolean,
override val pendingTransactions: Set<TxHistoryItem>,
override val networkAddress: NetworkAddress?,
override val networkAddress: NetworkAddress,
) : Status(isError = false)
}

View file

@ -6,18 +6,19 @@ package com.tangem.domain.tokens.model
sealed class NetworkAddress {
/** The default or currently selected network address. */
abstract val defaultAddress: String
abstract val defaultAddress: Address
/** The set of available network addresses to choose from. */
abstract val availableAddresses: Set<Address>
/**
* Represents a single static network address.
*
* @property defaultAddress The static network address.
*/
data class Single(override val defaultAddress: String) : NetworkAddress() {
data class Single(override val defaultAddress: Address) : NetworkAddress() {
init {
checkDefaultAddress()
}
override val availableAddresses: Set<Address> = setOf(defaultAddress)
}
/**
@ -27,17 +28,26 @@ sealed class NetworkAddress {
* @property availableAddresses The set of available network addresses to choose from.
*/
data class Selectable(
override val defaultAddress: String,
val availableAddresses: Set<String>,
override val defaultAddress: Address,
override val availableAddresses: Set<Address>,
) : NetworkAddress() {
init {
checkDefaultAddress()
require(availableAddresses.isNotEmpty()) { "Available network addresses must not be empty" }
}
}
protected fun checkDefaultAddress() {
require(defaultAddress.isNotBlank()) { "Selected network address must not be blank" }
data class Address(
val value: String,
val type: Type,
) {
enum class Type {
Primary, Secondary,
}
init {
require(value.isNotBlank()) { "Address value must not be blank" }
}
}
}

View file

@ -118,7 +118,9 @@ internal class GetPrimaryCurrencyStatusUpdatesUseCaseTest {
amount = BigDecimal.TEN,
hasCurrentNetworkTransactions = false,
pendingTransactions = emptySet(),
networkAddress = NetworkAddress.Single(defaultAddress = "mock"),
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)
}

View file

@ -51,7 +51,9 @@ internal object MockNetworks {
network = network3,
value = NetworkStatus.NoAccount(
amountToCreateAccount = amountToCreateAccount,
address = NetworkAddress.Single(defaultAddress = "mock"),
address = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
errorMessage = "",
),
)
@ -67,7 +69,9 @@ internal object MockNetworks {
MockTokens.token3.id to CryptoCurrencyAmountStatus.Loaded(BigDecimal.TEN),
),
pendingTransactions = emptyMap(),
address = NetworkAddress.Single(defaultAddress = "mock"),
address = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)
@ -80,7 +84,9 @@ internal object MockNetworks {
MockTokens.token6.id to CryptoCurrencyAmountStatus.Loaded(BigDecimal.TEN),
),
pendingTransactions = emptyMap(),
address = NetworkAddress.Single(defaultAddress = "mock"),
address = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)
@ -94,7 +100,9 @@ internal object MockNetworks {
MockTokens.token10.id to CryptoCurrencyAmountStatus.Loaded(BigDecimal.TEN),
),
pendingTransactions = emptyMap(),
address = NetworkAddress.Single(defaultAddress = "mock"),
address = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)

View file

@ -3,6 +3,7 @@ package com.tangem.domain.tokens.mock
import arrow.core.nonEmptyListOf
import com.tangem.domain.tokens.model.CryptoCurrencyAmountStatus
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.tokens.model.NetworkStatus
import java.math.BigDecimal
@ -64,7 +65,9 @@ internal object MockTokensStates {
priceChange = MockQuotes.quote7.priceChange,
fiatRate = MockQuotes.quote7.fiatRate,
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = null,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)
@ -75,7 +78,9 @@ internal object MockTokensStates {
priceChange = MockQuotes.quote8.priceChange,
fiatRate = MockQuotes.quote8.fiatRate,
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = null,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)
@ -86,7 +91,9 @@ internal object MockTokensStates {
priceChange = MockQuotes.quote9.priceChange,
fiatRate = MockQuotes.quote9.fiatRate,
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = null,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)
@ -97,7 +104,9 @@ internal object MockTokensStates {
priceChange = MockQuotes.quote10.priceChange,
fiatRate = MockQuotes.quote10.fiatRate,
amountToCreateAccount = MockNetworks.amountToCreateAccount,
networkAddress = null,
networkAddress = NetworkAddress.Single(
defaultAddress = NetworkAddress.Address(value = "mock", NetworkAddress.Address.Type.Primary),
),
),
)

View file

@ -2,12 +2,11 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.state.factory
import androidx.paging.PagingData
import arrow.core.Either
import com.tangem.blockchain.common.address.Address
import com.tangem.common.Provider
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
import com.tangem.core.ui.components.transactions.state.TransactionState
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.event.consumedEvent
@ -17,6 +16,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
import com.tangem.domain.tokens.error.CurrencyStatusError
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
import com.tangem.domain.tokens.model.NetworkAddress
import com.tangem.domain.tokens.model.TokenActionsState
import com.tangem.domain.tokens.model.warnings.CryptoCurrencyWarning
import com.tangem.domain.txhistory.models.TxHistoryItem
@ -174,7 +174,7 @@ internal class TokenDetailsStateFactory(
fun getStateWithReceiveBottomSheet(
currency: CryptoCurrency,
addresses: List<Address>,
networkAddress: NetworkAddress,
sendCopyAnalyticsEvent: () -> Unit,
sendShareAnalyticsEvent: () -> Unit,
): TokenDetailsState {
@ -186,12 +186,7 @@ internal class TokenDetailsStateFactory(
name = currency.name,
symbol = currency.symbol,
network = currency.network.name,
addresses = addresses.map {
AddressModel(
value = it.value,
type = AddressModel.Type.valueOf(it.type.name),
)
},
addresses = networkAddress.availableAddresses.mapToAddressModels(currency).toImmutableList(),
onCopyClick = sendCopyAnalyticsEvent,
onShareClick = sendShareAnalyticsEvent,
),
@ -199,20 +194,16 @@ internal class TokenDetailsStateFactory(
)
}
fun getStateWithChooseAddressBottomSheet(addresses: List<Address>): TokenDetailsState {
fun getStateWithChooseAddressBottomSheet(
currency: CryptoCurrency,
networkAddress: NetworkAddress,
): TokenDetailsState {
return currentStateProvider().copy(
bottomSheetConfig = TangemBottomSheetConfig(
isShow = true,
onDismissRequest = clickIntents::onDismissBottomSheet,
content = ChooseAddressBottomSheetConfig(
addressModels = addresses
.map { address ->
AddressModel(
value = address.value,
type = AddressModel.Type.valueOf(address.type.name),
)
}
.toImmutableList(),
addressModels = networkAddress.availableAddresses.mapToAddressModels(currency).toImmutableList(),
onClick = clickIntents::onAddressTypeSelected,
),
),

View file

@ -23,12 +23,12 @@ 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.models.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
@ -67,7 +67,6 @@ internal class TokenDetailsViewModel @Inject constructor(
private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
private val getCurrencyWarningsUseCase: GetCurrencyWarningsUseCase,
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
private val walletManagersFacade: WalletManagersFacade,
private val isDemoCardUseCase: IsDemoCardUseCase,
private val reduxStateHolder: ReduxStateHolder,
private val analyticsEventsHandler: AnalyticsEventHandler,
@ -303,19 +302,15 @@ internal class TokenDetailsViewModel @Inject constructor(
}
override fun onReceiveClick() {
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
val networkAddress = cryptoCurrencyStatus?.value?.networkAddress ?: return
viewModelScope.launch(dispatchers.io) {
val addresses = walletManagersFacade.getAddress(
userWalletId = userWalletId,
network = cryptoCurrency.network,
)
analyticsEventsHandler.send(event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened)
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrency.symbol))
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ReceiveScreenOpened)
uiState = stateFactory.getStateWithReceiveBottomSheet(
currency = cryptoCurrency,
addresses = addresses,
networkAddress = networkAddress,
sendCopyAnalyticsEvent = {
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol))
},
@ -378,22 +373,23 @@ internal class TokenDetailsViewModel @Inject constructor(
}
private fun openExplorer() {
viewModelScope.launch(dispatchers.io) {
val addresses = walletManagersFacade.getAddress(
userWalletId = userWalletId,
network = cryptoCurrency.network,
)
val currencyStatus = cryptoCurrencyStatus ?: return
if (addresses.size == 1) {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
addressType = AddressType.Default,
),
)
} else {
uiState = stateFactory.getStateWithChooseAddressBottomSheet(addresses = addresses)
viewModelScope.launch(dispatchers.io) {
when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> {
uiState = stateFactory.getStateWithChooseAddressBottomSheet(cryptoCurrency, addresses)
}
is NetworkAddress.Single -> {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
currency = cryptoCurrency,
addressType = AddressType.Default,
),
)
}
null -> Unit
}
}
}

View file

@ -19,6 +19,7 @@ import com.tangem.core.navigation.AppScreen
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
import com.tangem.core.ui.components.transactions.state.TxHistoryState
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.WrappedList
@ -42,10 +43,7 @@ import com.tangem.domain.settings.*
import com.tangem.domain.tokens.*
import com.tangem.domain.tokens.error.TokenListError
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.NetworkGroup
import com.tangem.domain.tokens.model.TokenList
import com.tangem.domain.tokens.model.*
import com.tangem.domain.tokens.models.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
@ -689,20 +687,11 @@ internal class WalletViewModel @Inject constructor(
}
override fun onReceiveClick(cryptoCurrencyStatus: CryptoCurrencyStatus) {
val state = uiState as? WalletState.ContentState ?: return
analyticsEventsHandler.send(
event = TokenScreenAnalyticsEvent.ButtonReceive(cryptoCurrencyStatus.currency.symbol),
)
viewModelScope.launch(dispatchers.io) {
val userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
val addresses = walletManagersFacade.getAddress(
userWalletId = userWallet.walletId,
network = cryptoCurrencyStatus.currency.network,
)
analyticsEventsHandler.send(event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened)
val currency = cryptoCurrencyStatus.currency
@ -711,12 +700,11 @@ internal class WalletViewModel @Inject constructor(
name = currency.name,
symbol = currency.symbol,
network = currency.network.name,
addresses = addresses.map {
AddressModel(
value = it.value,
type = AddressModel.Type.valueOf(it.type.name),
)
},
addresses = cryptoCurrencyStatus.value.networkAddress
?.availableAddresses
?.mapToAddressModels(currency)
.orEmpty()
.toImmutableList(),
onCopyClick = {
analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol))
},
@ -821,41 +809,39 @@ internal class WalletViewModel @Inject constructor(
private fun openExplorer() {
val state = uiState as? WalletState.ContentState ?: return
val currency = singleWalletCryptoCurrencyStatus?.currency ?: return
val currencyStatus = singleWalletCryptoCurrencyStatus ?: return
val currency = currencyStatus.currency
viewModelScope.launch(dispatchers.main) {
val userWalletId = getWallet(state.walletsListConfig.selectedWalletIndex).walletId
val addresses = walletManagersFacade.getAddress(userWalletId = userWalletId, network = currency.network)
if (addresses.size == 1) {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
currency = currency,
addressType = AddressType.Default,
),
)
} else {
uiState = stateFactory.getStateWithOpenWalletBottomSheet(
ChooseAddressBottomSheetConfig(
addressModels = addresses
.map { address ->
AddressModel(
value = address.value,
type = AddressModel.Type.valueOf(address.type.name),
when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> {
uiState = stateFactory.getStateWithOpenWalletBottomSheet(
ChooseAddressBottomSheetConfig(
addressModels = addresses.availableAddresses
.mapToAddressModels(currency)
.toImmutableList(),
onClick = {
onAddressTypeSelected(
userWalletId = userWalletId,
currency = currency,
addressModel = it,
)
}
.toImmutableList(),
onClick = {
onAddressTypeSelected(
userWalletId = userWalletId,
currency = currency,
addressModel = it,
)
},
),
)
},
),
)
}
is NetworkAddress.Single -> {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
currency = currency,
addressType = AddressType.Default,
),
)
}
null -> Unit
}
}
}

View file

@ -1,12 +1,12 @@
package com.tangem.feature.wallet.presentation.wallet.viewmodels.intents
import com.tangem.blockchain.common.address.Address
import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.api.AnalyticsEventHandler
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.chooseaddress.ChooseAddressBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
import com.tangem.core.ui.components.bottomsheets.tokenreceive.TokenReceiveBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.tokenreceive.mapToAddressModels
import com.tangem.core.ui.extensions.WrappedList
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
@ -20,6 +20,7 @@ import com.tangem.domain.tokens.RemoveCurrencyUseCase
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.models.analytics.TokenReceiveAnalyticsEvent
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
import com.tangem.domain.walletconnect.WalletConnectActions
@ -141,15 +142,15 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
)
viewModelScope.launch(dispatchers.main) {
val currency = cryptoCurrencyStatus.currency
val addresses = walletManagersFacade.getAddress(userWalletId = userWalletId, network = currency.network)
analyticsEventHandler.send(event = TokenReceiveAnalyticsEvent.ReceiveScreenOpened)
stateHolder.update(
OpenBottomSheetTransformer(
userWalletId = userWalletId,
content = createReceiveBottomSheetContent(currency, addresses),
content = createReceiveBottomSheetContent(
currency = cryptoCurrencyStatus.currency,
addresses = cryptoCurrencyStatus.value.networkAddress?.availableAddresses ?: return@launch,
),
onDismissBottomSheet = {
stateHolder.update(CloseBottomSheetTransformer(userWalletId = userWalletId))
},
@ -160,18 +161,13 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
private fun createReceiveBottomSheetContent(
currency: CryptoCurrency,
addresses: List<Address>,
addresses: Set<NetworkAddress.Address>,
): TangemBottomSheetConfigContent {
return TokenReceiveBottomSheetConfig(
name = currency.name,
symbol = currency.symbol,
network = currency.network.name,
addresses = addresses.map { address ->
AddressModel(
value = address.value,
type = AddressModel.Type.valueOf(address.type.name),
)
},
addresses = addresses.mapToAddressModels(currency).toImmutableList(),
onCopyClick = {
analyticsEventHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(currency.symbol))
},
@ -329,40 +325,36 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
val userWalletId = stateHolder.getSelectedWalletId()
viewModelScope.launch(dispatchers.main) {
val currency = getPrimaryCurrencyStatusUpdatesUseCase.unwrap(userWalletId)?.currency ?: return@launch
val addresses = walletManagersFacade.getAddress(userWalletId = userWalletId, network = currency.network)
val currencyStatus = getPrimaryCurrencyStatusUpdatesUseCase.unwrap(userWalletId) ?: return@launch
if (addresses.size == 1) {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
currency = currency,
addressType = AddressType.Default,
),
)
} else {
showChooseAddressBottomSheet(userWalletId, addresses, currency)
when (val addresses = currencyStatus.value.networkAddress) {
is NetworkAddress.Selectable -> {
showChooseAddressBottomSheet(userWalletId, addresses.availableAddresses, currencyStatus.currency)
}
is NetworkAddress.Single -> {
router.openUrl(
url = getExploreUrlUseCase(
userWalletId = userWalletId,
currency = currencyStatus.currency,
addressType = AddressType.Default,
),
)
}
null -> Unit
}
}
}
private fun showChooseAddressBottomSheet(
userWalletId: UserWalletId,
addresses: List<Address>,
addresses: Set<NetworkAddress.Address>,
currency: CryptoCurrency,
) {
stateHolder.update(
OpenBottomSheetTransformer(
userWalletId = userWalletId,
content = ChooseAddressBottomSheetConfig(
addressModels = addresses
.map { address ->
AddressModel(
value = address.value,
type = AddressModel.Type.valueOf(address.type.name),
)
}
.toImmutableList(),
addressModels = addresses.mapToAddressModels(currency).toImmutableList(),
onClick = {
onAddressTypeSelected(
userWalletId = userWalletId,

View file

@ -88,10 +88,10 @@ spr-client = "3.6.2"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "develop-396"
tangemBlockchainSdk = "develop-401"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-312"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
# endregion Tangem
# region Tools