Updated on 2026-08-14
This commit is contained in:
parent
a3a0d325da
commit
c683506ffa
16 changed files with 389 additions and 76 deletions
|
|
@ -40,6 +40,7 @@ dependencies {
|
|||
implementation(deps.androidx.constraintLayout)
|
||||
implementation(deps.androidx.activity.compose)
|
||||
implementation(deps.androidx.browser)
|
||||
implementation(deps.androidx.paging.runtime)
|
||||
implementation(deps.lifecycle.runtime.ktx)
|
||||
implementation(deps.lifecycle.common.java8)
|
||||
implementation(deps.lifecycle.viewModel.ktx)
|
||||
|
|
@ -56,6 +57,7 @@ dependencies {
|
|||
implementation(deps.compose.shimmer)
|
||||
implementation(deps.compose.ui)
|
||||
implementation(deps.compose.ui.tooling)
|
||||
implementation(deps.compose.paging)
|
||||
|
||||
/** Firebase libraries */
|
||||
implementation(platform(deps.firebase.bom))
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ class LoadAvailableCoinsService(
|
|||
MoshiConverter.networkMoshi.adapter(CurrenciesFromJson::class.java)
|
||||
|
||||
suspend fun getSupportedTokens(
|
||||
isTestNet: Boolean = false,
|
||||
isTestNet: Boolean,
|
||||
supportedBlockchains: List<Blockchain>,
|
||||
page: Int,
|
||||
searchInput: String? = null,
|
||||
searchInput: String?,
|
||||
): Result<LoadedCoins> {
|
||||
if (isTestNet) {
|
||||
return Result.Success(
|
||||
|
|
@ -33,15 +33,17 @@ class LoadAvailableCoinsService(
|
|||
),
|
||||
)
|
||||
}
|
||||
val offset = page * LOAD_PER_PAGE
|
||||
val result = loadCoins(supportedBlockchains, offset, searchInput)
|
||||
|
||||
return when (result) {
|
||||
val offset = page * LOAD_PER_PAGE
|
||||
return when (val result = loadCoins(supportedBlockchains, offset, searchInput)) {
|
||||
is Result.Success -> {
|
||||
val data = result.data
|
||||
|
||||
Result.Success(
|
||||
LoadedCoins(
|
||||
currencies = data.coins.map { Currency.fromCoinResponse(it, data.imageHost) },
|
||||
currencies = data.coins.map {
|
||||
Currency.fromCoinResponse(currency = it, imageHost = data.imageHost)
|
||||
},
|
||||
moreAvailable = data.total > offset + LOAD_PER_PAGE,
|
||||
),
|
||||
)
|
||||
|
|
@ -55,22 +57,24 @@ class LoadAvailableCoinsService(
|
|||
private suspend fun loadCoins(
|
||||
supportedBlockchains: List<Blockchain>,
|
||||
offset: Int,
|
||||
searchInput: String? = null,
|
||||
searchInput: String?,
|
||||
): Result<CoinsResponse> {
|
||||
return withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
tangemTechApi.getCoins(
|
||||
networkIds = supportedBlockchains.toSet().joinToString(",", transform = Blockchain::toNetworkId),
|
||||
networkIds = supportedBlockchains.joinToString(
|
||||
separator = ",",
|
||||
transform = Blockchain::toNetworkId,
|
||||
),
|
||||
active = true,
|
||||
searchText = searchInput,
|
||||
offset = offset,
|
||||
limit = LOAD_PER_PAGE,
|
||||
)
|
||||
}
|
||||
.onSuccess { return@withContext Result.Success(it) }
|
||||
.onFailure { return@withContext Result.Failure(it) }
|
||||
|
||||
error("Unreachable code because runCatching must return result")
|
||||
}.fold(
|
||||
onSuccess = { Result.Success(it) },
|
||||
onFailure = { Result.Failure(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,15 +87,15 @@ class LoadAvailableCoinsService(
|
|||
private fun List<Currency>.filter(searchInput: String?): List<Currency> {
|
||||
if (searchInput.isNullOrBlank()) return this
|
||||
|
||||
return filter {
|
||||
it.symbol.contains(searchInput, ignoreCase = true) ||
|
||||
it.name.contains(searchInput, ignoreCase = true)
|
||||
return filter { currency ->
|
||||
currency.symbol.contains(searchInput, ignoreCase = true) ||
|
||||
currency.name.contains(searchInput, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private companion object {
|
||||
const val LOAD_PER_PAGE = 100
|
||||
private const val FILE_NAME_TESTNET_COINS = "testnet_tokens"
|
||||
const val FILE_NAME_TESTNET_COINS = "testnet_tokens"
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
package com.tangem.tap.features.tokens.data
|
||||
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.features.tokens.domain.TokensListRepository
|
||||
import com.tangem.tap.features.tokens.domain.models.Token
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Default repository implementation of tokens list feature
|
||||
*
|
||||
* @property tangemTechApi Tangem Tech API
|
||||
* @property dispatchers coroutine dispatchers provider
|
||||
* @property reduxStateHolder redux state holder
|
||||
* @property testnetTokensStorage storage for getting testnet tokens data
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class DefaultTokensListRepository(
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val reduxStateHolder: AppStateHolder,
|
||||
private val testnetTokensStorage: TestnetTokensStorage,
|
||||
) : TokensListRepository {
|
||||
|
||||
override fun getAvailableTokens(searchText: String?): Flow<PagingData<Token>> {
|
||||
return Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 100,
|
||||
prefetchDistance = 70,
|
||||
enablePlaceholders = false,
|
||||
),
|
||||
pagingSourceFactory = {
|
||||
if (reduxStateHolder.scanResponse?.card?.isTestCard == true) {
|
||||
TestnetTokensPagingSource(testnetTokensStorage, searchText)
|
||||
} else {
|
||||
TangemApiTokensPagingSource(
|
||||
api = tangemTechApi,
|
||||
dispatchers = dispatchers,
|
||||
reduxStateHolder = reduxStateHolder,
|
||||
searchText = searchText,
|
||||
)
|
||||
}
|
||||
},
|
||||
).flow
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.tap.features.tokens.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.domain.common.extensions.supportedBlockchains
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.tap.features.tokens.data.converters.CoinsResponseConverter
|
||||
import com.tangem.tap.features.tokens.domain.models.Token
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
|
||||
/**
|
||||
* Paging source that get tokens by Tangem Tech API
|
||||
*
|
||||
* @property api Tangem Tech API
|
||||
* @property dispatchers coroutine dispatchers provider
|
||||
* @property reduxStateHolder redux state holder
|
||||
* @property searchText search text
|
||||
*/
|
||||
internal class TangemApiTokensPagingSource(
|
||||
private val api: TangemTechApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val reduxStateHolder: AppStateHolder,
|
||||
private val searchText: String?,
|
||||
) : PagingSource<Int, Token>() {
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, Token>): Int? {
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(other = 1)
|
||||
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(other = 1)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Token> {
|
||||
val page = params.key ?: 0
|
||||
|
||||
return runCatching(dispatchers.io) {
|
||||
val supportedBlockchains = reduxStateHolder.scanResponse?.card?.supportedBlockchains()
|
||||
?: Blockchain.values().toList()
|
||||
|
||||
api.getCoins(
|
||||
networkIds = supportedBlockchains.joinToString(separator = ",", transform = Blockchain::toNetworkId),
|
||||
active = true,
|
||||
searchText = searchText,
|
||||
offset = page * params.loadSize,
|
||||
limit = params.loadSize,
|
||||
)
|
||||
}.fold(
|
||||
onSuccess = { response ->
|
||||
LoadResult.Page(
|
||||
data = CoinsResponseConverter.convert(response),
|
||||
prevKey = if (page == 0) null else page.minus(other = 1),
|
||||
nextKey = if (response.coins.isEmpty()) null else page.plus(other = 1),
|
||||
)
|
||||
},
|
||||
onFailure = { LoadResult.Error(it) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.tap.features.tokens.data
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.paging.PagingState
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.tap.features.tokens.data.converters.TestnetTokensConfigConverter
|
||||
import com.tangem.tap.features.tokens.domain.models.Token
|
||||
|
||||
/**
|
||||
* Paging source that get tokens by testnet tokens config
|
||||
*
|
||||
* @property testnetTokensStorage testnet tokens config storage
|
||||
* @property searchText search text
|
||||
*/
|
||||
internal class TestnetTokensPagingSource(
|
||||
private val testnetTokensStorage: TestnetTokensStorage,
|
||||
private val searchText: String?,
|
||||
) : PagingSource<Int, Token>() {
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, Token>): Int? = null
|
||||
|
||||
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Token> {
|
||||
return LoadResult.Page(
|
||||
data = TestnetTokensConfigConverter.convert(
|
||||
value = testnetTokensStorage.getConfig().searchByText(searchText),
|
||||
),
|
||||
prevKey = null,
|
||||
nextKey = null,
|
||||
)
|
||||
}
|
||||
|
||||
private fun TestnetTokensConfig.searchByText(searchText: String?): TestnetTokensConfig {
|
||||
if (searchText.isNullOrBlank()) return this
|
||||
|
||||
return copy(
|
||||
tokens = tokens.filter { token ->
|
||||
token.symbol.contains(other = searchText, ignoreCase = true) ||
|
||||
token.name.contains(other = searchText, ignoreCase = true)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.features.tokens.data.converters
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.api.tangemTech.models.CoinsResponse
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.tap.domain.tokens.getIconUrl
|
||||
import com.tangem.tap.features.tokens.domain.models.Token
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converter from data model [CoinsResponse] to list of domain models [Token]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object CoinsResponseConverter : Converter<CoinsResponse, List<Token>> {
|
||||
|
||||
override fun convert(value: CoinsResponse): List<Token> {
|
||||
return value.coins.map { token ->
|
||||
Token(
|
||||
id = token.id,
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
iconUrl = getIconUrl(token.id, value.imageHost),
|
||||
networks = token.networks.mapNotNull { network ->
|
||||
val blockchain = Blockchain.fromNetworkId(network.networkId) ?: return@mapNotNull null
|
||||
|
||||
Token.Network(
|
||||
id = network.networkId,
|
||||
blockchain = blockchain,
|
||||
address = network.contractAddress,
|
||||
iconUrl = getIconUrl(network.networkId, value.imageHost),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.features.tokens.data.converters
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.datasource.local.testnet.models.TestnetTokensConfig
|
||||
import com.tangem.domain.common.extensions.fromNetworkId
|
||||
import com.tangem.tap.domain.tokens.getIconUrl
|
||||
import com.tangem.tap.features.tokens.domain.models.Token
|
||||
import com.tangem.utils.converter.Converter
|
||||
|
||||
/**
|
||||
* Converter from data model [TestnetTokensConfig] to list of domain models [Token]
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal object TestnetTokensConfigConverter : Converter<TestnetTokensConfig, List<Token>> {
|
||||
|
||||
override fun convert(value: TestnetTokensConfig): List<Token> {
|
||||
return value.tokens.map { token ->
|
||||
Token(
|
||||
id = token.id,
|
||||
name = token.name,
|
||||
symbol = token.symbol,
|
||||
iconUrl = getIconUrl(id = token.id, imageHost = null),
|
||||
networks = token.networks?.mapNotNull { network ->
|
||||
val blockchain = Blockchain.fromNetworkId(network.id) ?: return@mapNotNull null
|
||||
|
||||
Token.Network(
|
||||
id = network.id,
|
||||
blockchain = blockchain,
|
||||
address = network.address,
|
||||
iconUrl = getIconUrl(id = network.id, imageHost = null),
|
||||
)
|
||||
}.orEmpty(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package com.tangem.tap.features.tokens.di
|
||||
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.datasource.local.testnet.TestnetTokensStorage
|
||||
import com.tangem.tap.features.tokens.data.DefaultTokensListRepository
|
||||
import com.tangem.tap.features.tokens.domain.TokensListRepository
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
internal object TokensListRepositoryModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesTokensListRepository(
|
||||
tangemTechApi: TangemTechApi,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
reduxStateHolder: AppStateHolder,
|
||||
testnetTokensStorage: TestnetTokensStorage,
|
||||
): TokensListRepository {
|
||||
return DefaultTokensListRepository(
|
||||
tangemTechApi = tangemTechApi,
|
||||
dispatchers = dispatchers,
|
||||
reduxStateHolder = reduxStateHolder,
|
||||
testnetTokensStorage = testnetTokensStorage,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.tap.features.tokens.domain
|
||||
|
||||
import androidx.paging.PagingData
|
||||
import com.tangem.tap.features.tokens.domain.models.Token
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
/**
|
||||
* Repository of tokens list feature
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal interface TokensListRepository {
|
||||
|
||||
/**
|
||||
* Get available tokens list
|
||||
*
|
||||
* @param searchText search text
|
||||
*/
|
||||
fun getAvailableTokens(searchText: String?): Flow<PagingData<Token>>
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package com.tangem.tap.features.tokens.domain.models
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
|
||||
/**
|
||||
* Domain model of token for tokens list screen
|
||||
*
|
||||
* @property id token id
|
||||
* @property name token name
|
||||
* @property symbol token brief name. Example, "BTC"
|
||||
* @property iconUrl token icon url
|
||||
* @property networks token networks
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal data class Token(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val iconUrl: String,
|
||||
val networks: List<Network>,
|
||||
) {
|
||||
|
||||
/**
|
||||
* Domain model of network for tokens list screen
|
||||
*
|
||||
* @property id network id
|
||||
* @property blockchain blockchain
|
||||
* @property address address. If address equals null, it means it is the main network of token
|
||||
* @property iconUrl network icon url
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
data class Network(
|
||||
val id: String,
|
||||
val blockchain: Blockchain,
|
||||
val address: String?,
|
||||
val iconUrl: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -13,10 +13,7 @@ sealed class TokensAction : Action {
|
|||
|
||||
data class AllowToAddTokens(val allow: Boolean) : TokensAction()
|
||||
|
||||
data class LoadCurrencies(
|
||||
val supportedBlockchains: List<Blockchain>? = null,
|
||||
val scanResponse: ScanResponse? = null,
|
||||
) : TokensAction() {
|
||||
data class LoadCurrencies(val scanResponse: ScanResponse? = null) : TokensAction() {
|
||||
data class Success(val currencies: List<Currency>, val loadMore: Boolean) : TokensAction()
|
||||
object Failure : TokensAction()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,11 +76,8 @@ class TokensMiddleware {
|
|||
private fun handleLoadCurrencies(scanResponse: ScanResponse?, newSearchInput: String? = null) {
|
||||
val tokensState = store.state.tokensState
|
||||
|
||||
val isTestcard = scanResponse?.card?.isTestCard ?: false
|
||||
|
||||
val supportedBlockchains: List<Blockchain> =
|
||||
scanResponse?.card?.supportedBlockchains() ?: Blockchain.values().toList()
|
||||
.filter { !it.isTestnet() }
|
||||
val supportedBlockchains: List<Blockchain> = scanResponse?.card?.supportedBlockchains()
|
||||
?: Blockchain.values().toList().filterNot(Blockchain::isTestnet)
|
||||
|
||||
val loadCoinsService = LoadAvailableCoinsService(
|
||||
tangemTechApi = store.state.domainNetworks.tangemTechService.api,
|
||||
|
|
@ -89,34 +86,26 @@ class TokensMiddleware {
|
|||
)
|
||||
|
||||
scope.launch {
|
||||
val loadCoinsResult = if (newSearchInput == null) {
|
||||
loadCoinsService.getSupportedTokens(
|
||||
isTestcard,
|
||||
supportedBlockchains,
|
||||
tokensState.pageToLoad,
|
||||
tokensState.searchInput,
|
||||
)
|
||||
} else {
|
||||
loadCoinsService.getSupportedTokens(
|
||||
isTestNet = isTestcard,
|
||||
supportedBlockchains = supportedBlockchains,
|
||||
page = 0,
|
||||
searchInput = newSearchInput.ifBlank { null },
|
||||
)
|
||||
}
|
||||
when (loadCoinsResult) {
|
||||
is Result.Success -> {
|
||||
val currencies = loadCoinsResult.data.currencies
|
||||
.filter(supportedBlockchains.toSet())
|
||||
store.dispatchOnMain(
|
||||
val result = loadCoinsService.getSupportedTokens(
|
||||
isTestNet = scanResponse?.card?.isTestCard ?: false,
|
||||
supportedBlockchains = supportedBlockchains,
|
||||
page = if (newSearchInput == null) tokensState.pageToLoad else 0,
|
||||
searchInput = if (newSearchInput == null) tokensState.searchInput else newSearchInput.ifBlank { null },
|
||||
)
|
||||
|
||||
store.dispatchOnMain(
|
||||
action = when (result) {
|
||||
is Result.Success -> {
|
||||
TokensAction.LoadCurrencies.Success(
|
||||
currencies,
|
||||
loadCoinsResult.data.moreAvailable,
|
||||
),
|
||||
)
|
||||
}
|
||||
is Result.Failure -> store.dispatchOnMain(TokensAction.LoadCurrencies.Failure)
|
||||
}
|
||||
currencies = result.data.currencies.filter(supportedBlockchains.toSet()),
|
||||
loadMore = result.data.moreAvailable,
|
||||
)
|
||||
}
|
||||
is Result.Failure -> {
|
||||
TokensAction.LoadCurrencies.Failure
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ private fun internalReduce(action: Action, state: AppState): TokensState {
|
|||
addedBlockchains = action.wallets.toNonCustomBlockchains(action.derivationStyle),
|
||||
addedTokens = action.wallets.toNonCustomTokensWithBlockchains(action.derivationStyle),
|
||||
addedWallets = action.wallets,
|
||||
derivationStyle = action.derivationStyle,
|
||||
)
|
||||
}
|
||||
is TokensAction.AllowToAddTokens -> {
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ data class TokensState(
|
|||
val currencies: List<Currency> = emptyList(),
|
||||
val searchInput: String? = null,
|
||||
val allowToAdd: Boolean = true,
|
||||
val derivationStyle: DerivationStyle? = null,
|
||||
val scanResponse: ScanResponse? = null,
|
||||
val needToLoadMore: Boolean = true,
|
||||
val pageToLoad: Int = 0,
|
||||
|
|
@ -67,19 +66,19 @@ data class TokenWithBlockchain(
|
|||
|
||||
fun List<Currency>.filter(supportedBlockchains: Set<Blockchain>?): List<Currency> {
|
||||
if (supportedBlockchains == null) return this
|
||||
return map {
|
||||
it.copy(
|
||||
contracts =
|
||||
it.contracts.filter {
|
||||
supportedBlockchains.contains(it.blockchain) &&
|
||||
(it.blockchain.canHandleTokens() || it.address == null)
|
||||
|
||||
return map { currency ->
|
||||
currency.copy(
|
||||
contracts = currency.contracts.filter { contract ->
|
||||
supportedBlockchains.contains(contract.blockchain) &&
|
||||
(contract.blockchain.canHandleTokens() || contract.address == null)
|
||||
},
|
||||
)
|
||||
}.filterNot {
|
||||
it.contracts.isNullOrEmpty() && !supportedBlockchains.contains(Blockchain.fromNetworkId(it.id))
|
||||
}.filterNot { currency ->
|
||||
currency.contracts.isEmpty() && !supportedBlockchains.contains(Blockchain.fromNetworkId(currency.id))
|
||||
}
|
||||
}
|
||||
|
||||
enum class LoadCoinsState {
|
||||
LOADING, LOADED, ERROR
|
||||
LOADING, LOADED,
|
||||
}
|
||||
|
|
@ -6,7 +6,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
|
|||
import com.badoo.mvicore.modelWatcher
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.common.TapWorkarounds.derivationStyle
|
||||
import com.tangem.domain.common.TapWorkarounds.isTestCard
|
||||
import com.tangem.tap.common.analytics.events.MainScreen
|
||||
import com.tangem.tap.common.analytics.events.ManageTokens
|
||||
import com.tangem.tap.common.analytics.events.Portfolio
|
||||
|
|
@ -15,7 +14,6 @@ import com.tangem.tap.common.extensions.hide
|
|||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.tokens.CurrenciesRepository
|
||||
import com.tangem.tap.features.tokens.redux.TokensAction
|
||||
import com.tangem.tap.features.wallet.models.TotalBalance
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
|
|
@ -110,15 +108,7 @@ class MultiWalletView : WalletView() {
|
|||
binding.btnAddToken.setOnClickListener {
|
||||
val card = store.state.globalState.scanResponse!!.card
|
||||
Analytics.send(Portfolio.ButtonManageTokens())
|
||||
store.dispatch(
|
||||
TokensAction.LoadCurrencies(
|
||||
supportedBlockchains = CurrenciesRepository.getBlockchains(
|
||||
card.firmwareVersion,
|
||||
card.isTestCard,
|
||||
),
|
||||
scanResponse = store.state.globalState.scanResponse,
|
||||
),
|
||||
)
|
||||
store.dispatch(TokensAction.LoadCurrencies(scanResponse = store.state.globalState.scanResponse))
|
||||
store.dispatch(TokensAction.AllowToAddTokens(true))
|
||||
store.dispatch(
|
||||
TokensAction.SetAddedCurrencies(
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ androidxConstraintLayout = "2.1.4"
|
|||
androidxCore = "1.9.0"
|
||||
androidxFragment = "1.5.3"
|
||||
androidxLifecycle = "2.5.1"
|
||||
androidx-paging = "3.1.1"
|
||||
# endregion AndroidX
|
||||
|
||||
# region Compose
|
||||
|
|
@ -27,6 +28,7 @@ compose-material = "1.3.1"
|
|||
compose-constraint = "1.0.1"
|
||||
compose-navigation = "2.5.3"
|
||||
compose-accompanist = "0.28.0"
|
||||
compose-paging = "1.0.0-alpha18"
|
||||
# endregion Compose
|
||||
|
||||
# region Other libraries
|
||||
|
|
@ -116,6 +118,7 @@ androidx-browser = { module = "androidx.browser:browser", version.ref = "android
|
|||
androidx-constraintLayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidxConstraintLayout" }
|
||||
androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCore" }
|
||||
androidx-fragment-ktx = { module = "androidx.fragment:fragment-ktx", version.ref = "androidxFragment" }
|
||||
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "androidx-paging" }
|
||||
lifecycle-common-java8 = { module = "androidx.lifecycle:lifecycle-common-java8", version.ref = "androidxLifecycle" }
|
||||
lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" }
|
||||
lifecycle-viewModel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidxLifecycle" }
|
||||
|
|
@ -135,6 +138,7 @@ compose-navigation-hilt = { module = "androidx.hilt:hilt-navigation-compose", ve
|
|||
compose-accompanist-appCompatTheme = { module = "com.google.accompanist:accompanist-appcompat-theme", version.ref = "compose-accompanist" }
|
||||
compose-accompanist-systemUiController = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "compose-accompanist" }
|
||||
compose-accompanist-webView = { module = "com.google.accompanist:accompanist-webview", version.ref = "compose-accompanist" }
|
||||
compose-paging = { module = "androidx.paging:paging-compose", version.ref = "compose-paging" }
|
||||
# endregion Compose
|
||||
|
||||
# region Firebase
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue