diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index 28bf6c2485..b58485ab88 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -62,7 +62,9 @@ class TapApplication : Application() { NetworkConnectivity.createInstance(store, this) preferencesStorage = PreferencesStorage(this) PicassoHelper.initPicassoWithCaching(this) - currenciesRepository = CurrenciesRepository(this) + currenciesRepository = CurrenciesRepository( + this, store.state.domainNetworks.tangemTechService + ) walletConnectRepository = WalletConnectRepository(this) initFeedbackManager() diff --git a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt index 3349096d24..cce3a27c15 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -15,7 +15,7 @@ import com.tangem.tap.domain.configurable.config.ConfigManager import com.tangem.tap.domain.extensions.isMultiwalletAllowed import com.tangem.tap.domain.extensions.makePrimaryWalletManager import com.tangem.tap.domain.extensions.makeWalletManagersForApp -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.network.NetworkConnectivity diff --git a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt index 8053bb2959..bff4c0c3b7 100644 --- a/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt +++ b/app/src/main/java/com/tangem/tap/domain/extensions/WalletManagerFactory.kt @@ -10,7 +10,7 @@ import com.tangem.common.hdWallet.DerivationPath import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.redux.Currency fun WalletManagerFactory.makeWalletManagerForApp( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt index b8b4799532..d57336841e 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/ScanProductTask.kt @@ -28,9 +28,11 @@ import com.tangem.operations.issuerAndUserData.ReadIssuerDataCommand import com.tangem.tap.domain.TapSdkError import com.tangem.tap.domain.extensions.getPrimaryCurve import com.tangem.tap.domain.extensions.getSingleWallet -import com.tangem.tap.domain.tokens.BlockchainNetwork import com.tangem.tap.domain.tokens.CurrenciesRepository +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.preferencesStorage +import com.tangem.tap.scope +import kotlinx.coroutines.launch class ScanProductTask( val card: Card? = null, @@ -192,47 +194,50 @@ private class ScanWalletProcessor( session: CardSession, callback: (result: CompletionResult) -> Unit ) { - val derivations = collectDerivations(card) - if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { - callback( - CompletionResult.Success( - ScanResponse( - card = card, - productType = ProductType.Wallet, - walletData = session.environment.walletData, - primaryCard = primaryCard + scope.launch { + val derivations = collectDerivations(card) + if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { + callback( + CompletionResult.Success( + ScanResponse( + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + primaryCard = primaryCard + ) ) ) - ) - return - } - - DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> - when (result) { - is CompletionResult.Success -> { - val response = ScanResponse( - card = card, - productType = ProductType.Wallet, - walletData = session.environment.walletData, - derivedKeys = result.data.entries, - primaryCard = primaryCard - ) - callback(CompletionResult.Success(response)) + return@launch + } + DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> + when (result) { + is CompletionResult.Success -> { + val response = ScanResponse( + card = card, + productType = ProductType.Wallet, + walletData = session.environment.walletData, + derivedKeys = result.data.entries, + primaryCard = primaryCard + ) + callback(CompletionResult.Success(response)) + } + is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } - is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) } } } - private fun getBlockchainsToDerive(card: Card): List { + private suspend fun getBlockchainsToDerive(card: Card): List { val currenciesRepository = currenciesRepository ?: return emptyList() + val cardCurrencies = currenciesRepository.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed).toMutableList() val blockchainsToDerive = cardCurrencies.ifEmpty { mutableListOf( BlockchainNetwork(Blockchain.Bitcoin, card), - BlockchainNetwork(Blockchain.Ethereum, card)) + BlockchainNetwork(Blockchain.Ethereum, card) + ) } if (card.settings.isHDWalletAllowed) { @@ -260,7 +265,7 @@ private class ScanWalletProcessor( return blockchainsToDerive.distinct() } - private fun collectDerivations(card: Card): Map> { + private suspend fun collectDerivations(card: Card): Map> { val blockchains = getBlockchainsToDerive(card) val derivations = mutableMapOf>() diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt index 01c51ceee4..74e1c47a52 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt @@ -2,26 +2,31 @@ package com.tangem.tap.domain.tokens import android.app.Application import android.content.Context -import com.squareup.moshi.Json import com.squareup.moshi.JsonAdapter -import com.squareup.moshi.JsonClass import com.squareup.moshi.Types import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.WalletManager -import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.common.services.Result +import com.tangem.domain.common.extensions.toNetworkId +import com.tangem.network.api.tangemTech.TangemTechService import com.tangem.network.common.MoshiConverter import com.tangem.tap.common.extensions.appendIf import com.tangem.tap.common.extensions.readJsonFileToString -import com.tangem.tap.domain.extensions.setCustomIconUrl +import com.tangem.tap.domain.tokens.models.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.ObsoleteTokenDao +import com.tangem.tap.domain.tokens.models.TokenDao import com.tangem.tap.features.demo.DemoHelper +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope import timber.log.Timber import java.util.* -class CurrenciesRepository(val context: Application) { +class CurrenciesRepository( + private val context: Application, + private val tangemNetworkService: TangemTechService +) { private val moshi = MoshiConverter.defaultMoshi() private val blockchainsAdapter: JsonAdapter> = moshi.adapter( @@ -41,7 +46,7 @@ class CurrenciesRepository(val context: Application) { fun saveUpdatedCurrency(cardId: String, blockchainNetwork: BlockchainNetwork) { var changed = false - val currencies = loadSavedCurrencies(cardId).map { + val currencies = loadSavedCurrenciesWithoutMigration(cardId).map { if (it == blockchainNetwork) { changed = true blockchainNetwork @@ -54,7 +59,7 @@ class CurrenciesRepository(val context: Application) { } fun removeToken(cardId: String, token: Token, blockchainNetwork: BlockchainNetwork) { - val currencies = loadSavedCurrencies(cardId).map { + val currencies = loadSavedCurrenciesWithoutMigration(cardId).map { if (it == blockchainNetwork) { it.copy(tokens = it.tokens.filterNot { it == token }) } else { @@ -65,7 +70,8 @@ class CurrenciesRepository(val context: Application) { } fun removeBlockchain(cardId: String, blockchainNetwork: BlockchainNetwork) { - val currencies = loadSavedCurrencies(cardId).filterNot { it == blockchainNetwork } + val currencies = loadSavedCurrenciesWithoutMigration(cardId) + .filterNot { it == blockchainNetwork } saveCurrencies(cardId, currencies) } @@ -98,12 +104,12 @@ class CurrenciesRepository(val context: Application) { } } - fun loadSavedCurrencies( + suspend fun loadSavedCurrencies( cardId: String, isHdWalletSupported: Boolean = false ): List { if (DemoHelper.isDemoCardId(cardId)) { - return loadDemoCurrencies(cardId) + return loadDemoCurrencies() } return try { val json = context.readFileText(getFileNameForBlockchains(cardId)) @@ -113,7 +119,21 @@ class CurrenciesRepository(val context: Application) { } } - private fun loadDemoCurrencies(cardId: String): List { + fun loadSavedCurrenciesWithoutMigration( + cardId: String, + ): List { + if (DemoHelper.isDemoCardId(cardId)) { + return loadDemoCurrencies() + } + return try { + val json = context.readFileText(getFileNameForBlockchains(cardId)) + blockchainNetworkAdapter.fromJson(json)?.distinct() ?: emptyList() + } catch (exception: Exception) { + emptyList() + } + } + + private fun loadDemoCurrencies(): List { return DemoHelper.config.demoBlockchains.map { BlockchainNetwork( blockchain = it, @@ -123,7 +143,7 @@ class CurrenciesRepository(val context: Application) { } } - private fun tryToLoadPreviousFormatAndMigrate( + private suspend fun tryToLoadPreviousFormatAndMigrate( cardId: String, isHdWalletSupported: Boolean = false ): List { @@ -137,12 +157,12 @@ class CurrenciesRepository(val context: Application) { } } - private fun loadSavedCurrenciesOldWay( + private suspend fun loadSavedCurrenciesOldWay( cardId: String, isHdWalletSupported: Boolean = false ): List { val blockchains = loadSavedBlockchains(cardId) val tokens = loadSavedTokens(cardId) - val currencies = getSupportedTokens() + val ids = getTokensIds(tokens) val derivationStyle = if (isHdWalletSupported) DerivationStyle.LEGACY else null val blockchainNetworks = blockchains.map { blockchain -> BlockchainNetwork( @@ -152,10 +172,7 @@ class CurrenciesRepository(val context: Application) { .filter { it.blockchainDao.toBlockchain() == blockchain } .map { val token = it.toToken() - val id = currencies - .find { it.contracts.find { it.address == token.contractAddress } != null } - ?.id - token.copy(id = id) + token.copy(id = ids[token.contractAddress]) } ) } @@ -163,6 +180,21 @@ class CurrenciesRepository(val context: Application) { return blockchainNetworks } + private suspend fun getTokensIds(tokens: List): Map = coroutineScope { + tokens.map { + async { + tangemNetworkService.getTokens( + it.contractAddress, + it.blockchainDao.toBlockchain().toNetworkId() + ) + } + }.map { it.await() } + .map { (it as? Result.Success)?.data?.coins?.firstOrNull()?.id } + .mapIndexedNotNull { index, s -> + if (s == null) null else tokens[index].contractAddress to s + }.toMap() + } + fun saveCurrencies(cardId: String, currencies: List) { val json = blockchainNetworkAdapter.toJson(currencies) context.rewriteFile(json, getFileNameForBlockchains(cardId)) @@ -177,9 +209,8 @@ class CurrenciesRepository(val context: Application) { } } - fun getSupportedTokens(isTestNet: Boolean = false): List { - val fileName = if (isTestNet) "testnet_tokens" else "tokens" - val json = context.assets.readJsonFileToString(fileName) + fun getTestnetCoins(): List { + val json = context.assets.readJsonFileToString(FILE_NAME_TESTNET_COINS) return currenciesAdapter.fromJson(json)!!.coins .map { Currency.fromJsonObject(it) } } @@ -232,6 +263,7 @@ class CurrenciesRepository(val context: Application) { companion object { private const val FILE_NAME_PREFIX_TOKENS = "tokens" private const val FILE_NAME_PREFIX_BLOCKCHAINS = "blockchains" + private const val FILE_NAME_TESTNET_COINS = "testnet_tokens" fun getFileNameForTokens(cardId: String): String = "${FILE_NAME_PREFIX_TOKENS}_$cardId" fun getFileNameForBlockchains(cardId: String): String = @@ -245,145 +277,3 @@ fun Blockchain.getTokensName(): String { else -> this.fullName } } - -@JsonClass(generateAdapter = true) -data class TokenDao( - val name: String, - val symbol: String, - val contractAddress: String, - val decimalCount: Int, - @Json(name = "blockchain") - val blockchainDao: BlockchainDao, - val customIconUrl: String? = null, - val type: String? = null -) { - fun toToken(): Token { - return Token( - name = name, - symbol = symbol, - contractAddress = contractAddress, - decimals = decimalCount, - ).apply { - customIconUrl?.let { this.setCustomIconUrl(it) } - } - } - -} - -@JsonClass(generateAdapter = true) -data class BlockchainDao( - @Json(name = "key") - val name: String, - @Json(name = "testnet") - val isTestNet: Boolean -) { - fun toBlockchain(): Blockchain { - val blockchain = Blockchain.values().find { it.name.lowercase() == name.lowercase() } - ?: throw Exception("Invalid BlockchainDao") - return if (!isTestNet) blockchain else blockchain.getTestnetVersion() - ?: throw Exception("Invalid BlockchainDao") - } - - companion object { - fun fromBlockchain(blockchain: Blockchain): BlockchainDao { - val name = blockchain.name.removeSuffix("Testnet").lowercase() - return BlockchainDao(name, blockchain.isTestnet()) - } - } -} - -@JsonClass(generateAdapter = true) -data class ObsoleteTokenDao( - val name: String, - val symbol: String, - val contractAddress: String, - val decimalCount: Int, - val customIconUrl: String?, -) { - fun toTokenDao(blockchain: Blockchain): TokenDao { - return TokenDao( - name = name, - symbol = symbol, - contractAddress = contractAddress, - decimalCount = decimalCount, - blockchainDao = BlockchainDao.fromBlockchain(blockchain), - customIconUrl = customIconUrl - ) - } -} - -//@JsonClass(generateAdapter = true) -//data class CardCurrenciesDao( -// val tokens: List, -// val blockchains: List, -//) { -// fun toCardCurrencies(): CardCurrencies { -// return CardCurrencies( -// tokens = tokens.map { it.toToken() }.distinct(), -// blockchains = blockchains -// ) -// } -// -// companion object { -// fun fromCardCurrencies(cardCurrencies: CardCurrencies): CardCurrenciesDao { -// return CardCurrenciesDao( -// tokens = cardCurrencies.tokens.map { TokenDao.fromToken(it) }.distinct(), -// blockchains = cardCurrencies.blockchains -// ) -// } -// } -//} - -data class CardCurrencies( - val tokens: List, - val blockchains: List, -) - -@JsonClass(generateAdapter = true) -data class BlockchainNetwork( - val blockchain: Blockchain, - val derivationPath: String?, - val tokens: List -) { - - constructor(blockchain: Blockchain, card: Card) : this( - blockchain = blockchain, - derivationPath = if (card.settings.isHDWalletAllowed) blockchain.derivationPath(card.derivationStyle)?.rawPath else null, - tokens = emptyList() - ) - - - fun updateTokens(tokens: List): BlockchainNetwork { - return copy( - tokens = (this.tokens + tokens).distinct() - ) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (javaClass != other?.javaClass) return false - - other as BlockchainNetwork - - if (blockchain != other.blockchain) return false - if (derivationPath != other.derivationPath) return false - - return true - } - - override fun hashCode(): Int { - var result = blockchain.hashCode() - result = 31 * result + (derivationPath?.hashCode() ?: 0) - return result - } - - companion object { - fun fromWalletManager(walletManager: WalletManager): BlockchainNetwork { - return BlockchainNetwork( - walletManager.wallet.blockchain, - walletManager.wallet.publicKey.derivationPath?.rawPath, - walletManager.cardTokens.toList() - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt b/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt index 5a4df2c316..6e94b5a88a 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/Currency.kt @@ -3,6 +3,7 @@ package com.tangem.tap.domain.tokens import com.squareup.moshi.JsonClass import com.tangem.blockchain.common.Blockchain import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.network.api.tangemTech.CoinsResponse @JsonClass(generateAdapter = true) data class CurrencyFromJson( @@ -44,10 +45,20 @@ data class Currency( id = currency.id, name = currency.name, symbol = currency.symbol, - iconUrl = getIconUrl(currency.id), + iconUrl = getIconUrl(currency.id, null), contracts = currency.networks?.toContracts() ?: emptyList() ) } + + fun fromCoinResponse(currency: CoinsResponse.Coin, imageHost: String?): Currency { + return Currency( + id = currency.id, + name = currency.name, + symbol = currency.symbol, + iconUrl = getIconUrl(currency.id, imageHost), + contracts = currency.networks.mapNotNull { Contract.fromNetwork(it, imageHost) } + ) + } } } @@ -67,12 +78,26 @@ data class Contract( blockchain = blockchain, address = contract.contractAddress, decimalCount = contract.decimalCount, - iconUrl = getIconUrl(contract.networkId) + iconUrl = getIconUrl(contract.networkId, null) + ) + } + + fun fromNetwork(contract: CoinsResponse.Coin.Network, imageHost: String?): Contract? { + val blockchain = Blockchain.fromNetworkId(contract.networkId) ?: return null + return Contract( + networkId = contract.networkId, + blockchain = blockchain, + address = contract.contractAddress, + decimalCount = contract.decimalCount?.toInt(), + iconUrl = getIconUrl(contract.networkId, imageHost) ) } } } -fun getIconUrl(id: String): String { - return "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/large/$id.png" -} \ No newline at end of file +fun getIconUrl(id: String, imageHost: String? = null): String { + return "${imageHost ?: DEFAULT_IMAGE_HOST}large/$id.png" +} + +private const val DEFAULT_IMAGE_HOST = + "https://s3.eu-central-1.amazonaws.com/tangem.api/coins/" \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt b/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt new file mode 100644 index 0000000000..55301f5089 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt @@ -0,0 +1,70 @@ +package com.tangem.tap.domain.tokens + +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.services.Result +import com.tangem.domain.common.extensions.toNetworkId +import com.tangem.network.api.tangemTech.CoinsResponse +import com.tangem.network.api.tangemTech.TangemTechService + +class LoadAvailableCoinsService( + private val networkService: TangemTechService, + private val currenciesRepository: CurrenciesRepository +) { + + suspend fun getSupportedTokens( + isTestNet: Boolean = false, + supportedBlockchains: List, + page: Int, + searchInput: String? = null + ): Result { + if (isTestNet) { + return Result.Success( + LoadedCoins( + currencies = currenciesRepository.getTestnetCoins(), + moreAvailable = false, + ) + ) + } + val offset = page * LOAD_PER_PAGE + val result = loadCoins(supportedBlockchains, offset, searchInput) + + return when (result) { + is Result.Success -> { + val data = result.data + Result.Success( + LoadedCoins( + currencies = data.coins.map { Currency.fromCoinResponse(it, data.imageHost) }, + moreAvailable = data.total > offset + LOAD_PER_PAGE, + ) + ) + } + is Result.Failure -> { + Result.Failure(result.error) + } + } + } + + private suspend fun loadCoins( + supportedBlockchains: List, + offset: Int, + searchInput: String? = null + ): Result { + val networkIds = supportedBlockchains.toSet().map { it.toNetworkId() } + return networkService.getListOfCoins( + networkIds = networkIds, + offset = offset, + limit = LOAD_PER_PAGE, + searchText = searchInput + ) + } + + companion object { + const val LOAD_PER_PAGE = 100 + } +} + + +data class LoadedCoins( + val currencies: List, + val moreAvailable: Boolean, +) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainDao.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainDao.kt new file mode 100644 index 0000000000..f8704f04cb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainDao.kt @@ -0,0 +1,27 @@ +package com.tangem.tap.domain.tokens.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.tangem.blockchain.common.Blockchain + +@JsonClass(generateAdapter = true) +data class BlockchainDao( + @Json(name = "key") + val name: String, + @Json(name = "testnet") + val isTestNet: Boolean +) { + fun toBlockchain(): Blockchain { + val blockchain = Blockchain.values().find { it.name.lowercase() == name.lowercase() } + ?: throw Exception("Invalid BlockchainDao") + return if (!isTestNet) blockchain else blockchain.getTestnetVersion() + ?: throw Exception("Invalid BlockchainDao") + } + + companion object { + fun fromBlockchain(blockchain: Blockchain): BlockchainDao { + val name = blockchain.name.removeSuffix("Testnet").lowercase() + return BlockchainDao(name, blockchain.isTestnet()) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainNetwork.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainNetwork.kt new file mode 100644 index 0000000000..c6d64bb386 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/models/BlockchainNetwork.kt @@ -0,0 +1,57 @@ +package com.tangem.tap.domain.tokens.models + +import com.squareup.moshi.JsonClass +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.WalletManager +import com.tangem.common.card.Card +import com.tangem.domain.common.TapWorkarounds.derivationStyle + +@JsonClass(generateAdapter = true) +data class BlockchainNetwork( + val blockchain: Blockchain, + val derivationPath: String?, + val tokens: List +) { + + constructor(blockchain: Blockchain, card: Card) : this( + blockchain = blockchain, + derivationPath = if (card.settings.isHDWalletAllowed) blockchain.derivationPath(card.derivationStyle)?.rawPath else null, + tokens = emptyList() + ) + + + fun updateTokens(tokens: List): BlockchainNetwork { + return copy( + tokens = (this.tokens + tokens).distinct() + ) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as BlockchainNetwork + + if (blockchain != other.blockchain) return false + if (derivationPath != other.derivationPath) return false + + return true + } + + override fun hashCode(): Int { + var result = blockchain.hashCode() + result = 31 * result + (derivationPath?.hashCode() ?: 0) + return result + } + + companion object { + fun fromWalletManager(walletManager: WalletManager): BlockchainNetwork { + return BlockchainNetwork( + walletManager.wallet.blockchain, + walletManager.wallet.publicKey.derivationPath?.rawPath, + walletManager.cardTokens.toList() + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/ObsoleteTokenDao.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/ObsoleteTokenDao.kt new file mode 100644 index 0000000000..19d8bbf73d --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/models/ObsoleteTokenDao.kt @@ -0,0 +1,24 @@ +package com.tangem.tap.domain.tokens.models + +import com.squareup.moshi.JsonClass +import com.tangem.blockchain.common.Blockchain + +@JsonClass(generateAdapter = true) +data class ObsoleteTokenDao( + val name: String, + val symbol: String, + val contractAddress: String, + val decimalCount: Int, + val customIconUrl: String?, +) { + fun toTokenDao(blockchain: Blockchain): TokenDao { + return TokenDao( + name = name, + symbol = symbol, + contractAddress = contractAddress, + decimalCount = decimalCount, + blockchainDao = BlockchainDao.fromBlockchain(blockchain), + customIconUrl = customIconUrl + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/models/TokenDao.kt b/app/src/main/java/com/tangem/tap/domain/tokens/models/TokenDao.kt new file mode 100644 index 0000000000..a8a717b1ce --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/tokens/models/TokenDao.kt @@ -0,0 +1,30 @@ +package com.tangem.tap.domain.tokens.models + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import com.tangem.blockchain.common.Token +import com.tangem.tap.domain.extensions.setCustomIconUrl + +@JsonClass(generateAdapter = true) +data class TokenDao( + val name: String, + val symbol: String, + val contractAddress: String, + val decimalCount: Int, + @Json(name = "blockchain") + val blockchainDao: BlockchainDao, + val customIconUrl: String? = null, + val type: String? = null +) { + fun toToken(): Token { + return Token( + name = name, + symbol = symbol, + contractAddress = contractAddress, + decimals = decimalCount, + ).apply { + customIconUrl?.let { this.setCustomIconUrl(it) } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt index cb9b4061b5..8dbaafc988 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectMiddleware.kt @@ -16,15 +16,17 @@ import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.extensions.isMultiwalletAllowed import com.tangem.tap.domain.extensions.makeWalletManagerForApp -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.domain.walletconnect.BnbHelper import com.tangem.tap.domain.walletconnect.WalletConnectManager import com.tangem.tap.domain.walletconnect.WalletConnectNetworkUtils import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction +import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.wallet.R +import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.Middleware @@ -257,14 +259,16 @@ class WalletConnectMiddleware { blockchainToMake, card.derivationStyle?.let { DerivationParams.Default(it) } ) - if (currenciesRepository.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) - .find { it.blockchain == blockchainToMake } != null - ) { - walletManager?.let { - currenciesRepository.saveUpdatedCurrency( - card.cardId, - BlockchainNetwork.fromWalletManager(walletManager) - ) + scope.launch { + if (currenciesRepository.loadSavedCurrencies(card.cardId, card.settings.isHDWalletAllowed) + .find { it.blockchain == blockchainToMake } != null + ) { + walletManager?.let { + currenciesRepository.saveUpdatedCurrency( + card.cardId, + BlockchainNetwork.fromWalletManager(walletManager) + ) + } } } return walletManager diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt index 795e331174..1727c81944 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt @@ -24,7 +24,7 @@ import com.tangem.tap.domain.TangemSigner import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.minimalAmount -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.demo.DemoTransactionSender import com.tangem.tap.features.demo.isDemoWallet import com.tangem.tap.features.send.redux.* diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt index e95dc24e86..72d7919766 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensAction.kt @@ -17,7 +17,8 @@ sealed class TokensAction : Action { val supportedBlockchains: List? = null, val scanResponse: ScanResponse? = null ) : TokensAction() { - data class Success(val currencies: List) : TokensAction() + data class Success(val currencies: List, val loadMore: Boolean) : TokensAction() + object Failure : TokensAction() } data class SetAddedCurrencies( @@ -32,4 +33,6 @@ sealed class TokensAction : Action { ) : TokensAction() object PrepareAndNavigateToAddCustomToken : TokensAction() + + data class SetSearchInput(val searchInput: String) : TokensAction() } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt index 1c91c67b5f..48f848e65a 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensMiddleware.kt @@ -8,11 +8,13 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.toMapKey import com.tangem.common.hdWallet.DerivationPath +import com.tangem.common.services.Result import com.tangem.domain.DomainWrapped import com.tangem.domain.common.KeyWalletPublicKey import com.tangem.domain.common.ScanResponse import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction import com.tangem.domain.redux.domainStore @@ -26,10 +28,10 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.makeWalletManagerForApp -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.LoadAvailableCoinsService +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.redux.Currency import com.tangem.tap.features.wallet.redux.WalletAction -import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -40,26 +42,65 @@ class TokensMiddleware { { next -> { action -> when (action) { - is TokensAction.LoadCurrencies -> handleLoadCurrencies(action) + is TokensAction.LoadCurrencies -> handleLoadCurrencies(action.scanResponse) is TokensAction.SaveChanges -> handleSaveChanges(action) - is TokensAction.PrepareAndNavigateToAddCustomToken -> handleAddingCustomToken(action) + is TokensAction.PrepareAndNavigateToAddCustomToken -> handleAddingCustomToken( + action + ) + is TokensAction.SetSearchInput -> { + handleLoadCurrencies( + scanResponse = store.state.globalState.scanResponse, + action.searchInput + ) + } + } next(action) } } } - private fun handleLoadCurrencies(action: TokensAction.LoadCurrencies) { - val scanResponse = store.state.globalState.scanResponse + private fun handleLoadCurrencies(scanResponse: ScanResponse?, newSearchInput: String? = null) { + val tokensState = store.state.tokensState + val isTestcard = scanResponse?.card?.isTestCard ?: false + val supportedBlockchains: List = + scanResponse?.card?.supportedBlockchains() ?: Blockchain.values().toList() + .filter { !it.isTestnet() } + + val loadCoinsService = LoadAvailableCoinsService( + store.state.domainNetworks.tangemTechService, + currenciesRepository + ) + scope.launch { - val currencies = async { - currenciesRepository.getSupportedTokens(isTestcard) - .filter(action.supportedBlockchains?.toSet()) + + val loadCoinsResult = if (newSearchInput == null) { + loadCoinsService.getSupportedTokens( + isTestcard, + supportedBlockchains, + tokensState.pageToLoad, + tokensState.searchInput + ) + } else { + loadCoinsService.getSupportedTokens( + isTestcard, supportedBlockchains, 0, newSearchInput.ifBlank { null } + ) } - delay(600) - store.dispatchOnMain(TokensAction.LoadCurrencies.Success(currencies.await())) + when (loadCoinsResult) { + is Result.Success -> { + val currencies = loadCoinsResult.data.currencies + .filter(supportedBlockchains.toSet()) + store.dispatchOnMain( + TokensAction.LoadCurrencies.Success( + currencies, loadCoinsResult.data.moreAvailable + ) + ) + } + is Result.Failure -> store.dispatchOnMain(TokensAction.LoadCurrencies.Failure) + } + } } @@ -74,7 +115,8 @@ class TokensMiddleware { ) val blockchainsToAdd = action.addedBlockchains.filter { !currentBlockchains.contains(it) } - val blockchainsToRemove = currentBlockchains.filter { !action.addedBlockchains.contains(it) } + val blockchainsToRemove = + currentBlockchains.filter { !action.addedBlockchains.contains(it) } val tokensToAdd = action.addedTokens.filter { !currentTokens.contains(it) } val tokensToRemove = currentTokens.filter { token -> @@ -82,11 +124,13 @@ class TokensMiddleware { } val derivationStyle = scanResponse.card.derivationStyle - removeCurrenciesIfNeeded(convertToCurrencies( - blockchains = blockchainsToRemove, - tokens = tokensToRemove, - derivationStyle = derivationStyle - )) + removeCurrenciesIfNeeded( + convertToCurrencies( + blockchains = blockchainsToRemove, + tokens = tokensToRemove, + derivationStyle = derivationStyle + ) + ) if (tokensToAdd.isEmpty() && blockchainsToAdd.isEmpty()) { store.dispatchDebugErrorNotification("Nothing to save") @@ -246,7 +290,8 @@ class TokensMiddleware { val rawDerivationPath = currency.derivationPath ?: currency.blockchain.derivationPath(derivationStyle)?.rawPath - val blockchainNetwork = BlockchainNetwork(currency.blockchain, rawDerivationPath, emptyList()) + val blockchainNetwork = + BlockchainNetwork(currency.blockchain, rawDerivationPath, emptyList()) WalletAction.MultiWallet.AddToken(currency.token, blockchainNetwork) } } @@ -297,8 +342,15 @@ class TokensMiddleware { walletStore.walletsData.map { walletData -> walletData.currency } }.flatten().map { when (it) { - is Currency.Blockchain -> DomainWrapped.Currency.Blockchain(it.blockchain, it.derivationPath) - is Currency.Token -> DomainWrapped.Currency.Token(it.token, it.blockchain, it.derivationPath) + is Currency.Blockchain -> DomainWrapped.Currency.Blockchain( + it.blockchain, + it.derivationPath + ) + is Currency.Token -> DomainWrapped.Currency.Token( + it.token, + it.blockchain, + it.derivationPath + ) } } domainStore.dispatch(AddCustomTokenAction.Init.SetAddedCurrencies(addedCurrencies)) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt index 6ac0dbd968..374f6ad820 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensReducer.kt @@ -15,9 +15,24 @@ private fun internalReduce(action: Action, state: AppState): TokensState { val tokensState = state.tokensState return when (action) { is TokensAction.ResetState -> TokensState() - is TokensAction.LoadCurrencies -> tokensState.copy(scanResponse = action.scanResponse) + is TokensAction.LoadCurrencies -> { + val loadingState = if (tokensState.currencies.isEmpty()) { + LoadCoinsState.LOADING + } else { + LoadCoinsState.LOADED + } + tokensState.copy( + scanResponse = action.scanResponse, + loadCoinsState = loadingState + ) + } is TokensAction.LoadCurrencies.Success -> { - tokensState.copy(currencies = action.currencies) + tokensState.copy( + currencies = tokensState.currencies + action.currencies, + needToLoadMore = action.loadMore, + pageToLoad = tokensState.pageToLoad + 1, + loadCoinsState = LoadCoinsState.LOADED + ) } is TokensAction.SetAddedCurrencies -> { @@ -38,6 +53,15 @@ private fun internalReduce(action: Action, state: AppState): TokensState { is TokensAction.AllowToAddTokens -> { tokensState.copy(allowToAdd = action.allow) } + is TokensAction.SetSearchInput -> { + tokensState.copy( + searchInput = action.searchInput.ifBlank { null }, + needToLoadMore = true, + currencies = emptyList(), + pageToLoad = 0, + loadCoinsState = LoadCoinsState.LOADING + ) + } else -> tokensState } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt index 6c8e6a58e8..1c60655d24 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/redux/TokensState.kt @@ -17,9 +17,13 @@ data class TokensState( val nonRemovableTokens: List = emptyList(), val nonRemovableBlockchains: List = emptyList(), val currencies: List = emptyList(), + val searchInput: String? = null, val allowToAdd: Boolean = true, val derivationStyle: DerivationStyle? = null, - val scanResponse: ScanResponse? = null + val scanResponse: ScanResponse? = null, + val needToLoadMore: Boolean = true, + val pageToLoad: Int = 0, + val loadCoinsState: LoadCoinsState = LoadCoinsState.LOADING, ) : StateType { fun canHandleToken(token: TokenWithBlockchain): Boolean { @@ -74,4 +78,8 @@ fun List.filter(supportedBlockchains: Set?): List = mutableStateOf(store.state.tokensState), - searchInput: MutableState, onSaveChanges: (List, List) -> Unit, - onNetworkItemClicked: (ContractAddress) -> Unit + onNetworkItemClicked: (ContractAddress) -> Unit, + onLoadMore: () -> Unit ) { val context = LocalContext.current val addedTokensState = remember { mutableStateOf(tokensState.value.addedTokens) } @@ -76,7 +77,7 @@ fun CurrenciesScreen( ) { AnimatedVisibility( - visible = tokensState.value.currencies.isEmpty(), + visible = tokensState.value.loadCoinsState == LoadCoinsState.LOADING, enter = fadeIn(), exit = fadeOut() ) { @@ -90,7 +91,7 @@ fun CurrenciesScreen( } } AnimatedVisibility( - visible = tokensState.value.currencies.isNotEmpty(), + visible = tokensState.value.loadCoinsState == LoadCoinsState.LOADED, enter = fadeIn(animationSpec = tween(1000)), exit = fadeOut(animationSpec = tween(1000)) ) { @@ -103,7 +104,6 @@ fun CurrenciesScreen( nonRemovableBlockchains = tokensState.value.nonRemovableBlockchains, addedTokens = addedTokensState.value, addedBlockchains = addedBlockchainsState.value, - searchInput = searchInput.value, allowToAdd = tokensState.value.allowToAdd, onAddCurrencyToggled = { currency, token -> onAddCurrencyToggleClick(currency, token) @@ -118,7 +118,8 @@ fun CurrenciesScreen( } }, - onNetworkItemClicked = onNetworkItemClicked + onNetworkItemClicked = onNetworkItemClicked, + onLoadMore = onLoadMore ) } } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt b/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt index d55cbd35a4..e6fa6a6d1c 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/ui/compose/ListOfCurrencies.kt @@ -3,8 +3,9 @@ package com.tangem.tap.features.tokens.ui.compose import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.runtime.Composable +import androidx.compose.runtime.SideEffect import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier @@ -23,10 +24,10 @@ fun ListOfCurrencies( nonRemovableBlockchains: List, addedTokens: List, addedBlockchains: List, - searchInput: String, allowToAdd: Boolean, onAddCurrencyToggled: (Currency, TokenWithBlockchain?) -> Unit, - onNetworkItemClicked: (ContractAddress) -> Unit + onNetworkItemClicked: (ContractAddress) -> Unit, + onLoadMore: () -> Unit ) { val expandedCurrencies = remember { mutableStateOf(listOf("")) } @@ -46,31 +47,28 @@ fun ListOfCurrencies( .fillMaxSize(), contentPadding = PaddingValues(bottom = 90.dp) ) { - - val filteredCurrencies = if (searchInput.isBlank()) { - currencies - } else { - currencies.asSequence().filter { - it.name.lowercase().contains(searchInput) || it.symbol.lowercase() - .contains(searchInput) - }.toList() - } item { header() } - items(filteredCurrencies) { currency -> - CurrencyItem( - currency = currency, - nonRemovableTokens = nonRemovableTokens, - nonRemovableBlockchains = nonRemovableBlockchains, - addedTokens = addedTokens, - addedBlockchains = addedBlockchains, - allowToAdd = allowToAdd, - expanded = expandedCurrencies.value.contains(currency.id), - onCurrencyClick = onCurrencyClick, - onAddCurrencyToggled = onAddCurrencyToggled, - onNetworkItemClicked = onNetworkItemClicked - ) - } + itemsIndexed(currencies) { index, currency -> + val lastIndex = currencies.lastIndex + if (currencies.isNotEmpty()) { + if (index + 40 == lastIndex) { + SideEffect { onLoadMore() } + } + CurrencyItem( + currency = currency, + nonRemovableTokens = nonRemovableTokens, + nonRemovableBlockchains = nonRemovableBlockchains, + addedTokens = addedTokens, + addedBlockchains = addedBlockchains, + allowToAdd = allowToAdd, + expanded = expandedCurrencies.value.contains(currency.id), + onCurrencyClick = onCurrencyClick, + onAddCurrencyToggled = onAddCurrencyToggled, + onNetworkItemClicked = onNetworkItemClicked + ) + } + } } } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt index ce2ce4b63d..d11ccd4aea 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletAction.kt @@ -1,11 +1,7 @@ package com.tangem.tap.features.wallet.redux import android.content.Context -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType import com.tangem.common.card.Card import com.tangem.tap.common.entities.FiatCurrency @@ -13,10 +9,10 @@ import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.wallet.R -import java.math.BigDecimal import org.rekotlin.Action +import java.math.BigDecimal sealed class WalletAction : Action { diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt index c815b4b404..acf43410bb 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/WalletState.kt @@ -1,13 +1,7 @@ package com.tangem.tap.features.wallet.redux import android.graphics.Bitmap -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle -import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.blockchain.common.address.AddressType import com.tangem.blockchain.extensions.isAboveZero import com.tangem.common.extensions.isZero @@ -23,21 +17,16 @@ import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.extensions.buyIsAllowed import com.tangem.tap.domain.extensions.sellIsAllowed import com.tangem.tap.domain.extensions.toSendableAmounts -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState import com.tangem.tap.features.tokens.redux.TokenWithBlockchain -import com.tangem.tap.features.wallet.models.PendingTransaction -import com.tangem.tap.features.wallet.models.TotalBalance -import com.tangem.tap.features.wallet.models.WalletRent -import com.tangem.tap.features.wallet.models.WalletWarning -import com.tangem.tap.features.wallet.models.toPendingTransactions -import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken +import com.tangem.tap.features.wallet.models.* import com.tangem.tap.features.wallet.ui.BalanceStatus import com.tangem.tap.features.wallet.ui.BalanceWidgetData import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.store -import java.math.BigDecimal import org.rekotlin.StateType +import java.math.BigDecimal import kotlin.properties.ReadOnlyProperty data class WalletState( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt index 22ce291074..ace70c1988 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/MultiWalletMiddleware.kt @@ -9,7 +9,7 @@ import com.tangem.tap.common.redux.navigation.AppScreen import com.tangem.tap.common.redux.navigation.NavigationAction import com.tangem.tap.currenciesRepository import com.tangem.tap.domain.extensions.makeWalletManagerForApp -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.demo.isDemoCard import com.tangem.tap.features.wallet.redux.Currency diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt index a7437c94eb..a63dc38fe0 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/MultiWalletReducer.kt @@ -7,7 +7,7 @@ import com.tangem.common.extensions.isZero import com.tangem.tap.common.extensions.toFiatString import com.tangem.tap.common.extensions.toFormattedCurrencyString import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.removeUnknownTransactions import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.redux.* diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt index ce9b5cf80a..afd28986a2 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/OnWalletLoadedReducer.kt @@ -8,7 +8,7 @@ import com.tangem.tap.common.extensions.toFiatValue import com.tangem.tap.common.extensions.toFormattedCurrencyString import com.tangem.tap.common.extensions.toFormattedFiatValue import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.removeUnknownTransactions import com.tangem.tap.features.wallet.models.toPendingTransactions import com.tangem.tap.features.wallet.redux.* diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt index e4126a3f8c..80f5442084 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/reducers/WalletReducer.kt @@ -14,7 +14,7 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.TapError import com.tangem.tap.domain.extensions.getArtworkUrl import com.tangem.tap.domain.getFirstToken -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.models.WalletRent import com.tangem.tap.features.wallet.redux.* import com.tangem.tap.features.wallet.ui.BalanceStatus diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt index f267c25f89..4cf03e0d20 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/WalletDetailsFragment.kt @@ -19,7 +19,7 @@ import com.tangem.tap.common.extensions.* import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.StateDialog import com.tangem.tap.common.redux.navigation.NavigationAction -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.onboarding.getQRReceiveMessage import com.tangem.tap.features.wallet.models.PendingTransaction import com.tangem.tap.features.wallet.redux.* diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt index e72efc46ac..6b99ef8be9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/test/TestWalletDetailsActions.kt @@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.WalletManager import com.tangem.tap.common.TestAction import com.tangem.tap.common.TestActions -import com.tangem.tap.domain.tokens.BlockchainNetwork +import com.tangem.tap.domain.tokens.models.BlockchainNetwork import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.store import java.math.BigDecimal diff --git a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt index 9a1f717c34..807fa627f8 100644 --- a/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt +++ b/domain/src/main/java/com/tangem/domain/features/addCustomToken/AddCustomTokenService.kt @@ -18,7 +18,7 @@ class AddCustomTokenService( active: Boolean? = null, ): Result> { val networksIds = selectNetworksForSearch(networkId) - val result = tangemTechService.coins(contractAddress, networksIds, active) + val result = tangemTechService.getTokens(contractAddress, networksIds, active) return when (result) { is Result.Success -> { var coinsList = mutableListOf() diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt index bc5b09f51f..c21fb92726 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/Responses.kt @@ -9,7 +9,7 @@ interface HttpResponse sealed interface TangemTechResponse : HttpResponse data class CoinsResponse( - val imageHost: String, + val imageHost: String?, val coins: List, val total: Int ) : TangemTechResponse { diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt index f2c689e15c..0081f4ee24 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechApi.kt @@ -11,8 +11,11 @@ interface TangemTechApi { @GET("coins") suspend fun coins( @Query("contractAddress") contractAddress: String? = null, - @Query("networkIds") networkId: String? = null, + @Query("networkIds") networkIds: String? = null, @Query("active") active: Boolean? = null, + @Query("searchText") searchText: String? = null, + @Query("offset") offset: Int? = null, + @Query("limit") limit: Int? = null ): CoinsResponse @GET("rates") diff --git a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt index e4c3321f3b..2cfac4a830 100644 --- a/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt +++ b/network/src/main/java/com/tangem/network/api/tangemTech/TangemTechService.kt @@ -15,15 +15,37 @@ class TangemTechService { private val headerInterceptors = mutableListOf( CacheControlHttpInterceptor(cacheMaxAge) ) - + private var api: TangemTechApi = createApi() - suspend fun coins( - contractAddress: String? = null, + suspend fun getTokens( + contractAddress: String, networkId: String? = null, active: Boolean? = null, ): Result = withContext(Dispatchers.IO) { - performRequest { api.coins(contractAddress, networkId, active) } + performRequest { + api.coins( + contractAddress = contractAddress, + networkIds = networkId, + active = active + ) + } + } + + suspend fun getListOfCoins( + networkIds: List, + searchText: String? = null, + offset: Int? = null, + limit: Int? = null + ): Result = withContext(Dispatchers.IO) { + performRequest { + api.coins( + networkIds = networkIds.joinToString(","), + searchText = searchText, + offset = offset, + limit = limit + ) + } } suspend fun rates(