diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b3831aa789..f3691ec571 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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)) 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 index 51c4c304e2..39e845f7b2 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/LoadAvailableCoinsService.kt @@ -20,10 +20,10 @@ class LoadAvailableCoinsService( MoshiConverter.networkMoshi.adapter(CurrenciesFromJson::class.java) suspend fun getSupportedTokens( - isTestNet: Boolean = false, + isTestNet: Boolean, supportedBlockchains: List, page: Int, - searchInput: String? = null, + searchInput: String?, ): Result { 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, offset: Int, - searchInput: String? = null, + searchInput: String?, ): Result { 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.filter(searchInput: String?): List { 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" } } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/data/DefaultTokensListRepository.kt b/app/src/main/java/com/tangem/tap/features/tokens/data/DefaultTokensListRepository.kt new file mode 100644 index 0000000000..ba522709c6 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/data/DefaultTokensListRepository.kt @@ -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> { + 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 + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/data/TangemApiTokensPagingSource.kt b/app/src/main/java/com/tangem/tap/features/tokens/data/TangemApiTokensPagingSource.kt new file mode 100644 index 0000000000..d9d63213bd --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/data/TangemApiTokensPagingSource.kt @@ -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() { + + override fun getRefreshKey(state: PagingState): 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): LoadResult { + 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) }, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/data/TestnetTokensPagingSource.kt b/app/src/main/java/com/tangem/tap/features/tokens/data/TestnetTokensPagingSource.kt new file mode 100644 index 0000000000..87943ee140 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/data/TestnetTokensPagingSource.kt @@ -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() { + + override fun getRefreshKey(state: PagingState): Int? = null + + override suspend fun load(params: LoadParams): LoadResult { + 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) + }, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/data/converters/CoinsResponseConverter.kt b/app/src/main/java/com/tangem/tap/features/tokens/data/converters/CoinsResponseConverter.kt new file mode 100644 index 0000000000..9fa6492d80 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/data/converters/CoinsResponseConverter.kt @@ -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> { + + override fun convert(value: CoinsResponse): List { + 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), + ) + }, + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/data/converters/TestnetTokensConfigConverter.kt b/app/src/main/java/com/tangem/tap/features/tokens/data/converters/TestnetTokensConfigConverter.kt new file mode 100644 index 0000000000..6a3b684b51 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/data/converters/TestnetTokensConfigConverter.kt @@ -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> { + + override fun convert(value: TestnetTokensConfig): List { + 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(), + ) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/di/TokensListRepositoryModule.kt b/app/src/main/java/com/tangem/tap/features/tokens/di/TokensListRepositoryModule.kt new file mode 100644 index 0000000000..e71a3a6531 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/di/TokensListRepositoryModule.kt @@ -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, + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/domain/TokensListRepository.kt b/app/src/main/java/com/tangem/tap/features/tokens/domain/TokensListRepository.kt new file mode 100644 index 0000000000..95fdde59ff --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/domain/TokensListRepository.kt @@ -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> +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/tokens/domain/models/Token.kt b/app/src/main/java/com/tangem/tap/features/tokens/domain/models/Token.kt new file mode 100644 index 0000000000..d5724c314c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/tokens/domain/models/Token.kt @@ -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, +) { + + /** + * 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, + ) +} \ No newline at end of file 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 612a51e449..78cfc5ac9e 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 @@ -13,10 +13,7 @@ sealed class TokensAction : Action { data class AllowToAddTokens(val allow: Boolean) : TokensAction() - data class LoadCurrencies( - val supportedBlockchains: List? = null, - val scanResponse: ScanResponse? = null, - ) : TokensAction() { + data class LoadCurrencies(val scanResponse: ScanResponse? = null) : TokensAction() { data class Success(val currencies: List, val loadMore: Boolean) : TokensAction() object Failure : TokensAction() } 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 305df3393e..9b3be6dca7 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 @@ -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 = - scanResponse?.card?.supportedBlockchains() ?: Blockchain.values().toList() - .filter { !it.isTestnet() } + val supportedBlockchains: List = 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 + } + }, + ) } } 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 e8ab2383d8..c792805d9b 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 @@ -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 -> { 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 5c4f723467..3cb59514c8 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 @@ -18,7 +18,6 @@ data class TokensState( val currencies: List = 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.filter(supportedBlockchains: Set?): List { 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, } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt index fd920555a0..d735ef9615 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/wallet/MultiWalletView.kt @@ -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( diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index c39054104a..940a09f956 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -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