Updated on 2026-08-14
This commit is contained in:
parent
642bcca31f
commit
81a16c8bb1
46 changed files with 610 additions and 615 deletions
|
|
@ -28,13 +28,13 @@ internal object TokensDomainModule {
|
|||
|
||||
@Provides
|
||||
@ViewModelScoped
|
||||
fun provideGetTokenUseCase(
|
||||
fun provideGetPrimaryCurrencyUseCase(
|
||||
tokensRepository: TokensRepository,
|
||||
quotesRepository: QuotesRepository,
|
||||
networksRepository: NetworksRepository,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): GetTokenUseCase {
|
||||
return GetTokenUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
|
||||
): GetPrimaryCurrencyUseCase {
|
||||
return GetPrimaryCurrencyUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers)
|
||||
}
|
||||
|
||||
@Provides
|
||||
|
|
|
|||
|
|
@ -7,61 +7,61 @@ import java.math.BigDecimal
|
|||
internal object MockQuotes {
|
||||
|
||||
val quote1 = Quote(
|
||||
tokenId = MockTokens.token1.id,
|
||||
currencyId = MockTokens.token1.id,
|
||||
fiatRate = BigDecimal("1.23"),
|
||||
priceChange = BigDecimal("0.01"),
|
||||
)
|
||||
|
||||
val quote2 = Quote(
|
||||
tokenId = MockTokens.token2.id,
|
||||
currencyId = MockTokens.token2.id,
|
||||
fiatRate = BigDecimal("2.34"),
|
||||
priceChange = BigDecimal("-0.02"),
|
||||
)
|
||||
|
||||
val quote3 = Quote(
|
||||
tokenId = MockTokens.token3.id,
|
||||
currencyId = MockTokens.token3.id,
|
||||
fiatRate = BigDecimal("3.45"),
|
||||
priceChange = BigDecimal("0.03"),
|
||||
)
|
||||
|
||||
val quote4 = Quote(
|
||||
tokenId = MockTokens.token4.id,
|
||||
currencyId = MockTokens.token4.id,
|
||||
fiatRate = BigDecimal("4.56"),
|
||||
priceChange = BigDecimal("-0.04"),
|
||||
)
|
||||
|
||||
val quote5 = Quote(
|
||||
tokenId = MockTokens.token5.id,
|
||||
currencyId = MockTokens.token5.id,
|
||||
fiatRate = BigDecimal("5.67"),
|
||||
priceChange = BigDecimal("0.05"),
|
||||
)
|
||||
|
||||
val quote6 = Quote(
|
||||
tokenId = MockTokens.token6.id,
|
||||
currencyId = MockTokens.token6.id,
|
||||
fiatRate = BigDecimal("6.78"),
|
||||
priceChange = BigDecimal("-0.06"),
|
||||
)
|
||||
|
||||
val quote7 = Quote(
|
||||
tokenId = MockTokens.token7.id,
|
||||
currencyId = MockTokens.token7.id,
|
||||
fiatRate = BigDecimal("7.89"),
|
||||
priceChange = BigDecimal("0.07"),
|
||||
)
|
||||
|
||||
val quote8 = Quote(
|
||||
tokenId = MockTokens.token8.id,
|
||||
currencyId = MockTokens.token8.id,
|
||||
fiatRate = BigDecimal("8.90"),
|
||||
priceChange = BigDecimal("-0.08"),
|
||||
)
|
||||
|
||||
val quote9 = Quote(
|
||||
tokenId = MockTokens.token9.id,
|
||||
currencyId = MockTokens.token9.id,
|
||||
fiatRate = BigDecimal("9.01"),
|
||||
priceChange = BigDecimal("0.09"),
|
||||
)
|
||||
|
||||
val quote10 = Quote(
|
||||
tokenId = MockTokens.token10.id,
|
||||
currencyId = MockTokens.token10.id,
|
||||
fiatRate = BigDecimal("10.12"),
|
||||
priceChange = BigDecimal("-0.10"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,25 +1,23 @@
|
|||
package com.tangem.data.tokens.mock
|
||||
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
internal object MockTokens {
|
||||
|
||||
val token1
|
||||
get() = Token(
|
||||
id = Token.ID("token1"),
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID("token1"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 1",
|
||||
symbol = "T1",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token2
|
||||
get() = Token(
|
||||
id = Token.ID("token2"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token2"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 2",
|
||||
symbol = "T2",
|
||||
|
|
@ -30,8 +28,8 @@ internal object MockTokens {
|
|||
derivationPath = null,
|
||||
)
|
||||
val token3
|
||||
get() = Token(
|
||||
id = Token.ID("token3"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token3"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 3",
|
||||
symbol = "T3",
|
||||
|
|
@ -42,20 +40,18 @@ internal object MockTokens {
|
|||
derivationPath = null,
|
||||
)
|
||||
val token4
|
||||
get() = Token(
|
||||
id = Token.ID("token4"),
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID("token4"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 4",
|
||||
symbol = "T4",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token5
|
||||
get() = Token(
|
||||
id = Token.ID("token5"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token5"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 5",
|
||||
symbol = "T5",
|
||||
|
|
@ -66,8 +62,8 @@ internal object MockTokens {
|
|||
derivationPath = null,
|
||||
)
|
||||
val token6
|
||||
get() = Token(
|
||||
id = Token.ID("token6"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token6"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 6",
|
||||
symbol = "T6",
|
||||
|
|
@ -78,20 +74,18 @@ internal object MockTokens {
|
|||
derivationPath = null,
|
||||
)
|
||||
val token7
|
||||
get() = Token(
|
||||
id = Token.ID("token7"),
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID("token7"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 7",
|
||||
symbol = "T7",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token8
|
||||
get() = Token(
|
||||
id = Token.ID("token8"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token8"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 8",
|
||||
symbol = "T8",
|
||||
|
|
@ -102,8 +96,8 @@ internal object MockTokens {
|
|||
derivationPath = null,
|
||||
)
|
||||
val token9
|
||||
get() = Token(
|
||||
id = Token.ID("token9"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token9"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 9",
|
||||
symbol = "T9",
|
||||
|
|
@ -114,8 +108,8 @@ internal object MockTokens {
|
|||
derivationPath = null,
|
||||
)
|
||||
val token10
|
||||
get() = Token(
|
||||
id = Token.ID("token10"),
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token10"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 10",
|
||||
symbol = "T10",
|
||||
|
|
|
|||
|
|
@ -1,15 +1,15 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import com.tangem.data.common.cache.CacheRegistry
|
||||
import com.tangem.data.tokens.utils.CardTokensFactory
|
||||
import com.tangem.data.tokens.utils.ResponseTokensFactory
|
||||
import com.tangem.data.tokens.utils.CardCurrenciesFactory
|
||||
import com.tangem.data.tokens.utils.ResponseCurrenciesFactory
|
||||
import com.tangem.data.tokens.utils.UserTokensResponseFactory
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.datasource.local.token.UserTokensStore
|
||||
import com.tangem.datasource.local.userwallet.UserWalletsStore
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.repository.TokensRepository
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
@ -27,18 +27,18 @@ internal class DefaultTokensRepository(
|
|||
) : TokensRepository {
|
||||
|
||||
private val demoConfig = DemoConfig()
|
||||
private val responseTokensFactory = ResponseTokensFactory(demoConfig)
|
||||
private val cardTokensFactory = CardTokensFactory(demoConfig)
|
||||
private val responseCurrenciesFactory = ResponseCurrenciesFactory(demoConfig)
|
||||
private val cardCurrenciesFactory = CardCurrenciesFactory(demoConfig)
|
||||
private val userTokensResponseFactory = UserTokensResponseFactory()
|
||||
|
||||
override suspend fun saveTokens(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: Set<Token>,
|
||||
currencies: Set<CryptoCurrency>,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
val response = userTokensResponseFactory.createUserTokensResponse(
|
||||
tokens = tokens,
|
||||
currencies = currencies,
|
||||
isGroupedByNetwork = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
|
@ -46,7 +46,7 @@ internal class DefaultTokensRepository(
|
|||
storeAndPushTokens(userWalletId, response)
|
||||
}
|
||||
|
||||
override suspend fun getSingleCurrencyWalletToken(userWalletId: UserWalletId): Token {
|
||||
override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {
|
||||
val userWallet = withContext(dispatchers.io) {
|
||||
requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
"Unable to find a user wallet with provided ID: $userWalletId"
|
||||
|
|
@ -56,11 +56,13 @@ internal class DefaultTokensRepository(
|
|||
"Single currency wallet excepted, but multi currency wallet was found: $userWalletId"
|
||||
}
|
||||
|
||||
return cardTokensFactory
|
||||
.createPrimaryTokenForSingleCurrencyCard(userWallet.scanResponse)
|
||||
return cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse)
|
||||
}
|
||||
|
||||
override fun getMultiCurrencyWalletTokens(userWalletId: UserWalletId, refresh: Boolean): Flow<Set<Token>> {
|
||||
override fun getMultiCurrencyWalletCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<CryptoCurrency>> {
|
||||
return channelFlow {
|
||||
val userWallet = withContext(dispatchers.io) {
|
||||
requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) {
|
||||
|
|
@ -72,7 +74,7 @@ internal class DefaultTokensRepository(
|
|||
}
|
||||
|
||||
launch(dispatchers.io) {
|
||||
getMultiCurrencyWalletTokens(userWallet).collectLatest(::send)
|
||||
getMultiCurrencyWalletCurrencies(userWallet).collectLatest(::send)
|
||||
}
|
||||
|
||||
launch(dispatchers.io) {
|
||||
|
|
@ -93,9 +95,9 @@ internal class DefaultTokensRepository(
|
|||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private fun getMultiCurrencyWalletTokens(userWallet: UserWallet): Flow<Set<Token>> {
|
||||
private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow<Set<CryptoCurrency>> {
|
||||
return userTokensStore.get(userWallet.walletId).map { storedTokens ->
|
||||
responseTokensFactory.createTokens(
|
||||
responseCurrenciesFactory.createTokens(
|
||||
response = storedTokens,
|
||||
card = userWallet.scanResponse.card,
|
||||
)
|
||||
|
|
@ -131,7 +133,9 @@ internal class DefaultTokensRepository(
|
|||
if (NOT_FOUND_HTTP_CODE in errorMessage) {
|
||||
val response = userTokensStore.getSyncOrNull(userWallet.walletId)
|
||||
?: userTokensResponseFactory.createUserTokensResponse(
|
||||
tokens = cardTokensFactory.createDefaultTokensForMultiCurrencyCard(userWallet.scanResponse.card),
|
||||
currencies = cardCurrenciesFactory.createDefaultCoinsForMultiCurrencyCard(
|
||||
userWallet.scanResponse.card,
|
||||
),
|
||||
isGroupedByNetwork = false,
|
||||
isSortedByBalance = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import com.tangem.data.tokens.mock.MockNetworks
|
||||
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.Token
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -19,7 +19,7 @@ internal class MockNetworksRepository : NetworksRepository {
|
|||
|
||||
override fun getNetworkStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Map<Network.ID, Set<Token.ID>>,
|
||||
networks: Map<Network.ID, Set<CryptoCurrency.ID>>,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return flowOf(
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
package com.tangem.data.tokens.repository
|
||||
|
||||
import com.tangem.data.tokens.mock.MockQuotes
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
internal class MockQuotesRepository : QuotesRepository {
|
||||
|
||||
override fun getQuotes(tokensIds: Set<Token.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
override fun getQuotes(tokensIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
return flowOf(
|
||||
MockQuotes.quotes
|
||||
.filter { it.tokenId in tokensIds }
|
||||
.filter { it.currencyId in tokensIds }
|
||||
.toSet(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import com.tangem.domain.common.util.cardTypesResolver
|
|||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
import com.tangem.domain.tokens.model.Token as DomainToken
|
||||
|
||||
internal class CardTokensFactory(private val demoConfig: DemoConfig) {
|
||||
internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) {
|
||||
|
||||
fun createDefaultTokensForMultiCurrencyCard(card: CardDTO): Set<DomainToken> {
|
||||
fun createDefaultCoinsForMultiCurrencyCard(card: CardDTO): Set<CryptoCurrency.Coin> {
|
||||
var blockchains = if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
demoConfig.demoBlockchains
|
||||
} else {
|
||||
|
|
@ -26,7 +26,7 @@ internal class CardTokensFactory(private val demoConfig: DemoConfig) {
|
|||
return blockchains.mapNotNull { createCoin(it, card) }.toSet()
|
||||
}
|
||||
|
||||
fun createPrimaryTokenForSingleCurrencyCard(scanResponse: ScanResponse): DomainToken {
|
||||
fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency {
|
||||
val card = scanResponse.card
|
||||
val resolver = scanResponse.cardTypesResolver
|
||||
val blockchain = resolver.getBlockchain()
|
||||
|
|
@ -41,13 +41,13 @@ internal class CardTokensFactory(private val demoConfig: DemoConfig) {
|
|||
return primaryToken ?: coin
|
||||
}
|
||||
|
||||
private fun createToken(sdkToken: SdkToken, blockchain: Blockchain, card: CardDTO): DomainToken? {
|
||||
private fun createToken(sdkToken: SdkToken, blockchain: Blockchain, card: CardDTO): CryptoCurrency.Token? {
|
||||
if (blockchain != Blockchain.Unknown) {
|
||||
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
|
||||
return null
|
||||
}
|
||||
|
||||
return DomainToken(
|
||||
return CryptoCurrency.Token(
|
||||
id = getTokenId(blockchain, sdkToken),
|
||||
networkId = getNetworkId(blockchain),
|
||||
name = sdkToken.name,
|
||||
|
|
@ -60,21 +60,19 @@ internal class CardTokensFactory(private val demoConfig: DemoConfig) {
|
|||
)
|
||||
}
|
||||
|
||||
private fun createCoin(blockchain: Blockchain, card: CardDTO): DomainToken? {
|
||||
private fun createCoin(blockchain: Blockchain, card: CardDTO): CryptoCurrency.Coin? {
|
||||
if (blockchain != Blockchain.Unknown) {
|
||||
Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain")
|
||||
return null
|
||||
}
|
||||
|
||||
return DomainToken(
|
||||
return CryptoCurrency.Coin(
|
||||
id = getCoinId(blockchain),
|
||||
networkId = getNetworkId(blockchain),
|
||||
name = blockchain.fullName,
|
||||
symbol = blockchain.currency,
|
||||
iconUrl = getCoinIconUrl(blockchain),
|
||||
decimals = blockchain.decimals(),
|
||||
isCustom = false,
|
||||
contractAddress = null,
|
||||
derivationPath = getDerivationPath(blockchain, card),
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) {
|
||||
|
||||
fun createTokens(response: UserTokensResponse, card: CardDTO): Set<CryptoCurrency> {
|
||||
return response.tokens.mapNotNull { createToken(it, card) }.toSet()
|
||||
}
|
||||
|
||||
private fun createToken(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? {
|
||||
var blockchain = Blockchain.fromNetworkId(responseToken.networkId)
|
||||
if (blockchain == null || blockchain == Blockchain.Unknown) {
|
||||
Timber.e("Unable to find a blockchain with the network ID: ${responseToken.networkId}")
|
||||
return null
|
||||
}
|
||||
|
||||
if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
blockchain = blockchain.getTestnetVersion() ?: blockchain
|
||||
}
|
||||
|
||||
val sdkToken = createSdkToken(responseToken)
|
||||
return if (sdkToken == null) {
|
||||
createCoin(blockchain, responseToken)
|
||||
} else {
|
||||
createToken(blockchain, sdkToken, responseToken.derivationPath)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSdkToken(token: UserTokensResponse.Token): SdkToken? {
|
||||
return token.contractAddress?.let { contractAddress ->
|
||||
SdkToken(
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
contractAddress = contractAddress,
|
||||
decimals = token.decimals,
|
||||
id = token.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createCoin(blockchain: Blockchain, responseToken: UserTokensResponse.Token): CryptoCurrency.Coin {
|
||||
return CryptoCurrency.Coin(
|
||||
id = getCoinId(blockchain),
|
||||
networkId = getNetworkId(blockchain),
|
||||
name = responseToken.name,
|
||||
symbol = responseToken.symbol,
|
||||
decimals = responseToken.decimals,
|
||||
derivationPath = responseToken.derivationPath,
|
||||
iconUrl = getCoinIconUrl(blockchain),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createToken(blockchain: Blockchain, sdkToken: Token, derivationPath: String?): CryptoCurrency.Token {
|
||||
val id = getTokenId(blockchain, sdkToken)
|
||||
|
||||
return CryptoCurrency.Token(
|
||||
id = id,
|
||||
networkId = getNetworkId(blockchain),
|
||||
name = sdkToken.name,
|
||||
symbol = sdkToken.symbol,
|
||||
decimals = sdkToken.decimals,
|
||||
derivationPath = derivationPath,
|
||||
iconUrl = getTokenIconUrl(blockchain, sdkToken),
|
||||
contractAddress = sdkToken.contractAddress,
|
||||
isCustom = isCustomToken(id),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.tangem.data.tokens.utils
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import timber.log.Timber
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
|
||||
internal class ResponseTokensFactory(private val demoConfig: DemoConfig) {
|
||||
|
||||
fun createTokens(response: UserTokensResponse, card: CardDTO): Set<Token> {
|
||||
return response.tokens.mapNotNull { createToken(it, card) }.toSet()
|
||||
}
|
||||
|
||||
private fun createToken(token: UserTokensResponse.Token, card: CardDTO): Token? {
|
||||
var blockchain = Blockchain.fromNetworkId(token.networkId)
|
||||
if (blockchain == null) {
|
||||
Timber.e("Unable to find a blockchain with the network ID: ${token.networkId}")
|
||||
return null
|
||||
}
|
||||
|
||||
if (demoConfig.isDemoCardId(card.cardId)) {
|
||||
blockchain = blockchain.getTestnetVersion() ?: blockchain
|
||||
}
|
||||
|
||||
val sdkToken = createSdkToken(token)
|
||||
val (tokenId, iconUrl) = if (sdkToken == null) {
|
||||
getCoinId(blockchain) to getCoinIconUrl(blockchain)
|
||||
} else {
|
||||
getTokenId(blockchain, sdkToken) to getTokenIconUrl(blockchain, sdkToken)
|
||||
}
|
||||
|
||||
return Token(
|
||||
id = tokenId,
|
||||
networkId = getNetworkId(blockchain),
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
decimals = token.decimals,
|
||||
iconUrl = iconUrl,
|
||||
contractAddress = token.contractAddress,
|
||||
derivationPath = token.derivationPath,
|
||||
isCustom = isCustomToken(tokenId),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createSdkToken(token: UserTokensResponse.Token): SdkToken? {
|
||||
return token.contractAddress?.let { contractAddress ->
|
||||
SdkToken(
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
contractAddress = contractAddress,
|
||||
decimals = token.decimals,
|
||||
id = token.id,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -6,9 +6,9 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
|||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.blockchain.common.Token as SdkToken
|
||||
import com.tangem.domain.tokens.model.Token as DomainToken
|
||||
|
||||
private const val DEFAULT_TOKENS_ICONS_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins"
|
||||
private const val TOKEN_ICON_SIZE = "large"
|
||||
|
|
@ -19,7 +19,7 @@ private const val TOKEN_ID_PREFIX = "token_"
|
|||
private const val CUSTOM_TOKEN_ID_PREFIX = "custom_token_"
|
||||
private const val TOKEN_ID_DELIMITER = '#'
|
||||
|
||||
internal fun isCustomToken(tokenId: DomainToken.ID): Boolean {
|
||||
internal fun isCustomToken(tokenId: CryptoCurrency.ID): Boolean {
|
||||
return tokenId.value.startsWith(CUSTOM_TOKEN_ID_PREFIX)
|
||||
}
|
||||
|
||||
|
|
@ -41,17 +41,17 @@ internal fun getNetworkId(blockchain: Blockchain): Network.ID {
|
|||
return Network.ID(value)
|
||||
}
|
||||
|
||||
internal fun getCoinId(blockchain: Blockchain): DomainToken.ID {
|
||||
internal fun getCoinId(blockchain: Blockchain): CryptoCurrency.ID {
|
||||
return getTokenOrCoinId(blockchain, token = null)
|
||||
}
|
||||
|
||||
internal fun getTokenId(blockchain: Blockchain, token: SdkToken): DomainToken.ID {
|
||||
internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency.ID {
|
||||
return getTokenOrCoinId(blockchain, token)
|
||||
}
|
||||
|
||||
internal fun getResponseTokenId(token: DomainToken): String? {
|
||||
return token.id.value.substringAfter(TOKEN_ID_DELIMITER)
|
||||
.takeUnless { token.isCustom }
|
||||
internal fun getResponseTokenId(currency: CryptoCurrency): String? {
|
||||
return currency.id.value.substringAfter(TOKEN_ID_DELIMITER)
|
||||
.takeUnless { currency is CryptoCurrency.Token && currency.isCustom }
|
||||
}
|
||||
|
||||
internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? {
|
||||
|
|
@ -74,7 +74,7 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? {
|
|||
return coinId?.let(::getTokenIconUrlFromDefaultHost)
|
||||
}
|
||||
|
||||
private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): DomainToken.ID {
|
||||
private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): CryptoCurrency.ID {
|
||||
val sdkTokenId = token?.id
|
||||
val (prefix, suffix) = when {
|
||||
token == null -> COIN_ID_PREFIX to blockchain.toCoinId()
|
||||
|
|
@ -89,7 +89,7 @@ private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): DomainTo
|
|||
append(suffix.lowercase())
|
||||
}
|
||||
|
||||
return DomainToken.ID(value)
|
||||
return CryptoCurrency.ID(value)
|
||||
}
|
||||
|
||||
private fun getTokenIconUrlFromDefaultHost(tokenId: String): String {
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@ package com.tangem.data.tokens.utils
|
|||
|
||||
import com.tangem.datasource.api.tangemTech.models.UserTokensResponse
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
internal class UserTokensResponseFactory {
|
||||
|
||||
fun createUserTokensResponse(
|
||||
tokens: Set<Token>,
|
||||
currencies: Set<CryptoCurrency>,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): UserTokensResponse {
|
||||
return UserTokensResponse(
|
||||
tokens = tokens.map(::createResponseToken),
|
||||
tokens = currencies.map(::createResponseToken),
|
||||
group = if (isGroupedByNetwork) {
|
||||
UserTokensResponse.GroupType.NETWORK
|
||||
} else {
|
||||
|
|
@ -26,17 +26,17 @@ internal class UserTokensResponseFactory {
|
|||
)
|
||||
}
|
||||
|
||||
private fun createResponseToken(domainToken: Token): UserTokensResponse.Token {
|
||||
val blockchain = getBlockchain(domainToken.networkId)
|
||||
private fun createResponseToken(currency: CryptoCurrency): UserTokensResponse.Token {
|
||||
val blockchain = getBlockchain(currency.networkId)
|
||||
|
||||
return UserTokensResponse.Token(
|
||||
id = getResponseTokenId(domainToken),
|
||||
id = getResponseTokenId(currency),
|
||||
networkId = blockchain.toNetworkId(),
|
||||
derivationPath = domainToken.derivationPath,
|
||||
name = domainToken.name,
|
||||
symbol = domainToken.symbol,
|
||||
decimals = domainToken.decimals,
|
||||
contractAddress = domainToken.contractAddress,
|
||||
derivationPath = currency.derivationPath,
|
||||
name = currency.name,
|
||||
symbol = currency.symbol,
|
||||
decimals = currency.decimals,
|
||||
contractAddress = (currency as? CryptoCurrency.Token)?.contractAddress,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@ import arrow.core.raise.either
|
|||
import arrow.core.raise.ensureNotNull
|
||||
import arrow.core.toNonEmptySetOrNull
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.repository.TokensRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
|
|
@ -22,7 +22,7 @@ class ApplyTokenListSortingUseCase(
|
|||
|
||||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
sortedTokensIds: Set<Pair<Network.ID, Token.ID>>,
|
||||
sortedTokensIds: Set<Pair<Network.ID, CryptoCurrency.ID>>,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): Either<TokenListSortingError, Unit> {
|
||||
|
|
@ -30,7 +30,7 @@ class ApplyTokenListSortingUseCase(
|
|||
either {
|
||||
applySorting(
|
||||
userWalletId = userWalletId,
|
||||
tokens = sortTokens(sortedTokensIds, getTokens(userWalletId)),
|
||||
tokens = sortTokens(sortedTokensIds, getCurrencies(userWalletId)),
|
||||
isGrouped = isGroupedByNetwork,
|
||||
isSortedByBalance = isSortedByBalance,
|
||||
)
|
||||
|
|
@ -39,14 +39,14 @@ class ApplyTokenListSortingUseCase(
|
|||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.sortTokens(
|
||||
sortedTokensIds: Set<Pair<Network.ID, Token.ID>>,
|
||||
unsortedTokens: Set<Token>,
|
||||
): Set<Token> = withContext(dispatchers.default) {
|
||||
sortedTokensIds: Set<Pair<Network.ID, CryptoCurrency.ID>>,
|
||||
unsortedTokens: Set<CryptoCurrency>,
|
||||
): Set<CryptoCurrency> = withContext(dispatchers.default) {
|
||||
val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) {
|
||||
TokenListSortingError.TokenListIsEmpty
|
||||
}
|
||||
|
||||
val sortedTokens = sortedMapOf<Int, Token>()
|
||||
val sortedTokens = sortedMapOf<Int, CryptoCurrency>()
|
||||
|
||||
unsortedTokens.forEach { token ->
|
||||
val index = nonEmptySortedTokensIds.indexOfFirst { (networkId, tokenId) ->
|
||||
|
|
@ -65,9 +65,9 @@ class ApplyTokenListSortingUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenListSortingError>.getTokens(userWalletId: UserWalletId): Set<Token> {
|
||||
private suspend fun Raise<TokenListSortingError>.getCurrencies(userWalletId: UserWalletId): Set<CryptoCurrency> {
|
||||
val tokens = catch(
|
||||
block = { tokensRepository.getMultiCurrencyWalletTokens(userWalletId, refresh = false).firstOrNull() },
|
||||
block = { tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() },
|
||||
catch = { raise(TokenListSortingError.DataError(it)) },
|
||||
)
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ class ApplyTokenListSortingUseCase(
|
|||
|
||||
private suspend fun Raise<TokenListSortingError>.applySorting(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: Set<Token>,
|
||||
tokens: Set<CryptoCurrency>,
|
||||
isGrouped: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
) = withContext(dispatchers.io) {
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import arrow.core.raise.recover
|
|||
import arrow.core.right
|
||||
import com.tangem.domain.tokens.error.TokenError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenError
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.domain.tokens.operations.TokensStatusesOperations
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.tokens.repository.TokensRepository
|
||||
|
|
@ -18,14 +18,17 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
|
||||
class GetTokenUseCase(
|
||||
class GetPrimaryCurrencyUseCase(
|
||||
private val tokensRepository: TokensRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Flow<Either<TokenError, TokenStatus>> {
|
||||
operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean = false,
|
||||
): Flow<Either<TokenError, CryptoCurrencyStatus>> {
|
||||
return channelFlow {
|
||||
recover(
|
||||
block = {
|
||||
|
|
@ -40,8 +43,11 @@ class GetTokenUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun Raise<TokenError>.getToken(userWalletId: UserWalletId, refresh: Boolean): Flow<TokenStatus> {
|
||||
val operations = TokensStatusesOperations(
|
||||
private suspend fun Raise<TokenError>.getToken(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<CryptoCurrencyStatus> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
tokensRepository = tokensRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
|
|
@ -49,9 +55,9 @@ class GetTokenUseCase(
|
|||
refresh = refresh,
|
||||
dispatchers = dispatchers,
|
||||
raise = this,
|
||||
transformError = TokensStatusesOperations.Error::mapToTokenError,
|
||||
transformError = CurrenciesStatusesOperations.Error::mapToTokenError,
|
||||
)
|
||||
|
||||
return operations.getSingleCurrencyWalletTokenStatusFlow()
|
||||
return operations.getPrimaryCurrencyStatusFlow()
|
||||
}
|
||||
}
|
||||
|
|
@ -7,10 +7,10 @@ import arrow.core.raise.recover
|
|||
import arrow.core.right
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.operations.TokenListOperations
|
||||
import com.tangem.domain.tokens.operations.TokensStatusesOperations
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.QuotesRepository
|
||||
import com.tangem.domain.tokens.repository.TokensRepository
|
||||
|
|
@ -51,21 +51,21 @@ class GetTokenListUseCase(
|
|||
private fun Raise<TokenListError>.getTokensStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<TokenStatus>> {
|
||||
val operations = TokensStatusesOperations(
|
||||
): Flow<Set<CryptoCurrencyStatus>> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
userWalletId = userWalletId,
|
||||
refresh = refresh,
|
||||
useCase = this@GetTokenListUseCase,
|
||||
raise = this,
|
||||
transformError = TokensStatusesOperations.Error::mapToTokenListError,
|
||||
transformError = CurrenciesStatusesOperations.Error::mapToTokenListError,
|
||||
)
|
||||
|
||||
return operations.getMultiCurrencyWalletTokensStatusesFlow()
|
||||
return operations.getMultiCurrencyWalletStatusesFlow()
|
||||
}
|
||||
|
||||
private fun Raise<TokenListError>.createTokenList(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: Set<TokenStatus>,
|
||||
tokens: Set<CryptoCurrencyStatus>,
|
||||
): Flow<TokenList> {
|
||||
val operations = TokenListOperations(
|
||||
userWalletId = userWalletId,
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class ToggleTokenListGroupingUseCase(
|
|||
val sortingOperations = getSortingOperations(tokenList)
|
||||
val tokens = sortingOperations.getTokens()
|
||||
|
||||
val networks = getNetworks(tokens.map { it.networkId }.toSet())
|
||||
val networks = getNetworks(tokens.map { it.currency.networkId }.toSet())
|
||||
return TokenList.GroupedByNetwork(
|
||||
groups = sortingOperations.getGroupedTokens(networks),
|
||||
totalFiatBalance = tokenList.totalFiatBalance,
|
||||
|
|
@ -55,7 +55,7 @@ class ToggleTokenListGroupingUseCase(
|
|||
val sortingOperations = getSortingOperations(tokenList)
|
||||
|
||||
return TokenList.Ungrouped(
|
||||
tokens = sortingOperations.getTokens(),
|
||||
currencies = sortingOperations.getTokens(),
|
||||
totalFiatBalance = tokenList.totalFiatBalance,
|
||||
sortedBy = sortingOperations.getSortType(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class ToggleTokenListSortingUseCase(
|
|||
val operations = getSortingOperations(tokenList)
|
||||
|
||||
return tokenList.copy(
|
||||
tokens = operations.getTokens(),
|
||||
currencies = operations.getTokens(),
|
||||
sortedBy = operations.getSortType(),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.domain.tokens.error.mapper
|
||||
|
||||
import com.tangem.domain.tokens.error.TokenError
|
||||
import com.tangem.domain.tokens.operations.TokensStatusesOperations
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
|
||||
internal fun TokensStatusesOperations.Error.mapToTokenError(): TokenError {
|
||||
internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): TokenError {
|
||||
return when (this) {
|
||||
is TokensStatusesOperations.Error.DataError -> TokenError.DataError(this.cause)
|
||||
is TokensStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is TokensStatusesOperations.Error.EmptyQuotes,
|
||||
is TokensStatusesOperations.Error.EmptyTokens,
|
||||
is CurrenciesStatusesOperations.Error.DataError -> TokenError.DataError(this.cause)
|
||||
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
-> TokenError.UnableToCreateToken
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
package com.tangem.domain.tokens.error.mapper
|
||||
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
import com.tangem.domain.tokens.operations.TokenListOperations
|
||||
import com.tangem.domain.tokens.operations.TokensStatusesOperations
|
||||
|
||||
internal fun TokensStatusesOperations.Error.mapToTokenListError(): TokenListError {
|
||||
internal fun CurrenciesStatusesOperations.Error.mapToTokenListError(): TokenListError {
|
||||
return when (this) {
|
||||
is TokensStatusesOperations.Error.DataError -> TokenListError.DataError(this.cause)
|
||||
is TokensStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is TokensStatusesOperations.Error.EmptyQuotes,
|
||||
is TokensStatusesOperations.Error.EmptyTokens,
|
||||
is CurrenciesStatusesOperations.Error.DataError -> TokenListError.DataError(this.cause)
|
||||
is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses,
|
||||
is CurrenciesStatusesOperations.Error.EmptyQuotes,
|
||||
is CurrenciesStatusesOperations.Error.EmptyCurrencies,
|
||||
-> TokenListError.EmptyTokens
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,61 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
sealed class CryptoCurrency {
|
||||
|
||||
abstract val id: ID
|
||||
abstract val networkId: Network.ID
|
||||
abstract val name: String
|
||||
abstract val symbol: String
|
||||
abstract val decimals: Int
|
||||
abstract val iconUrl: String?
|
||||
abstract val derivationPath: String?
|
||||
|
||||
data class Coin(
|
||||
override val id: ID,
|
||||
override val networkId: Network.ID,
|
||||
override val name: String,
|
||||
override val symbol: String,
|
||||
override val decimals: Int,
|
||||
override val iconUrl: String?,
|
||||
override val derivationPath: String?,
|
||||
) : CryptoCurrency() {
|
||||
|
||||
init {
|
||||
checkProperties()
|
||||
}
|
||||
}
|
||||
|
||||
data class Token(
|
||||
override val id: ID,
|
||||
override val networkId: Network.ID,
|
||||
override val name: String,
|
||||
override val symbol: String,
|
||||
override val decimals: Int,
|
||||
override val iconUrl: String?,
|
||||
override val derivationPath: String?,
|
||||
val contractAddress: String,
|
||||
val isCustom: Boolean,
|
||||
) : CryptoCurrency() {
|
||||
|
||||
init {
|
||||
checkProperties()
|
||||
require(contractAddress.isNotBlank()) { "Token contract address must not be blank" }
|
||||
}
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class ID(val value: String) {
|
||||
|
||||
init {
|
||||
require(value.isNotBlank()) { "Crypto currency ID must not be blank" }
|
||||
}
|
||||
}
|
||||
|
||||
protected fun checkProperties() {
|
||||
require(name.isNotBlank()) { "Crypto currency name must not be blank" }
|
||||
require(symbol.isNotBlank()) { "Crypto currency symbol must not be blank" }
|
||||
require(iconUrl?.isNotBlank() ?: true) { "Crypto currency icon URL must not be blank" }
|
||||
require(decimals > 0) { "Crypto currency decimal must not be less then zero, but it is: $decimals" }
|
||||
require(derivationPath?.isNotBlank() ?: true) { "Crypto currency derivation path must not be blank" }
|
||||
}
|
||||
}
|
||||
|
|
@ -2,14 +2,8 @@ package com.tangem.domain.tokens.model
|
|||
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class TokenStatus(
|
||||
val id: Token.ID,
|
||||
val networkId: Network.ID,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val decimals: Int,
|
||||
val iconUrl: String?,
|
||||
val isCoin: Boolean,
|
||||
data class CryptoCurrencyStatus(
|
||||
val currency: CryptoCurrency,
|
||||
val value: Status,
|
||||
) {
|
||||
|
||||
|
|
@ -2,5 +2,5 @@ package com.tangem.domain.tokens.model
|
|||
|
||||
data class NetworkGroup(
|
||||
val network: Network,
|
||||
val tokens: Set<TokenStatus>,
|
||||
val currencies: Set<CryptoCurrencyStatus>,
|
||||
)
|
||||
|
|
@ -8,16 +8,16 @@ data class NetworkStatus(
|
|||
) {
|
||||
|
||||
sealed class Status {
|
||||
open val amounts: Map<Token.ID, BigDecimal>? = null
|
||||
open val amounts: Map<CryptoCurrency.ID, BigDecimal>? = null
|
||||
}
|
||||
|
||||
object Unreachable : Status()
|
||||
|
||||
object MissedDerivation : Status()
|
||||
|
||||
data class TransactionInProgress(override val amounts: Map<Token.ID, BigDecimal>) : Status()
|
||||
data class TransactionInProgress(override val amounts: Map<CryptoCurrency.ID, BigDecimal>) : Status()
|
||||
|
||||
data class Verified(override val amounts: Map<Token.ID, BigDecimal>) : Status()
|
||||
data class Verified(override val amounts: Map<CryptoCurrency.ID, BigDecimal>) : Status()
|
||||
|
||||
data class NoAccount(val amountToCreateAccount: BigDecimal) : Status()
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ package com.tangem.domain.tokens.model
|
|||
import java.math.BigDecimal
|
||||
|
||||
data class Quote(
|
||||
val tokenId: Token.ID,
|
||||
val currencyId: CryptoCurrency.ID,
|
||||
val fiatRate: BigDecimal,
|
||||
val priceChange: BigDecimal,
|
||||
)
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
package com.tangem.domain.tokens.model
|
||||
|
||||
data class Token(
|
||||
val id: ID,
|
||||
val networkId: Network.ID,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val iconUrl: String?,
|
||||
val decimals: Int,
|
||||
val isCustom: Boolean,
|
||||
val contractAddress: String?,
|
||||
val derivationPath: String?,
|
||||
) {
|
||||
|
||||
init {
|
||||
require(name.isNotBlank()) { "Token name must not be blank" }
|
||||
require(symbol.isNotBlank()) { "Token symbol must not be blank" }
|
||||
require(iconUrl?.isNotBlank() ?: true) { "Token icon URL must not be blank" }
|
||||
require(decimals > 0) { "Token decimal must not be less then zero, but it is: $decimals" }
|
||||
require(contractAddress?.isNotBlank() ?: true) { "Token contract address must not be blank" }
|
||||
require(derivationPath?.isNotBlank() ?: true) { "Token derivation path must not be blank" }
|
||||
}
|
||||
|
||||
@JvmInline
|
||||
value class ID(val value: String) {
|
||||
|
||||
init {
|
||||
require(value.isNotBlank()) { "Token ID value must not be blank" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ sealed class TokenList {
|
|||
) : TokenList()
|
||||
|
||||
data class Ungrouped(
|
||||
val tokens: Set<TokenStatus>,
|
||||
val currencies: Set<CryptoCurrencyStatus>,
|
||||
override val totalFiatBalance: FiatBalance,
|
||||
override val sortedBy: SortType,
|
||||
) : TokenList()
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import kotlinx.coroutines.flow.*
|
|||
import kotlinx.coroutines.withContext
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class TokensStatusesOperations<E>(
|
||||
internal class CurrenciesStatusesOperations<E>(
|
||||
private val tokensRepository: TokensRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
|
|
@ -24,7 +24,7 @@ internal class TokensStatusesOperations<E>(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
raise: Raise<E>,
|
||||
transformError: (Error) -> E,
|
||||
) : DelegatedRaise<TokensStatusesOperations.Error, E>(raise, transformError) {
|
||||
) : DelegatedRaise<CurrenciesStatusesOperations.Error, E>(raise, transformError) {
|
||||
|
||||
constructor(
|
||||
userWalletId: UserWalletId,
|
||||
|
|
@ -43,8 +43,8 @@ internal class TokensStatusesOperations<E>(
|
|||
transformError = transformError,
|
||||
)
|
||||
|
||||
fun getMultiCurrencyWalletTokensStatusesFlow(): Flow<Set<TokenStatus>> {
|
||||
return getMultiCurrencyWalletTokens().flatMapConcat {
|
||||
fun getMultiCurrencyWalletStatusesFlow(): Flow<Set<CryptoCurrencyStatus>> {
|
||||
return getMultiCurrencyWalletCurrencies().flatMapConcat {
|
||||
val tokens = it.toNonEmptySetOrNull()
|
||||
|
||||
if (tokens == null) {
|
||||
|
|
@ -60,12 +60,12 @@ internal class TokensStatusesOperations<E>(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getSingleCurrencyWalletTokenStatusFlow(): Flow<TokenStatus> {
|
||||
val token = getSingleCurrencyWalletToken()
|
||||
suspend fun getPrimaryCurrencyStatusFlow(): Flow<CryptoCurrencyStatus> {
|
||||
val token = getPrimaryCurrency()
|
||||
|
||||
val quoteFlow = getQuotes(nonEmptySetOf(token.id))
|
||||
.map { quotes ->
|
||||
quotes.singleOrNull { it.tokenId == token.id }
|
||||
quotes.singleOrNull { it.currencyId == token.id }
|
||||
}
|
||||
|
||||
val statusFlow = getNetworksStatues(groupTokens(nonEmptySetOf(token)))
|
||||
|
|
@ -79,61 +79,69 @@ internal class TokensStatusesOperations<E>(
|
|||
}
|
||||
|
||||
private suspend fun createTokensStatuses(
|
||||
tokens: Set<Token>,
|
||||
tokens: Set<CryptoCurrency>,
|
||||
quotes: Set<Quote>,
|
||||
networkStatuses: Set<NetworkStatus>,
|
||||
): Set<TokenStatus> = withContext(dispatchers.default) {
|
||||
): Set<CryptoCurrencyStatus> = withContext(dispatchers.default) {
|
||||
tokens.mapTo(hashSetOf()) { token ->
|
||||
val quote = quotes.firstOrNull { it.tokenId == token.id }
|
||||
val quote = quotes.firstOrNull { it.currencyId == token.id }
|
||||
val networkStatus = networkStatuses.firstOrNull { it.networkId == token.networkId }
|
||||
|
||||
createStatus(token, quote, networkStatus)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun createStatus(token: Token, quote: Quote?, networkStatus: NetworkStatus?): TokenStatus {
|
||||
val tokenStatusOperations = TokenStatusOperations(
|
||||
token = token,
|
||||
private suspend fun createStatus(
|
||||
token: CryptoCurrency,
|
||||
quote: Quote?,
|
||||
networkStatus: NetworkStatus?,
|
||||
): CryptoCurrencyStatus {
|
||||
val currencyStatusOperations = CurrencyStatusOperations(
|
||||
currency = token,
|
||||
quote = quote,
|
||||
networkStatus = networkStatus,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
|
||||
return tokenStatusOperations.createTokenStatus()
|
||||
return currencyStatusOperations.createTokenStatus()
|
||||
}
|
||||
|
||||
private fun getMultiCurrencyWalletTokens(): Flow<Set<Token>> {
|
||||
return tokensRepository.getMultiCurrencyWalletTokens(userWalletId, refresh)
|
||||
private fun getMultiCurrencyWalletCurrencies(): Flow<Set<CryptoCurrency>> {
|
||||
return tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh)
|
||||
.catch { raise(Error.DataError(it)) }
|
||||
.onEmpty { raise(Error.EmptyTokens) }
|
||||
.onEmpty { raise(Error.EmptyCurrencies) }
|
||||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private suspend fun getSingleCurrencyWalletToken(): Token {
|
||||
private suspend fun getPrimaryCurrency(): CryptoCurrency {
|
||||
return withContext(dispatchers.io) {
|
||||
catch(
|
||||
block = { tokensRepository.getSingleCurrencyWalletToken(userWalletId) },
|
||||
block = { tokensRepository.getPrimaryCurrency(userWalletId) },
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getQuotes(tokensIds: NonEmptySet<Token.ID>): Flow<Set<Quote>> {
|
||||
private fun getQuotes(tokensIds: NonEmptySet<CryptoCurrency.ID>): Flow<Set<Quote>> {
|
||||
return quotesRepository.getQuotes(tokensIds, refresh)
|
||||
.catch { raise(Error.DataError(it)) }
|
||||
.onEmpty { raise(Error.EmptyQuotes) }
|
||||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private fun getNetworksStatues(groupedTokens: Map<Network.ID, NonEmptySet<Token.ID>>): Flow<Set<NetworkStatus>> {
|
||||
private fun getNetworksStatues(
|
||||
groupedTokens: Map<Network.ID, NonEmptySet<CryptoCurrency.ID>>,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return networksRepository.getNetworkStatuses(userWalletId, groupedTokens, refresh)
|
||||
.catch { raise(Error.DataError(it)) }
|
||||
.onEmpty { raise(Error.EmptyNetworksStatuses) }
|
||||
.flowOn(dispatchers.io)
|
||||
}
|
||||
|
||||
private suspend fun groupTokens(tokens: NonEmptySet<Token>): Map<Network.ID, NonEmptySet<Token.ID>> =
|
||||
withContext(dispatchers.default) {
|
||||
private suspend fun groupTokens(
|
||||
tokens: NonEmptySet<CryptoCurrency>,
|
||||
): Map<Network.ID, NonEmptySet<CryptoCurrency.ID>> {
|
||||
return withContext(dispatchers.default) {
|
||||
tokens
|
||||
.groupBy { it.networkId }
|
||||
.mapValues { (_, tokens) ->
|
||||
|
|
@ -143,10 +151,11 @@ internal class TokensStatusesOperations<E>(
|
|||
.toNonEmptySet()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Error {
|
||||
|
||||
object EmptyTokens : Error()
|
||||
object EmptyCurrencies : Error()
|
||||
|
||||
object EmptyQuotes : Error()
|
||||
|
||||
|
|
@ -1,39 +1,33 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenStatusOperations(
|
||||
private val token: Token,
|
||||
internal class CurrencyStatusOperations(
|
||||
private val currency: CryptoCurrency,
|
||||
private val quote: Quote?,
|
||||
private val networkStatus: NetworkStatus?,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
suspend fun createTokenStatus(): TokenStatus = withContext(dispatchers.default) {
|
||||
TokenStatus(
|
||||
id = token.id,
|
||||
networkId = token.networkId,
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
isCoin = token.contractAddress == null,
|
||||
decimals = token.decimals,
|
||||
iconUrl = token.iconUrl,
|
||||
suspend fun createTokenStatus(): CryptoCurrencyStatus = withContext(dispatchers.default) {
|
||||
CryptoCurrencyStatus(
|
||||
currency = currency,
|
||||
value = createStatus(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun createStatus(): TokenStatus.Status {
|
||||
private fun createStatus(): CryptoCurrencyStatus.Status {
|
||||
return when (val status = networkStatus?.value) {
|
||||
null -> TokenStatus.Loading
|
||||
is NetworkStatus.MissedDerivation -> TokenStatus.MissedDerivation
|
||||
is NetworkStatus.Unreachable -> TokenStatus.Unreachable
|
||||
is NetworkStatus.NoAccount -> TokenStatus.NoAccount
|
||||
null -> CryptoCurrencyStatus.Loading
|
||||
is NetworkStatus.MissedDerivation -> CryptoCurrencyStatus.MissedDerivation
|
||||
is NetworkStatus.Unreachable -> CryptoCurrencyStatus.Unreachable
|
||||
is NetworkStatus.NoAccount -> CryptoCurrencyStatus.NoAccount
|
||||
is NetworkStatus.TransactionInProgress,
|
||||
is NetworkStatus.Verified,
|
||||
-> createStatus(
|
||||
|
|
@ -43,17 +37,17 @@ internal class TokenStatusOperations(
|
|||
}
|
||||
}
|
||||
|
||||
private fun createStatus(amount: BigDecimal, hasTransactionsInProgress: Boolean): TokenStatus.Status {
|
||||
private fun createStatus(amount: BigDecimal, hasTransactionsInProgress: Boolean): CryptoCurrencyStatus.Status {
|
||||
return when {
|
||||
token.isCustom -> TokenStatus.Custom(
|
||||
currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom(
|
||||
amount = amount,
|
||||
fiatAmount = calculateFiatAmountOrNull(amount, quote?.fiatRate),
|
||||
fiatRate = quote?.fiatRate,
|
||||
priceChange = quote?.priceChange,
|
||||
hasTransactionsInProgress = hasTransactionsInProgress,
|
||||
)
|
||||
quote == null -> TokenStatus.Loading
|
||||
else -> TokenStatus.Loaded(
|
||||
quote == null -> CryptoCurrencyStatus.Loading
|
||||
else -> CryptoCurrencyStatus.Loaded(
|
||||
amount = amount,
|
||||
fiatAmount = calculateFiatAmount(amount, quote.fiatRate),
|
||||
fiatRate = quote.fiatRate,
|
||||
|
|
@ -64,7 +58,7 @@ internal class TokenStatusOperations(
|
|||
}
|
||||
|
||||
private fun getTokenAmount(): BigDecimal {
|
||||
val amount = networkStatus?.value?.amounts?.get(token.id)
|
||||
val amount = networkStatus?.value?.amounts?.get(currency.id)
|
||||
return amount ?: error("Incorrect network status: $networkStatus")
|
||||
}
|
||||
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
package com.tangem.domain.tokens.operations
|
||||
|
||||
import arrow.core.NonEmptySet
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenListFiatBalanceOperations(
|
||||
private val tokens: NonEmptySet<TokenStatus>,
|
||||
private val currencies: NonEmptySet<CryptoCurrencyStatus>,
|
||||
private val isAnyTokenLoading: Boolean,
|
||||
private val dispatcher: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
|
@ -18,25 +18,25 @@ internal class TokenListFiatBalanceOperations(
|
|||
var fiatBalance: TokenList.FiatBalance = TokenList.FiatBalance.Loading
|
||||
if (isAnyTokenLoading) return@withContext fiatBalance
|
||||
|
||||
for (token in tokens) {
|
||||
for (token in currencies) {
|
||||
when (val status = token.value) {
|
||||
is TokenStatus.Loading -> {
|
||||
is CryptoCurrencyStatus.Loading -> {
|
||||
fiatBalance = TokenList.FiatBalance.Loading
|
||||
break
|
||||
}
|
||||
is TokenStatus.MissedDerivation,
|
||||
is TokenStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> {
|
||||
fiatBalance = TokenList.FiatBalance.Failed
|
||||
break
|
||||
}
|
||||
is TokenStatus.NoAccount -> {
|
||||
is CryptoCurrencyStatus.NoAccount -> {
|
||||
fiatBalance = recalculateBalanceForNoAccountStatus(fiatBalance)
|
||||
}
|
||||
is TokenStatus.Loaded -> {
|
||||
is CryptoCurrencyStatus.Loaded -> {
|
||||
fiatBalance = recalculateBalance(status, fiatBalance)
|
||||
}
|
||||
is TokenStatus.Custom -> {
|
||||
is CryptoCurrencyStatus.Custom -> {
|
||||
fiatBalance = recalculateBalance(status, fiatBalance)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ internal class TokenListFiatBalanceOperations(
|
|||
}
|
||||
|
||||
private fun recalculateBalance(
|
||||
status: TokenStatus.Loaded,
|
||||
status: CryptoCurrencyStatus.Loaded,
|
||||
currentBalance: TokenList.FiatBalance,
|
||||
): TokenList.FiatBalance {
|
||||
return with(currentBalance) {
|
||||
|
|
@ -71,7 +71,7 @@ internal class TokenListFiatBalanceOperations(
|
|||
}
|
||||
|
||||
private fun recalculateBalance(
|
||||
status: TokenStatus.Custom,
|
||||
status: CryptoCurrencyStatus.Custom,
|
||||
currentBalance: TokenList.FiatBalance,
|
||||
): TokenList.FiatBalance {
|
||||
return with(currentBalance) {
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ import arrow.core.raise.ensureNotNull
|
|||
import arrow.core.toNonEmptySetOrNull
|
||||
import com.tangem.domain.core.raise.DelegatedRaise
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.domain.tokens.repository.NetworksRepository
|
||||
import com.tangem.domain.tokens.repository.TokensRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
|
@ -22,7 +22,7 @@ internal class TokenListOperations<E>(
|
|||
private val tokensRepository: TokensRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
private val userWalletId: UserWalletId,
|
||||
private val tokens: Set<TokenStatus>,
|
||||
private val tokens: Set<CryptoCurrencyStatus>,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
raise: Raise<E>,
|
||||
transform: (Error) -> E,
|
||||
|
|
@ -30,7 +30,7 @@ internal class TokenListOperations<E>(
|
|||
|
||||
constructor(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: Set<TokenStatus>,
|
||||
tokens: Set<CryptoCurrencyStatus>,
|
||||
useCase: GetTokenListUseCase,
|
||||
raise: Raise<E>,
|
||||
transform: (Error) -> E,
|
||||
|
|
@ -55,7 +55,7 @@ internal class TokenListOperations<E>(
|
|||
val tokensNes = tokens.toNonEmptySetOrNull()
|
||||
?: return@withContext TokenList.NotInitialized
|
||||
|
||||
val isAnyTokenLoading = tokensNes.any { it.value is TokenStatus.Loading }
|
||||
val isAnyTokenLoading = tokensNes.any { it.value is CryptoCurrencyStatus.Loading }
|
||||
val fiatBalanceOperations = TokenListFiatBalanceOperations(tokensNes, isAnyTokenLoading, dispatchers)
|
||||
|
||||
createTokenList(
|
||||
|
|
@ -69,14 +69,14 @@ internal class TokenListOperations<E>(
|
|||
}
|
||||
|
||||
private suspend fun createTokenList(
|
||||
tokens: NonEmptySet<TokenStatus>,
|
||||
tokens: NonEmptySet<CryptoCurrencyStatus>,
|
||||
fiatBalance: TokenList.FiatBalance,
|
||||
isAnyTokenLoading: Boolean,
|
||||
isGrouped: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
): TokenList {
|
||||
val sortingOperations = TokenListSortingOperations(
|
||||
tokens = tokens,
|
||||
currencies = tokens,
|
||||
isAnyTokenLoading = isAnyTokenLoading,
|
||||
sortByBalance = isSortedByBalance,
|
||||
dispatchers = dispatchers,
|
||||
|
|
@ -90,7 +90,7 @@ internal class TokenListOperations<E>(
|
|||
}
|
||||
|
||||
private suspend fun createTokenList(
|
||||
tokens: NonEmptySet<TokenStatus>,
|
||||
tokens: NonEmptySet<CryptoCurrencyStatus>,
|
||||
sortingOperations: TokenListSortingOperations<*>,
|
||||
fiatBalance: TokenList.FiatBalance,
|
||||
isGrouped: Boolean,
|
||||
|
|
@ -108,9 +108,9 @@ internal class TokenListOperations<E>(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getNetworks(tokensNes: NonEmptySet<TokenStatus>): Set<Network> {
|
||||
private suspend fun getNetworks(tokensNes: NonEmptySet<CryptoCurrencyStatus>): Set<Network> {
|
||||
return withContext(dispatchers.io) {
|
||||
val networksIds = tokensNes.map { it.networkId }.toNonEmptySet()
|
||||
val networksIds = tokensNes.map { it.currency.networkId }.toNonEmptySet()
|
||||
catch(
|
||||
block = { networksRepository.getNetworks(networksIds) },
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
|
|
@ -124,7 +124,7 @@ internal class TokenListOperations<E>(
|
|||
): TokenList.Ungrouped = TokenList.Ungrouped(
|
||||
sortedBy = sortingOperations.getSortType(),
|
||||
totalFiatBalance = fiatBalance,
|
||||
tokens = sortingOperations.getTokens(),
|
||||
currencies = sortingOperations.getTokens(),
|
||||
)
|
||||
|
||||
private suspend fun createGroupedTokenList(
|
||||
|
|
@ -138,13 +138,13 @@ internal class TokenListOperations<E>(
|
|||
)
|
||||
|
||||
private fun createUnsortedUngroupedTokenList(
|
||||
tokens: NonEmptySet<TokenStatus>,
|
||||
tokens: NonEmptySet<CryptoCurrencyStatus>,
|
||||
fiatBalance: TokenList.FiatBalance,
|
||||
): TokenList.Ungrouped {
|
||||
return TokenList.Ungrouped(
|
||||
sortedBy = TokenList.SortType.NONE,
|
||||
totalFiatBalance = fiatBalance,
|
||||
tokens = tokens,
|
||||
currencies = tokens,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,16 +6,16 @@ import arrow.core.raise.ensure
|
|||
import arrow.core.raise.ensureNotNull
|
||||
import arrow.core.toNonEmptySetOrNull
|
||||
import com.tangem.domain.core.raise.DelegatedRaise
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.domain.tokens.model.NetworkGroup
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenListSortingOperations<E>(
|
||||
private val tokens: Set<TokenStatus>,
|
||||
private val currencies: Set<CryptoCurrencyStatus>,
|
||||
private val isAnyTokenLoading: Boolean,
|
||||
private val sortByBalance: Boolean,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -31,9 +31,9 @@ internal class TokenListSortingOperations<E>(
|
|||
sortByBalance: Boolean = tokenList.sortedBy == TokenList.SortType.BALANCE,
|
||||
isAnyTokenLoading: Boolean = tokenList.totalFiatBalance is TokenList.FiatBalance.Loading,
|
||||
) : this(
|
||||
tokens = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.tokens }.toSet()
|
||||
is TokenList.Ungrouped -> tokenList.tokens
|
||||
currencies = when (tokenList) {
|
||||
is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.currencies }.toSet()
|
||||
is TokenList.Ungrouped -> tokenList.currencies
|
||||
is TokenList.NotInitialized -> emptySet()
|
||||
},
|
||||
isAnyTokenLoading = isAnyTokenLoading,
|
||||
|
|
@ -45,7 +45,7 @@ internal class TokenListSortingOperations<E>(
|
|||
|
||||
suspend fun getGroupedTokens(networks: Set<Network>): NonEmptySet<NetworkGroup> {
|
||||
return withContext(dispatchers.default) {
|
||||
ensure(tokens.isNotEmpty()) { Error.EmptyTokens }
|
||||
ensure(currencies.isNotEmpty()) { Error.EmptyTokens }
|
||||
val networksNes = ensureNotNull(networks.toNonEmptySetOrNull()) {
|
||||
Error.EmptyNetworks
|
||||
}
|
||||
|
|
@ -58,9 +58,9 @@ internal class TokenListSortingOperations<E>(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getTokens(): NonEmptySet<TokenStatus> {
|
||||
suspend fun getTokens(): NonEmptySet<CryptoCurrencyStatus> {
|
||||
return withContext(dispatchers.default) {
|
||||
val tokensNes = ensureNotNull(tokens.toNonEmptySetOrNull()) {
|
||||
val tokensNes = ensureNotNull(currencies.toNonEmptySetOrNull()) {
|
||||
Error.EmptyTokens
|
||||
}
|
||||
|
||||
|
|
@ -71,8 +71,8 @@ internal class TokenListSortingOperations<E>(
|
|||
fun getSortType() = if (sortByBalance) TokenList.SortType.BALANCE else TokenList.SortType.NONE
|
||||
|
||||
private fun groupTokens(networks: NonEmptySet<Network>): NonEmptySet<NetworkGroup> {
|
||||
val groupedTokens = tokens
|
||||
.groupBy { it.networkId }
|
||||
val groupedTokens = currencies
|
||||
.groupBy { it.currency.networkId }
|
||||
.map { (networkId, tokens) ->
|
||||
val network = ensureNotNull(networks.firstOrNull { it.id == networkId }) {
|
||||
Error.NetworkNotFound(networkId)
|
||||
|
|
@ -80,7 +80,7 @@ internal class TokenListSortingOperations<E>(
|
|||
|
||||
NetworkGroup(
|
||||
network = network,
|
||||
tokens = ensureNotNull(tokens.toNonEmptySetOrNull()) { Error.EmptyTokens },
|
||||
currencies = ensureNotNull(tokens.toNonEmptySetOrNull()) { Error.EmptyTokens },
|
||||
)
|
||||
}
|
||||
.toNonEmptySetOrNull()
|
||||
|
|
@ -91,9 +91,9 @@ internal class TokenListSortingOperations<E>(
|
|||
private fun groupAndSortTokensByBalance(networks: NonEmptySet<Network>): NonEmptySet<NetworkGroup> {
|
||||
val groupsWithSortedTokens = groupTokens(networks)
|
||||
.map { group ->
|
||||
val tokens = group.tokens as? NonEmptySet<TokenStatus>
|
||||
val tokens = group.currencies as? NonEmptySet<CryptoCurrencyStatus>
|
||||
?: error("Tokens can not be empty here")
|
||||
group.copy(tokens = sortTokensByBalance(tokens))
|
||||
group.copy(currencies = sortTokensByBalance(tokens))
|
||||
}
|
||||
.toNonEmptySet()
|
||||
|
||||
|
|
@ -104,7 +104,7 @@ internal class TokenListSortingOperations<E>(
|
|||
}
|
||||
}
|
||||
|
||||
private fun sortTokensByBalance(tokens: NonEmptySet<TokenStatus>): NonEmptySet<TokenStatus> {
|
||||
private fun sortTokensByBalance(tokens: NonEmptySet<CryptoCurrencyStatus>): NonEmptySet<CryptoCurrencyStatus> {
|
||||
return if (isAnyTokenLoading) {
|
||||
tokens
|
||||
} else {
|
||||
|
|
@ -117,7 +117,7 @@ internal class TokenListSortingOperations<E>(
|
|||
private fun sortGroupsByBalance(groupsWithSortedTokens: NonEmptySet<NetworkGroup>): NonEmptySet<NetworkGroup> {
|
||||
return groupsWithSortedTokens
|
||||
.sortedByDescending { group ->
|
||||
group.tokens.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }
|
||||
group.currencies.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }
|
||||
}
|
||||
.toNonEmptySetOrNull()
|
||||
?: error("Tokens can not be empty here")
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
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.Token
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
|
@ -15,7 +15,7 @@ interface NetworksRepository {
|
|||
|
||||
fun getNetworkStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Map<Network.ID, Set<Token.ID>>,
|
||||
networks: Map<Network.ID, Set<CryptoCurrency.ID>>,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<NetworkStatus>>
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
|
|
@ -9,5 +9,5 @@ import kotlinx.coroutines.flow.Flow
|
|||
* */
|
||||
interface QuotesRepository {
|
||||
|
||||
fun getQuotes(tokensIds: Set<Token.ID>, refresh: Boolean): Flow<Set<Quote>>
|
||||
fun getQuotes(tokensIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>>
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.domain.tokens.repository
|
||||
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
|
|
@ -11,14 +11,14 @@ interface TokensRepository {
|
|||
|
||||
suspend fun saveTokens(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: Set<Token>,
|
||||
currencies: Set<CryptoCurrency>,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
)
|
||||
|
||||
suspend fun getSingleCurrencyWalletToken(userWalletId: UserWalletId): Token
|
||||
suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency
|
||||
|
||||
fun getMultiCurrencyWalletTokens(userWalletId: UserWalletId, refresh: Boolean): Flow<Set<Token>>
|
||||
fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow<Set<CryptoCurrency>>
|
||||
|
||||
fun isTokensGrouped(userWalletId: UserWalletId): Flow<Boolean>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import arrow.core.right
|
|||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.tokens.error.TokenListSortingError
|
||||
import com.tangem.domain.tokens.mock.MockTokens
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.repository.MockTokensRepository
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider
|
||||
|
|
@ -191,7 +191,7 @@ internal class ApplyTokenListSortingUseCaseTest {
|
|||
|
||||
private fun getTokensRepository(
|
||||
sortTokensResult: Either<DataError, Unit> = Unit.right(),
|
||||
tokens: Flow<Either<DataError, Set<Token>>> = flowOf(MockTokens.tokens.right()),
|
||||
tokens: Flow<Either<DataError, Set<CryptoCurrency>>> = flowOf(MockTokens.tokens.right()),
|
||||
): MockTokensRepository {
|
||||
return MockTokensRepository(sortTokensResult, tokens, emptyFlow(), emptyFlow())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import com.tangem.domain.tokens.mock.MockNetworks
|
|||
import com.tangem.domain.tokens.mock.MockQuotes
|
||||
import com.tangem.domain.tokens.mock.MockTokenLists
|
||||
import com.tangem.domain.tokens.mock.MockTokens
|
||||
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.model.Token
|
||||
import com.tangem.domain.tokens.repository.MockNetworksRepository
|
||||
import com.tangem.domain.tokens.repository.MockQuotesRepository
|
||||
import com.tangem.domain.tokens.repository.MockTokensRepository
|
||||
|
|
@ -224,7 +224,7 @@ internal class GetTokenListUseCaseTest {
|
|||
fun `when tokens is empty then not initialized token list should be received`() = runTest {
|
||||
val expectedResult = MockTokenLists.notInitializedTokenList.right()
|
||||
|
||||
val useCase = getUseCase(tokens = flowOf(emptySet<Token>().right()))
|
||||
val useCase = getUseCase(tokens = flowOf(emptySet<CryptoCurrency>().right()))
|
||||
|
||||
// When
|
||||
val result = useCase(userWalletId).first()
|
||||
|
|
@ -318,7 +318,7 @@ internal class GetTokenListUseCaseTest {
|
|||
}
|
||||
|
||||
private fun getUseCase(
|
||||
tokens: Flow<Either<DataError, Set<Token>>> = flowOf(MockTokens.tokens.right()),
|
||||
tokens: Flow<Either<DataError, Set<CryptoCurrency>>> = flowOf(MockTokens.tokens.right()),
|
||||
quotes: Flow<Either<DataError, Set<Quote>>> = flowOf(MockQuotes.quotes.right()),
|
||||
networks: Either<DataError, Set<Network>> = MockNetworks.networks.right(),
|
||||
statuses: Flow<Either<DataError, Set<NetworkStatus>>> = flowOf(MockNetworks.errorNetworksStatuses.right()),
|
||||
|
|
|
|||
|
|
@ -9,22 +9,22 @@ internal object MockNetworksGroups {
|
|||
|
||||
val networkGroup1 = NetworkGroup(
|
||||
network = MockNetworks.network1,
|
||||
tokens = MockTokensStates.failedTokenStates
|
||||
.filter { it.networkId == MockNetworks.network1.id }
|
||||
currencies = MockTokensStates.failedTokenStates
|
||||
.filter { it.currency.networkId == MockNetworks.network1.id }
|
||||
.toNonEmptySetOrNull()!!,
|
||||
)
|
||||
|
||||
val networkGroup2 = NetworkGroup(
|
||||
network = MockNetworks.network2,
|
||||
tokens = MockTokensStates.failedTokenStates
|
||||
.filter { it.networkId == MockNetworks.network2.id }
|
||||
currencies = MockTokensStates.failedTokenStates
|
||||
.filter { it.currency.networkId == MockNetworks.network2.id }
|
||||
.toNonEmptySetOrNull()!!,
|
||||
)
|
||||
|
||||
val networkGroup3 = NetworkGroup(
|
||||
network = MockNetworks.network3,
|
||||
tokens = MockTokensStates.failedTokenStates
|
||||
.filter { it.networkId == MockNetworks.network3.id }
|
||||
currencies = MockTokensStates.failedTokenStates
|
||||
.filter { it.currency.networkId == MockNetworks.network3.id }
|
||||
.toNonEmptySetOrNull()!!,
|
||||
)
|
||||
|
||||
|
|
@ -32,21 +32,21 @@ internal object MockNetworksGroups {
|
|||
|
||||
val loadedNetworksGroups = failedNetworksGroups.map { group ->
|
||||
group.copy(
|
||||
tokens = MockTokensStates.loadedTokensStates
|
||||
.filter { it.networkId == group.network.id }
|
||||
currencies = MockTokensStates.loadedTokensStates
|
||||
.filter { it.currency.networkId == group.network.id }
|
||||
.toNonEmptySetOrNull()!!,
|
||||
)
|
||||
}.toNonEmptySet()
|
||||
|
||||
val sortedNetworksGroups = loadedNetworksGroups.map { group ->
|
||||
group.copy(
|
||||
tokens = group.tokens
|
||||
currencies = group.currencies
|
||||
.sortedByDescending { it.value.fiatAmount }
|
||||
.toNonEmptySetOrNull()!!,
|
||||
)
|
||||
}
|
||||
.sortedByDescending { group ->
|
||||
group.tokens.sumOf { it.value.fiatAmount!! }
|
||||
group.currencies.sumOf { it.value.fiatAmount!! }
|
||||
}
|
||||
.toNonEmptySetOrNull()!!
|
||||
}
|
||||
|
|
@ -8,61 +8,61 @@ import java.math.BigDecimal
|
|||
internal object MockQuotes {
|
||||
|
||||
val quote1 = Quote(
|
||||
tokenId = MockTokens.token1.id,
|
||||
currencyId = MockTokens.token1.id,
|
||||
fiatRate = BigDecimal("1.23"),
|
||||
priceChange = BigDecimal("0.01"),
|
||||
)
|
||||
|
||||
val quote2 = Quote(
|
||||
tokenId = MockTokens.token2.id,
|
||||
currencyId = MockTokens.token2.id,
|
||||
fiatRate = BigDecimal("2.34"),
|
||||
priceChange = BigDecimal("-0.02"),
|
||||
)
|
||||
|
||||
val quote3 = Quote(
|
||||
tokenId = MockTokens.token3.id,
|
||||
currencyId = MockTokens.token3.id,
|
||||
fiatRate = BigDecimal("3.45"),
|
||||
priceChange = BigDecimal("0.03"),
|
||||
)
|
||||
|
||||
val quote4 = Quote(
|
||||
tokenId = MockTokens.token4.id,
|
||||
currencyId = MockTokens.token4.id,
|
||||
fiatRate = BigDecimal("4.56"),
|
||||
priceChange = BigDecimal("-0.04"),
|
||||
)
|
||||
|
||||
val quote5 = Quote(
|
||||
tokenId = MockTokens.token5.id,
|
||||
currencyId = MockTokens.token5.id,
|
||||
fiatRate = BigDecimal("5.67"),
|
||||
priceChange = BigDecimal("0.05"),
|
||||
)
|
||||
|
||||
val quote6 = Quote(
|
||||
tokenId = MockTokens.token6.id,
|
||||
currencyId = MockTokens.token6.id,
|
||||
fiatRate = BigDecimal("6.78"),
|
||||
priceChange = BigDecimal("-0.06"),
|
||||
)
|
||||
|
||||
val quote7 = Quote(
|
||||
tokenId = MockTokens.token7.id,
|
||||
currencyId = MockTokens.token7.id,
|
||||
fiatRate = BigDecimal("7.89"),
|
||||
priceChange = BigDecimal("0.07"),
|
||||
)
|
||||
|
||||
val quote8 = Quote(
|
||||
tokenId = MockTokens.token8.id,
|
||||
currencyId = MockTokens.token8.id,
|
||||
fiatRate = BigDecimal("8.90"),
|
||||
priceChange = BigDecimal("-0.08"),
|
||||
)
|
||||
|
||||
val quote9 = Quote(
|
||||
tokenId = MockTokens.token9.id,
|
||||
currencyId = MockTokens.token9.id,
|
||||
fiatRate = BigDecimal("9.01"),
|
||||
priceChange = BigDecimal("0.09"),
|
||||
)
|
||||
|
||||
val quote10 = Quote(
|
||||
tokenId = MockTokens.token10.id,
|
||||
currencyId = MockTokens.token10.id,
|
||||
fiatRate = BigDecimal("10.12"),
|
||||
priceChange = BigDecimal("-0.10"),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import arrow.core.toNonEmptySetOrNull
|
|||
import com.tangem.domain.tokens.mock.MockNetworksGroups.failedNetworksGroups
|
||||
import com.tangem.domain.tokens.mock.MockNetworksGroups.loadedNetworksGroups
|
||||
import com.tangem.domain.tokens.mock.MockNetworksGroups.sortedNetworksGroups
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import java.math.BigDecimal
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
|
|
@ -24,7 +24,7 @@ internal object MockTokenLists {
|
|||
)
|
||||
|
||||
val emptyUngroupedTokenList = TokenList.Ungrouped(
|
||||
tokens = emptySet(),
|
||||
currencies = emptySet(),
|
||||
totalFiatBalance = TokenList.FiatBalance.Failed,
|
||||
sortedBy = TokenList.SortType.NONE,
|
||||
)
|
||||
|
|
@ -36,14 +36,14 @@ internal object MockTokenLists {
|
|||
)
|
||||
|
||||
val failedUngroupedTokenList = TokenList.Ungrouped(
|
||||
tokens = MockTokensStates.failedTokenStates,
|
||||
currencies = MockTokensStates.failedTokenStates,
|
||||
totalFiatBalance = TokenList.FiatBalance.Failed,
|
||||
sortedBy = TokenList.SortType.NONE,
|
||||
)
|
||||
|
||||
val loadingUngroupedTokenList = with(failedUngroupedTokenList) {
|
||||
copy(
|
||||
tokens = tokens.map { it.copy(value = TokenStatus.Loading) }.toNonEmptySetOrNull()!!,
|
||||
currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptySetOrNull()!!,
|
||||
totalFiatBalance = TokenList.FiatBalance.Loading,
|
||||
)
|
||||
}
|
||||
|
|
@ -53,8 +53,8 @@ internal object MockTokenLists {
|
|||
totalFiatBalance = TokenList.FiatBalance.Loading,
|
||||
groups = groups.map { group ->
|
||||
group.copy(
|
||||
tokens = group.tokens
|
||||
.map { it.copy(value = TokenStatus.Loading) }
|
||||
currencies = group.currencies
|
||||
.map { it.copy(value = CryptoCurrencyStatus.Loading) }
|
||||
.toNonEmptySetOrNull()!!,
|
||||
)
|
||||
}.toNonEmptySetOrNull()!!,
|
||||
|
|
@ -66,7 +66,7 @@ internal object MockTokenLists {
|
|||
val tokens = MockTokensStates.loadedTokensStates
|
||||
|
||||
return failedUngroupedTokenList.copy(
|
||||
tokens = tokens,
|
||||
currencies = tokens,
|
||||
sortedBy = TokenList.SortType.NONE,
|
||||
totalFiatBalance = TokenList.FiatBalance.Loaded(
|
||||
amount = tokens.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO },
|
||||
|
|
@ -84,7 +84,7 @@ internal object MockTokenLists {
|
|||
sortedBy = TokenList.SortType.NONE,
|
||||
totalFiatBalance = TokenList.FiatBalance.Loaded(
|
||||
amount = groups
|
||||
.flatMap { it.tokens as NonEmptySet<TokenStatus> }
|
||||
.flatMap { it.currencies as NonEmptySet<CryptoCurrencyStatus> }
|
||||
.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO },
|
||||
isAllAmountsSummarized = true,
|
||||
),
|
||||
|
|
@ -98,7 +98,7 @@ internal object MockTokenLists {
|
|||
.toNonEmptySetOrNull()!!
|
||||
|
||||
return unsortedUngroupedTokenList.copy(
|
||||
tokens = tokens,
|
||||
currencies = tokens,
|
||||
sortedBy = TokenList.SortType.BALANCE,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,119 +1,123 @@
|
|||
package com.tangem.domain.tokens.mock
|
||||
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
|
||||
internal object MockTokens {
|
||||
|
||||
val token1 = Token(
|
||||
id = Token.ID("token1"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 1",
|
||||
symbol = "T1",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token2 = Token(
|
||||
id = Token.ID("token2"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 2",
|
||||
symbol = "T2",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token3 = Token(
|
||||
id = Token.ID("token3"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 3",
|
||||
symbol = "T3",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token4 = Token(
|
||||
id = Token.ID("token4"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 4",
|
||||
symbol = "T4",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token5 = Token(
|
||||
id = Token.ID("token5"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 5",
|
||||
symbol = "T5",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token6 = Token(
|
||||
id = Token.ID("token6"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 6",
|
||||
symbol = "T6",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token7 = Token(
|
||||
id = Token.ID("token7"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 7",
|
||||
symbol = "T7",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token8 = Token(
|
||||
id = Token.ID("token8"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 8",
|
||||
symbol = "T8",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token9 = Token(
|
||||
id = Token.ID("token9"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 9",
|
||||
symbol = "T9",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token10 = Token(
|
||||
id = Token.ID("token10"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 10",
|
||||
symbol = "T10",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token1
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID("token1"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 1",
|
||||
symbol = "T1",
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token2
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token2"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 2",
|
||||
symbol = "T2",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token3
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token3"),
|
||||
networkId = MockNetworks.network1.id,
|
||||
name = "Token 3",
|
||||
symbol = "T3",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token4
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID("token4"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 4",
|
||||
symbol = "T4",
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token5
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token5"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 5",
|
||||
symbol = "T5",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token6
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token6"),
|
||||
networkId = MockNetworks.network2.id,
|
||||
name = "Token 6",
|
||||
symbol = "T6",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token7
|
||||
get() = CryptoCurrency.Coin(
|
||||
id = CryptoCurrency.ID("token7"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 7",
|
||||
symbol = "T7",
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
derivationPath = null,
|
||||
)
|
||||
val token8
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token8"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 8",
|
||||
symbol = "T8",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token9
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token9"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 9",
|
||||
symbol = "T9",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
val token10
|
||||
get() = CryptoCurrency.Token(
|
||||
id = CryptoCurrency.ID("token10"),
|
||||
networkId = MockNetworks.network3.id,
|
||||
name = "Token 10",
|
||||
symbol = "T10",
|
||||
isCustom = false,
|
||||
decimals = 8,
|
||||
iconUrl = null,
|
||||
contractAddress = "address",
|
||||
derivationPath = null,
|
||||
)
|
||||
|
||||
val tokens = setOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10)
|
||||
}
|
||||
|
|
@ -1,120 +1,60 @@
|
|||
package com.tangem.domain.tokens.mock
|
||||
|
||||
import arrow.core.nonEmptySetOf
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkStatus
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
internal object MockTokensStates {
|
||||
|
||||
val tokenState1 = TokenStatus(
|
||||
id = MockTokens.token1.id,
|
||||
networkId = MockTokens.token1.networkId,
|
||||
name = MockTokens.token1.name,
|
||||
symbol = MockTokens.token1.symbol,
|
||||
decimals = MockTokens.token1.decimals,
|
||||
iconUrl = MockTokens.token1.iconUrl,
|
||||
isCoin = MockTokens.token1.contractAddress == null,
|
||||
value = TokenStatus.Unreachable,
|
||||
val tokenState1 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token1,
|
||||
value = CryptoCurrencyStatus.Unreachable,
|
||||
)
|
||||
|
||||
val tokenState2 = TokenStatus(
|
||||
id = MockTokens.token2.id,
|
||||
networkId = MockTokens.token2.networkId,
|
||||
name = MockTokens.token2.name,
|
||||
symbol = MockTokens.token2.symbol,
|
||||
decimals = MockTokens.token2.decimals,
|
||||
iconUrl = MockTokens.token2.iconUrl,
|
||||
isCoin = MockTokens.token2.contractAddress == null,
|
||||
value = TokenStatus.Unreachable,
|
||||
val tokenState2 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token2,
|
||||
value = CryptoCurrencyStatus.Unreachable,
|
||||
)
|
||||
|
||||
val tokenState3 = TokenStatus(
|
||||
id = MockTokens.token3.id,
|
||||
networkId = MockTokens.token3.networkId,
|
||||
name = MockTokens.token3.name,
|
||||
symbol = MockTokens.token3.symbol,
|
||||
decimals = MockTokens.token3.decimals,
|
||||
iconUrl = MockTokens.token3.iconUrl,
|
||||
isCoin = MockTokens.token3.contractAddress == null,
|
||||
value = TokenStatus.Unreachable,
|
||||
val tokenState3 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token3,
|
||||
value = CryptoCurrencyStatus.Unreachable,
|
||||
)
|
||||
|
||||
val tokenState4 = TokenStatus(
|
||||
id = MockTokens.token4.id,
|
||||
networkId = MockTokens.token4.networkId,
|
||||
name = MockTokens.token4.name,
|
||||
symbol = MockTokens.token4.symbol,
|
||||
decimals = MockTokens.token4.decimals,
|
||||
iconUrl = MockTokens.token4.iconUrl,
|
||||
isCoin = MockTokens.token4.contractAddress == null,
|
||||
value = TokenStatus.MissedDerivation,
|
||||
val tokenState4 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token4,
|
||||
value = CryptoCurrencyStatus.MissedDerivation,
|
||||
)
|
||||
|
||||
val tokenState5 = TokenStatus(
|
||||
id = MockTokens.token5.id,
|
||||
networkId = MockTokens.token5.networkId,
|
||||
name = MockTokens.token5.name,
|
||||
symbol = MockTokens.token5.symbol,
|
||||
decimals = MockTokens.token5.decimals,
|
||||
iconUrl = MockTokens.token5.iconUrl,
|
||||
isCoin = MockTokens.token5.contractAddress == null,
|
||||
value = TokenStatus.MissedDerivation,
|
||||
val tokenState5 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token5,
|
||||
value = CryptoCurrencyStatus.MissedDerivation,
|
||||
)
|
||||
|
||||
val tokenState6 = TokenStatus(
|
||||
id = MockTokens.token6.id,
|
||||
networkId = MockTokens.token6.networkId,
|
||||
name = MockTokens.token6.name,
|
||||
symbol = MockTokens.token6.symbol,
|
||||
decimals = MockTokens.token6.decimals,
|
||||
iconUrl = MockTokens.token6.iconUrl,
|
||||
isCoin = MockTokens.token6.contractAddress == null,
|
||||
value = TokenStatus.MissedDerivation,
|
||||
val tokenState6 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token6,
|
||||
value = CryptoCurrencyStatus.MissedDerivation,
|
||||
)
|
||||
|
||||
val tokenState7 = TokenStatus(
|
||||
id = MockTokens.token7.id,
|
||||
networkId = MockTokens.token7.networkId,
|
||||
name = MockTokens.token7.name,
|
||||
symbol = MockTokens.token7.symbol,
|
||||
decimals = MockTokens.token7.decimals,
|
||||
iconUrl = MockTokens.token7.iconUrl,
|
||||
isCoin = MockTokens.token7.contractAddress == null,
|
||||
value = TokenStatus.NoAccount,
|
||||
val tokenState7 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token7,
|
||||
value = CryptoCurrencyStatus.NoAccount,
|
||||
)
|
||||
|
||||
val tokenState8 = TokenStatus(
|
||||
id = MockTokens.token8.id,
|
||||
networkId = MockTokens.token8.networkId,
|
||||
name = MockTokens.token8.name,
|
||||
symbol = MockTokens.token8.symbol,
|
||||
decimals = MockTokens.token8.decimals,
|
||||
iconUrl = MockTokens.token8.iconUrl,
|
||||
isCoin = MockTokens.token8.contractAddress == null,
|
||||
value = TokenStatus.NoAccount,
|
||||
val tokenState8 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token8,
|
||||
value = CryptoCurrencyStatus.NoAccount,
|
||||
)
|
||||
|
||||
val tokenState9 = TokenStatus(
|
||||
id = MockTokens.token9.id,
|
||||
networkId = MockTokens.token9.networkId,
|
||||
name = MockTokens.token9.name,
|
||||
symbol = MockTokens.token9.symbol,
|
||||
decimals = MockTokens.token9.decimals,
|
||||
iconUrl = MockTokens.token9.iconUrl,
|
||||
isCoin = MockTokens.token9.contractAddress == null,
|
||||
value = TokenStatus.NoAccount,
|
||||
val tokenState9 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token9,
|
||||
value = CryptoCurrencyStatus.NoAccount,
|
||||
)
|
||||
|
||||
val tokenState10 = TokenStatus(
|
||||
id = MockTokens.token10.id,
|
||||
networkId = MockTokens.token10.networkId,
|
||||
name = MockTokens.token10.name,
|
||||
symbol = MockTokens.token10.symbol,
|
||||
decimals = MockTokens.token10.decimals,
|
||||
iconUrl = MockTokens.token10.iconUrl,
|
||||
isCoin = MockTokens.token10.contractAddress == null,
|
||||
value = TokenStatus.NoAccount,
|
||||
val tokenState10 = CryptoCurrencyStatus(
|
||||
currency = MockTokens.token10,
|
||||
value = CryptoCurrencyStatus.NoAccount,
|
||||
)
|
||||
|
||||
val failedTokenStates = nonEmptySetOf(
|
||||
|
|
@ -130,14 +70,15 @@ internal object MockTokensStates {
|
|||
tokenState10,
|
||||
)
|
||||
|
||||
val loadedTokensStates = failedTokenStates.map { state ->
|
||||
val networkStatus = MockNetworks.verifiedNetworksStatuses.first { it.networkId == state.networkId }
|
||||
val amount = (networkStatus.value as NetworkStatus.Verified).amounts[state.id]!!
|
||||
val quote = MockQuotes.quotes.first { it.tokenId == state.id }
|
||||
val loadedTokensStates = failedTokenStates.map { status ->
|
||||
val networkStatus = MockNetworks.verifiedNetworksStatuses
|
||||
.first { it.networkId == status.currency.networkId }
|
||||
val amount = (networkStatus.value as NetworkStatus.Verified).amounts[status.currency.id]!!
|
||||
val quote = MockQuotes.quotes.first { it.currencyId == status.currency.id }
|
||||
val fiatAmount = amount * quote.fiatRate
|
||||
|
||||
state.copy(
|
||||
value = TokenStatus.Loaded(
|
||||
status.copy(
|
||||
value = CryptoCurrencyStatus.Loaded(
|
||||
amount = amount,
|
||||
fiatAmount = fiatAmount,
|
||||
fiatRate = quote.fiatRate,
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@ package com.tangem.domain.tokens.repository
|
|||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.core.error.DataError
|
||||
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.Token
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
|
@ -21,7 +21,7 @@ internal class MockNetworksRepository(
|
|||
|
||||
override fun getNetworkStatuses(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Map<Network.ID, Set<Token.ID>>,
|
||||
networks: Map<Network.ID, Set<CryptoCurrency.ID>>,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<NetworkStatus>> {
|
||||
return statuses.map { it.getOrElse { e -> throw e } }
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ package com.tangem.domain.tokens.repository
|
|||
import arrow.core.Either
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Quote
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
|
|
@ -12,7 +12,7 @@ internal class MockQuotesRepository(
|
|||
private val quotes: Flow<Either<DataError, Set<Quote>>>,
|
||||
) : QuotesRepository {
|
||||
|
||||
override fun getQuotes(tokensIds: Set<Token.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
override fun getQuotes(tokensIds: Set<CryptoCurrency.ID>, refresh: Boolean): Flow<Set<Quote>> {
|
||||
return quotes.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
}
|
||||
|
|
@ -4,19 +4,19 @@ import arrow.core.Either
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.domain.core.error.DataError
|
||||
import com.tangem.domain.tokens.mock.MockTokens
|
||||
import com.tangem.domain.tokens.model.Token
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
internal class MockTokensRepository(
|
||||
private val sortTokensResult: Either<DataError, Unit>,
|
||||
private val tokens: Flow<Either<DataError, Set<Token>>>,
|
||||
private val tokens: Flow<Either<DataError, Set<CryptoCurrency>>>,
|
||||
private val isGrouped: Flow<Either<DataError, Boolean>>,
|
||||
private val isSortedByBalance: Flow<Either<DataError, Boolean>>,
|
||||
) : TokensRepository {
|
||||
|
||||
var tokensIdsAfterSortingApply: Set<Token>? = null
|
||||
var tokensIdsAfterSortingApply: Set<CryptoCurrency>? = null
|
||||
private set
|
||||
|
||||
var isTokensGroupedAfterSortingApply: Boolean? = null
|
||||
|
|
@ -27,22 +27,25 @@ internal class MockTokensRepository(
|
|||
|
||||
override suspend fun saveTokens(
|
||||
userWalletId: UserWalletId,
|
||||
tokens: Set<Token>,
|
||||
currencies: Set<CryptoCurrency>,
|
||||
isGroupedByNetwork: Boolean,
|
||||
isSortedByBalance: Boolean,
|
||||
) {
|
||||
sortTokensResult.onLeft { throw it }
|
||||
|
||||
tokensIdsAfterSortingApply = tokens
|
||||
tokensIdsAfterSortingApply = currencies
|
||||
isTokensGroupedAfterSortingApply = isGroupedByNetwork
|
||||
isTokensSortedByBalanceAfterSortingApply = isSortedByBalance
|
||||
}
|
||||
|
||||
override suspend fun getSingleCurrencyWalletToken(userWalletId: UserWalletId): Token {
|
||||
override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency {
|
||||
return MockTokens.token1
|
||||
}
|
||||
|
||||
override fun getMultiCurrencyWalletTokens(userWalletId: UserWalletId, refresh: Boolean): Flow<Set<Token>> {
|
||||
override fun getMultiCurrencyWalletCurrencies(
|
||||
userWalletId: UserWalletId,
|
||||
refresh: Boolean,
|
||||
): Flow<Set<CryptoCurrency>> {
|
||||
return tokens.map { it.getOrElse { e -> throw e } }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,49 +3,50 @@ package com.tangem.feature.wallet.presentation.wallet.utils
|
|||
import androidx.annotation.DrawableRes
|
||||
import com.tangem.core.ui.components.marketprice.PriceChangeConfig
|
||||
import com.tangem.core.ui.utils.BigDecimalFormatter
|
||||
import com.tangem.domain.tokens.model.TokenStatus
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.feature.wallet.impl.R
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class TokenStatusToTokenItemConverter(
|
||||
internal class CryptoCurrencyStatusToTokenItemConverter(
|
||||
private val isWalletContentHidden: Boolean,
|
||||
private val fiatCurrencyCode: String,
|
||||
private val fiatCurrencySymbol: String,
|
||||
) : Converter<TokenStatus, TokenItemState> {
|
||||
) : Converter<CryptoCurrencyStatus, TokenItemState> {
|
||||
|
||||
private val TokenStatus.networkIconResId: Int?
|
||||
private val CryptoCurrencyStatus.networkIconResId: Int?
|
||||
@DrawableRes get() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
return if (isCoin) null else R.drawable.img_eth_22
|
||||
return if (currency is CryptoCurrency.Token) null else R.drawable.img_eth_22
|
||||
}
|
||||
|
||||
private val TokenStatus.tokenIconResId: Int
|
||||
private val CryptoCurrencyStatus.tokenIconResId: Int
|
||||
@DrawableRes get() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
return R.drawable.img_eth_22
|
||||
}
|
||||
|
||||
override fun convert(value: TokenStatus): TokenItemState {
|
||||
override fun convert(value: CryptoCurrencyStatus): TokenItemState {
|
||||
return when (value.value) {
|
||||
is TokenStatus.Loading -> TokenItemState.Loading
|
||||
is TokenStatus.Loaded,
|
||||
is TokenStatus.Custom,
|
||||
is CryptoCurrencyStatus.Loading -> TokenItemState.Loading
|
||||
is CryptoCurrencyStatus.Loaded,
|
||||
is CryptoCurrencyStatus.Custom,
|
||||
-> value.mapToTokenItemState()
|
||||
// TODO: Add other token item states, currently not designed
|
||||
is TokenStatus.MissedDerivation,
|
||||
is TokenStatus.NoAccount,
|
||||
is TokenStatus.Unreachable,
|
||||
is CryptoCurrencyStatus.MissedDerivation,
|
||||
is CryptoCurrencyStatus.NoAccount,
|
||||
is CryptoCurrencyStatus.Unreachable,
|
||||
-> value.mapToUnreachableTokenItemState()
|
||||
}
|
||||
}
|
||||
|
||||
private fun TokenStatus.mapToTokenItemState(): TokenItemState.Content {
|
||||
private fun CryptoCurrencyStatus.mapToTokenItemState(): TokenItemState.Content {
|
||||
return TokenItemState.Content(
|
||||
id = this.id.value,
|
||||
name = this.name,
|
||||
tokenIconUrl = this.iconUrl,
|
||||
id = currency.id.value,
|
||||
name = currency.name,
|
||||
tokenIconUrl = currency.iconUrl,
|
||||
tokenIconResId = this.tokenIconResId,
|
||||
networkIconResId = this.networkIconResId,
|
||||
amount = getFormattedAmount(),
|
||||
|
|
@ -61,27 +62,27 @@ internal class TokenStatusToTokenItemConverter(
|
|||
)
|
||||
}
|
||||
|
||||
private fun TokenStatus.getFormattedAmount(): String {
|
||||
private fun CryptoCurrencyStatus.getFormattedAmount(): String {
|
||||
val amount = value.amount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, symbol, decimals)
|
||||
return BigDecimalFormatter.formatCryptoAmount(amount, currency.symbol, currency.decimals)
|
||||
}
|
||||
|
||||
private fun TokenStatus.getFormattedFiatAmount(): String {
|
||||
private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String {
|
||||
val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN
|
||||
|
||||
return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol)
|
||||
}
|
||||
|
||||
private fun TokenStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable(
|
||||
id = this.id.value,
|
||||
name = this.name,
|
||||
tokenIconUrl = this.iconUrl,
|
||||
private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable(
|
||||
id = currency.id.value,
|
||||
name = currency.name,
|
||||
tokenIconUrl = currency.iconUrl,
|
||||
tokenIconResId = this.tokenIconResId,
|
||||
networkIconResId = this.networkIconResId,
|
||||
)
|
||||
|
||||
private fun TokenStatus.getPriceChangeConfig(): PriceChangeConfig {
|
||||
private fun CryptoCurrencyStatus.getPriceChangeConfig(): PriceChangeConfig {
|
||||
val priceChange = value.priceChange
|
||||
?: return PriceChangeConfig(UNKNOWN_AMOUNT_SIGN, PriceChangeConfig.Type.DOWN)
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.feature.wallet.presentation.wallet.utils
|
||||
|
||||
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.TokenStatus
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState.TokensListItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.utils.LoadingItemsProvider.getLoadingMultiCurrencyTokens
|
||||
|
|
@ -19,7 +19,7 @@ internal class TokenListToContentItemsConverter(
|
|||
private val clickCallbacks: WalletClickCallbacks,
|
||||
) : Converter<TokenList, WalletTokensListState> {
|
||||
|
||||
private val tokenStatusConverter = TokenStatusToTokenItemConverter(
|
||||
private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter(
|
||||
isWalletContentHidden = isWalletContentHidden,
|
||||
fiatCurrencyCode = fiatCurrencyCode,
|
||||
fiatCurrencySymbol = fiatCurrencySymbol,
|
||||
|
|
@ -47,7 +47,7 @@ internal class TokenListToContentItemsConverter(
|
|||
}
|
||||
|
||||
private fun TokenList.Ungrouped.mapToMultiCurrencyItems(): PersistentList<TokensListItemState> {
|
||||
return tokens.fold(initial = persistentListOf()) { acc, token ->
|
||||
return currencies.fold(initial = persistentListOf()) { acc, token ->
|
||||
acc.mutate { it.addToken(token) }
|
||||
}
|
||||
}
|
||||
|
|
@ -55,14 +55,14 @@ internal class TokenListToContentItemsConverter(
|
|||
private fun MutableList<TokensListItemState>.addGroup(group: NetworkGroup): List<TokensListItemState> {
|
||||
this.add(TokensListItemState.NetworkGroupTitle(group.network.name))
|
||||
|
||||
group.tokens.forEach { token ->
|
||||
group.currencies.forEach { token ->
|
||||
this.addToken(token)
|
||||
}
|
||||
|
||||
return this
|
||||
}
|
||||
|
||||
private fun MutableList<TokensListItemState>.addToken(token: TokenStatus): List<TokensListItemState> {
|
||||
private fun MutableList<TokensListItemState>.addToken(token: CryptoCurrencyStatus): List<TokensListItemState> {
|
||||
val tokenItemState = tokenStatusConverter.convert(token)
|
||||
|
||||
this.add(TokensListItemState.Token(tokenItemState))
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels
|
|||
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.domain.common.CardTypesResolver
|
||||
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.TokenStatus
|
||||
import com.tangem.feature.wallet.presentation.common.state.TokenItemState
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification
|
||||
import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder
|
||||
|
|
@ -123,12 +123,12 @@ internal class WalletNotificationsListFactory(
|
|||
|
||||
private fun TokenList.hasMissedDerivations(): Boolean {
|
||||
val statuses = when (this) {
|
||||
is TokenList.GroupedByNetwork -> groups.flatMap(NetworkGroup::tokens).map(TokenStatus::value)
|
||||
is TokenList.Ungrouped -> tokens.map(TokenStatus::value)
|
||||
is TokenList.GroupedByNetwork -> groups.flatMap(NetworkGroup::currencies).map(CryptoCurrencyStatus::value)
|
||||
is TokenList.Ungrouped -> currencies.map(CryptoCurrencyStatus::value)
|
||||
TokenList.NotInitialized -> emptyList()
|
||||
}
|
||||
|
||||
return statuses.any { it is TokenStatus.MissedDerivation }
|
||||
return statuses.any { it is CryptoCurrencyStatus.MissedDerivation }
|
||||
}
|
||||
|
||||
private companion object {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue