From ec07825276a48d867e9c2118bdf874645ae0c98f Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 27 Jul 2023 11:44:04 +0300 Subject: [PATCH 01/52] Updated on 2026-08-14 --- .../com/tangem/utils/converter/Converter.kt | 12 +- .../tangem/utils/converter/TwoWayConverter.kt | 6 +- data/tokens/build.gradle.kts | 1 - .../tangem/data/tokens/di/TokensDataModule.kt | 19 ++- .../tangem/data/tokens/mock/MockNetworks.kt | 58 -------- .../com/tangem/data/tokens/mock/MockQuotes.kt | 70 --------- .../com/tangem/data/tokens/mock/MockTokens.kt | 133 ------------------ .../repository/DefaultNetworksRepository.kt | 106 ++++++++++++++ .../repository/MockNetworksRepository.kt | 31 ---- .../tokens/repository/MockQuotesRepository.kt | 14 +- .../data/tokens/utils/NetworkConverter.kt | 31 ++++ .../data/tokens/utils/NetworkStatusFactory.kt | 57 ++++++++ .../data/tokens/utils/TokensOperations.kt | 2 +- .../tokens/utils/UserTokensResponseFactory.kt | 2 +- .../DefaultWalletManagersFacade.kt | 11 +- .../model/CryptoCurrencyAmount.kt | 1 + .../utils/UpdateWalletManagerResultFactory.kt | 15 +- .../CurrenciesStatusesOperations.kt | 52 +++---- .../tokens/repository/NetworksRepository.kt | 5 +- .../tokens/repository/QuotesRepository.kt | 4 +- .../repository/MockNetworksRepository.kt | 3 +- .../tokens/repository/MockQuotesRepository.kt | 2 +- 22 files changed, 273 insertions(+), 362 deletions(-) delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt diff --git a/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt b/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt index 6af667ae08..b818478a45 100644 --- a/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt +++ b/core/utils/src/main/java/com/tangem/utils/converter/Converter.kt @@ -1,8 +1,14 @@ package com.tangem.utils.converter -interface Converter { +interface Converter { + fun convert(value: I): O - fun convertList(input: List): List { - return input.map { convert(it) } + + fun convertList(input: Collection): List { + return input.map(::convert) + } + + fun convertSet(input: Collection): Set { + return input.mapTo(hashSetOf(), ::convert) } } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt b/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt index 444e4048ab..50da29ecd0 100644 --- a/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt +++ b/core/utils/src/main/java/com/tangem/utils/converter/TwoWayConverter.kt @@ -1,8 +1,10 @@ package com.tangem.utils.converter -interface TwoWayConverter : Converter { +interface TwoWayConverter : Converter { + fun convertBack(value: O): I - fun convertListBack(input: List): List { + + fun convertListBack(input: Collection): List { return input.map { convertBack(it) } } } \ No newline at end of file diff --git a/data/tokens/build.gradle.kts b/data/tokens/build.gradle.kts index 5b3c4c0e7a..c908cd02dd 100644 --- a/data/tokens/build.gradle.kts +++ b/data/tokens/build.gradle.kts @@ -25,7 +25,6 @@ dependencies { /** Project - Utils */ implementation(projects.core.utils) - // FIXME: For blockchain extensions, remove after refactoring implementation(projects.domain.legacy) /** Tangem SDKs */ diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index 2b512574c4..25f7f9defd 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -1,8 +1,8 @@ package com.tangem.data.tokens.di import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.repository.DefaultNetworksRepository import com.tangem.data.tokens.repository.DefaultTokensRepository -import com.tangem.data.tokens.repository.MockNetworksRepository import com.tangem.data.tokens.repository.MockQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.token.UserTokensStore @@ -10,6 +10,7 @@ import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.tokens.repository.TokensRepository +import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -39,5 +40,19 @@ internal object TokensDataModule { @Provides @Singleton - fun provideNetworksRepository(): NetworksRepository = MockNetworksRepository() + fun provideNetworksRepository( + walletManagersFacade: WalletManagersFacade, + userWalletsStore: UserWalletsStore, + userTokensStore: UserTokensStore, + cacheRegistry: CacheRegistry, + dispatchers: CoroutineDispatcherProvider, + ): NetworksRepository { + return DefaultNetworksRepository( + walletManagersFacade, + userWalletsStore, + userTokensStore, + cacheRegistry, + dispatchers, + ) + } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt deleted file mode 100644 index 8278cce392..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockNetworks.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.tangem.data.tokens.mock - -import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.models.Network -import java.math.BigDecimal - -@Suppress("MemberVisibilityCanBePrivate") -internal object MockNetworks { - - val network1 = Network( - id = Network.ID("network1"), - name = "Network One", - ) - - val network2 = Network( - id = Network.ID("network2"), - name = "Network Two", - ) - - val network3 = Network( - id = Network.ID("network3"), - name = "Network Three", - ) - - val networks = setOf(network1, network2, network3) - - val networkStatus1 = NetworkStatus( - networkId = network1.id, - value = NetworkStatus.Verified( - amounts = mapOf( - MockTokens.token1.id to BigDecimal("123.1234556789"), - MockTokens.token2.id to BigDecimal("42.2"), - MockTokens.token3.id to BigDecimal("1000000000.5"), - ), - hasTransactionsInProgress = false, - ), - ) - - val networkStatus2 = NetworkStatus( - networkId = network2.id, - value = NetworkStatus.MissedDerivation, - ) - - val networkStatus3 = NetworkStatus( - networkId = network3.id, - value = NetworkStatus.Verified( - amounts = mapOf( - MockTokens.token7.id to BigDecimal.ZERO, - MockTokens.token8.id to BigDecimal.TEN, - MockTokens.token9.id to BigDecimal.TEN, - MockTokens.token10.id to BigDecimal.TEN, - ), - hasTransactionsInProgress = false, - ), - ) - - val networksStatuses = setOf(networkStatus1, networkStatus2, networkStatus3) -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt deleted file mode 100644 index f89c208a52..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockQuotes.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.tangem.data.tokens.mock - -import com.tangem.domain.tokens.model.Quote -import java.math.BigDecimal - -@Suppress("MemberVisibilityCanBePrivate") -internal object MockQuotes { - - val quote1 = Quote( - currencyId = MockTokens.token1.id, - fiatRate = BigDecimal("1.23"), - priceChange = BigDecimal("0.01"), - ) - - val quote2 = Quote( - currencyId = MockTokens.token2.id, - fiatRate = BigDecimal("2.34"), - priceChange = BigDecimal("-0.02"), - ) - - val quote3 = Quote( - currencyId = MockTokens.token3.id, - fiatRate = BigDecimal("3.45"), - priceChange = BigDecimal("0.03"), - ) - - val quote4 = Quote( - currencyId = MockTokens.token4.id, - fiatRate = BigDecimal("4.56"), - priceChange = BigDecimal("-0.04"), - ) - - val quote5 = Quote( - currencyId = MockTokens.token5.id, - fiatRate = BigDecimal("5.67"), - priceChange = BigDecimal("0.05"), - ) - - val quote6 = Quote( - currencyId = MockTokens.token6.id, - fiatRate = BigDecimal("6.78"), - priceChange = BigDecimal("-0.06"), - ) - - val quote7 = Quote( - currencyId = MockTokens.token7.id, - fiatRate = BigDecimal("7.89"), - priceChange = BigDecimal("0.07"), - ) - - val quote8 = Quote( - currencyId = MockTokens.token8.id, - fiatRate = BigDecimal("8.90"), - priceChange = BigDecimal("-0.08"), - ) - - val quote9 = Quote( - currencyId = MockTokens.token9.id, - fiatRate = BigDecimal("9.01"), - priceChange = BigDecimal("0.09"), - ) - - val quote10 = Quote( - currencyId = MockTokens.token10.id, - fiatRate = BigDecimal("10.12"), - priceChange = BigDecimal("-0.10"), - ) - - val quotes = setOf(quote1, quote2, quote3, quote4, quote5, quote6, quote7, quote8, quote9, quote10) -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt deleted file mode 100644 index e18e4c126d..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/mock/MockTokens.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.tangem.data.tokens.mock - -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.wallets.models.UserWalletId - -internal object MockTokens { - - val token1 - get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token1"), - networkId = MockNetworks.network1.id, - name = "Token 1", - symbol = "T1", - decimals = 8, - iconUrl = null, - derivationPath = null, - ) - val token2 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token2"), - networkId = MockNetworks.network1.id, - name = "Token 2", - symbol = "T2", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token3 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token3"), - networkId = MockNetworks.network1.id, - name = "Token 3", - symbol = "T3", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token4 - get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token4"), - networkId = MockNetworks.network2.id, - name = "Token 4", - symbol = "T4", - decimals = 8, - iconUrl = null, - derivationPath = null, - ) - val token5 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token5"), - networkId = MockNetworks.network2.id, - name = "Token 5", - symbol = "T5", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token6 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token6"), - networkId = MockNetworks.network2.id, - name = "Token 6", - symbol = "T6", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token7 - get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token7"), - networkId = MockNetworks.network3.id, - name = "Token 7", - symbol = "T7", - decimals = 8, - iconUrl = null, - derivationPath = null, - ) - val token8 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token8"), - networkId = MockNetworks.network3.id, - name = "Token 8", - symbol = "T8", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token9 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token9"), - networkId = MockNetworks.network3.id, - name = "Token 9", - symbol = "T9", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - val token10 - get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token10"), - networkId = MockNetworks.network3.id, - name = "Token 10", - symbol = "T10", - isCustom = false, - decimals = 8, - iconUrl = null, - contractAddress = "address", - derivationPath = null, - ) - - val tokens - get() = mapOf( - UserWalletId(stringValue = "123") to setOf( - token1, token2, token3, token4, token5, - token6, token7, token8, token9, token10, - ), - UserWalletId(stringValue = "321") to setOf(token1, token2, token3), - UserWalletId(stringValue = "42") to setOf(token7, token8, token9, token10), - UserWalletId(stringValue = "24") to setOf(token4, token5, token6), - ) -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt new file mode 100644 index 0000000000..dbde700089 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -0,0 +1,106 @@ +package com.tangem.data.tokens.repository + +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.utils.NetworkConverter +import com.tangem.data.tokens.utils.NetworkStatusFactory +import com.tangem.data.tokens.utils.ResponseCurrenciesFactory +import com.tangem.datasource.local.token.UserTokensStore +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.demo.DemoConfig +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.extensions.addOrReplace +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch + +internal class DefaultNetworksRepository( + private val walletManagersFacade: WalletManagersFacade, + private val userWalletsStore: UserWalletsStore, + private val userTokensStore: UserTokensStore, + private val cacheRegistry: CacheRegistry, + private val dispatchers: CoroutineDispatcherProvider, +) : NetworksRepository { + + private val networkConverter by lazy { NetworkConverter() } + private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(DemoConfig()) } + private val networkStatusFactory by lazy { NetworkStatusFactory() } + + private val networksStatuses: MutableStateFlow> = MutableStateFlow(hashSetOf()) + + override fun getNetworks(networksIds: Set): Set { + return networkConverter.convertSet(networksIds) + } + + override fun getNetworkStatuses( + userWalletId: UserWalletId, + networks: Set, + refresh: Boolean, + ): Flow> = channelFlow { + networksStatuses.collectLatest(::send) + + launch(dispatchers.io) { + fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh) + } + } + + private suspend fun fetchNetworksStatusesIfCacheExpired( + userWalletId: UserWalletId, + networks: Set, + refresh: Boolean, + ) { + cacheRegistry.invokeOnExpire( + key = getNetworksStatusesCacheKey(userWalletId), + skipCache = refresh, + block = { fetchNetworksStatuses(userWalletId, networks) }, + ) + } + + private suspend fun fetchNetworksStatuses(userWalletId: UserWalletId, networks: Set) { + coroutineScope { + networks + .map { networkId -> + async { + fetchNetworkStatus(userWalletId, networkId) + } + } + .awaitAll() + } + } + + private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, networkId: Network.ID) { + val currencies = getCurrencies(userWalletId) + val result = walletManagersFacade.update( + userWalletId = userWalletId, + networkId = networkId, + extraTokens = currencies.filterIsInstanceTo(hashSetOf()), + ) + val networkStatus = networkStatusFactory.createNetworkStatus(networkId, result, currencies) + + networksStatuses.update { statuses -> + statuses.apply { + addOrReplace(networkStatus) { it.networkId == networkStatus.networkId } + } + } + } + + private suspend fun getCurrencies(userWalletId: UserWalletId): Set { + val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "Unable to find user wallet with provided ID: $userWalletId" + } + val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { + "Unable to find tokens response for user wallet with provided ID: $userWalletId" + } + + return responseCurrenciesFactory.createTokens(response, userWallet.scanResponse.card) + } + + private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId): String = "network_status_$userWalletId" +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt deleted file mode 100644 index 3455d66764..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockNetworksRepository.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.data.tokens.repository - -import com.tangem.data.tokens.mock.MockNetworks -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.models.Network -import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.wallets.models.UserWalletId -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -internal class MockNetworksRepository : NetworksRepository { - - override fun getNetworks(networksIds: Set): Set { - return MockNetworks.networks - .filter { it.id in networksIds } - .toSet() - } - - override fun getNetworkStatuses( - userWalletId: UserWalletId, - networks: Map>, - refresh: Boolean, - ): Flow> { - return flowOf( - MockNetworks.networksStatuses - .filter { it.networkId in networks.keys } - .toSet(), - ) - } -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt index 345f17e6bc..841ce3207f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt @@ -1,19 +1,23 @@ package com.tangem.data.tokens.repository -import com.tangem.data.tokens.mock.MockQuotes import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.repository.QuotesRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +import java.math.BigDecimal internal class MockQuotesRepository : QuotesRepository { - override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { return flowOf( - MockQuotes.quotes - .filter { it.currencyId in tokensIds } - .toSet(), + currenciesIds.map { + Quote( + currencyId = it, + fiatRate = BigDecimal.ZERO, + priceChange = BigDecimal.ZERO, + ) + }.toSet(), ) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt new file mode 100644 index 0000000000..23e27fe466 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkConverter.kt @@ -0,0 +1,31 @@ +package com.tangem.data.tokens.utils + +import com.tangem.blockchain.common.Blockchain +import com.tangem.domain.tokens.models.Network +import com.tangem.utils.converter.Converter +import timber.log.Timber + +internal class NetworkConverter : Converter { + + override fun convert(value: Network.ID): Network? { + val blockchain = Blockchain.fromId(value.value) + + if (blockchain == Blockchain.Unknown) { + Timber.e("Unable to convert Unknown blockchain to the domain network model") + return null + } + + return Network( + id = value, + name = blockchain.fullName, + ) + } + + override fun convertList(input: Collection): List { + return input.mapNotNull(::convert) + } + + override fun convertSet(input: Collection): Set { + return input.mapNotNullTo(hashSetOf(), ::convert) + } +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt new file mode 100644 index 0000000000..d27da42e10 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -0,0 +1,57 @@ +package com.tangem.data.tokens.utils + +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount +import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult +import timber.log.Timber +import java.math.BigDecimal + +internal class NetworkStatusFactory { + + fun createNetworkStatus( + networkId: Network.ID, + result: UpdateWalletManagerResult, + currencies: Set, + ): NetworkStatus { + return NetworkStatus( + networkId = networkId, + value = when (result) { + is UpdateWalletManagerResult.MissedDerivation -> NetworkStatus.MissedDerivation + is UpdateWalletManagerResult.Unreachable -> NetworkStatus.Unreachable + is UpdateWalletManagerResult.NoAccount -> NetworkStatus.NoAccount(result.amountToCreateAccount) + is UpdateWalletManagerResult.Verified -> NetworkStatus.Verified( + amounts = formatAmounts(result.tokensAmounts, currencies), + hasTransactionsInProgress = result.hasTransactionsInProgress, + ) + }, + ) + } + + private fun formatAmounts( + amounts: Set, + currencies: Set, + ): Map { + val formattedAmounts = hashMapOf() + + currencies.forEach { currency -> + val amount = when (currency) { + is CryptoCurrency.Coin -> amounts.singleOrNull { it is CryptoCurrencyAmount.Coin } + is CryptoCurrency.Token -> amounts.singleOrNull { + it is CryptoCurrencyAmount.Token && + it.id == getTokenIdString(currency) && + it.tokenContractAddress == currency.contractAddress + } + }?.value + + if (amount == null) { + Timber.e("Unable to find a token amount for: ${currency.name}") + } else { + formattedAmounts[currency.id] = amount + } + } + + return formattedAmounts + } +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index 2d0b915285..1ff2268f53 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -49,7 +49,7 @@ internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency return getTokenOrCoinId(blockchain, token) } -internal fun getResponseTokenId(currency: CryptoCurrency): String? { +internal fun getTokenIdString(currency: CryptoCurrency): String? { return currency.id.value.substringAfter(TOKEN_ID_DELIMITER) .takeUnless { currency is CryptoCurrency.Token && currency.isCustom } } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index ffb4fc9c10..898c62c8b3 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -30,7 +30,7 @@ internal class UserTokensResponseFactory { val blockchain = getBlockchain(currency.networkId) return UserTokensResponse.Token( - id = getResponseTokenId(currency), + id = getTokenIdString(currency), networkId = blockchain.toNetworkId(), derivationPath = currency.derivationPath, name = currency.name, diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 09bc5ac1dd..1b942ab4ce 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -86,7 +86,7 @@ class DefaultWalletManagersFacade( return try { if (demoConfig.isDemoCardId(userWallet.scanResponse.card.cardId)) { - updateDemoWalletManager(walletManager, extraTokens) + updateDemoWalletManager(walletManager) } else { updateWalletManager(walletManager) } @@ -95,14 +95,11 @@ class DefaultWalletManagersFacade( } } - private fun updateDemoWalletManager( - walletManager: WalletManager, - tokens: Set, - ): UpdateWalletManagerResult { + private fun updateDemoWalletManager(walletManager: WalletManager): UpdateWalletManagerResult { val amount = demoConfig.getBalance(walletManager.wallet.blockchain) walletManager.wallet.setAmount(amount) - return resultFactory.getDemoResult(amount, tokens) + return resultFactory.getDemoResult(walletManager, amount) } private suspend fun updateWalletManager(walletManager: WalletManager): UpdateWalletManagerResult { @@ -149,7 +146,7 @@ class DefaultWalletManagersFacade( if (tokens.isEmpty()) return val tokensToAdd = sdkTokenConverter - .convertList(tokens.toList()) + .convertList(tokens) .filter { it !in walletManager.cardTokens } walletManager.addTokens(tokensToAdd) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt index 192be587e5..ae72f37ec4 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/model/CryptoCurrencyAmount.kt @@ -9,6 +9,7 @@ sealed class CryptoCurrencyAmount { data class Coin(override val value: BigDecimal) : CryptoCurrencyAmount() data class Token( + val id: String?, val tokenContractAddress: String, override val value: BigDecimal, ) : CryptoCurrencyAmount() diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt index bdf864f4fe..df5737de85 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/UpdateWalletManagerResultFactory.kt @@ -1,11 +1,7 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.TransactionStatus -import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.* import com.tangem.domain.common.extensions.amountToCreateAccount -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult import timber.log.Timber @@ -26,9 +22,9 @@ internal class UpdateWalletManagerResultFactory { ) } - fun getDemoResult(demoAmount: Amount, tokens: Set): UpdateWalletManagerResult.Verified { + fun getDemoResult(walletManager: WalletManager, demoAmount: Amount): UpdateWalletManagerResult.Verified { return UpdateWalletManagerResult.Verified( - tokensAmounts = getDemoTokensAmounts(demoAmount, tokens), + tokensAmounts = getDemoTokensAmounts(demoAmount, walletManager.cardTokens), hasTransactionsInProgress = false, ) } @@ -53,18 +49,19 @@ internal class UpdateWalletManagerResultFactory { return amounts.mapNotNullTo(mutableAmounts, ::getTokenAmount) } - private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set): Set { + private fun getDemoTokensAmounts(demoAmount: Amount, tokens: Set): Set { val amountValue = demoAmount.value ?: BigDecimal.ZERO val demoAmounts = hashSetOf(CryptoCurrencyAmount.Coin(amountValue)) return tokens.mapTo(demoAmounts) { token -> - CryptoCurrencyAmount.Token(token.contractAddress, amountValue) + CryptoCurrencyAmount.Token(token.id, token.contractAddress, amountValue) } } private fun getTokenAmount(amount: Amount): CryptoCurrencyAmount? { return when (val type = amount.type) { is AmountType.Token -> CryptoCurrencyAmount.Token( + id = type.token.id, tokenContractAddress = type.token.contractAddress, value = getAmountValue(amount) ?: return null, ) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 6ce5fc8b60..7b49295f4a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -46,36 +46,43 @@ internal class CurrenciesStatusesOperations( fun getMultiCurrencyWalletStatusesFlow(): Flow> { return getMultiCurrencyWalletCurrencies().flatMapConcat { - val tokens = it.toNonEmptySetOrNull() + val currencies = it.toNonEmptySetOrNull() - if (tokens == null) { + if (currencies == null) { flowOf(emptySet()) } else { - val tokensIds = tokens.map { token -> token.id }.toNonEmptySet() - val groupedTokens = groupTokens(tokens) + val currencyIdToNetworkId = currencies.associate { currency -> + currency.id to currency.networkId + } + val currenciesIds = requireNotNull(currencyIdToNetworkId.keys.toNonEmptySetOrNull()) { + "Currencies IDs cannot be empty" + } + val networksIds = requireNotNull(currencyIdToNetworkId.values.toNonEmptySetOrNull()) { + "Networks IDs cannot be empty" + } - combine(getQuotes(tokensIds), getNetworksStatues(groupedTokens)) { quotes, networksStatuses -> - createTokensStatuses(tokens, quotes, networksStatuses) + combine(getQuotes(currenciesIds), getNetworksStatues(networksIds)) { quotes, networksStatuses -> + createTokensStatuses(currencies, quotes, networksStatuses) } } } } suspend fun getPrimaryCurrencyStatusFlow(): Flow { - val token = getPrimaryCurrency() + val currency = getPrimaryCurrency() - val quoteFlow = getQuotes(nonEmptySetOf(token.id)) + val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) .map { quotes -> - quotes.singleOrNull { it.currencyId == token.id } + quotes.singleOrNull { it.currencyId == currency.id } } - val statusFlow = getNetworksStatues(groupTokens(nonEmptySetOf(token))) + val statusFlow = getNetworksStatues(nonEmptySetOf(currency.networkId)) .map { statuses -> - statuses.singleOrNull { it.networkId == token.networkId } + statuses.singleOrNull { it.networkId == currency.networkId } } return combine(quoteFlow, statusFlow) { quote, networkStatus -> - createStatus(token, quote, networkStatus) + createStatus(currency, quote, networkStatus) } } @@ -132,30 +139,13 @@ internal class CurrenciesStatusesOperations( .flowOn(dispatchers.io) } - private fun getNetworksStatues( - groupedTokens: Map>, - ): Flow> { - return networksRepository.getNetworkStatuses(userWalletId, groupedTokens, refresh) + private fun getNetworksStatues(networks: NonEmptySet): Flow> { + return networksRepository.getNetworkStatuses(userWalletId, networks, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyNetworksStatuses) } .flowOn(dispatchers.io) } - private suspend fun groupTokens( - tokens: NonEmptySet, - ): Map> { - return withContext(dispatchers.default) { - tokens - .groupBy { it.networkId } - .mapValues { (_, tokens) -> - // Can not be empty - tokens.toNonEmptySetOrNull()!! - .map { it.id } - .toNonEmptySet() - } - } - } - sealed class Error { object EmptyCurrencies : Error() diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt index 5acf293b11..6dcec4843b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/NetworksRepository.kt @@ -1,6 +1,5 @@ package com.tangem.domain.tokens.repository -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.models.Network import com.tangem.domain.wallets.models.UserWalletId @@ -23,13 +22,13 @@ interface NetworksRepository { * Retrieves the statuses of specified blockchain networks for a specific user wallet. * * @param userWalletId The unique identifier of the user wallet. - * @param networks A map of network IDs to sets of cryptocurrency IDs, representing the networks for which statuses are to be retrieved. + * @param networks A set of network IDs which statuses are to be retrieved. * @param refresh A boolean flag indicating whether the data should be refreshed. * @return A [Flow] emitting a set of [NetworkStatus] objects corresponding to the specified networks. */ fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Set, refresh: Boolean, ): Flow> } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt index 1c783ce342..c56cb3d7d9 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt @@ -12,9 +12,9 @@ interface QuotesRepository { /** * Retrieves the quotes for a set of specified cryptocurrencies, identified by their unique IDs. * - * @param tokensIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved. + * @param currenciesIds The unique identifiers of the cryptocurrencies for which quotes are to be retrieved. * @param refresh A boolean flag indicating whether the data should be refreshed. * @return A [Flow] emitting a set of quotes corresponding to the specified cryptocurrencies. */ - fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> + fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt index ced27621b7..5ac5c7c8fa 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockNetworksRepository.kt @@ -3,7 +3,6 @@ package com.tangem.domain.tokens.repository import arrow.core.Either import arrow.core.getOrElse import com.tangem.domain.core.error.DataError -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.models.Network import com.tangem.domain.wallets.models.UserWalletId @@ -21,7 +20,7 @@ internal class MockNetworksRepository( override fun getNetworkStatuses( userWalletId: UserWalletId, - networks: Map>, + networks: Set, refresh: Boolean, ): Flow> { return statuses.map { it.getOrElse { e -> throw e } } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt index 9ec660b113..a94b8a72df 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt @@ -12,7 +12,7 @@ internal class MockQuotesRepository( private val quotes: Flow>>, ) : QuotesRepository { - override fun getQuotes(tokensIds: Set, refresh: Boolean): Flow> { + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { return quotes.map { it.getOrElse { e -> throw e } } } } \ No newline at end of file From 4a058736ee11cd4977d4357fd03e47e77b8d62c9 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 7 Aug 2023 12:17:55 +0800 Subject: [PATCH 02/52] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt | 2 +- .../implementation/BiometricUserWalletsListManager.kt | 2 +- .../implementation/RuntimeUserWalletsListManager.kt | 2 +- .../implementation/BiometricUserWalletsKeysRepository.kt | 2 +- .../tangem/tap/features/details/redux/DetailsMiddleware.kt | 2 +- .../onboarding/products/twins/redux/TwinCardsMiddleware.kt | 4 ++-- .../tap/features/saveWallet/redux/SaveWalletMiddleware.kt | 2 +- .../tap/features/wallet/redux/middlewares/WalletMiddleware.kt | 2 +- .../features/walletSelector/redux/WalletSelectorMiddleware.kt | 2 +- .../tap/features/walletSelector/ui/WalletSelectorViewModel.kt | 4 ++-- .../tangem/tap/features/welcome/redux/WelcomeMiddleware.kt | 2 +- .../com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt | 2 +- domain/wallets/build.gradle.kts | 4 ++++ .../com/tangem/domain/wallets/legacy}/UserWalletsListError.kt | 4 ++-- .../wallets/legacy}/UserWalletsListManagerExtensions.kt | 3 +-- .../java/com/tangem/domain/wallets/models/SaveWalletError.kt | 1 - 16 files changed, 21 insertions(+), 19 deletions(-) rename {app/src/main/java/com/tangem/tap/domain/userWalletList => domain/wallets/src/main/java/com/tangem/domain/wallets/legacy}/UserWalletsListError.kt (94%) rename {app/src/main/java/com/tangem/tap/domain/userWalletList => domain/wallets/src/main/java/com/tangem/domain/wallets/legacy}/UserWalletsListManagerExtensions.kt (95%) diff --git a/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt b/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt index 569a62b33a..256bd62a5f 100644 --- a/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt +++ b/app/src/main/java/com/tangem/tap/LockUserWalletsTimer.kt @@ -5,8 +5,8 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction +import com.tangem.domain.wallets.legacy.asLockable import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.domain.userWalletList.asLockable import kotlinx.coroutines.* import timber.log.Timber import kotlin.time.Duration diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt index f28f3b06ee..76a94f34bd 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/BiometricUserWalletsListManager.kt @@ -2,10 +2,10 @@ package com.tangem.tap.domain.userWalletList.implementation import com.tangem.common.* import com.tangem.common.extensions.guard +import com.tangem.domain.wallets.legacy.UserWalletsListError import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.userWalletList.UserWalletsListError import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey import com.tangem.tap.domain.userWalletList.repository.SelectedUserWalletRepository import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt index d1cd26b6ff..eedc68771b 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/implementation/RuntimeUserWalletsListManager.kt @@ -2,10 +2,10 @@ package com.tangem.tap.domain.userWalletList.implementation import com.tangem.common.CompletionResult import com.tangem.common.catching +import com.tangem.domain.wallets.legacy.UserWalletsListError import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.userWalletList.UserWalletsListError import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt index b8c9ce2bc5..0d4dd5d1c5 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/BiometricUserWalletsKeysRepository.kt @@ -8,8 +8,8 @@ import com.tangem.common.biometric.BiometricManager import com.tangem.common.biometric.BiometricStorage import com.tangem.common.core.TangemSdkError import com.tangem.common.services.secure.SecureStorage +import com.tangem.domain.wallets.legacy.UserWalletsListError import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.tap.domain.userWalletList.UserWalletsListError import com.tangem.tap.domain.userWalletList.model.UserWalletEncryptionKey import com.tangem.tap.domain.userWalletList.repository.UserWalletsKeysRepository import kotlinx.coroutines.Dispatchers diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt index 478d0cb1bd..5778b35562 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/DetailsMiddleware.kt @@ -15,6 +15,7 @@ import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.domain.wallets.legacy.isLockedSync import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Settings @@ -27,7 +28,6 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation import com.tangem.tap.domain.userWalletList.di.provideRuntimeImplementation -import com.tangem.tap.domain.userWalletList.isLockedSync import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt index 023e3137c4..f68fa7e806 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/redux/TwinCardsMiddleware.kt @@ -11,6 +11,7 @@ import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.UserWalletIdBuilder +import com.tangem.domain.wallets.legacy.isLockedSync import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Onboarding @@ -21,7 +22,6 @@ import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.twins.TwinCardsManager -import com.tangem.tap.domain.userWalletList.isLockedSync import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE import com.tangem.tap.features.onboarding.OnboardingDialog import com.tangem.tap.features.onboarding.OnboardingHelper @@ -50,7 +50,7 @@ private val twinsWalletMiddleware: Middleware = { dispatch, state -> @Suppress("LongMethod", "ComplexMethod", "MagicNumber") private fun handle(action: Action, dispatch: DispatchFunction) { - val action = action as? TwinCardsAction ?: return + if (action !is TwinCardsAction) return val globalState = store.state.globalState val onboardingManager = globalState.onboardingState.onboardingManager diff --git a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt index 8261f5bef3..5305d35dfb 100644 --- a/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/saveWallet/redux/SaveWalletMiddleware.kt @@ -11,6 +11,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.legacy.UserWalletsListManager +import com.tangem.domain.wallets.legacy.isLockable import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.MainScreen @@ -20,7 +21,6 @@ import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.userWalletList.di.provideBiometricImplementation -import com.tangem.tap.domain.userWalletList.isLockable import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.proxy.redux.DaggerGraphState import kotlinx.coroutines.launch diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index a96e7e6197..e7d5ff135d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -13,6 +13,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.userwallets.GetCardImageUseCase +import com.tangem.domain.wallets.legacy.lockIfLockable import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic @@ -24,7 +25,6 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.userWalletList.lockIfLockable import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.send.redux.PrepareSendScreen diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt index acac8cd6a8..3ee7ad011b 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/redux/WalletSelectorMiddleware.kt @@ -8,6 +8,7 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.userwallets.UserWalletIdBuilder +import com.tangem.domain.wallets.legacy.unlockIfLockable import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.tap.* @@ -20,7 +21,6 @@ import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.model.TotalFiatBalance import com.tangem.tap.domain.model.WalletStoreModel -import com.tangem.tap.domain.userWalletList.unlockIfLockable import com.tangem.tap.proxy.redux.DaggerGraphState import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt index 6fb9347118..1915bd51df 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/WalletSelectorViewModel.kt @@ -4,11 +4,11 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.common.core.TangemError import com.tangem.core.analytics.Analytics +import com.tangem.domain.wallets.legacy.UserWalletsListError +import com.tangem.domain.wallets.legacy.isLocked import com.tangem.domain.wallets.models.UserWalletId import com.tangem.tap.common.analytics.events.MyWallets import com.tangem.tap.common.extensions.dispatchOnMain -import com.tangem.tap.domain.userWalletList.UserWalletsListError -import com.tangem.tap.domain.userWalletList.isLocked import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction import com.tangem.tap.features.walletSelector.redux.WalletSelectorState diff --git a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt index f18499bf50..ee103b5965 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/redux/WelcomeMiddleware.kt @@ -9,6 +9,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.UserWalletBuilder +import com.tangem.domain.wallets.legacy.unlockIfLockable import com.tangem.tap.* import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Basic @@ -16,7 +17,6 @@ import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.onUserWalletSelected import com.tangem.tap.common.redux.AppState -import com.tangem.tap.domain.userWalletList.unlockIfLockable import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler import com.tangem.tap.features.signin.redux.SignInAction import com.tangem.tap.proxy.redux.DaggerGraphState diff --git a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt index fd40c5038b..3731da6378 100644 --- a/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/welcome/ui/WelcomeViewModel.kt @@ -3,9 +3,9 @@ package com.tangem.tap.features.welcome.ui import androidx.lifecycle.ViewModel import com.tangem.common.core.TangemError import com.tangem.core.analytics.Analytics +import com.tangem.domain.wallets.legacy.UserWalletsListError import com.tangem.tap.common.analytics.events.SignIn import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.userWalletList.UserWalletsListError import com.tangem.tap.features.details.ui.cardsettings.TextReference import com.tangem.tap.features.welcome.redux.WelcomeAction import com.tangem.tap.features.welcome.redux.WelcomeState diff --git a/domain/wallets/build.gradle.kts b/domain/wallets/build.gradle.kts index 3216145e52..474b10e64e 100644 --- a/domain/wallets/build.gradle.kts +++ b/domain/wallets/build.gradle.kts @@ -10,6 +10,10 @@ android { dependencies { + // region Core modules + implementation(projects.core.res) + // endregion + // region Domain modules implementation(projects.domain.legacy) implementation(projects.domain.models) diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListError.kt similarity index 94% rename from app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListError.kt index ec9ed95b1d..0bb0ef990b 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListError.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListError.kt @@ -1,7 +1,7 @@ -package com.tangem.tap.domain.userWalletList +package com.tangem.domain.wallets.legacy import com.tangem.common.core.TangemError -import com.tangem.wallet.R +import com.tangem.domain.wallets.R sealed class UserWalletsListError(code: Int) : TangemError(code) { diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManagerExtensions.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt similarity index 95% rename from app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManagerExtensions.kt rename to domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt index 8086bded7c..aa65450d99 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/UserWalletsListManagerExtensions.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/legacy/UserWalletsListManagerExtensions.kt @@ -1,7 +1,6 @@ -package com.tangem.tap.domain.userWalletList +package com.tangem.domain.wallets.legacy import com.tangem.common.CompletionResult -import com.tangem.domain.wallets.legacy.UserWalletsListManager import com.tangem.domain.wallets.models.UserWallet import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SaveWalletError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SaveWalletError.kt index 97ff31ef11..f292875ed2 100644 --- a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SaveWalletError.kt +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SaveWalletError.kt @@ -5,6 +5,5 @@ package com.tangem.domain.wallets.models */ sealed interface SaveWalletError { - // TODO: Finalize in next PRs object CommonError : SaveWalletError } \ No newline at end of file From dc0b2a7e70f950ccf413d31c52f9270e0fa1b5d6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 7 Aug 2023 18:36:05 +0800 Subject: [PATCH 03/52] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 7 +++++ .../wallets/models/UnlockWalletError.kt | 6 ++++ .../wallets/usecase/UnlockWalletsUseCase.kt | 31 +++++++++++++++++++ 3 files changed, 44 insertions(+) create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/models/UnlockWalletError.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/UnlockWalletsUseCase.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 9321638b94..6c0ebccfc6 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -5,6 +5,7 @@ import com.tangem.domain.wallets.legacy.WalletsStateHolder import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.SaveWalletUseCase +import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -32,4 +33,10 @@ internal object WalletsDomainModule { fun providesGetExploreUrlUseCase(walletsManagersFacade: WalletManagersFacade): GetExploreUrlUseCase { return GetExploreUrlUseCase(walletsManagersFacade = walletsManagersFacade) } + + @Provides + @ViewModelScoped + fun providesUnlockWalletUseCase(walletsStateHolder: WalletsStateHolder): UnlockWalletsUseCase { + return UnlockWalletsUseCase(walletsStateHolder = walletsStateHolder) + } } \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/UnlockWalletError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/UnlockWalletError.kt new file mode 100644 index 0000000000..311cdc5d0f --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/UnlockWalletError.kt @@ -0,0 +1,6 @@ +package com.tangem.domain.wallets.models + +sealed interface UnlockWalletError { + + object CommonError : UnlockWalletError +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/UnlockWalletsUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/UnlockWalletsUseCase.kt new file mode 100644 index 0000000000..3459902720 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/UnlockWalletsUseCase.kt @@ -0,0 +1,31 @@ +package com.tangem.domain.wallets.usecase + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.common.doOnFailure +import com.tangem.common.doOnSuccess +import com.tangem.domain.wallets.legacy.WalletsStateHolder +import com.tangem.domain.wallets.legacy.asLockable +import com.tangem.domain.wallets.models.UnlockWalletError + +/** + * Unlock wallets use case + * + * @property walletsStateHolder wallets state holder + * +[REDACTED_AUTHOR] + */ +class UnlockWalletsUseCase(private val walletsStateHolder: WalletsStateHolder) { + + suspend operator fun invoke(): Either { + val userWalletsListManager = walletsStateHolder.userWalletsListManager?.asLockable() + ?: return UnlockWalletError.CommonError.left() + + userWalletsListManager.unlock() + .doOnSuccess { return Unit.right() } + .doOnFailure { return UnlockWalletError.CommonError.left() } + + return Unit.right() + } +} \ No newline at end of file From 1943ed248c7ec5983b2a7b88b8acae71fe5a3b3b Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 7 Aug 2023 18:09:33 +0800 Subject: [PATCH 04/52] Updated on 2026-08-14 --- .../presentation/common/WalletPreviewData.kt | 601 ++++++++++-------- .../wallet/state/WalletStateHolder.kt | 20 +- .../factory/WalletSkeletonStateConverter.kt | 4 +- .../state/factory/WalletStateFactory.kt | 4 +- .../WalletLoadedTxHistoryConverter.kt | 2 +- .../WalletLoadingTxHistoryConverter.kt | 2 +- .../presentation/wallet/ui/WalletScreen.kt | 132 +--- .../wallet/ui/components/WalletsList.kt | 29 +- .../{ => common}/WalletBottomSheet.kt | 2 +- .../ui/components/{ => common}/WalletCard.kt | 4 +- .../ui/components/common/WalletContent.kt | 32 + .../components/common/WalletNotifications.kt | 20 + .../common/WalletPullToRefreshIndicator.kt | 26 + .../ui/components/common/WalletSideEffects.kt | 36 ++ .../components/{ => common}/WalletTopBar.kt | 2 +- .../MultiCurrencyOrganizeButton.kt | 16 + .../SingleCurrencyControlButtons.kt | 27 + .../SingleCurrencyMarketPriceBlock.kt | 18 + .../wallet/ui/utils/ScrollOffsetCollector.kt | 10 +- .../TokenListErrorToWalletStateConverter.kt | 2 +- .../utils/TokenListToWalletStateConverter.kt | 2 +- 21 files changed, 573 insertions(+), 418 deletions(-) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/{ => common}/WalletBottomSheet.kt (99%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/{ => common}/WalletCard.kt (99%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletPullToRefreshIndicator.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/{ => common}/WalletTopBar.kt (99%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 722d10e9cb..ba60a1451b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -19,320 +19,357 @@ import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.flowOf import java.util.UUID +@Suppress("LargeClass") internal object WalletPreviewData { - val walletTopBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) + val walletTopBarConfig by lazy { WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) } - val walletCardContentState = WalletCardState.Content( - id = UserWalletId(UUID.randomUUID().toString()), - title = "Wallet 1", - balance = "8923,05 $", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = null, - ) + val walletCardContentState by lazy { + WalletCardState.Content( + id = UserWalletId("123"), + title = "Wallet 1", + balance = "8923,05 $", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = null, + ) + } - val walletCardLoadingState = WalletCardState.Loading( - id = UserWalletId(UUID.randomUUID().toString()), - title = "Wallet 1", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = null, - ) + val walletCardLoadingState by lazy { + WalletCardState.Loading( + id = UserWalletId("321"), + title = "Wallet 1", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = null, + ) + } - val walletCardHiddenContentState = WalletCardState.HiddenContent( - id = UserWalletId(UUID.randomUUID().toString()), - title = "Wallet 1", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = null, - ) + val walletCardHiddenContentState by lazy { + WalletCardState.HiddenContent( + id = UserWalletId("42"), + title = "Wallet 1", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = null, + ) + } - val walletCardErrorState = WalletCardState.Error( - id = UserWalletId(UUID.randomUUID().toString()), - title = "Wallet 1", - additionalInfo = "3 cards • Seed enabled", - imageResId = R.drawable.ill_businessman_3d, - onClick = null, - ) + val walletCardErrorState by lazy { + WalletCardState.Error( + id = UserWalletId("24"), + title = "Wallet 1", + additionalInfo = "3 cards • Seed enabled", + imageResId = R.drawable.ill_businessman_3d, + onClick = null, + ) + } - val wallets = mapOf( - UserWalletId(stringValue = "123") to walletCardContentState, - UserWalletId(stringValue = "321") to walletCardLoadingState, - UserWalletId(stringValue = "42") to walletCardHiddenContentState, - UserWalletId(stringValue = "24") to walletCardErrorState, - ) + val wallets by lazy { + mapOf( + UserWalletId(stringValue = "123") to walletCardContentState, + UserWalletId(stringValue = "321") to walletCardLoadingState, + UserWalletId(stringValue = "42") to walletCardHiddenContentState, + UserWalletId(stringValue = "24") to walletCardErrorState, + ) + } - val walletListConfig = WalletsListConfig( - selectedWalletIndex = 0, - wallets = wallets.values.toPersistentList(), - onWalletChange = {}, - ) + val walletListConfig by lazy { + WalletsListConfig( + selectedWalletIndex = 0, + wallets = wallets.values.toPersistentList(), + onWalletChange = {}, + ) + } - val tokenItemVisibleState = TokenItemState.Content( - id = UUID.randomUUID().toString(), - tokenIconUrl = null, - tokenIconResId = R.drawable.img_polygon_22, - networkIconResId = R.drawable.img_polygon_22, - name = "Polygon", - amount = "5,412 MATIC", - hasPending = true, - tokenOptions = TokenOptionsState.Visible( - fiatAmount = "321 $", - priceChange = PriceChangeConfig( - valueInPercent = "2%", - type = PriceChangeConfig.Type.UP, + val tokenItemVisibleState by lazy { + TokenItemState.Content( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + amount = "5,412 MATIC", + hasPending = true, + tokenOptions = TokenOptionsState.Visible( + fiatAmount = "321 $", + priceChange = PriceChangeConfig( + valueInPercent = "2%", + type = PriceChangeConfig.Type.UP, + ), ), - ), - ) + ) + } - val tokenItemHiddenState = TokenItemState.Content( - id = UUID.randomUUID().toString(), - tokenIconUrl = null, - tokenIconResId = R.drawable.img_polygon_22, - networkIconResId = R.drawable.img_polygon_22, - name = "Polygon", - amount = "5,412 MATIC", - hasPending = true, - tokenOptions = TokenOptionsState.Hidden( - priceChange = PriceChangeConfig( - valueInPercent = "2%", - type = PriceChangeConfig.Type.UP, + val tokenItemHiddenState by lazy { + TokenItemState.Content( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + amount = "5,412 MATIC", + hasPending = true, + tokenOptions = TokenOptionsState.Hidden( + priceChange = PriceChangeConfig( + valueInPercent = "2%", + type = PriceChangeConfig.Type.UP, + ), ), - ), - ) + ) + } - val tokenItemDragState = TokenItemState.Draggable( - id = UUID.randomUUID().toString(), - tokenIconUrl = null, - tokenIconResId = R.drawable.img_polygon_22, - networkIconResId = R.drawable.img_polygon_22, - name = "Polygon", - fiatAmount = "3 172,14 $", - ) + val tokenItemDragState by lazy { + TokenItemState.Draggable( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + fiatAmount = "3 172,14 $", + ) + } - val tokenItemUnreachableState = TokenItemState.Unreachable( - id = UUID.randomUUID().toString(), - tokenIconUrl = null, - tokenIconResId = R.drawable.img_polygon_22, - networkIconResId = R.drawable.img_polygon_22, - name = "Polygon", - ) + val tokenItemUnreachableState by lazy { + TokenItemState.Unreachable( + id = UUID.randomUUID().toString(), + tokenIconUrl = null, + tokenIconResId = R.drawable.img_polygon_22, + networkIconResId = R.drawable.img_polygon_22, + name = "Polygon", + ) + } - val loadingTokenItemState = TokenItemState.Loading + val loadingTokenItemState by lazy { TokenItemState.Loading } private const val networksSize = 10 private const val tokensSize = 3 - val draggableItems = List(networksSize) { it } - .flatMap { index -> - val lastNetworkIndex = networksSize - 1 - val lastTokenIndex = tokensSize - 1 - val networkNumber = index + 1 + val draggableItems by lazy { + List(networksSize) { it } + .flatMap { index -> + val lastNetworkIndex = networksSize - 1 + val lastTokenIndex = tokensSize - 1 + val networkNumber = index + 1 - val group = DraggableItem.GroupHeader( - id = "group_$networkNumber", - networkName = "$networkNumber", - roundingMode = when (index) { - 0 -> DraggableItem.RoundingMode.Top() - lastNetworkIndex -> DraggableItem.RoundingMode.Bottom() - else -> DraggableItem.RoundingMode.None - }, - ) - - val tokens: MutableList = mutableListOf() - repeat(times = tokensSize) { i -> - val tokenNumber = i + 1 - tokens.add( - DraggableItem.Token( - tokenItemState = tokenItemDragState.copy( - id = "${group.id}_token_$tokenNumber", - name = "Token $tokenNumber from $networkNumber network", - networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 }, - ), - groupId = group.id, - roundingMode = when { - i == lastTokenIndex && index == lastNetworkIndex -> DraggableItem.RoundingMode.Bottom() - else -> DraggableItem.RoundingMode.None - }, - ), + val group = DraggableItem.GroupHeader( + id = "group_$networkNumber", + networkName = "$networkNumber", + roundingMode = when (index) { + 0 -> DraggableItem.RoundingMode.Top() + lastNetworkIndex -> DraggableItem.RoundingMode.Bottom() + else -> DraggableItem.RoundingMode.None + }, ) - } - val divider = DraggableItem.GroupPlaceholder(id = "divider_$networkNumber") + val tokens: MutableList = mutableListOf() + repeat(times = tokensSize) { i -> + val tokenNumber = i + 1 + tokens.add( + DraggableItem.Token( + tokenItemState = tokenItemDragState.copy( + id = "${group.id}_token_$tokenNumber", + name = "Token $tokenNumber from $networkNumber network", + networkIconResId = R.drawable.img_eth_22.takeIf { i != 0 }, + ), + groupId = group.id, + roundingMode = when { + i == lastTokenIndex && index == lastNetworkIndex -> DraggableItem.RoundingMode.Bottom() + else -> DraggableItem.RoundingMode.None + }, + ), + ) + } - buildList { - add(group) - addAll(tokens) - if (index != lastNetworkIndex) { - add(divider) + val divider = DraggableItem.GroupPlaceholder(id = "divider_$networkNumber") + + buildList { + add(group) + addAll(tokens) + if (index != lastNetworkIndex) { + add(divider) + } } } - } - .toPersistentList() + .toPersistentList() + } - val draggableTokens = draggableItems - .filterIsInstance() - .toMutableList() - .also { - it[0] = it[0].copy(roundingMode = DraggableItem.RoundingMode.Top()) - } - .toPersistentList() + val draggableTokens by lazy { + draggableItems + .filterIsInstance() + .toMutableList() + .also { + it[0] = it[0].copy(roundingMode = DraggableItem.RoundingMode.Top()) + } + .toPersistentList() + } - val groupedOrganizeTokensState = OrganizeTokensStateHolder( - itemsState = OrganizeTokensListState.GroupedByNetwork( - items = draggableItems, - ), - header = OrganizeTokensStateHolder.HeaderConfig( - onSortByBalanceClick = {}, - onGroupByNetworkClick = {}, - ), - dragConfig = OrganizeTokensStateHolder.DragConfig( - onItemDragged = { _, _ -> }, - onDragStart = {}, - canDragItemOver = { _, _ -> false }, - onItemDragEnd = {}, - ), - actions = OrganizeTokensStateHolder.ActionsConfig( - onApplyClick = {}, - onCancelClick = {}, - ), - ) - - val organizeTokensState = groupedOrganizeTokensState.copy( - itemsState = OrganizeTokensListState.Ungrouped( - items = draggableTokens, - ), - ) - - val bottomSheet = WalletBottomSheetConfig( - isShow = false, - onDismissRequest = {}, - content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( - onUnlockClick = {}, - onScanClick = {}, - ), - ) - - private val manageButtons = persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), - WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), - WalletManageButton.CopyAddress(onClick = {}), - ) - - val multicurrencyWalletScreenState = WalletStateHolder.MultiCurrencyContent( - onBackClick = {}, - topBarConfig = walletTopBarConfig, - walletsListConfig = walletListConfig, - tokensListState = WalletTokensListState.Content( - persistentListOf( - WalletTokensListState.TokensListItemState.NetworkGroupTitle("Bitcoin"), - WalletTokensListState.TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_1", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - amount = "1,89340821 ETH", - ), - ), - WalletTokensListState.TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_2", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - amount = "1,89340821 ETH", - ), - ), - WalletTokensListState.TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_3", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - amount = "1,89340821 ETH", - ), - ), - WalletTokensListState.TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_4", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - amount = "1,89340821 ETH", - ), - ), - WalletTokensListState.TokensListItemState.NetworkGroupTitle("Ethereum"), - WalletTokensListState.TokensListItemState.Token( - tokenItemVisibleState.copy( - id = "token_5", - name = "Ethereum", - tokenIconResId = R.drawable.img_eth_22, - networkIconResId = null, - amount = "1,89340821 ETH", - ), - ), + val groupedOrganizeTokensState by lazy { + OrganizeTokensStateHolder( + itemsState = OrganizeTokensListState.GroupedByNetwork( + items = draggableItems, ), - onOrganizeTokensClick = {}, - ), - pullToRefreshConfig = WalletPullToRefreshConfig( - isRefreshing = false, - onRefresh = {}, - ), - notifications = persistentListOf( - WalletNotification.UnreachableNetworks, - WalletNotification.LikeTangemApp(onClick = {}), - WalletNotification.BackupCard(onClick = {}), - WalletNotification.ScanCard(onClick = {}), - ), - bottomSheet = bottomSheet, - ) - - val singleWalletScreenState = WalletStateHolder.SingleCurrencyContent( - onBackClick = {}, - topBarConfig = walletTopBarConfig, - walletsListConfig = walletListConfig, - pullToRefreshConfig = WalletPullToRefreshConfig( - isRefreshing = false, - onRefresh = {}, - ), - notifications = persistentListOf(WalletNotification.LikeTangemApp(onClick = {})), - buttons = manageButtons.map(WalletManageButton::config).toPersistentList(), - bottomSheet = bottomSheet, - marketPriceBlockState = MarketPriceBlockState.Content( - currencyName = "BTC", - price = "98900.12$", - priceChangeConfig = PriceChangeConfig( - valueInPercent = "5.16%", - type = PriceChangeConfig.Type.UP, + header = OrganizeTokensStateHolder.HeaderConfig( + onSortByBalanceClick = {}, + onGroupByNetworkClick = {}, ), - ), - txHistoryState = WalletTxHistoryState.Content( - flowOf( - PagingData.from( - listOf( - WalletTxHistoryState.TxHistoryItemState.Title(onExploreClick = {}), - WalletTxHistoryState.TxHistoryItemState.GroupTitle("Today"), - WalletTxHistoryState.TxHistoryItemState.Transaction( - TransactionState.Sending( - address = "33BddS...ga2B", - amount = "-0.500913 BTC", - timestamp = "8:41", - ), + dragConfig = OrganizeTokensStateHolder.DragConfig( + onItemDragged = { _, _ -> }, + onDragStart = {}, + canDragItemOver = { _, _ -> false }, + onItemDragEnd = {}, + ), + actions = OrganizeTokensStateHolder.ActionsConfig( + onApplyClick = {}, + onCancelClick = {}, + ), + ) + } + + val organizeTokensState by lazy { + groupedOrganizeTokensState.copy( + itemsState = OrganizeTokensListState.Ungrouped( + items = draggableTokens, + ), + ) + } + + val bottomSheet by lazy { + WalletBottomSheetConfig( + isShow = false, + onDismissRequest = {}, + content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + onUnlockClick = {}, + onScanClick = {}, + ), + ) + } + + private val manageButtons by lazy { + persistentListOf( + WalletManageButton.Buy(onClick = {}), + WalletManageButton.Send(onClick = {}), + WalletManageButton.Receive(onClick = {}), + WalletManageButton.Exchange(onClick = {}), + WalletManageButton.CopyAddress(onClick = {}), + ) + } + + val multicurrencyWalletScreenState by lazy { + WalletStateHolder.MultiCurrencyContent( + onBackClick = {}, + topBarConfig = walletTopBarConfig, + walletsListConfig = walletListConfig, + tokensListState = WalletTokensListState.Content( + persistentListOf( + WalletTokensListState.TokensListItemState.NetworkGroupTitle("Bitcoin"), + WalletTokensListState.TokensListItemState.Token( + tokenItemVisibleState.copy( + id = "token_1", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", ), - WalletTxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"), - WalletTxHistoryState.TxHistoryItemState.Transaction( - TransactionState.Sending( - address = "33BddS...ga2B", - amount = "-0.500913 BTC", - timestamp = "8:41", + ), + WalletTokensListState.TokensListItemState.Token( + tokenItemVisibleState.copy( + id = "token_2", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), + ), + WalletTokensListState.TokensListItemState.Token( + tokenItemVisibleState.copy( + id = "token_3", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), + ), + WalletTokensListState.TokensListItemState.Token( + tokenItemVisibleState.copy( + id = "token_4", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), + ), + WalletTokensListState.TokensListItemState.NetworkGroupTitle("Ethereum"), + WalletTokensListState.TokensListItemState.Token( + tokenItemVisibleState.copy( + id = "token_5", + name = "Ethereum", + tokenIconResId = R.drawable.img_eth_22, + networkIconResId = null, + amount = "1,89340821 ETH", + ), + ), + ), + onOrganizeTokensClick = {}, + ), + pullToRefreshConfig = WalletPullToRefreshConfig( + isRefreshing = false, + onRefresh = {}, + ), + notifications = persistentListOf( + WalletNotification.UnreachableNetworks, + WalletNotification.LikeTangemApp(onClick = {}), + WalletNotification.BackupCard(onClick = {}), + WalletNotification.ScanCard(onClick = {}), + ), + bottomSheetConfig = bottomSheet, + ) + } + + val singleWalletScreenState by lazy { + WalletStateHolder.SingleCurrencyContent( + onBackClick = {}, + topBarConfig = walletTopBarConfig, + walletsListConfig = walletListConfig, + pullToRefreshConfig = WalletPullToRefreshConfig( + isRefreshing = false, + onRefresh = {}, + ), + notifications = persistentListOf(WalletNotification.LikeTangemApp(onClick = {})), + buttons = manageButtons.map(WalletManageButton::config).toPersistentList(), + bottomSheetConfig = bottomSheet, + marketPriceBlockState = MarketPriceBlockState.Content( + currencyName = "BTC", + price = "98900.12$", + priceChangeConfig = PriceChangeConfig( + valueInPercent = "5.16%", + type = PriceChangeConfig.Type.UP, + ), + ), + txHistoryState = WalletTxHistoryState.Content( + flowOf( + PagingData.from( + listOf( + WalletTxHistoryState.TxHistoryItemState.Title(onExploreClick = {}), + WalletTxHistoryState.TxHistoryItemState.GroupTitle("Today"), + WalletTxHistoryState.TxHistoryItemState.Transaction( + TransactionState.Sending( + address = "33BddS...ga2B", + amount = "-0.500913 BTC", + timestamp = "8:41", + ), + ), + WalletTxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"), + WalletTxHistoryState.TxHistoryItemState.Transaction( + TransactionState.Sending( + address = "33BddS...ga2B", + amount = "-0.500913 BTC", + timestamp = "8:41", + ), ), ), ), ), ), - ), - ) + ) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt index 216dc94148..0967820a37 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt @@ -26,7 +26,7 @@ internal sealed class WalletStateHolder( open val walletsListConfig: WalletsListConfig, open val pullToRefreshConfig: WalletPullToRefreshConfig, open val notifications: ImmutableList, - open val bottomSheet: WalletBottomSheetConfig? = null, + open val bottomSheetConfig: WalletBottomSheetConfig? = null, ) { fun copySealed( @@ -35,7 +35,7 @@ internal sealed class WalletStateHolder( walletsListConfig: WalletsListConfig = this.walletsListConfig, pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig, notifications: ImmutableList = this.notifications, - bottomSheet: WalletBottomSheetConfig? = this.bottomSheet, + bottomSheet: WalletBottomSheetConfig? = this.bottomSheetConfig, ): WalletStateHolder { return when (this) { is MultiCurrencyContent -> this.copy( @@ -44,7 +44,7 @@ internal sealed class WalletStateHolder( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheet, ) is SingleCurrencyContent -> this.copy( onBackClick = onBackClick, @@ -52,7 +52,7 @@ internal sealed class WalletStateHolder( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheet, ) is UnlockWalletContent -> this.copy( onBackClick = onBackClick, @@ -80,7 +80,7 @@ internal sealed class WalletStateHolder( override val walletsListConfig: WalletsListConfig, override val pullToRefreshConfig: WalletPullToRefreshConfig, override val notifications: ImmutableList, - override val bottomSheet: WalletBottomSheetConfig? = null, + override val bottomSheetConfig: WalletBottomSheetConfig? = null, val tokensListState: WalletTokensListState, ) : WalletStateHolder( onBackClick = onBackClick, @@ -88,7 +88,7 @@ internal sealed class WalletStateHolder( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheetConfig, ) /** @@ -109,7 +109,7 @@ internal sealed class WalletStateHolder( override val walletsListConfig: WalletsListConfig, override val pullToRefreshConfig: WalletPullToRefreshConfig, override val notifications: ImmutableList, - override val bottomSheet: WalletBottomSheetConfig? = null, + override val bottomSheetConfig: WalletBottomSheetConfig? = null, val buttons: ImmutableList, val marketPriceBlockState: MarketPriceBlockState, val txHistoryState: WalletTxHistoryState, @@ -119,7 +119,7 @@ internal sealed class WalletStateHolder( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheetConfig, ) /** @@ -151,7 +151,7 @@ internal sealed class WalletStateHolder( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = persistentListOf(WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick)), - bottomSheet = WalletBottomSheetConfig( + bottomSheetConfig = WalletBottomSheetConfig( isShow = false, onDismissRequest = onBottomSheetDismissRequest, content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( @@ -183,6 +183,6 @@ internal sealed class WalletStateHolder( ), pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}), notifications = persistentListOf(), - bottomSheet = null, + bottomSheetConfig = null, ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 72419fce8b..f1fa529bb3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -49,7 +49,7 @@ internal class WalletSkeletonStateConverter( onOrganizeTokensClick = clickIntents::onOrganizeTokensClick, ), notifications = persistentListOf(), - bottomSheet = null, + bottomSheetConfig = null, ) } @@ -63,7 +63,7 @@ internal class WalletSkeletonStateConverter( walletsListConfig = createWalletsListConfig(wallets), pullToRefreshConfig = createPullToRefreshConfig(), notifications = persistentListOf(), - bottomSheet = null, + bottomSheetConfig = null, buttons = WalletPreviewData.singleWalletScreenState.buttons, // TODO: create buttons marketPriceBlockState = MarketPriceBlockState.Loading( currencyName = cardTypeResolver.getBlockchain().currency, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 453c182e49..9dffe74272 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -93,7 +93,9 @@ internal class WalletStateFactory( state.copySealed( bottomSheet = WalletBottomSheetConfig( isShow = true, - onDismissRequest = { state.copySealed(bottomSheet = state.bottomSheet?.copy(isShow = false)) }, + onDismissRequest = { + state.copySealed(bottomSheet = state.bottomSheetConfig?.copy(isShow = false)) + }, content = content, ), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index 5ed89bf2e9..7c692c4c53 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -69,7 +69,7 @@ internal class WalletLoadedTxHistoryConverter( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheetConfig, buttons = getButtons(), marketPriceBlockState = getLoadingMarketPriceBlockState(), txHistoryState = txHistoryState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index da2936afcc..ea98634f3c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -76,7 +76,7 @@ internal class WalletLoadingTxHistoryConverter( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheetConfig, buttons = getButtons(), marketPriceBlockState = getLoadingMarketPriceBlockState(), txHistoryState = txHistoryState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 05f45bd9e4..9ac404298b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -6,8 +6,6 @@ import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.* import androidx.compose.material.ExperimentalMaterialApi -import androidx.compose.material.pullrefresh.PullRefreshIndicator -import androidx.compose.material.pullrefresh.PullRefreshState import androidx.compose.material.pullrefresh.pullRefresh import androidx.compose.material.pullrefresh.rememberPullRefreshState import androidx.compose.material3.* @@ -17,22 +15,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider -import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.collectAsLazyPagingItems -import com.tangem.core.ui.components.buttons.HorizontalActionChips -import com.tangem.core.ui.components.marketprice.MarketPriceBlock -import com.tangem.core.ui.components.notifications.Notification import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState -import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletBottomSheet -import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletTopBar import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList -import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.OrganizeTokensButton -import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems -import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems -import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector +import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* +import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeButton +import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.controlButtons +import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.marketPriceBlock import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimator /** @@ -43,10 +35,10 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimat [REDACTED_AUTHOR] */ @OptIn(ExperimentalMaterialApi::class) -@Suppress("LongMethod") @Composable internal fun WalletScreen(state: WalletStateHolder) { BackHandler(onBack = state.onBackClick) + val walletsListState = rememberLazyListState() Scaffold( @@ -54,7 +46,7 @@ internal fun WalletScreen(state: WalletStateHolder) { containerColor = TangemTheme.colors.background.secondary, ) { scaffoldPaddings -> - val changeableItemModifier = Modifier.changeWalletAnimator(walletsListState) + val movableItemModifier = Modifier.changeWalletAnimator(walletsListState) val pullRefreshState = rememberPullRefreshState( refreshing = state.pullToRefreshConfig.isRefreshing, onRefresh = state.pullToRefreshConfig.onRefresh, @@ -65,76 +57,50 @@ internal fun WalletScreen(state: WalletStateHolder) { .padding(paddingValues = scaffoldPaddings) .pullRefresh(pullRefreshState), ) { - val txHistoryItems = if (state is WalletStateHolder.SingleCurrencyContent) { - if (state.txHistoryState is WalletTxHistoryState.ContentState) { - state.txHistoryState.items.collectAsLazyPagingItems() - } else { - null - } + val txHistoryItems = if (state is WalletStateHolder.SingleCurrencyContent && + state.txHistoryState is WalletTxHistoryState.ContentState + ) { + (state.txHistoryState as? WalletTxHistoryState.ContentState)?.items?.collectAsLazyPagingItems() } else { null } + val betweenItemsPadding = TangemTheme.dimens.spacing14 + val horizontalPadding = TangemTheme.dimens.spacing16 + val itemModifier = movableItemModifier + .padding(top = betweenItemsPadding) + .padding(horizontal = horizontalPadding) + LazyColumn( modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(vertical = TangemTheme.dimens.spacing8), horizontalAlignment = Alignment.CenterHorizontally, ) { item { - WalletsList( - config = state.walletsListConfig, - lazyListState = walletsListState, + WalletsList(config = state.walletsListConfig, lazyListState = walletsListState) + } + + if (state is WalletStateHolder.SingleCurrencyContent) { + controlButtons( + configs = state.buttons, + modifier = movableItemModifier.padding(top = betweenItemsPadding), ) } - if (state is WalletStateHolder.SingleCurrencyContent) { - item { - HorizontalActionChips( - buttons = state.buttons, - modifier = changeableItemModifier.padding(top = TangemTheme.dimens.spacing14), - contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), - ) - } - } - - items( - items = state.notifications, - itemContent = { item -> - Notification( - state = item.state, - modifier = changeableItemModifier - .padding(top = TangemTheme.dimens.spacing14) - .padding(horizontal = TangemTheme.dimens.spacing16), - ) - }, - ) + notifications(configs = state.notifications, modifier = itemModifier) if (state is WalletStateHolder.SingleCurrencyContent) { - item { - MarketPriceBlock( - state = state.marketPriceBlockState, - modifier = changeableItemModifier - .padding(top = TangemTheme.dimens.spacing14) - .padding(horizontal = TangemTheme.dimens.spacing16), - ) - } + marketPriceBlock(state = state.marketPriceBlockState, modifier = itemModifier) } - contentItems(state = state, txHistoryItems = txHistoryItems, modifier = changeableItemModifier) + contentItems(state = state, txHistoryItems = txHistoryItems, modifier = movableItemModifier) if (state is WalletStateHolder.MultiCurrencyContent) { - item { - OrganizeTokensButton( - onClick = state.tokensListState.onOrganizeTokensClick, - modifier = changeableItemModifier - .padding(top = TangemTheme.dimens.spacing14) - .padding(horizontal = TangemTheme.dimens.spacing16), - ) - } + organizeButton(onClick = state.tokensListState.onOrganizeTokensClick, modifier = itemModifier) } } - PullToRefreshIndicator( + WalletPullToRefreshIndicator( isRefreshing = state.pullToRefreshConfig.isRefreshing, state = pullRefreshState, modifier = Modifier.align(Alignment.TopCenter), @@ -142,46 +108,12 @@ internal fun WalletScreen(state: WalletStateHolder) { } } - state.bottomSheet?.let { bottomSheetConfig -> - if (bottomSheetConfig.isShow) WalletBottomSheet(config = bottomSheetConfig) + val bottomSheetConfig = state.bottomSheetConfig + if (bottomSheetConfig != null && bottomSheetConfig.isShow) { + WalletBottomSheet(config = bottomSheetConfig) } - LaunchedEffect(key1 = walletsListState, key2 = state.walletsListConfig.onWalletChange) { - snapshotFlow { walletsListState.layoutInfo.visibleItemsInfo } - .collect(collector = ScrollOffsetCollector(callback = state.walletsListConfig.onWalletChange)) - } -} - -private fun LazyListScope.contentItems( - state: WalletStateHolder, - txHistoryItems: LazyPagingItems?, - modifier: Modifier = Modifier, -) { - when (state) { - is WalletStateHolder.MultiCurrencyContent -> { - tokensListItems(state = state.tokensListState, modifier = modifier) - } - is WalletStateHolder.SingleCurrencyContent -> { - txHistoryItems( - state = state.txHistoryState, - txHistoryItems = txHistoryItems, - modifier = modifier, - ) - } - is WalletStateHolder.Loading, - is WalletStateHolder.UnlockWalletContent, - -> Unit - } -} - -@OptIn(ExperimentalMaterialApi::class) -@Composable -private fun PullToRefreshIndicator(isRefreshing: Boolean, state: PullRefreshState, modifier: Modifier = Modifier) { - PullRefreshIndicator( - refreshing = isRefreshing, - state = state, - modifier = modifier, - ) + WalletSideEffects(lazyListState = walletsListState, walletsListConfig = state.walletsListConfig) } // region Preview diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index 34dc515850..462ca897b1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -11,6 +11,9 @@ import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.tooling.preview.Preview @@ -18,23 +21,25 @@ import androidx.compose.ui.unit.dp import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig +import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletCard /** * Wallets list component * - * @param config config - * @param modifier modifier + * @param config config + * @param lazyListState main content container list state * [REDACTED_AUTHOR] */ @OptIn(ExperimentalFoundationApi::class) @Composable -internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState, modifier: Modifier = Modifier) { +internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState) { val horizontalCardPadding = TangemTheme.dimens.spacing16 - val itemWidth = LocalConfiguration.current.screenWidthDp.dp - horizontalCardPadding * 2 + val screenWidth = LocalConfiguration.current.screenWidthDp.dp + val itemWidth by remember(screenWidth) { derivedStateOf { screenWidth - horizontalCardPadding * 2 } } LazyRow( - modifier = modifier.background(color = TangemTheme.colors.background.secondary), + modifier = Modifier.background(color = TangemTheme.colors.background.secondary), state = lazyListState, contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), @@ -48,22 +53,16 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState @Preview @Composable -private fun Preview_WalletHeader_LightTheme() { +private fun Preview_WalletsList_LightTheme() { TangemTheme(isDark = false) { - WalletsList( - config = WalletPreviewData.walletListConfig, - lazyListState = rememberLazyListState(), - ) + WalletsList(config = WalletPreviewData.walletListConfig, lazyListState = rememberLazyListState()) } } @Preview @Composable -private fun Preview_WalletHeader_DarkTheme() { +private fun Preview_WalletsList_DarkTheme() { TangemTheme(isDark = true) { - WalletsList( - config = WalletPreviewData.walletListConfig, - lazyListState = rememberLazyListState(), - ) + WalletsList(config = WalletPreviewData.walletListConfig, lazyListState = rememberLazyListState()) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt similarity index 99% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletBottomSheet.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt index fb7a83e1bf..6081e5c0da 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components +package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt similarity index 99% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index afc27705b9..59be3295fd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components +package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.annotation.DrawableRes import androidx.compose.foundation.Image @@ -31,6 +31,8 @@ private const val DOTS = "•••" * * @param state state * @param modifier modifier + * +[REDACTED_AUTHOR] */ @Composable internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt new file mode 100644 index 0000000000..798f141c4b --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -0,0 +1,32 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.common + +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.ui.Modifier +import androidx.paging.compose.LazyPagingItems +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems +import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems + +/** + * Wallet content + * + * @param state wallet state + * @param txHistoryItems transaction history items + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +internal fun LazyListScope.contentItems( + state: WalletStateHolder, + txHistoryItems: LazyPagingItems?, + modifier: Modifier = Modifier, +) { + when (state) { + is WalletStateHolder.MultiCurrencyContent -> tokensListItems(state.tokensListState, modifier) + is WalletStateHolder.SingleCurrencyContent -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier) + is WalletStateHolder.Loading, + is WalletStateHolder.UnlockWalletContent, + -> Unit + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt new file mode 100644 index 0000000000..67d25518b8 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.common + +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.items +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.notifications.Notification +import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification +import kotlinx.collections.immutable.ImmutableList + +/** + * Wallet notifications + * + * @param configs list of notifications + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +internal fun LazyListScope.notifications(configs: ImmutableList, modifier: Modifier = Modifier) { + items(items = configs, itemContent = { Notification(state = it.state, modifier = modifier) }) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletPullToRefreshIndicator.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletPullToRefreshIndicator.kt new file mode 100644 index 0000000000..5c26f03441 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletPullToRefreshIndicator.kt @@ -0,0 +1,26 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.common + +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.PullRefreshState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +/** + * "Pull to refresh" indicator + * + * @param isRefreshing indicator is currently refreshing or not + * @param state indicator state + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +@OptIn(ExperimentalMaterialApi::class) +@Composable +internal fun WalletPullToRefreshIndicator( + isRefreshing: Boolean, + state: PullRefreshState, + modifier: Modifier = Modifier, +) { + PullRefreshIndicator(refreshing = isRefreshing, state = state, modifier = modifier) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt new file mode 100644 index 0000000000..1467c145b0 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt @@ -0,0 +1,36 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.common + +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.snapshotFlow +import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig +import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector + +/** + * Wallet screen side effects + * + * @param lazyListState lazy list state + * @param walletsListConfig wallets list config + * +[REDACTED_AUTHOR] + */ +@Composable +internal fun WalletSideEffects(lazyListState: LazyListState, walletsListConfig: WalletsListConfig) { + LaunchedEffect(key1 = walletsListConfig.selectedWalletIndex) { + lazyListState.scrollToItem(walletsListConfig.selectedWalletIndex) + } + + val dragInteraction = lazyListState.interactionSource.interactions.collectAsState(initial = null) + LaunchedEffect(key1 = lazyListState, key2 = walletsListConfig.onWalletChange) { + snapshotFlow { lazyListState.layoutInfo.visibleItemsInfo } + .collect( + collector = ScrollOffsetCollector( + lazyListState = lazyListState, + dragInteraction = dragInteraction, + callback = walletsListConfig.onWalletChange, + ), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletTopBar.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt similarity index 99% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletTopBar.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt index 54db11db4d..6d23d5706f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletTopBar.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components +package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.material3.* import androidx.compose.runtime.Composable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt new file mode 100644 index 0000000000..a8fb1add43 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt @@ -0,0 +1,16 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency + +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.ui.Modifier + +/** + * Organize tokens button + * + * @param onClick callback is invoked when button is clicked + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +internal fun LazyListScope.organizeButton(onClick: (() -> Unit)?, modifier: Modifier = Modifier) { + item { OrganizeTokensButton(onClick = onClick, modifier = modifier) } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt new file mode 100644 index 0000000000..d118b99fbc --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt @@ -0,0 +1,27 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency + +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.buttons.HorizontalActionChips +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.res.TangemTheme +import kotlinx.collections.immutable.ImmutableList + +/** + * Single currency control buttons. Like, "Buy", "Sell", etc + * + * @param configs list of buttons + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +internal fun LazyListScope.controlButtons(configs: ImmutableList, modifier: Modifier = Modifier) { + item { + HorizontalActionChips( + buttons = configs, + modifier = modifier, + contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt new file mode 100644 index 0000000000..5e8d88c954 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt @@ -0,0 +1,18 @@ +package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency + +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.marketprice.MarketPriceBlock +import com.tangem.core.ui.components.marketprice.MarketPriceBlockState + +/** + * Single currency market price block + * + * @param state component state + * @param modifier modifier + * +[REDACTED_AUTHOR] + */ +internal fun LazyListScope.marketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) { + item { MarketPriceBlock(state = state, modifier = modifier) } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt index 41cbf50ec1..1cfc37798f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/utils/ScrollOffsetCollector.kt @@ -1,6 +1,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui.utils +import androidx.compose.foundation.interaction.Interaction import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.State import kotlinx.coroutines.flow.FlowCollector import kotlin.math.abs @@ -9,17 +12,22 @@ import kotlin.math.abs * If first visible item offset is greater than half item size, then [callback] be invoked. * If last visible item offset is greater than half item size, then [callback] be invoked. * - * @property callback lambda be invoked when current scroll items is changed + * @property lazyListState lazy list state + * @property dragInteraction current drag interaction + * @property callback lambda be invoked when current scroll items is changed * [REDACTED_AUTHOR] */ internal class ScrollOffsetCollector( + private val lazyListState: LazyListState, + private val dragInteraction: State, private val callback: (Int) -> Unit, ) : FlowCollector> { private val LazyListItemInfo.halfItemSize get() = size.div(other = 2) override suspend fun emit(value: List) { + if (!lazyListState.isScrollInProgress || dragInteraction.value == null || value.size <= 1) return val firstItem = value.firstOrNull() ?: return val lastItem = value.lastOrNull() ?: return diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt index b93109b6f6..6f039ad4d2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt @@ -20,7 +20,7 @@ internal class TokenListErrorToWalletStateConverter( walletsListConfig = state.walletsListConfig, pullToRefreshConfig = state.pullToRefreshConfig, notifications = state.notifications, - bottomSheet = state.bottomSheet, + bottomSheetConfig = state.bottomSheetConfig, tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index 06ec3d5b8a..e1a7a84ea9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -50,7 +50,7 @@ internal class TokenListToWalletStateConverter( walletsListConfig = walletsListConfig, pullToRefreshConfig = pullToRefreshConfig, notifications = notifications, - bottomSheet = bottomSheet, + bottomSheetConfig = bottomSheetConfig, tokensListState = tokenListToContentConverter.convert(value = tokenList), ) } From dd186d6fbcc455db635c0d36886c61e64ccd426b Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Aug 2023 12:56:20 +0800 Subject: [PATCH 05/52] Updated on 2026-08-14 --- .../presentation/common/WalletPreviewData.kt | 7 +- .../wallet/state/WalletLoading.kt | 37 +++ .../wallet/state/WalletLockedState.kt | 24 ++ .../wallet/state/WalletMultiCurrencyState.kt | 54 ++++ .../wallet/state/WalletSingleCurrencyState.kt | 68 +++++ .../wallet/state/WalletStateHolder.kt | 247 ++++++------------ .../WalletBottomSheetConfig.kt | 2 +- .../state/{ => components}/WalletCardState.kt | 2 +- .../WalletLockedContentState.kt | 2 +- .../{ => components}/WalletManageButton.kt | 2 +- .../{ => components}/WalletNotification.kt | 2 +- .../WalletPullToRefreshConfig.kt | 2 +- .../WalletTokensListState.kt | 2 +- .../{ => components}/WalletTopBarConfig.kt | 2 +- .../WalletTxHistoryState.kt | 2 +- .../{ => components}/WalletsListConfig.kt | 2 +- .../WalletLoadedTokensListConverter.kt | 2 + .../factory/WalletSkeletonStateConverter.kt | 81 ++++-- .../state/factory/WalletStateFactory.kt | 67 ++++- .../WalletLoadedTxHistoryConverter.kt | 9 +- .../WalletLoadingTxHistoryConverter.kt | 9 +- .../WalletTxHistoryItemFlowConverter.kt | 4 +- .../presentation/wallet/ui/WalletScreen.kt | 12 +- .../wallet/ui/components/WalletsList.kt | 2 +- .../ui/components/common/WalletBottomSheet.kt | 2 +- .../wallet/ui/components/common/WalletCard.kt | 2 +- .../ui/components/common/WalletContent.kt | 12 +- .../components/common/WalletNotifications.kt | 2 +- .../ui/components/common/WalletSideEffects.kt | 2 +- .../ui/components/common/WalletTopBar.kt | 2 +- .../multicurrency/MultiCurrencyContent.kt | 2 +- .../multicurrency/MultiCurrencyContentItem.kt | 2 +- .../singlecurrency/SingleCurrencyContent.kt | 2 +- .../SingleCurrencyContentItem.kt | 2 +- .../singlecurrency/TxHistoryGroupTitle.kt | 2 +- .../singlecurrency/TxHistoryTitle.kt | 2 +- .../utils/FiatBalanceToWalletCardConverter.kt | 2 +- .../wallet/utils/LoadingItemsProvider.kt | 2 +- .../TokenListErrorToWalletStateConverter.kt | 5 +- .../utils/TokenListToContentItemsConverter.kt | 4 +- .../utils/TokenListToWalletStateConverter.kt | 16 +- .../wallet/viewmodels/WalletClickIntents.kt | 4 + .../WalletNotificationsListFactory.kt | 7 +- .../wallet/viewmodels/WalletViewModel.kt | 52 +++- 44 files changed, 499 insertions(+), 270 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletBottomSheetConfig.kt (98%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletCardState.kt (97%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{content => components}/WalletLockedContentState.kt (73%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletManageButton.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletNotification.kt (98%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletPullToRefreshConfig.kt (78%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{content => components}/WalletTokensListState.kt (96%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletTopBarConfig.kt (80%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{content => components}/WalletTxHistoryState.kt (97%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/{ => components}/WalletsListConfig.kt (86%) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index ba60a1451b..bce4d536e1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -12,8 +12,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder import com.tangem.feature.wallet.presentation.wallet.state.* -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.flowOf @@ -255,7 +254,7 @@ internal object WalletPreviewData { } val multicurrencyWalletScreenState by lazy { - WalletStateHolder.MultiCurrencyContent( + WalletMultiCurrencyState.Content( onBackClick = {}, topBarConfig = walletTopBarConfig, walletsListConfig = walletListConfig, @@ -326,7 +325,7 @@ internal object WalletPreviewData { } val singleWalletScreenState by lazy { - WalletStateHolder.SingleCurrencyContent( + WalletSingleCurrencyState.Content( onBackClick = {}, topBarConfig = walletTopBarConfig, walletsListConfig = walletListConfig, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt new file mode 100644 index 0000000000..55e211840f --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt @@ -0,0 +1,37 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.wallet.presentation.wallet.state.components.* +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +/** + * Loading wallet state + * + * @property onBackClick Lambda be invoked when back button is clicked + * +[REDACTED_AUTHOR] + */ +internal data class WalletLoading(override val onBackClick: () -> Unit) : WalletStateHolder() { + + override val topBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) + + override val walletsListConfig = WalletsListConfig( + selectedWalletIndex = 0, + wallets = persistentListOf( + WalletCardState.Loading( + id = UserWalletId(stringValue = ""), + title = "", + additionalInfo = "", + imageResId = null, + ), + ), + onWalletChange = {}, + ) + + override val pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}) + + override val notifications: ImmutableList = persistentListOf() + + override val bottomSheetConfig = null +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt new file mode 100644 index 0000000000..352b0687a2 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt @@ -0,0 +1,24 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +/** + * Locked wallet state + * +[REDACTED_AUTHOR] + */ +internal sealed interface WalletLockedState { + + /** Lambda be invoked when unlock wallet notification is clicked */ + val onUnlockWalletsNotificationClick: () -> Unit + + /** Lambda be invoked when unlock wallet button is clicked */ + val onUnlockClick: () -> Unit + + /** Lambda be invoked when scan button is clicked */ + val onScanClick: () -> Unit + + /** Bottom sheet visibility */ + val isBottomSheetShow: Boolean + + /** Lambda be invoked when bottom sheet is dismissed */ + val onBottomSheetDismiss: () -> Unit +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt new file mode 100644 index 0000000000..0b93c6033d --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt @@ -0,0 +1,54 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.feature.wallet.presentation.wallet.state.components.* +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +/** + * Multi currency wallet state + * +[REDACTED_AUTHOR] + */ +internal sealed class WalletMultiCurrencyState : WalletStateHolder() { + + /** Tokens list state */ + abstract val tokensListState: WalletTokensListState + + data class Content( + override val onBackClick: () -> Unit, + override val topBarConfig: WalletTopBarConfig, + override val walletsListConfig: WalletsListConfig, + override val pullToRefreshConfig: WalletPullToRefreshConfig, + override val notifications: ImmutableList, + override val bottomSheetConfig: WalletBottomSheetConfig?, + override val tokensListState: WalletTokensListState, + ) : WalletMultiCurrencyState() + + data class Locked( + override val onBackClick: () -> Unit, + override val topBarConfig: WalletTopBarConfig, + override val walletsListConfig: WalletsListConfig, + override val pullToRefreshConfig: WalletPullToRefreshConfig, + override val onUnlockWalletsNotificationClick: () -> Unit, + override val onUnlockClick: () -> Unit, + override val onScanClick: () -> Unit, + override val isBottomSheetShow: Boolean = false, + override val onBottomSheetDismiss: () -> Unit = {}, + ) : WalletMultiCurrencyState(), WalletLockedState { + + override val notifications = persistentListOf( + WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick), + ) + + override val bottomSheetConfig = WalletBottomSheetConfig( + isShow = isBottomSheetShow, + onDismissRequest = onBottomSheetDismiss, + content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + onUnlockClick = onUnlockClick, + onScanClick = onScanClick, + ), + ) + + override val tokensListState: WalletTokensListState = WalletTokensListState.Locked + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt new file mode 100644 index 0000000000..2a01cb5668 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -0,0 +1,68 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.feature.wallet.presentation.wallet.state.components.* +import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf + +/** + * Single currency wallet content state + * +[REDACTED_AUTHOR] + */ +internal sealed class WalletSingleCurrencyState : WalletStateHolder() { + + /** Manage buttons */ + abstract val buttons: ImmutableList + + /** Market price block state */ + abstract val marketPriceBlockState: MarketPriceBlockState? + + /** Transactions history state */ + abstract val txHistoryState: WalletTxHistoryState + + data class Content( + override val onBackClick: () -> Unit, + override val topBarConfig: WalletTopBarConfig, + override val walletsListConfig: WalletsListConfig, + override val pullToRefreshConfig: WalletPullToRefreshConfig, + override val notifications: ImmutableList, + override val bottomSheetConfig: WalletBottomSheetConfig?, + override val buttons: ImmutableList, + override val marketPriceBlockState: MarketPriceBlockState, + override val txHistoryState: WalletTxHistoryState, + ) : WalletSingleCurrencyState() + + data class Locked( + override val onBackClick: () -> Unit, + override val topBarConfig: WalletTopBarConfig, + override val walletsListConfig: WalletsListConfig, + override val pullToRefreshConfig: WalletPullToRefreshConfig, + override val buttons: ImmutableList, + override val onUnlockWalletsNotificationClick: () -> Unit, + override val onUnlockClick: () -> Unit, + override val onScanClick: () -> Unit, + override val isBottomSheetShow: Boolean = false, + override val onBottomSheetDismiss: () -> Unit = {}, + val onExploreClick: () -> Unit, + ) : WalletSingleCurrencyState(), WalletLockedState { + + override val notifications = persistentListOf( + WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick), + ) + + override val bottomSheetConfig = WalletBottomSheetConfig( + isShow = isBottomSheetShow, + onDismissRequest = onBottomSheetDismiss, + content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( + onUnlockClick = onUnlockClick, + onScanClick = onScanClick, + ), + ) + + override val marketPriceBlockState = null + + override val txHistoryState: WalletTxHistoryState = WalletTxHistoryState.Locked(onExploreClick) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt index 0967820a37..25338db85c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt @@ -1,33 +1,32 @@ package com.tangem.feature.wallet.presentation.wallet.state -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletLockedContentState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf /** * Wallet screen state holder * - * @property onBackClick lambda be invoked when back button is clicked - * @property topBarConfig top bar config - * @property walletsListConfig wallets list config - * @property pullToRefreshConfig pull to refresh config - * @property notifications notifications - * [REDACTED_AUTHOR] */ -internal sealed class WalletStateHolder( - open val onBackClick: () -> Unit, - open val topBarConfig: WalletTopBarConfig, - open val walletsListConfig: WalletsListConfig, - open val pullToRefreshConfig: WalletPullToRefreshConfig, - open val notifications: ImmutableList, - open val bottomSheetConfig: WalletBottomSheetConfig? = null, -) { +internal sealed class WalletStateHolder { + + /** Lambda be invoked when back button is clicked */ + abstract val onBackClick: () -> Unit + + /** Top bar config */ + abstract val topBarConfig: WalletTopBarConfig + + /** Wallets list config */ + abstract val walletsListConfig: WalletsListConfig + + /** Pull to refresh config */ + abstract val pullToRefreshConfig: WalletPullToRefreshConfig + + /** Notifications */ + abstract val notifications: ImmutableList + + /** Bottom sheet config */ + abstract val bottomSheetConfig: WalletBottomSheetConfig? fun copySealed( onBackClick: () -> Unit = this.onBackClick, @@ -38,151 +37,67 @@ internal sealed class WalletStateHolder( bottomSheet: WalletBottomSheetConfig? = this.bottomSheetConfig, ): WalletStateHolder { return when (this) { - is MultiCurrencyContent -> this.copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheet, - ) - is SingleCurrencyContent -> this.copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheet, - ) - is UnlockWalletContent -> this.copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - ) - is Loading -> copy(onBackClick = onBackClick) + is WalletLoading -> { + copy(onBackClick = onBackClick) + } + is WalletMultiCurrencyState.Content -> { + copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + notifications = notifications, + bottomSheetConfig = bottomSheet, + ) + } + is WalletMultiCurrencyState.Locked -> { + if (bottomSheet != null) { + copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + isBottomSheetShow = bottomSheet.isShow, + onBottomSheetDismiss = bottomSheet.onDismissRequest, + ) + } else { + copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + ) + } + } + is WalletSingleCurrencyState.Content -> { + copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + notifications = notifications, + bottomSheetConfig = bottomSheet, + ) + } + is WalletSingleCurrencyState.Locked -> { + if (bottomSheet != null) { + copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + isBottomSheetShow = bottomSheet.isShow, + onBottomSheetDismiss = bottomSheet.onDismissRequest, + ) + } else { + copy( + onBackClick = onBackClick, + topBarConfig = topBarConfig, + walletsListConfig = walletsListConfig, + pullToRefreshConfig = pullToRefreshConfig, + ) + } + } } } - - /** - * Multi currency wallet content state - * - * @property onBackClick lambda be invoked when back button is clicked - * @property topBarConfig top bar config - * @property walletsListConfig wallets list config - * @property pullToRefreshConfig pull to refresh config - * @property tokensListState token list state - * @property notifications notifications - */ - data class MultiCurrencyContent( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val notifications: ImmutableList, - override val bottomSheetConfig: WalletBottomSheetConfig? = null, - val tokensListState: WalletTokensListState, - ) : WalletStateHolder( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - ) - - /** - * Single currency wallet content state - * - * @property onBackClick lambda be invoked when back button is clicked - * @property topBarConfig top bar config - * @property walletsListConfig wallets list config - * @property pullToRefreshConfig pull to refresh config - * @property notifications notifications - * @property buttons manage buttons - * @property marketPriceBlockState market price block state - * @property txHistoryState transactions history state - */ - data class SingleCurrencyContent( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val notifications: ImmutableList, - override val bottomSheetConfig: WalletBottomSheetConfig? = null, - val buttons: ImmutableList, - val marketPriceBlockState: MarketPriceBlockState, - val txHistoryState: WalletTxHistoryState, - ) : WalletStateHolder( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - ) - - /** - * Unlock wallet content state - * - * @property onBackClick lambda be invoked when back button is clicked - * @property topBarConfig top bar config - * @property walletsListConfig wallets list config - * @property pullToRefreshConfig pull to refresh config - * @property lockedContentState locked content state - * @property onUnlockWalletsNotificationClick lambda be invoked when unlock wallets notification is clicked - * @property onBottomSheetDismissRequest lambda be invoked when bottom sheet is dismissed - * @property onUnlockClick lambda be invoked when unlock button is clicked - * @property onScanClick lambda be invoked when scan card button is clicked - */ - data class UnlockWalletContent( - override val onBackClick: () -> Unit, - override val topBarConfig: WalletTopBarConfig, - override val walletsListConfig: WalletsListConfig, - override val pullToRefreshConfig: WalletPullToRefreshConfig, - val lockedContentState: WalletLockedContentState, - val onUnlockWalletsNotificationClick: () -> Unit, - val onBottomSheetDismissRequest: () -> Unit, - val onUnlockClick: () -> Unit, - val onScanClick: () -> Unit, - ) : WalletStateHolder( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = persistentListOf(WalletNotification.UnlockWallets(onUnlockWalletsNotificationClick)), - bottomSheetConfig = WalletBottomSheetConfig( - isShow = false, - onDismissRequest = onBottomSheetDismissRequest, - content = WalletBottomSheetConfig.BottomSheetContentConfig.UnlockWallets( - onUnlockClick = onUnlockClick, - onScanClick = onScanClick, - ), - ), - ) - - /** - * Loading state - * - * @property onBackClick lambda be invoked when back button is clicked - */ - data class Loading(override val onBackClick: () -> Unit) : WalletStateHolder( - onBackClick = onBackClick, - topBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}), - walletsListConfig = WalletsListConfig( - selectedWalletIndex = 0, - wallets = persistentListOf( - WalletCardState.Loading( - id = UserWalletId(stringValue = ""), - title = "", - additionalInfo = "", - imageResId = null, - ), - ), - onWalletChange = {}, - ), - pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}), - notifications = persistentListOf(), - bottomSheetConfig = null, - ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletBottomSheetConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt similarity index 98% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletBottomSheetConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt index 5a8f12209b..eef9a95ef0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletBottomSheetConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletBottomSheetConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components import androidx.annotation.DrawableRes import androidx.compose.ui.graphics.Color diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt similarity index 97% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt index b1f88360b1..2da80de62f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletCardState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletCardState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components import androidx.annotation.DrawableRes import androidx.compose.runtime.Immutable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletLockedContentState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt similarity index 73% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletLockedContentState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt index 9867aed606..c0a0206ecc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletLockedContentState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.content +package com.tangem.feature.wallet.presentation.wallet.state.components /** * Wallet locked content state. diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletManageButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletManageButton.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt index cb07819add..61441600c3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletManageButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt similarity index 98% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt index 842f92ccfe..99a59df14d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletNotification.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletNotification.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components import com.tangem.core.ui.components.notifications.NotificationState import com.tangem.core.ui.extensions.TextReference diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletPullToRefreshConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletPullToRefreshConfig.kt similarity index 78% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletPullToRefreshConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletPullToRefreshConfig.kt index 8aafa384f0..6714af85cc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletPullToRefreshConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletPullToRefreshConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components /** * Wallet screen top bar config diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt similarity index 96% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletTokensListState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index 4f7e51d4d3..9fa10a5446 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.content +package com.tangem.feature.wallet.presentation.wallet.state.components import com.tangem.feature.wallet.presentation.common.state.TokenItemState import kotlinx.collections.immutable.ImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletTopBarConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTopBarConfig.kt similarity index 80% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletTopBarConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTopBarConfig.kt index f1838ea250..52e984f940 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletTopBarConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTopBarConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components /** * Wallet screen top bar config diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletTxHistoryState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt similarity index 97% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletTxHistoryState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt index 5252fa8981..b09a0410c8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/content/WalletTxHistoryState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state.content +package com.tangem.feature.wallet.presentation.wallet.state.components import androidx.paging.PagingData import com.tangem.core.ui.components.transactions.TransactionState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletsListConfig.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt similarity index 86% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletsListConfig.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt index 0349eb57bd..20eac2dec5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletsListConfig.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletsListConfig.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.state +package com.tangem.feature.wallet.presentation.wallet.state.components import kotlinx.collections.immutable.ImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt index 293d66e1bc..9ffc95c838 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt @@ -24,12 +24,14 @@ import com.tangem.utils.converter.Converter internal class WalletLoadedTokensListConverter( private val currentStateProvider: Provider, cardTypeResolverProvider: Provider, + isLockedWalletProvider: Provider, clickIntents: WalletClickIntents, ) : Converter { private val tokenListStateConverter = TokenListToWalletStateConverter( currentStateProvider = currentStateProvider, cardTypeResolverProvider = cardTypeResolverProvider, + isLockedWalletProvider = isLockedWalletProvider, isWalletContentHidden = false, // TODO: [REDACTED_JIRA] fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA] fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA] diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index f1fa529bb3..cd97249197 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -1,18 +1,21 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import androidx.paging.PagingData +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver -import com.tangem.feature.wallet.presentation.wallet.state.* -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.components.* +import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletSkeletonStateConverter.SkeletonModel import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.flow @@ -26,45 +29,59 @@ import kotlinx.coroutines.flow.flow */ internal class WalletSkeletonStateConverter( private val clickIntents: WalletClickIntents, -) : Converter, WalletStateHolder> { +) : Converter { - override fun convert(value: List): WalletStateHolder { - val cardTypeResolver = requireNotNull(value.firstOrNull()).scanResponse.cardTypesResolver + override fun convert(value: SkeletonModel): WalletStateHolder { + val wallet = requireNotNull(value.wallets.getOrNull(value.selectedWalletIndex)) { "Empty wallet list" } + val cardTypeResolver = wallet.scanResponse.cardTypesResolver - return if (cardTypeResolver.isMultiwalletAllowed()) { - createMultiCurrencyState(value) - } else { - createSingleCurrencyState(value, cardTypeResolver) + return when { + cardTypeResolver.isMultiwalletAllowed() -> createMultiCurrencyState(value) + !cardTypeResolver.isMultiwalletAllowed() -> createSingleCurrencyState(value, cardTypeResolver) + else -> error("Illegal wallet state: $wallet") } } - private fun createMultiCurrencyState(wallets: List): WalletStateHolder.MultiCurrencyContent { - return WalletStateHolder.MultiCurrencyContent( + /** + * Create [WalletMultiCurrencyState.Content]. + * Tokens and notifications are updated asynchronously. + * + * @param value converted value + */ + private fun createMultiCurrencyState(value: SkeletonModel): WalletMultiCurrencyState.Content { + return WalletMultiCurrencyState.Content( onBackClick = clickIntents::onBackClick, topBarConfig = createTopBarConfig(), - walletsListConfig = createWalletsListConfig(wallets), + walletsListConfig = createWalletsListConfig(value), pullToRefreshConfig = createPullToRefreshConfig(), tokensListState = WalletTokensListState.Content( items = persistentListOf(), - onOrganizeTokensClick = clickIntents::onOrganizeTokensClick, + onOrganizeTokensClick = null, ), notifications = persistentListOf(), bottomSheetConfig = null, ) } + /** + * Create [WalletSingleCurrencyState.Content]. + * Transactions, notifications and market price are updated asynchronously. + * + * @param value converted value + * @param cardTypeResolver card type resolver + */ private fun createSingleCurrencyState( - wallets: List, + value: SkeletonModel, cardTypeResolver: CardTypesResolver, - ): WalletStateHolder.SingleCurrencyContent { - return WalletStateHolder.SingleCurrencyContent( + ): WalletSingleCurrencyState.Content { + return WalletSingleCurrencyState.Content( onBackClick = clickIntents::onBackClick, topBarConfig = createTopBarConfig(), - walletsListConfig = createWalletsListConfig(wallets), + walletsListConfig = createWalletsListConfig(value), pullToRefreshConfig = createPullToRefreshConfig(), notifications = persistentListOf(), bottomSheetConfig = null, - buttons = WalletPreviewData.singleWalletScreenState.buttons, // TODO: create buttons + buttons = getButtons(), marketPriceBlockState = MarketPriceBlockState.Loading( currencyName = cardTypeResolver.getBlockchain().currency, ), @@ -81,10 +98,10 @@ internal class WalletSkeletonStateConverter( ) } - private fun createWalletsListConfig(wallets: List): WalletsListConfig { + private fun createWalletsListConfig(value: SkeletonModel): WalletsListConfig { return WalletsListConfig( - selectedWalletIndex = 0, - wallets = wallets.map { wallet -> + selectedWalletIndex = value.selectedWalletIndex, + wallets = value.wallets.map { wallet -> val cardTypeResolver = wallet.scanResponse.cardTypesResolver WalletCardState.Loading( id = wallet.walletId, @@ -103,4 +120,22 @@ internal class WalletSkeletonStateConverter( private fun createPullToRefreshConfig(): WalletPullToRefreshConfig { return WalletPullToRefreshConfig(isRefreshing = false, onRefresh = clickIntents::onRefreshSwipe) } + + // TODO: [REDACTED_JIRA] + private fun getButtons(): ImmutableList { + return persistentListOf( + WalletManageButton.Buy(onClick = {}), + WalletManageButton.Send(onClick = {}), + WalletManageButton.Receive(onClick = {}), + WalletManageButton.Exchange(onClick = {}), + WalletManageButton.CopyAddress(onClick = {}), + ) + .map(WalletManageButton::config) + .toImmutableList() + } + + data class SkeletonModel( + val wallets: List, + val selectedWalletIndex: Int, + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 9dffe74272..a3bb1b915c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList @@ -10,26 +11,34 @@ import com.tangem.domain.txhistory.error.TxHistoryListError import com.tangem.domain.txhistory.error.TxHistoryStateError import com.tangem.domain.txhistory.model.TxHistoryItem import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.WalletBottomSheetConfig -import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletLoadedTokensListConverter.LoadedTokensListModel import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadedTxHistoryConverter import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadingTxHistoryConverter import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow /** * Main factory for creating [WalletStateHolder] * * @property currentStateProvider current ui state provider - * @param currentCardTypeResolverProvider current card type resolver + * @property currentCardTypeResolverProvider current card type resolver + * @property isLockedWalletProvider current wallet is locked or not * @property clickIntents screen click intents */ internal class WalletStateFactory( private val currentStateProvider: Provider, - currentCardTypeResolverProvider: Provider, + private val currentCardTypeResolverProvider: Provider, + private val isLockedWalletProvider: Provider, private val clickIntents: WalletClickIntents, ) { @@ -39,6 +48,7 @@ internal class WalletStateFactory( WalletLoadedTokensListConverter( currentStateProvider = currentStateProvider, cardTypeResolverProvider = currentCardTypeResolverProvider, + isLockedWalletProvider = isLockedWalletProvider, clickIntents = clickIntents, ) } @@ -59,9 +69,13 @@ internal class WalletStateFactory( ) } - fun getInitialState(): WalletStateHolder = WalletStateHolder.Loading(onBackClick = clickIntents::onBackClick) + fun getInitialState(): WalletStateHolder = WalletLoading(onBackClick = clickIntents::onBackClick) - fun getSkeletonState(wallets: List): WalletStateHolder = skeletonConverter.convert(wallets) + fun getSkeletonState(wallets: List, index: Int): WalletStateHolder { + return skeletonConverter.convert( + value = WalletSkeletonStateConverter.SkeletonModel(wallets = wallets, selectedWalletIndex = index), + ) + } fun getStateByTokensList( tokenListEither: Either, @@ -111,4 +125,45 @@ internal class WalletStateFactory( ): WalletStateHolder { return loadedTxHistoryConverter.convert(txHistoryEither) } + + fun getLockedState(): WalletStateHolder { + val cardTypeResolver = currentCardTypeResolverProvider() + val state = currentStateProvider() + return if (cardTypeResolver.isMultiwalletAllowed()) { + WalletMultiCurrencyState.Locked( + onBackClick = state.onBackClick, + topBarConfig = state.topBarConfig, + walletsListConfig = state.walletsListConfig, + pullToRefreshConfig = state.pullToRefreshConfig, + onUnlockWalletsNotificationClick = clickIntents::onUnlockWalletNotificationClick, + onUnlockClick = clickIntents::onUnlockWalletClick, + onScanClick = clickIntents::onScanCardClick, + ) + } else { + WalletSingleCurrencyState.Locked( + onBackClick = state.onBackClick, + topBarConfig = state.topBarConfig, + walletsListConfig = state.walletsListConfig, + pullToRefreshConfig = state.pullToRefreshConfig, + buttons = getButtons(), + onUnlockWalletsNotificationClick = clickIntents::onUnlockWalletNotificationClick, + onUnlockClick = clickIntents::onUnlockWalletClick, + onScanClick = clickIntents::onScanCardClick, + onExploreClick = clickIntents::onExploreClick, + ) + } + } + + // TODO: [REDACTED_JIRA] + private fun getButtons(): ImmutableList { + return persistentListOf( + WalletManageButton.Buy(onClick = {}), + WalletManageButton.Send(onClick = {}), + WalletManageButton.Receive(onClick = {}), + WalletManageButton.Exchange(onClick = {}), + WalletManageButton.CopyAddress(onClick = {}), + ) + .map(WalletManageButton::config) + .toImmutableList() + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index 7c692c4c53..8f4e728d13 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -8,9 +8,10 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.error.TxHistoryListError import com.tangem.domain.txhistory.model.TxHistoryItem -import com.tangem.feature.wallet.presentation.wallet.state.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList @@ -62,8 +63,8 @@ internal class WalletLoadedTxHistoryConverter( private fun WalletStateHolder.copySingleCurrencyContent( txHistoryState: WalletTxHistoryState, - ): WalletStateHolder.SingleCurrencyContent { - return WalletStateHolder.SingleCurrencyContent( + ): WalletSingleCurrencyState { + return WalletSingleCurrencyState.Content( onBackClick = onBackClick, topBarConfig = topBarConfig, walletsListConfig = walletsListConfig, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index ea98634f3c..ffe28ac46d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -8,9 +8,10 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.TransactionState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.error.TxHistoryStateError -import com.tangem.feature.wallet.presentation.wallet.state.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList @@ -69,8 +70,8 @@ internal class WalletLoadingTxHistoryConverter( private fun WalletStateHolder.copySingleCurrencyContent( txHistoryState: WalletTxHistoryState, - ): WalletStateHolder.SingleCurrencyContent { - return WalletStateHolder.SingleCurrencyContent( + ): WalletSingleCurrencyState { + return WalletSingleCurrencyState.Content( onBackClick = onBackClick, topBarConfig = topBarConfig, walletsListConfig = walletsListConfig, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index 2fe01c3e0e..7d61b17351 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -5,8 +5,8 @@ import androidx.paging.* import com.tangem.blockchain.common.Blockchain import com.tangem.core.ui.components.transactions.TransactionState import com.tangem.domain.txhistory.model.TxHistoryItem -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState.TxHistoryItemState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.isToday diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 9ac404298b..cc15ab700c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -18,8 +18,10 @@ import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameter import androidx.paging.compose.collectAsLazyPagingItems import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeButton @@ -57,7 +59,7 @@ internal fun WalletScreen(state: WalletStateHolder) { .padding(paddingValues = scaffoldPaddings) .pullRefresh(pullRefreshState), ) { - val txHistoryItems = if (state is WalletStateHolder.SingleCurrencyContent && + val txHistoryItems = if (state is WalletSingleCurrencyState && state.txHistoryState is WalletTxHistoryState.ContentState ) { (state.txHistoryState as? WalletTxHistoryState.ContentState)?.items?.collectAsLazyPagingItems() @@ -80,7 +82,7 @@ internal fun WalletScreen(state: WalletStateHolder) { WalletsList(config = state.walletsListConfig, lazyListState = walletsListState) } - if (state is WalletStateHolder.SingleCurrencyContent) { + if (state is WalletSingleCurrencyState) { controlButtons( configs = state.buttons, modifier = movableItemModifier.padding(top = betweenItemsPadding), @@ -89,13 +91,13 @@ internal fun WalletScreen(state: WalletStateHolder) { notifications(configs = state.notifications, modifier = itemModifier) - if (state is WalletStateHolder.SingleCurrencyContent) { + if (state is WalletSingleCurrencyState.Content) { marketPriceBlock(state = state.marketPriceBlockState, modifier = itemModifier) } contentItems(state = state, txHistoryItems = txHistoryItems, modifier = movableItemModifier) - if (state is WalletStateHolder.MultiCurrencyContent) { + if (state is WalletMultiCurrencyState) { organizeButton(onClick = state.tokensListState.onOrganizeTokensClick, modifier = itemModifier) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index 462ca897b1..b2505603a2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.ui.components.common.WalletCard /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt index 6081e5c0da..bf78b633da 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletBottomSheet.kt @@ -18,7 +18,7 @@ import com.tangem.core.ui.components.SecondaryButtonIconStart import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.WalletBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig /** * Wallet bottom sheet with detail notification information diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index 59be3295fd..fd0498866a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -22,7 +22,7 @@ import com.tangem.core.ui.components.ResizableText import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState private const val DOTS = "•••" diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index 798f141c4b..f95107b7e9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -3,8 +3,11 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems +import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems @@ -23,10 +26,9 @@ internal fun LazyListScope.contentItems( modifier: Modifier = Modifier, ) { when (state) { - is WalletStateHolder.MultiCurrencyContent -> tokensListItems(state.tokensListState, modifier) - is WalletStateHolder.SingleCurrencyContent -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier) - is WalletStateHolder.Loading, - is WalletStateHolder.UnlockWalletContent, + is WalletMultiCurrencyState -> tokensListItems(state.tokensListState, modifier) + is WalletSingleCurrencyState -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier) + is WalletLoading, -> Unit } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index 67d25518b8..4b2f3cffb7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.ui.Modifier import com.tangem.core.ui.components.notifications.Notification -import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import kotlinx.collections.immutable.ImmutableList /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt index 1467c145b0..0d415eaa74 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletSideEffects.kt @@ -5,7 +5,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.snapshotFlow -import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.ui.utils.ScrollOffsetCollector /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt index 6d23d5706f..5b809bf2e7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletTopBar.kt @@ -7,7 +7,7 @@ import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.wallet.state.WalletTopBarConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTopBarConfig /** * Wallet screen top bar diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index c8fc7782f3..331d8de5dd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrenc import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.ui.Modifier -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletContentItemDecoration /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt index a387c5f956..3cdfd7517d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt @@ -4,7 +4,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem import com.tangem.feature.wallet.presentation.common.component.TokenItem -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState /** * Multi-currency content item diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt index 3684c90d62..d70b42ab44 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt @@ -9,7 +9,7 @@ import androidx.paging.compose.itemsIndexed import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlock import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletContentItemDecoration /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt index 58489e3a3d..8a20e22efe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt @@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurren import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import com.tangem.core.ui.components.transactions.Transaction -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState /** * Single currency content item diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt index 275f2fce6d..b3b8875b39 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt @@ -9,7 +9,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState.TxHistoryItemState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState /** * Transactions block group title diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt index a71ec939db..58e06ab395 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt @@ -12,7 +12,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTxHistoryState.TxHistoryItemState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState /** * Transactions block title diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt index b197bf76e8..c8b1d0b3b3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt @@ -5,7 +5,7 @@ import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory -import com.tangem.feature.wallet.presentation.wallet.state.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState import com.tangem.utils.converter.Converter internal class FiatBalanceToWalletCardConverter( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt index 067db48037..174c0b9f50 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt index 6f039ad4d2..2697e71bd3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt @@ -2,8 +2,9 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.common.Provider import com.tangem.domain.tokens.error.TokenListError +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.persistentListOf @@ -14,7 +15,7 @@ internal class TokenListErrorToWalletStateConverter( // TODO: [REDACTED_JIRA] override fun convert(value: TokenListError): WalletStateHolder { val state = currentStateProvider() - return WalletStateHolder.MultiCurrencyContent( + return WalletMultiCurrencyState.Content( onBackClick = state.onBackClick, topBarConfig = state.topBarConfig, walletsListConfig = state.walletsListConfig, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index 114cd724f9..e76e7535f6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -3,8 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState.TokensListItemState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState import com.tangem.feature.wallet.presentation.wallet.utils.LoadingItemsProvider.getLoadingMultiCurrencyTokens import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index e1a7a84ea9..f94b216186 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -4,18 +4,20 @@ import com.tangem.common.Provider import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder.MultiCurrencyContent -import com.tangem.feature.wallet.presentation.wallet.state.WalletsListConfig -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter.TokensListModel import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toPersistentList +@Suppress("LongParameterList") internal class TokenListToWalletStateConverter( private val currentStateProvider: Provider, private val cardTypeResolverProvider: Provider, + private val isLockedWalletProvider: Provider, private val isWalletContentHidden: Boolean, private val fiatCurrencyCode: String, private val fiatCurrencySymbol: String, @@ -43,8 +45,8 @@ internal class TokenListToWalletStateConverter( ) } - private fun WalletStateHolder.updateWithTokenList(tokenList: TokenList): MultiCurrencyContent { - return MultiCurrencyContent( + private fun WalletStateHolder.updateWithTokenList(tokenList: TokenList): WalletMultiCurrencyState.Content { + return WalletMultiCurrencyState.Content( onBackClick = onBackClick, topBarConfig = topBarConfig, walletsListConfig = walletsListConfig, @@ -60,7 +62,7 @@ internal class TokenListToWalletStateConverter( val selectedWalletCard = walletsListConfig.wallets[selectedWalletIndex] val converter = FiatBalanceToWalletCardConverter( currentState = selectedWalletCard, - isLockedState = this is WalletStateHolder.UnlockWalletContent, + isLockedState = isLockedWalletProvider(), cardTypeResolverProvider = cardTypeResolverProvider, isWalletContentHidden = isWalletContentHidden, fiatCurrencyCode = fiatCurrencyCode, @@ -75,7 +77,7 @@ internal class TokenListToWalletStateConverter( } private fun WalletStateHolder.getRefreshingStatus(): Boolean { - return if (this is MultiCurrencyContent) { + return if (this is WalletMultiCurrencyState.Content) { tokensListState.items.any { tokensListItemState -> tokensListItemState is WalletTokensListState.TokensListItemState.Token && tokensListItemState.state is TokenItemState.Loading diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt index 503b697419..89d57acf5b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt @@ -31,4 +31,8 @@ internal interface WalletClickIntents { fun onReloadClick() fun onExploreClick() + + fun onUnlockWalletClick() + + fun onUnlockWalletNotificationClick() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index aace48a09d..a7fa8f46f0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -6,9 +6,10 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.wallet.state.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.content.WalletTokensListState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow @@ -117,7 +118,7 @@ internal class WalletNotificationsListFactory( } return currentStateProvider().let { state -> - state is WalletStateHolder.MultiCurrencyContent && state.tokensListState.items.any(isUnreachableState) + state is WalletMultiCurrencyState.Content && state.tokensListState.items.any(isUnreachableState) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 4c1275a2bd..6d08a629c2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -24,8 +24,10 @@ import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.domain.wallets.usecase.SaveWalletUseCase +import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.state.* +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel @@ -55,6 +57,7 @@ internal class WalletViewModel @Inject constructor( private val txHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase, private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase, + private val unlockWalletsUseCase: UnlockWalletsUseCase, private val dispatchers: CoroutineDispatcherProvider, ) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents { @@ -74,6 +77,7 @@ internal class WalletViewModel @Inject constructor( currentCardTypeResolverProvider = Provider { getCardTypeResolver(index = uiState.walletsListConfig.selectedWalletIndex) }, + isLockedWalletProvider = Provider { wallets[uiState.walletsListConfig.selectedWalletIndex].isLocked }, clickIntents = this, ) @@ -90,24 +94,31 @@ internal class WalletViewModel @Inject constructor( getWalletsUseCase() .flowWithLifecycle(owner.lifecycle) .distinctUntilChanged() - .onEach { wallets -> - if (wallets.isEmpty()) return@onEach - this.wallets = wallets - - uiState = stateFactory.getSkeletonState(wallets = wallets) - - updateContentItems(index = 0) - } + .onEach(::updateWallets) .flowOn(dispatchers.io) .launchIn(viewModelScope) } + private fun updateWallets(sourceList: List) { + if (sourceList.isEmpty() || sourceList.all(UserWallet::isLocked)) return + + wallets = sourceList + + val unlockedWalletIndex = sourceList.indexOfFirst { !it.isLocked } + uiState = stateFactory.getSkeletonState( + wallets = wallets, + index = if (unlockedWalletIndex == -1) 0 else unlockedWalletIndex, + ) + + updateContentItems(index = unlockedWalletIndex) + } + private fun updateContentItems(index: Int, isRefreshing: Boolean = false) { val cardTypeResolver = getCardTypeResolver(index) - if (cardTypeResolver.isMultiwalletAllowed()) { - updateByTokensList(index, isRefreshing) - } else { - updateByTxHistory(index) + when { + getWallet(index).isLocked -> uiState = stateFactory.getLockedState() + cardTypeResolver.isMultiwalletAllowed() -> updateByTokensList(index, isRefreshing) + !cardTypeResolver.isMultiwalletAllowed() -> updateByTxHistory(index) } } @@ -120,7 +131,10 @@ internal class WalletViewModel @Inject constructor( isRefreshing = isRefreshing, ) - tokenListEither.onRight { updateNotifications(index = index, tokenList = it) } + updateNotifications( + index = index, + tokenList = tokenListEither.fold(ifLeft = { null }, ifRight = { it }), + ) } .flowOn(dispatchers.io) .launchIn(viewModelScope) @@ -278,4 +292,16 @@ internal class WalletViewModel @Inject constructor( ) } } + + override fun onUnlockWalletClick() { + viewModelScope.launch(dispatchers.io) { + unlockWalletsUseCase() + } + } + + override fun onUnlockWalletNotificationClick() { + uiState = stateFactory.getStateWithOpenBottomSheet( + content = requireNotNull(uiState.bottomSheetConfig?.content), + ) + } } \ No newline at end of file From 3f5f3ef68be274c01a71de3ff4c8b2bec3edd7ea Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 7 Aug 2023 13:01:47 +0300 Subject: [PATCH 06/52] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 16 +-- .../tangem/data/tokens/di/TokensDataModule.kt | 10 +- ...tory.kt => DefaultCurrenciesRepository.kt} | 129 ++++++++++++------ .../repository/DefaultNetworksRepository.kt | 11 +- .../data/tokens/utils/NetworkStatusFactory.kt | 2 +- .../tokens/utils/ResponseCurrenciesFactory.kt | 18 ++- .../data/tokens/utils/TokensOperations.kt | 11 +- .../tokens/utils/UserTokensResponseFactory.kt | 2 +- .../com/tangem/domain/core/error/DataError.kt | 5 + .../tokens/ApplyTokenListSortingUseCase.kt | 10 +- .../domain/tokens/GetCurrencyUseCase.kt | 82 +++++++++++ .../tokens/GetPrimaryCurrencyUseCase.kt | 31 +++-- .../domain/tokens/GetTokenListUseCase.kt | 6 +- .../domain/tokens/error/CurrencyError.kt | 8 ++ .../tangem/domain/tokens/error/TokenError.kt | 8 -- .../mapper/GetWalletTokenErrorMappers.kt | 8 +- .../CurrenciesStatusesOperations.kt | 40 ++++-- .../tokens/operations/TokenListOperations.kt | 10 +- ...sRepository.kt => CurrenciesRepository.kt} | 25 +++- .../ApplyTokenListSortingUseCaseTest.kt | 10 +- .../tokens/GetPrimaryCurrencyUseCaseTest.kt | 16 +-- .../domain/tokens/GetTokenListUseCaseTest.kt | 4 +- ...ository.kt => MockCurrenciesRepository.kt} | 17 ++- 23 files changed, 351 insertions(+), 128 deletions(-) rename data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/{DefaultTokensRepository.kt => DefaultCurrenciesRepository.kt} (55%) create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt create mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt delete mode 100644 domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt rename domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/{TokensRepository.kt => CurrenciesRepository.kt} (63%) rename domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/{MockTokensRepository.kt => MockCurrenciesRepository.kt} (81%) diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 8fc08144df..274f5bbe46 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -1,9 +1,9 @@ package com.tangem.tap.di.domain import com.tangem.domain.tokens.* +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -18,23 +18,23 @@ internal object TokensDomainModule { @Provides @ViewModelScoped fun provideGetTokenListUseCase( - tokensRepository: TokensRepository, + currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, ): GetTokenListUseCase { - return GetTokenListUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers) + return GetTokenListUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) } @Provides @ViewModelScoped fun provideGetPrimaryCurrencyUseCase( - tokensRepository: TokensRepository, + currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, networksRepository: NetworksRepository, dispatchers: CoroutineDispatcherProvider, - ): GetPrimaryCurrencyUseCase { - return GetPrimaryCurrencyUseCase(tokensRepository, quotesRepository, networksRepository, dispatchers) + ): GetCurrencyUseCase { + return GetCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) } @Provides @@ -55,9 +55,9 @@ internal object TokensDomainModule { @Provides @ViewModelScoped fun provideApplyTokenListSortingUseCase( - tokensRepository: TokensRepository, + currenciesRepository: CurrenciesRepository, dispatchers: CoroutineDispatcherProvider, ): ApplyTokenListSortingUseCase { - return ApplyTokenListSortingUseCase(tokensRepository, dispatchers) + return ApplyTokenListSortingUseCase(currenciesRepository, dispatchers) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index 25f7f9defd..a5adf06ad8 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -1,15 +1,15 @@ package com.tangem.data.tokens.di import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.repository.DefaultCurrenciesRepository import com.tangem.data.tokens.repository.DefaultNetworksRepository -import com.tangem.data.tokens.repository.DefaultTokensRepository import com.tangem.data.tokens.repository.MockQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module @@ -24,14 +24,14 @@ internal object TokensDataModule { @Provides @Singleton - fun provideTokensRepository( + fun provideCurrenciesRepository( tangemTechApi: TangemTechApi, userTokensStore: UserTokensStore, userWalletsStore: UserWalletsStore, cacheRegistry: CacheRegistry, dispatchers: CoroutineDispatcherProvider, - ): TokensRepository { - return DefaultTokensRepository(tangemTechApi, userTokensStore, userWalletsStore, cacheRegistry, dispatchers) + ): CurrenciesRepository { + return DefaultCurrenciesRepository(tangemTechApi, userTokensStore, userWalletsStore, cacheRegistry, dispatchers) } @Provides diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt similarity index 55% rename from data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt rename to data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 704e25fa68..21c8020e2d 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultTokensRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -8,23 +8,27 @@ import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.core.error.DataError import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.repository.TokensRepository +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import timber.log.Timber -internal class DefaultTokensRepository( +internal class DefaultCurrenciesRepository( private val tangemTechApi: TangemTechApi, private val userTokensStore: UserTokensStore, private val userWalletsStore: UserWalletsStore, private val cacheRegistry: CacheRegistry, private val dispatchers: CoroutineDispatcherProvider, -) : TokensRepository { +) : CurrenciesRepository { private val demoConfig = DemoConfig() private val responseCurrenciesFactory = ResponseCurrenciesFactory(demoConfig) @@ -37,6 +41,8 @@ internal class DefaultTokensRepository( isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { + ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true) + val response = userTokensResponseFactory.createUserTokensResponse( currencies = currencies, isGroupedByNetwork = isGroupedByNetwork, @@ -46,58 +52,72 @@ internal class DefaultTokensRepository( storeAndPushTokens(userWalletId, response) } - override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { - val userWallet = withContext(dispatchers.io) { - requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { - "Unable to find a user wallet with provided ID: $userWalletId" - } - } - require(!userWallet.isMultiCurrency) { - "Single currency wallet excepted, but multi currency wallet was found: $userWalletId" - } + override suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { + return withContext(dispatchers.io) { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = false) - return cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) + cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) + } } override fun getMultiCurrencyWalletCurrencies( userWalletId: UserWalletId, refresh: Boolean, - ): Flow> { + ): Flow> = channelFlow { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) + + launch(dispatchers.io) { + getMultiCurrencyWalletCurrencies(userWallet).collect(::send) + } + + launch(dispatchers.io) { + fetchTokensIfCacheExpired(userWallet, refresh) + } + } + + override suspend fun getMultiCurrencyWalletCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency = withContext(dispatchers.io) { + val userWallet = getUserWallet(userWalletId) + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) + + val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { + "Unable to find tokens response for user wallet with provided ID: $userWalletId" + } + + responseCurrenciesFactory.createCurrency(id, response, userWallet.scanResponse.card) + } + + override fun isTokensGrouped(userWalletId: UserWalletId): Flow { return channelFlow { - val userWallet = withContext(dispatchers.io) { - requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { - "Unable to find a user wallet with provided ID: $userWalletId" - } - } - require(userWallet.isMultiCurrency) { - "Multi currency wallet excepted, but single currency wallet was found: $userWalletId" - } + ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true) launch(dispatchers.io) { - getMultiCurrencyWalletCurrencies(userWallet).collectLatest(::send) - } - - launch(dispatchers.io) { - fetchTokensIfCacheExpired(userWallet, refresh) + userTokensStore.get(userWalletId) + .map { it.group == UserTokensResponse.GroupType.NETWORK } + .collect(::send) } } } - override fun isTokensGrouped(userWalletId: UserWalletId): Flow { - return userTokensStore.get(userWalletId) - .map { it.group == UserTokensResponse.GroupType.NETWORK } - .flowOn(dispatchers.io) - } - override fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow { - return userTokensStore.get(userWalletId) - .map { it.sort == UserTokensResponse.SortType.BALANCE } - .flowOn(dispatchers.io) + return channelFlow { + ensureIsCorrectUserWallet(userWalletId, isMultiCurrencyWalletExpected = true) + + launch(dispatchers.io) { + userTokensStore.get(userWalletId) + .map { it.sort == UserTokensResponse.SortType.BALANCE } + .collect(::send) + } + } } private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow> { return userTokensStore.get(userWallet.walletId).map { storedTokens -> - responseCurrenciesFactory.createTokens( + responseCurrenciesFactory.createCurrencies( response = storedTokens, card = userWallet.scanResponse.card, ) @@ -146,6 +166,39 @@ internal class DefaultTokensRepository( } } + private suspend fun getUserWallet(userWalletId: UserWalletId): UserWallet { + return requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "Unable to find a user wallet with provided ID: $userWalletId" + } + } + + private suspend fun ensureIsCorrectUserWallet(userWalletId: UserWalletId, isMultiCurrencyWalletExpected: Boolean) { + val userWallet = getUserWallet(userWalletId) + + ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected) + } + + private fun ensureIsCorrectUserWallet(userWallet: UserWallet, isMultiCurrencyWalletExpected: Boolean) { + val userWalletId = userWallet.walletId + + val message = when { + !userWallet.isMultiCurrency && isMultiCurrencyWalletExpected -> { + "Multi currency wallet expected, but single currency wallet was found: $userWalletId" + } + userWallet.isMultiCurrency && !isMultiCurrencyWalletExpected -> { + "Single currency wallet expected, but multi currency wallet was found: $userWalletId" + } + else -> null + } + + if (message != null) { + val error = DataError.UserWalletError.WrongUserWallet(message) + + Timber.e(error) + throw error + } + } + private fun getTokensCacheKey(userWalletId: UserWalletId): String = "tokens_cache_key_${userWalletId.stringValue}" private companion object { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index dbde700089..bf4206d543 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -18,7 +18,10 @@ import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch internal class DefaultNetworksRepository( @@ -44,7 +47,9 @@ internal class DefaultNetworksRepository( networks: Set, refresh: Boolean, ): Flow> = channelFlow { - networksStatuses.collectLatest(::send) + launch(dispatchers.io) { + networksStatuses.collect(::send) + } launch(dispatchers.io) { fetchNetworksStatusesIfCacheExpired(userWalletId, networks, refresh) @@ -99,7 +104,7 @@ internal class DefaultNetworksRepository( "Unable to find tokens response for user wallet with provided ID: $userWalletId" } - return responseCurrenciesFactory.createTokens(response, userWallet.scanResponse.card) + return responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse.card) } private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId): String = "network_status_$userWalletId" diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index d27da42e10..f76f363033 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -40,7 +40,7 @@ internal class NetworkStatusFactory { is CryptoCurrency.Coin -> amounts.singleOrNull { it is CryptoCurrencyAmount.Coin } is CryptoCurrency.Token -> amounts.singleOrNull { it is CryptoCurrencyAmount.Token && - it.id == getTokenIdString(currency) && + it.id == getTokenIdString(currency.id) && it.tokenContractAddress == currency.contractAddress } }?.value diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt index 0a6c78b0e5..cbecf6a792 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -12,11 +12,23 @@ import com.tangem.blockchain.common.Token as SdkToken internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { - fun createTokens(response: UserTokensResponse, card: CardDTO): Set { - return response.tokens.mapNotNull { createToken(it, card) }.toSet() + fun createCurrency(currencyId: CryptoCurrency.ID, response: UserTokensResponse, card: CardDTO): CryptoCurrency { + val responseTokenId = getTokenIdString(currencyId) + + val token = requireNotNull(response.tokens.firstOrNull { it.id == responseTokenId }) { + "Unable find a token with provided ID: $responseTokenId" + } + + return requireNotNull(createCurrency(token, card)) { + "Unable to create a currency with provided ID: $currencyId" + } } - private fun createToken(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? { + fun createCurrencies(response: UserTokensResponse, card: CardDTO): Set { + return response.tokens.mapNotNull { createCurrency(it, card) }.toSet() + } + + private fun createCurrency(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? { var blockchain = Blockchain.fromNetworkId(responseToken.networkId) if (blockchain == null || blockchain == Blockchain.Unknown) { Timber.e("Unable to find a blockchain with the network ID: ${responseToken.networkId}") diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index 1ff2268f53..e7704bdc9e 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -49,9 +49,14 @@ internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency return getTokenOrCoinId(blockchain, token) } -internal fun getTokenIdString(currency: CryptoCurrency): String? { - return currency.id.value.substringAfter(TOKEN_ID_DELIMITER) - .takeUnless { currency is CryptoCurrency.Token && currency.isCustom } +internal fun getTokenIdString(currencyId: CryptoCurrency.ID): String? { + val idValue = currencyId.value + + return if (idValue.startsWith(CUSTOM_TOKEN_ID_PREFIX)) { + null + } else { + idValue.substringAfter(TOKEN_ID_DELIMITER) + } } internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index 898c62c8b3..437100ce08 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -30,7 +30,7 @@ internal class UserTokensResponseFactory { val blockchain = getBlockchain(currency.networkId) return UserTokensResponse.Token( - id = getTokenIdString(currency), + id = getTokenIdString(currency.id), networkId = blockchain.toNetworkId(), derivationPath = currency.derivationPath, name = currency.name, diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt index 11567ca43b..35c115a1e8 100644 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt +++ b/domain/core/src/main/kotlin/com/tangem/domain/core/error/DataError.kt @@ -6,4 +6,9 @@ sealed class DataError : Exception() { object NoInternetConnection : NetworkError() } + + sealed class UserWalletError : DataError() { + + data class WrongUserWallet(override val message: String) : UserWalletError() + } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 89d9776a18..907fae6af5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -9,14 +9,14 @@ import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.models.Network -import com.tangem.domain.tokens.repository.TokensRepository +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.withContext class ApplyTokenListSortingUseCase( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val dispatchers: CoroutineDispatcherProvider, ) { @@ -67,7 +67,9 @@ class ApplyTokenListSortingUseCase( private suspend fun Raise.getCurrencies(userWalletId: UserWalletId): Set { val tokens = catch( - block = { tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() }, + block = { + currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() + }, catch = { raise(TokenListSortingError.DataError(it)) }, ) @@ -83,7 +85,7 @@ class ApplyTokenListSortingUseCase( isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { catch( - block = { tokensRepository.saveTokens(userWalletId, tokens, isGrouped, isSortedByBalance) }, + block = { currenciesRepository.saveTokens(userWalletId, tokens, isGrouped, isSortedByBalance) }, catch = { raise(TokenListSortingError.DataError(it)) }, ) } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt new file mode 100644 index 0000000000..f49378314a --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt @@ -0,0 +1,82 @@ +package com.tangem.domain.tokens + +import arrow.core.Either +import arrow.core.left +import arrow.core.raise.Raise +import arrow.core.raise.recover +import arrow.core.right +import com.tangem.domain.tokens.error.CurrencyError +import com.tangem.domain.tokens.error.mapper.mapToTokenError +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations +import com.tangem.domain.tokens.repository.CurrenciesRepository +import com.tangem.domain.tokens.repository.NetworksRepository +import com.tangem.domain.tokens.repository.QuotesRepository +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.collectLatest + +/** + * Use case for fetching the status of a specific cryptocurrency associated with a user wallet. + * + * @property currenciesRepository Repository for managing and fetching cryptocurrencies. + * @property quotesRepository Repository for managing and fetching cryptocurrency quotes. + * @property networksRepository Repository for managing and fetching information related to blockchain networks. + * @property dispatchers Provides coroutine dispatchers. + */ +class GetCurrencyUseCase( + private val currenciesRepository: CurrenciesRepository, + private val quotesRepository: QuotesRepository, + private val networksRepository: NetworksRepository, + private val dispatchers: CoroutineDispatcherProvider, +) { + + /** + * Invokes the use case. + * + * @param userWalletId The unique identifier of the user's wallet. + * @param currencyId The unique identifier of the cryptocurrency. + * @param refresh A boolean flag indicating whether the data should be refreshed. + * @return A [Flow] emitting either a [CurrencyError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation. + */ + operator fun invoke( + userWalletId: UserWalletId, + currencyId: CryptoCurrency.ID, + refresh: Boolean = false, + ): Flow> { + return channelFlow { + recover( + block = { + getCurrency(userWalletId, currencyId, refresh).collectLatest { currencyStatus -> + send(currencyStatus.right()) + } + }, + recover = { error -> + send(error.left()) + }, + ) + } + } + + private suspend fun Raise.getCurrency( + userWalletId: UserWalletId, + currencyId: CryptoCurrency.ID, + refresh: Boolean, + ): Flow { + val operations = CurrenciesStatusesOperations( + currenciesRepository = currenciesRepository, + quotesRepository = quotesRepository, + networksRepository = networksRepository, + userWalletId = userWalletId, + refresh = refresh, + dispatchers = dispatchers, + raise = this, + transformError = CurrenciesStatusesOperations.Error::mapToTokenError, + ) + + return operations.getCurrencyStatusFlow(currencyId) + } +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt index bfc25b58da..3d0f9c2011 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt @@ -5,35 +5,50 @@ import arrow.core.left import arrow.core.raise.Raise import arrow.core.raise.recover import arrow.core.right -import com.tangem.domain.tokens.error.TokenError +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.error.mapper.mapToTokenError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import kotlinx.coroutines.flow.collectLatest +/** + * Use case for fetching the status of the primary cryptocurrency associated with a user wallet. + * + * @property currenciesRepository Repository for managing and fetching cryptocurrencies. + * @property quotesRepository Repository for managing and fetching cryptocurrency quotes. + * @property networksRepository Repository for managing and fetching information related to blockchain networks. + * @property dispatchers Provides coroutine dispatchers. + */ class GetPrimaryCurrencyUseCase( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val dispatchers: CoroutineDispatcherProvider, ) { + /** + * Invokes the use case. + * + * @param userWalletId The unique identifier of the user's wallet. + * @param refresh A boolean flag indicating whether the data should be refreshed. + * @return A [Flow] emitting either a [CurrencyError] or a [CryptoCurrencyStatus], indicating the result of the fetch operation. + */ operator fun invoke( userWalletId: UserWalletId, refresh: Boolean = false, - ): Flow> { + ): Flow> { return channelFlow { recover( block = { - getToken(userWalletId, refresh).collectLatest { token -> - send(token.right()) + getCurrency(userWalletId, refresh).collectLatest { currencyStatus -> + send(currencyStatus.right()) } }, recover = { error -> @@ -43,12 +58,12 @@ class GetPrimaryCurrencyUseCase( } } - private suspend fun Raise.getToken( + private suspend fun Raise.getCurrency( userWalletId: UserWalletId, refresh: Boolean, ): Flow { val operations = CurrenciesStatusesOperations( - tokensRepository = tokensRepository, + currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, userWalletId = userWalletId, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index 0a142e1241..ea19873b22 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -11,9 +11,9 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.operations.TokenListOperations +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow @@ -22,7 +22,7 @@ import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.flatMapConcat class GetTokenListUseCase( - internal val tokensRepository: TokensRepository, + internal val currenciesRepository: CurrenciesRepository, internal val quotesRepository: QuotesRepository, internal val networksRepository: NetworksRepository, internal val dispatchers: CoroutineDispatcherProvider, @@ -60,7 +60,7 @@ class GetTokenListUseCase( transformError = CurrenciesStatusesOperations.Error::mapToTokenListError, ) - return operations.getMultiCurrencyWalletStatusesFlow() + return operations.getCurrenciesStatusesFlow() } private fun Raise.createTokenList( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt new file mode 100644 index 0000000000..5d1ab7e2d8 --- /dev/null +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/CurrencyError.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.tokens.error + +sealed class CurrencyError { + + object UnableToCreateCurrency : CurrencyError() + + data class DataError(val cause: Throwable) : CurrencyError() +} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt deleted file mode 100644 index b8b3e0631b..0000000000 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/TokenError.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.domain.tokens.error - -sealed class TokenError { - - object UnableToCreateToken : TokenError() - - data class DataError(val cause: Throwable) : TokenError() -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt index e129b9fc6f..981f8785f2 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt @@ -1,15 +1,15 @@ package com.tangem.domain.tokens.error.mapper -import com.tangem.domain.tokens.error.TokenError +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): TokenError { +internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): CurrencyError { return when (this) { - is CurrenciesStatusesOperations.Error.DataError -> TokenError.DataError(this.cause) + is CurrenciesStatusesOperations.Error.DataError -> CurrencyError.DataError(this.cause) is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses, is CurrenciesStatusesOperations.Error.EmptyQuotes, is CurrenciesStatusesOperations.Error.EmptyCurrencies, is CurrenciesStatusesOperations.Error.UnableToCreateCurrencyStatus, - -> TokenError.UnableToCreateToken + -> CurrencyError.UnableToCreateCurrency } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 7b49295f4a..1b23b9910c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -7,9 +7,9 @@ import com.tangem.domain.core.raise.DelegatedRaise import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* @@ -17,7 +17,7 @@ import kotlinx.coroutines.withContext @Suppress("LongParameterList") internal class CurrenciesStatusesOperations( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, @@ -34,7 +34,7 @@ internal class CurrenciesStatusesOperations( raise: Raise, transformError: (Error) -> E, ) : this( - tokensRepository = useCase.tokensRepository, + currenciesRepository = useCase.currenciesRepository, quotesRepository = useCase.quotesRepository, networksRepository = useCase.networksRepository, userWalletId = userWalletId, @@ -44,7 +44,7 @@ internal class CurrenciesStatusesOperations( transformError = transformError, ) - fun getMultiCurrencyWalletStatusesFlow(): Flow> { + fun getCurrenciesStatusesFlow(): Flow> { return getMultiCurrencyWalletCurrencies().flatMapConcat { val currencies = it.toNonEmptySetOrNull() @@ -68,9 +68,19 @@ internal class CurrenciesStatusesOperations( } } + suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow { + val currency = getMultiCurrencyWalletCurrency(currencyId) + + return getCurrencyStatusFlow(currency) + } + suspend fun getPrimaryCurrencyStatusFlow(): Flow { val currency = getPrimaryCurrency() + return getCurrencyStatusFlow(currency) + } + + private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow { val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) .map { quotes -> quotes.singleOrNull { it.currencyId == currency.id } @@ -116,34 +126,36 @@ internal class CurrenciesStatusesOperations( return currencyStatusOperations.createTokenStatus() } + private suspend fun getMultiCurrencyWalletCurrency(currencyId: CryptoCurrency.ID): CryptoCurrency { + return catch( + block = { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, currencyId) }, + catch = { raise(Error.DataError(it)) }, + ) + } + private fun getMultiCurrencyWalletCurrencies(): Flow> { - return tokensRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) + return currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyCurrencies) } - .flowOn(dispatchers.io) } private suspend fun getPrimaryCurrency(): CryptoCurrency { - return withContext(dispatchers.io) { - catch( - block = { tokensRepository.getPrimaryCurrency(userWalletId) }, - catch = { raise(Error.DataError(it)) }, - ) - } + return catch( + block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }, + catch = { raise(Error.DataError(it)) }, + ) } private fun getQuotes(tokensIds: NonEmptySet): Flow> { return quotesRepository.getQuotes(tokensIds, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyQuotes) } - .flowOn(dispatchers.io) } private fun getNetworksStatues(networks: NonEmptySet): Flow> { return networksRepository.getNetworkStatuses(userWalletId, networks, refresh) .catch { raise(Error.DataError(it)) } .onEmpty { raise(Error.EmptyNetworksStatuses) } - .flowOn(dispatchers.io) } sealed class Error { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index 37b96a781c..da6c780e24 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -10,8 +10,8 @@ import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository -import com.tangem.domain.tokens.repository.TokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* @@ -19,7 +19,7 @@ import kotlinx.coroutines.withContext @Suppress("LongParameterList") internal class TokenListOperations( - private val tokensRepository: TokensRepository, + private val currenciesRepository: CurrenciesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, private val tokens: Set, @@ -35,7 +35,7 @@ internal class TokenListOperations( raise: Raise, transform: (Error) -> E, ) : this( - tokensRepository = useCase.tokensRepository, + currenciesRepository = useCase.currenciesRepository, networksRepository = useCase.networksRepository, userWalletId = userWalletId, tokens = tokens, @@ -149,14 +149,14 @@ internal class TokenListOperations( } private fun getIsGrouped(): Flow { - return tokensRepository.isTokensGrouped(userWalletId) + return currenciesRepository.isTokensGrouped(userWalletId) .catch { raise(Error.DataError(it)) } .onEmpty { emit(value = false) } .flowOn(dispatchers.io) } private fun getIsSortedByBalance(): Flow { - return tokensRepository.isTokensSortedByBalance(userWalletId) + return currenciesRepository.isTokensSortedByBalance(userWalletId) .catch { raise(Error.DataError(it)) } .onEmpty { emit(value = false) } .flowOn(dispatchers.io) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt similarity index 63% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt rename to domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 865c5fc4da..8b5e0fe9e5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/TokensRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -7,7 +7,7 @@ import kotlinx.coroutines.flow.Flow /** * Repository for everything related to the tokens of user wallet * */ -interface TokensRepository { +interface CurrenciesRepository { /** * Saves the given set of cryptocurrencies, along with the preferences for grouping and sorting, for a specific @@ -17,6 +17,8 @@ interface TokensRepository { * @param currencies The set of cryptocurrencies to be saved. * @param isGroupedByNetwork A boolean flag indicating whether the tokens should be grouped by network. * @param isSortedByBalance A boolean flag indicating whether the tokens should be sorted by balance. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ suspend fun saveTokens( userWalletId: UserWalletId, @@ -30,8 +32,10 @@ interface TokensRepository { * * @param userWalletId The unique identifier of the user wallet. * @return The primary cryptocurrency associated with the user wallet. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If multi-currency user wallet + * ID provided. */ - suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency + suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency /** * Retrieves the set of cryptocurrencies within a multi-currency wallet. @@ -39,14 +43,29 @@ interface TokensRepository { * @param userWalletId The unique identifier of the user wallet. * @param refresh A boolean flag indicating whether the data should be refreshed. * @return A [Flow] emitting the set of cryptocurrencies associated with the user wallet. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow> + /** + * Retrieves the cryptocurrency for a specific multi-currency user wallet. + * + * @param userWalletId The unique identifier of the user wallet. + * @param id The unique identifier of the cryptocurrency to be retrieved. + * @return The cryptocurrency associated with the user wallet and ID. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. + */ + suspend fun getMultiCurrencyWalletCurrency(userWalletId: UserWalletId, id: CryptoCurrency.ID): CryptoCurrency + /** * Determines whether the tokens within a specific multi-currency user wallet are grouped. * * @param userWalletId The unique identifier of the user wallet. * @return A [Flow] emitting a boolean value indicating whether the tokens are grouped. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ fun isTokensGrouped(userWalletId: UserWalletId): Flow @@ -55,6 +74,8 @@ interface TokensRepository { * * @param userWalletId The unique identifier of the user wallet. * @return A [Flow] emitting a boolean value indicating whether the tokens are sorted by balance. + * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet + * ID provided. */ fun isTokensSortedByBalance(userWalletId: UserWalletId): Flow } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index 0bdf923b6b..16ee99fff2 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -7,7 +7,7 @@ import com.tangem.domain.core.error.DataError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.mock.MockTokens import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.repository.MockTokensRepository +import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals @@ -183,16 +183,16 @@ internal class ApplyTokenListSortingUseCaseTest { .sortedBy { Random.nextInt(0, MockTokens.tokens.size) } .toSet() - private fun getUseCase(tokensRepository: MockTokensRepository = getTokensRepository()) = + private fun getUseCase(tokensRepository: MockCurrenciesRepository = getTokensRepository()) = ApplyTokenListSortingUseCase( - tokensRepository = tokensRepository, + currenciesRepository = tokensRepository, dispatchers = TestingCoroutineDispatcherProvider(), ) private fun getTokensRepository( sortTokensResult: Either = Unit.right(), tokens: Flow>> = flowOf(MockTokens.tokens.right()), - ): MockTokensRepository { - return MockTokensRepository(sortTokensResult, MockTokens.token1.right(), tokens, emptyFlow(), emptyFlow()) + ): MockCurrenciesRepository { + return MockCurrenciesRepository(sortTokensResult, MockTokens.token1.right(), tokens, emptyFlow(), emptyFlow()) } } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt index 95026c6926..c524f8d3ba 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt @@ -4,7 +4,7 @@ import arrow.core.Either import arrow.core.left import arrow.core.right import com.tangem.domain.core.error.DataError -import com.tangem.domain.tokens.error.TokenError +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.mock.MockNetworks import com.tangem.domain.tokens.mock.MockQuotes import com.tangem.domain.tokens.mock.MockTokens @@ -13,9 +13,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository -import com.tangem.domain.tokens.repository.MockTokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals @@ -47,7 +47,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when token getting failed then error should be received`() = runTest { // Given - val expectedResult = TokenError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() val useCase = getUseCase(token = DataError.NetworkError.NoInternetConnection.left()) @@ -61,7 +61,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when quotes getting failed then error should be received`() = runTest { // Given - val expectedResult = TokenError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() val useCase = getUseCase(quotes = flowOf(DataError.NetworkError.NoInternetConnection.left())) @@ -75,7 +75,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when networks statuses getting failed then error should be received`() = runTest { // Given - val expectedResult = TokenError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() val useCase = getUseCase(statuses = flowOf(DataError.NetworkError.NoInternetConnection.left())) @@ -88,7 +88,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when networks statuses flow is empty then error should be received`() = runTest { - val expectedResult = TokenError.UnableToCreateToken.left() + val expectedResult = CurrencyError.UnableToCreateCurrency.left() val useCase = getUseCase(statuses = flowOf()) @@ -101,7 +101,7 @@ internal class GetPrimaryCurrencyUseCaseTest { @Test fun `when quotes flow is empty then error should be received`() = runTest { - val expectedResult = TokenError.UnableToCreateToken.left() + val expectedResult = CurrencyError.UnableToCreateCurrency.left() val useCase = getUseCase(quotes = flowOf()) @@ -154,7 +154,7 @@ internal class GetPrimaryCurrencyUseCaseTest { statuses: Flow>> = flowOf(MockNetworks.verifiedNetworksStatuses.right()), ) = GetPrimaryCurrencyUseCase( dispatchers = dispatchers, - tokensRepository = MockTokensRepository( + currenciesRepository = MockCurrenciesRepository( sortTokensResult = Unit.right(), token = token, tokens = flowOf(), diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index 204bf4af9c..1d5cfd09c5 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -13,9 +13,9 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository -import com.tangem.domain.tokens.repository.MockTokensRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals @@ -326,7 +326,7 @@ internal class GetTokenListUseCaseTest { isSortedByBalance: Flow> = flowOf(MockTokenLists.isSortedByBalance.right()), ) = GetTokenListUseCase( dispatchers = dispatchers, - tokensRepository = MockTokensRepository( + currenciesRepository = MockCurrenciesRepository( sortTokensResult = Unit.right(), token = MockTokens.token1.right(), tokens = tokens, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt similarity index 81% rename from domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt rename to domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index ad5d4e0851..8ebf978969 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockTokensRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -8,13 +8,13 @@ import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map -internal class MockTokensRepository( +internal class MockCurrenciesRepository( private val sortTokensResult: Either, private val token: Either, private val tokens: Flow>>, private val isGrouped: Flow>, private val isSortedByBalance: Flow>, -) : TokensRepository { +) : CurrenciesRepository { var tokensIdsAfterSortingApply: Set? = null private set @@ -38,7 +38,7 @@ internal class MockTokensRepository( isTokensSortedByBalanceAfterSortingApply = isSortedByBalance } - override suspend fun getPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { + override suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency { return token.getOrElse { e -> throw e } } @@ -49,6 +49,17 @@ internal class MockTokensRepository( return tokens.map { it.getOrElse { e -> throw e } } } + override suspend fun getMultiCurrencyWalletCurrency( + userWalletId: UserWalletId, + id: CryptoCurrency.ID, + ): CryptoCurrency { + val token = token.getOrElse { e -> throw e } + + require(token.id == id) + + return token + } + override fun isTokensGrouped(userWalletId: UserWalletId): Flow { return isGrouped.map { it.getOrElse { e -> throw e } } } From d1a09370c7b81b4316857aa18eb2ae9e66eef221 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 13:09:47 +0500 Subject: [PATCH 07/52] Updated on 2026-08-14 --- .../tap/data/RuntimeUserWalletsStore.kt | 3 + .../tangem/tap/proxy/TxHistoryManagerImpl.kt | 86 ------------------- .../com/tangem/tap/proxy/di/ProxyModule.kt | 7 -- .../local/userwallet/UserWalletsStore.kt | 2 + data/txhistory/build.gradle.kts | 6 ++ .../data/txhistory/di/TxHistoryDataModule.kt | 12 ++- .../data/txhistory/mock/MockTxHistoryItems.kt | 70 --------------- .../repository/DefaultTxHistoryRepository.kt | 66 ++++++++++++++ .../repository/MockTxHistoryRepository.kt | 25 ------ .../paging/TxHistoryPagingSource.kt | 22 ++--- domain/legacy/build.gradle.kts | 1 + .../DefaultWalletManagersFacade.kt | 69 +++++++++++++-- .../walletmanager/WalletManagersFacade.kt | 34 ++++++++ .../SdkTransactionHistoryItemConverter.kt | 29 +++++++ .../SdkTransactionHistoryStateConverter.kt | 16 ++++ domain/txhistory/build.gradle.kts | 2 + domain/txhistory/models/.gitignore | 1 + domain/txhistory/models/build.gradle.kts | 4 + .../txhistory/models/PaginationWrapper.kt | 8 ++ .../domain/txhistory/models}/TxHistoryItem.kt | 4 +- .../txhistory/models}/TxHistoryListError.kt | 2 +- .../domain/txhistory/models/TxHistoryState.kt | 15 ++++ .../txhistory/models}/TxHistoryStateError.kt | 2 +- .../repository/TxHistoryRepository.kt | 15 ++-- .../usecase/GetTxHistoryItemsCountUseCase.kt | 5 +- .../usecase/GetTxHistoryItemsUseCase.kt | 10 ++- features/tokendetails/impl/build.gradle.kts | 4 + features/wallet/impl/build.gradle.kts | 1 + .../state/factory/WalletStateFactory.kt | 6 +- .../WalletLoadedTxHistoryConverter.kt | 4 +- .../WalletLoadingTxHistoryConverter.kt | 2 +- .../WalletTxHistoryItemFlowConverter.kt | 4 +- .../wallet/viewmodels/WalletViewModel.kt | 21 +++-- gradle/dependencies.toml | 2 +- .../com/tangem/lib/crypto/TxHistoryManager.kt | 18 ---- .../txhistory/ProxyTransactionHistoryItem.kt | 21 ----- .../txhistory/ProxyTransactionHistoryState.kt | 15 ---- .../txhistory/ProxyTransactionStatus.kt | 3 - settings.gradle.kts | 1 + 39 files changed, 317 insertions(+), 301 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/proxy/TxHistoryManagerImpl.kt delete mode 100644 data/txhistory/src/main/kotlin/com/tangem/data/txhistory/mock/MockTxHistoryItems.kt create mode 100644 data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt delete mode 100644 data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/MockTxHistoryRepository.kt create mode 100644 domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt create mode 100644 domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt create mode 100644 domain/txhistory/models/.gitignore create mode 100644 domain/txhistory/models/build.gradle.kts create mode 100644 domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt rename domain/txhistory/{src/main/kotlin/com/tangem/domain/txhistory/model => models/src/main/kotlin/com/tangem/domain/txhistory/models}/TxHistoryItem.kt (87%) rename domain/txhistory/{src/main/kotlin/com/tangem/domain/txhistory/error => models/src/main/kotlin/com/tangem/domain/txhistory/models}/TxHistoryListError.kt (75%) create mode 100644 domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryState.kt rename domain/txhistory/{src/main/kotlin/com/tangem/domain/txhistory/error => models/src/main/kotlin/com/tangem/domain/txhistory/models}/TxHistoryStateError.kt (85%) delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/TxHistoryManager.kt delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryItem.kt delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryState.kt delete mode 100644 libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionStatus.kt diff --git a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt index d46dfadcad..a84ddc77c6 100644 --- a/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt +++ b/app/src/main/java/com/tangem/tap/data/RuntimeUserWalletsStore.kt @@ -12,6 +12,9 @@ internal class RuntimeUserWalletsStore( private val walletsStateHolder: WalletsStateHolder, ) : UserWalletsStore { + override val selectedUserWalletOrNull: UserWallet? + get() = walletsStateHolder.userWalletsListManager?.selectedUserWalletSync + override suspend fun getSyncOrNull(key: UserWalletId): UserWallet? { return walletsStateHolder.userWalletsListManager ?.userWallets diff --git a/app/src/main/java/com/tangem/tap/proxy/TxHistoryManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/TxHistoryManagerImpl.kt deleted file mode 100644 index ff8b430a00..0000000000 --- a/app/src/main/java/com/tangem/tap/proxy/TxHistoryManagerImpl.kt +++ /dev/null @@ -1,86 +0,0 @@ -package com.tangem.tap.proxy - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.TransactionStatus -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.txhistory.TransactionHistoryItem -import com.tangem.blockchain.common.txhistory.TransactionHistoryState -import com.tangem.blockchain.extensions.Result -import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.extensions.fromNetworkId -import com.tangem.lib.crypto.TxHistoryManager -import com.tangem.lib.crypto.models.ProxyAmount -import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryItem -import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryState -import com.tangem.lib.crypto.models.txhistory.ProxyTransactionStatus - -class TxHistoryManagerImpl( - private val appStateHolder: AppStateHolder, -) : TxHistoryManager { - - override suspend fun checkTxHistoryState(networkId: String, derivationPath: String?): ProxyTransactionHistoryState { - val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } - val walletManager = getActualWalletManager(blockchain, derivationPath) - val state = walletManager.getTransactionHistoryState(address = walletManager.wallet.address) - return state.mapToProxy() - } - - override suspend fun getTxHistoryItems( - networkId: String, - derivationPath: String?, - page: Int, - pageSize: Int, - ): List { - val blockchain = requireNotNull(Blockchain.fromNetworkId(networkId)) { "blockchain not found" } - val walletManager = getActualWalletManager(blockchain, derivationPath) - val itemsResult = walletManager.getTransactionsHistory( - address = walletManager.wallet.address, - page = page, - pageSize = pageSize, - ) - - return when (itemsResult) { - is Result.Success -> itemsResult.data.map { historyItem -> historyItem.mapToProxy() } - is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage) - } - } - - private fun getActualWalletManager(blockchain: Blockchain, derivationPath: String?): WalletManager { - val blockchainNetwork = BlockchainNetwork(blockchain, derivationPath, emptyList()) - val walletManager = appStateHolder.walletState?.getWalletManager(blockchainNetwork) - return requireNotNull(walletManager) { "no wallet manager found" } - } - - private fun TransactionHistoryState.mapToProxy(): ProxyTransactionHistoryState { - return when (this) { - TransactionHistoryState.Success.Empty -> ProxyTransactionHistoryState.Success.Empty - is TransactionHistoryState.Failed.FetchError -> ProxyTransactionHistoryState.Failed.FetchError(exception) - TransactionHistoryState.NotImplemented -> ProxyTransactionHistoryState.NotImplemented - is TransactionHistoryState.Success.HasTransactions -> - ProxyTransactionHistoryState.Success.HasTransactions(txCount) - } - } - - private fun TransactionHistoryItem.mapToProxy() = ProxyTransactionHistoryItem( - txHash = txHash, - timestamp = timestamp, - direction = when (val direction = direction) { - is TransactionHistoryItem.TransactionDirection.Incoming -> - ProxyTransactionHistoryItem.TransactionDirection.Incoming(direction.from) - is TransactionHistoryItem.TransactionDirection.Outgoing -> - ProxyTransactionHistoryItem.TransactionDirection.Outgoing(direction.to) - }, - status = when (status) { - TransactionStatus.Confirmed -> ProxyTransactionStatus.Confirmed - TransactionStatus.Unconfirmed -> ProxyTransactionStatus.Unconfirmed - }, - type = when (type) { - TransactionHistoryItem.TransactionType.Transfer -> ProxyTransactionHistoryItem.TransactionType.Transfer - }, - amount = ProxyAmount( - currencySymbol = amount.currencySymbol, - value = requireNotNull(amount.value) { "Amount value must not be null" }, - decimals = amount.decimals, - ), - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt index 9bae439352..fcd78bd469 100644 --- a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt +++ b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt @@ -8,7 +8,6 @@ import com.tangem.domain.common.util.cardTypesResolver import com.tangem.feature.learn2earn.domain.api.Learn2earnDependencyProvider import com.tangem.lib.crypto.DerivationManager import com.tangem.lib.crypto.TransactionManager -import com.tangem.lib.crypto.TxHistoryManager import com.tangem.lib.crypto.UserWalletManager import com.tangem.tap.proxy.* import dagger.Module @@ -59,12 +58,6 @@ class ProxyModule { ) } - @Provides - @Singleton - fun provideTxHistoryManager(appStateHolder: AppStateHolder): TxHistoryManager { - return TxHistoryManagerImpl(appStateHolder = appStateHolder) - } - // regions FeatureConsumers @Provides @Singleton diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt index a42253ee50..c905063cc3 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/userwallet/UserWalletsStore.kt @@ -5,5 +5,7 @@ import com.tangem.domain.wallets.models.UserWalletId interface UserWalletsStore { + val selectedUserWalletOrNull: UserWallet? + suspend fun getSyncOrNull(key: UserWalletId): UserWallet? } \ No newline at end of file diff --git a/data/txhistory/build.gradle.kts b/data/txhistory/build.gradle.kts index d2aa98d0fd..7ffda888cc 100644 --- a/data/txhistory/build.gradle.kts +++ b/data/txhistory/build.gradle.kts @@ -10,7 +10,13 @@ android { } dependencies { + implementation(projects.core.utils) + implementation(projects.core.datasource) + implementation(projects.domain.legacy) + implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory) + implementation(projects.domain.txhistory.models) + implementation(projects.domain.wallets.models) implementation(deps.kotlin.coroutines) implementation(deps.androidx.paging.runtime) diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt index 6fe0d348de..da8b08603c 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/di/TxHistoryDataModule.kt @@ -1,7 +1,9 @@ package com.tangem.data.txhistory.di -import com.tangem.data.txhistory.repository.MockTxHistoryRepository +import com.tangem.data.txhistory.repository.DefaultTxHistoryRepository +import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.txhistory.repository.TxHistoryRepository +import com.tangem.domain.walletmanager.WalletManagersFacade import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -14,5 +16,11 @@ internal object TxHistoryDataModule { @Provides @Singleton - fun provideTxHistoryRepository(): TxHistoryRepository = MockTxHistoryRepository() + fun provideTxHistoryRepository( + walletManagersFacade: WalletManagersFacade, + userWalletsStore: UserWalletsStore, + ): TxHistoryRepository = DefaultTxHistoryRepository( + walletManagersFacade = walletManagersFacade, + userWalletsStore = userWalletsStore, + ) } \ No newline at end of file diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/mock/MockTxHistoryItems.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/mock/MockTxHistoryItems.kt deleted file mode 100644 index 583fbaa5ee..0000000000 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/mock/MockTxHistoryItems.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.tangem.data.txhistory.mock - -import com.tangem.domain.txhistory.model.TxHistoryItem -import java.math.BigDecimal - -internal object MockTxHistoryItems { - - private val txHistoryItem1 = TxHistoryItem( - txHash = "noster", - timestamp = System.currentTimeMillis(), - direction = TxHistoryItem.TransactionDirection.Incoming("address"), - status = TxHistoryItem.TxStatus.Confirmed, - type = TxHistoryItem.TransactionType.Transfer, - amount = BigDecimal("1000000000.5"), - ) - - private val txHistoryItem2 = TxHistoryItem( - txHash = "noster", - timestamp = 1689844346000, - direction = TxHistoryItem.TransactionDirection.Incoming("address2"), - status = TxHistoryItem.TxStatus.Unconfirmed, - type = TxHistoryItem.TransactionType.Transfer, - amount = BigDecimal("1000000000.5"), - ) - - private val txHistoryItem3 = TxHistoryItem( - txHash = "noster", - timestamp = 1689757946000, - direction = TxHistoryItem.TransactionDirection.Outgoing("address3"), - status = TxHistoryItem.TxStatus.Confirmed, - type = TxHistoryItem.TransactionType.Transfer, - amount = BigDecimal("1000000000.5"), - ) - - private val txHistoryItem4 = TxHistoryItem( - txHash = "noster", - timestamp = 1689671546000, - direction = TxHistoryItem.TransactionDirection.Incoming("address4"), - status = TxHistoryItem.TxStatus.Confirmed, - type = TxHistoryItem.TransactionType.Transfer, - amount = BigDecimal("1000000000.5"), - ) - - private val txHistoryItem5 = TxHistoryItem( - txHash = "noster", - timestamp = 1689585146000, - direction = TxHistoryItem.TransactionDirection.Outgoing("address5"), - status = TxHistoryItem.TxStatus.Unconfirmed, - type = TxHistoryItem.TransactionType.Transfer, - amount = BigDecimal("1000000000.5"), - ) - - private val txHistoryItem6 = TxHistoryItem( - txHash = "noster", - timestamp = 1689585146000, - direction = TxHistoryItem.TransactionDirection.Incoming("address6"), - status = TxHistoryItem.TxStatus.Confirmed, - type = TxHistoryItem.TransactionType.Transfer, - amount = BigDecimal("1000000000.5"), - ) - - val txHistoryItems = listOf( - txHistoryItem1, - txHistoryItem2, - txHistoryItem3, - txHistoryItem4, - txHistoryItem5, - txHistoryItem6, - ) -} \ No newline at end of file diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt new file mode 100644 index 0000000000..a042400894 --- /dev/null +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/DefaultTxHistoryRepository.kt @@ -0,0 +1,66 @@ +package com.tangem.data.txhistory.repository + +import androidx.paging.Pager +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource +import com.tangem.datasource.local.userwallet.UserWalletsStore +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryState +import com.tangem.domain.txhistory.models.TxHistoryStateError +import com.tangem.domain.txhistory.repository.TxHistoryRepository +import com.tangem.domain.walletmanager.WalletManagersFacade +import com.tangem.domain.wallets.models.UserWallet +import kotlinx.coroutines.flow.Flow + +class DefaultTxHistoryRepository( + private val walletManagersFacade: WalletManagersFacade, + private val userWalletsStore: UserWalletsStore, +) : TxHistoryRepository { + + override suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int { + val userWallet = getUserWallet() + val state = walletManagersFacade.getTxHistoryState( + userWalletId = userWallet.walletId, + networkId = networkId, + rawDerivationPath = derivationPath, + ) + return when (state) { + is TxHistoryState.Failed.FetchError -> throw TxHistoryStateError.DataError(state.exception) + TxHistoryState.NotImplemented -> throw TxHistoryStateError.TxHistoryNotImplemented + TxHistoryState.Success.Empty -> throw TxHistoryStateError.EmptyTxHistories + is TxHistoryState.Success.HasTransactions -> state.txCount + } + } + + override fun getTxHistoryItems( + networkId: Network.ID, + derivationPath: String?, + pageSize: Int, + ): Flow> { + val userWallet = getUserWallet() + return Pager( + config = PagingConfig( + pageSize = pageSize, + ), + pagingSourceFactory = { + TxHistoryPagingSource( + loadPage = { page: Int, pageSize: Int -> + walletManagersFacade.getTxHistoryItems( + userWalletId = userWallet.walletId, + networkId = networkId, + rawDerivationPath = derivationPath, + page = page, + pageSize = pageSize, + ) + }, + ) + }, + ).flow + } + + private fun getUserWallet(): UserWallet = requireNotNull(userWalletsStore.selectedUserWalletOrNull) { + "Selected wallet must not be null" + } +} \ No newline at end of file diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/MockTxHistoryRepository.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/MockTxHistoryRepository.kt deleted file mode 100644 index 59829686e4..0000000000 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/MockTxHistoryRepository.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.tangem.data.txhistory.repository - -import androidx.paging.Pager -import androidx.paging.PagingConfig -import androidx.paging.PagingData -import com.tangem.data.txhistory.repository.paging.TxHistoryPagingSource -import com.tangem.domain.txhistory.model.TxHistoryItem -import com.tangem.domain.txhistory.repository.TxHistoryRepository -import kotlinx.coroutines.flow.Flow - -internal class MockTxHistoryRepository : TxHistoryRepository { - - override suspend fun getTxHistoryItemsCount(networkId: String, derivationPath: String): Int { - return 0 - } - - override fun getTxHistoryItems(networkId: String, pageSize: Int): Flow> { - return Pager( - config = PagingConfig( - pageSize = pageSize, - ), - pagingSourceFactory = { TxHistoryPagingSource() }, - ).flow - } -} \ No newline at end of file diff --git a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt index 3eba3ed6ce..a3ebb06194 100644 --- a/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt +++ b/data/txhistory/src/main/kotlin/com/tangem/data/txhistory/repository/paging/TxHistoryPagingSource.kt @@ -2,12 +2,14 @@ package com.tangem.data.txhistory.repository.paging import androidx.paging.PagingSource import androidx.paging.PagingState -import com.tangem.data.txhistory.mock.MockTxHistoryItems -import com.tangem.domain.txhistory.model.TxHistoryItem +import com.tangem.domain.txhistory.models.PaginationWrapper +import com.tangem.domain.txhistory.models.TxHistoryItem private const val INITIAL_PAGE = 1 -internal class TxHistoryPagingSource : PagingSource() { +internal class TxHistoryPagingSource( + private val loadPage: suspend (page: Int, pageSize: Int) -> PaginationWrapper, +) : PagingSource() { override fun getRefreshKey(state: PagingState): Int? { return state.anchorPosition?.let { anchorPosition -> @@ -19,20 +21,12 @@ internal class TxHistoryPagingSource : PagingSource() { override suspend fun load(params: LoadParams): LoadResult { val currentPage = params.key ?: INITIAL_PAGE return try { - // TODO: [REDACTED_JIRA] - // val result = txHistoryManager.getTxHistoryItems( - // networkId = networkId, - // derivationPath = derivationPath, - // page = currentPage, - // pageSize = params.loadSize, - // ) - val result = MockTxHistoryItems.txHistoryItems + val result = loadPage(currentPage, params.loadSize) LoadResult.Page( - data = result, + data = result.items, prevKey = if (currentPage > INITIAL_PAGE) currentPage.minus(1) else null, - // TODO: handle end of reached [REDACTED_JIRA] - nextKey = null, + nextKey = if (result.page < result.totalPages) currentPage.plus(1) else null, ) } catch (e: Exception) { LoadResult.Error(e) diff --git a/domain/legacy/build.gradle.kts b/domain/legacy/build.gradle.kts index 99665896d3..eb321b25e0 100644 --- a/domain/legacy/build.gradle.kts +++ b/domain/legacy/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { implementation(projects.domain.models) implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) + implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets.models) /** Tangem libraries */ diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 1b942ab4ce..a798df5e73 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -3,6 +3,7 @@ package com.tangem.domain.walletmanager import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.BlockchainSdkError import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.extensions.Result import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore @@ -12,10 +13,11 @@ import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.PaginationWrapper +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryState import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult -import com.tangem.domain.walletmanager.utils.SdkTokenConverter -import com.tangem.domain.walletmanager.utils.UpdateWalletManagerResultFactory -import com.tangem.domain.walletmanager.utils.WalletManagerFactory +import com.tangem.domain.walletmanager.utils.* import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import timber.log.Timber @@ -32,24 +34,22 @@ class DefaultWalletManagersFacade( private val resultFactory by lazy { UpdateWalletManagerResultFactory() } private val walletManagerFactory by lazy { WalletManagerFactory(configManager) } private val sdkTokenConverter by lazy { SdkTokenConverter() } + private val txHistoryStateConverter by lazy { SdkTransactionHistoryStateConverter() } + private val txHistoryItemConverter by lazy { SdkTransactionHistoryItemConverter() } override suspend fun update( userWalletId: UserWalletId, networkId: Network.ID, extraTokens: Set, ): UpdateWalletManagerResult { - val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { - "Unable to find a user wallet with provided ID: $userWalletId" - } + val userWallet = getUserWallet(userWalletId) val blockchain = Blockchain.fromId(networkId.value) return getAndUpdateWalletManager(userWallet, blockchain, extraTokens) } override suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String { - val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { - "Unable to find a user wallet with provided ID: $userWalletId" - } + val userWallet = getUserWallet(userWalletId) val blockchain = Blockchain.fromId(networkId.value) @@ -63,6 +63,57 @@ class DefaultWalletManagersFacade( .orEmpty() } + override suspend fun getTxHistoryState( + userWalletId: UserWalletId, + networkId: Network.ID, + rawDerivationPath: String?, + ): TxHistoryState { + val userWallet = getUserWallet(userWalletId) + val blockchain = Blockchain.fromId(networkId.value) + val derivationPath = rawDerivationPath?.let(::DerivationPath) + val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) { + "Unable to get a wallet manager for blockchain: $blockchain" + } + return walletManager + .getTransactionHistoryState(walletManager.wallet.address) + .let(txHistoryStateConverter::convert) + } + + override suspend fun getTxHistoryItems( + userWalletId: UserWalletId, + networkId: Network.ID, + rawDerivationPath: String?, + page: Int, + pageSize: Int, + ): PaginationWrapper { + val userWallet = getUserWallet(userWalletId) + val blockchain = Blockchain.fromId(networkId.value) + val derivationPath = rawDerivationPath?.let(::DerivationPath) + val walletManager = requireNotNull(getOrCreateWalletManager(userWallet, blockchain, derivationPath)) { + "Unable to get a wallet manager for blockchain: $blockchain" + } + val itemsResult = walletManager.getTransactionsHistory( + address = walletManager.wallet.address, + page = page, + pageSize = pageSize, + ) + + return when (itemsResult) { + is Result.Success -> PaginationWrapper( + page = itemsResult.data.page, + totalPages = itemsResult.data.totalPages, + itemsOnPage = itemsResult.data.itemsOnPage, + items = txHistoryItemConverter.convertList(itemsResult.data.items), + ) + is Result.Failure -> error(itemsResult.error.message ?: itemsResult.error.customMessage) + } + } + + private suspend fun getUserWallet(userWalletId: UserWalletId) = + requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { + "Unable to find a user wallet with provided ID: $userWalletId" + } + private suspend fun getAndUpdateWalletManager( userWallet: UserWallet, blockchain: Blockchain, diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index a2ed074675..9a9a85aa35 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -2,6 +2,9 @@ package com.tangem.domain.walletmanager import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.PaginationWrapper +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryState import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult import com.tangem.domain.wallets.models.UserWalletId @@ -26,4 +29,35 @@ interface WalletManagersFacade { ): UpdateWalletManagerResult suspend fun getExploreUrl(userWalletId: UserWalletId, networkId: Network.ID): String + + /** + * Returns transactions count + * + * @param userWalletId The ID of the user's wallet. + * @param networkId The network ID. + * @param rawDerivationPath Derivation path in raw form. + + */ + suspend fun getTxHistoryState( + userWalletId: UserWalletId, + networkId: Network.ID, + rawDerivationPath: String?, + ): TxHistoryState + + /** + * Returns transaction history items wrapped to pagination + * + * @param userWalletId The ID of the user's wallet. + * @param networkId The network ID. + * @param rawDerivationPath Derivation path in raw form. + * @param page Pagination page. + * @param pageSize Pagination size. + */ + suspend fun getTxHistoryItems( + userWalletId: UserWalletId, + networkId: Network.ID, + rawDerivationPath: String?, + page: Int, + pageSize: Int, + ): PaginationWrapper } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt new file mode 100644 index 0000000000..8df6e63c54 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryItemConverter.kt @@ -0,0 +1,29 @@ +package com.tangem.domain.walletmanager.utils + +import com.tangem.blockchain.common.TransactionStatus +import com.tangem.blockchain.common.txhistory.TransactionHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.utils.converter.Converter +import com.tangem.blockchain.common.txhistory.TransactionHistoryItem as SdkTransactionHistoryItem + +internal class SdkTransactionHistoryItemConverter : Converter { + + override fun convert(value: SdkTransactionHistoryItem): TxHistoryItem = TxHistoryItem( + txHash = value.txHash, + timestampInMillis = value.timestamp, + direction = when (val direction = value.direction) { + is SdkTransactionHistoryItem.TransactionDirection.Incoming -> + TxHistoryItem.TransactionDirection.Incoming(direction.from) + is SdkTransactionHistoryItem.TransactionDirection.Outgoing -> + TxHistoryItem.TransactionDirection.Outgoing(direction.to) + }, + status = when (value.status) { + TransactionStatus.Confirmed -> TxHistoryItem.TxStatus.Confirmed + TransactionStatus.Unconfirmed -> TxHistoryItem.TxStatus.Unconfirmed + }, + type = when (value.type) { + TransactionHistoryItem.TransactionType.Transfer -> TxHistoryItem.TransactionType.Transfer + }, + amount = requireNotNull(value.amount.value) { "Transaction amount value must not be null" }, + ) +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt new file mode 100644 index 0000000000..6750e05410 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTransactionHistoryStateConverter.kt @@ -0,0 +1,16 @@ +package com.tangem.domain.walletmanager.utils + +import com.tangem.blockchain.common.txhistory.TransactionHistoryState +import com.tangem.domain.txhistory.models.TxHistoryState +import com.tangem.utils.converter.Converter +import com.tangem.blockchain.common.txhistory.TransactionHistoryState as SdkTransactionHistoryState + +internal class SdkTransactionHistoryStateConverter : Converter { + + override fun convert(value: TransactionHistoryState): TxHistoryState = when (value) { + is TransactionHistoryState.Success.Empty -> TxHistoryState.Success.Empty + is TransactionHistoryState.Success.HasTransactions -> TxHistoryState.Success.HasTransactions(value.txCount) + is TransactionHistoryState.Failed.FetchError -> TxHistoryState.Failed.FetchError(value.exception) + is TransactionHistoryState.NotImplemented -> TxHistoryState.NotImplemented + } +} \ No newline at end of file diff --git a/domain/txhistory/build.gradle.kts b/domain/txhistory/build.gradle.kts index 1237e8f782..2ba0547f59 100644 --- a/domain/txhistory/build.gradle.kts +++ b/domain/txhistory/build.gradle.kts @@ -14,4 +14,6 @@ dependencies { implementation(deps.androidx.paging.runtime) implementation(projects.core.utils) + implementation(projects.domain.tokens.models) + implementation(projects.domain.txhistory.models) } \ No newline at end of file diff --git a/domain/txhistory/models/.gitignore b/domain/txhistory/models/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/txhistory/models/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/txhistory/models/build.gradle.kts b/domain/txhistory/models/build.gradle.kts new file mode 100644 index 0000000000..7ff7fb7522 --- /dev/null +++ b/domain/txhistory/models/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} \ No newline at end of file diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt new file mode 100644 index 0000000000..92ea34de36 --- /dev/null +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/PaginationWrapper.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.txhistory.models + +data class PaginationWrapper( + val page: Int, + val totalPages: Int, + val itemsOnPage: Int, + val items: List, +) \ No newline at end of file diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/model/TxHistoryItem.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt similarity index 87% rename from domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/model/TxHistoryItem.kt rename to domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt index 41d5c76d8e..447a9395ff 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/model/TxHistoryItem.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryItem.kt @@ -1,10 +1,10 @@ -package com.tangem.domain.txhistory.model +package com.tangem.domain.txhistory.models import java.math.BigDecimal data class TxHistoryItem( val txHash: String, - val timestamp: Long, + val timestampInMillis: Long, val direction: TransactionDirection, val status: TxStatus, val type: TransactionType, diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/error/TxHistoryListError.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryListError.kt similarity index 75% rename from domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/error/TxHistoryListError.kt rename to domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryListError.kt index 1538b27e0b..618980c881 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/error/TxHistoryListError.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryListError.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.txhistory.error +package com.tangem.domain.txhistory.models sealed class TxHistoryListError : Throwable() { data class DataError(override val cause: Throwable) : TxHistoryListError() diff --git a/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryState.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryState.kt new file mode 100644 index 0000000000..0436102e66 --- /dev/null +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryState.kt @@ -0,0 +1,15 @@ +package com.tangem.domain.txhistory.models + +sealed class TxHistoryState { + + sealed class Success : TxHistoryState() { + object Empty : Success() + data class HasTransactions(val txCount: Int) : Success() + } + + sealed class Failed : TxHistoryState() { + data class FetchError(val exception: Exception) : Failed() + } + + object NotImplemented : TxHistoryState() +} \ No newline at end of file diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/error/TxHistoryStateError.kt b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryStateError.kt similarity index 85% rename from domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/error/TxHistoryStateError.kt rename to domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryStateError.kt index 3f93a3cf12..1b5f07333c 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/error/TxHistoryStateError.kt +++ b/domain/txhistory/models/src/main/kotlin/com/tangem/domain/txhistory/models/TxHistoryStateError.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.txhistory.error +package com.tangem.domain.txhistory.models sealed class TxHistoryStateError : Throwable() { diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt index 1ebb8ae1dd..8ff4c05cff 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/repository/TxHistoryRepository.kt @@ -1,16 +1,21 @@ package com.tangem.domain.txhistory.repository import androidx.paging.PagingData -import com.tangem.domain.txhistory.error.TxHistoryListError -import com.tangem.domain.txhistory.error.TxHistoryStateError -import com.tangem.domain.txhistory.model.TxHistoryItem +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryListError +import com.tangem.domain.txhistory.models.TxHistoryStateError +import com.tangem.domain.txhistory.models.TxHistoryItem import kotlinx.coroutines.flow.Flow interface TxHistoryRepository { @Throws(TxHistoryStateError::class) - suspend fun getTxHistoryItemsCount(networkId: String, derivationPath: String): Int + suspend fun getTxHistoryItemsCount(networkId: Network.ID, derivationPath: String?): Int @Throws(TxHistoryListError::class) - fun getTxHistoryItems(networkId: String, pageSize: Int): Flow> + fun getTxHistoryItems( + networkId: Network.ID, + derivationPath: String?, + pageSize: Int, + ): Flow> } \ No newline at end of file diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt index 1ecdc72dd6..172c8c2b3f 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsCountUseCase.kt @@ -3,12 +3,13 @@ package com.tangem.domain.txhistory.usecase import arrow.core.Either import arrow.core.raise.catch import arrow.core.raise.either -import com.tangem.domain.txhistory.error.TxHistoryStateError +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.txhistory.repository.TxHistoryRepository class GetTxHistoryItemsCountUseCase(private val repository: TxHistoryRepository) { - suspend operator fun invoke(networkId: String, derivationPath: String): Either { + suspend operator fun invoke(networkId: Network.ID, derivationPath: String?): Either { return either { catch( block = { repository.getTxHistoryItemsCount(networkId, derivationPath) }, diff --git a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt index 9f3fc2fbe0..38fbf4b87e 100644 --- a/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt +++ b/domain/txhistory/src/main/kotlin/com/tangem/domain/txhistory/usecase/GetTxHistoryItemsUseCase.kt @@ -3,8 +3,9 @@ package com.tangem.domain.txhistory.usecase import androidx.paging.PagingData import arrow.core.Either import arrow.core.raise.either -import com.tangem.domain.txhistory.error.TxHistoryListError -import com.tangem.domain.txhistory.model.TxHistoryItem +import com.tangem.domain.tokens.models.Network +import com.tangem.domain.txhistory.models.TxHistoryListError +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.repository.TxHistoryRepository import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch @@ -14,12 +15,13 @@ private const val DEFAULT_PAGE_SIZE = 20 class GetTxHistoryItemsUseCase(private val repository: TxHistoryRepository) { operator fun invoke( - networkId: String, + networkId: Network.ID, + derivationPath: String?, pageSize: Int = DEFAULT_PAGE_SIZE, ): Either>> { return either { repository - .getTxHistoryItems(networkId = networkId, pageSize = pageSize) + .getTxHistoryItems(networkId = networkId, derivationPath = derivationPath, pageSize = pageSize) .catch { raise(TxHistoryListError.DataError(it)) } } } diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index d3af011dcd..e37da473bc 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(deps.compose.coil) implementation(deps.kotlin.immutable.collections) + implementation(deps.arrow.core) /** DI */ implementation(deps.hilt.android) @@ -37,6 +38,9 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.navigation) + implementation(projects.domain.txhistory) + implementation(projects.domain.txhistory.models) + /** Feature Apis */ implementation(projects.features.tokendetails.api) } \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index 265fb7ce41..d517ab1def 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -53,6 +53,7 @@ dependencies { implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory) + implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index a3bb1b915c..69a67bea5e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -7,9 +7,9 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList -import com.tangem.domain.txhistory.error.TxHistoryListError -import com.tangem.domain.txhistory.error.TxHistoryStateError -import com.tangem.domain.txhistory.model.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryListError +import com.tangem.domain.txhistory.models.TxHistoryStateError +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index 8f4e728d13..1364a89682 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -6,8 +6,8 @@ import com.tangem.common.Provider import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.txhistory.error.TxHistoryListError -import com.tangem.domain.txhistory.model.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryListError +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index ffe28ac46d..8ea55731fd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -7,7 +7,7 @@ import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.TransactionState import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.txhistory.error.TxHistoryStateError +import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index 7d61b17351..698ac67c5a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -4,7 +4,7 @@ import android.text.format.DateUtils import androidx.paging.* import com.tangem.blockchain.common.Blockchain import com.tangem.core.ui.components.transactions.TransactionState -import com.tangem.domain.txhistory.model.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -180,7 +180,7 @@ internal class WalletTxHistoryItemFlowConverter( * * @see [convert] */ - private fun TxHistoryItem.getRawTimestamp() = this.timestamp.toString() + private fun TxHistoryItem.getRawTimestamp() = this.timestampInMillis.toString() private fun TxHistoryItemState?.getTimestamp(): Long? { return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 6d08a629c2..6083b4973d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -5,12 +5,12 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.* import androidx.paging.cachedIn -import com.tangem.blockchain.common.DerivationStyle import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.domain.card.* import com.tangem.domain.common.CardTypesResolver +import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase @@ -143,23 +143,30 @@ internal class WalletViewModel @Inject constructor( private fun updateByTxHistory(index: Int) { viewModelScope.launch(dispatchers.io) { - val blockchain = getWallet(index).scanResponse.cardTypesResolver.getBlockchain() + val wallet = getWallet(index) + val blockchain = wallet.scanResponse.cardTypesResolver.getBlockchain() + val derivationPath = blockchain.derivationPath(style = wallet.scanResponse.card.derivationStyle)?.rawPath val txHistoryItemsCountEither = txHistoryItemsCountUseCase( - networkId = blockchain.id, - derivationPath = requireNotNull(blockchain.derivationPath(style = DerivationStyle.LEGACY)).rawPath, + networkId = Network.ID(blockchain.id), + derivationPath = derivationPath, ) uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither) - txHistoryItemsCountEither.onRight { updateTxHistory(networkId = blockchain.id) } + txHistoryItemsCountEither.onRight { + updateTxHistory( + networkId = Network.ID(blockchain.id), + derivationPath = derivationPath, + ) + } updateNotifications(index) } } - private fun updateTxHistory(networkId: String) { + private fun updateTxHistory(networkId: Network.ID, derivationPath: String?) { uiState = stateFactory.getLoadedTxHistoryState( - txHistoryEither = txHistoryItemsUseCase(networkId = networkId).map { it.cachedIn(viewModelScope) }, + txHistoryEither = txHistoryItemsUseCase(networkId, derivationPath).map { it.cachedIn(viewModelScope) }, ) } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index edb6ad70d8..06a5bc01d8 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-297" +tangemBlockchainSdk = "develop-306" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-280" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/TxHistoryManager.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/TxHistoryManager.kt deleted file mode 100644 index de50d8b4ee..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/TxHistoryManager.kt +++ /dev/null @@ -1,18 +0,0 @@ -package com.tangem.lib.crypto - -import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryItem -import com.tangem.lib.crypto.models.txhistory.ProxyTransactionHistoryState - -interface TxHistoryManager { - - @Throws(IllegalStateException::class) - suspend fun checkTxHistoryState(networkId: String, derivationPath: String?): ProxyTransactionHistoryState - - @Throws(IllegalStateException::class) - suspend fun getTxHistoryItems( - networkId: String, - derivationPath: String?, - page: Int, - pageSize: Int, - ): List -} \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryItem.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryItem.kt deleted file mode 100644 index 387a7518e6..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryItem.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.lib.crypto.models.txhistory - -import com.tangem.lib.crypto.models.ProxyAmount - -data class ProxyTransactionHistoryItem( - val txHash: String, - val timestamp: Long, - val direction: TransactionDirection, - val status: ProxyTransactionStatus, - val type: TransactionType, - val amount: ProxyAmount, -) { - sealed interface TransactionDirection { - data class Incoming(val from: String) : TransactionDirection - data class Outgoing(val to: String) : TransactionDirection - } - - sealed interface TransactionType { - object Transfer : TransactionType - } -} \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryState.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryState.kt deleted file mode 100644 index ddeeba4d3f..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionHistoryState.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.tangem.lib.crypto.models.txhistory - -sealed class ProxyTransactionHistoryState { - - sealed class Success : ProxyTransactionHistoryState() { - object Empty : Success() - data class HasTransactions(val txCount: Int) : Success() - } - - sealed class Failed : ProxyTransactionHistoryState() { - data class FetchError(val exception: Exception) : Failed() - } - - object NotImplemented : ProxyTransactionHistoryState() -} \ No newline at end of file diff --git a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionStatus.kt b/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionStatus.kt deleted file mode 100644 index 2e5ffd5aaf..0000000000 --- a/libs/crypto/src/main/java/com/tangem/lib/crypto/models/txhistory/ProxyTransactionStatus.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.tangem.lib.crypto.models.txhistory - -enum class ProxyTransactionStatus { Confirmed, Unconfirmed } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 7378b7b744..8c859f1b92 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -107,6 +107,7 @@ include(":domain:tokens:models") include(":domain:wallets") include(":domain:wallets:models") include(":domain:txhistory") +include(":domain:txhistory:models") // endregion Domain modules // region Data modules From 3b33d4db0abd8232da44095176747ddd88fdd10d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 13:24:48 +0300 Subject: [PATCH 08/52] Updated on 2026-08-14 --- .../warningMessage/WarningMessagesManager.kt | 6 +- ...tUserWalletsPublicInformationRepository.kt | 4 +- .../DefaultWalletAmountsRepository.kt | 4 +- .../DefaultWalletManagersStore.kt | 4 +- .../com/tangem/utils/extensions/Collection.kt | 91 ----------- .../java/com/tangem/utils/extensions/List.kt | 51 ++++++ .../repository/DefaultNetworksRepository.kt | 19 ++- .../tokens/repository/MockQuotesRepository.kt | 16 +- .../data/tokens/utils/NetworkStatusFactory.kt | 30 ++-- .../domain/core/raise/DelegatedRaise.kt | 13 -- .../domain/tokens/GetCurrencyUseCase.kt | 36 ++--- .../tokens/GetPrimaryCurrencyUseCase.kt | 36 ++--- .../domain/tokens/GetTokenListUseCase.kt | 49 +++--- .../tokens/ToggleTokenListGroupingUseCase.kt | 46 +++--- .../tokens/ToggleTokenListSortingUseCase.kt | 18 ++- .../mapper/GetWalletTokenErrorMappers.kt | 2 +- .../CurrenciesStatusesOperations.kt | 153 ++++++++++-------- .../operations/CurrencyStatusOperations.kt | 25 +-- .../TokenListFiatBalanceOperations.kt | 56 +++---- .../tokens/operations/TokenListOperations.kt | 125 +++++++------- .../operations/TokenListSortingOperations.kt | 56 +++---- .../domain/tokens/GetTokenListUseCaseTest.kt | 3 +- ...ryptoCurrencyStatusToTokenItemConverter.kt | 2 +- gradle/dependencies.toml | 2 +- 24 files changed, 363 insertions(+), 484 deletions(-) create mode 100644 core/utils/src/main/java/com/tangem/utils/extensions/List.kt delete mode 100644 domain/core/src/main/kotlin/com/tangem/domain/core/raise/DelegatedRaise.kt diff --git a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt index 01bb883f69..183ba76207 100644 --- a/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/configurable/warningMessage/WarningMessagesManager.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain.configurable.warningMessage import com.tangem.blockchain.common.Blockchain -import com.tangem.utils.extensions.removeByReplace +import com.tangem.utils.extensions.removeBy import com.tangem.wallet.R import java.util.concurrent.CopyOnWriteArrayList @@ -44,12 +44,12 @@ class WarningMessagesManager { } fun removeWarnings(origin: WarningMessage.Origin) { - warningsList.removeByReplace { it.origin == origin } + warningsList.removeBy { it.origin == origin } sortByPriority() } fun removeWarnings(messageRes: Int) { - warningsList.removeByReplace { it.messageResId == messageRes } + warningsList.removeBy { it.messageResId == messageRes } } fun containsWarning(warning: WarningMessage) = warning in warningsList diff --git a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt index 4ab48a80a9..1f64334fd7 100644 --- a/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/userWalletList/repository/implementation/DefaultUserWalletsPublicInformationRepository.kt @@ -12,7 +12,7 @@ import com.tangem.domain.wallets.models.UserWalletId import com.tangem.tap.domain.userWalletList.model.UserWalletPublicInformation import com.tangem.tap.domain.userWalletList.repository.UserWalletsPublicInformationRepository import com.tangem.tap.domain.userWalletList.utils.publicInformation -import com.tangem.utils.extensions.plusOrReplace +import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -29,7 +29,7 @@ internal class DefaultUserWalletsPublicInformationRepository( getAll() .flatMap { savedInformation -> val infoToSave = withContext(Dispatchers.Default) { - savedInformation.plusOrReplace(userWallet.publicInformation) { + savedInformation.addOrReplace(userWallet.publicInformation) { userWallet.walletId == it.walletId } } diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt index 6fa441f106..0203abef4b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletAmountsRepository.kt @@ -31,7 +31,7 @@ import com.tangem.tap.features.wallet.models.getPendingTransactions import com.tangem.tap.proxy.redux.DaggerGraphState import com.tangem.tap.store import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.extensions.plusOrReplace +import com.tangem.utils.extensions.addOrReplace import kotlinx.coroutines.* import kotlinx.coroutines.flow.firstOrNull import timber.log.Timber @@ -429,7 +429,7 @@ internal class DefaultWalletAmountsRepository( withContext(Dispatchers.Default) { WalletManagerStorage.update { prevManagers -> val newManagersForUserWallet = prevManagers[userWalletId].orEmpty() - .plusOrReplace(walletManager) { + .addOrReplace(walletManager) { it.wallet.blockchain == walletManager.wallet.blockchain } diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt index bed4e6a55c..36bc7723c2 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/walletmanager/DefaultWalletManagersStore.kt @@ -5,7 +5,7 @@ import com.tangem.blockchain.common.WalletManager import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.extensions.plusOrReplace +import com.tangem.utils.extensions.addOrReplace internal class DefaultWalletManagersStore( dataStore: StringKeyDataStore>, @@ -32,7 +32,7 @@ internal class DefaultWalletManagersStore( val walletManagers = getSyncOrNull(userWalletId) val updatedWalletManagers = walletManagers - ?.plusOrReplace(walletManager) { + ?.addOrReplace(walletManager) { it.wallet.blockchain == walletManager.wallet.blockchain && it.wallet.publicKey == walletManager.wallet.publicKey } diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt b/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt index 36644cfda9..9930c5cb77 100644 --- a/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt +++ b/core/utils/src/main/java/com/tangem/utils/extensions/Collection.kt @@ -18,95 +18,4 @@ fun Collection.isSingleItem(): Boolean = this.size == 1 */ fun Collection.copy(): Collection { return this.map { it } -} - -/** - * Adds the specified element to the collection or replaces an existing element. - * The predicate defines the condition to replace the existing element. - * - * @param item The element to be added or replace the existing one. - * @param predicate The condition to replace an existing element. - * @return The modified [List] after adding or replacing the element. - */ -inline fun Collection.plusOrReplace(item: T, predicate: (T) -> Boolean): List { - val mutableList = this as? MutableList ?: ArrayList(this) - - mutableList.addOrReplace(item, predicate) - - return mutableList -} - -/** - * Adds the specified element to the collection or replaces an existing element. - * The predicate defines the condition to replace the existing element. - * - * @param item The element to be added or replace the existing one. - * @param predicate The condition to replace an existing element. - */ -inline fun MutableCollection.addOrReplace(item: T, predicate: (T) -> Boolean) { - val isReplaced = replaceBy(item, predicate) - - if (!isReplaced) { - add(item) - } -} - -/** - * Removes an element from the collection based on the provided predicate. - * Uses iterator, avoid using it in COW collections - * - * @param predicate The condition to remove an element. - * @return [Boolean] indicating whether an element was removed. - */ -inline fun MutableCollection.removeByIterate(predicate: (T) -> Boolean): Boolean { - var removed = false - val iterator = this.iterator() - - for (e in iterator) { - if (predicate(e)) { - iterator.remove() - removed = true - - break - } - } - - return removed -} - -/** - * Removes an element from the collection based on the provided predicate. - * Uses removeAll() method and could be used for COW collections - * - * @param predicate The condition to remove an element. - * @return [Boolean] indicating whether an element was removed. - */ -fun MutableList.removeByReplace(predicate: (T) -> Boolean): Boolean { - val toRemove = this.filter(predicate) - this.removeAll(toRemove) - return toRemove.isNotEmpty() -} - -/** - * Replaces an element in the collection with the provided item based on the predicate. - * - * @param item The element to replace the existing one. - * @param predicate The condition to replace an existing element. - * @return [Boolean] indicating whether an element was replaced. - */ -inline fun MutableCollection.replaceBy(item: T, predicate: (T) -> Boolean): Boolean { - var replaced = false - val mutableList = this as? MutableList ?: ArrayList(this) - val iterator = mutableList.listIterator() - - for (e in iterator) { - if (predicate(e)) { - iterator.set(item) - replaced = true - - break - } - } - - return replaced } \ No newline at end of file diff --git a/core/utils/src/main/java/com/tangem/utils/extensions/List.kt b/core/utils/src/main/java/com/tangem/utils/extensions/List.kt new file mode 100644 index 0000000000..ee1c005d8c --- /dev/null +++ b/core/utils/src/main/java/com/tangem/utils/extensions/List.kt @@ -0,0 +1,51 @@ +package com.tangem.utils.extensions + +/** + * Removes an element from the collection based on the provided predicate. + * + * @param predicate The condition to remove an element. + * @return [Boolean] indicating whether an element was removed. + */ +fun MutableList.removeBy(predicate: (T) -> Boolean): Boolean { + val toRemove = this.filter(predicate) + this.removeAll(toRemove) + return toRemove.isNotEmpty() +} + +/** + * Replaces an element in the list with the provided item based on the predicate. + * + * @param item The element to replace the existing one. + * @param predicate The condition to replace an existing element. + * @return [Boolean] indicating whether an element was replaced. + */ +inline fun MutableList.replaceBy(item: T, predicate: (T) -> Boolean): Boolean { + val index = indexOfFirst(predicate) + + if (index == -1) { + return false + } + + this[index] = item + + return true +} + +/** + * Adds the specified element to the list or replaces an existing element. + * The predicate defines the condition to replace the existing element. + * + * @param item The element to be added or replace the existing one. + * @param predicate The condition to replace an existing element. + * @return The modified [List] after adding or replacing the element. + */ +inline fun List.addOrReplace(item: T, predicate: (T) -> Boolean): List { + val mutableList = this.toMutableList() + val isReplaced = mutableList.replaceBy(item, predicate) + + if (!isReplaced) { + mutableList.add(item) + } + + return mutableList +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index bf4206d543..398586bf6c 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -36,7 +36,7 @@ internal class DefaultNetworksRepository( private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(DemoConfig()) } private val networkStatusFactory by lazy { NetworkStatusFactory() } - private val networksStatuses: MutableStateFlow> = MutableStateFlow(hashSetOf()) + private val networksStatuses: MutableStateFlow> = MutableStateFlow(emptyList()) override fun getNetworks(networksIds: Set): Set { return networkConverter.convertSet(networksIds) @@ -48,7 +48,9 @@ internal class DefaultNetworksRepository( refresh: Boolean, ): Flow> = channelFlow { launch(dispatchers.io) { - networksStatuses.collect(::send) + networksStatuses.collect { + send(it.toSet()) + } } launch(dispatchers.io) { @@ -82,17 +84,22 @@ internal class DefaultNetworksRepository( private suspend fun fetchNetworkStatus(userWalletId: UserWalletId, networkId: Network.ID) { val currencies = getCurrencies(userWalletId) + .asSequence() + .filter { it.networkId == networkId } + val result = walletManagersFacade.update( userWalletId = userWalletId, networkId = networkId, extraTokens = currencies.filterIsInstanceTo(hashSetOf()), ) - val networkStatus = networkStatusFactory.createNetworkStatus(networkId, result, currencies) + val networkStatus = networkStatusFactory.createNetworkStatus( + networkId = networkId, + result = result, + currencies = currencies.toSet(), + ) networksStatuses.update { statuses -> - statuses.apply { - addOrReplace(networkStatus) { it.networkId == networkStatus.networkId } - } + statuses.addOrReplace(networkStatus) { it.networkId == networkStatus.networkId } } } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt index 841ce3207f..e37146cbba 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt @@ -3,21 +3,27 @@ package com.tangem.data.tokens.repository import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.Quote import com.tangem.domain.tokens.repository.QuotesRepository +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.channelFlow import java.math.BigDecimal +import kotlin.random.Random internal class MockQuotesRepository : QuotesRepository { override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { - return flowOf( - currenciesIds.map { + return channelFlow { + val quotes = currenciesIds.map { Quote( currencyId = it, fiatRate = BigDecimal.ZERO, priceChange = BigDecimal.ZERO, ) - }.toSet(), - ) + }.toSet() + + delay(Random.nextLong(from = 200, until = 2_000)) + + send(quotes) + } } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index f76f363033..6dc955d4e5 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -5,7 +5,6 @@ import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.models.Network import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult -import timber.log.Timber import java.math.BigDecimal internal class NetworkStatusFactory { @@ -33,25 +32,20 @@ internal class NetworkStatusFactory { amounts: Set, currencies: Set, ): Map { - val formattedAmounts = hashMapOf() - - currencies.forEach { currency -> - val amount = when (currency) { - is CryptoCurrency.Coin -> amounts.singleOrNull { it is CryptoCurrencyAmount.Coin } - is CryptoCurrency.Token -> amounts.singleOrNull { - it is CryptoCurrencyAmount.Token && - it.id == getTokenIdString(currency.id) && - it.tokenContractAddress == currency.contractAddress + return amounts + .asSequence() + .mapNotNull { amount -> + val currency = when (amount) { + is CryptoCurrencyAmount.Coin -> currencies.singleOrNull { it is CryptoCurrency.Coin } + is CryptoCurrencyAmount.Token -> currencies.firstOrNull { + it is CryptoCurrency.Token && + getTokenIdString(it.id) == amount.id && + it.contractAddress == amount.tokenContractAddress + } } - }?.value - if (amount == null) { - Timber.e("Unable to find a token amount for: ${currency.name}") - } else { - formattedAmounts[currency.id] = amount + currency?.id?.let { it to amount.value } } - } - - return formattedAmounts + .toMap() } } \ No newline at end of file diff --git a/domain/core/src/main/kotlin/com/tangem/domain/core/raise/DelegatedRaise.kt b/domain/core/src/main/kotlin/com/tangem/domain/core/raise/DelegatedRaise.kt deleted file mode 100644 index ffde660eb6..0000000000 --- a/domain/core/src/main/kotlin/com/tangem/domain/core/raise/DelegatedRaise.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.domain.core.raise - -import arrow.core.raise.Raise - -abstract class DelegatedRaise( - private val otherRaise: Raise, - private val transformError: (Error) -> OtherError, -) : Raise { - - override fun raise(r: Error): Nothing { - otherRaise.raise(transformError(r)) - } -} \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt index f49378314a..6c9aa54e98 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt @@ -1,12 +1,8 @@ package com.tangem.domain.tokens import arrow.core.Either -import arrow.core.left -import arrow.core.raise.Raise -import arrow.core.raise.recover -import arrow.core.right import com.tangem.domain.tokens.error.CurrencyError -import com.tangem.domain.tokens.error.mapper.mapToTokenError +import com.tangem.domain.tokens.error.mapper.mapToCurrencyError import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations @@ -15,9 +11,7 @@ import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.* /** * Use case for fetching the status of a specific cryptocurrency associated with a user wallet. @@ -47,36 +41,26 @@ class GetCurrencyUseCase( currencyId: CryptoCurrency.ID, refresh: Boolean = false, ): Flow> { - return channelFlow { - recover( - block = { - getCurrency(userWalletId, currencyId, refresh).collectLatest { currencyStatus -> - send(currencyStatus.right()) - } - }, - recover = { error -> - send(error.left()) - }, - ) - } + return flow { + emitAll(getCurrency(userWalletId, currencyId, refresh)) + }.flowOn(dispatchers.io) } - private suspend fun Raise.getCurrency( + private suspend fun getCurrency( userWalletId: UserWalletId, currencyId: CryptoCurrency.ID, refresh: Boolean, - ): Flow { + ): Flow> { val operations = CurrenciesStatusesOperations( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, userWalletId = userWalletId, refresh = refresh, - dispatchers = dispatchers, - raise = this, - transformError = CurrenciesStatusesOperations.Error::mapToTokenError, ) - return operations.getCurrencyStatusFlow(currencyId) + return operations.getCurrencyStatusFlow(currencyId).map { maybeCurrency -> + maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError) + } } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt index 3d0f9c2011..58a8bba978 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCase.kt @@ -1,12 +1,8 @@ package com.tangem.domain.tokens import arrow.core.Either -import arrow.core.left -import arrow.core.raise.Raise -import arrow.core.raise.recover -import arrow.core.right import com.tangem.domain.tokens.error.CurrencyError -import com.tangem.domain.tokens.error.mapper.mapToTokenError +import com.tangem.domain.tokens.error.mapper.mapToCurrencyError import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -14,9 +10,7 @@ import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.* /** * Use case for fetching the status of the primary cryptocurrency associated with a user wallet. @@ -44,35 +38,25 @@ class GetPrimaryCurrencyUseCase( userWalletId: UserWalletId, refresh: Boolean = false, ): Flow> { - return channelFlow { - recover( - block = { - getCurrency(userWalletId, refresh).collectLatest { currencyStatus -> - send(currencyStatus.right()) - } - }, - recover = { error -> - send(error.left()) - }, - ) - } + return flow { + emitAll(getPrimaryCurrency(userWalletId, refresh)) + }.flowOn(dispatchers.io) } - private suspend fun Raise.getCurrency( + private suspend fun getPrimaryCurrency( userWalletId: UserWalletId, refresh: Boolean, - ): Flow { + ): Flow> { val operations = CurrenciesStatusesOperations( currenciesRepository = currenciesRepository, quotesRepository = quotesRepository, networksRepository = networksRepository, userWalletId = userWalletId, refresh = refresh, - dispatchers = dispatchers, - raise = this, - transformError = CurrenciesStatusesOperations.Error::mapToTokenError, ) - return operations.getPrimaryCurrencyStatusFlow() + return operations.getPrimaryCurrencyStatusFlow().map { maybeCurrency -> + maybeCurrency.mapLeft(CurrenciesStatusesOperations.Error::mapToCurrencyError) + } } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index ea19873b22..1b046264dd 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -2,9 +2,6 @@ package com.tangem.domain.tokens import arrow.core.Either import arrow.core.left -import arrow.core.raise.Raise -import arrow.core.raise.recover -import arrow.core.right import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.mapper.mapToTokenListError import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -16,10 +13,11 @@ import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.flatMapConcat +import kotlinx.coroutines.flow.flatMapMerge +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map class GetTokenListUseCase( internal val currenciesRepository: CurrenciesRepository, @@ -28,53 +26,48 @@ class GetTokenListUseCase( internal val dispatchers: CoroutineDispatcherProvider, ) { + @OptIn(ExperimentalCoroutinesApi::class) operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = true): Flow> { - return channelFlow { - recover( - block = { - getTokenList(userWalletId, refresh).collectLatest { list -> - send(list.right()) - } + return getTokensStatuses(userWalletId, refresh).flatMapMerge flatMap@{ maybeTokens -> + maybeTokens.fold( + ifLeft = { error -> + flowOf(error.left()) }, - recover = { error -> - send(error.left()) + ifRight = { tokens -> + createTokenList(userWalletId, tokens) }, ) } } - private fun Raise.getTokenList(userWalletId: UserWalletId, refresh: Boolean): Flow { - return getTokensStatuses(userWalletId, refresh).flatMapConcat { tokens -> - createTokenList(userWalletId, tokens) - } - } - private fun Raise.getTokensStatuses( + private fun getTokensStatuses( userWalletId: UserWalletId, refresh: Boolean, - ): Flow> { + ): Flow>> { val operations = CurrenciesStatusesOperations( userWalletId = userWalletId, refresh = refresh, useCase = this@GetTokenListUseCase, - raise = this, - transformError = CurrenciesStatusesOperations.Error::mapToTokenListError, ) return operations.getCurrenciesStatusesFlow() + .map { maybeCurrenciesStatuses -> + maybeCurrenciesStatuses.mapLeft(CurrenciesStatusesOperations.Error::mapToTokenListError) + } } - private fun Raise.createTokenList( + private fun createTokenList( userWalletId: UserWalletId, tokens: Set, - ): Flow { + ): Flow> { val operations = TokenListOperations( userWalletId = userWalletId, tokens = tokens, useCase = this@GetTokenListUseCase, - raise = this, - transform = TokenListOperations.Error::mapToTokenListError, ) - return operations.getTokenListFlow() + return operations.getTokenListFlow().map { maybeTokenList -> + maybeTokenList.mapLeft(TokenListOperations.Error::mapToTokenListError) + } } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt index fe70f6aacd..07f86f5069 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListGroupingUseCase.kt @@ -1,10 +1,7 @@ package com.tangem.domain.tokens import arrow.core.Either -import arrow.core.raise.Raise -import arrow.core.raise.catch -import arrow.core.raise.either -import arrow.core.raise.ensure +import arrow.core.raise.* import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.error.mapper.mapToTokenListSortingError import com.tangem.domain.tokens.model.TokenList @@ -35,47 +32,40 @@ class ToggleTokenListGroupingUseCase( } } - private suspend fun Raise.groupTokens( - tokenList: TokenList.Ungrouped, - ): TokenList.GroupedByNetwork { - val sortingOperations = getSortingOperations(tokenList) - val tokens = sortingOperations.getTokens() + private fun Raise.groupTokens(tokenList: TokenList.Ungrouped): TokenList.GroupedByNetwork { + val sortingOperations = TokenListSortingOperations(tokenList) + val tokens = withError(TokenListSortingOperations.Error::mapToTokenListSortingError) { + sortingOperations.getTokens().bind() + } val networks = getNetworks(tokens.map { it.currency.networkId }.toSet()) return TokenList.GroupedByNetwork( - groups = sortingOperations.getGroupedTokens(networks), + groups = withError(TokenListSortingOperations.Error::mapToTokenListSortingError) { + sortingOperations.getGroupedTokens(networks).bind() + }, totalFiatBalance = tokenList.totalFiatBalance, sortedBy = sortingOperations.getSortType(), ) } - private suspend fun Raise.ungroupTokens( + private fun Raise.ungroupTokens( tokenList: TokenList.GroupedByNetwork, ): TokenList.Ungrouped { - val sortingOperations = getSortingOperations(tokenList) + val sortingOperations = TokenListSortingOperations(tokenList) return TokenList.Ungrouped( - currencies = sortingOperations.getTokens(), + currencies = withError(TokenListSortingOperations.Error::mapToTokenListSortingError) { + sortingOperations.getTokens().bind() + }, totalFiatBalance = tokenList.totalFiatBalance, sortedBy = sortingOperations.getSortType(), ) } - private fun Raise.getSortingOperations(tokenList: TokenList): TokenListSortingOperations<*> { - return TokenListSortingOperations( - tokenList = tokenList, - dispatchers = dispatchers, - raise = this, - transformError = TokenListSortingOperations.Error::mapToTokenListSortingError, + private fun Raise.getNetworks(networksIds: Set): Set { + return catch( + block = { networksRepository.getNetworks(networksIds) }, + catch = { raise(TokenListSortingError.DataError(it)) }, ) } - - private suspend fun Raise.getNetworks(networksIds: Set): Set { - return withContext(dispatchers.io) { - catch( - block = { networksRepository.getNetworks(networksIds) }, - catch = { raise(TokenListSortingError.DataError(it)) }, - ) - } - } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt index b91ce9521a..483b822928 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ToggleTokenListSortingUseCase.kt @@ -4,6 +4,7 @@ import arrow.core.Either import arrow.core.raise.Raise import arrow.core.raise.either import arrow.core.raise.ensure +import arrow.core.raise.withError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.error.mapper.mapToTokenListSortingError import com.tangem.domain.tokens.model.TokenList @@ -31,36 +32,37 @@ class ToggleTokenListSortingUseCase( } } - private suspend fun Raise.sortGroupedTokenList( + private fun Raise.sortGroupedTokenList( tokenList: TokenList.GroupedByNetwork, ): TokenList.GroupedByNetwork { val operations = getSortingOperations(tokenList) val networks = tokenList.groups.map { it.network }.toSet() return tokenList.copy( - groups = operations.getGroupedTokens(networks), + groups = withError(TokenListSortingOperations.Error::mapToTokenListSortingError) { + operations.getGroupedTokens(networks).bind() + }, sortedBy = operations.getSortType(), ) } - private suspend fun Raise.sortUngroupedTokenList( + private fun Raise.sortUngroupedTokenList( tokenList: TokenList.Ungrouped, ): TokenList.Ungrouped { val operations = getSortingOperations(tokenList) return tokenList.copy( - currencies = operations.getTokens(), + currencies = withError(TokenListSortingOperations.Error::mapToTokenListSortingError) { + operations.getTokens().bind() + }, sortedBy = operations.getSortType(), ) } - private fun Raise.getSortingOperations(tokenList: TokenList): TokenListSortingOperations<*> { + private fun getSortingOperations(tokenList: TokenList): TokenListSortingOperations { return TokenListSortingOperations( tokenList = tokenList, sortByBalance = tokenList.sortedBy != TokenList.SortType.BALANCE, - dispatchers = dispatchers, - raise = this, - transformError = TokenListSortingOperations.Error::mapToTokenListSortingError, ) } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt index 981f8785f2..51fc33c463 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/error/mapper/GetWalletTokenErrorMappers.kt @@ -3,7 +3,7 @@ package com.tangem.domain.tokens.error.mapper import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations -internal fun CurrenciesStatusesOperations.Error.mapToTokenError(): CurrencyError { +internal fun CurrenciesStatusesOperations.Error.mapToCurrencyError(): CurrencyError { return when (this) { is CurrenciesStatusesOperations.Error.DataError -> CurrencyError.DataError(this.cause) is CurrenciesStatusesOperations.Error.EmptyNetworksStatuses, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 1b23b9910c..6e1032e5c1 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -1,9 +1,7 @@ package com.tangem.domain.tokens.operations import arrow.core.* -import arrow.core.raise.Raise -import arrow.core.raise.catch -import com.tangem.domain.core.raise.DelegatedRaise +import arrow.core.raise.* import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.* import com.tangem.domain.tokens.models.Network @@ -11,97 +9,98 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* -import kotlinx.coroutines.withContext -@Suppress("LongParameterList") -internal class CurrenciesStatusesOperations( +internal class CurrenciesStatusesOperations( private val currenciesRepository: CurrenciesRepository, private val quotesRepository: QuotesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, private val refresh: Boolean, - private val dispatchers: CoroutineDispatcherProvider, - raise: Raise, - transformError: (Error) -> E, -) : DelegatedRaise(raise, transformError) { +) { constructor( userWalletId: UserWalletId, refresh: Boolean, useCase: GetTokenListUseCase, - raise: Raise, - transformError: (Error) -> E, ) : this( currenciesRepository = useCase.currenciesRepository, quotesRepository = useCase.quotesRepository, networksRepository = useCase.networksRepository, userWalletId = userWalletId, refresh = refresh, - dispatchers = useCase.dispatchers, - raise = raise, - transformError = transformError, ) - fun getCurrenciesStatusesFlow(): Flow> { - return getMultiCurrencyWalletCurrencies().flatMapConcat { - val currencies = it.toNonEmptySetOrNull() + @OptIn(ExperimentalCoroutinesApi::class) + fun getCurrenciesStatusesFlow(): Flow>> { + return getMultiCurrencyWalletCurrencies().flatMapMerge flatMap@{ maybeCurrencies -> + val nonEmptyCurrencies = maybeCurrencies.fold( + ifLeft = { error -> + return@flatMap flowOf(error.left()) + }, + ifRight = { it.toNonEmptySetOrNull() }, + ) ?: return@flatMap flowOf(emptySet().right()) - if (currencies == null) { - flowOf(emptySet()) - } else { - val currencyIdToNetworkId = currencies.associate { currency -> - currency.id to currency.networkId - } - val currenciesIds = requireNotNull(currencyIdToNetworkId.keys.toNonEmptySetOrNull()) { - "Currencies IDs cannot be empty" - } - val networksIds = requireNotNull(currencyIdToNetworkId.values.toNonEmptySetOrNull()) { - "Networks IDs cannot be empty" - } + val (networksIds, currenciesIds) = getIds(nonEmptyCurrencies) - combine(getQuotes(currenciesIds), getNetworksStatues(networksIds)) { quotes, networksStatuses -> - createTokensStatuses(currencies, quotes, networksStatuses) + combine( + getQuotes(currenciesIds), + getNetworksStatuses(networksIds), + ) { maybeQuotes, maybeNetworksStatuses -> + either { + createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes.bind(), maybeNetworksStatuses.bind()) } } } } - suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow { - val currency = getMultiCurrencyWalletCurrency(currencyId) + suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow> { + val currency = recover( + block = { getMultiCurrencyWalletCurrency(currencyId) }, + recover = { return flowOf(it.left()) }, + ) return getCurrencyStatusFlow(currency) } - suspend fun getPrimaryCurrencyStatusFlow(): Flow { - val currency = getPrimaryCurrency() + suspend fun getPrimaryCurrencyStatusFlow(): Flow> { + val currency = recover( + block = { getPrimaryCurrency() }, + recover = { return flowOf(it.left()) }, + ) return getCurrencyStatusFlow(currency) } - private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow { + private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow> { val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) - .map { quotes -> - quotes.singleOrNull { it.currencyId == currency.id } + .map { maybeQuotes -> + maybeQuotes.map { quotes -> + quotes.singleOrNull { it.currencyId == currency.id } + } } - val statusFlow = getNetworksStatues(nonEmptySetOf(currency.networkId)) - .map { statuses -> - statuses.singleOrNull { it.networkId == currency.networkId } + val statusFlow = getNetworksStatuses(nonEmptySetOf(currency.networkId)) + .map { maybeStatuses -> + maybeStatuses.map { statuses -> + statuses.singleOrNull { it.networkId == currency.networkId } + } } - return combine(quoteFlow, statusFlow) { quote, networkStatus -> - createStatus(currency, quote, networkStatus) + return combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus -> + either { + createStatus(currency, maybeQuote.bind(), maybeNetworkStatus.bind()) + } } } - private suspend fun createTokensStatuses( - tokens: Set, + private fun createCurrenciesStatuses( + currencies: NonEmptySet, quotes: Set, networkStatuses: Set, - ): Set = withContext(dispatchers.default) { - tokens.mapTo(hashSetOf()) { token -> + ): Set { + return currencies.mapTo(hashSetOf()) { token -> val quote = quotes.firstOrNull { it.currencyId == token.id } val networkStatus = networkStatuses.firstOrNull { it.networkId == token.networkId } @@ -109,7 +108,7 @@ internal class CurrenciesStatusesOperations( } } - private suspend fun createStatus( + private fun createStatus( token: CryptoCurrency, quote: Quote?, networkStatus: NetworkStatus?, @@ -118,44 +117,58 @@ internal class CurrenciesStatusesOperations( currency = token, quote = quote, networkStatus = networkStatus, - dispatchers = dispatchers, - raise = this, - transformError = { Error.UnableToCreateCurrencyStatus }, ) return currencyStatusOperations.createTokenStatus() } - private suspend fun getMultiCurrencyWalletCurrency(currencyId: CryptoCurrency.ID): CryptoCurrency { - return catch( - block = { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, currencyId) }, - catch = { raise(Error.DataError(it)) }, - ) - } - - private fun getMultiCurrencyWalletCurrencies(): Flow> { + private fun getMultiCurrencyWalletCurrencies(): Flow>> { return currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) - .catch { raise(Error.DataError(it)) } - .onEmpty { raise(Error.EmptyCurrencies) } + .map, Either>> { it.right() } + .catch { emit(Error.DataError(it).left()) } + .onEmpty { emit(Error.EmptyCurrencies.left()) } } - private suspend fun getPrimaryCurrency(): CryptoCurrency { + private suspend fun Raise.getMultiCurrencyWalletCurrency(currencyId: CryptoCurrency.ID): CryptoCurrency { + return Either.catch { currenciesRepository.getMultiCurrencyWalletCurrency(userWalletId, currencyId) } + .mapLeft { Error.DataError(it) } + .bind() + } + + private suspend fun Raise.getPrimaryCurrency(): CryptoCurrency { return catch( block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) }, catch = { raise(Error.DataError(it)) }, ) } - private fun getQuotes(tokensIds: NonEmptySet): Flow> { + private fun getQuotes(tokensIds: NonEmptySet): Flow>> { return quotesRepository.getQuotes(tokensIds, refresh) - .catch { raise(Error.DataError(it)) } - .onEmpty { raise(Error.EmptyQuotes) } + .map, Either>> { it.right() } + .catch { emit(Error.DataError(it).left()) } + .onEmpty { emit(Error.EmptyQuotes.left()) } } - private fun getNetworksStatues(networks: NonEmptySet): Flow> { + private fun getNetworksStatuses(networks: NonEmptySet): Flow>> { return networksRepository.getNetworkStatuses(userWalletId, networks, refresh) - .catch { raise(Error.DataError(it)) } - .onEmpty { raise(Error.EmptyNetworksStatuses) } + .map, Either>> { it.right() } + .catch { emit(Error.DataError(it).left()) } + .onEmpty { emit(Error.EmptyNetworksStatuses.left()) } + } + + private fun getIds( + currencies: NonEmptySet, + ): Pair, NonEmptySet> { + val currencyIdToNetworkId = currencies.associate { currency -> + currency.id to currency.networkId + } + val currenciesIds = currencyIdToNetworkId.keys.toNonEmptySetOrNull() + val networksIds = currencyIdToNetworkId.values.toNonEmptySetOrNull() + + requireNotNull(currenciesIds) { "Currencies IDs cannot be empty" } + requireNotNull(networksIds) { "Networks IDs cannot be empty" } + + return networksIds to currenciesIds } sealed class Error { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index 50f348f3d3..d654ee6cd5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -1,28 +1,18 @@ package com.tangem.domain.tokens.operations -import arrow.core.raise.Raise -import arrow.core.raise.ensureNotNull -import com.tangem.domain.core.raise.DelegatedRaise import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.model.Quote -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext import java.math.BigDecimal -internal class CurrencyStatusOperations( +internal class CurrencyStatusOperations( private val currency: CryptoCurrency, private val quote: Quote?, private val networkStatus: NetworkStatus?, - private val dispatchers: CoroutineDispatcherProvider, - raise: Raise, - transformError: (Error) -> OtherError, -) : DelegatedRaise(raise, transformError) { +) { - suspend fun createTokenStatus(): CryptoCurrencyStatus = withContext(dispatchers.default) { - CryptoCurrencyStatus(currency, createStatus()) - } + fun createTokenStatus(): CryptoCurrencyStatus = CryptoCurrencyStatus(currency, createStatus()) private fun createStatus(): CryptoCurrencyStatus.Status { return when (val status = networkStatus?.value) { @@ -35,9 +25,7 @@ internal class CurrencyStatusOperations( } private fun createStatus(status: NetworkStatus.Verified): CryptoCurrencyStatus.Status { - val amount = ensureNotNull(status.amounts[currency.id]) { - Error.UnableToFindAmount(currency.id) - } + val amount = status.amounts[currency.id] ?: return CryptoCurrencyStatus.Unreachable return when { currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom( @@ -67,9 +55,4 @@ internal class CurrencyStatusOperations( private fun calculateFiatAmount(amount: BigDecimal, fiatRate: BigDecimal): BigDecimal { return amount * fiatRate } - - sealed class Error { - - data class UnableToFindAmount(val currencyId: CryptoCurrency.ID) : Error() - } } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt index dd3cb367d5..2373f7f6ef 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt @@ -3,48 +3,44 @@ package com.tangem.domain.tokens.operations import arrow.core.NonEmptySet import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext import java.math.BigDecimal internal class TokenListFiatBalanceOperations( private val currencies: NonEmptySet, private val isAnyTokenLoading: Boolean, - private val dispatcher: CoroutineDispatcherProvider, ) { - suspend fun calculateFiatBalance(): TokenList.FiatBalance { - return withContext(dispatcher.single) { - var fiatBalance: TokenList.FiatBalance = TokenList.FiatBalance.Loading - if (isAnyTokenLoading) return@withContext fiatBalance + fun calculateFiatBalance(): TokenList.FiatBalance { + var fiatBalance: TokenList.FiatBalance = TokenList.FiatBalance.Loading + if (isAnyTokenLoading) return fiatBalance - for (token in currencies) { - when (val status = token.value) { - is CryptoCurrencyStatus.Loading -> { - fiatBalance = TokenList.FiatBalance.Loading - break - } - is CryptoCurrencyStatus.MissedDerivation, - is CryptoCurrencyStatus.Unreachable, - -> { - fiatBalance = TokenList.FiatBalance.Failed - break - } - is CryptoCurrencyStatus.NoAccount -> { - fiatBalance = recalculateBalanceForNoAccountStatus(fiatBalance) - } - is CryptoCurrencyStatus.Loaded -> { - fiatBalance = recalculateBalance(status, fiatBalance) - } - is CryptoCurrencyStatus.Custom -> { - fiatBalance = recalculateBalance(status, fiatBalance) - } + for (token in currencies) { + when (val status = token.value) { + is CryptoCurrencyStatus.Loading -> { + fiatBalance = TokenList.FiatBalance.Loading + break + } + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.Unreachable, + -> { + fiatBalance = TokenList.FiatBalance.Failed + break + } + is CryptoCurrencyStatus.NoAccount -> { + fiatBalance = recalculateBalanceForNoAccountStatus(fiatBalance) + } + is CryptoCurrencyStatus.Loaded -> { + fiatBalance = recalculateBalance(status, fiatBalance) + } + is CryptoCurrencyStatus.Custom -> { + fiatBalance = recalculateBalance(status, fiatBalance) } } - - fiatBalance } + + return fiatBalance } + private fun recalculateBalanceForNoAccountStatus(currentBalance: TokenList.FiatBalance): TokenList.FiatBalance { return with(currentBalance) { (this as? TokenList.FiatBalance.Loaded)?.copy( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index da6c780e24..ada01f316b 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -1,11 +1,7 @@ package com.tangem.domain.tokens.operations -import arrow.core.NonEmptySet -import arrow.core.raise.Raise -import arrow.core.raise.catch -import arrow.core.raise.ensureNotNull -import arrow.core.toNonEmptySetOrNull -import com.tangem.domain.core.raise.DelegatedRaise +import arrow.core.* +import arrow.core.raise.* import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList @@ -13,62 +9,55 @@ import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.* -import kotlinx.coroutines.withContext @Suppress("LongParameterList") -internal class TokenListOperations( +internal class TokenListOperations( private val currenciesRepository: CurrenciesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, private val tokens: Set, - private val dispatchers: CoroutineDispatcherProvider, - raise: Raise, - transform: (Error) -> E, -) : DelegatedRaise(raise, transform) { +) { constructor( userWalletId: UserWalletId, tokens: Set, useCase: GetTokenListUseCase, - raise: Raise, - transform: (Error) -> E, ) : this( currenciesRepository = useCase.currenciesRepository, networksRepository = useCase.networksRepository, userWalletId = userWalletId, tokens = tokens, - dispatchers = useCase.dispatchers, - raise = raise, - transform = transform, ) - fun getTokenListFlow(): Flow { - return combine(getIsGrouped(), getIsSortedByBalance()) { isGrouped, isSortedByBalance -> - createTokenList(isGrouped, isSortedByBalance) + fun getTokenListFlow(): Flow> { + return combine( + getIsGrouped(), + getIsSortedByBalance(), + ) { isGrouped, isSortedByBalance -> + either { + createTokenList(isGrouped.bind(), isSortedByBalance.bind()) + } } } - private suspend fun createTokenList(isGrouped: Boolean, isSortedByBalance: Boolean): TokenList { - return withContext(dispatchers.default) { - val tokensNes = tokens.toNonEmptySetOrNull() - ?: return@withContext TokenList.NotInitialized + private fun Raise.createTokenList(isGrouped: Boolean, isSortedByBalance: Boolean): TokenList { + val tokensNes = tokens.toNonEmptySetOrNull() + ?: return TokenList.NotInitialized - val isAnyTokenLoading = tokensNes.any { it.value is CryptoCurrencyStatus.Loading } - val fiatBalanceOperations = TokenListFiatBalanceOperations(tokensNes, isAnyTokenLoading, dispatchers) + val isAnyTokenLoading = tokensNes.any { it.value is CryptoCurrencyStatus.Loading } + val fiatBalanceOperations = TokenListFiatBalanceOperations(tokensNes, isAnyTokenLoading) - createTokenList( - tokens = tokensNes, - fiatBalance = fiatBalanceOperations.calculateFiatBalance(), - isAnyTokenLoading = isAnyTokenLoading, - isGrouped = isGrouped, - isSortedByBalance = isSortedByBalance, - ) - } + return createTokenList( + tokens = tokensNes, + fiatBalance = fiatBalanceOperations.calculateFiatBalance(), + isAnyTokenLoading = isAnyTokenLoading, + isGrouped = isGrouped, + isSortedByBalance = isSortedByBalance, + ) } - private suspend fun createTokenList( + private fun Raise.createTokenList( tokens: NonEmptySet, fiatBalance: TokenList.FiatBalance, isAnyTokenLoading: Boolean, @@ -79,19 +68,14 @@ internal class TokenListOperations( currencies = tokens, isAnyTokenLoading = isAnyTokenLoading, sortByBalance = isSortedByBalance, - dispatchers = dispatchers, - raise = this, - transformError = { e -> - Error.fromTokenListOperations(e) { createUnsortedUngroupedTokenList(tokens, fiatBalance) } - }, ) return createTokenList(tokens, sortingOperations, fiatBalance, isGrouped) } - private suspend fun createTokenList( + private fun Raise.createTokenList( tokens: NonEmptySet, - sortingOperations: TokenListSortingOperations<*>, + sortingOperations: TokenListSortingOperations, fiatBalance: TokenList.FiatBalance, isGrouped: Boolean, ): TokenList { @@ -108,37 +92,46 @@ internal class TokenListOperations( } } - private suspend fun getNetworks(tokensNes: NonEmptySet): Set { - return withContext(dispatchers.io) { - val networksIds = tokensNes.map { it.currency.networkId }.toNonEmptySet() - catch( - block = { networksRepository.getNetworks(networksIds) }, - catch = { raise(Error.DataError(it)) }, - ) - } + private fun Raise.getNetworks(tokensNes: NonEmptySet): Set { + val networksIds = tokensNes.map { it.currency.networkId }.toNonEmptySet() + + return catch( + block = { networksRepository.getNetworks(networksIds) }, + catch = { raise(Error.DataError(it)) }, + ) } - private suspend fun createUngroupedTokenList( - sortingOperations: TokenListSortingOperations<*>, + private fun Raise.createUngroupedTokenList( + sortingOperations: TokenListSortingOperations, fiatBalance: TokenList.FiatBalance, ): TokenList.Ungrouped = TokenList.Ungrouped( sortedBy = sortingOperations.getSortType(), totalFiatBalance = fiatBalance, - currencies = sortingOperations.getTokens(), + currencies = withError( + transform = { e -> + Error.fromTokenListOperations(e) { createUnsortedUngroupedTokenList(tokens, fiatBalance) } + }, + block = { sortingOperations.getTokens().bind() }, + ), ) - private suspend fun createGroupedTokenList( - sortingOperations: TokenListSortingOperations<*>, + private fun Raise.createGroupedTokenList( + sortingOperations: TokenListSortingOperations, fiatBalance: TokenList.FiatBalance, networks: NonEmptySet, ): TokenList.GroupedByNetwork = TokenList.GroupedByNetwork( sortedBy = sortingOperations.getSortType(), totalFiatBalance = fiatBalance, - groups = sortingOperations.getGroupedTokens(networks), + groups = withError( + transform = { e -> + Error.fromTokenListOperations(e) { createUnsortedUngroupedTokenList(tokens, fiatBalance) } + }, + block = { sortingOperations.getGroupedTokens(networks).bind() }, + ), ) private fun createUnsortedUngroupedTokenList( - tokens: NonEmptySet, + tokens: Set, fiatBalance: TokenList.FiatBalance, ): TokenList.Ungrouped { return TokenList.Ungrouped( @@ -148,18 +141,18 @@ internal class TokenListOperations( ) } - private fun getIsGrouped(): Flow { + private fun getIsGrouped(): Flow> { return currenciesRepository.isTokensGrouped(userWalletId) - .catch { raise(Error.DataError(it)) } - .onEmpty { emit(value = false) } - .flowOn(dispatchers.io) + .map> { it.right() } + .catch { emit(Error.DataError(it).left()) } + .onEmpty { emit(value = false.right()) } } - private fun getIsSortedByBalance(): Flow { + private fun getIsSortedByBalance(): Flow> { return currenciesRepository.isTokensSortedByBalance(userWalletId) - .catch { raise(Error.DataError(it)) } - .onEmpty { emit(value = false) } - .flowOn(dispatchers.io) + .map> { it.right() } + .catch { emit(Error.DataError(it).left()) } + .onEmpty { emit(value = false.right()) } } sealed class Error { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt index 06c3041651..5bf58c6cf7 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt @@ -1,33 +1,26 @@ package com.tangem.domain.tokens.operations +import arrow.core.Either import arrow.core.NonEmptySet import arrow.core.raise.Raise +import arrow.core.raise.either import arrow.core.raise.ensure import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptySetOrNull -import com.tangem.domain.core.raise.DelegatedRaise import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.models.Network -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import kotlinx.coroutines.withContext import java.math.BigDecimal -internal class TokenListSortingOperations( +internal class TokenListSortingOperations( private val currencies: Set, private val isAnyTokenLoading: Boolean, private val sortByBalance: Boolean, - private val dispatchers: CoroutineDispatcherProvider, - raise: Raise, - transformError: (Error) -> E, -) : DelegatedRaise(raise, transformError) { +) { constructor( tokenList: TokenList, - dispatchers: CoroutineDispatcherProvider, - raise: Raise, - transformError: (Error) -> E, sortByBalance: Boolean = tokenList.sortedBy == TokenList.SortType.BALANCE, isAnyTokenLoading: Boolean = tokenList.totalFiatBalance is TokenList.FiatBalance.Loading, ) : this( @@ -38,39 +31,32 @@ internal class TokenListSortingOperations( }, isAnyTokenLoading = isAnyTokenLoading, sortByBalance = sortByBalance, - dispatchers = dispatchers, - raise = raise, - transformError = transformError, ) - suspend fun getGroupedTokens(networks: Set): NonEmptySet { - return withContext(dispatchers.default) { - ensure(currencies.isNotEmpty()) { Error.EmptyTokens } - val networksNes = ensureNotNull(networks.toNonEmptySetOrNull()) { - Error.EmptyNetworks - } + fun getGroupedTokens(networks: Set): Either> = either { + ensure(currencies.isNotEmpty()) { Error.EmptyTokens } + val networksNes = ensureNotNull(networks.toNonEmptySetOrNull()) { + Error.EmptyNetworks + } - if (sortByBalance) { - groupAndSortTokensByBalance(networksNes) - } else { - groupTokens(networksNes) - } + if (sortByBalance) { + groupAndSortTokensByBalance(networksNes) + } else { + groupTokens(networksNes) } } - suspend fun getTokens(): NonEmptySet { - return withContext(dispatchers.default) { - val tokensNes = ensureNotNull(currencies.toNonEmptySetOrNull()) { - Error.EmptyTokens - } - - if (sortByBalance) sortTokensByBalance(tokensNes) else tokensNes + fun getTokens(): Either> = either { + val tokensNes = ensureNotNull(currencies.toNonEmptySetOrNull()) { + Error.EmptyTokens } + + if (sortByBalance) sortTokensByBalance(tokensNes) else tokensNes } - fun getSortType() = if (sortByBalance) TokenList.SortType.BALANCE else TokenList.SortType.NONE + fun getSortType(): TokenList.SortType = if (sortByBalance) TokenList.SortType.BALANCE else TokenList.SortType.NONE - private fun groupTokens(networks: NonEmptySet): NonEmptySet { + private fun Raise.groupTokens(networks: NonEmptySet): NonEmptySet { val groupedTokens = currencies .groupBy { it.currency.networkId } .map { (networkId, tokens) -> @@ -88,7 +74,7 @@ internal class TokenListSortingOperations( return ensureNotNull(groupedTokens) { Error.EmptyTokens } } - private fun groupAndSortTokensByBalance(networks: NonEmptySet): NonEmptySet { + private fun Raise.groupAndSortTokensByBalance(networks: NonEmptySet): NonEmptySet { val groupsWithSortedTokens = groupTokens(networks) .map { group -> val tokens = group.currencies as? NonEmptySet diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index 1d5cfd09c5..e7dfb90fd8 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -19,6 +19,7 @@ import com.tangem.domain.tokens.repository.MockQuotesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider import junit.framework.TestCase.assertEquals +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.* import kotlinx.coroutines.test.runTest import org.junit.Test @@ -145,7 +146,7 @@ internal class GetTokenListUseCaseTest { tokens = flowOf( MockTokens.tokens.right(), error, - ), + ).map { delay(timeMillis = 1_000); it }, ) // When diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index 75084dad74..b67250b33b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -19,7 +19,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter( private val CryptoCurrencyStatus.networkIconResId: Int? @DrawableRes get() { // TODO: [REDACTED_JIRA] - return if (currency is CryptoCurrency.Token) null else R.drawable.img_eth_22 + return if (currency is CryptoCurrency.Coin) null else R.drawable.img_eth_22 } private val CryptoCurrencyStatus.tokenIconResId: Int diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 06a5bc01d8..790f87d7f0 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -40,7 +40,7 @@ appsflyer = "6.5.1" armadillo = "0.9.0" coil = "2.1.0" compose-shimmer = "1.0.3" -coroutine = "1.5.2" +coroutine = "1.7.2" desugarJdkLibs = "1.1.5" firebase = "26.0.0" googleMaterialComponent = "1.6.1" From 2336c837cb9e96b496d4d7b4f0724ef540b2a41b Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 18:44:09 +0800 Subject: [PATCH 09/52] Updated on 2026-08-14 --- .../tap/di/domain/WalletsDomainModule.kt | 17 +++++++-- core/res/src/main/res/values-ru/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 1 + .../wallets/models/GetSelectedWalletError.kt | 8 ++++ .../wallets/models/SelectWalletError.kt | 8 ++++ .../usecase/GetSelectedWalletUseCase.kt | 32 ++++++++++++++++ .../wallets/usecase/SelectWalletUseCase.kt | 37 +++++++++++++++++++ .../src/main/res/drawable/ic_currency_24.xml | 9 +++++ 9 files changed, 110 insertions(+), 4 deletions(-) create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/models/SelectWalletError.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt create mode 100644 domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SelectWalletUseCase.kt create mode 100644 features/wallet/impl/src/main/res/drawable/ic_currency_24.xml diff --git a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt index 6c0ebccfc6..92f1340058 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/WalletsDomainModule.kt @@ -2,10 +2,7 @@ package com.tangem.tap.di.domain import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.WalletsStateHolder -import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase -import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.domain.wallets.usecase.SaveWalletUseCase -import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase +import com.tangem.domain.wallets.usecase.* import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -22,6 +19,12 @@ internal object WalletsDomainModule { return GetWalletsUseCase(walletsStateHolder = walletsStateHolder) } + @Provides + @ViewModelScoped + fun providesGetSelectedWalletUseCase(walletsStateHolder: WalletsStateHolder): GetSelectedWalletUseCase { + return GetSelectedWalletUseCase(walletsStateHolder = walletsStateHolder) + } + @Provides @ViewModelScoped fun providesSaveWalletUseCase(walletsStateHolder: WalletsStateHolder): SaveWalletUseCase { @@ -39,4 +42,10 @@ internal object WalletsDomainModule { fun providesUnlockWalletUseCase(walletsStateHolder: WalletsStateHolder): UnlockWalletsUseCase { return UnlockWalletsUseCase(walletsStateHolder = walletsStateHolder) } + + @Provides + @ViewModelScoped + fun providesSelectWalletUseCase(walletsStateHolder: WalletsStateHolder): SelectWalletUseCase { + return SelectWalletUseCase(walletsStateHolder = walletsStateHolder) + } } \ No newline at end of file diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 7076371f81..6966257f9a 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -177,6 +177,7 @@ По вашему промокоду не было покупки кошелька, а значит вы не можете получить бонус. Купите кошелек Tangem, отсканируйте его в приложении и получите бонус. Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту Отсканируйте карту + Токены Вам необходимо установить единый код доступа для защиты всех ваших карт Защита Позже вы сможете установить индивидуальный код доступа для каждой карты diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 0b2d148627..87318128aa 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -150,6 +150,7 @@ 該金額不包括您的部分資金 要訪問所有的網路您需要掃描卡片 掃描卡片 + 代幣 您必須設置一個單一的訪問代碼來保護您的所有錢包 保護 您可以稍後在每張卡上設置單獨的訪問密碼 diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index e207afb74d..b958ba0f9f 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -174,6 +174,7 @@ There was no purchase of a wallet using your promo code, which means you cannot receive a bonus. Buy Tangem wallet, scan it in the app, and get the bonus. To access all the networks you need to scan the card Scan your card + Tokens You have to set up a single access code to protect all your wallets Protect You can set up an individual access code on each card later diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt new file mode 100644 index 0000000000..ad0867fa6e --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/GetSelectedWalletError.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.wallets.models + +sealed interface GetSelectedWalletError { + + object DataError : GetSelectedWalletError + + object NoUserWalletSelected : GetSelectedWalletError +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SelectWalletError.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SelectWalletError.kt new file mode 100644 index 0000000000..a357f06ca9 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/models/SelectWalletError.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.wallets.models + +sealed interface SelectWalletError { + + object DataError : SelectWalletError + + object UnableToSelectUserWallet : SelectWalletError +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt new file mode 100644 index 0000000000..89d6fe1405 --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/GetSelectedWalletUseCase.kt @@ -0,0 +1,32 @@ +package com.tangem.domain.wallets.usecase + +import arrow.core.Either +import arrow.core.raise.either +import arrow.core.raise.ensureNotNull +import com.tangem.domain.wallets.legacy.WalletsStateHolder +import com.tangem.domain.wallets.models.GetSelectedWalletError +import com.tangem.domain.wallets.models.UserWallet + +/** + * Use case for getting selected wallet + * + * @property walletsStateHolder state holder for getting static initialized 'userWalletsListManager' + * +[REDACTED_AUTHOR] + */ +class GetSelectedWalletUseCase(private val walletsStateHolder: WalletsStateHolder) { + + operator fun invoke(): Either { + return either { + val userWalletsListManager = ensureNotNull( + value = walletsStateHolder.userWalletsListManager, + raise = { GetSelectedWalletError.DataError }, + ) + + ensureNotNull( + value = userWalletsListManager.selectedUserWalletSync, + raise = { GetSelectedWalletError.NoUserWalletSelected }, + ) + } + } +} \ No newline at end of file diff --git a/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SelectWalletUseCase.kt b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SelectWalletUseCase.kt new file mode 100644 index 0000000000..aa16ad812b --- /dev/null +++ b/domain/wallets/src/main/java/com/tangem/domain/wallets/usecase/SelectWalletUseCase.kt @@ -0,0 +1,37 @@ +package com.tangem.domain.wallets.usecase + +import arrow.core.Either +import arrow.core.left +import arrow.core.raise.either +import arrow.core.raise.ensureNotNull +import arrow.core.right +import com.tangem.common.doOnFailure +import com.tangem.common.doOnSuccess +import com.tangem.domain.wallets.legacy.WalletsStateHolder +import com.tangem.domain.wallets.models.SelectWalletError +import com.tangem.domain.wallets.models.UserWalletId + +/** + * Use case for selecting wallet + * + * @property walletsStateHolder state holder for getting static initialized 'userWalletsListManager' + * +[REDACTED_AUTHOR] + */ +class SelectWalletUseCase(private val walletsStateHolder: WalletsStateHolder) { + + suspend operator fun invoke(userWalletId: UserWalletId): Either { + return either { + val userWalletsListManager = ensureNotNull( + value = walletsStateHolder.userWalletsListManager, + raise = { SelectWalletError.DataError }, + ) + + userWalletsListManager.select(userWalletId) + .doOnSuccess { return Unit.right() } + .doOnFailure { return SelectWalletError.UnableToSelectUserWallet.left() } + + return Unit.right() + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/res/drawable/ic_currency_24.xml b/features/wallet/impl/src/main/res/drawable/ic_currency_24.xml new file mode 100644 index 0000000000..bee87c2b68 --- /dev/null +++ b/features/wallet/impl/src/main/res/drawable/ic_currency_24.xml @@ -0,0 +1,9 @@ + + + From c266b4c8c63aba7b18fdb8e80131389beab2fa2d Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 17:12:38 +0800 Subject: [PATCH 10/52] Updated on 2026-08-14 --- .../presentation/common/WalletPreviewData.kt | 18 +++++----- .../wallet/state/WalletSingleCurrencyState.kt | 7 ++-- .../state/components/WalletManageButton.kt | 36 ++++++++++++++----- .../state/components/WalletTokensListState.kt | 23 ++++++++---- .../state/components/WalletTxHistoryState.kt | 33 ++++++++++++++++- .../factory/WalletSkeletonStateConverter.kt | 5 +-- .../state/factory/WalletStateFactory.kt | 8 ++--- .../WalletLoadedTxHistoryConverter.kt | 8 ++--- .../WalletLoadingTxHistoryConverter.kt | 6 +--- .../wallet/ui/components/WalletsList.kt | 7 +++- .../multicurrency/MultiCurrencyContent.kt | 12 ++++--- .../multicurrency/MultiCurrencyContentItem.kt | 3 +- .../MultiCurrencyOrganizeButton.kt | 4 ++- .../singlecurrency/SingleCurrencyContent.kt | 14 +++++--- .../SingleCurrencyControlButtons.kt | 11 +++--- .../SingleCurrencyMarketPriceBlock.kt | 4 ++- .../utils/TokenListToContentItemsConverter.kt | 3 +- 17 files changed, 137 insertions(+), 65 deletions(-) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index bce4d536e1..56a3cec48c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -5,6 +5,7 @@ import com.tangem.core.ui.R import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.components.transactions.TransactionState +import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState @@ -13,6 +14,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListS import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder import com.tangem.feature.wallet.presentation.wallet.state.* import com.tangem.feature.wallet.presentation.wallet.state.components.* +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.flow.flowOf @@ -260,8 +262,8 @@ internal object WalletPreviewData { walletsListConfig = walletListConfig, tokensListState = WalletTokensListState.Content( persistentListOf( - WalletTokensListState.TokensListItemState.NetworkGroupTitle("Bitcoin"), - WalletTokensListState.TokensListItemState.Token( + TokensListItemState.NetworkGroupTitle(TextReference.Str("Bitcoin")), + TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_1", name = "Ethereum", @@ -270,7 +272,7 @@ internal object WalletPreviewData { amount = "1,89340821 ETH", ), ), - WalletTokensListState.TokensListItemState.Token( + TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_2", name = "Ethereum", @@ -279,7 +281,7 @@ internal object WalletPreviewData { amount = "1,89340821 ETH", ), ), - WalletTokensListState.TokensListItemState.Token( + TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_3", name = "Ethereum", @@ -288,7 +290,7 @@ internal object WalletPreviewData { amount = "1,89340821 ETH", ), ), - WalletTokensListState.TokensListItemState.Token( + TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_4", name = "Ethereum", @@ -297,8 +299,8 @@ internal object WalletPreviewData { amount = "1,89340821 ETH", ), ), - WalletTokensListState.TokensListItemState.NetworkGroupTitle("Ethereum"), - WalletTokensListState.TokensListItemState.Token( + TokensListItemState.NetworkGroupTitle(TextReference.Str("Ethereum")), + TokensListItemState.Token( tokenItemVisibleState.copy( id = "token_5", name = "Ethereum", @@ -334,7 +336,7 @@ internal object WalletPreviewData { onRefresh = {}, ), notifications = persistentListOf(WalletNotification.LikeTangemApp(onClick = {})), - buttons = manageButtons.map(WalletManageButton::config).toPersistentList(), + buttons = manageButtons, bottomSheetConfig = bottomSheet, marketPriceBlockState = MarketPriceBlockState.Content( currencyName = "BTC", diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt index 2a01cb5668..f29b229afd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -1,6 +1,5 @@ package com.tangem.feature.wallet.presentation.wallet.state -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList @@ -14,7 +13,7 @@ import kotlinx.collections.immutable.persistentListOf internal sealed class WalletSingleCurrencyState : WalletStateHolder() { /** Manage buttons */ - abstract val buttons: ImmutableList + abstract val buttons: ImmutableList /** Market price block state */ abstract val marketPriceBlockState: MarketPriceBlockState? @@ -29,7 +28,7 @@ internal sealed class WalletSingleCurrencyState : WalletStateHolder() { override val pullToRefreshConfig: WalletPullToRefreshConfig, override val notifications: ImmutableList, override val bottomSheetConfig: WalletBottomSheetConfig?, - override val buttons: ImmutableList, + override val buttons: ImmutableList, override val marketPriceBlockState: MarketPriceBlockState, override val txHistoryState: WalletTxHistoryState, ) : WalletSingleCurrencyState() @@ -39,7 +38,7 @@ internal sealed class WalletSingleCurrencyState : WalletStateHolder() { override val topBarConfig: WalletTopBarConfig, override val walletsListConfig: WalletsListConfig, override val pullToRefreshConfig: WalletPullToRefreshConfig, - override val buttons: ImmutableList, + override val buttons: ImmutableList, override val onUnlockWalletsNotificationClick: () -> Unit, override val onUnlockClick: () -> Unit, override val onScanClick: () -> Unit, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt index 61441600c3..6d6055c045 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletManageButton.kt @@ -13,16 +13,34 @@ import com.tangem.feature.wallet.impl.R */ sealed class WalletManageButton(val config: ActionButtonConfig) { + /** Lambda be invoked when manage button is clicked */ + abstract val onClick: (() -> Unit)? + /** * Buy * * @param onClick lambda be invoked when manage button is clicked */ - data class Buy(val onClick: () -> Unit) : WalletManageButton( + data class Buy(override val onClick: (() -> Unit)? = null) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.common_buy), iconResId = R.drawable.ic_plus_24, - onClick = onClick, + onClick = onClick ?: {}, + enabled = onClick != null, + ), + ) + + /** + * Sell + * + * @param onClick lambda be invoked when manage button is clicked + */ + data class Sell(override val onClick: (() -> Unit)? = null) : WalletManageButton( + config = ActionButtonConfig( + text = TextReference.Res(id = R.string.common_sell), + iconResId = R.drawable.ic_currency_24, + onClick = onClick ?: {}, + enabled = onClick != null, ), ) @@ -31,11 +49,12 @@ sealed class WalletManageButton(val config: ActionButtonConfig) { * * @param onClick lambda be invoked when manage button is clicked */ - data class Send(val onClick: () -> Unit) : WalletManageButton( + data class Send(override val onClick: (() -> Unit)? = null) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.common_send), iconResId = R.drawable.ic_arrow_up_24, - onClick = onClick, + onClick = onClick ?: {}, + enabled = onClick != null, ), ) @@ -44,7 +63,7 @@ sealed class WalletManageButton(val config: ActionButtonConfig) { * * @param onClick lambda be invoked when manage button is clicked */ - data class Receive(val onClick: () -> Unit) : WalletManageButton( + data class Receive(override val onClick: () -> Unit) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.common_receive), iconResId = R.drawable.ic_arrow_down_24, @@ -57,11 +76,12 @@ sealed class WalletManageButton(val config: ActionButtonConfig) { * * @param onClick lambda be invoked when manage button is clicked */ - data class Exchange(val onClick: () -> Unit) : WalletManageButton( + data class Exchange(override val onClick: (() -> Unit)? = null) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.common_exchange), iconResId = R.drawable.ic_exchange_vertical_24, - onClick = onClick, + onClick = onClick ?: {}, + enabled = onClick != null, ), ) @@ -70,7 +90,7 @@ sealed class WalletManageButton(val config: ActionButtonConfig) { * * @param onClick lambda be invoked when manage button is clicked */ - data class CopyAddress(val onClick: () -> Unit) : WalletManageButton( + data class CopyAddress(override val onClick: () -> Unit) : WalletManageButton( config = ActionButtonConfig( text = TextReference.Res(id = R.string.common_copy_address), iconResId = R.drawable.ic_copy_24, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index 9fa10a5446..b9505c2802 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -1,5 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.components +import com.tangem.core.ui.extensions.TextReference +import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -12,17 +14,26 @@ import kotlinx.collections.immutable.persistentListOf * [REDACTED_AUTHOR] */ -// TODO: Finalize strings [REDACTED_JIRA] internal sealed class WalletTokensListState( open val items: ImmutableList, open val onOrganizeTokensClick: (() -> Unit)?, ) { + /** Loading content state */ + object Loading : WalletTokensListState( + items = persistentListOf( + TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), + TokensListItemState.Token(state = TokenItemState.Loading), + TokensListItemState.Token(state = TokenItemState.Loading), + ), + onOrganizeTokensClick = null, + ) + /** * Content state * - * @property items content items - * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked + * @property items content items + * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked */ data class Content( override val items: ImmutableList, @@ -33,7 +44,7 @@ internal sealed class WalletTokensListState( object Locked : WalletTokensListState( items = persistentListOf( - TokensListItemState.NetworkGroupTitle(networkName = "Tokens"), + TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), TokensListItemState.Token(state = TokenItemState.Loading), ), onOrganizeTokensClick = null, @@ -46,9 +57,9 @@ internal sealed class WalletTokensListState( /** * Network group title item * - * @property networkName network name + * @property value network name */ - data class NetworkGroupTitle(val networkName: String) : TokensListItemState + data class NetworkGroupTitle(val value: TextReference) : TokensListItemState /** * Token item diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt index b09a0410c8..66448a4f71 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt @@ -20,7 +20,38 @@ internal sealed interface WalletTxHistoryState { sealed class ContentState(open val items: Flow>) : WalletTxHistoryState /** - * Content state + * Loading state + * + * @property onExploreClick lambda be invoke when explore button was clicked + */ + data class Loading(val onExploreClick: () -> Unit) : ContentState( + items = flowOf( + PagingData.from( + listOf( + TxHistoryItemState.Title(onExploreClick = onExploreClick), + TxHistoryItemState.Transaction(state = TransactionState.Loading), + ), + ), + ), + ) + + /** + * Wallet transaction history state with loading transactions + * + * @property itemsCount count of loading transactions + */ + data class ContentWithLoadingItems(val itemsCount: Int) : ContentState( + items = flowOf( + value = PagingData.from( + data = buildList(capacity = itemsCount) { + add(TxHistoryItemState.Transaction(state = TransactionState.Loading)) + }, + ), + ), + ) + + /** + * Wallet transaction history state with content * * @property items content items */ diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index cd97249197..9289845a10 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -1,7 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import androidx.paging.PagingData -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver @@ -122,7 +121,7 @@ internal class WalletSkeletonStateConverter( } // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { + private fun getButtons(): ImmutableList { return persistentListOf( WalletManageButton.Buy(onClick = {}), WalletManageButton.Send(onClick = {}), @@ -130,8 +129,6 @@ internal class WalletSkeletonStateConverter( WalletManageButton.Exchange(onClick = {}), WalletManageButton.CopyAddress(onClick = {}), ) - .map(WalletManageButton::config) - .toImmutableList() } data class SkeletonModel( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 69a67bea5e..4d31745689 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -3,13 +3,12 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError -import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState @@ -24,7 +23,6 @@ import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.Wal import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow /** @@ -155,7 +153,7 @@ internal class WalletStateFactory( } // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { + private fun getButtons(): ImmutableList { return persistentListOf( WalletManageButton.Buy(onClick = {}), WalletManageButton.Send(onClick = {}), @@ -163,7 +161,5 @@ internal class WalletStateFactory( WalletManageButton.Exchange(onClick = {}), WalletManageButton.CopyAddress(onClick = {}), ) - .map(WalletManageButton::config) - .toImmutableList() } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index 1364a89682..801c6556a2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -3,11 +3,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryItem +import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton @@ -16,7 +15,6 @@ import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickInten import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.Flow /** @@ -78,7 +76,7 @@ internal class WalletLoadedTxHistoryConverter( } // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { + private fun getButtons(): ImmutableList { return persistentListOf( WalletManageButton.Buy(onClick = {}), WalletManageButton.Send(onClick = {}), @@ -86,8 +84,6 @@ internal class WalletLoadedTxHistoryConverter( WalletManageButton.Exchange(onClick = {}), WalletManageButton.CopyAddress(onClick = {}), ) - .map(WalletManageButton::config) - .toImmutableList() } private fun getLoadingMarketPriceBlockState(): MarketPriceBlockState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index 8ea55731fd..a43a3f8355 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -3,7 +3,6 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.TransactionState import com.tangem.domain.common.CardTypesResolver @@ -16,7 +15,6 @@ import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickInten import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf -import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.flowOf /** @@ -85,7 +83,7 @@ internal class WalletLoadingTxHistoryConverter( } // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { + private fun getButtons(): ImmutableList { return persistentListOf( WalletManageButton.Buy(onClick = {}), WalletManageButton.Send(onClick = {}), @@ -93,8 +91,6 @@ internal class WalletLoadingTxHistoryConverter( WalletManageButton.Exchange(onClick = {}), WalletManageButton.CopyAddress(onClick = {}), ) - .map(WalletManageButton::config) - .toImmutableList() } private fun getLoadingMarketPriceBlockState(): MarketPriceBlockState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index b2505603a2..dcae40e426 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -46,7 +46,12 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState), ) { items(items = config.wallets, key = { it.id.stringValue }) { state -> - WalletCard(state = state, modifier = Modifier.width(itemWidth)) + WalletCard( + state = state, + modifier = Modifier + .animateItemPlacement() + .width(itemWidth), + ) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 331d8de5dd..88112d5865 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.ui.Modifier @@ -14,6 +15,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletConten * [REDACTED_AUTHOR] */ +@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifier: Modifier = Modifier) { itemsIndexed( items = state.items, @@ -21,10 +23,12 @@ internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifie itemContent = { index, item -> MultiCurrencyContentItem( state = item, - modifier = modifier.walletContentItemDecoration( - currentIndex = index, - lastIndex = state.items.lastIndex, - ), + modifier = modifier + .animateItemPlacement() + .walletContentItemDecoration( + currentIndex = index, + lastIndex = state.items.lastIndex, + ), ) }, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt index 3cdfd7517d..a9ac43d4ab 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContentItem.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrenc import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.tangem.core.ui.extensions.resolveReference import com.tangem.feature.wallet.presentation.common.component.NetworkGroupItem import com.tangem.feature.wallet.presentation.common.component.TokenItem import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState @@ -18,7 +19,7 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletToke internal fun MultiCurrencyContentItem(state: WalletTokensListState.TokensListItemState, modifier: Modifier = Modifier) { when (state) { is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> { - NetworkGroupItem(networkName = state.networkName, modifier = modifier) + NetworkGroupItem(networkName = state.value.resolveReference(), modifier = modifier) } is WalletTokensListState.TokensListItemState.Token -> { TokenItem(state = state.state, modifier = modifier) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt index a8fb1add43..a995ca2715 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier @@ -11,6 +12,7 @@ import androidx.compose.ui.Modifier * [REDACTED_AUTHOR] */ +@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.organizeButton(onClick: (() -> Unit)?, modifier: Modifier = Modifier) { - item { OrganizeTokensButton(onClick = onClick, modifier = modifier) } + item { OrganizeTokensButton(onClick = onClick, modifier = modifier.animateItemPlacement()) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt index d70b42ab44..3d7d9de32a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope @@ -52,6 +53,7 @@ internal fun LazyListScope.txHistoryItems( } } +@OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.contentItems( txHistoryItems: LazyPagingItems, modifier: Modifier = Modifier, @@ -64,20 +66,24 @@ private fun LazyListScope.contentItems( SingleCurrencyContentItem( state = item, - modifier = modifier.walletContentItemDecoration( - currentIndex = index, - lastIndex = txHistoryItems.itemSnapshotList.lastIndex, - ), + modifier = modifier + .animateItemPlacement() + .walletContentItemDecoration( + currentIndex = index, + lastIndex = txHistoryItems.itemSnapshotList.lastIndex, + ), ) }, ) } +@OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) { item { EmptyTransactionBlock( state = state, modifier = modifier + .animateItemPlacement() .padding(horizontal = TangemTheme.dimens.spacing16, vertical = TangemTheme.dimens.spacing12) .fillMaxWidth(), ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt index d118b99fbc..f88ea6d07f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt @@ -1,12 +1,14 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import com.tangem.core.ui.components.buttons.HorizontalActionChips -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton import kotlinx.collections.immutable.ImmutableList +import kotlinx.collections.immutable.toImmutableList /** * Single currency control buttons. Like, "Buy", "Sell", etc @@ -16,11 +18,12 @@ import kotlinx.collections.immutable.ImmutableList * [REDACTED_AUTHOR] */ -internal fun LazyListScope.controlButtons(configs: ImmutableList, modifier: Modifier = Modifier) { +@OptIn(ExperimentalFoundationApi::class) +internal fun LazyListScope.controlButtons(configs: ImmutableList, modifier: Modifier = Modifier) { item { HorizontalActionChips( - buttons = configs, - modifier = modifier, + buttons = configs.map(WalletManageButton::config).toImmutableList(), + modifier = modifier.animateItemPlacement(), contentPadding = PaddingValues(horizontal = TangemTheme.dimens.spacing16), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt index 5e8d88c954..b839d42baf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import com.tangem.core.ui.components.marketprice.MarketPriceBlock @@ -13,6 +14,7 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState * [REDACTED_AUTHOR] */ +@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.marketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) { - item { MarketPriceBlock(state = state, modifier = modifier) } + item { MarketPriceBlock(state = state, modifier = modifier.animateItemPlacement()) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index e76e7535f6..d20d628c57 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.utils +import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList @@ -53,7 +54,7 @@ internal class TokenListToContentItemsConverter( } private fun MutableList.addGroup(group: NetworkGroup): List { - this.add(TokensListItemState.NetworkGroupTitle(group.network.name)) + this.add(TokensListItemState.NetworkGroupTitle(TextReference.Str(group.network.name))) group.currencies.forEach { token -> this.addToken(token) From 31e4cafad07bcf9ecac7b2052485b176d5944faf Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Aug 2023 16:10:09 +0300 Subject: [PATCH 11/52] Updated on 2026-08-14 --- .../twins/CreateSecondTwinWalletTask.kt | 5 +++ .../tap/domain/twins/IncompatibleTwinCard.kt | 12 +++++ core/res/src/main/res/values-ru/strings.xml | 1 + .../src/main/res/values-zh-rTW/strings.xml | 1 + core/res/src/main/res/values/strings.xml | 1 + .../com/tangem/domain/common/TwinsHelper.kt | 45 ++++++++++++++++--- 6 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt diff --git a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt index 628c041112..d91c57c086 100644 --- a/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/twins/CreateSecondTwinWalletTask.kt @@ -34,6 +34,11 @@ class CreateSecondTwinWalletTask( return } + if (!TwinsHelper.isTwinsCompatible(firstCardId, card.cardId)) { + callback(CompletionResult.Failure(IncompatibleTwinCard)) + return + } + session.setMessage(preparingMessage) PurgeWalletCommand(publicKey).run(session) { response -> when (response) { diff --git a/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt b/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt new file mode 100644 index 0000000000..10afdb020a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/domain/twins/IncompatibleTwinCard.kt @@ -0,0 +1,12 @@ +package com.tangem.tap.domain.twins + +import com.tangem.common.core.TangemError +import com.tangem.tap.tangemSdkManager +import com.tangem.wallet.R + +object IncompatibleTwinCard : TangemError(code = 50005) { + override var customMessage: String = tangemSdkManager.getString( + R.string.twin_error_wrong_twin, + ) + override val messageResId: Int? = null +} \ No newline at end of file diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index 6966257f9a..ed6016a6d2 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -428,6 +428,7 @@ на: %s В процессе… Вы отсканировали ту же карту. Для создания twin-кошелька вам необходимо отсканировать карту с номером %d + Вы отсканировали не ту twin-карту. Пожалуйста, попробуйте отсканировать другую Это карта, которую вы держите в руках. У парной карты номер %s.\n\nОбе карты можно использовать для вывода средств из этого кошелька. Один кошелек. Две карты. Сканировать карту #%s diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 87318128aa..711e9ca9c5 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -374,6 +374,7 @@ 無法加載交易 進行中… 您掃描了同一張卡片。要創建雙錢包,您需要掃描編號為 %d 的卡 + 你掃描錯了胞胎卡。請嘗試另一個 這一個是你手裡拿著的,另一個是編號為 %s 的,這兩張卡都可以用來從這個錢包中提取資金 一個錢包,兩張卡片 掃描卡片 #%s diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index b958ba0f9f..6b09dce789 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -420,6 +420,7 @@ to: %s In progress… You\'ve scanned the same card. To create a twin wallet you need to scan the card with number %d + You\'ve scanned wrong twin card. Please try another one This one that you are holding in your hands and the other one with number %s.\n\nBoth cards can be used to extract funds from this wallet. One wallet. Two cards. Scan the card #%s diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TwinsHelper.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TwinsHelper.kt index f6aa02a2c5..73c31f5a28 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TwinsHelper.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TwinsHelper.kt @@ -4,8 +4,17 @@ import com.tangem.crypto.CryptoUtils import com.tangem.domain.models.scan.CardDTO object TwinsHelper { - private val firstCardSeries = listOf("CB61", "CB64") - private val secondCardSeries = listOf("CB62", "CB65") + + /** + * Card compatibility + * cb61 <-> cb62 + * cb64 <-> cb65 + * + */ + private const val FIRST_CARD_FIRST_SERIES = "CB61" + private const val SECOND_CARD_FIRST_SERIES = "CB62" + private const val FIRST_CARD_SECOND_SERIES = "CB64" + private const val SECOND_CARD_SECOND_SERIES = "CB65" @Suppress("MagicNumber") fun verifyTwinPublicKey(issuerData: ByteArray, cardWalletPublicKey: ByteArray?): Boolean { @@ -16,10 +25,15 @@ object TwinsHelper { return CryptoUtils.verify(cardWalletPublicKey, publicKey, signedKey) } - fun getTwinCardNumber(cardId: String): TwinCardNumber? = when { - firstCardSeries.any(cardId::startsWith) -> TwinCardNumber.First - secondCardSeries.any(cardId::startsWith) -> TwinCardNumber.Second - else -> null + fun getTwinCardNumber(cardId: String): TwinCardNumber? { + val isFirstCard = cardId.startsWith(FIRST_CARD_FIRST_SERIES) || + cardId.startsWith(FIRST_CARD_SECOND_SERIES) + if (isFirstCard) return TwinCardNumber.First + + val isSecondCard = cardId.startsWith(SECOND_CARD_FIRST_SERIES) || + cardId.startsWith(SECOND_CARD_SECOND_SERIES) + if (isSecondCard) return TwinCardNumber.Second + return null } @Suppress("MagicNumber") @@ -30,6 +44,25 @@ object TwinsHelper { val twinCardNumber = getTwinCardNumber(cardId)?.number ?: 1 return "$twinCardId #$twinCardNumber" } + + /** + * Twins compatibility + * cb61 <-> cb62 + * cb64 <-> cb65 + */ + fun isTwinsCompatible(firstCardId: String, secondCardId: String): Boolean { + if (firstCardId.startsWith(FIRST_CARD_FIRST_SERIES) && + secondCardId.startsWith(SECOND_CARD_FIRST_SERIES) + ) { + return true + } + if (firstCardId.startsWith(FIRST_CARD_SECOND_SERIES) && + secondCardId.startsWith(SECOND_CARD_SECOND_SERIES) + ) { + return true + } + return false + } } enum class TwinCardNumber(val number: Int) { From 36b4671a05d085a923be91b4a2a4a828437d68d5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 28 Jul 2023 10:50:24 +0300 Subject: [PATCH 12/52] Updated on 2026-08-14 --- .../presentation/common/WalletPreviewData.kt | 10 +- .../organizetokens/OrganizeTokensIntents.kt | 24 ++ .../organizetokens/OrganizeTokensScreen.kt | 20 +- .../organizetokens/OrganizeTokensState.kt | 155 +++++++++++ .../OrganizeTokensStateHolder.kt | 253 +++++++----------- .../organizetokens/OrganizeTokensViewModel.kt | 18 +- .../utils/common/DraggableItemOperations.kt | 34 +++ .../{ => common}/DraggableItemsOperations.kt | 48 ++-- .../utils/common/IdsOperations.kt | 8 + .../OrganiseTokensListStateOperations.kt | 19 ++ .../utils/common/TokenListOperations.kt | 14 + .../converter/InProgressStateConverter.kt | 23 ++ .../converter/TokenListToStateConverter.kt | 31 +++ .../error/TokenListErrorConverter.kt | 18 ++ .../error/TokenListSortingErrorConverter.kt | 18 ++ .../CryptoCurrencyToDraggableItemConverter.kt | 56 ++++ .../NetworkGroupToDraggableItemsConverter.kt | 41 +++ .../items/TokenListToListStateConverter.kt | 42 +++ 18 files changed, 629 insertions(+), 203 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/{ => common}/DraggableItemsOperations.kt (78%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 56a3cec48c..bb2f454948 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -11,7 +11,7 @@ import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState import com.tangem.feature.wallet.presentation.wallet.state.* import com.tangem.feature.wallet.presentation.wallet.state.components.* import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState @@ -205,21 +205,21 @@ internal object WalletPreviewData { } val groupedOrganizeTokensState by lazy { - OrganizeTokensStateHolder( + OrganizeTokensState( itemsState = OrganizeTokensListState.GroupedByNetwork( items = draggableItems, ), - header = OrganizeTokensStateHolder.HeaderConfig( + header = OrganizeTokensState.HeaderConfig( onSortByBalanceClick = {}, onGroupByNetworkClick = {}, ), - dragConfig = OrganizeTokensStateHolder.DragConfig( + dndConfig = OrganizeTokensState.DragAndDropConfig( onItemDragged = { _, _ -> }, onDragStart = {}, canDragItemOver = { _, _ -> false }, onItemDragEnd = {}, ), - actions = OrganizeTokensStateHolder.ActionsConfig( + actions = OrganizeTokensState.ActionsConfig( onApplyClick = {}, onCancelClick = {}, ), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt new file mode 100644 index 0000000000..14891fef93 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt @@ -0,0 +1,24 @@ +package com.tangem.feature.wallet.presentation.organizetokens + +import org.burnoutcrew.reorderable.ItemPosition + +internal interface OrganizeTokensIntents { + + fun onBackClick() + + fun onSortClick() + + fun onGroupClick() + + fun onApplyClick() + + fun onCancelClick() + + fun onItemDragged(from: ItemPosition, to: ItemPosition) + + fun canDragItemOver(dragOver: ItemPosition, dragging: ItemPosition): Boolean + + fun onItemDraggingStart(item: DraggableItem) + + fun onItemDraggingEnd() +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index 87f1b076b2..381920e1ae 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -34,7 +34,7 @@ import com.tangem.feature.wallet.presentation.common.component.DraggableTokenIte import org.burnoutcrew.reorderable.* @Composable -internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Modifier = Modifier) { +internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier = Modifier) { val tokensListState = rememberLazyListState() Scaffold( @@ -47,7 +47,7 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Mo modifier = Modifier.padding(paddingValues), listState = tokensListState, state = state.itemsState, - dragConfig = state.dragConfig, + dragConfig = state.dndConfig, ) }, floatingActionButtonPosition = FabPosition.Center, @@ -62,7 +62,7 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensStateHolder, modifier: Mo private fun TokenList( listState: LazyListState, state: OrganizeTokensListState, - dragConfig: OrganizeTokensStateHolder.DragConfig, + dragConfig: OrganizeTokensState.DragAndDropConfig, modifier: Modifier = Modifier, ) { Box(modifier = modifier) { @@ -166,7 +166,7 @@ private fun BottomGradient(modifier: Modifier = Modifier) { @Composable private fun TopBar( - config: OrganizeTokensStateHolder.HeaderConfig, + config: OrganizeTokensState.HeaderConfig, tokensListState: LazyListState, modifier: Modifier = Modifier, ) { @@ -211,7 +211,7 @@ private fun TopBar( config = ActionButtonConfig( text = TextReference.Res(id = R.string.organize_tokens_sort_by_balance), iconResId = R.drawable.ic_sort_24, - onClick = config.onSortByBalanceClick, + onClick = config.onSortClick, ), modifier = Modifier.weight(1f), color = TangemTheme.colors.background.primary, @@ -220,7 +220,7 @@ private fun TopBar( config = ActionButtonConfig( text = TextReference.Res(id = R.string.organize_tokens_group), iconResId = R.drawable.ic_group_24, - onClick = config.onGroupByNetworkClick, + onClick = config.onGroupClick, ), modifier = Modifier.weight(1f), color = TangemTheme.colors.background.primary, @@ -230,7 +230,7 @@ private fun TopBar( } @Composable -private fun Actions(config: OrganizeTokensStateHolder.ActionsConfig, modifier: Modifier = Modifier) { +private fun Actions(config: OrganizeTokensState.ActionsConfig, modifier: Modifier = Modifier) { Row( modifier = modifier .padding(horizontal = TangemTheme.dimens.spacing16) @@ -308,7 +308,7 @@ private fun Modifier.applyShapeAndShadow(roundingMode: DraggableItem.RoundingMod @Preview(showBackground = true, widthDp = 360) @Composable private fun OrganizeTokensScreenPreview_Light( - @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensStateHolder, + @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensState, ) { TangemTheme { OrganizeTokensScreen(state) @@ -318,14 +318,14 @@ private fun OrganizeTokensScreenPreview_Light( @Preview(showBackground = true, widthDp = 360) @Composable private fun OrganizeTokensScreenPreview_Dark( - @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensStateHolder, + @PreviewParameter(OrganizeTokensStateProvider::class) state: OrganizeTokensState, ) { TangemTheme(isDark = true) { OrganizeTokensScreen(state) } } -private class OrganizeTokensStateProvider : CollectionPreviewParameterProvider( +private class OrganizeTokensStateProvider : CollectionPreviewParameterProvider( collection = listOf( WalletPreviewData.organizeTokensState, WalletPreviewData.groupedOrganizeTokensState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt new file mode 100644 index 0000000000..872529e9b9 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt @@ -0,0 +1,155 @@ +package com.tangem.feature.wallet.presentation.organizetokens + +import androidx.compose.runtime.Immutable +import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem.RoundingMode +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf +import org.burnoutcrew.reorderable.ItemPosition + +@Immutable +internal data class OrganizeTokensState( + val onBackClick: () -> Unit, + val itemsState: OrganizeTokensListState, + val header: HeaderConfig, + val actions: ActionsConfig, + val dndConfig: DragAndDropConfig, +) { + + data class HeaderConfig( + val isEnabled: Boolean = false, + val isSortedByBalance: Boolean = false, + val isGrouped: Boolean = false, + val onSortClick: () -> Unit, + val onGroupClick: () -> Unit, + ) + + data class ActionsConfig( + val canApply: Boolean = false, + val showApplyProgress: Boolean = false, + val onApplyClick: () -> Unit, + val onCancelClick: () -> Unit, + ) + + data class DragAndDropConfig( + val onItemDragged: (ItemPosition, ItemPosition) -> Unit, + val canDragItemOver: (ItemPosition, ItemPosition) -> Boolean, + val onItemDragEnd: () -> Unit, + val onDragStart: (DraggableItem) -> Unit, + ) +} + +@Immutable +internal sealed class OrganizeTokensListState { + abstract val items: PersistentList + + data class GroupedByNetwork( + override val items: PersistentList, + ) : OrganizeTokensListState() + + data class Ungrouped( + override val items: PersistentList, + ) : OrganizeTokensListState() + + object Empty : OrganizeTokensListState() { + override val items: PersistentList = persistentListOf() + } +} + +/** + * Helper class for the DND list items + * + * @property id ID of the item + * @property roundingMode item [RoundingMode] + * @property showShadow if true then item should be elevated + * */ +@Immutable +internal sealed class DraggableItem { + abstract val id: String + abstract val roundingMode: RoundingMode + abstract val showShadow: Boolean + + /** + * Item for network group header. + * + * @property id ID of the network group + * @property networkName network group name + * @property roundingMode item [RoundingMode] + * @property showShadow if true then item should be elevated + * */ + data class GroupHeader( + override val id: String, + val networkName: String, + override val roundingMode: RoundingMode = RoundingMode.None, + override val showShadow: Boolean = false, + ) : DraggableItem() + + /** + * Item for token. + * + * @property tokenItemState state of the token item + * @property groupId ID of the network group which contains this token + * @property id ID of the token + * @property roundingMode item [RoundingMode] + * @property showShadow if true then item should be elevated + * */ + data class Token( + val tokenItemState: TokenItemState.Draggable, + val groupId: String, + override val showShadow: Boolean = false, + override val roundingMode: RoundingMode = RoundingMode.None, + ) : DraggableItem() { + override val id: String = tokenItemState.id + } + + /** + * Helper item used to detect possible positions where a network group can be placed. + * Used only on [OrganizeTokensListState.GroupedByNetwork] and placed between network groups. + * + * @property id ID of the placeholder + * */ + data class GroupPlaceholder( + override val id: String, + ) : DraggableItem() { + override val showShadow: Boolean = false + override val roundingMode: RoundingMode = RoundingMode.None + } + + /** + * Rounding mode of the [DraggableItem] + * + * @property showGap if true then item should have padding on rounded side + * */ + @Immutable + sealed class RoundingMode { + abstract val showGap: Boolean + + /** + * In this mode, item is not rounded + * */ + object None : RoundingMode() { + override val showGap: Boolean = false + } + + /** + * In this mode, item should have a rounded top side + * + * @property showGap if true then item should have top padding + * */ + data class Top(override val showGap: Boolean = false) : RoundingMode() + + /** + * In this mode, item should have a rounded bottom side + * + * @property showGap if true then item should have bottom padding + * */ + data class Bottom(override val showGap: Boolean = false) : RoundingMode() + + /** + * In this mode, item should have a rounded all sides + * + * @property showGap if true then item should have top and bottom padding + * */ + data class All(override val showGap: Boolean = false) : RoundingMode() + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt index 4da6dcb29c..6a997d5eab 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt @@ -1,179 +1,120 @@ package com.tangem.feature.wallet.presentation.organizetokens -import androidx.compose.runtime.Immutable -import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.toPersistentList -import org.burnoutcrew.reorderable.ItemPosition +import com.tangem.common.Provider +import com.tangem.domain.tokens.error.TokenListError +import com.tangem.domain.tokens.error.TokenListSortingError +import com.tangem.domain.tokens.model.TokenList +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.updateSorting +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.TokenListToStateConverter +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error.TokenListErrorConverter +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error.TokenListSortingErrorConverter +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.CryptoCurrencyToDraggableItemConverter +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.NetworkGroupToDraggableItemsConverter +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.TokenListToListStateConverter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.* -internal data class OrganizeTokensStateHolder( - val header: HeaderConfig, - val itemsState: OrganizeTokensListState, - val dragConfig: DragConfig, - val actions: ActionsConfig, +@Suppress("unused", "MemberVisibilityCanBePrivate") // TODO: Will be used in next MR +internal class OrganizeTokensStateHolder( + private val intents: OrganizeTokensIntents, + private val fiatCurrencyCode: String, + private val fiatCurrencySymbol: String, + private val onSubscription: () -> Unit, + scope: CoroutineScope, ) { - data class HeaderConfig( - val onSortByBalanceClick: () -> Unit, - val onGroupByNetworkClick: () -> Unit, - ) + private val stateFlowInternal: MutableStateFlow = MutableStateFlow(getInitialState()) - data class ActionsConfig( - val onApplyClick: () -> Unit, - val onCancelClick: () -> Unit, - ) + private val tokenListConverter by lazy { + val tokensConverter = CryptoCurrencyToDraggableItemConverter( + fiatCurrencyCode = fiatCurrencyCode, + fiatCurrencySymbol = fiatCurrencySymbol, + ) + val itemsConverter = TokenListToListStateConverter( + tokensConverter = tokensConverter, + groupsConverter = NetworkGroupToDraggableItemsConverter(tokensConverter), + ) - data class DragConfig( - val onItemDragged: (from: ItemPosition, to: ItemPosition) -> Unit, - val canDragItemOver: (dragOver: ItemPosition, dragging: ItemPosition) -> Boolean, - val onItemDragEnd: () -> Unit, - val onDragStart: (item: DraggableItem) -> Unit, - ) -} + TokenListToStateConverter(Provider(stateFlowInternal::value), itemsConverter) + } -@Immutable -internal sealed interface OrganizeTokensListState { - val items: PersistentList + private val inProgressStateConverter by lazy { + InProgressStateConverter() + } - data class GroupedByNetwork( - override val items: PersistentList, - ) : OrganizeTokensListState + private val tokenListErrorConverter by lazy { + TokenListErrorConverter(Provider(stateFlowInternal::value), inProgressStateConverter) + } - data class Ungrouped( - override val items: PersistentList, - ) : OrganizeTokensListState + private val tokenListSortingErrorConverter by lazy { + TokenListSortingErrorConverter(Provider(stateFlowInternal::value), inProgressStateConverter) + } - @Suppress("UNCHECKED_CAST") - fun updateItems(update: (PersistentList) -> List): OrganizeTokensListState { - val updatedItems = update(this.items).toPersistentList() + val stateFlow: StateFlow = stateFlowInternal + .onSubscription { onSubscription() } + .stateIn( + scope = scope, + started = SharingStarted.WhileSubscribed(), + initialValue = getInitialState(), + ) - return when (this) { - is GroupedByNetwork -> this.copy(items = updatedItems) - is Ungrouped -> this.copy(items = updatedItems as PersistentList) + var tokenList: TokenList? = null + private set + + fun updateStateWithTokenList(tokenList: TokenList) { + updateState { tokenListConverter.convert(tokenList) } + this.tokenList = tokenList + } + + fun updateStateToDisplayProgress() { + updateState { inProgressStateConverter.convert(value = this) } + } + + fun updateStateToHideProgress() { + updateState { inProgressStateConverter.convertBack(value = this) } + } + + fun updateStateWithManualSorting(itemsState: OrganizeTokensListState) { + updateState { + copy( + header = header.copy(isSortedByBalance = false), + itemsState = itemsState, + ) } - } -} - -/** - * Helper class for the DND list items - * - * @property id ID of the item - * @property roundingMode item [RoundingMode] - * @property showShadow if true then item should be elevated - * */ -@Immutable -internal sealed interface DraggableItem { - val id: String - val roundingMode: RoundingMode - val showShadow: Boolean - - /** - * Item for network group header. - * - * @property id ID of the network group - * @property networkName network group name - * @property roundingMode item [RoundingMode] - * @property showShadow if true then item should be elevated - * */ - data class GroupHeader( - override val id: String, - val networkName: String, - override val roundingMode: RoundingMode = RoundingMode.None, - override val showShadow: Boolean = false, - ) : DraggableItem - - /** - * Item for token. - * - * @property tokenItemState state of the token item - * @property groupId ID of the network group which contains this token - * @property id ID of the token - * @property roundingMode item [RoundingMode] - * @property showShadow if true then item should be elevated - * */ - data class Token( - val tokenItemState: TokenItemState.Draggable, - val groupId: String, - override val showShadow: Boolean = false, - override val roundingMode: RoundingMode = RoundingMode.None, - ) : DraggableItem { - override val id: String = tokenItemState.id + tokenList = tokenList?.updateSorting(isSortedByBalance = false) } - /** - * Helper item used to detect possible positions where a network group can be placed. - * Used only on [OrganizeTokensListState.GroupedByNetwork] and placed between network groups. - * - * @property id ID of the placeholder - * */ - data class GroupPlaceholder( - override val id: String, - ) : DraggableItem { - override val showShadow: Boolean = false - override val roundingMode: RoundingMode = RoundingMode.None + fun updateStateWithError(error: TokenListError) { + updateState { tokenListErrorConverter.convert(error) } } - /** - * Update item [RoundingMode] - * - * @param mode new [RoundingMode] - * - * @return updated [DraggableItem] - * */ - fun roundingMode(mode: RoundingMode): DraggableItem = when (this) { - is GroupPlaceholder -> this - is GroupHeader -> this.copy(roundingMode = mode) - is Token -> this.copy(roundingMode = mode) + fun updateStateWithError(error: TokenListSortingError) { + updateState { tokenListSortingErrorConverter.convert(error) } } - /** - * Update item shadow visibility - * - * @param show if true then item should be elevated - * - * @return updated [DraggableItem] - * */ - fun showShadow(show: Boolean): DraggableItem = when (this) { - is GroupPlaceholder -> this - is GroupHeader -> this.copy(showShadow = show) - is Token -> this.copy(showShadow = show) + private fun getInitialState(): OrganizeTokensState { + return OrganizeTokensState( + onBackClick = intents::onBackClick, + itemsState = OrganizeTokensListState.Empty, + header = OrganizeTokensState.HeaderConfig( + onSortClick = intents::onSortClick, + onGroupClick = intents::onGroupClick, + ), + actions = OrganizeTokensState.ActionsConfig( + onApplyClick = intents::onApplyClick, + onCancelClick = intents::onCancelClick, + ), + dndConfig = OrganizeTokensState.DragAndDropConfig( + onItemDragged = intents::onItemDragged, + onDragStart = intents::onItemDraggingStart, + onItemDragEnd = intents::onItemDraggingEnd, + canDragItemOver = intents::canDragItemOver, + ), + ) } - /** - * Rounding mode of the [DraggableItem] - * - * @property showGap if true then item should have padding on rounded side - * */ - @Immutable - sealed interface RoundingMode { - val showGap: Boolean - - /** - * In this mode, item is not rounded - * */ - object None : RoundingMode { - override val showGap: Boolean = false - } - - /** - * In this mode, item should have a rounded top side - * - * @property showGap if true then item should have top padding - * */ - data class Top(override val showGap: Boolean = false) : RoundingMode - - /** - * In this mode, item should have a rounded bottom side - * - * @property showGap if true then item should have bottom padding - * */ - data class Bottom(override val showGap: Boolean = false) : RoundingMode - - /** - * In this mode, item should have a rounded all sides - * - * @property showGap if true then item should have top and bottom padding - * */ - data class All(override val showGap: Boolean = false) : RoundingMode + private fun updateState(block: OrganizeTokensState.() -> OrganizeTokensState) { + stateFlowInternal.update(block) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt index 52f391935b..f3fce2c7ea 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt @@ -8,9 +8,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder.DragConfig -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensStateHolder.HeaderConfig -import com.tangem.feature.wallet.presentation.organizetokens.utils.* +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.* import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.router.WalletRoute import dagger.hilt.android.lifecycle.HiltViewModel @@ -35,22 +33,22 @@ internal class OrganizeTokensViewModel @Inject constructor(savedStateHandle: Sav UserWalletId(userWalletIdValue) } - var uiState: OrganizeTokensStateHolder by mutableStateOf(getInitialState()) + var uiState: OrganizeTokensState by mutableStateOf(getInitialState()) private set - private fun getInitialState(): OrganizeTokensStateHolder = WalletPreviewData.organizeTokensState.copy( + private fun getInitialState(): OrganizeTokensState = WalletPreviewData.organizeTokensState.copy( itemsState = OrganizeTokensListState.Ungrouped( items = WalletPreviewData.draggableTokens, ), - dragConfig = DragConfig( + dndConfig = OrganizeTokensState.DragAndDropConfig( onItemDragged = this::moveItem, canDragItemOver = this::checkCanMoveItemOver, onItemDragEnd = this::endMoving, onDragStart = this::startMoving, ), - header = HeaderConfig( - onSortByBalanceClick = { /* no-op */ }, - onGroupByNetworkClick = this::toggleTokensByNetworkGrouping, + header = OrganizeTokensState.HeaderConfig( + onSortClick = { /* no-op */ }, + onGroupClick = this::toggleTokensByNetworkGrouping, ), ) @@ -62,6 +60,7 @@ internal class OrganizeTokensViewModel @Inject constructor(savedStateHandle: Sav is OrganizeTokensListState.Ungrouped -> OrganizeTokensListState.GroupedByNetwork( items = WalletPreviewData.draggableItems, ) + is OrganizeTokensListState.Empty -> itemsState } uiState = uiState.copy(itemsState = newListState) @@ -95,6 +94,7 @@ internal class OrganizeTokensViewModel @Inject constructor(savedStateHandle: Sav is DraggableItem.Token -> when (uiState.itemsState) { is OrganizeTokensListState.GroupedByNetwork -> items.divideGroups(movingItem) is OrganizeTokensListState.Ungrouped -> items.divideItems(movingItem) + is OrganizeTokensListState.Empty -> uiState.itemsState.items } is DraggableItem.GroupPlaceholder -> items } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt new file mode 100644 index 0000000000..3fb4746efb --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt @@ -0,0 +1,34 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.common + +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem.RoundingMode + +/** + * Update item [RoundingMode] + * + * @param mode new [RoundingMode] + * + * @return updated [DraggableItem] + * */ +internal fun DraggableItem.updateRoundingMode(mode: RoundingMode): DraggableItem = when (this) { + is DraggableItem.GroupPlaceholder -> this + is DraggableItem.GroupHeader -> this.copy(roundingMode = mode) + is DraggableItem.Token -> this.copy(roundingMode = mode) +} + +/** + * Update item shadow visibility + * + * @param show if true then item should be elevated + * + * @return updated [DraggableItem] + * */ +internal fun DraggableItem.updateShadowVisibility(show: Boolean): DraggableItem = when (this) { + is DraggableItem.GroupPlaceholder -> this + is DraggableItem.GroupHeader -> this.copy(showShadow = show) + is DraggableItem.Token -> this.copy(showShadow = show) +} + +internal fun getGroupPlaceholder(index: Int): DraggableItem.GroupPlaceholder { + return DraggableItem.GroupPlaceholder(id = "placeholder_${index.inc()}") +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/DraggableItemsOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt similarity index 78% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/DraggableItemsOperations.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt index 9bad24193c..58ae4fcefd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/DraggableItemsOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.organizetokens.utils +package com.tangem.feature.wallet.presentation.organizetokens.utils.common import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem import kotlinx.collections.immutable.PersistentList @@ -59,13 +59,15 @@ internal fun PersistentList.moveItem(fromIndex: Int, toIndex: Int internal fun List.divideItems(movingItem: DraggableItem): List { return this.map { it - .roundingMode(DraggableItem.RoundingMode.All(showGap = true)) - .showShadow(show = it.id == movingItem.id) + .updateRoundingMode(DraggableItem.RoundingMode.All(showGap = true)) + .updateShadowVisibility(show = it.id == movingItem.id) } } -internal fun List.uniteItems(): List { +@Suppress("UNCHECKED_CAST") // Erased type +internal fun List.uniteItems(): List { val lastItemIndex = this.lastIndex + return this.mapIndexed { index, item -> val mode = when (index) { 0 -> DraggableItem.RoundingMode.Top() @@ -74,9 +76,9 @@ internal fun List.uniteItems(): List { } item - .roundingMode(mode) - .showShadow(show = false) - } + .updateRoundingMode(mode) + .updateShadowVisibility(show = false) + } as List } // TODO: Move to domain @@ -131,51 +133,51 @@ internal fun List.divideGroups(movingItem: DraggableItem): List { item - .roundingMode(DraggableItem.RoundingMode.All(showGap = true)) - .showShadow(show = true) + .updateRoundingMode(DraggableItem.RoundingMode.All(showGap = true)) + .updateShadowVisibility(show = true) } // Case when moving item is a token and current item is the group of the moving token movingItem is DraggableItem.Token && item.id == movingItem.groupId -> { item - .roundingMode(DraggableItem.RoundingMode.All(showGap = true)) - .showShadow(show = true) + .updateRoundingMode(DraggableItem.RoundingMode.All(showGap = true)) + .updateShadowVisibility(show = true) } // Case when both moving item and current item are tokens and belong to the same group movingItem is DraggableItem.Token && item is DraggableItem.Token && item.groupId == movingItem.groupId -> { item - .roundingMode(DraggableItem.RoundingMode.All(showGap = true)) - .showShadow(show = false) + .updateRoundingMode(DraggableItem.RoundingMode.All(showGap = true)) + .updateShadowVisibility(show = false) } // Case when current item is the first item in the list index == 0 -> { item - .roundingMode(DraggableItem.RoundingMode.Top()) - .showShadow(show = false) + .updateRoundingMode(DraggableItem.RoundingMode.Top()) + .updateShadowVisibility(show = false) } // Case when current item is the last item in the list index == lastItemIndex -> { item - .roundingMode(DraggableItem.RoundingMode.Bottom()) - .showShadow(show = false) + .updateRoundingMode(DraggableItem.RoundingMode.Bottom()) + .updateShadowVisibility(show = false) } // Case when previous item is a GroupPlaceholder this[index - 1] is DraggableItem.GroupPlaceholder -> { item - .roundingMode(DraggableItem.RoundingMode.Top(showGap = true)) - .showShadow(show = false) + .updateRoundingMode(DraggableItem.RoundingMode.Top(showGap = true)) + .updateShadowVisibility(show = false) } // Case when next item is a GroupPlaceholder this[index + 1] is DraggableItem.GroupPlaceholder -> { item - .roundingMode(DraggableItem.RoundingMode.Bottom(showGap = true)) - .showShadow(show = false) + .updateRoundingMode(DraggableItem.RoundingMode.Bottom(showGap = true)) + .updateShadowVisibility(show = false) } // Default case when none of the above conditions are met else -> { item - .roundingMode(DraggableItem.RoundingMode.None) - .showShadow(show = false) + .updateRoundingMode(DraggableItem.RoundingMode.None) + .updateShadowVisibility(show = false) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt new file mode 100644 index 0000000000..d7e86bcff6 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt @@ -0,0 +1,8 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.common + +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.Network + +internal fun getTokenItemId(currencyId: CryptoCurrency.ID): String = currencyId.value + +internal fun getGroupHeaderId(networkId: Network.ID): String = networkId.value \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt new file mode 100644 index 0000000000..bf5c601b7e --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt @@ -0,0 +1,19 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.common + +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.toPersistentList + +@Suppress("UNCHECKED_CAST") +internal inline fun OrganizeTokensListState.updateItems( + update: (PersistentList) -> List, +): OrganizeTokensListState { + val updatedItems = update(items).toPersistentList() + + return when (this) { + is OrganizeTokensListState.GroupedByNetwork -> copy(items = updatedItems) + is OrganizeTokensListState.Ungrouped -> copy(items = updatedItems as PersistentList) + is OrganizeTokensListState.Empty -> this + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt new file mode 100644 index 0000000000..4f1478d2bf --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/TokenListOperations.kt @@ -0,0 +1,14 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.common + +import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.tokens.model.TokenList.SortType + +internal fun TokenList.updateSorting(isSortedByBalance: Boolean): TokenList { + val sortType = if (isSortedByBalance) SortType.BALANCE else SortType.NONE + + return when (this) { + is TokenList.GroupedByNetwork -> this.copy(sortedBy = sortType) + is TokenList.Ungrouped -> this.copy(sortedBy = sortType) + is TokenList.NotInitialized -> this + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt new file mode 100644 index 0000000000..fc5c571ad9 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt @@ -0,0 +1,23 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter + +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.utils.converter.TwoWayConverter + +internal class InProgressStateConverter : TwoWayConverter { + + override fun convert(value: OrganizeTokensState): OrganizeTokensState { + return value.copy( + actions = value.actions.copy( + showApplyProgress = true, + ), + ) + } + + override fun convertBack(value: OrganizeTokensState): OrganizeTokensState { + return value.copy( + actions = value.actions.copy( + showApplyProgress = false, + ), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt new file mode 100644 index 0000000000..be5a3a12dc --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt @@ -0,0 +1,31 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter + +import com.tangem.common.Provider +import com.tangem.domain.tokens.model.TokenList +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.TokenListToListStateConverter +import com.tangem.utils.converter.Converter + +internal class TokenListToStateConverter( + private val currentState: Provider, + private val itemsConverter: TokenListToListStateConverter, +) : Converter { + + override fun convert(value: TokenList): OrganizeTokensState { + val state = currentState() + val itemsState = itemsConverter.convert(value) + + return state.copy( + itemsState = itemsState, + header = state.header.copy( + isEnabled = itemsState !is OrganizeTokensListState.Empty, + isSortedByBalance = value.sortedBy == TokenList.SortType.BALANCE, + isGrouped = value is TokenList.GroupedByNetwork, + ), + actions = state.actions.copy( + canApply = itemsState !is OrganizeTokensListState.Empty, + ), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt new file mode 100644 index 0000000000..f32dfbb528 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt @@ -0,0 +1,18 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error + +import com.tangem.common.Provider +import com.tangem.domain.tokens.error.TokenListError +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter +import com.tangem.utils.converter.Converter + +internal class TokenListErrorConverter( + private val currentState: Provider, + private val inProgressStateConverter: InProgressStateConverter, +) : Converter { + + // TODO: [REDACTED_JIRA] + override fun convert(value: TokenListError): OrganizeTokensState { + return inProgressStateConverter.convertBack(currentState()) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt new file mode 100644 index 0000000000..3072cc33c4 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt @@ -0,0 +1,18 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error + +import com.tangem.common.Provider +import com.tangem.domain.tokens.error.TokenListSortingError +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter +import com.tangem.utils.converter.Converter + +internal class TokenListSortingErrorConverter( + private val currentState: Provider, + private val inProgressStateConverter: InProgressStateConverter, +) : Converter { + + // TODO: [REDACTED_JIRA] + override fun convert(value: TokenListSortingError): OrganizeTokensState { + return inProgressStateConverter.convertBack(currentState()) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt new file mode 100644 index 0000000000..f0e892fc45 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -0,0 +1,56 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items + +import androidx.annotation.DrawableRes +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.wallet.impl.R +import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId +import com.tangem.utils.converter.Converter + +internal class CryptoCurrencyToDraggableItemConverter( + private val fiatCurrencyCode: String, + private val fiatCurrencySymbol: String, +) : Converter { + + private val CryptoCurrency.networkIconResId: Int? + @DrawableRes get() { + // TODO: [REDACTED_JIRA] + return if (this is CryptoCurrency.Coin) null else R.drawable.img_eth_22 + } + + private val CryptoCurrency.tokenIconResId: Int + @DrawableRes get() { + // TODO: [REDACTED_JIRA] + return R.drawable.img_eth_22 + } + + override fun convert(value: CryptoCurrencyStatus): DraggableItem.Token { + return DraggableItem.Token( + tokenItemState = createToTokenItemState(value), + groupId = getGroupHeaderId(value.currency.networkId), + ) + } + + private fun createToTokenItemState(currencyStatus: CryptoCurrencyStatus): TokenItemState.Draggable { + val currency = currencyStatus.currency + + return TokenItemState.Draggable( + id = getTokenItemId(currency.id), + tokenIconUrl = currency.iconUrl, + tokenIconResId = currency.tokenIconResId, + networkIconResId = currency.networkIconResId, + name = currency.name, + fiatAmount = getFormattedFiatAmount(currencyStatus), + ) + } + + private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus): String { + val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + + return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt new file mode 100644 index 0000000000..47e698cf55 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items + +import com.tangem.domain.tokens.model.NetworkGroup +import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupPlaceholder +import com.tangem.utils.converter.Converter + +internal class NetworkGroupToDraggableItemsConverter( + private val itemConverter: CryptoCurrencyToDraggableItemConverter, +) : Converter> { + + override fun convert(value: NetworkGroup): List { + return buildList { + add(createGroupHeader(value)) + addAll(createTokens(value)) + } + } + + override fun convertList(input: Collection): List> { + val lastItemIndex = input.size - 1 + + return input.mapIndexed { index, networkGroup -> + convert(networkGroup).toMutableList() + .also { mutableGroup -> + if (index != lastItemIndex) { + mutableGroup.add(getGroupPlaceholder(index)) + } + } + } + } + + private fun createGroupHeader(group: NetworkGroup) = DraggableItem.GroupHeader( + id = getGroupHeaderId(group.network.id), + networkName = group.network.name, + ) + + private fun createTokens(group: NetworkGroup): List { + return itemConverter.convertList(group.currencies.toList()) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt new file mode 100644 index 0000000000..f62bd3abb9 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt @@ -0,0 +1,42 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items + +import com.tangem.domain.tokens.model.TokenList +import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteItems +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toPersistentList + +internal class TokenListToListStateConverter( + private val groupsConverter: NetworkGroupToDraggableItemsConverter, + private val tokensConverter: CryptoCurrencyToDraggableItemConverter, +) : Converter { + + override fun convert(value: TokenList): OrganizeTokensListState { + return when (value) { + is TokenList.GroupedByNetwork -> createListState(value) + is TokenList.Ungrouped -> createListState(value) + is TokenList.NotInitialized -> createEmptyListState() + } + } + + private fun createListState(tokenList: TokenList.GroupedByNetwork): OrganizeTokensListState.GroupedByNetwork { + return OrganizeTokensListState.GroupedByNetwork( + items = groupsConverter.convertList(tokenList.groups) + .flatten() + .uniteItems() + .toPersistentList(), + ) + } + + private fun createListState(tokenList: TokenList.Ungrouped): OrganizeTokensListState.Ungrouped { + return OrganizeTokensListState.Ungrouped( + items = tokensConverter.convertList(tokenList.currencies) + .uniteItems() + .toPersistentList(), + ) + } + + private fun createEmptyListState(): OrganizeTokensListState.Empty { + return OrganizeTokensListState.Empty + } +} \ No newline at end of file From eab83c7675634a07e50220778249774afee19463 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 20:20:50 +0300 Subject: [PATCH 13/52] Updated on 2026-08-14 --- .../presentation/common/WalletPreviewData.kt | 11 +-- .../organizetokens/OrganizeTokensIntents.kt | 1 + .../organizetokens/OrganizeTokensScreen.kt | 3 + .../OrganizeTokensStateHolder.kt | 2 + .../organizetokens/OrganizeTokensViewModel.kt | 3 + .../DraggableItem.kt} | 82 +++++++------------ .../model/OrganizeTokensListState.kt | 22 +++++ .../model/OrganizeTokensState.kt | 36 ++++++++ .../utils/common/DraggableItemOperations.kt | 29 +------ .../utils/common/DraggableItemsOperations.kt | 2 +- .../OrganiseTokensListStateOperations.kt | 4 +- .../converter/InProgressStateConverter.kt | 2 +- .../converter/TokenListToStateConverter.kt | 4 +- .../error/TokenListErrorConverter.kt | 2 +- .../error/TokenListSortingErrorConverter.kt | 2 +- .../CryptoCurrencyToDraggableItemConverter.kt | 2 +- .../NetworkGroupToDraggableItemsConverter.kt | 2 +- .../items/TokenListToListStateConverter.kt | 2 +- 18 files changed, 113 insertions(+), 98 deletions(-) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/{OrganizeTokensState.kt => model/DraggableItem.kt} (65%) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensListState.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensState.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index bb2f454948..afe19472ec 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -9,9 +9,9 @@ import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.wallet.state.* import com.tangem.feature.wallet.presentation.wallet.state.components.* import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState.TokensListItemState @@ -206,12 +206,13 @@ internal object WalletPreviewData { val groupedOrganizeTokensState by lazy { OrganizeTokensState( + onBackClick = {}, itemsState = OrganizeTokensListState.GroupedByNetwork( items = draggableItems, ), header = OrganizeTokensState.HeaderConfig( - onSortByBalanceClick = {}, - onGroupByNetworkClick = {}, + onSortClick = {}, + onGroupClick = {}, ), dndConfig = OrganizeTokensState.DragAndDropConfig( onItemDragged = { _, _ -> }, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt index 14891fef93..250b29d900 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import org.burnoutcrew.reorderable.ItemPosition internal interface OrganizeTokensIntents { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index 381920e1ae..ce0aabb8b1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -31,6 +31,9 @@ import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.common.component.DraggableNetworkGroupItem import com.tangem.feature.wallet.presentation.common.component.DraggableTokenItem +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import org.burnoutcrew.reorderable.* @Composable diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt index 6a997d5eab..33161ae553 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt @@ -4,6 +4,8 @@ import com.tangem.common.Provider import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.model.TokenList +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.common.updateSorting import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.TokenListToStateConverter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt index f3fce2c7ea..b3fa65555b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt @@ -8,6 +8,9 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.WalletPreviewData +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.common.* import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.router.WalletRoute diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt similarity index 65% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt index 872529e9b9..5d367c0436 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/DraggableItem.kt @@ -1,60 +1,8 @@ -package com.tangem.feature.wallet.presentation.organizetokens +package com.tangem.feature.wallet.presentation.organizetokens.model import androidx.compose.runtime.Immutable import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem.RoundingMode -import kotlinx.collections.immutable.PersistentList -import kotlinx.collections.immutable.persistentListOf -import org.burnoutcrew.reorderable.ItemPosition - -@Immutable -internal data class OrganizeTokensState( - val onBackClick: () -> Unit, - val itemsState: OrganizeTokensListState, - val header: HeaderConfig, - val actions: ActionsConfig, - val dndConfig: DragAndDropConfig, -) { - - data class HeaderConfig( - val isEnabled: Boolean = false, - val isSortedByBalance: Boolean = false, - val isGrouped: Boolean = false, - val onSortClick: () -> Unit, - val onGroupClick: () -> Unit, - ) - - data class ActionsConfig( - val canApply: Boolean = false, - val showApplyProgress: Boolean = false, - val onApplyClick: () -> Unit, - val onCancelClick: () -> Unit, - ) - - data class DragAndDropConfig( - val onItemDragged: (ItemPosition, ItemPosition) -> Unit, - val canDragItemOver: (ItemPosition, ItemPosition) -> Boolean, - val onItemDragEnd: () -> Unit, - val onDragStart: (DraggableItem) -> Unit, - ) -} - -@Immutable -internal sealed class OrganizeTokensListState { - abstract val items: PersistentList - - data class GroupedByNetwork( - override val items: PersistentList, - ) : OrganizeTokensListState() - - data class Ungrouped( - override val items: PersistentList, - ) : OrganizeTokensListState() - - object Empty : OrganizeTokensListState() { - override val items: PersistentList = persistentListOf() - } -} +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem.RoundingMode /** * Helper class for the DND list items @@ -152,4 +100,30 @@ internal sealed class DraggableItem { * */ data class All(override val showGap: Boolean = false) : RoundingMode() } + + /** + * Update item [RoundingMode] + * + * @param mode new [RoundingMode] + * + * @return updated [DraggableItem] + * */ + fun updateRoundingMode(mode: RoundingMode): DraggableItem = when (this) { + is GroupPlaceholder -> this + is GroupHeader -> this.copy(roundingMode = mode) + is Token -> this.copy(roundingMode = mode) + } + + /** + * Update item shadow visibility + * + * @param show if true then item should be elevated + * + * @return updated [DraggableItem] + * */ + fun updateShadowVisibility(show: Boolean): DraggableItem = when (this) { + is GroupPlaceholder -> this + is GroupHeader -> this.copy(showShadow = show) + is Token -> this.copy(showShadow = show) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensListState.kt new file mode 100644 index 0000000000..3f87cab410 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensListState.kt @@ -0,0 +1,22 @@ +package com.tangem.feature.wallet.presentation.organizetokens.model + +import androidx.compose.runtime.Immutable +import kotlinx.collections.immutable.PersistentList +import kotlinx.collections.immutable.persistentListOf + +@Immutable +internal sealed class OrganizeTokensListState { + abstract val items: PersistentList + + data class GroupedByNetwork( + override val items: PersistentList, + ) : OrganizeTokensListState() + + data class Ungrouped( + override val items: PersistentList, + ) : OrganizeTokensListState() + + object Empty : OrganizeTokensListState() { + override val items: PersistentList = persistentListOf() + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensState.kt new file mode 100644 index 0000000000..30d8e846c1 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/model/OrganizeTokensState.kt @@ -0,0 +1,36 @@ +package com.tangem.feature.wallet.presentation.organizetokens.model + +import androidx.compose.runtime.Immutable +import org.burnoutcrew.reorderable.ItemPosition + +@Immutable +internal data class OrganizeTokensState( + val onBackClick: () -> Unit, + val itemsState: OrganizeTokensListState, + val header: HeaderConfig, + val actions: ActionsConfig, + val dndConfig: DragAndDropConfig, +) { + + data class HeaderConfig( + val isEnabled: Boolean = false, + val isSortedByBalance: Boolean = false, + val isGrouped: Boolean = false, + val onSortClick: () -> Unit, + val onGroupClick: () -> Unit, + ) + + data class ActionsConfig( + val canApply: Boolean = false, + val showApplyProgress: Boolean = false, + val onApplyClick: () -> Unit, + val onCancelClick: () -> Unit, + ) + + data class DragAndDropConfig( + val onItemDragged: (ItemPosition, ItemPosition) -> Unit, + val canDragItemOver: (ItemPosition, ItemPosition) -> Boolean, + val onItemDragEnd: () -> Unit, + val onDragStart: (DraggableItem) -> Unit, + ) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt index 3fb4746efb..acfcac13b7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemOperations.kt @@ -1,33 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem.RoundingMode - -/** - * Update item [RoundingMode] - * - * @param mode new [RoundingMode] - * - * @return updated [DraggableItem] - * */ -internal fun DraggableItem.updateRoundingMode(mode: RoundingMode): DraggableItem = when (this) { - is DraggableItem.GroupPlaceholder -> this - is DraggableItem.GroupHeader -> this.copy(roundingMode = mode) - is DraggableItem.Token -> this.copy(roundingMode = mode) -} - -/** - * Update item shadow visibility - * - * @param show if true then item should be elevated - * - * @return updated [DraggableItem] - * */ -internal fun DraggableItem.updateShadowVisibility(show: Boolean): DraggableItem = when (this) { - is DraggableItem.GroupPlaceholder -> this - is DraggableItem.GroupHeader -> this.copy(showShadow = show) - is DraggableItem.Token -> this.copy(showShadow = show) -} +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem internal fun getGroupPlaceholder(index: Int): DraggableItem.GroupPlaceholder { return DraggableItem.GroupPlaceholder(id = "placeholder_${index.inc()}") diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt index 58ae4fcefd..74ada001e8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/DraggableItemsOperations.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import kotlinx.collections.immutable.PersistentList import org.burnoutcrew.reorderable.ItemPosition diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt index bf5c601b7e..4c041f01fe 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/OrganiseTokensListStateOperations.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import kotlinx.collections.immutable.PersistentList import kotlinx.collections.immutable.toPersistentList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt index fc5c571ad9..4c26e09a29 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/InProgressStateConverter.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.utils.converter.TwoWayConverter internal class InProgressStateConverter : TwoWayConverter { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt index be5a3a12dc..613d5e3dfc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/TokenListToStateConverter.kt @@ -2,8 +2,8 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter import com.tangem.common.Provider import com.tangem.domain.tokens.model.TokenList -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items.TokenListToListStateConverter import com.tangem.utils.converter.Converter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt index f32dfbb528..9c11b12566 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt @@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.er import com.tangem.common.Provider import com.tangem.domain.tokens.error.TokenListError -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter import com.tangem.utils.converter.Converter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt index 3072cc33c4..fab77e7e34 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt @@ -2,7 +2,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.er import com.tangem.common.Provider import com.tangem.domain.tokens.error.TokenListSortingError -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter import com.tangem.utils.converter.Converter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt index f0e892fc45..8c2cac767f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -6,7 +6,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTokenItemId import com.tangem.utils.converter.Converter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt index 47e698cf55..ddd09e1da9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/NetworkGroupToDraggableItemsConverter.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items import com.tangem.domain.tokens.model.NetworkGroup -import com.tangem.feature.wallet.presentation.organizetokens.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupHeaderId import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getGroupPlaceholder import com.tangem.utils.converter.Converter diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt index f62bd3abb9..bb1b829d47 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/TokenListToListStateConverter.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items import com.tangem.domain.tokens.model.TokenList -import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensListState +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.utils.common.uniteItems import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.toPersistentList From 9ce639e08054fb8f57ed76822a6cec9f0d9a4f72 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 8 Aug 2023 23:50:09 +0300 Subject: [PATCH 14/52] Updated on 2026-08-14 --- .../repository/DefaultCurrenciesRepository.kt | 6 +-- .../repository/DefaultNetworksRepository.kt | 4 +- .../tokens/utils/CardCurrenciesFactory.kt | 4 +- .../tokens/utils/ResponseCurrenciesFactory.kt | 4 +- .../tokens/utils/UserTokensResponseFactory.kt | 2 +- .../tokens/ApplyTokenListSortingUseCase.kt | 19 +++++----- .../domain/tokens/GetTokenListUseCase.kt | 4 +- .../domain/tokens/model/NetworkGroup.kt | 4 +- .../tangem/domain/tokens/model/TokenList.kt | 8 ++-- .../CurrenciesStatusesOperations.kt | 18 ++++----- .../TokenListFiatBalanceOperations.kt | 4 +- .../tokens/operations/TokenListOperations.kt | 24 ++++++------ .../operations/TokenListSortingOperations.kt | 37 +++++++++---------- .../tokens/repository/CurrenciesRepository.kt | 10 ++--- .../ApplyTokenListSortingUseCaseTest.kt | 17 ++++----- .../domain/tokens/GetTokenListUseCaseTest.kt | 4 +- .../domain/tokens/mock/MockNetworksGroups.kt | 20 +++++----- .../domain/tokens/mock/MockTokenLists.kt | 18 ++++----- .../tangem/domain/tokens/mock/MockTokens.kt | 2 +- .../domain/tokens/mock/MockTokensStates.kt | 6 +-- .../repository/MockCurrenciesRepository.kt | 8 ++-- 21 files changed, 110 insertions(+), 113 deletions(-) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 21c8020e2d..2c1c49b48b 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -37,7 +37,7 @@ internal class DefaultCurrenciesRepository( override suspend fun saveTokens( userWalletId: UserWalletId, - currencies: Set, + currencies: List, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { @@ -64,7 +64,7 @@ internal class DefaultCurrenciesRepository( override fun getMultiCurrencyWalletCurrencies( userWalletId: UserWalletId, refresh: Boolean, - ): Flow> = channelFlow { + ): Flow> = channelFlow { val userWallet = getUserWallet(userWalletId) ensureIsCorrectUserWallet(userWallet, isMultiCurrencyWalletExpected = true) @@ -115,7 +115,7 @@ internal class DefaultCurrenciesRepository( } } - private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow> { + private fun getMultiCurrencyWalletCurrencies(userWallet: UserWallet): Flow> { return userTokensStore.get(userWallet.walletId).map { storedTokens -> responseCurrenciesFactory.createCurrencies( response = storedTokens, diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index 398586bf6c..dc5a8c2223 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -90,7 +90,7 @@ internal class DefaultNetworksRepository( val result = walletManagersFacade.update( userWalletId = userWalletId, networkId = networkId, - extraTokens = currencies.filterIsInstanceTo(hashSetOf()), + extraTokens = currencies.filterIsInstance().toSet(), ) val networkStatus = networkStatusFactory.createNetworkStatus( networkId = networkId, @@ -103,7 +103,7 @@ internal class DefaultNetworksRepository( } } - private suspend fun getCurrencies(userWalletId: UserWalletId): Set { + private suspend fun getCurrencies(userWalletId: UserWalletId): List { val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find user wallet with provided ID: $userWalletId" } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt index 15fd96b0aa..52724b7ac1 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt @@ -12,7 +12,7 @@ import com.tangem.blockchain.common.Token as SdkToken internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { - fun createDefaultCoinsForMultiCurrencyCard(card: CardDTO): Set { + fun createDefaultCoinsForMultiCurrencyCard(card: CardDTO): List { var blockchains = if (demoConfig.isDemoCardId(card.cardId)) { demoConfig.demoBlockchains } else { @@ -23,7 +23,7 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { blockchains = blockchains.mapNotNull { it.getTestnetVersion() } } - return blockchains.mapNotNull { createCoin(it, card) }.toSet() + return blockchains.mapNotNull { createCoin(it, card) } } fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt index cbecf6a792..ab27676061 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -24,8 +24,8 @@ internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { } } - fun createCurrencies(response: UserTokensResponse, card: CardDTO): Set { - return response.tokens.mapNotNull { createCurrency(it, card) }.toSet() + fun createCurrencies(response: UserTokensResponse, card: CardDTO): List { + return response.tokens.mapNotNull { createCurrency(it, card) } } private fun createCurrency(responseToken: UserTokensResponse.Token, card: CardDTO): CryptoCurrency? { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index 437100ce08..aac91b05ba 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -7,7 +7,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency internal class UserTokensResponseFactory { fun createUserTokensResponse( - currencies: Set, + currencies: List, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ): UserTokensResponse { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 907fae6af5..187b2e5be0 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -5,6 +5,7 @@ import arrow.core.raise.Raise import arrow.core.raise.catch import arrow.core.raise.either import arrow.core.raise.ensureNotNull +import arrow.core.toNonEmptyListOrNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.model.CryptoCurrency @@ -22,7 +23,7 @@ class ApplyTokenListSortingUseCase( suspend operator fun invoke( userWalletId: UserWalletId, - sortedTokensIds: Set>, + sortedTokensIds: List>, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ): Either { @@ -39,16 +40,16 @@ class ApplyTokenListSortingUseCase( } private suspend fun Raise.sortTokens( - sortedTokensIds: Set>, - unsortedTokens: Set, - ): Set = withContext(dispatchers.default) { + sortedTokensIds: List>, + unsortedTokens: List, + ): List = withContext(dispatchers.default) { val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) { TokenListSortingError.TokenListIsEmpty } val sortedTokens = sortedMapOf() - unsortedTokens.forEach { token -> + unsortedTokens.distinct().forEach { token -> val index = nonEmptySortedTokensIds.indexOfFirst { (networkId, tokenId) -> networkId == token.networkId && tokenId == token.id } @@ -60,12 +61,12 @@ class ApplyTokenListSortingUseCase( } } - ensureNotNull(sortedTokens.values.toNonEmptySetOrNull()) { + ensureNotNull(sortedTokens.values.toNonEmptyListOrNull()) { TokenListSortingError.TokenListIsEmpty } } - private suspend fun Raise.getCurrencies(userWalletId: UserWalletId): Set { + private suspend fun Raise.getCurrencies(userWalletId: UserWalletId): List { val tokens = catch( block = { currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh = false).firstOrNull() @@ -73,14 +74,14 @@ class ApplyTokenListSortingUseCase( catch = { raise(TokenListSortingError.DataError(it)) }, ) - return ensureNotNull(tokens?.toNonEmptySetOrNull()) { + return ensureNotNull(tokens?.toNonEmptyListOrNull()) { TokenListSortingError.TokenListIsEmpty } } private suspend fun Raise.applySorting( userWalletId: UserWalletId, - tokens: Set, + tokens: List, isGrouped: Boolean, isSortedByBalance: Boolean, ) = withContext(dispatchers.io) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index 1b046264dd..f27dadfec9 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -43,7 +43,7 @@ class GetTokenListUseCase( private fun getTokensStatuses( userWalletId: UserWalletId, refresh: Boolean, - ): Flow>> { + ): Flow>> { val operations = CurrenciesStatusesOperations( userWalletId = userWalletId, refresh = refresh, @@ -58,7 +58,7 @@ class GetTokenListUseCase( private fun createTokenList( userWalletId: UserWalletId, - tokens: Set, + tokens: List, ): Flow> { val operations = TokenListOperations( userWalletId = userWalletId, diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt index 89264ac332..d2463c0f16 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkGroup.kt @@ -8,9 +8,9 @@ import com.tangem.domain.tokens.models.Network * This class encapsulates a collection of cryptocurrency statuses, all of which are part of the same blockchain network. * * @property network The blockchain network associated with the group. - * @property currencies A set of cryptocurrency statuses that belong to the network. + * @property currencies A list of cryptocurrency statuses that belong to the network. */ data class NetworkGroup( val network: Network, - val currencies: Set, + val currencies: List, ) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt index 55327b82da..c447c3ac8d 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/TokenList.kt @@ -18,12 +18,12 @@ sealed class TokenList { /** * Represents tokens that are grouped by their network. * - * @property groups A set of network groups containing tokens. + * @property groups A list of network groups containing tokens. * @property totalFiatBalance The total fiat balance across all groups. * @property sortedBy The criteria used for sorting the tokens within the groups. */ data class GroupedByNetwork( - val groups: Set, + val groups: List, override val totalFiatBalance: FiatBalance, override val sortedBy: SortType, ) : TokenList() @@ -31,12 +31,12 @@ sealed class TokenList { /** * Represents tokens that are not grouped by any specific criteria. * - * @property currencies A set of cryptocurrency statuses. + * @property currencies A list of cryptocurrency statuses. * @property totalFiatBalance The total fiat balance across all currencies. * @property sortedBy The criteria used for sorting the currencies. */ data class Ungrouped( - val currencies: Set, + val currencies: List, override val totalFiatBalance: FiatBalance, override val sortedBy: SortType, ) : TokenList() diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 6e1032e5c1..1fbd547bbc 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -33,14 +33,14 @@ internal class CurrenciesStatusesOperations( ) @OptIn(ExperimentalCoroutinesApi::class) - fun getCurrenciesStatusesFlow(): Flow>> { + fun getCurrenciesStatusesFlow(): Flow>> { return getMultiCurrencyWalletCurrencies().flatMapMerge flatMap@{ maybeCurrencies -> val nonEmptyCurrencies = maybeCurrencies.fold( ifLeft = { error -> return@flatMap flowOf(error.left()) }, - ifRight = { it.toNonEmptySetOrNull() }, - ) ?: return@flatMap flowOf(emptySet().right()) + ifRight = { it.toNonEmptyListOrNull() }, + ) ?: return@flatMap flowOf(emptyList().right()) val (networksIds, currenciesIds) = getIds(nonEmptyCurrencies) @@ -96,11 +96,11 @@ internal class CurrenciesStatusesOperations( } private fun createCurrenciesStatuses( - currencies: NonEmptySet, + currencies: NonEmptyList, quotes: Set, networkStatuses: Set, - ): Set { - return currencies.mapTo(hashSetOf()) { token -> + ): List { + return currencies.map { token -> val quote = quotes.firstOrNull { it.currencyId == token.id } val networkStatus = networkStatuses.firstOrNull { it.networkId == token.networkId } @@ -122,9 +122,9 @@ internal class CurrenciesStatusesOperations( return currencyStatusOperations.createTokenStatus() } - private fun getMultiCurrencyWalletCurrencies(): Flow>> { + private fun getMultiCurrencyWalletCurrencies(): Flow>> { return currenciesRepository.getMultiCurrencyWalletCurrencies(userWalletId, refresh) - .map, Either>> { it.right() } + .map, Either>> { it.right() } .catch { emit(Error.DataError(it).left()) } .onEmpty { emit(Error.EmptyCurrencies.left()) } } @@ -157,7 +157,7 @@ internal class CurrenciesStatusesOperations( } private fun getIds( - currencies: NonEmptySet, + currencies: NonEmptyList, ): Pair, NonEmptySet> { val currencyIdToNetworkId = currencies.associate { currency -> currency.id to currency.networkId diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt index 2373f7f6ef..830950ae1e 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt @@ -1,12 +1,12 @@ package com.tangem.domain.tokens.operations -import arrow.core.NonEmptySet +import arrow.core.NonEmptyList import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import java.math.BigDecimal internal class TokenListFiatBalanceOperations( - private val currencies: NonEmptySet, + private val currencies: NonEmptyList, private val isAnyTokenLoading: Boolean, ) { diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt index ada01f316b..627df695af 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListOperations.kt @@ -16,12 +16,12 @@ internal class TokenListOperations( private val currenciesRepository: CurrenciesRepository, private val networksRepository: NetworksRepository, private val userWalletId: UserWalletId, - private val tokens: Set, + private val tokens: List, ) { constructor( userWalletId: UserWalletId, - tokens: Set, + tokens: List, useCase: GetTokenListUseCase, ) : this( currenciesRepository = useCase.currenciesRepository, @@ -42,14 +42,14 @@ internal class TokenListOperations( } private fun Raise.createTokenList(isGrouped: Boolean, isSortedByBalance: Boolean): TokenList { - val tokensNes = tokens.toNonEmptySetOrNull() + val nonEmptyCurrencies = tokens.toNonEmptyListOrNull() ?: return TokenList.NotInitialized - val isAnyTokenLoading = tokensNes.any { it.value is CryptoCurrencyStatus.Loading } - val fiatBalanceOperations = TokenListFiatBalanceOperations(tokensNes, isAnyTokenLoading) + val isAnyTokenLoading = nonEmptyCurrencies.any { it.value is CryptoCurrencyStatus.Loading } + val fiatBalanceOperations = TokenListFiatBalanceOperations(nonEmptyCurrencies, isAnyTokenLoading) return createTokenList( - tokens = tokensNes, + currencies = nonEmptyCurrencies, fiatBalance = fiatBalanceOperations.calculateFiatBalance(), isAnyTokenLoading = isAnyTokenLoading, isGrouped = isGrouped, @@ -58,23 +58,23 @@ internal class TokenListOperations( } private fun Raise.createTokenList( - tokens: NonEmptySet, + currencies: NonEmptyList, fiatBalance: TokenList.FiatBalance, isAnyTokenLoading: Boolean, isGrouped: Boolean, isSortedByBalance: Boolean, ): TokenList { val sortingOperations = TokenListSortingOperations( - currencies = tokens, + currencies = currencies, isAnyTokenLoading = isAnyTokenLoading, sortByBalance = isSortedByBalance, ) - return createTokenList(tokens, sortingOperations, fiatBalance, isGrouped) + return createTokenList(currencies, sortingOperations, fiatBalance, isGrouped) } private fun Raise.createTokenList( - tokens: NonEmptySet, + tokens: NonEmptyList, sortingOperations: TokenListSortingOperations, fiatBalance: TokenList.FiatBalance, isGrouped: Boolean, @@ -92,7 +92,7 @@ internal class TokenListOperations( } } - private fun Raise.getNetworks(tokensNes: NonEmptySet): Set { + private fun Raise.getNetworks(tokensNes: NonEmptyList): Set { val networksIds = tokensNes.map { it.currency.networkId }.toNonEmptySet() return catch( @@ -131,7 +131,7 @@ internal class TokenListOperations( ) private fun createUnsortedUngroupedTokenList( - tokens: Set, + tokens: List, fiatBalance: TokenList.FiatBalance, ): TokenList.Ungrouped { return TokenList.Ungrouped( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt index 5bf58c6cf7..4e609a140f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListSortingOperations.kt @@ -1,12 +1,10 @@ package com.tangem.domain.tokens.operations -import arrow.core.Either -import arrow.core.NonEmptySet +import arrow.core.* import arrow.core.raise.Raise import arrow.core.raise.either import arrow.core.raise.ensure import arrow.core.raise.ensureNotNull -import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList @@ -14,7 +12,7 @@ import com.tangem.domain.tokens.models.Network import java.math.BigDecimal internal class TokenListSortingOperations( - private val currencies: Set, + private val currencies: List, private val isAnyTokenLoading: Boolean, private val sortByBalance: Boolean, ) { @@ -25,15 +23,15 @@ internal class TokenListSortingOperations( isAnyTokenLoading: Boolean = tokenList.totalFiatBalance is TokenList.FiatBalance.Loading, ) : this( currencies = when (tokenList) { - is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.currencies }.toSet() + is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.currencies } is TokenList.Ungrouped -> tokenList.currencies - is TokenList.NotInitialized -> emptySet() + is TokenList.NotInitialized -> emptyList() }, isAnyTokenLoading = isAnyTokenLoading, sortByBalance = sortByBalance, ) - fun getGroupedTokens(networks: Set): Either> = either { + fun getGroupedTokens(networks: Set): Either> = either { ensure(currencies.isNotEmpty()) { Error.EmptyTokens } val networksNes = ensureNotNull(networks.toNonEmptySetOrNull()) { Error.EmptyNetworks @@ -46,17 +44,17 @@ internal class TokenListSortingOperations( } } - fun getTokens(): Either> = either { - val tokensNes = ensureNotNull(currencies.toNonEmptySetOrNull()) { + fun getTokens(): Either> = either { + val nonEmptyCurrencies = ensureNotNull(currencies.toNonEmptyListOrNull()) { Error.EmptyTokens } - if (sortByBalance) sortTokensByBalance(tokensNes) else tokensNes + if (sortByBalance) sortTokensByBalance(nonEmptyCurrencies) else nonEmptyCurrencies } fun getSortType(): TokenList.SortType = if (sortByBalance) TokenList.SortType.BALANCE else TokenList.SortType.NONE - private fun Raise.groupTokens(networks: NonEmptySet): NonEmptySet { + private fun Raise.groupTokens(networks: NonEmptySet): NonEmptyList { val groupedTokens = currencies .groupBy { it.currency.networkId } .map { (networkId, tokens) -> @@ -66,22 +64,21 @@ internal class TokenListSortingOperations( NetworkGroup( network = network, - currencies = ensureNotNull(tokens.toNonEmptySetOrNull()) { Error.EmptyTokens }, + currencies = ensureNotNull(tokens.toNonEmptyListOrNull()) { Error.EmptyTokens }, ) } - .toNonEmptySetOrNull() + .toNonEmptyListOrNull() return ensureNotNull(groupedTokens) { Error.EmptyTokens } } - private fun Raise.groupAndSortTokensByBalance(networks: NonEmptySet): NonEmptySet { + private fun Raise.groupAndSortTokensByBalance(networks: NonEmptySet): NonEmptyList { val groupsWithSortedTokens = groupTokens(networks) .map { group -> - val tokens = group.currencies as? NonEmptySet + val tokens = group.currencies as? NonEmptyList ?: error("Tokens can not be empty here") group.copy(currencies = sortTokensByBalance(tokens)) } - .toNonEmptySet() return if (isAnyTokenLoading) { groupsWithSortedTokens @@ -90,22 +87,22 @@ internal class TokenListSortingOperations( } } - private fun sortTokensByBalance(tokens: NonEmptySet): NonEmptySet { + private fun sortTokensByBalance(tokens: NonEmptyList): NonEmptyList { return if (isAnyTokenLoading) { tokens } else { tokens.sortedByDescending { it.value.fiatAmount ?: BigDecimal.ZERO } - .toNonEmptySetOrNull() + .toNonEmptyListOrNull() ?: error("Tokens can not be empty here") } } - private fun sortGroupsByBalance(groupsWithSortedTokens: NonEmptySet): NonEmptySet { + private fun sortGroupsByBalance(groupsWithSortedTokens: NonEmptyList): NonEmptyList { return groupsWithSortedTokens .sortedByDescending { group -> group.currencies.sumOf { it.value.fiatAmount ?: BigDecimal.ZERO } } - .toNonEmptySetOrNull() + .toNonEmptyListOrNull() ?: error("Tokens can not be empty here") } diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 8b5e0fe9e5..85db960be0 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -10,11 +10,11 @@ import kotlinx.coroutines.flow.Flow interface CurrenciesRepository { /** - * Saves the given set of cryptocurrencies, along with the preferences for grouping and sorting, for a specific + * Saves the given list of cryptocurrencies, along with the preferences for grouping and sorting, for a specific * multi-currency user wallet. * * @param userWalletId The unique identifier of the user wallet. - * @param currencies The set of cryptocurrencies to be saved. + * @param currencies The list of cryptocurrencies to be saved. * @param isGroupedByNetwork A boolean flag indicating whether the tokens should be grouped by network. * @param isSortedByBalance A boolean flag indicating whether the tokens should be sorted by balance. * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet @@ -22,7 +22,7 @@ interface CurrenciesRepository { */ suspend fun saveTokens( userWalletId: UserWalletId, - currencies: Set, + currencies: List, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) @@ -38,7 +38,7 @@ interface CurrenciesRepository { suspend fun getSingleCurrencyWalletPrimaryCurrency(userWalletId: UserWalletId): CryptoCurrency /** - * Retrieves the set of cryptocurrencies within a multi-currency wallet. + * Retrieves the list of cryptocurrencies within a multi-currency wallet. * * @param userWalletId The unique identifier of the user wallet. * @param refresh A boolean flag indicating whether the data should be refreshed. @@ -46,7 +46,7 @@ interface CurrenciesRepository { * @throws com.tangem.domain.core.error.DataError.UserWalletError.WrongUserWallet If single-currency user wallet * ID provided. */ - fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow> + fun getMultiCurrencyWalletCurrencies(userWalletId: UserWalletId, refresh: Boolean): Flow> /** * Retrieves the cryptocurrency for a specific multi-currency user wallet. diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index 16ee99fff2..b56a18415e 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -32,7 +32,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokensIds = emptySet(), + sortedTokensIds = emptyList(), isGroupedByNetwork = false, isSortedByBalance = false, ) @@ -54,7 +54,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokensIds = MockTokens.tokens.map { it.networkId to it.id }.toSet(), + sortedTokensIds = MockTokens.tokens.map { it.networkId to it.id }, isGroupedByNetwork = false, isSortedByBalance = false, ) @@ -76,7 +76,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + sortedTokensIds = expectedTokens.map { it.networkId to it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -100,7 +100,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + sortedTokensIds = expectedTokens.map { it.networkId to it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -124,7 +124,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + sortedTokensIds = expectedTokens.map { it.networkId to it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -148,7 +148,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }.toSet(), + sortedTokensIds = expectedTokens.map { it.networkId to it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -170,7 +170,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokensIds = getSortedTokens().drop(n = 3).map { it.networkId to it.id }.toSet(), + sortedTokensIds = getSortedTokens().drop(n = 3).map { it.networkId to it.id }, isGroupedByNetwork = false, isSortedByBalance = false, ) @@ -181,7 +181,6 @@ internal class ApplyTokenListSortingUseCaseTest { private fun getSortedTokens() = MockTokens.tokens .sortedBy { Random.nextInt(0, MockTokens.tokens.size) } - .toSet() private fun getUseCase(tokensRepository: MockCurrenciesRepository = getTokensRepository()) = ApplyTokenListSortingUseCase( @@ -191,7 +190,7 @@ internal class ApplyTokenListSortingUseCaseTest { private fun getTokensRepository( sortTokensResult: Either = Unit.right(), - tokens: Flow>> = flowOf(MockTokens.tokens.right()), + tokens: Flow>> = flowOf(MockTokens.tokens.right()), ): MockCurrenciesRepository { return MockCurrenciesRepository(sortTokensResult, MockTokens.token1.right(), tokens, emptyFlow(), emptyFlow()) } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index e7dfb90fd8..4336ee54a3 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -225,7 +225,7 @@ internal class GetTokenListUseCaseTest { fun `when tokens is empty then not initialized token list should be received`() = runTest { val expectedResult = MockTokenLists.notInitializedTokenList.right() - val useCase = getUseCase(tokens = flowOf(emptySet().right())) + val useCase = getUseCase(tokens = flowOf(emptyList().right())) // When val result = useCase(userWalletId).first() @@ -319,7 +319,7 @@ internal class GetTokenListUseCaseTest { } private fun getUseCase( - tokens: Flow>> = flowOf(MockTokens.tokens.right()), + tokens: Flow>> = flowOf(MockTokens.tokens.right()), quotes: Flow>> = flowOf(MockQuotes.quotes.right()), networks: Either> = MockNetworks.networks.right(), statuses: Flow>> = flowOf(MockNetworks.errorNetworksStatuses.right()), diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt index ebda181faf..ab3078de1d 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockNetworksGroups.kt @@ -1,7 +1,7 @@ package com.tangem.domain.tokens.mock -import arrow.core.nonEmptySetOf -import arrow.core.toNonEmptySetOrNull +import arrow.core.nonEmptyListOf +import arrow.core.toNonEmptyListOrNull import com.tangem.domain.tokens.model.NetworkGroup @Suppress("MemberVisibilityCanBePrivate") @@ -11,42 +11,42 @@ internal object MockNetworksGroups { network = MockNetworks.network1, currencies = MockTokensStates.failedTokenStates .filter { it.currency.networkId == MockNetworks.network1.id } - .toNonEmptySetOrNull()!!, + .toNonEmptyListOrNull()!!, ) val networkGroup2 = NetworkGroup( network = MockNetworks.network2, currencies = MockTokensStates.failedTokenStates .filter { it.currency.networkId == MockNetworks.network2.id } - .toNonEmptySetOrNull()!!, + .toNonEmptyListOrNull()!!, ) val networkGroup3 = NetworkGroup( network = MockNetworks.network3, currencies = MockTokensStates.failedTokenStates .filter { it.currency.networkId == MockNetworks.network3.id } - .toNonEmptySetOrNull()!!, + .toNonEmptyListOrNull()!!, ) - val failedNetworksGroups = nonEmptySetOf(networkGroup1, networkGroup2, networkGroup3) + val failedNetworksGroups = nonEmptyListOf(networkGroup1, networkGroup2, networkGroup3) val loadedNetworksGroups = failedNetworksGroups.map { group -> group.copy( currencies = MockTokensStates.loadedTokensStates .filter { it.currency.networkId == group.network.id } - .toNonEmptySetOrNull()!!, + .toNonEmptyListOrNull()!!, ) - }.toNonEmptySet() + } val sortedNetworksGroups = loadedNetworksGroups.map { group -> group.copy( currencies = group.currencies .sortedByDescending { it.value.fiatAmount } - .toNonEmptySetOrNull()!!, + .toNonEmptyListOrNull()!!, ) } .sortedByDescending { group -> group.currencies.sumOf { it.value.fiatAmount!! } } - .toNonEmptySetOrNull()!! + .toNonEmptyListOrNull()!! } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt index 403dcb5046..de1f837afd 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt @@ -1,7 +1,7 @@ package com.tangem.domain.tokens.mock -import arrow.core.NonEmptySet -import arrow.core.toNonEmptySetOrNull +import arrow.core.NonEmptyList +import arrow.core.toNonEmptyListOrNull import com.tangem.domain.tokens.mock.MockNetworksGroups.failedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.loadedNetworksGroups import com.tangem.domain.tokens.mock.MockNetworksGroups.sortedNetworksGroups @@ -18,13 +18,13 @@ internal object MockTokenLists { val notInitializedTokenList = TokenList.NotInitialized val emptyGroupedTokenList = TokenList.GroupedByNetwork( - groups = emptySet(), + groups = emptyList(), totalFiatBalance = TokenList.FiatBalance.Failed, sortedBy = TokenList.SortType.NONE, ) val emptyUngroupedTokenList = TokenList.Ungrouped( - currencies = emptySet(), + currencies = emptyList(), totalFiatBalance = TokenList.FiatBalance.Failed, sortedBy = TokenList.SortType.NONE, ) @@ -43,7 +43,7 @@ internal object MockTokenLists { val loadingUngroupedTokenList = with(failedUngroupedTokenList) { copy( - currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptySetOrNull()!!, + currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptyListOrNull()!!, totalFiatBalance = TokenList.FiatBalance.Loading, ) } @@ -55,9 +55,9 @@ internal object MockTokenLists { group.copy( currencies = group.currencies .map { it.copy(value = CryptoCurrencyStatus.Loading) } - .toNonEmptySetOrNull()!!, + .toNonEmptyListOrNull()!!, ) - }.toNonEmptySetOrNull()!!, + }.toNonEmptyListOrNull()!!, ) } @@ -84,7 +84,7 @@ internal object MockTokenLists { sortedBy = TokenList.SortType.NONE, totalFiatBalance = TokenList.FiatBalance.Loaded( amount = groups - .flatMap { it.currencies as NonEmptySet } + .flatMap { it.currencies as NonEmptyList } .sumOf { it.value.fiatAmount ?: BigDecimal.ZERO }, isAllAmountsSummarized = true, ), @@ -95,7 +95,7 @@ internal object MockTokenLists { get() { val tokens = MockTokensStates.loadedTokensStates .sortedByDescending { it.value.fiatAmount } - .toNonEmptySetOrNull()!! + .toNonEmptyListOrNull()!! return unsortedUngroupedTokenList.copy( currencies = tokens, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt index b0af037c5e..7ff7ba350a 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt @@ -119,5 +119,5 @@ internal object MockTokens { derivationPath = null, ) - val tokens = setOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10) + val tokens = listOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10) } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt index 4df0a5d525..93acd79dc2 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt @@ -1,6 +1,6 @@ package com.tangem.domain.tokens.mock -import arrow.core.nonEmptySetOf +import arrow.core.nonEmptyListOf import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus @@ -57,7 +57,7 @@ internal object MockTokensStates { value = CryptoCurrencyStatus.NoAccount, ) - val failedTokenStates = nonEmptySetOf( + val failedTokenStates = nonEmptyListOf( tokenState1, tokenState2, tokenState3, @@ -86,5 +86,5 @@ internal object MockTokensStates { hasTransactionsInProgress = false, ), ) - }.toNonEmptySet() + } } \ No newline at end of file diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index 8ebf978969..9570b02157 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -11,12 +11,12 @@ import kotlinx.coroutines.flow.map internal class MockCurrenciesRepository( private val sortTokensResult: Either, private val token: Either, - private val tokens: Flow>>, + private val tokens: Flow>>, private val isGrouped: Flow>, private val isSortedByBalance: Flow>, ) : CurrenciesRepository { - var tokensIdsAfterSortingApply: Set? = null + var tokensIdsAfterSortingApply: List? = null private set var isTokensGroupedAfterSortingApply: Boolean? = null @@ -27,7 +27,7 @@ internal class MockCurrenciesRepository( override suspend fun saveTokens( userWalletId: UserWalletId, - currencies: Set, + currencies: List, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ) { @@ -45,7 +45,7 @@ internal class MockCurrenciesRepository( override fun getMultiCurrencyWalletCurrencies( userWalletId: UserWalletId, refresh: Boolean, - ): Flow> { + ): Flow> { return tokens.map { it.getOrElse { e -> throw e } } } From 7e992b986b168877a0d63753c2ed76a2b68cca84 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 17:49:23 +0800 Subject: [PATCH 15/52] Updated on 2026-08-14 --- .../wallet/state/WalletLoading.kt | 37 ------ .../wallet/state/WalletMultiCurrencyState.kt | 2 +- .../wallet/state/WalletSingleCurrencyState.kt | 2 +- .../presentation/wallet/state/WalletState.kt | 41 ++++++ .../wallet/state/WalletStateHolder.kt | 103 --------------- .../WalletLoadedTokensListConverter.kt | 16 ++- .../factory/WalletSkeletonStateConverter.kt | 117 ++++++++--------- .../state/factory/WalletStateFactory.kt | 111 ++++++++++------ .../WalletLoadedTxHistoryConverter.kt | 51 ++------ .../WalletLoadingTxHistoryConverter.kt | 69 ++-------- .../presentation/wallet/ui/WalletScreen.kt | 24 ++-- .../ui/components/common/WalletContent.kt | 7 +- .../components/common/WalletNotifications.kt | 8 +- .../wallet/utils/TokenListErrorConverter.kt | 21 +++ .../TokenListErrorToWalletStateConverter.kt | 28 ---- .../utils/TokenListToWalletStateConverter.kt | 46 +++---- .../wallet/viewmodels/WalletClickIntents.kt | 2 + .../WalletNotificationsListFactory.kt | 4 +- .../wallet/viewmodels/WalletViewModel.kt | 120 +++++++++++++----- 19 files changed, 349 insertions(+), 460 deletions(-) delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt deleted file mode 100644 index 55e211840f..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLoading.kt +++ /dev/null @@ -1,37 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf - -/** - * Loading wallet state - * - * @property onBackClick Lambda be invoked when back button is clicked - * -[REDACTED_AUTHOR] - */ -internal data class WalletLoading(override val onBackClick: () -> Unit) : WalletStateHolder() { - - override val topBarConfig = WalletTopBarConfig(onScanCardClick = {}, onMoreClick = {}) - - override val walletsListConfig = WalletsListConfig( - selectedWalletIndex = 0, - wallets = persistentListOf( - WalletCardState.Loading( - id = UserWalletId(stringValue = ""), - title = "", - additionalInfo = "", - imageResId = null, - ), - ), - onWalletChange = {}, - ) - - override val pullToRefreshConfig = WalletPullToRefreshConfig(isRefreshing = false, onRefresh = {}) - - override val notifications: ImmutableList = persistentListOf() - - override val bottomSheetConfig = null -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt index 0b93c6033d..87d675cd95 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletMultiCurrencyState.kt @@ -9,7 +9,7 @@ import kotlinx.collections.immutable.persistentListOf * [REDACTED_AUTHOR] */ -internal sealed class WalletMultiCurrencyState : WalletStateHolder() { +internal sealed class WalletMultiCurrencyState : WalletState.ContentState() { /** Tokens list state */ abstract val tokensListState: WalletTokensListState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt index f29b229afd..616d8d5f0b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -10,7 +10,7 @@ import kotlinx.collections.immutable.persistentListOf * [REDACTED_AUTHOR] */ -internal sealed class WalletSingleCurrencyState : WalletStateHolder() { +internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { /** Manage buttons */ abstract val buttons: ImmutableList diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt new file mode 100644 index 0000000000..570accc073 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt @@ -0,0 +1,41 @@ +package com.tangem.feature.wallet.presentation.wallet.state + +import com.tangem.feature.wallet.presentation.wallet.state.components.* +import kotlinx.collections.immutable.ImmutableList + +/** + * Wallet screen state + * +[REDACTED_AUTHOR] + */ +internal sealed class WalletState { + + /** Lambda be invoked when back button is clicked */ + abstract val onBackClick: () -> Unit + + /** Wallet screen content state */ + sealed class ContentState : WalletState() { + + /** Top bar config */ + abstract val topBarConfig: WalletTopBarConfig + + /** Wallets list config */ + abstract val walletsListConfig: WalletsListConfig + + /** Pull to refresh config */ + abstract val pullToRefreshConfig: WalletPullToRefreshConfig + + /** Notifications */ + abstract val notifications: ImmutableList + + /** Bottom sheet config */ + abstract val bottomSheetConfig: WalletBottomSheetConfig? + } + + /** + * Initial state + * + * @property onBackClick lambda be invoked when back button is clicked + */ + data class Initial(override val onBackClick: () -> Unit) : WalletState() +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt deleted file mode 100644 index 25338db85c..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletStateHolder.kt +++ /dev/null @@ -1,103 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state - -import com.tangem.feature.wallet.presentation.wallet.state.components.* -import kotlinx.collections.immutable.ImmutableList - -/** - * Wallet screen state holder - * -[REDACTED_AUTHOR] - */ -internal sealed class WalletStateHolder { - - /** Lambda be invoked when back button is clicked */ - abstract val onBackClick: () -> Unit - - /** Top bar config */ - abstract val topBarConfig: WalletTopBarConfig - - /** Wallets list config */ - abstract val walletsListConfig: WalletsListConfig - - /** Pull to refresh config */ - abstract val pullToRefreshConfig: WalletPullToRefreshConfig - - /** Notifications */ - abstract val notifications: ImmutableList - - /** Bottom sheet config */ - abstract val bottomSheetConfig: WalletBottomSheetConfig? - - fun copySealed( - onBackClick: () -> Unit = this.onBackClick, - topBarConfig: WalletTopBarConfig = this.topBarConfig, - walletsListConfig: WalletsListConfig = this.walletsListConfig, - pullToRefreshConfig: WalletPullToRefreshConfig = this.pullToRefreshConfig, - notifications: ImmutableList = this.notifications, - bottomSheet: WalletBottomSheetConfig? = this.bottomSheetConfig, - ): WalletStateHolder { - return when (this) { - is WalletLoading -> { - copy(onBackClick = onBackClick) - } - is WalletMultiCurrencyState.Content -> { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheet, - ) - } - is WalletMultiCurrencyState.Locked -> { - if (bottomSheet != null) { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - isBottomSheetShow = bottomSheet.isShow, - onBottomSheetDismiss = bottomSheet.onDismissRequest, - ) - } else { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - ) - } - } - is WalletSingleCurrencyState.Content -> { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheet, - ) - } - is WalletSingleCurrencyState.Locked -> { - if (bottomSheet != null) { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - isBottomSheetShow = bottomSheet.isShow, - onBottomSheetDismiss = bottomSheet.onDismissRequest, - ) - } else { - copy( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - ) - } - } - } - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt index 9ffc95c838..6b4f617a1b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt @@ -5,28 +5,30 @@ import com.tangem.common.Provider import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletLoadedTokensListConverter.LoadedTokensListModel -import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorToWalletStateConverter +import com.tangem.feature.wallet.presentation.wallet.utils.TokenListErrorConverter import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter /** - * Converter from loaded [TokenListError] or [TokenList] to [WalletStateHolder] + * Converter from loaded [TokenListError] or [TokenList] to [WalletMultiCurrencyState] * * @property currentStateProvider current ui state provider * @param cardTypeResolverProvider card type resolver + * @param isLockedWalletProvider current wallet is locked or not provider * @param clickIntents screen click intents * [REDACTED_AUTHOR] */ internal class WalletLoadedTokensListConverter( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, cardTypeResolverProvider: Provider, isLockedWalletProvider: Provider, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListStateConverter = TokenListToWalletStateConverter( currentStateProvider = currentStateProvider, @@ -38,11 +40,11 @@ internal class WalletLoadedTokensListConverter( clickIntents = clickIntents, ) - private val tokenListErrorStateConverter = TokenListErrorToWalletStateConverter( + private val tokenListErrorStateConverter = TokenListErrorConverter( currentStateProvider = currentStateProvider, ) - override fun convert(value: LoadedTokensListModel): WalletStateHolder { + override fun convert(value: LoadedTokensListModel): WalletMultiCurrencyState.Content { return value.tokenListEither.fold( ifLeft = tokenListErrorStateConverter::convert, ifRight = { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 9289845a10..6c86a81215 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -1,15 +1,14 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory -import androidx.paging.PagingData +import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.domain.WalletImageResolver import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.* import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletSkeletonStateConverter.SkeletonModel import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -17,62 +16,43 @@ import com.tangem.utils.converter.Converter import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList -import kotlinx.coroutines.flow.flow /** - * Converter from loaded list of [UserWallet] to skeleton state of screen [WalletStateHolder] + * Converter from loaded list of [UserWallet] to skeleton state of screen [WalletState.ContentState] * - * @property clickIntents screen click intents + * @property currentStateProvider current ui state provider + * @property clickIntents screen click intents * [REDACTED_AUTHOR] */ internal class WalletSkeletonStateConverter( + private val currentStateProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter { +) : Converter { - override fun convert(value: SkeletonModel): WalletStateHolder { - val wallet = requireNotNull(value.wallets.getOrNull(value.selectedWalletIndex)) { "Empty wallet list" } - val cardTypeResolver = wallet.scanResponse.cardTypesResolver + override fun convert(value: SkeletonModel): WalletState.ContentState { + val cardTypeResolver = value.wallets[value.selectedWalletIndex].scanResponse.cardTypesResolver - return when { - cardTypeResolver.isMultiwalletAllowed() -> createMultiCurrencyState(value) - !cardTypeResolver.isMultiwalletAllowed() -> createSingleCurrencyState(value, cardTypeResolver) - else -> error("Illegal wallet state: $wallet") + return if (cardTypeResolver.isMultiwalletAllowed()) { + createMultiCurrencyState(value = value) + } else { + createSingleCurrencyState(value = value, currencyName = cardTypeResolver.getBlockchain().currency) } } - /** - * Create [WalletMultiCurrencyState.Content]. - * Tokens and notifications are updated asynchronously. - * - * @param value converted value - */ - private fun createMultiCurrencyState(value: SkeletonModel): WalletMultiCurrencyState.Content { + private fun createMultiCurrencyState(value: SkeletonModel): WalletMultiCurrencyState { return WalletMultiCurrencyState.Content( onBackClick = clickIntents::onBackClick, topBarConfig = createTopBarConfig(), walletsListConfig = createWalletsListConfig(value), pullToRefreshConfig = createPullToRefreshConfig(), - tokensListState = WalletTokensListState.Content( - items = persistentListOf(), - onOrganizeTokensClick = null, - ), + tokensListState = WalletTokensListState.Loading, notifications = persistentListOf(), bottomSheetConfig = null, ) } - /** - * Create [WalletSingleCurrencyState.Content]. - * Transactions, notifications and market price are updated asynchronously. - * - * @param value converted value - * @param cardTypeResolver card type resolver - */ - private fun createSingleCurrencyState( - value: SkeletonModel, - cardTypeResolver: CardTypesResolver, - ): WalletSingleCurrencyState.Content { + private fun createSingleCurrencyState(value: SkeletonModel, currencyName: String): WalletSingleCurrencyState { return WalletSingleCurrencyState.Content( onBackClick = clickIntents::onBackClick, topBarConfig = createTopBarConfig(), @@ -81,12 +61,8 @@ internal class WalletSkeletonStateConverter( notifications = persistentListOf(), bottomSheetConfig = null, buttons = getButtons(), - marketPriceBlockState = MarketPriceBlockState.Loading( - currencyName = cardTypeResolver.getBlockchain().currency, - ), - txHistoryState = WalletTxHistoryState.Content( - items = flow { PagingData.empty() }, - ), + marketPriceBlockState = MarketPriceBlockState.Loading(currencyName = currencyName), + txHistoryState = WalletTxHistoryState.Loading(onExploreClick = clickIntents::onExploreClick), ) } @@ -100,22 +76,43 @@ internal class WalletSkeletonStateConverter( private fun createWalletsListConfig(value: SkeletonModel): WalletsListConfig { return WalletsListConfig( selectedWalletIndex = value.selectedWalletIndex, - wallets = value.wallets.map { wallet -> - val cardTypeResolver = wallet.scanResponse.cardTypesResolver - WalletCardState.Loading( - id = wallet.walletId, - title = wallet.name, - additionalInfo = WalletAdditionalInfoFactory.resolve( - cardTypesResolver = cardTypeResolver, - isLocked = wallet.isLocked, - ), - imageResId = WalletImageResolver.resolve(cardTypesResolver = cardTypeResolver), - ) - }.toImmutableList(), + wallets = value.wallets.map(::createWalletState).toImmutableList(), onWalletChange = clickIntents::onWalletChange, ) } + private fun createWalletState(wallet: UserWallet): WalletCardState { + val state = currentStateProvider() + + // If it isn't first initialization (example, when user unlocks wallet) + return if (state is WalletState.ContentState) { + val initializedWallet = state.walletsListConfig.wallets.first { it.id == wallet.walletId } + + // If wallet is initialized, return it, otherwise return loading state + if (initializedWallet !is WalletCardState.Loading) { + initializedWallet + } else { + createWalletLoadingState(wallet) + } + } else { + createWalletLoadingState(wallet) + } + } + + private fun createWalletLoadingState(wallet: UserWallet): WalletCardState { + val cardTypeResolver = wallet.scanResponse.cardTypesResolver + + return WalletCardState.Loading( + id = wallet.walletId, + title = wallet.name, + additionalInfo = WalletAdditionalInfoFactory.resolve( + cardTypesResolver = cardTypeResolver, + isLocked = wallet.isLocked, + ), + imageResId = WalletImageResolver.resolve(cardTypesResolver = cardTypeResolver), + ) + } + private fun createPullToRefreshConfig(): WalletPullToRefreshConfig { return WalletPullToRefreshConfig(isRefreshing = false, onRefresh = clickIntents::onRefreshSwipe) } @@ -123,16 +120,14 @@ internal class WalletSkeletonStateConverter( // TODO: [REDACTED_JIRA] private fun getButtons(): ImmutableList { return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), + WalletManageButton.Buy(), + WalletManageButton.Send(), WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), + WalletManageButton.Exchange(), + WalletManageButton.Sell(), WalletManageButton.CopyAddress(onClick = {}), ) } - data class SkeletonModel( - val wallets: List, - val selectedWalletIndex: Int, - ) + data class SkeletonModel(val wallets: List, val selectedWalletIndex: Int) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 4d31745689..0f4d3bd755 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -10,14 +10,12 @@ import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.domain.wallets.models.UserWallet -import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification -import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletLoadedTokensListConverter.LoadedTokensListModel import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadedTxHistoryConverter import com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory.WalletLoadingTxHistoryConverter import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -26,7 +24,7 @@ import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.Flow /** - * Main factory for creating [WalletStateHolder] + * Main factory for creating [WalletState] * * @property currentStateProvider current ui state provider * @property currentCardTypeResolverProvider current card type resolver @@ -34,13 +32,13 @@ import kotlinx.coroutines.flow.Flow * @property clickIntents screen click intents */ internal class WalletStateFactory( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val currentCardTypeResolverProvider: Provider, private val isLockedWalletProvider: Provider, private val clickIntents: WalletClickIntents, ) { - private val skeletonConverter by lazy { WalletSkeletonStateConverter(clickIntents = clickIntents) } + private val skeletonConverter by lazy { WalletSkeletonStateConverter(currentStateProvider, clickIntents) } private val loadedTokensListConverter by lazy { WalletLoadedTokensListConverter( @@ -54,7 +52,6 @@ internal class WalletStateFactory( private val loadingTransactionsStateConverter by lazy { WalletLoadingTxHistoryConverter( currentStateProvider = currentStateProvider, - currentCardTypeResolverProvider = currentCardTypeResolverProvider, clickIntents = clickIntents, ) } @@ -67,70 +64,97 @@ internal class WalletStateFactory( ) } - fun getInitialState(): WalletStateHolder = WalletLoading(onBackClick = clickIntents::onBackClick) + fun getInitialState(): WalletState = WalletState.Initial(onBackClick = clickIntents::onBackClick) - fun getSkeletonState(wallets: List, index: Int): WalletStateHolder { + fun getSkeletonState(wallets: List, selectedWalletIndex: Int): WalletState { return skeletonConverter.convert( - value = WalletSkeletonStateConverter.SkeletonModel(wallets = wallets, selectedWalletIndex = index), + value = WalletSkeletonStateConverter.SkeletonModel( + wallets = wallets, + selectedWalletIndex = selectedWalletIndex, + ), ) } - fun getStateByTokensList( - tokenListEither: Either, - isRefreshing: Boolean, - ): WalletStateHolder { + fun getStateByTokensList(tokenListEither: Either, isRefreshing: Boolean): WalletState { return loadedTokensListConverter.convert( - value = LoadedTokensListModel(tokenListEither = tokenListEither, isRefreshing = isRefreshing), + value = WalletLoadedTokensListConverter.LoadedTokensListModel( + tokenListEither = tokenListEither, + isRefreshing = isRefreshing, + ), ) } - fun getStateByNotifications(notifications: ImmutableList): WalletStateHolder { - return currentStateProvider().copySealed(notifications = notifications) - } - - fun getStateAfterWalletChanging(index: Int): WalletStateHolder { - return currentStateProvider().let { stateHolder -> - stateHolder.copySealed(walletsListConfig = stateHolder.walletsListConfig.copy(selectedWalletIndex = index)) + fun getStateByNotifications(notifications: ImmutableList): WalletState { + return when (val state = currentStateProvider()) { + is WalletMultiCurrencyState.Content -> state.copy(notifications = notifications) + is WalletSingleCurrencyState.Content -> state.copy(notifications = notifications) + else -> state } } - fun getStateAfterContentRefreshing(): WalletStateHolder { - return currentStateProvider().let { state -> - state.copySealed(pullToRefreshConfig = state.pullToRefreshConfig.copy(isRefreshing = true)) - } + fun getStateAfterContentRefreshing(): WalletState { + return currentStateProvider() } - fun getStateWithOpenBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletStateHolder { - return currentStateProvider().let { state -> - state.copySealed( - bottomSheet = WalletBottomSheetConfig( + fun getStateWithOpenBottomSheet(content: WalletBottomSheetConfig.BottomSheetContentConfig): WalletState { + return when (val state = currentStateProvider() as WalletState.ContentState) { + is WalletMultiCurrencyState.Content -> state.copy( + bottomSheetConfig = WalletBottomSheetConfig( isShow = true, - onDismissRequest = { - state.copySealed(bottomSheet = state.bottomSheetConfig?.copy(isShow = false)) - }, + onDismissRequest = clickIntents::onBottomSheetDismiss, content = content, ), ) + is WalletMultiCurrencyState.Locked -> state.copy( + isBottomSheetShow = true, + onBottomSheetDismiss = clickIntents::onBottomSheetDismiss, + ) + is WalletSingleCurrencyState.Content -> state.copy( + bottomSheetConfig = WalletBottomSheetConfig( + isShow = true, + onDismissRequest = clickIntents::onBottomSheetDismiss, + content = content, + ), + ) + is WalletSingleCurrencyState.Locked -> state.copy( + isBottomSheetShow = true, + onBottomSheetDismiss = clickIntents::onBottomSheetDismiss, + ) } } - fun getLoadingTxHistoryState(itemsCountEither: Either): WalletStateHolder { + fun getStateWithClosedBottomSheet(): WalletState { + return when (val state = currentStateProvider() as WalletState.ContentState) { + is WalletMultiCurrencyState.Content -> state.copy( + bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), + ) + is WalletMultiCurrencyState.Locked -> state.copy(isBottomSheetShow = false) + is WalletSingleCurrencyState.Content -> state.copy( + bottomSheetConfig = state.bottomSheetConfig?.copy(isShow = false), + ) + is WalletSingleCurrencyState.Locked -> state.copy(isBottomSheetShow = false) + } + } + + fun getLoadingTxHistoryState(itemsCountEither: Either): WalletState { return loadingTransactionsStateConverter.convert(value = itemsCountEither) } fun getLoadedTxHistoryState( txHistoryEither: Either>>, - ): WalletStateHolder { + ): WalletState { return loadedTxHistoryConverter.convert(txHistoryEither) } - fun getLockedState(): WalletStateHolder { + fun getLockedState(): WalletState { val cardTypeResolver = currentCardTypeResolverProvider() - val state = currentStateProvider() + val state = requireNotNull(currentStateProvider() as? WalletState.ContentState) return if (cardTypeResolver.isMultiwalletAllowed()) { WalletMultiCurrencyState.Locked( onBackClick = state.onBackClick, - topBarConfig = state.topBarConfig, + topBarConfig = state.topBarConfig.copy( + onMoreClick = clickIntents::onUnlockWalletNotificationClick, + ), walletsListConfig = state.walletsListConfig, pullToRefreshConfig = state.pullToRefreshConfig, onUnlockWalletsNotificationClick = clickIntents::onUnlockWalletNotificationClick, @@ -140,7 +164,9 @@ internal class WalletStateFactory( } else { WalletSingleCurrencyState.Locked( onBackClick = state.onBackClick, - topBarConfig = state.topBarConfig, + topBarConfig = state.topBarConfig.copy( + onMoreClick = clickIntents::onUnlockWalletNotificationClick, + ), walletsListConfig = state.walletsListConfig, pullToRefreshConfig = state.pullToRefreshConfig, buttons = getButtons(), @@ -155,10 +181,11 @@ internal class WalletStateFactory( // TODO: [REDACTED_JIRA] private fun getButtons(): ImmutableList { return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), + WalletManageButton.Buy(), + WalletManageButton.Send(), WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), + WalletManageButton.Exchange(), + WalletManageButton.Sell(), WalletManageButton.CopyAddress(onClick = {}), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index 801c6556a2..d0c95681cb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -3,18 +3,14 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.flow.Flow /** @@ -27,10 +23,10 @@ import kotlinx.coroutines.flow.Flow [REDACTED_AUTHOR] */ internal class WalletLoadedTxHistoryConverter( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val currentCardTypeResolverProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter>>, WalletStateHolder> { +) : Converter>>, WalletState> { private val walletTxHistoryItemFlowConverter by lazy { WalletTxHistoryItemFlowConverter( @@ -39,12 +35,12 @@ internal class WalletLoadedTxHistoryConverter( ) } - override fun convert(value: Either>>): WalletStateHolder { + override fun convert(value: Either>>): WalletState { return value.fold(ifLeft = ::convertError, ifRight = ::convert) } - private fun convertError(error: TxHistoryListError): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( + private fun convertError(error: TxHistoryListError): WalletState { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = when (error) { is TxHistoryListError.DataError -> { WalletTxHistoryState.Error(onReloadClick = clickIntents::onReloadClick) @@ -53,40 +49,9 @@ internal class WalletLoadedTxHistoryConverter( ) } - private fun convert(items: Flow>): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( + private fun convert(items: Flow>): WalletState { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = walletTxHistoryItemFlowConverter.convert(value = items), ) } - - private fun WalletStateHolder.copySingleCurrencyContent( - txHistoryState: WalletTxHistoryState, - ): WalletSingleCurrencyState { - return WalletSingleCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - buttons = getButtons(), - marketPriceBlockState = getLoadingMarketPriceBlockState(), - txHistoryState = txHistoryState, - ) - } - - // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { - return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), - WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), - WalletManageButton.CopyAddress(onClick = {}), - ) - } - - private fun getLoadingMarketPriceBlockState(): MarketPriceBlockState { - return MarketPriceBlockState.Loading(currencyName = currentCardTypeResolverProvider().getBlockchain().currency) - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index a43a3f8355..c689acf2df 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -1,57 +1,33 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory -import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.TransactionState -import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf -import kotlinx.coroutines.flow.flowOf /** - * Converter from loading tx history to [WalletTxHistoryState] + * Converter from loading tx history state to [WalletSingleCurrencyState.Content] * * @property currentStateProvider current state provider - * @property currentCardTypeResolverProvider current card type resolver provider * @property clickIntents screen click intents * [REDACTED_AUTHOR] */ internal class WalletLoadingTxHistoryConverter( - private val currentStateProvider: Provider, - private val currentCardTypeResolverProvider: Provider, + private val currentStateProvider: Provider, private val clickIntents: WalletClickIntents, -) : Converter, WalletStateHolder> { +) : Converter, WalletSingleCurrencyState.Content> { - override fun convert(value: Either): WalletStateHolder { + override fun convert(value: Either): WalletSingleCurrencyState.Content { return value.fold(ifLeft = ::convertError, ifRight = ::convert) } - private fun convert(value: Int): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( - txHistoryState = WalletTxHistoryState.Content( - items = flowOf( - value = PagingData.from( - data = buildList(capacity = value) { - add(WalletTxHistoryState.TxHistoryItemState.Transaction(state = TransactionState.Loading)) - }, - ), - ), - ), - ) - } - - private fun convertError(error: TxHistoryStateError): WalletStateHolder { - return currentStateProvider().copySingleCurrencyContent( + private fun convertError(error: TxHistoryStateError): WalletSingleCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = when (error) { is TxHistoryStateError.EmptyTxHistories -> { WalletTxHistoryState.Empty(onBuyClick = clickIntents::onBuyClick) @@ -66,34 +42,9 @@ internal class WalletLoadingTxHistoryConverter( ) } - private fun WalletStateHolder.copySingleCurrencyContent( - txHistoryState: WalletTxHistoryState, - ): WalletSingleCurrencyState { - return WalletSingleCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - buttons = getButtons(), - marketPriceBlockState = getLoadingMarketPriceBlockState(), - txHistoryState = txHistoryState, + private fun convert(value: Int): WalletSingleCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( + txHistoryState = WalletTxHistoryState.ContentWithLoadingItems(itemsCount = value), ) } - - // TODO: [REDACTED_JIRA] - private fun getButtons(): ImmutableList { - return persistentListOf( - WalletManageButton.Buy(onClick = {}), - WalletManageButton.Send(onClick = {}), - WalletManageButton.Receive(onClick = {}), - WalletManageButton.Exchange(onClick = {}), - WalletManageButton.CopyAddress(onClick = {}), - ) - } - - private fun getLoadingMarketPriceBlockState(): MarketPriceBlockState { - return MarketPriceBlockState.Loading(currencyName = currentCardTypeResolverProvider().getBlockchain().currency) - } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index cc15ab700c..0c8e6d0aa7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -20,7 +20,7 @@ import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* @@ -36,11 +36,19 @@ import com.tangem.feature.wallet.presentation.wallet.ui.utils.changeWalletAnimat * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalMaterialApi::class) @Composable -internal fun WalletScreen(state: WalletStateHolder) { +internal fun WalletScreen(state: WalletState) { BackHandler(onBack = state.onBackClick) + when (state) { + is WalletState.ContentState -> WalletContent(state = state) + is WalletState.Initial -> Unit + } +} + +@OptIn(ExperimentalMaterialApi::class) +@Composable +private fun WalletContent(state: WalletState.ContentState) { val walletsListState = rememberLazyListState() Scaffold( @@ -121,9 +129,7 @@ internal fun WalletScreen(state: WalletStateHolder) { // region Preview @Preview @Composable -private fun WalletScreenPreview_Light( - @PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder, -) { +private fun WalletScreenPreview_Light(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) { TangemTheme { WalletScreen(state) } @@ -131,15 +137,13 @@ private fun WalletScreenPreview_Light( @Preview @Composable -private fun WalletScreenPreview_Dark( - @PreviewParameter(WalletScreenParameterProvider::class) state: WalletStateHolder, -) { +private fun WalletScreenPreview_Dark(@PreviewParameter(WalletScreenParameterProvider::class) state: WalletState) { TangemTheme(isDark = true) { WalletScreen(state) } } -private class WalletScreenParameterProvider : CollectionPreviewParameterProvider( +private class WalletScreenParameterProvider : CollectionPreviewParameterProvider( collection = listOf( WalletPreviewData.multicurrencyWalletScreenState, WalletPreviewData.singleWalletScreenState, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index f95107b7e9..cbd8a73c90 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -3,10 +3,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems -import com.tangem.feature.wallet.presentation.wallet.state.WalletLoading import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems @@ -21,14 +20,12 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrenc [REDACTED_AUTHOR] */ internal fun LazyListScope.contentItems( - state: WalletStateHolder, + state: WalletState.ContentState, txHistoryItems: LazyPagingItems?, modifier: Modifier = Modifier, ) { when (state) { is WalletMultiCurrencyState -> tokensListItems(state.tokensListState, modifier) is WalletSingleCurrencyState -> txHistoryItems(state.txHistoryState, txHistoryItems, modifier) - is WalletLoading, - -> Unit } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index 4b2f3cffb7..baa0144e1b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items import androidx.compose.ui.Modifier @@ -15,6 +16,11 @@ import kotlinx.collections.immutable.ImmutableList * [REDACTED_AUTHOR] */ +@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.notifications(configs: ImmutableList, modifier: Modifier = Modifier) { - items(items = configs, itemContent = { Notification(state = it.state, modifier = modifier) }) + items( + items = configs, + key = { it.state.title.hashCode() }, + itemContent = { Notification(state = it.state, modifier = modifier.animateItemPlacement()) }, + ) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt new file mode 100644 index 0000000000..4d8b4cc1f4 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt @@ -0,0 +1,21 @@ +package com.tangem.feature.wallet.presentation.wallet.utils + +import com.tangem.common.Provider +import com.tangem.domain.tokens.error.TokenListError +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.persistentListOf + +internal class TokenListErrorConverter( + private val currentStateProvider: Provider, +) : Converter { + + // TODO: [REDACTED_JIRA] + override fun convert(value: TokenListError): WalletMultiCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy( + tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt deleted file mode 100644 index 2697e71bd3..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorToWalletStateConverter.kt +++ /dev/null @@ -1,28 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.utils - -import com.tangem.common.Provider -import com.tangem.domain.tokens.error.TokenListError -import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.utils.converter.Converter -import kotlinx.collections.immutable.persistentListOf - -internal class TokenListErrorToWalletStateConverter( - private val currentStateProvider: Provider, -) : Converter { - - // TODO: [REDACTED_JIRA] - override fun convert(value: TokenListError): WalletStateHolder { - val state = currentStateProvider() - return WalletMultiCurrencyState.Content( - onBackClick = state.onBackClick, - topBarConfig = state.topBarConfig, - walletsListConfig = state.walletsListConfig, - pullToRefreshConfig = state.pullToRefreshConfig, - notifications = state.notifications, - bottomSheetConfig = state.bottomSheetConfig, - tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), - ) - } -} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index f94b216186..885c12dd52 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -5,7 +5,7 @@ import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig import com.tangem.feature.wallet.presentation.wallet.utils.TokenListToWalletStateConverter.TokensListModel @@ -15,14 +15,14 @@ import kotlinx.collections.immutable.toPersistentList @Suppress("LongParameterList") internal class TokenListToWalletStateConverter( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val cardTypeResolverProvider: Provider, private val isLockedWalletProvider: Provider, private val isWalletContentHidden: Boolean, private val fiatCurrencyCode: String, private val fiatCurrencySymbol: String, clickIntents: WalletClickIntents, -) : Converter { +) : Converter { private val tokenListToContentConverter = TokenListToContentItemsConverter( isWalletContentHidden = isWalletContentHidden, @@ -31,33 +31,20 @@ internal class TokenListToWalletStateConverter( clickIntents = clickIntents, ) - override fun convert(value: TokensListModel): WalletStateHolder { - val state = currentStateProvider() - return state - .updateWithTokenList(tokenList = value.tokenList) - .copySealed( - walletsListConfig = state.updateSelectedWallet(value.tokenList.totalFiatBalance), - pullToRefreshConfig = if (value.isRefreshing) { - state.pullToRefreshConfig.copy(isRefreshing = state.getRefreshingStatus()) - } else { - state.pullToRefreshConfig - }, - ) - } - - private fun WalletStateHolder.updateWithTokenList(tokenList: TokenList): WalletMultiCurrencyState.Content { - return WalletMultiCurrencyState.Content( - onBackClick = onBackClick, - topBarConfig = topBarConfig, - walletsListConfig = walletsListConfig, - pullToRefreshConfig = pullToRefreshConfig, - notifications = notifications, - bottomSheetConfig = bottomSheetConfig, - tokensListState = tokenListToContentConverter.convert(value = tokenList), + override fun convert(value: TokensListModel): WalletMultiCurrencyState.Content { + val state = requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content) + return state.copy( + walletsListConfig = state.updateSelectedWallet(fiatBalance = value.tokenList.totalFiatBalance), + pullToRefreshConfig = if (value.isRefreshing) { + state.pullToRefreshConfig.copy(isRefreshing = state.getRefreshingStatus()) + } else { + state.pullToRefreshConfig + }, + tokensListState = tokenListToContentConverter.convert(value = value.tokenList), ) } - private fun WalletStateHolder.updateSelectedWallet(fiatBalance: TokenList.FiatBalance): WalletsListConfig { + private fun WalletMultiCurrencyState.updateSelectedWallet(fiatBalance: TokenList.FiatBalance): WalletsListConfig { val selectedWalletIndex = walletsListConfig.selectedWalletIndex val selectedWalletCard = walletsListConfig.wallets[selectedWalletIndex] val converter = FiatBalanceToWalletCardConverter( @@ -70,13 +57,12 @@ internal class TokenListToWalletStateConverter( ) return walletsListConfig.copy( - wallets = walletsListConfig.wallets - .toPersistentList() + wallets = walletsListConfig.wallets.toPersistentList() .set(index = selectedWalletIndex, element = converter.convert(fiatBalance)), ) } - private fun WalletStateHolder.getRefreshingStatus(): Boolean { + private fun WalletState.getRefreshingStatus(): Boolean { return if (this is WalletMultiCurrencyState.Content) { tokensListState.items.any { tokensListItemState -> tokensListItemState is WalletTokensListState.TokensListItemState.Token && diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt index 89d57acf5b..e38b9bd0f3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt @@ -35,4 +35,6 @@ internal interface WalletClickIntents { fun onUnlockWalletClick() fun onUnlockWalletNotificationClick() + + fun onBottomSheetDismiss() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index a7fa8f46f0..18bfbbbe34 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -7,7 +7,7 @@ import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletNotification import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import kotlinx.collections.immutable.ImmutableList @@ -26,7 +26,7 @@ import kotlinx.coroutines.flow.flow [REDACTED_AUTHOR] */ internal class WalletNotificationsListFactory( - private val currentStateProvider: Provider, + private val currentStateProvider: Provider, private val wasCardScannedCallback: suspend (String) -> Boolean, private val isUserAlreadyRateAppCallback: suspend () -> Boolean, private val isDemoCardCallback: (String) -> Boolean, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 6083b4973d..bc50b0d051 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -21,17 +21,20 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.usecase.GetExploreUrlUseCase -import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.domain.wallets.usecase.SaveWalletUseCase -import com.tangem.domain.wallets.usecase.UnlockWalletsUseCase +import com.tangem.domain.wallets.usecase.* import com.tangem.feature.wallet.presentation.router.InnerWalletRouter -import com.tangem.feature.wallet.presentation.wallet.state.* +import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState +import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.properties.Delegates @@ -41,11 +44,13 @@ import kotlin.properties.Delegates * [REDACTED_AUTHOR] */ -@Suppress("LongParameterList") +@Suppress("LongParameterList", "TooManyFunctions") @HiltViewModel internal class WalletViewModel @Inject constructor( private val getWalletsUseCase: GetWalletsUseCase, private val saveWalletUseCase: SaveWalletUseCase, + private val getSelectedWalletUseCase: GetSelectedWalletUseCase, + private val selectWalletUseCase: SelectWalletUseCase, private val getBiometricsStatusUseCase: GetBiometricsStatusUseCase, private val setAccessCodeRequestPolicyUseCase: SetAccessCodeRequestPolicyUseCase, private val getAccessCodeSavingStatusUseCase: GetAccessCodeSavingStatusUseCase, @@ -75,14 +80,18 @@ internal class WalletViewModel @Inject constructor( private val stateFactory = WalletStateFactory( currentStateProvider = Provider { uiState }, currentCardTypeResolverProvider = Provider { - getCardTypeResolver(index = uiState.walletsListConfig.selectedWalletIndex) + getCardTypeResolver( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + ) + }, + isLockedWalletProvider = Provider { + wallets[requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex].isLocked }, - isLockedWalletProvider = Provider { wallets[uiState.walletsListConfig.selectedWalletIndex].isLocked }, clickIntents = this, ) /** Screen state */ - var uiState: WalletStateHolder by mutableStateOf(stateFactory.getInitialState()) + var uiState: WalletState by mutableStateOf(stateFactory.getInitialState()) private set private var wallets: List by Delegates.notNull() @@ -100,17 +109,26 @@ internal class WalletViewModel @Inject constructor( } private fun updateWallets(sourceList: List) { - if (sourceList.isEmpty() || sourceList.all(UserWallet::isLocked)) return + if (sourceList.isEmpty()) return wallets = sourceList - val unlockedWalletIndex = sourceList.indexOfFirst { !it.isLocked } - uiState = stateFactory.getSkeletonState( - wallets = wallets, - index = if (unlockedWalletIndex == -1) 0 else unlockedWalletIndex, - ) + val currentState = uiState + val selectedWalletIndex = if (currentState is WalletLockedState) { + when (currentState) { + is WalletMultiCurrencyState.Locked -> currentState.walletsListConfig.selectedWalletIndex + is WalletSingleCurrencyState.Locked -> currentState.walletsListConfig.selectedWalletIndex + } + } else { + val selectedWallet = getSelectedWalletUseCase().fold( + ifLeft = { error("Selected wallet is null") }, + ifRight = { it }, + ) + sourceList.indexOfFirst { it.walletId == selectedWallet.walletId } + } - updateContentItems(index = unlockedWalletIndex) + uiState = stateFactory.getSkeletonState(wallets = sourceList, selectedWalletIndex = selectedWalletIndex) + updateContentItems(index = selectedWalletIndex) } private fun updateContentItems(index: Int, isRefreshing: Boolean = false) { @@ -122,8 +140,12 @@ internal class WalletViewModel @Inject constructor( } } - private fun updateByTokensList(index: Int, isRefreshing: Boolean) { - getTokenListUseCase(userWalletId = uiState.walletsListConfig.wallets[index].id) + private fun updateByTokensList(index: Int, isRefreshing: Boolean = false) { + val state = requireNotNull(uiState as? WalletMultiCurrencyState) { + "Impossible to update tokens list if state isn't WalletMultiCurrencyState" + } + + getTokenListUseCase(userWalletId = state.walletsListConfig.wallets[index].id) .distinctUntilChanged() .onEach { tokenListEither -> uiState = stateFactory.getStateByTokensList( @@ -144,7 +166,7 @@ internal class WalletViewModel @Inject constructor( private fun updateByTxHistory(index: Int) { viewModelScope.launch(dispatchers.io) { val wallet = getWallet(index) - val blockchain = wallet.scanResponse.cardTypesResolver.getBlockchain() + val blockchain = getCardTypeResolver(index).getBlockchain() val derivationPath = blockchain.derivationPath(style = wallet.scanResponse.card.derivationStyle)?.rawPath val txHistoryItemsCountEither = txHistoryItemsCountUseCase( @@ -160,8 +182,9 @@ internal class WalletViewModel @Inject constructor( derivationPath = derivationPath, ) } - updateNotifications(index) } + + updateNotifications(index) } private fun updateTxHistory(networkId: Network.ID, derivationPath: String?) { @@ -179,7 +202,20 @@ internal class WalletViewModel @Inject constructor( .onEach { uiState = stateFactory.getStateByNotifications(notifications = it) } .flowOn(dispatchers.io) .launchIn(viewModelScope) - .saveIn(jobHolder = notificationsJobHolder) + .saveIn(notificationsJobHolder) + } + + override fun onStop(owner: LifecycleOwner) { + viewModelScope.launch(dispatchers.io) { + saveSelectedWallet() + } + } + + private suspend fun saveSelectedWallet() { + val state = uiState + if (state is WalletState.ContentState) { + selectWalletUseCase(getWallet(index = state.walletsListConfig.selectedWalletIndex).walletId) + } } private fun getWallet(index: Int): UserWallet { @@ -258,21 +294,30 @@ internal class WalletViewModel @Inject constructor( } override fun onWalletChange(index: Int) { - if (uiState.walletsListConfig.selectedWalletIndex == index) return + val state = requireNotNull(uiState as? WalletState.ContentState) { + "Impossible to change wallet if state isn't WalletState.ContentState" + } - uiState = stateFactory.getStateAfterWalletChanging(index = index) + if (state.walletsListConfig.selectedWalletIndex == index) return + + uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index) updateContentItems(index = index) } override fun onRefreshSwipe() { uiState = stateFactory.getStateAfterContentRefreshing() - updateContentItems(index = uiState.walletsListConfig.selectedWalletIndex, isRefreshing = true) + + updateContentItems( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + isRefreshing = true, + ) } override fun onOrganizeTokensClick() { - val index = uiState.walletsListConfig.selectedWalletIndex - val walletId = uiState.walletsListConfig.wallets[index].id + val state = requireNotNull(uiState as? WalletState.ContentState) + val index = state.walletsListConfig.selectedWalletIndex + val walletId = state.walletsListConfig.wallets[index].id router.openOrganizeTokensScreen(walletId) } @@ -283,12 +328,16 @@ internal class WalletViewModel @Inject constructor( override fun onReloadClick() { uiState = stateFactory.getStateAfterContentRefreshing() - updateByTxHistory(index = uiState.walletsListConfig.selectedWalletIndex) + updateByTxHistory( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + ) } override fun onExploreClick() { viewModelScope.launch(dispatchers.io) { - val wallet = getWallet(uiState.walletsListConfig.selectedWalletIndex) + val wallet = getWallet( + index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, + ) router.openTxHistoryWebsite( url = getExploreUrlUseCase( userWalletId = wallet.walletId, @@ -307,8 +356,19 @@ internal class WalletViewModel @Inject constructor( } override fun onUnlockWalletNotificationClick() { + val state = requireNotNull(uiState as? WalletLockedState) { + "Impossible to unlock wallet if state isn't WalletLockedState" + } + uiState = stateFactory.getStateWithOpenBottomSheet( - content = requireNotNull(uiState.bottomSheetConfig?.content), + content = when (state) { + is WalletMultiCurrencyState.Locked -> state.bottomSheetConfig.content + is WalletSingleCurrencyState.Locked -> state.bottomSheetConfig.content + }, ) } + + override fun onBottomSheetDismiss() { + uiState = stateFactory.getStateWithClosedBottomSheet() + } } \ No newline at end of file From 3e1f52942c6666bd7e7e02577d83c344b12f80d2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Aug 2023 12:21:28 +0300 Subject: [PATCH 16/52] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- core/datasource/build.gradle.kts | 1 + .../api/tangemTech/TangemTechApi.kt | 7 ++ .../api/tangemTech/models/QuotesResponse.kt | 19 +++++ .../tangem/datasource/di/QuotesStoreModule.kt | 31 ++++++++ .../datastore/SharedPreferencesDataStore.kt | 76 +++++++++++++++++++ .../local/quote/DefaultQuotesStore.kt | 26 +++++++ .../datasource/local/quote/QuotesStore.kt | 12 +++ .../local/quote/model/StoredQuote.kt | 9 +++ .../repository/DefaultCurrenciesRepository.kt | 2 +- .../repository/DefaultNetworksRepository.kt | 2 +- .../tokens/repository/MockQuotesRepository.kt | 4 +- .../tokens/utils/CardCurrenciesFactory.kt | 2 +- .../data/tokens/utils/NetworkStatusFactory.kt | 2 +- .../tokens/utils/ResponseCurrenciesFactory.kt | 2 +- .../data/tokens/utils/TokensOperations.kt | 2 +- .../tokens/utils/UserTokensResponseFactory.kt | 2 +- domain/legacy/build.gradle.kts | 1 - .../DefaultWalletManagersFacade.kt | 2 +- .../walletmanager/WalletManagersFacade.kt | 2 +- .../walletmanager/utils/SdkTokenConverter.kt | 2 +- .../domain/tokens/models}/CryptoCurrency.kt | 4 +- .../com/tangem/domain/tokens/models}/Quote.kt | 2 +- .../tokens/ApplyTokenListSortingUseCase.kt | 2 +- .../domain/tokens/GetCurrencyUseCase.kt | 2 +- .../tokens/model/CryptoCurrencyStatus.kt | 1 + .../domain/tokens/model/NetworkStatus.kt | 1 + .../CurrenciesStatusesOperations.kt | 2 + .../operations/CurrencyStatusOperations.kt | 4 +- .../tokens/repository/CurrenciesRepository.kt | 2 +- .../tokens/repository/QuotesRepository.kt | 4 +- .../ApplyTokenListSortingUseCaseTest.kt | 2 +- .../tokens/GetPrimaryCurrencyUseCaseTest.kt | 4 +- .../domain/tokens/GetTokenListUseCaseTest.kt | 4 +- .../tangem/domain/tokens/mock/MockQuotes.kt | 2 +- .../tangem/domain/tokens/mock/MockTokens.kt | 2 +- .../repository/MockCurrenciesRepository.kt | 2 +- .../tokens/repository/MockQuotesRepository.kt | 4 +- .../utils/common/IdsOperations.kt | 2 +- .../CryptoCurrencyToDraggableItemConverter.kt | 2 +- ...ryptoCurrencyStatusToTokenItemConverter.kt | 2 +- 41 files changed, 220 insertions(+), 38 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/QuotesResponse.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/QuotesStoreModule.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/quote/model/StoredQuote.kt rename domain/tokens/{src/main/kotlin/com/tangem/domain/tokens/model => models/src/main/java/com/tangem/domain/tokens/models}/CryptoCurrency.kt (97%) rename domain/tokens/{src/main/kotlin/com/tangem/domain/tokens/model => models/src/main/java/com/tangem/domain/tokens/models}/Quote.kt (92%) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index 8c1c53b739..fad890b2a0 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit 8c1c53b73950698d4acfa4d925b8c14bf6f0da63 +Subproject commit fad890b2a0b552be60d124949ca0a3a4d672dac1 diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index 7f08451e5b..bbd2e57c0e 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { implementation(projects.core.utils) implementation(projects.libs.auth) implementation(projects.domain.core) + implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) /** Tangem libraries */ diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt index e75594060f..0a609246ac 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/TangemTechApi.kt @@ -60,4 +60,11 @@ interface TangemTechApi { @Query(value = "locale") locale: String, @Query(value = "shops") shops: String, ): SalesResponse + + @GET("quotes") + suspend fun getQuotes( + @Query("currencyId") currencyId: String, + @Query("coinIds") coinIds: String, + @Query("fields") fields: String = "price,priceChange24h,lastUpdatedAt", + ): QuotesResponse } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/QuotesResponse.kt b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/QuotesResponse.kt new file mode 100644 index 0000000000..e3cd9723b9 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/api/tangemTech/models/QuotesResponse.kt @@ -0,0 +1,19 @@ +package com.tangem.datasource.api.tangemTech.models + +import com.squareup.moshi.Json +import java.math.BigDecimal + +data class QuotesResponse( + @Json(name = "quotes") + val quotes: Map, +) { + + data class Quote( + @Json(name = "price") + val price: BigDecimal, + @Json(name = "priceChange24h") + val priceChange: BigDecimal, + @Json(name = "lastUpdatedAt") + val lastUpdated: String, + ) +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/QuotesStoreModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/QuotesStoreModule.kt new file mode 100644 index 0000000000..a62a3e4f31 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/QuotesStoreModule.kt @@ -0,0 +1,31 @@ +package com.tangem.datasource.di + +import android.content.Context +import com.squareup.moshi.Moshi +import com.tangem.datasource.local.datastore.SharedPreferencesDataStore +import com.tangem.datasource.local.quote.DefaultQuotesStore +import com.tangem.datasource.local.quote.QuotesStore +import com.tangem.datasource.local.quote.model.StoredQuote +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object QuotesStoreModule { + + @Provides + @Singleton + fun provideQuotesStore(@ApplicationContext context: Context, @NetworkMoshi moshi: Moshi): QuotesStore { + return DefaultQuotesStore( + dataStore = SharedPreferencesDataStore( + preferencesName = "quotes", + context = context, + adapter = moshi.adapter(StoredQuote::class.java), + ), + ) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt new file mode 100644 index 0000000000..e8952a5a82 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt @@ -0,0 +1,76 @@ +package com.tangem.datasource.local.datastore + +import android.content.Context +import android.content.Context.MODE_PRIVATE +import androidx.core.content.edit +import com.squareup.moshi.JsonAdapter +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import com.tangem.datasource.local.datastore.model.WriteTrigger +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.* +import timber.log.Timber + +internal class SharedPreferencesDataStore( + preferencesName: String, + private val context: Context, + private val adapter: JsonAdapter, +) : StringKeyDataStore { + + private val sharedPreferences by lazy { + context.getSharedPreferences(preferencesName, MODE_PRIVATE) + } + + private val writeTrigger = MutableSharedFlow( + replay = 1, + onBufferOverflow = BufferOverflow.DROP_OLDEST, + ) + + override fun get(key: String): Flow { + return writeTrigger + .onEmpty { emit(WriteTrigger) } + .map { getInternal(key) } + .filterNotNull() + } + + override suspend fun getSyncOrNull(key: String): Value? { + return getInternal(key) + } + + override suspend fun store(key: String, item: Value) { + try { + val json = adapter.toJson(item) + + sharedPreferences.edit { putString(key, json) } + writeTrigger.emit(WriteTrigger) + } catch (e: Throwable) { + Timber.e(e, "Unable to edit preferences: $key") + } + } + + override suspend fun store(items: Map) { + items.forEach { (key, item) -> + store(key, item) + } + } + + override suspend fun remove(key: String) { + sharedPreferences.edit { + remove(key) + } + } + + override suspend fun clear() { + sharedPreferences.edit { clear() } + } + + private fun getInternal(key: String): Value? { + return try { + val json = sharedPreferences.getString(key, null) ?: return null + + adapter.fromJson(json) + } catch (e: Throwable) { + Timber.e(e, "Unable to get value from preferences: $key") + null + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt new file mode 100644 index 0000000000..f359605e09 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt @@ -0,0 +1,26 @@ +package com.tangem.datasource.local.quote + +import com.tangem.datasource.api.tangemTech.models.QuotesResponse +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import com.tangem.datasource.local.quote.model.StoredQuote +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +internal class DefaultQuotesStore( + private val dataStore: StringKeyDataStore, +) : QuotesStore { + + override fun get(rawCurrenciesIds: Set): Flow> { + val flows = rawCurrenciesIds.map { rawCurrencyId -> + dataStore.get(rawCurrencyId) + } + + return combine(flows) { quotes -> quotes.toSet() } + } + + override suspend fun store(response: QuotesResponse) { + response.quotes.forEach { (rawCurrencyId, quote) -> + dataStore.store(rawCurrencyId, StoredQuote(rawCurrencyId, quote)) + } + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt new file mode 100644 index 0000000000..77a5a65c73 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt @@ -0,0 +1,12 @@ +package com.tangem.datasource.local.quote + +import com.tangem.datasource.api.tangemTech.models.QuotesResponse +import com.tangem.datasource.local.quote.model.StoredQuote +import kotlinx.coroutines.flow.Flow + +interface QuotesStore { + + fun get(rawCurrenciesIds: Set): Flow> + + suspend fun store(response: QuotesResponse) +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/model/StoredQuote.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/model/StoredQuote.kt new file mode 100644 index 0000000000..92397d1a81 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/model/StoredQuote.kt @@ -0,0 +1,9 @@ +package com.tangem.datasource.local.quote.model + +import com.squareup.moshi.Json +import com.tangem.datasource.api.tangemTech.models.QuotesResponse + +data class StoredQuote( + @Json(name = "rawCurrencyId") val rawCurrencyId: String, + @Json(name = "quote") val quote: QuotesResponse.Quote, +) \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt index 2c1c49b48b..2b1ade5f43 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultCurrenciesRepository.kt @@ -10,7 +10,7 @@ import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.core.error.DataError import com.tangem.domain.demo.DemoConfig -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index dc5a8c2223..d70869c7b8 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -7,8 +7,8 @@ import com.tangem.data.tokens.utils.ResponseCurrenciesFactory import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.demo.DemoConfig -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.walletmanager.WalletManagersFacade diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt index e37146cbba..91ca009e7c 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt @@ -1,7 +1,7 @@ package com.tangem.data.tokens.repository -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote import com.tangem.domain.tokens.repository.QuotesRepository import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt index 52724b7ac1..8d2ac0b3be 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt @@ -6,7 +6,7 @@ import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.DemoConfig import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index 6dc955d4e5..d38263cbab 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -1,7 +1,7 @@ package com.tangem.data.tokens.utils -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.walletmanager.model.CryptoCurrencyAmount import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt index ab27676061..7955f660dd 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -6,7 +6,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.demo.DemoConfig import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import timber.log.Timber import com.tangem.blockchain.common.Token as SdkToken diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index e7704bdc9e..091d698368 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -6,7 +6,7 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.blockchain.common.Token as SdkToken diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index aac91b05ba..21a66808da 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -2,7 +2,7 @@ package com.tangem.data.tokens.utils import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency internal class UserTokensResponseFactory { diff --git a/domain/legacy/build.gradle.kts b/domain/legacy/build.gradle.kts index eb321b25e0..9ba0f990f3 100644 --- a/domain/legacy/build.gradle.kts +++ b/domain/legacy/build.gradle.kts @@ -11,7 +11,6 @@ dependencies { implementation(project(":libs:auth")) implementation(projects.domain.demo) implementation(projects.domain.models) - implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets.models) diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index a798df5e73..34ef60f9ed 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -11,7 +11,7 @@ import com.tangem.datasource.local.walletmanager.WalletManagersStore import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.demo.DemoConfig -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt index 9a9a85aa35..c923ac2c76 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/WalletManagersFacade.kt @@ -1,6 +1,6 @@ package com.tangem.domain.walletmanager -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.txhistory.models.PaginationWrapper import com.tangem.domain.txhistory.models.TxHistoryItem diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt index 8fe4bdc174..fd556d4026 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt @@ -1,6 +1,6 @@ package com.tangem.domain.walletmanager.utils -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.utils.converter.Converter import com.tangem.blockchain.common.Token as SdkToken diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt similarity index 97% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt rename to domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt index 5143f90497..e523648894 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrency.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt @@ -1,6 +1,4 @@ -package com.tangem.domain.tokens.model - -import com.tangem.domain.tokens.models.Network +package com.tangem.domain.tokens.models /** * Represents a generic cryptocurrency. diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt similarity index 92% rename from domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt rename to domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt index 7247a75ed2..808d81e25a 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/Quote.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt @@ -1,4 +1,4 @@ -package com.tangem.domain.tokens.model +package com.tangem.domain.tokens.models import java.math.BigDecimal diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 187b2e5be0..2094f6fdfb 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -8,7 +8,7 @@ import arrow.core.raise.ensureNotNull import arrow.core.toNonEmptyListOrNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt index 6c9aa54e98..650abfd651 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetCurrencyUseCase.kt @@ -3,8 +3,8 @@ package com.tangem.domain.tokens import arrow.core.Either import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.error.mapper.mapToCurrencyError -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt index a735f3ed06..443ccfeca6 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.model +import com.tangem.domain.tokens.models.CryptoCurrency import java.math.BigDecimal /** diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt index 17bbaa392f..cfb815d9af 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/NetworkStatus.kt @@ -1,5 +1,6 @@ package com.tangem.domain.tokens.model +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import java.math.BigDecimal diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 1fbd547bbc..5d17887c55 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -4,7 +4,9 @@ import arrow.core.* import arrow.core.raise.* import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.* +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.models.Quote import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.tokens.repository.NetworksRepository import com.tangem.domain.tokens.repository.QuotesRepository diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index d654ee6cd5..8653aae3e5 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -1,9 +1,9 @@ package com.tangem.domain.tokens.operations -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote import java.math.BigDecimal internal class CurrencyStatusOperations( diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt index 85db960be0..91a04b5af8 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/CurrenciesRepository.kt @@ -1,6 +1,6 @@ package com.tangem.domain.tokens.repository -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt index c56cb3d7d9..39d6d8489d 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/repository/QuotesRepository.kt @@ -1,7 +1,7 @@ package com.tangem.domain.tokens.repository -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote import kotlinx.coroutines.flow.Flow /** diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index b56a18415e..77d37c2c2b 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -6,7 +6,7 @@ import arrow.core.right import com.tangem.domain.core.error.DataError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.mock.MockTokens -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt index c524f8d3ba..707e9e90ef 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt @@ -9,10 +9,10 @@ import com.tangem.domain.tokens.mock.MockNetworks import com.tangem.domain.tokens.mock.MockQuotes import com.tangem.domain.tokens.mock.MockTokens import com.tangem.domain.tokens.mock.MockTokensStates -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index 4336ee54a3..00d7db54b3 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -9,10 +9,10 @@ import com.tangem.domain.tokens.mock.MockNetworks import com.tangem.domain.tokens.mock.MockQuotes import com.tangem.domain.tokens.mock.MockTokenLists import com.tangem.domain.tokens.mock.MockTokens -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.NetworkStatus -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network +import com.tangem.domain.tokens.models.Quote import com.tangem.domain.tokens.repository.MockCurrenciesRepository import com.tangem.domain.tokens.repository.MockNetworksRepository import com.tangem.domain.tokens.repository.MockQuotesRepository diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt index d54da733fd..0607456029 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt @@ -1,7 +1,7 @@ package com.tangem.domain.tokens.mock import arrow.core.nonEmptySetOf -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.Quote import java.math.BigDecimal @Suppress("MemberVisibilityCanBePrivate") diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt index 7ff7ba350a..c5e38d7101 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt @@ -1,6 +1,6 @@ package com.tangem.domain.tokens.mock -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency internal object MockTokens { diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt index 9570b02157..7ac79d2d4c 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockCurrenciesRepository.kt @@ -3,7 +3,7 @@ package com.tangem.domain.tokens.repository import arrow.core.Either import arrow.core.getOrElse import com.tangem.domain.core.error.DataError -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt index a94b8a72df..baa2ef1599 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/repository/MockQuotesRepository.kt @@ -3,8 +3,8 @@ package com.tangem.domain.tokens.repository import arrow.core.Either import arrow.core.getOrElse import com.tangem.domain.core.error.DataError -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.Quote +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt index d7e86bcff6..e28e32c3a8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/common/IdsOperations.kt @@ -1,6 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.common -import com.tangem.domain.tokens.model.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network internal fun getTokenItemId(currencyId: CryptoCurrency.ID): String = currencyId.value diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt index 8c2cac767f..d92843a1f6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -2,8 +2,8 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.it import androidx.annotation.DrawableRes import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index b67250b33b..a3b84ee0b5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -3,8 +3,8 @@ package com.tangem.feature.wallet.presentation.wallet.utils import androidx.annotation.DrawableRes import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.domain.tokens.model.CryptoCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.utils.converter.Converter From 282cc27cb444c3b06876b54616e7fd3915659ea1 Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 17:40:52 +0300 Subject: [PATCH 17/52] Updated on 2026-08-14 --- app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt | 3 ++- .../com/tangem/tap/domain/tasks/product/ScanProductTask.kt | 6 ++---- .../com/tangem/tap/features/onboarding/OnboardingHelper.kt | 6 +----- .../java/com/tangem/data/card/sdk/DefaultCardSdkProvider.kt | 2 +- .../com/tangem/domain/common/TangemCardTypesResolver.kt | 3 +-- .../main/java/com/tangem/domain/common/TapWorkarounds.kt | 3 ++- gradle/dependencies.toml | 2 +- 7 files changed, 10 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 5bac8dc2b2..35325434de 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -246,12 +246,13 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit } companion object { + @Deprecated("Use [DefaultCardSdkProvider] instead") val config = Config( linkedTerminal = true, allowUntrustedCards = true, filter = CardFilter( allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(), - maxFirmwareVersion = FirmwareVersion(major = 6, minor = 21), + maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33), ), ) } 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 5bef94e017..6774c4e61d 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 @@ -51,7 +51,7 @@ class ScanProductTask( } val cardDto = CardDTO(card) - val error = getErrorIfExcludedCard(cardDto, card) + val error = getErrorIfExcludedCard(cardDto) if (error != null) { callback(CompletionResult.Failure(error)) return @@ -81,11 +81,9 @@ class ScanProductTask( } } - private fun getErrorIfExcludedCard(cardDto: CardDTO, card: Card): TangemError? { + private fun getErrorIfExcludedCard(cardDto: CardDTO): TangemError? { if (cardDto.isExcluded) return TapSdkError.CardForDifferentApp if (cardDto.isNotSupportedInThatRelease) return TapSdkError.CardNotSupportedByRelease - // todo check isImported to prevent using old app with imported wallet, remove before wallet 2.0 enabled ([REDACTED_TASK_KEY]) - if (card.wallets.any { it.isImported }) return TapSdkError.CardNotSupportedByRelease return null } } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt index 78f8cc873e..baf5ed2fcf 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/OnboardingHelper.kt @@ -6,7 +6,6 @@ import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.twinsIsTwinned import com.tangem.domain.models.scan.ProductType @@ -38,10 +37,7 @@ object OnboardingHelper { } } - // TODO for Shiba disabled check wallet 2, and only check canSkipBackup, enable when release wallet 2 - // ([REDACTED_TASK_KEY]) - // response.cardTypesResolver.isWallet2() -> { - !response.card.canSkipBackup -> { + response.cardTypesResolver.isWallet2() -> { val emptyWallets = response.card.wallets.isEmpty() val activationInProgress = cardInfoStorage.isActivationInProgress(cardId) val backupNotActive = response.card.backupStatus?.isActive != true diff --git a/data/card/src/main/java/com/tangem/data/card/sdk/DefaultCardSdkProvider.kt b/data/card/src/main/java/com/tangem/data/card/sdk/DefaultCardSdkProvider.kt index dddff7e3da..522b4d2b9b 100644 --- a/data/card/src/main/java/com/tangem/data/card/sdk/DefaultCardSdkProvider.kt +++ b/data/card/src/main/java/com/tangem/data/card/sdk/DefaultCardSdkProvider.kt @@ -38,7 +38,7 @@ internal class DefaultCardSdkProvider @Inject constructor() : CardSdkProvider, C allowUntrustedCards = true, filter = CardFilter( allowedCardTypes = FirmwareVersion.FirmwareType.values().toList(), - maxFirmwareVersion = FirmwareVersion(major = 4, minor = 52), + maxFirmwareVersion = FirmwareVersion(major = 6, minor = 33), ), ) } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index 0eb5a72085..757ce2bb3e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -30,8 +30,7 @@ internal class TangemCardTypesResolver( } override fun isWallet2(): Boolean { - // todo for now disabled to prevent Shiba cards using as wallet 2, enable when release wallet 2.0 ([REDACTED_TASK_KEY]) - return false // card.firmwareVersion >= FirmwareVersion.KeysImportAvailable && card.settings.isKeysImportAllowed + return card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && card.settings.isKeysImportAllowed } override fun isTangemTwins(): Boolean = productType == ProductType.Twins diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index ccd30b6eb0..3a04270558 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -14,6 +14,7 @@ object TapWorkarounds { private const val START_2_COIN_ISSUER = "start2coin" private const val TEST_CARD_BATCH = "99FF" private const val TEST_CARD_ID_STARTS_WITH = "FF99" + private val backupRequiredFirmwareVersion = FirmwareVersion(major = 6, minor = 21) val CardDTO.isTangemTwins: Boolean get() = TwinsHelper.getTwinCardNumber(cardId) != null @@ -26,7 +27,7 @@ object TapWorkarounds { // for cards 6.21 and higher backup is not skippable val CardDTO.canSkipBackup: Boolean - get() = this.firmwareVersion < FirmwareVersion.KeysImportAvailable + get() = this.firmwareVersion < backupRequiredFirmwareVersion val CardDTO.useOldStyleDerivation: Boolean get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95" diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 790f87d7f0..5ad57c37ed 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -82,7 +82,7 @@ okHttp-prettyLogging = "3.1.0" # region Tangem tangemBlockchainSdk = "develop-306" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-280" +tangemCardSdk = "develop-283" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem From 6903f532ab6d2ded3c1052796a6f9697f56b877c Mon Sep 17 00:00:00 2001 From: Tangem Date: Wed, 9 Aug 2023 11:18:23 +0500 Subject: [PATCH 18/52] Updated on 2026-08-14 --- core/ui/build.gradle.kts | 2 ++ .../tangem/core/ui/components/Modifiers.kt | 7 ++--- .../transactions/TransactionList.kt | 30 +++++++------------ .../transactions/TxHistoryContentItem.kt | 19 ++++++++++++ .../transactions}/TxHistoryGroupTitle.kt | 17 ++++------- .../components/transactions/TxHistoryState.kt | 19 +++++------- .../transactions}/TxHistoryTitle.kt | 22 +++++--------- .../wallet/WalletLockedContentState.kt | 7 +++++ .../presentation/common/WalletPreviewData.kt | 13 ++++---- .../wallet/state/WalletSingleCurrencyState.kt | 7 +++-- .../components/WalletLockedContentState.kt | 9 ------ .../state/components/WalletTokensListState.kt | 1 + .../factory/WalletSkeletonStateConverter.kt | 4 ++- .../WalletLoadedTxHistoryConverter.kt | 9 ++++-- .../WalletLoadingTxHistoryConverter.kt | 12 ++++++-- .../WalletTxHistoryItemFlowConverter.kt | 18 ++++++----- .../presentation/wallet/ui/WalletScreen.kt | 6 ++-- .../ui/components/common/WalletContent.kt | 6 ++-- .../multicurrency/MultiCurrencyContent.kt | 2 +- .../SingleCurrencyContentItem.kt | 29 ------------------ 20 files changed, 109 insertions(+), 130 deletions(-) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt => core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt (87%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt => core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt (72%) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt rename {features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency => core/ui/src/main/java/com/tangem/core/ui/components/transactions}/TxHistoryGroupTitle.kt (66%) rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt => core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt (85%) rename {features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency => core/ui/src/main/java/com/tangem/core/ui/components/transactions}/TxHistoryTitle.kt (74%) create mode 100644 core/ui/src/main/java/com/tangem/core/ui/components/wallet/WalletLockedContentState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt delete mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt diff --git a/core/ui/build.gradle.kts b/core/ui/build.gradle.kts index 94bb016bdc..5cabb03840 100644 --- a/core/ui/build.gradle.kts +++ b/core/ui/build.gradle.kts @@ -7,12 +7,14 @@ plugins { dependencies { /** AndroidX libraries */ implementation(deps.androidx.fragment.ktx) + implementation(deps.androidx.paging.runtime) /** Compose */ implementation(deps.compose.constraintLayout) implementation(deps.compose.foundation) implementation(deps.compose.material) implementation(deps.compose.material3) + implementation(deps.compose.paging) implementation(deps.compose.ui.tooling) /** Other libraries */ diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt b/core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt similarity index 87% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt index b06ad40f02..b644e4b42f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/decorations/WalletContentItemDecoration.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.decorations +package com.tangem.core.ui.components import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape @@ -7,10 +7,7 @@ import androidx.compose.ui.composed import androidx.compose.ui.draw.clip import com.tangem.core.ui.res.TangemTheme -/** -[REDACTED_AUTHOR] - */ -internal fun Modifier.walletContentItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed { +fun Modifier.walletContentItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed { val modifierWithHorizontalPadding = this.padding(horizontal = TangemTheme.dimens.spacing16) val isSingleItem = currentIndex == 0 && lastIndex == 0 when { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt similarity index 72% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt index 3d7d9de32a..79d71ca94f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContent.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency +package com.tangem.core.ui.components.transactions import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.fillMaxWidth @@ -9,42 +9,34 @@ import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.itemsIndexed import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlock import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState +import com.tangem.core.ui.components.walletContentItemDecoration import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState -import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletContentItemDecoration -/** - * LazyList extension for transactions history [WalletTxHistoryState] - * - * @param state state - * @param txHistoryItems transactions - * @param modifier modifier - */ -internal fun LazyListScope.txHistoryItems( - state: WalletTxHistoryState, - txHistoryItems: LazyPagingItems?, +fun LazyListScope.txHistoryItems( + state: TxHistoryState, + txHistoryItems: LazyPagingItems?, modifier: Modifier = Modifier, ) { when (state) { - is WalletTxHistoryState.ContentState -> { + is TxHistoryState.ContentState -> { contentItems( txHistoryItems = requireNotNull(txHistoryItems), modifier = modifier, ) } - is WalletTxHistoryState.Empty -> { + is TxHistoryState.Empty -> { nonContentItem( state = EmptyTransactionsBlockState.Empty(onClick = state.onBuyClick), modifier = modifier, ) } - is WalletTxHistoryState.Error -> { + is TxHistoryState.Error -> { nonContentItem( state = EmptyTransactionsBlockState.FailedToLoad(onClick = state.onReloadClick), modifier = modifier, ) } - is WalletTxHistoryState.NotSupported -> { + is TxHistoryState.NotSupported -> { nonContentItem( state = EmptyTransactionsBlockState.NotImplemented(onClick = state.onExploreClick), modifier = modifier, @@ -55,7 +47,7 @@ internal fun LazyListScope.txHistoryItems( @OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.contentItems( - txHistoryItems: LazyPagingItems, + txHistoryItems: LazyPagingItems, modifier: Modifier = Modifier, ) { itemsIndexed( @@ -64,7 +56,7 @@ private fun LazyListScope.contentItems( itemContent = { index, item -> if (item == null) return@itemsIndexed - SingleCurrencyContentItem( + TxHistoryContentItem( state = item, modifier = modifier .animateItemPlacement() diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt new file mode 100644 index 0000000000..2ee6736528 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt @@ -0,0 +1,19 @@ +package com.tangem.core.ui.components.transactions + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier + +@Composable +internal fun TxHistoryContentItem (state: TxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { + when (state) { + is TxHistoryState.TxHistoryItemState.GroupTitle -> { + TxHistoryGroupTitle(config = state, modifier = modifier) + } + is TxHistoryState.TxHistoryItemState.Title -> { + TxHistoryTitle(config = state, modifier = modifier) + } + is TxHistoryState.TxHistoryItemState.Transaction -> { + Transaction(state = state.state, modifier = modifier) + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt similarity index 66% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt index b3b8875b39..92e01a27ad 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryGroupTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency +package com.tangem.core.ui.components.transactions import androidx.compose.foundation.background import androidx.compose.foundation.layout.fillMaxWidth @@ -9,16 +9,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState -/** - * Transactions block group title - * - * @param config config - * @param modifier modifier - */ @Composable -internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) { +internal fun TxHistoryGroupTitle(config: TxHistoryState.TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) { Text( text = config.title, modifier = modifier @@ -38,7 +31,7 @@ internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier @Composable private fun Preview_TransactionsBlockGroupTitle_Light() { TangemTheme(isDark = false) { - TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today")) + TxHistoryGroupTitle(config = TxHistoryState.TxHistoryItemState.GroupTitle(title = "Today")) } } @@ -46,6 +39,6 @@ private fun Preview_TransactionsBlockGroupTitle_Light() { @Composable private fun Preview_TransactionsBlockGroupTitle_Dark() { TangemTheme(isDark = true) { - TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today")) + TxHistoryGroupTitle(config = TxHistoryState.TxHistoryItemState.GroupTitle(title = "Today")) } -} \ No newline at end of file +} diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt similarity index 85% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt index 66448a4f71..7b49f85587 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTxHistoryState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt @@ -1,23 +1,18 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components +package com.tangem.core.ui.components.transactions import androidx.paging.PagingData -import com.tangem.core.ui.components.transactions.TransactionState +import com.tangem.core.ui.components.wallet.WalletLockedContentState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf -/** - * Wallet transaction history state - * -[REDACTED_AUTHOR] - */ -internal sealed interface WalletTxHistoryState { +sealed interface TxHistoryState { /** * Wallet transaction history state with content * * @property items content items */ - sealed class ContentState(open val items: Flow>) : WalletTxHistoryState + sealed class ContentState(open val items: Flow>) : TxHistoryState /** * Loading state @@ -80,21 +75,21 @@ internal sealed interface WalletTxHistoryState { * * @property onBuyClick lambda be invoke when buy button was clicked */ - data class Empty(val onBuyClick: () -> Unit) : WalletTxHistoryState + data class Empty(val onBuyClick: () -> Unit) : TxHistoryState /** * Not supported tx history state * * @property onExploreClick lambda be invoke when explore button was clicked */ - data class NotSupported(val onExploreClick: () -> Unit) : WalletTxHistoryState + data class NotSupported(val onExploreClick: () -> Unit) : TxHistoryState /** * Error state * * @property onReloadClick lambda be invoke when reload button was clicked */ - data class Error(val onReloadClick: () -> Unit) : WalletTxHistoryState + data class Error(val onReloadClick: () -> Unit) : TxHistoryState /** Transactions history item state */ sealed interface TxHistoryItemState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt similarity index 74% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt index 58e06ab395..cc9626e5ff 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/TxHistoryTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt @@ -1,9 +1,8 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency +package com.tangem.core.ui.components.transactions import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* -import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -11,17 +10,10 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme -import com.tangem.feature.wallet.impl.R -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState +import com.tangem.core.ui.R -/** - * Transactions block title - * - * @param config config - * @param modifier modifier - */ @Composable -internal fun TxHistoryTitle(config: TxHistoryItemState.Title, modifier: Modifier = Modifier) { +internal fun TxHistoryTitle(config: TxHistoryState.TxHistoryItemState.Title, modifier: Modifier = Modifier) { Row( modifier = modifier .background(TangemTheme.colors.background.primary) @@ -40,7 +32,7 @@ internal fun TxHistoryTitle(config: TxHistoryItemState.Title, modifier: Modifier modifier = Modifier.clickable(onClick = config.onExploreClick), horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), ) { - Icon( + androidx.compose.material3.Icon( painter = painterResource(id = R.drawable.ic_compass_24), contentDescription = null, modifier = Modifier.size(size = TangemTheme.dimens.size18), @@ -59,7 +51,7 @@ internal fun TxHistoryTitle(config: TxHistoryItemState.Title, modifier: Modifier @Composable private fun Preview_TransactionsBlockTitle_Light() { TangemTheme(isDark = false) { - TxHistoryTitle(config = TxHistoryItemState.Title(onExploreClick = {})) + TxHistoryTitle(config = TxHistoryState.TxHistoryItemState.Title(onExploreClick = {})) } } @@ -67,6 +59,6 @@ private fun Preview_TransactionsBlockTitle_Light() { @Composable private fun Preview_TransactionsBlockTitle_Dark() { TangemTheme(isDark = true) { - TxHistoryTitle(config = TxHistoryItemState.Title(onExploreClick = {})) + TxHistoryTitle(config = TxHistoryState.TxHistoryItemState.Title(onExploreClick = {})) } -} \ No newline at end of file +} diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/wallet/WalletLockedContentState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/wallet/WalletLockedContentState.kt new file mode 100644 index 0000000000..e1c2049da6 --- /dev/null +++ b/core/ui/src/main/java/com/tangem/core/ui/components/wallet/WalletLockedContentState.kt @@ -0,0 +1,7 @@ +package com.tangem.core.ui.components.wallet + +/** + * Wallet locked content state. + * It allows to divide the locked content of multi-currency and single-currency wallets. + */ +interface WalletLockedContentState \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index afe19472ec..00f7dbdcfb 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -6,6 +6,7 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.components.transactions.TransactionState import com.tangem.core.ui.extensions.TextReference +import com.tangem.core.ui.components.transactions.TxHistoryState import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState @@ -347,21 +348,21 @@ internal object WalletPreviewData { type = PriceChangeConfig.Type.UP, ), ), - txHistoryState = WalletTxHistoryState.Content( + txHistoryState = TxHistoryState.Content( flowOf( PagingData.from( listOf( - WalletTxHistoryState.TxHistoryItemState.Title(onExploreClick = {}), - WalletTxHistoryState.TxHistoryItemState.GroupTitle("Today"), - WalletTxHistoryState.TxHistoryItemState.Transaction( + TxHistoryState.TxHistoryItemState.Title(onExploreClick = {}), + TxHistoryState.TxHistoryItemState.GroupTitle("Today"), + TxHistoryState.TxHistoryItemState.Transaction( TransactionState.Sending( address = "33BddS...ga2B", amount = "-0.500913 BTC", timestamp = "8:41", ), ), - WalletTxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"), - WalletTxHistoryState.TxHistoryItemState.Transaction( + TxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"), + TxHistoryState.TxHistoryItemState.Transaction( TransactionState.Sending( address = "33BddS...ga2B", amount = "-0.500913 BTC", diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt index 616d8d5f0b..0a8783fb4d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -1,6 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.state import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.TxHistoryState import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -19,7 +20,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { abstract val marketPriceBlockState: MarketPriceBlockState? /** Transactions history state */ - abstract val txHistoryState: WalletTxHistoryState + abstract val txHistoryState: TxHistoryState data class Content( override val onBackClick: () -> Unit, @@ -30,7 +31,7 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { override val bottomSheetConfig: WalletBottomSheetConfig?, override val buttons: ImmutableList, override val marketPriceBlockState: MarketPriceBlockState, - override val txHistoryState: WalletTxHistoryState, + override val txHistoryState: TxHistoryState, ) : WalletSingleCurrencyState() data class Locked( @@ -62,6 +63,6 @@ internal sealed class WalletSingleCurrencyState : WalletState.ContentState() { override val marketPriceBlockState = null - override val txHistoryState: WalletTxHistoryState = WalletTxHistoryState.Locked(onExploreClick) + override val txHistoryState: TxHistoryState = TxHistoryState.Locked(onExploreClick) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt deleted file mode 100644 index c0a0206ecc..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletLockedContentState.kt +++ /dev/null @@ -1,9 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.state.components - -/** - * Wallet locked content state. - * It allows to divide the locked content of multi-currency and single-currency wallets. - * -[REDACTED_AUTHOR] - */ -internal sealed interface WalletLockedContentState \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index b9505c2802..98430d72a6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.components import com.tangem.core.ui.extensions.TextReference import com.tangem.feature.wallet.impl.R +import com.tangem.core.ui.components.wallet.WalletLockedContentState import com.tangem.feature.wallet.presentation.common.state.TokenItemState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index 6c86a81215..aa2329d737 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -2,6 +2,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory @@ -62,7 +64,7 @@ internal class WalletSkeletonStateConverter( bottomSheetConfig = null, buttons = getButtons(), marketPriceBlockState = MarketPriceBlockState.Loading(currencyName = currencyName), - txHistoryState = WalletTxHistoryState.Loading(onExploreClick = clickIntents::onExploreClick), + txHistoryState = TxHistoryState.Loading(onExploreClick = clickIntents::onExploreClick), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index d0c95681cb..bf4f01fccc 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -3,10 +3,15 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.TxHistoryState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents @@ -14,7 +19,7 @@ import com.tangem.utils.converter.Converter import kotlinx.coroutines.flow.Flow /** - * Converter from loaded tx history to [WalletTxHistoryState] + * Converter from loaded tx history to [TxHistoryState] * * @property currentStateProvider current state provider * @property currentCardTypeResolverProvider current card type resolver provider @@ -43,7 +48,7 @@ internal class WalletLoadedTxHistoryConverter( return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = when (error) { is TxHistoryListError.DataError -> { - WalletTxHistoryState.Error(onReloadClick = clickIntents::onReloadClick) + TxHistoryState.Error(onReloadClick = clickIntents::onReloadClick) } }, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index c689acf2df..4112e72a79 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -2,6 +2,12 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import arrow.core.Either import com.tangem.common.Provider +import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig +import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.TransactionState +import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.domain.common.CardTypesResolver +import com.tangem.domain.txhistory.error.TxHistoryStateError import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState @@ -30,13 +36,13 @@ internal class WalletLoadingTxHistoryConverter( return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( txHistoryState = when (error) { is TxHistoryStateError.EmptyTxHistories -> { - WalletTxHistoryState.Empty(onBuyClick = clickIntents::onBuyClick) + TxHistoryState.Empty(onBuyClick = clickIntents::onBuyClick) } is TxHistoryStateError.DataError -> { - WalletTxHistoryState.Error(onReloadClick = clickIntents::onReloadClick) + TxHistoryState.Error(onReloadClick = clickIntents::onReloadClick) } is TxHistoryStateError.TxHistoryNotImplemented -> { - WalletTxHistoryState.NotSupported(onExploreClick = clickIntents::onExploreClick) + TxHistoryState.NotSupported(onExploreClick = clickIntents::onExploreClick) } }, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index 698ac67c5a..1bbe6e6069 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -4,6 +4,8 @@ import android.text.format.DateUtils import androidx.paging.* import com.tangem.blockchain.common.Blockchain import com.tangem.core.ui.components.transactions.TransactionState +import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.TxHistoryState.TxHistoryItemState import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState @@ -22,7 +24,7 @@ import java.math.BigDecimal import java.util.Locale /** - * Convert from [Flow] of [TxHistoryItem] to [WalletTxHistoryState] + * Convert from [Flow] of [TxHistoryItem] to [TxHistoryState] * * @property blockchain blockchain of transactions history * @property clickIntents screen click intents @@ -32,7 +34,7 @@ import java.util.Locale internal class WalletTxHistoryItemFlowConverter( private val blockchain: Blockchain, private val clickIntents: WalletClickIntents, -) : Converter>, WalletTxHistoryState> { +) : Converter>, TxHistoryState> { /** Example, 2 Aug, 2023 */ private val dateFormatter by lazy { @@ -56,8 +58,8 @@ internal class WalletTxHistoryItemFlowConverter( .withLocale(Locale.getDefault()) } - override fun convert(value: Flow>): WalletTxHistoryState { - return WalletTxHistoryState.Content( + override fun convert(value: Flow>): TxHistoryState { + return TxHistoryState.Content( items = value .map { pagingData -> pagingData @@ -163,9 +165,10 @@ internal class WalletTxHistoryItemFlowConverter( if (txHistoryItemState is TxHistoryItemState.Transaction && txHistoryItemState.state is TransactionState.Content ) { + val txContent = txHistoryItemState.state as TransactionState.Content txHistoryItemState.copy( - state = txHistoryItemState.state.copySealed( - timestamp = txHistoryItemState.state.timestamp.toTimeFormat(), + state = txContent.copySealed( + timestamp = txContent.timestamp.toTimeFormat(), ), ) } else { @@ -184,7 +187,8 @@ internal class WalletTxHistoryItemFlowConverter( private fun TxHistoryItemState?.getTimestamp(): Long? { return if (this is TxHistoryItemState.Transaction && this.state is TransactionState.Content) { - requireNotNull(this.state.timestamp.toLongOrNull()) { "Timestamp must be Long type" } + val txContent = this.state as TransactionState.Content + requireNotNull(txContent.timestamp.toLongOrNull()) { "Timestamp must be Long type" } } else { null } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index 0c8e6d0aa7..a9108f22e8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -16,12 +16,12 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.paging.compose.collectAsLazyPagingItems +import com.tangem.core.ui.components.transactions.TxHistoryState import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeButton @@ -68,9 +68,9 @@ private fun WalletContent(state: WalletState.ContentState) { .pullRefresh(pullRefreshState), ) { val txHistoryItems = if (state is WalletSingleCurrencyState && - state.txHistoryState is WalletTxHistoryState.ContentState + state.txHistoryState is TxHistoryState.ContentState ) { - (state.txHistoryState as? WalletTxHistoryState.ContentState)?.items?.collectAsLazyPagingItems() + (state.txHistoryState as? TxHistoryState.ContentState)?.items?.collectAsLazyPagingItems() } else { null } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index cbd8a73c90..0c30f19403 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -3,12 +3,12 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems +import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.txHistoryItems import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.tokensListItems -import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency.txHistoryItems /** * Wallet content @@ -21,7 +21,7 @@ import com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrenc */ internal fun LazyListScope.contentItems( state: WalletState.ContentState, - txHistoryItems: LazyPagingItems?, + txHistoryItems: LazyPagingItems?, modifier: Modifier = Modifier, ) { when (state) { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 88112d5865..87d943c4e3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -4,8 +4,8 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.walletContentItemDecoration import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState -import com.tangem.feature.wallet.presentation.wallet.ui.decorations.walletContentItemDecoration /** * LazyList extension for [WalletTokensListState] diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt deleted file mode 100644 index 8a20e22efe..0000000000 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyContentItem.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.feature.wallet.presentation.wallet.ui.components.singlecurrency - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.tangem.core.ui.components.transactions.Transaction -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState - -/** - * Single currency content item - * - * @param state state - * @param modifier modifier - * -[REDACTED_AUTHOR] - */ -@Composable -internal fun SingleCurrencyContentItem(state: WalletTxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { - when (state) { - is WalletTxHistoryState.TxHistoryItemState.GroupTitle -> { - TxHistoryGroupTitle(config = state, modifier = modifier) - } - is WalletTxHistoryState.TxHistoryItemState.Title -> { - TxHistoryTitle(config = state, modifier = modifier) - } - is WalletTxHistoryState.TxHistoryItemState.Transaction -> { - Transaction(state = state.state, modifier = modifier) - } - } -} \ No newline at end of file From d1a571094edf140c11f0e64eb6cbf45e22930cfe Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Aug 2023 15:32:06 +0500 Subject: [PATCH 19/52] Updated on 2026-08-14 --- .../core/ui/components/transactions/Transaction.kt | 1 + .../core/ui/components/transactions/TransactionList.kt | 5 +++-- .../ui/components/transactions/TxHistoryContentItem.kt | 1 + .../ui/components/transactions/TxHistoryGroupTitle.kt | 7 ++++--- .../core/ui/components/transactions/TxHistoryTitle.kt | 4 +++- .../transactions/{ => state}/TransactionState.kt | 2 +- .../transactions/{ => state}/TxHistoryState.kt | 2 +- .../Modifiers.kt => decorations/RoundedDecorations.kt} | 4 ++-- .../wallet/presentation/common/WalletPreviewData.kt | 4 ++-- .../wallet/state/WalletSingleCurrencyState.kt | 2 +- .../state/factory/WalletSkeletonStateConverter.kt | 3 +-- .../txhistory/WalletLoadedTxHistoryConverter.kt | 7 +------ .../txhistory/WalletLoadingTxHistoryConverter.kt | 10 ++-------- .../txhistory/WalletTxHistoryItemFlowConverter.kt | 8 +++----- .../wallet/presentation/wallet/ui/WalletScreen.kt | 2 +- .../wallet/ui/components/common/WalletContent.kt | 2 +- .../components/multicurrency/MultiCurrencyContent.kt | 10 +++++----- 17 files changed, 33 insertions(+), 41 deletions(-) rename core/ui/src/main/java/com/tangem/core/ui/components/transactions/{ => state}/TransactionState.kt (98%) rename core/ui/src/main/java/com/tangem/core/ui/components/transactions/{ => state}/TxHistoryState.kt (98%) rename core/ui/src/main/java/com/tangem/core/ui/{components/Modifiers.kt => decorations/RoundedDecorations.kt} (91%) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt index 1257a022a7..1d05888f7f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt @@ -21,6 +21,7 @@ import androidx.constraintlayout.compose.Dimension import com.tangem.core.ui.R import com.tangem.core.ui.components.CircleShimmer import com.tangem.core.ui.components.RectangleShimmer +import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.res.TangemTheme /** diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt index 79d71ca94f..e08d2249c7 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt @@ -9,7 +9,8 @@ import androidx.paging.compose.LazyPagingItems import androidx.paging.compose.itemsIndexed import com.tangem.core.ui.components.transactions.empty.EmptyTransactionBlock import com.tangem.core.ui.components.transactions.empty.EmptyTransactionsBlockState -import com.tangem.core.ui.components.walletContentItemDecoration +import com.tangem.core.ui.components.transactions.state.TxHistoryState +import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.res.TangemTheme fun LazyListScope.txHistoryItems( @@ -60,7 +61,7 @@ private fun LazyListScope.contentItems( state = item, modifier = modifier .animateItemPlacement() - .walletContentItemDecoration( + .roundedShapeItemDecoration( currentIndex = index, lastIndex = txHistoryItems.itemSnapshotList.lastIndex, ), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt index 2ee6736528..2d6833f625 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt @@ -2,6 +2,7 @@ package com.tangem.core.ui.components.transactions import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import com.tangem.core.ui.components.transactions.state.TxHistoryState @Composable internal fun TxHistoryContentItem (state: TxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt index 92e01a27ad..ef85a1ba0a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt @@ -8,10 +8,11 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview +import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.core.ui.res.TangemTheme @Composable -internal fun TxHistoryGroupTitle(config: TxHistoryState.TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) { +internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) { Text( text = config.title, modifier = modifier @@ -31,7 +32,7 @@ internal fun TxHistoryGroupTitle(config: TxHistoryState.TxHistoryItemState.Group @Composable private fun Preview_TransactionsBlockGroupTitle_Light() { TangemTheme(isDark = false) { - TxHistoryGroupTitle(config = TxHistoryState.TxHistoryItemState.GroupTitle(title = "Today")) + TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today")) } } @@ -39,6 +40,6 @@ private fun Preview_TransactionsBlockGroupTitle_Light() { @Composable private fun Preview_TransactionsBlockGroupTitle_Dark() { TangemTheme(isDark = true) { - TxHistoryGroupTitle(config = TxHistoryState.TxHistoryItemState.GroupTitle(title = "Today")) + TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today")) } } diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt index cc9626e5ff..74630b305a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt @@ -3,6 +3,7 @@ package com.tangem.core.ui.components.transactions import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* +import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -11,6 +12,7 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.R +import com.tangem.core.ui.components.transactions.state.TxHistoryState @Composable internal fun TxHistoryTitle(config: TxHistoryState.TxHistoryItemState.Title, modifier: Modifier = Modifier) { @@ -32,7 +34,7 @@ internal fun TxHistoryTitle(config: TxHistoryState.TxHistoryItemState.Title, mod modifier = Modifier.clickable(onClick = config.onExploreClick), horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), ) { - androidx.compose.material3.Icon( + Icon( painter = painterResource(id = R.drawable.ic_compass_24), contentDescription = null, modifier = Modifier.size(size = TangemTheme.dimens.size18), diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt similarity index 98% rename from core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt index b5b6adaaba..225389c2f4 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt @@ -1,4 +1,4 @@ -package com.tangem.core.ui.components.transactions +package com.tangem.core.ui.components.transactions.state /** * Transaction component state diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt similarity index 98% rename from core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt rename to core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt index 7b49f85587..9fb654d73b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt @@ -1,4 +1,4 @@ -package com.tangem.core.ui.components.transactions +package com.tangem.core.ui.components.transactions.state import androidx.paging.PagingData import com.tangem.core.ui.components.wallet.WalletLockedContentState diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt b/core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt similarity index 91% rename from core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt rename to core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt index b644e4b42f..362f4d95e9 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/Modifiers.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/decorations/RoundedDecorations.kt @@ -1,4 +1,4 @@ -package com.tangem.core.ui.components +package com.tangem.core.ui.decorations import androidx.compose.foundation.layout.padding import androidx.compose.foundation.shape.RoundedCornerShape @@ -7,7 +7,7 @@ import androidx.compose.ui.composed import androidx.compose.ui.draw.clip import com.tangem.core.ui.res.TangemTheme -fun Modifier.walletContentItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed { +fun Modifier.roundedShapeItemDecoration(currentIndex: Int, lastIndex: Int): Modifier = composed { val modifierWithHorizontalPadding = this.padding(horizontal = TangemTheme.dimens.spacing16) val isSingleItem = currentIndex == 0 && lastIndex == 0 when { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 00f7dbdcfb..cfb5eeb72e 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -4,9 +4,9 @@ import androidx.paging.PagingData import com.tangem.core.ui.R import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig -import com.tangem.core.ui.components.transactions.TransactionState +import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.extensions.TextReference -import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt index 0a8783fb4d..8433b8d9f7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletSingleCurrencyState.kt @@ -1,7 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.state import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.feature.wallet.presentation.wallet.state.components.* import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt index aa2329d737..f40a3b577a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSkeletonStateConverter.kt @@ -2,8 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.TxHistoryState -import com.tangem.domain.common.CardTypesResolver +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.wallets.models.UserWallet import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt index bf4f01fccc..d5a8f7cdbd 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadedTxHistoryConverter.kt @@ -3,17 +3,12 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState -import com.tangem.feature.wallet.presentation.wallet.state.WalletStateHolder -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletManageButton import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import kotlinx.coroutines.flow.Flow diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt index 4112e72a79..ef8131d180 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletLoadingTxHistoryConverter.kt @@ -2,16 +2,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import arrow.core.Either import com.tangem.common.Provider -import com.tangem.core.ui.components.buttons.actions.ActionButtonConfig -import com.tangem.core.ui.components.marketprice.MarketPriceBlockState -import com.tangem.core.ui.components.transactions.TransactionState -import com.tangem.core.ui.components.transactions.TxHistoryState -import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.txhistory.error.TxHistoryStateError +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.txhistory.models.TxHistoryStateError import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter @@ -50,7 +44,7 @@ internal class WalletLoadingTxHistoryConverter( private fun convert(value: Int): WalletSingleCurrencyState.Content { return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content).copy( - txHistoryState = WalletTxHistoryState.ContentWithLoadingItems(itemsCount = value), + txHistoryState = TxHistoryState.ContentWithLoadingItems(itemsCount = value), ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index 1bbe6e6069..878f41f995 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -3,12 +3,10 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory.txhistory import android.text.format.DateUtils import androidx.paging.* import com.tangem.blockchain.common.Blockchain -import com.tangem.core.ui.components.transactions.TransactionState -import com.tangem.core.ui.components.transactions.TxHistoryState -import com.tangem.core.ui.components.transactions.TxHistoryState.TxHistoryItemState +import com.tangem.core.ui.components.transactions.state.TransactionState +import com.tangem.core.ui.components.transactions.state.TxHistoryState +import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.domain.txhistory.models.TxHistoryItem -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState -import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTxHistoryState.TxHistoryItemState import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import com.tangem.utils.extensions.isToday diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index a9108f22e8..db0cee15f1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.paging.compose.collectAsLazyPagingItems -import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.res.TangemTheme import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt index 0c30f19403..211ad0e75f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletContent.kt @@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier import androidx.paging.compose.LazyPagingItems -import com.tangem.core.ui.components.transactions.TxHistoryState +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.components.transactions.txHistoryItems import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 87d943c4e3..76c454d49f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.ui.Modifier -import com.tangem.core.ui.components.walletContentItemDecoration +import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState /** @@ -25,10 +25,10 @@ internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifie state = item, modifier = modifier .animateItemPlacement() - .walletContentItemDecoration( - currentIndex = index, - lastIndex = state.items.lastIndex, - ), + .roundedShapeItemDecoration( + currentIndex = index, + lastIndex = state.items.lastIndex, + ), ) }, ) From 24ddb1963949e86b8f0459c141927db8ba412623 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Aug 2023 16:55:48 +0500 Subject: [PATCH 20/52] Updated on 2026-08-14 --- .../core/ui/components/transactions/TxHistoryContentItem.kt | 2 +- .../core/ui/components/transactions/TxHistoryGroupTitle.kt | 2 +- .../core/ui/components/transactions/TxHistoryTitle.kt | 2 +- .../ui/components/multicurrency/MultiCurrencyContent.kt | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt index 2d6833f625..3675b0c054 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt @@ -5,7 +5,7 @@ import androidx.compose.ui.Modifier import com.tangem.core.ui.components.transactions.state.TxHistoryState @Composable -internal fun TxHistoryContentItem (state: TxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { +internal fun TxHistoryContentItem(state: TxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { when (state) { is TxHistoryState.TxHistoryItemState.GroupTitle -> { TxHistoryGroupTitle(config = state, modifier = modifier) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt index ef85a1ba0a..a9b90c4a48 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt @@ -42,4 +42,4 @@ private fun Preview_TransactionsBlockGroupTitle_Dark() { TangemTheme(isDark = true) { TxHistoryGroupTitle(config = TxHistoryItemState.GroupTitle(title = "Today")) } -} +} \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt index 74630b305a..b62ade1867 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt @@ -63,4 +63,4 @@ private fun Preview_TransactionsBlockTitle_Dark() { TangemTheme(isDark = true) { TxHistoryTitle(config = TxHistoryState.TxHistoryItemState.Title(onExploreClick = {})) } -} +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index 76c454d49f..5ea672d364 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -26,9 +26,9 @@ internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifie modifier = modifier .animateItemPlacement() .roundedShapeItemDecoration( - currentIndex = index, - lastIndex = state.items.lastIndex, - ), + currentIndex = index, + lastIndex = state.items.lastIndex, + ), ) }, ) From b284a8195aff2f9744564aad39536865267486a5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Aug 2023 17:10:48 +0500 Subject: [PATCH 21/52] Updated on 2026-08-14 --- .../core/ui/components/transactions/TransactionList.kt | 9 ++++++++- .../ui/components/transactions/TxHistoryContentItem.kt | 2 +- .../ui/components/transactions/TxHistoryGroupTitle.kt | 6 ++++++ .../core/ui/components/transactions/TxHistoryTitle.kt | 8 +++++++- .../ui/components/transactions/state/TxHistoryState.kt | 3 +++ 5 files changed, 25 insertions(+), 3 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt index e08d2249c7..9ea544233d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt @@ -13,6 +13,13 @@ import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.core.ui.decorations.roundedShapeItemDecoration import com.tangem.core.ui.res.TangemTheme +/** + * LazyList extension for transactions history [TxHistoryState] + * + * @param state state + * @param txHistoryItems transactions + * @param modifier modifier + */ fun LazyListScope.txHistoryItems( state: TxHistoryState, txHistoryItems: LazyPagingItems?, @@ -57,7 +64,7 @@ private fun LazyListScope.contentItems( itemContent = { index, item -> if (item == null) return@itemsIndexed - TxHistoryContentItem( + TxHistoryListItem( state = item, modifier = modifier .animateItemPlacement() diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt index 3675b0c054..6f918f3cad 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryContentItem.kt @@ -5,7 +5,7 @@ import androidx.compose.ui.Modifier import com.tangem.core.ui.components.transactions.state.TxHistoryState @Composable -internal fun TxHistoryContentItem(state: TxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { +internal fun TxHistoryListItem(state: TxHistoryState.TxHistoryItemState, modifier: Modifier = Modifier) { when (state) { is TxHistoryState.TxHistoryItemState.GroupTitle -> { TxHistoryGroupTitle(config = state, modifier = modifier) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt index a9b90c4a48..c7eaaa7978 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryGroupTitle.kt @@ -11,6 +11,12 @@ import androidx.compose.ui.tooling.preview.Preview import com.tangem.core.ui.components.transactions.state.TxHistoryState.TxHistoryItemState import com.tangem.core.ui.res.TangemTheme +/** + * Transactions block group title + * + * @param config config + * @param modifier modifier + */ @Composable internal fun TxHistoryGroupTitle(config: TxHistoryItemState.GroupTitle, modifier: Modifier = Modifier) { Text( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt index b62ade1867..fd59b88c81 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TxHistoryTitle.kt @@ -10,10 +10,16 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.res.TangemTheme import com.tangem.core.ui.R import com.tangem.core.ui.components.transactions.state.TxHistoryState +import com.tangem.core.ui.res.TangemTheme +/** + * Transactions block title + * + * @param config config + * @param modifier modifier + */ @Composable internal fun TxHistoryTitle(config: TxHistoryState.TxHistoryItemState.Title, modifier: Modifier = Modifier) { Row( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt index 9fb654d73b..38db4fa180 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt @@ -5,6 +5,9 @@ import com.tangem.core.ui.components.wallet.WalletLockedContentState import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flowOf +/** + * Wallet transaction history state + */ sealed interface TxHistoryState { /** From 9b70e5bc8213c8a21f0c9ef7a628949849fb7490 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 08:26:23 +0300 Subject: [PATCH 22/52] Updated on 2026-08-14 --- .../local/quote/DefaultQuotesStore.kt | 7 +- .../datasource/local/quote/QuotesStore.kt | 3 +- .../tokens/repository/MockQuotesRepository.kt | 6 +- .../data/tokens/utils/NetworkStatusFactory.kt | 2 +- .../tokens/utils/ResponseCurrenciesFactory.kt | 2 +- .../data/tokens/utils/TokensOperations.kt | 47 +++++--------- .../tokens/utils/UserTokensResponseFactory.kt | 2 +- .../walletmanager/utils/SdkTokenConverter.kt | 2 +- .../domain/tokens/models/CryptoCurrency.kt | 65 +++++++++++++++++-- .../com/tangem/domain/tokens/models/Quote.kt | 4 +- .../CurrenciesStatusesOperations.kt | 10 +-- .../tangem/domain/tokens/mock/MockQuotes.kt | 20 +++--- .../tangem/domain/tokens/mock/MockTokens.kt | 21 +++--- .../domain/tokens/mock/MockTokensStates.kt | 2 +- 14 files changed, 116 insertions(+), 77 deletions(-) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt index f359605e09..78e6ffb72b 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/DefaultQuotesStore.kt @@ -3,6 +3,7 @@ package com.tangem.datasource.local.quote import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.datasource.local.datastore.core.StringKeyDataStore import com.tangem.datasource.local.quote.model.StoredQuote +import com.tangem.domain.tokens.models.CryptoCurrency import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -10,9 +11,9 @@ internal class DefaultQuotesStore( private val dataStore: StringKeyDataStore, ) : QuotesStore { - override fun get(rawCurrenciesIds: Set): Flow> { - val flows = rawCurrenciesIds.map { rawCurrencyId -> - dataStore.get(rawCurrencyId) + override fun get(currenciesIds: Set): Flow> { + val flows = currenciesIds.mapNotNull { currencyId -> + dataStore.get(currencyId.rawCurrencyId ?: return@mapNotNull null) } return combine(flows) { quotes -> quotes.toSet() } diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt index 77a5a65c73..f34d0039b8 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/quote/QuotesStore.kt @@ -2,11 +2,12 @@ package com.tangem.datasource.local.quote import com.tangem.datasource.api.tangemTech.models.QuotesResponse import com.tangem.datasource.local.quote.model.StoredQuote +import com.tangem.domain.tokens.models.CryptoCurrency import kotlinx.coroutines.flow.Flow interface QuotesStore { - fun get(rawCurrenciesIds: Set): Flow> + fun get(currenciesIds: Set): Flow> suspend fun store(response: QuotesResponse) } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt index 91ca009e7c..31e9c03dc5 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt @@ -13,13 +13,13 @@ internal class MockQuotesRepository : QuotesRepository { override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { return channelFlow { - val quotes = currenciesIds.map { + val quotes = currenciesIds.mapNotNullTo(hashSetOf()) { id -> Quote( - currencyId = it, + rawCurrencyId = id.rawCurrencyId ?: return@mapNotNullTo null, fiatRate = BigDecimal.ZERO, priceChange = BigDecimal.ZERO, ) - }.toSet() + } delay(Random.nextLong(from = 200, until = 2_000)) diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt index d38263cbab..6feee38172 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/NetworkStatusFactory.kt @@ -39,7 +39,7 @@ internal class NetworkStatusFactory { is CryptoCurrencyAmount.Coin -> currencies.singleOrNull { it is CryptoCurrency.Coin } is CryptoCurrencyAmount.Token -> currencies.firstOrNull { it is CryptoCurrency.Token && - getTokenIdString(it.id) == amount.id && + it.id.rawCurrencyId == amount.id && it.contractAddress == amount.tokenContractAddress } } diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt index 7955f660dd..89d6bfec27 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -13,7 +13,7 @@ import com.tangem.blockchain.common.Token as SdkToken internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { fun createCurrency(currencyId: CryptoCurrency.ID, response: UserTokensResponse, card: CardDTO): CryptoCurrency { - val responseTokenId = getTokenIdString(currencyId) + val responseTokenId = currencyId.rawCurrencyId val token = requireNotNull(response.tokens.firstOrNull { it.id == responseTokenId }) { "Unable find a token with provided ID: $responseTokenId" diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index 091d698368..c302d2246f 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -6,21 +6,21 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency.ID import com.tangem.domain.tokens.models.Network import com.tangem.blockchain.common.Token as SdkToken +import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.COIN_PREFIX as COIN_ID_PREFIX +import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.CUSTOM_TOKEN_PREFIX as CUSTOM_TOKEN_ID_PREFIX +import com.tangem.domain.tokens.models.CryptoCurrency.ID.Prefix.TOKEN_PREFIX as TOKEN_ID_PREFIX +import com.tangem.domain.tokens.models.CryptoCurrency.ID.Suffix.ContractAddress as CustomCurrencyIdSuffix +import com.tangem.domain.tokens.models.CryptoCurrency.ID.Suffix.RawID as CurrencyIdSuffix private const val DEFAULT_TOKENS_ICONS_HOST = "https://s3.eu-central-1.amazonaws.com/tangem.api/coins" private const val TOKEN_ICON_SIZE = "large" private const val TOKEN_ICON_EXT = "png" -private const val COIN_ID_PREFIX = "coin_" -private const val TOKEN_ID_PREFIX = "token_" -private const val CUSTOM_TOKEN_ID_PREFIX = "custom_token_" -private const val TOKEN_ID_DELIMITER = '#' - -internal fun isCustomToken(tokenId: CryptoCurrency.ID): Boolean { - return tokenId.value.startsWith(CUSTOM_TOKEN_ID_PREFIX) +internal fun isCustomToken(tokenId: ID): Boolean { + return tokenId.rawCurrencyId == null } internal fun getDerivationPath(blockchain: Blockchain, card: CardDTO): String? { @@ -41,24 +41,14 @@ internal fun getNetworkId(blockchain: Blockchain): Network.ID { return Network.ID(value) } -internal fun getCoinId(blockchain: Blockchain): CryptoCurrency.ID { +internal fun getCoinId(blockchain: Blockchain): ID { return getTokenOrCoinId(blockchain, token = null) } -internal fun getTokenId(blockchain: Blockchain, token: SdkToken): CryptoCurrency.ID { +internal fun getTokenId(blockchain: Blockchain, token: SdkToken): ID { return getTokenOrCoinId(blockchain, token) } -internal fun getTokenIdString(currencyId: CryptoCurrency.ID): String? { - val idValue = currencyId.value - - return if (idValue.startsWith(CUSTOM_TOKEN_ID_PREFIX)) { - null - } else { - idValue.substringAfter(TOKEN_ID_DELIMITER) - } -} - internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? { val tokenId = token.id @@ -79,22 +69,15 @@ internal fun getCoinIconUrl(blockchain: Blockchain): String? { return coinId?.let(::getTokenIconUrlFromDefaultHost) } -private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): CryptoCurrency.ID { +private fun getTokenOrCoinId(blockchain: Blockchain, token: SdkToken?): ID { val sdkTokenId = token?.id val (prefix, suffix) = when { - token == null -> COIN_ID_PREFIX to blockchain.toCoinId() - sdkTokenId == null -> CUSTOM_TOKEN_ID_PREFIX to token.contractAddress - else -> TOKEN_ID_PREFIX to sdkTokenId + token == null -> COIN_ID_PREFIX to CurrencyIdSuffix(rawId = blockchain.toCoinId()) + sdkTokenId == null -> CUSTOM_TOKEN_ID_PREFIX to CustomCurrencyIdSuffix(contractAddress = token.contractAddress) + else -> TOKEN_ID_PREFIX to CurrencyIdSuffix(rawId = sdkTokenId) } - val value = buildString { - append(prefix) - append(blockchain.id) - append(TOKEN_ID_DELIMITER) - append(suffix.lowercase()) - } - - return CryptoCurrency.ID(value) + return ID(prefix, getNetworkId(blockchain), suffix) } private fun getTokenIconUrlFromDefaultHost(tokenId: String): String { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt index 21a66808da..d112ad4476 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/UserTokensResponseFactory.kt @@ -30,7 +30,7 @@ internal class UserTokensResponseFactory { val blockchain = getBlockchain(currency.networkId) return UserTokensResponse.Token( - id = getTokenIdString(currency.id), + id = currency.id.rawCurrencyId, networkId = blockchain.toNetworkId(), derivationPath = currency.derivationPath, name = currency.name, diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt index fd556d4026..a6ceecff07 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/SdkTokenConverter.kt @@ -8,7 +8,7 @@ internal class SdkTokenConverter : Converter { override fun convert(value: CryptoCurrency.Token): SdkToken { return SdkToken( - id = value.id.value.takeUnless { value.isCustom }, + id = value.id.rawCurrencyId, name = value.name, symbol = value.symbol, contractAddress = value.contractAddress, diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt index e523648894..d31577a3a9 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt @@ -65,15 +65,68 @@ sealed class CryptoCurrency { } /** - * Value class for uniquely identifying a cryptocurrency. + * Represents a unique identifier for a cryptocurrency, constructed from various components. * - * @property value The unique identifier value. + * The ID is designed to ensure that different cryptocurrencies, whether they are standard tokens, custom tokens or + * standard coins, can be distinctly identified within a system. + * + * @property value Constructed unique identifier value, made up of prefix, network ID, and suffix. + * @property rawCurrencyId Represents not unique currency ID from the blockchain network. `null` if + * its ID of the custom token. */ - @JvmInline - value class ID(val value: String) { + data class ID( + private val prefix: Prefix, + private val networkId: Network.ID, + private val suffix: Suffix, + ) { - init { - require(value.isNotBlank()) { "Crypto currency ID must not be blank" } + val value: String = buildString { + append(prefix.value) + append(networkId.value) + append(DELIMITER) + append(suffix.value) + } + + val rawCurrencyId: String? = (suffix as? Suffix.RawID)?.rawId + + /** + * Represents the different types of prefixes that can be associated with a cryptocurrency ID. + * These prefixes can help in quickly categorizing the type of cryptocurrency. + */ + enum class Prefix(val value: String) { + /** Prefix for standard coins. */ + COIN_PREFIX(value = "coin_"), + + /** Prefix for standard tokens. */ + TOKEN_PREFIX(value = "token_"), + + /** Prefix for custom tokens. */ + CUSTOM_TOKEN_PREFIX(value = "custom_"), + } + + /** + * Represents the suffix part of the cryptocurrency ID. + * + * The suffix can either be a raw ID or a contract address. + */ + sealed class Suffix { + + /** The value of the suffix, which could be either a raw ID or a contract address. */ + abstract val value: String + + /** Represents a raw ID suffix. */ + data class RawID(val rawId: String) : Suffix() { + override val value: String = rawId + } + + /** Represents a contract address suffix. */ + data class ContractAddress(val contractAddress: String) : Suffix() { + override val value: String = contractAddress + } + } + + private companion object { + const val DELIMITER = '#' } } diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt index 808d81e25a..835e84fe4f 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/Quote.kt @@ -5,12 +5,12 @@ import java.math.BigDecimal /** * Represents a financial quote for a specific cryptocurrency, including its fiat exchange rate and price change. * - * @property currencyId The unique identifier of the cryptocurrency for which the quote is provided. + * @property rawCurrencyId The unique identifier of the token for which the quote is provided. * @property fiatRate The current fiat exchange rate for the cryptocurrency. * @property priceChange The price change for the cryptocurrency. */ data class Quote( - val currencyId: CryptoCurrency.ID, + val rawCurrencyId: String, val fiatRate: BigDecimal, val priceChange: BigDecimal, ) \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 5d17887c55..555b0fab08 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -79,7 +79,7 @@ internal class CurrenciesStatusesOperations( val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) .map { maybeQuotes -> maybeQuotes.map { quotes -> - quotes.singleOrNull { it.currencyId == currency.id } + quotes.singleOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } } } @@ -102,11 +102,11 @@ internal class CurrenciesStatusesOperations( quotes: Set, networkStatuses: Set, ): List { - return currencies.map { token -> - val quote = quotes.firstOrNull { it.currencyId == token.id } - val networkStatus = networkStatuses.firstOrNull { it.networkId == token.networkId } + return currencies.map { currency -> + val quote = quotes.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } + val networkStatus = networkStatuses.firstOrNull { it.networkId == currency.networkId } - createStatus(token, quote, networkStatus) + createStatus(currency, quote, networkStatus) } } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt index 0607456029..a7e128d112 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockQuotes.kt @@ -8,61 +8,61 @@ import java.math.BigDecimal internal object MockQuotes { val quote1 = Quote( - currencyId = MockTokens.token1.id, + rawCurrencyId = MockTokens.token1.id.rawCurrencyId!!, fiatRate = BigDecimal("1.23"), priceChange = BigDecimal("0.01"), ) val quote2 = Quote( - currencyId = MockTokens.token2.id, + rawCurrencyId = MockTokens.token2.id.rawCurrencyId!!, fiatRate = BigDecimal("2.34"), priceChange = BigDecimal("-0.02"), ) val quote3 = Quote( - currencyId = MockTokens.token3.id, + rawCurrencyId = MockTokens.token3.id.rawCurrencyId!!, fiatRate = BigDecimal("3.45"), priceChange = BigDecimal("0.03"), ) val quote4 = Quote( - currencyId = MockTokens.token4.id, + rawCurrencyId = MockTokens.token4.id.rawCurrencyId!!, fiatRate = BigDecimal("4.56"), priceChange = BigDecimal("-0.04"), ) val quote5 = Quote( - currencyId = MockTokens.token5.id, + rawCurrencyId = MockTokens.token5.id.rawCurrencyId!!, fiatRate = BigDecimal("5.67"), priceChange = BigDecimal("0.05"), ) val quote6 = Quote( - currencyId = MockTokens.token6.id, + rawCurrencyId = MockTokens.token6.id.rawCurrencyId!!, fiatRate = BigDecimal("6.78"), priceChange = BigDecimal("-0.06"), ) val quote7 = Quote( - currencyId = MockTokens.token7.id, + rawCurrencyId = MockTokens.token7.id.rawCurrencyId!!, fiatRate = BigDecimal("7.89"), priceChange = BigDecimal("0.07"), ) val quote8 = Quote( - currencyId = MockTokens.token8.id, + rawCurrencyId = MockTokens.token8.id.rawCurrencyId!!, fiatRate = BigDecimal("8.90"), priceChange = BigDecimal("-0.08"), ) val quote9 = Quote( - currencyId = MockTokens.token9.id, + rawCurrencyId = MockTokens.token9.id.rawCurrencyId!!, fiatRate = BigDecimal("9.01"), priceChange = BigDecimal("0.09"), ) val quote10 = Quote( - currencyId = MockTokens.token10.id, + rawCurrencyId = MockTokens.token10.id.rawCurrencyId!!, fiatRate = BigDecimal("10.12"), priceChange = BigDecimal("-0.10"), ) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt index c5e38d7101..7e22b04347 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt @@ -1,12 +1,13 @@ package com.tangem.domain.tokens.mock import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.CryptoCurrency.ID internal object MockTokens { val token1 get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token1"), + id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token1")), networkId = MockNetworks.network1.id, name = "Token 1", symbol = "T1", @@ -16,7 +17,7 @@ internal object MockTokens { ) val token2 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token2"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token2")), networkId = MockNetworks.network1.id, name = "Token 2", symbol = "T2", @@ -28,7 +29,7 @@ internal object MockTokens { ) val token3 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token3"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network1.id, ID.Suffix.RawID("token3")), networkId = MockNetworks.network1.id, name = "Token 3", symbol = "T3", @@ -40,7 +41,7 @@ internal object MockTokens { ) val token4 get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token4"), + id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token4")), networkId = MockNetworks.network2.id, name = "Token 4", symbol = "T4", @@ -50,7 +51,7 @@ internal object MockTokens { ) val token5 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token5"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token5")), networkId = MockNetworks.network2.id, name = "Token 5", symbol = "T5", @@ -62,7 +63,7 @@ internal object MockTokens { ) val token6 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token6"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network2.id, ID.Suffix.RawID("token6")), networkId = MockNetworks.network2.id, name = "Token 6", symbol = "T6", @@ -74,7 +75,7 @@ internal object MockTokens { ) val token7 get() = CryptoCurrency.Coin( - id = CryptoCurrency.ID("token7"), + id = ID(ID.Prefix.COIN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token7")), networkId = MockNetworks.network3.id, name = "Token 7", symbol = "T7", @@ -84,7 +85,7 @@ internal object MockTokens { ) val token8 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token8"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token8")), networkId = MockNetworks.network3.id, name = "Token 8", symbol = "T8", @@ -96,7 +97,7 @@ internal object MockTokens { ) val token9 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token9"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token9")), networkId = MockNetworks.network3.id, name = "Token 9", symbol = "T9", @@ -108,7 +109,7 @@ internal object MockTokens { ) val token10 get() = CryptoCurrency.Token( - id = CryptoCurrency.ID("token10"), + id = ID(ID.Prefix.TOKEN_PREFIX, MockNetworks.network3.id, ID.Suffix.RawID("token10")), networkId = MockNetworks.network3.id, name = "Token 10", symbol = "T10", diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt index 93acd79dc2..4dd2d81154 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt @@ -74,7 +74,7 @@ internal object MockTokensStates { val networkStatus = MockNetworks.verifiedNetworksStatuses .first { it.networkId == status.currency.networkId } val amount = (networkStatus.value as NetworkStatus.Verified).amounts[status.currency.id]!! - val quote = MockQuotes.quotes.first { it.currencyId == status.currency.id } + val quote = MockQuotes.quotes.first { it.rawCurrencyId == status.currency.id.rawCurrencyId } val fiatAmount = amount * quote.fiatRate status.copy( From 300179445d298fbffda8e729f9f523c7c115d5d7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 15:09:20 +0800 Subject: [PATCH 23/52] Updated on 2026-08-14 --- .../tap/di/domain/TokensDomainModule.kt | 13 +- .../marketprice/MarketPriceBlock.kt | 168 ++++++++++++------ .../marketprice/MarketPriceBlockState.kt | 2 + .../repository/DefaultNetworksRepository.kt | 20 ++- .../tokens/utils/CardCurrenciesFactory.kt | 2 +- .../wallet/state/WalletLockedState.kt | 8 + ...letSingleCurrencyLoadedBalanceConverter.kt | 120 +++++++++++++ .../state/factory/WalletStateFactory.kt | 16 ++ .../wallet/ui/components/common/WalletCard.kt | 152 ++++++++-------- .../wallet/viewmodels/JobHolder.kt | 4 +- .../wallet/viewmodels/WalletViewModel.kt | 62 ++++--- 11 files changed, 417 insertions(+), 150 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt diff --git a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt index 274f5bbe46..d5ab303555 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/TokensDomainModule.kt @@ -28,7 +28,7 @@ internal object TokensDomainModule { @Provides @ViewModelScoped - fun provideGetPrimaryCurrencyUseCase( + fun provideGetCurrencyUseCase( currenciesRepository: CurrenciesRepository, quotesRepository: QuotesRepository, networksRepository: NetworksRepository, @@ -37,6 +37,17 @@ internal object TokensDomainModule { return GetCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) } + @Provides + @ViewModelScoped + fun provideGetPrimaryCurrencyUseCase( + currenciesRepository: CurrenciesRepository, + quotesRepository: QuotesRepository, + networksRepository: NetworksRepository, + dispatchers: CoroutineDispatcherProvider, + ): GetPrimaryCurrencyUseCase { + return GetPrimaryCurrencyUseCase(currenciesRepository, quotesRepository, networksRepository, dispatchers) + } + @Provides @ViewModelScoped fun provideToggleTokenListGroupingUseCase( diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt index d7f217308c..4192706627 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlock.kt @@ -1,5 +1,7 @@ package com.tangem.core.ui.components.marketprice +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -20,15 +22,22 @@ import androidx.compose.ui.unit.Dp import com.tangem.core.ui.R import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.BigDecimalFormatter /** - * @see Figma component */ @Composable fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) { var rootWidth by remember { mutableStateOf(value = 0) } + Column( modifier = modifier .background( @@ -42,81 +51,139 @@ fun MarketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing6), horizontalAlignment = Alignment.Start, ) { - Text( - text = stringResource(id = R.string.wallet_marketplace_block_title, state.currencyName), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.subtitle2, - ) + Title(currencyName = state.currencyName) - when (state) { - is MarketPriceBlockState.Loading -> { - RectangleShimmer( - modifier = Modifier.size(width = TangemTheme.dimens.size158, height = TangemTheme.dimens.size20), - ) - } - is MarketPriceBlockState.Content -> { - Price( - config = state, + Content(state = state, rootWidth = rootWidth) + } +} + +@Composable +private fun Title(currencyName: String) { + Text( + text = stringResource(id = R.string.wallet_marketplace_block_title, currencyName), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.subtitle2, + ) +} + +@OptIn(ExperimentalAnimationApi::class) +@Composable +private fun Content(state: MarketPriceBlockState, rootWidth: Int) { + AnimatedContent(targetState = state, label = "Update the content") { marketPriceBlockState -> + when (marketPriceBlockState) { + is MarketPriceBlockState.Content, + is MarketPriceBlockState.Error, + -> { + PriceContent( + state = marketPriceBlockState, priceWidthDp = with(LocalDensity.current) { rootWidth.div(other = 2).toDp() }, ) } + is MarketPriceBlockState.Loading -> LoadingContent() } } } @Composable -private fun Price(config: MarketPriceBlockState.Content, priceWidthDp: Dp) { +private fun PriceContent(state: MarketPriceBlockState, priceWidthDp: Dp) { Row( - modifier = Modifier, verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8), ) { - Text( - text = config.price, - modifier = Modifier.widthIn(max = priceWidthDp), - color = TangemTheme.colors.text.primary1, - overflow = TextOverflow.Ellipsis, - maxLines = 1, - style = TangemTheme.typography.body2, - ) + PriceBlock(state = state, priceWidthDp = priceWidthDp) - PriceChangeInPercent(config.priceChangeConfig) + QuoteTimeStatus() + } +} - Text( - text = stringResource(id = R.string.wallet_marketprice_block_update_time), - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.body2, - ) +@OptIn(ExperimentalAnimationApi::class) +@Composable +private fun PriceBlock(state: MarketPriceBlockState, priceWidthDp: Dp) { + val priceModifier = Modifier.widthIn(max = priceWidthDp) + AnimatedContent(targetState = state, label = "Update the price block") { marketPriceBlockState -> + if (marketPriceBlockState is MarketPriceBlockState.Content) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8), + ) { + Price(price = marketPriceBlockState.price, modifier = priceModifier) + + PriceChangeInPercent(marketPriceBlockState.priceChangeConfig) + } + } else { + Price(price = BigDecimalFormatter.EMPTY_BALANCE_SIGN, modifier = priceModifier) + } } } +@Composable +private fun Price(price: String, modifier: Modifier = Modifier) { + Text( + text = price, + modifier = modifier, + color = TangemTheme.colors.text.primary1, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = TangemTheme.typography.body2, + ) +} + +@OptIn(ExperimentalAnimationApi::class) @Composable private fun PriceChangeInPercent(config: PriceChangeConfig) { + AnimatedContent(targetState = config.type, label = "Update price change") { type -> + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), + ) { + Image( + painter = painterResource( + id = when (type) { + PriceChangeConfig.Type.UP -> R.drawable.img_arrow_up_8 + PriceChangeConfig.Type.DOWN -> R.drawable.img_arrow_down_8 + }, + ), + contentDescription = null, + ) + + Text( + text = config.valueInPercent, + color = when (type) { + PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent + PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning + }, + style = TangemTheme.typography.body2, + ) + } + } +} + +@Composable +private fun LoadingContent() { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing4), + horizontalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing8), ) { - Image( - painter = painterResource( - id = when (config.type) { - PriceChangeConfig.Type.UP -> R.drawable.img_arrow_up_8 - PriceChangeConfig.Type.DOWN -> R.drawable.img_arrow_down_8 - }, + RectangleShimmer( + modifier = Modifier.size( + width = TangemTheme.dimens.size158, + height = TangemTheme.dimens.size20, ), - contentDescription = null, ) - Text( - text = config.valueInPercent, - color = when (config.type) { - PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent - PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning - }, - style = TangemTheme.typography.body2, - ) + QuoteTimeStatus() } } +@Composable +private fun QuoteTimeStatus() { + Text( + text = stringResource(id = R.string.wallet_marketprice_block_update_time), + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + ) +} + @Preview @Composable private fun Preview_MarketPriceBlock_Light( @@ -143,7 +210,7 @@ private class WalletMarketPriceBlockStateProvider : CollectionPreviewParameterPr collection = listOf( MarketPriceBlockState.Content( currencyName = "BTC", - price = "98900", + price = "98900 $", priceChangeConfig = PriceChangeConfig( valueInPercent = "5.16%", type = PriceChangeConfig.Type.DOWN, @@ -151,12 +218,13 @@ private class WalletMarketPriceBlockStateProvider : CollectionPreviewParameterPr ), MarketPriceBlockState.Content( currencyName = "BTC", - price = "98900", + price = "98900 $", priceChangeConfig = PriceChangeConfig( valueInPercent = "10.89%", type = PriceChangeConfig.Type.UP, ), ), MarketPriceBlockState.Loading(currencyName = "BTC"), + MarketPriceBlockState.Error(currencyName = "BTC"), ), ) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlockState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlockState.kt index ae59401aa9..30b652ea68 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlockState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/marketprice/MarketPriceBlockState.kt @@ -7,6 +7,8 @@ sealed interface MarketPriceBlockState { val currencyName: String + data class Error(override val currencyName: String) : MarketPriceBlockState + data class Loading(override val currencyName: String) : MarketPriceBlockState data class Content( diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt index d70869c7b8..e9e235c8d1 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultNetworksRepository.kt @@ -1,6 +1,7 @@ package com.tangem.data.tokens.repository import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.utils.CardCurrenciesFactory import com.tangem.data.tokens.utils.NetworkConverter import com.tangem.data.tokens.utils.NetworkStatusFactory import com.tangem.data.tokens.utils.ResponseCurrenciesFactory @@ -32,8 +33,10 @@ internal class DefaultNetworksRepository( private val dispatchers: CoroutineDispatcherProvider, ) : NetworksRepository { + private val demoConfig by lazy { DemoConfig() } private val networkConverter by lazy { NetworkConverter() } - private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(DemoConfig()) } + private val cardCurrenciesFactory by lazy { CardCurrenciesFactory(demoConfig) } + private val responseCurrenciesFactory by lazy { ResponseCurrenciesFactory(demoConfig) } private val networkStatusFactory by lazy { NetworkStatusFactory() } private val networksStatuses: MutableStateFlow> = MutableStateFlow(emptyList()) @@ -107,11 +110,18 @@ internal class DefaultNetworksRepository( val userWallet = requireNotNull(userWalletsStore.getSyncOrNull(userWalletId)) { "Unable to find user wallet with provided ID: $userWalletId" } - val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { - "Unable to find tokens response for user wallet with provided ID: $userWalletId" - } - return responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse.card) + return if (userWallet.isMultiCurrency) { + val response = requireNotNull(userTokensStore.getSyncOrNull(userWalletId)) { + "Unable to find tokens response for user wallet with provided ID: $userWalletId" + } + + responseCurrenciesFactory.createCurrencies(response, userWallet.scanResponse.card) + } else { + val currency = cardCurrenciesFactory.createPrimaryCurrencyForSingleCurrencyCard(userWallet.scanResponse) + + listOf(currency) + } } private fun getNetworksStatusesCacheKey(userWalletId: UserWalletId): String = "network_status_$userWalletId" diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt index 8d2ac0b3be..9a5858f0dc 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt @@ -61,7 +61,7 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { } private fun createCoin(blockchain: Blockchain, card: CardDTO): CryptoCurrency.Coin? { - if (blockchain != Blockchain.Unknown) { + if (blockchain == Blockchain.Unknown) { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt index 352b0687a2..dd3bbbeee7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletLockedState.kt @@ -21,4 +21,12 @@ internal sealed interface WalletLockedState { /** Lambda be invoked when bottom sheet is dismissed */ val onBottomSheetDismiss: () -> Unit + + /** Get selected wallet index */ + fun getSelectedWalletIndex(): Int { + return when (this) { + is WalletMultiCurrencyState.Locked -> walletsListConfig.selectedWalletIndex + is WalletSingleCurrencyState.Locked -> walletsListConfig.selectedWalletIndex + } + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt new file mode 100644 index 0000000000..6087b1f2fa --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -0,0 +1,120 @@ +package com.tangem.feature.wallet.presentation.wallet.state.factory + +import arrow.core.Either +import com.tangem.common.Provider +import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.marketprice.PriceChangeConfig +import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.tokens.error.CurrencyError +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState +import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletsListConfig +import com.tangem.utils.converter.Converter +import kotlinx.collections.immutable.toPersistentList +import java.math.BigDecimal + +internal class WalletSingleCurrencyLoadedBalanceConverter( + private val currentStateProvider: Provider, + private val fiatCurrencyCode: String, + private val fiatCurrencySymbol: String, +) : Converter, WalletSingleCurrencyState.Content> { + + override fun convert(value: Either): WalletSingleCurrencyState.Content { + return value.fold(ifLeft = { convertError() }, ifRight = ::convert) + } + + private fun convertError(): WalletSingleCurrencyState.Content { + return requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) + } + + private fun convert(status: CryptoCurrencyStatus): WalletSingleCurrencyState.Content { + val state = requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) + val currencyName = state.marketPriceBlockState.currencyName + return state.copy( + walletsListConfig = getUpdatedSelectedWallet(status.value, state), + marketPriceBlockState = getMarketPriceState(status = status.value, currencyName = currencyName), + ) + } + + private fun getMarketPriceState(status: CryptoCurrencyStatus.Status, currencyName: String): MarketPriceBlockState { + return when (status) { + is CryptoCurrencyStatus.Loaded -> MarketPriceBlockState.Content( + currencyName = currencyName, + price = BigDecimalFormatter.formatFiatAmount( + fiatAmount = status.fiatRate, + fiatCurrencyCode = fiatCurrencyCode, + fiatCurrencySymbol = fiatCurrencySymbol, + ), + priceChangeConfig = PriceChangeConfig( + valueInPercent = BigDecimalFormatter.formatPercent( + percent = status.priceChange, + useAbsoluteValue = true, + ), + type = if (status.priceChange > BigDecimal.ZERO) { + PriceChangeConfig.Type.UP + } else { + PriceChangeConfig.Type.DOWN + }, + ), + ) + is CryptoCurrencyStatus.Loading -> MarketPriceBlockState.Loading(currencyName) + is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.NoAccount, + is CryptoCurrencyStatus.Unreachable, + -> MarketPriceBlockState.Error(currencyName) + } + } + + private fun getUpdatedSelectedWallet( + status: CryptoCurrencyStatus.Status, + state: WalletSingleCurrencyState, + ): WalletsListConfig { + val selectedWallet = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex] + val updatedWallet = when (status) { + is CryptoCurrencyStatus.Loaded -> { + WalletCardState.Content( + id = selectedWallet.id, + title = selectedWallet.title, + additionalInfo = selectedWallet.additionalInfo, + imageResId = selectedWallet.imageResId, + onClick = selectedWallet.onClick, + balance = BigDecimalFormatter.formatFiatAmount( + fiatAmount = status.fiatAmount, + fiatCurrencyCode = fiatCurrencyCode, + fiatCurrencySymbol = fiatCurrencySymbol, + ), + ) + } + is CryptoCurrencyStatus.Loading -> { + WalletCardState.Loading( + id = selectedWallet.id, + title = selectedWallet.title, + additionalInfo = selectedWallet.additionalInfo, + imageResId = selectedWallet.imageResId, + onClick = selectedWallet.onClick, + ) + } + is CryptoCurrencyStatus.MissedDerivation, + is CryptoCurrencyStatus.NoAccount, + is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.Unreachable, + -> { + WalletCardState.Error( + id = selectedWallet.id, + title = selectedWallet.title, + additionalInfo = selectedWallet.additionalInfo, + imageResId = selectedWallet.imageResId, + onClick = selectedWallet.onClick, + ) + } + } + + return state.walletsListConfig.copy( + wallets = state.walletsListConfig.wallets.toPersistentList() + .set(index = state.walletsListConfig.selectedWalletIndex, element = updatedWallet), + ) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 0f4d3bd755..c1ab20757d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -4,7 +4,9 @@ import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider import com.tangem.domain.common.CardTypesResolver +import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.error.TokenListError +import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.txhistory.models.TxHistoryItem import com.tangem.domain.txhistory.models.TxHistoryListError @@ -64,6 +66,14 @@ internal class WalletStateFactory( ) } + private val singleCurrencyLoadedBalanceConverter by lazy { + WalletSingleCurrencyLoadedBalanceConverter( + currentStateProvider = currentStateProvider, + fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA] + fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA] + ) + } + fun getInitialState(): WalletState = WalletState.Initial(onBackClick = clickIntents::onBackClick) fun getSkeletonState(wallets: List, selectedWalletIndex: Int): WalletState { @@ -189,4 +199,10 @@ internal class WalletStateFactory( WalletManageButton.CopyAddress(onClick = {}), ) } + + fun getSingleCurrencyLoadedBalanceState( + cryptoCurrencyEither: Either, + ): WalletState { + return singleCurrencyLoadedBalanceConverter.convert(cryptoCurrencyEither) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt index fd0498866a..ccff84d714 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletCard.kt @@ -1,6 +1,9 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.common import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* import androidx.compose.material3.Icon @@ -20,6 +23,7 @@ import com.tangem.core.ui.components.FontSizeRange import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.ResizableText import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.BigDecimalFormatter import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState @@ -64,85 +68,92 @@ internal fun WalletCard(state: WalletCardState, modifier: Modifier = Modifier) { } val imageWidth = TangemTheme.dimens.size120 - state.imageResId?.let { - WalletImage( - id = it, - modifier = Modifier.constrainAs(imageItem) { - centerVerticallyTo(parent) - top.linkTo(parent.top) - end.linkTo(parent.end) - height = Dimension.fillToConstraints - width = Dimension.value(imageWidth) - }, - ) - } + WalletImage( + id = state.imageResId, + modifier = Modifier.constrainAs(imageItem) { + centerVerticallyTo(parent) + top.linkTo(parent.top) + end.linkTo(parent.end) + height = Dimension.fillToConstraints + width = Dimension.value(imageWidth) + }, + ) } } } +@OptIn(ExperimentalAnimationApi::class) @Composable private fun Title(state: WalletCardState) { - when (state) { - is WalletCardState.HiddenContent -> { - Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) { + AnimatedContent(targetState = state, label = "Update the title") { + when (it) { + is WalletCardState.HiddenContent -> { + Row(horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing4)) { + Text( + text = it.title, + color = TangemTheme.colors.text.tertiary, + style = TangemTheme.typography.body2, + maxLines = 1, + ) + Icon( + modifier = Modifier.size(size = TangemTheme.dimens.size20), + painter = painterResource(id = R.drawable.ic_eye_off_24), + contentDescription = null, + tint = TangemTheme.colors.icon.informative, + ) + } + } + is WalletCardState.Content, + is WalletCardState.Error, + is WalletCardState.Loading, + -> { Text( - text = state.title, + text = it.title, color = TangemTheme.colors.text.tertiary, style = TangemTheme.typography.body2, maxLines = 1, ) - Icon( - modifier = Modifier.size(size = TangemTheme.dimens.size20), - painter = painterResource(id = R.drawable.ic_eye_off_24), - contentDescription = null, - tint = TangemTheme.colors.icon.informative, - ) } } - else -> { - Text( - text = state.title, - color = TangemTheme.colors.text.tertiary, - style = TangemTheme.typography.body2, - maxLines = 1, - ) - } } } +@OptIn(ExperimentalAnimationApi::class) @Composable private fun Balance(state: WalletCardState) { - when (state) { - is WalletCardState.Content -> { - ResizableText( - text = state.balance, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.h2, - fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize), - modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32), - ) - } - is WalletCardState.Loading -> { - RectangleShimmer( - modifier = Modifier.size( - width = TangemTheme.dimens.size102, - height = TangemTheme.dimens.size24, - ), - ) - } - is WalletCardState.HiddenContent -> { - Text( - text = DOTS, - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.h2, - ) - } - is WalletCardState.Error -> { - Text( - text = "—", - color = TangemTheme.colors.text.primary1, - style = TangemTheme.typography.h2, - ) + AnimatedContent(targetState = state, label = "Update the balance") { + when (it) { + is WalletCardState.Content -> { + ResizableText( + text = it.balance, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.h2, + fontSizeRange = FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize), + modifier = Modifier.defaultMinSize(minHeight = TangemTheme.dimens.size32), + ) + } + is WalletCardState.Loading -> { + RectangleShimmer( + modifier = Modifier.size( + width = TangemTheme.dimens.size102, + height = TangemTheme.dimens.size24, + ), + ) + } + is WalletCardState.HiddenContent -> { + Text( + text = DOTS, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.h2, + ) + } + is WalletCardState.Error -> { + Text( + text = BigDecimalFormatter.EMPTY_BALANCE_SIGN, + color = TangemTheme.colors.text.primary1, + style = TangemTheme.typography.h2, + ) + } } } } @@ -157,22 +168,23 @@ private fun AdditionalInfo(description: String) { } @Composable -private fun WalletImage(@DrawableRes id: Int, modifier: Modifier = Modifier) { - Image( - painter = painterResource(id), - contentDescription = null, - modifier = modifier, - contentScale = ContentScale.FillWidth, - ) +private fun WalletImage(@DrawableRes id: Int?, modifier: Modifier = Modifier) { + AnimatedVisibility(visible = id != null, modifier = modifier) { + Image( + painter = painterResource(id = requireNotNull(id)), + contentDescription = null, + contentScale = ContentScale.FillWidth, + ) + } } // region Preview -@Preview +@Preview(widthDp = 360, heightDp = 360) @Composable private fun Preview_WalletCard_LightTheme(@PreviewParameter(WalletCardStateProvider::class) state: WalletCardState) { TangemTheme(isDark = false) { - WalletCard(state) + WalletCard(state = state, modifier = Modifier.fillMaxWidth()) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt index 94c0e30ade..6414445119 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt @@ -11,8 +11,8 @@ internal class JobHolder { private var job: Job? = null - /** Update current job */ - fun update(job: Job) { + /** Update current [job] */ + fun update(job: Job?) { this.job?.cancel() this.job = job } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index bc50b0d051..eb8e6247e1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -5,6 +5,8 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.* import androidx.paging.cachedIn +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.DerivationStyle import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess @@ -14,6 +16,7 @@ import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase +import com.tangem.domain.tokens.GetPrimaryCurrencyUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.models.Network @@ -21,6 +24,7 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.models.UserWallet +import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.* import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState @@ -55,6 +59,7 @@ internal class WalletViewModel @Inject constructor( private val setAccessCodeRequestPolicyUseCase: SetAccessCodeRequestPolicyUseCase, private val getAccessCodeSavingStatusUseCase: GetAccessCodeSavingStatusUseCase, private val getTokenListUseCase: GetTokenListUseCase, + private val getPrimaryCurrencyUseCase: GetPrimaryCurrencyUseCase, private val getCardWasScannedUseCase: GetCardWasScannedUseCase, private val isUserAlreadyRateAppUseCase: IsUserAlreadyRateAppUseCase, private val isDemoCardUseCase: IsDemoCardUseCase, @@ -97,6 +102,7 @@ internal class WalletViewModel @Inject constructor( private var wallets: List by Delegates.notNull() private val tokensJobHolder = JobHolder() + private val marketPriceJobHolder = JobHolder() private val notificationsJobHolder = JobHolder() override fun onCreate(owner: LifecycleOwner) { @@ -115,10 +121,7 @@ internal class WalletViewModel @Inject constructor( val currentState = uiState val selectedWalletIndex = if (currentState is WalletLockedState) { - when (currentState) { - is WalletMultiCurrencyState.Locked -> currentState.walletsListConfig.selectedWalletIndex - is WalletSingleCurrencyState.Locked -> currentState.walletsListConfig.selectedWalletIndex - } + currentState.getSelectedWalletIndex() } else { val selectedWallet = getSelectedWalletUseCase().fold( ifLeft = { error("Selected wallet is null") }, @@ -135,12 +138,12 @@ internal class WalletViewModel @Inject constructor( val cardTypeResolver = getCardTypeResolver(index) when { getWallet(index).isLocked -> uiState = stateFactory.getLockedState() - cardTypeResolver.isMultiwalletAllowed() -> updateByTokensList(index, isRefreshing) - !cardTypeResolver.isMultiwalletAllowed() -> updateByTxHistory(index) + cardTypeResolver.isMultiwalletAllowed() -> updateMultiCurrencyContent(index, isRefreshing) + !cardTypeResolver.isMultiwalletAllowed() -> updateSingleCurrencyContent(index) } } - private fun updateByTokensList(index: Int, isRefreshing: Boolean = false) { + private fun updateMultiCurrencyContent(index: Int, isRefreshing: Boolean = false) { val state = requireNotNull(uiState as? WalletMultiCurrencyState) { "Impossible to update tokens list if state isn't WalletMultiCurrencyState" } @@ -163,11 +166,19 @@ internal class WalletViewModel @Inject constructor( .saveIn(tokensJobHolder) } - private fun updateByTxHistory(index: Int) { + private fun updateSingleCurrencyContent(index: Int) { + val wallet = getWallet(index) + updateTxHistory( + blockchain = getCardTypeResolver(index).getBlockchain(), + derivationStyle = wallet.scanResponse.card.derivationStyle, + ) + updateMarketPrice(userWalletId = wallet.walletId) + updateNotifications(index) + } + + private fun updateTxHistory(blockchain: Blockchain, derivationStyle: DerivationStyle?) { viewModelScope.launch(dispatchers.io) { - val wallet = getWallet(index) - val blockchain = getCardTypeResolver(index).getBlockchain() - val derivationPath = blockchain.derivationPath(style = wallet.scanResponse.card.derivationStyle)?.rawPath + val derivationPath = blockchain.derivationPath(style = derivationStyle)?.rawPath val txHistoryItemsCountEither = txHistoryItemsCountUseCase( networkId = Network.ID(blockchain.id), @@ -177,20 +188,25 @@ internal class WalletViewModel @Inject constructor( uiState = stateFactory.getLoadingTxHistoryState(itemsCountEither = txHistoryItemsCountEither) txHistoryItemsCountEither.onRight { - updateTxHistory( - networkId = Network.ID(blockchain.id), - derivationPath = derivationPath, + uiState = stateFactory.getLoadedTxHistoryState( + txHistoryEither = txHistoryItemsUseCase( + networkId = Network.ID(blockchain.id), + derivationPath = derivationPath, + ).map { + it.cachedIn(viewModelScope) + }, ) } } - - updateNotifications(index) } - private fun updateTxHistory(networkId: Network.ID, derivationPath: String?) { - uiState = stateFactory.getLoadedTxHistoryState( - txHistoryEither = txHistoryItemsUseCase(networkId, derivationPath).map { it.cachedIn(viewModelScope) }, - ) + private fun updateMarketPrice(userWalletId: UserWalletId) { + getPrimaryCurrencyUseCase(userWalletId = userWalletId) + .distinctUntilChanged() + .onEach { uiState = stateFactory.getSingleCurrencyLoadedBalanceState(cryptoCurrencyEither = it) } + .flowOn(dispatchers.io) + .launchIn(viewModelScope) + .saveIn(marketPriceJobHolder) } private fun updateNotifications(index: Int, tokenList: TokenList? = null) { @@ -300,6 +316,10 @@ internal class WalletViewModel @Inject constructor( if (state.walletsListConfig.selectedWalletIndex == index) return + tokensJobHolder.update(job = null) + marketPriceJobHolder.update(job = null) + notificationsJobHolder.update(job = null) + uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index) updateContentItems(index = index) @@ -328,7 +348,7 @@ internal class WalletViewModel @Inject constructor( override fun onReloadClick() { uiState = stateFactory.getStateAfterContentRefreshing() - updateByTxHistory( + updateSingleCurrencyContent( index = requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex, ) } From 9c524c81131e1f17b1e6e5c0fa8602b621dd4d34 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 17:30:05 +0800 Subject: [PATCH 24/52] Updated on 2026-08-14 --- .../wallet/domain/WalletImageResolver.kt | 2 +- .../presentation/wallet/state/WalletState.kt | 14 +++++ ...letSingleCurrencyLoadedBalanceConverter.kt | 11 +++- .../state/factory/WalletStateFactory.kt | 1 + .../wallet/viewmodels/WalletStateCache.kt | 22 ++++++++ .../wallet/viewmodels/WalletStateHolder.kt | 40 ++++++++++++++ .../viewmodels/WalletStateHolderDelegate.kt | 20 +++++++ .../wallet/viewmodels/WalletViewModel.kt | 54 ++++++++++++++++--- 8 files changed, 153 insertions(+), 11 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletImageResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletImageResolver.kt index 2dd09d7535..c54155dd33 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletImageResolver.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/domain/WalletImageResolver.kt @@ -42,7 +42,7 @@ internal object WalletImageResolver { return when (cardTypesResolver.getBlockchain()) { Blockchain.Bitcoin -> R.drawable.ill_note_btc_120_106 Blockchain.Ethereum -> R.drawable.ill_note_ethereum_120_106 - Blockchain.Binance -> R.drawable.ill_note_binance_120_106 + Blockchain.BSC -> R.drawable.ill_note_binance_120_106 Blockchain.Dogecoin -> R.drawable.ill_note_doge_120_106 Blockchain.Cardano -> R.drawable.ill_note_cardano_120_106 Blockchain.XRP -> R.drawable.ill_note_xrp_120_106 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt index 570accc073..9cc1f3fa27 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/WalletState.kt @@ -30,6 +30,20 @@ internal sealed class WalletState { /** Bottom sheet config */ abstract val bottomSheetConfig: WalletBottomSheetConfig? + + /** + * Util function that allow to make a copy + * + * @param walletsListConfig wallets list config + */ + fun copySealed(walletsListConfig: WalletsListConfig = this.walletsListConfig): ContentState { + return when (this) { + is WalletMultiCurrencyState.Content -> copy(walletsListConfig = walletsListConfig) + is WalletMultiCurrencyState.Locked -> copy(walletsListConfig = walletsListConfig) + is WalletSingleCurrencyState.Content -> copy(walletsListConfig = walletsListConfig) + is WalletSingleCurrencyState.Locked -> copy(walletsListConfig = walletsListConfig) + } + } } /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index 6087b1f2fa..143c38fdb4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -5,8 +5,10 @@ import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState @@ -17,6 +19,7 @@ import java.math.BigDecimal internal class WalletSingleCurrencyLoadedBalanceConverter( private val currentStateProvider: Provider, + private val cardTypeResolverProvider: Provider, private val fiatCurrencyCode: String, private val fiatCurrencySymbol: String, ) : Converter, WalletSingleCurrencyState.Content> { @@ -33,7 +36,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( val state = requireNotNull(currentStateProvider() as? WalletSingleCurrencyState.Content) val currencyName = state.marketPriceBlockState.currencyName return state.copy( - walletsListConfig = getUpdatedSelectedWallet(status.value, state), + walletsListConfig = getUpdatedSelectedWallet(status = status.value, state = state), marketPriceBlockState = getMarketPriceState(status = status.value, currencyName = currencyName), ) } @@ -78,7 +81,11 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( WalletCardState.Content( id = selectedWallet.id, title = selectedWallet.title, - additionalInfo = selectedWallet.additionalInfo, + additionalInfo = WalletAdditionalInfoFactory.resolve( + cardTypesResolver = cardTypeResolverProvider(), + isLocked = false, + currencyAmount = status.amount, + ), imageResId = selectedWallet.imageResId, onClick = selectedWallet.onClick, balance = BigDecimalFormatter.formatFiatAmount( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index c1ab20757d..80436dcaa8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -69,6 +69,7 @@ internal class WalletStateFactory( private val singleCurrencyLoadedBalanceConverter by lazy { WalletSingleCurrencyLoadedBalanceConverter( currentStateProvider = currentStateProvider, + cardTypeResolverProvider = currentCardTypeResolverProvider, fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA] fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA] ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt new file mode 100644 index 0000000000..c5d4e10424 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateCache.kt @@ -0,0 +1,22 @@ +package com.tangem.feature.wallet.presentation.wallet.viewmodels + +import com.tangem.domain.wallets.models.UserWalletId +import com.tangem.feature.wallet.presentation.wallet.state.WalletState + +/** + * Wallet state cache. It allows to switch the wallets without additional loading like a PagerView. + * +[REDACTED_AUTHOR] + */ +internal object WalletStateCache { + + private val states = mutableMapOf() + + /** Get state by [userWalletId] */ + fun getState(userWalletId: UserWalletId): WalletState? = states[userWalletId] + + /** Add or update [state] by [userWalletId] */ + fun update(userWalletId: UserWalletId, state: WalletState) { + states[userWalletId] = state + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt new file mode 100644 index 0000000000..59c1f39f13 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolder.kt @@ -0,0 +1,40 @@ +package com.tangem.feature.wallet.presentation.wallet.viewmodels + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import com.tangem.feature.wallet.presentation.wallet.state.WalletState + +/** + * Wallet state holder + * + * @param initialState initial ui state + * +[REDACTED_AUTHOR] + */ +internal class WalletStateHolder(initialState: WalletState) { + + /** Screen state */ + var uiState: WalletState by mutableStateOf(initialState) + private set + + /** Set screen [state] */ + fun setState(state: WalletState) { + when (state) { + is WalletState.ContentState -> { + cache(state = state) + + uiState = state + } + is WalletState.Initial -> Unit + } + } + + /** Cache [state] [WalletState.ContentState] to [WalletStateCache] */ + private fun cache(state: WalletState.ContentState) { + val selectedWalletIndex = state.walletsListConfig.selectedWalletIndex + val selectedWalletId = state.walletsListConfig.wallets[selectedWalletIndex].id + + WalletStateCache.update(userWalletId = selectedWalletId, state = state) + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt new file mode 100644 index 0000000000..25c3d12698 --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletStateHolderDelegate.kt @@ -0,0 +1,20 @@ +package com.tangem.feature.wallet.presentation.wallet.viewmodels + +import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty + +internal class WalletStateHolderDelegate( + private val uiStateHolder: WalletStateHolder, +) : ReadWriteProperty { + + override fun getValue(thisRef: Any?, property: KProperty<*>): WalletState = uiStateHolder.uiState + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: WalletState) { + uiStateHolder.setState(value) + } +} + +internal fun uiStateHolder(initialState: WalletState): ReadWriteProperty { + return WalletStateHolderDelegate(uiStateHolder = WalletStateHolder(initialState = initialState)) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index eb8e6247e1..06ca0ab8c5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -1,8 +1,5 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.lifecycle.* import androidx.paging.cachedIn import com.tangem.blockchain.common.Blockchain @@ -10,6 +7,8 @@ import com.tangem.blockchain.common.DerivationStyle import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess +import com.tangem.core.ui.components.marketprice.MarketPriceBlockState +import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.card.* import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.TapWorkarounds.derivationStyle @@ -26,12 +25,15 @@ import com.tangem.domain.userwallets.UserWalletBuilder import com.tangem.domain.wallets.models.UserWallet import com.tangem.domain.wallets.models.UserWalletId import com.tangem.domain.wallets.usecase.* +import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.wallet.state.WalletLockedState import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState import com.tangem.feature.wallet.presentation.wallet.state.components.WalletBottomSheetConfig +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCardState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.hilt.android.lifecycle.HiltViewModel @@ -48,7 +50,7 @@ import kotlin.properties.Delegates * [REDACTED_AUTHOR] */ -@Suppress("LongParameterList", "TooManyFunctions") +@Suppress("LargeClass", "LongParameterList", "TooManyFunctions") @HiltViewModel internal class WalletViewModel @Inject constructor( private val getWalletsUseCase: GetWalletsUseCase, @@ -96,8 +98,7 @@ internal class WalletViewModel @Inject constructor( ) /** Screen state */ - var uiState: WalletState by mutableStateOf(stateFactory.getInitialState()) - private set + var uiState: WalletState by uiStateHolder(initialState = stateFactory.getInitialState()) private var wallets: List by Delegates.notNull() @@ -131,6 +132,7 @@ internal class WalletViewModel @Inject constructor( } uiState = stateFactory.getSkeletonState(wallets = sourceList, selectedWalletIndex = selectedWalletIndex) + updateContentItems(index = selectedWalletIndex) } @@ -316,13 +318,49 @@ internal class WalletViewModel @Inject constructor( if (state.walletsListConfig.selectedWalletIndex == index) return + /* + * When wallet is changed it's necessary to stop the last jobs. + * If jobs aren't stopped and wallet is changed then it will update state for the prev wallet. + */ tokensJobHolder.update(job = null) marketPriceJobHolder.update(job = null) notificationsJobHolder.update(job = null) - uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index) + val cacheState = WalletStateCache.getState(userWalletId = state.walletsListConfig.wallets[index].id) + if (cacheState != null) { + uiState = if (cacheState is WalletState.ContentState) { + cacheState.copySealed(walletsListConfig = state.walletsListConfig.copy(selectedWalletIndex = index)) + } else { + cacheState + } - updateContentItems(index = index) + if (cacheState.isLoadingState()) updateContentItems(index) + } else { + uiState = stateFactory.getSkeletonState(wallets = wallets, selectedWalletIndex = index) + updateContentItems(index = index) + } + } + + private fun WalletState.isLoadingState(): Boolean { + // Check the base components + if (this is WalletState.ContentState) { + walletsListConfig.wallets[walletsListConfig.selectedWalletIndex] is WalletCardState.Loading || + notifications.isEmpty() + } + + // Check the special components + return when (this) { + is WalletMultiCurrencyState -> { + tokensListState is WalletTokensListState.Loading || + tokensListState.items + .filterIsInstance() + .any { it.state is TokenItemState.Loading } + } + is WalletSingleCurrencyState -> { + txHistoryState is TxHistoryState.Loading || marketPriceBlockState is MarketPriceBlockState.Loading + } + is WalletState.Initial -> false + } } override fun onRefreshSwipe() { From 11687f636815254b885da2fd6ff91ee3cf8377ff Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 10 Aug 2023 17:25:56 +0400 Subject: [PATCH 25/52] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TangemSdkManager.kt | 11 +++- .../model/builders/WalletStoreBuilder.kt | 10 ++-- .../tasks/product/CreateProductWalletTask.kt | 11 ++-- .../domain/tasks/product/ScanProductTask.kt | 53 +++++++++---------- .../DefaultWalletCurrenciesManager.kt | 16 ++++-- .../domain/DefaultCustomTokenInteractor.kt | 5 +- .../viewmodels/AddCustomTokenViewModel.kt | 6 +-- .../walletconnect/WalletConnectMiddleware.kt | 29 ++++++---- .../redux/walletconnect/WalletConnectState.kt | 2 +- .../redux/OnboardingOtherCardsMiddleware.kt | 7 +-- .../redux/OnboardingWalletMiddleware.kt | 6 ++- .../domain/DefaultTokensListInteractor.kt | 26 +++++---- .../tokens/legacy/redux/TokensAction.kt | 2 +- .../tokens/legacy/redux/TokensMiddleware.kt | 18 +++---- .../tokens/legacy/redux/TokensReducer.kt | 2 +- .../tap/features/wallet/models/Currency.kt | 3 +- .../wallet/ui/WalletDetailsFragment.kt | 33 +++--------- .../wallet/ui/adapters/WalletAdapter.kt | 8 ++- .../wallet/ui/images/CurrencyIconView.kt | 2 +- .../wallet/ui/wallet/MultiWalletView.kt | 6 +-- .../tangem/tap/proxy/DerivationManagerImpl.kt | 17 +++--- .../repository/DefaultCurrenciesRepository.kt | 4 +- .../tokens/utils/CardCurrenciesFactory.kt | 30 +++++++---- .../data/tokens/utils/TokensOperations.kt | 12 ++--- .../tangem/domain/common/BlockchainNetwork.kt | 11 ++-- .../domain/common/DerivationStyleProvider.kt | 26 +++++++++ .../tangem/domain/common/TapWorkarounds.kt | 9 ---- .../domain/common/extensions/Blockchain.kt | 12 +++++ .../common/util/ScanResponseExtensions.kt | 8 +++ .../features/addCustomToken/CustomCurrency.kt | 2 +- .../addCustomToken/redux/AddCustomTokenHub.kt | 45 +++------------- .../redux/AddCustomTokenState.kt | 31 ++--------- .../DefaultWalletManagersFacade.kt | 8 +-- .../wallet/viewmodels/WalletViewModel.kt | 7 +-- 34 files changed, 239 insertions(+), 239 deletions(-) create mode 100644 domain/legacy/src/main/java/com/tangem/domain/common/DerivationStyleProvider.kt diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 35325434de..0c0c30de42 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -16,6 +16,7 @@ import com.tangem.crypto.bip39.DefaultMnemonic import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.operations.ScanTask @@ -80,7 +81,10 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit suspend fun createProductWallet(scanResponse: ScanResponse): CompletionResult { return runTaskAsync( - CreateProductWalletTask(scanResponse.cardTypesResolver), + CreateProductWalletTask( + cardTypesResolver = scanResponse.cardTypesResolver, + derivationStyleProvider = scanResponse.derivationStyleProvider, + ), scanResponse.card.cardId, Message(resources.getString(R.string.initial_message_create_wallet_body)), ) @@ -92,7 +96,10 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit ): CompletionResult { return when (val seedResult = DefaultMnemonic(mnemonic, tangemSdk.wordlist).generateSeed()) { is CompletionResult.Success -> runTaskAsync( - CreateProductWalletTask(scanResponse.cardTypesResolver, seedResult.data), + CreateProductWalletTask( + cardTypesResolver = scanResponse.cardTypesResolver, + derivationStyleProvider = scanResponse.derivationStyleProvider, + ), scanResponse.card.cardId, Message(resources.getString(R.string.initial_message_create_wallet_body)), ) diff --git a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt index 81532e5bb9..7bd2f77fe1 100644 --- a/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt +++ b/app/src/main/java/com/tangem/tap/domain/model/builders/WalletStoreBuilder.kt @@ -1,11 +1,15 @@ package com.tangem.tap.domain.model.builders import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider -import com.tangem.blockchain.common.* +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.derivation.DerivationStyle import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.wallets.models.UserWallet import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.domain.model.WalletStoreModel @@ -47,7 +51,7 @@ private class BlockchainNetworkWalletStoreBuilderImpl( } override fun build(): WalletStoreModel { - val cardDerivationStyle = userWallet.scanResponse.card.derivationStyle + val cardDerivationStyle = userWallet.scanResponse.derivationStyleProvider.getDerivationStyle() val blockchainWalletData = blockchainNetwork.getBlockchainWalletData(walletManager, cardDerivationStyle) val tokensWalletsData = blockchainNetwork.getTokensWalletsData( walletManager = walletManager, diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index d23e9fde5c..ede3d71091 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -13,8 +13,9 @@ import com.tangem.common.extensions.toMapKey import com.tangem.common.map import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.KeyWalletPublicKey import com.tangem.operations.CommandResponse @@ -56,6 +57,7 @@ private data class CreateWalletResponse( class CreateProductWalletTask( private val cardTypesResolver: CardTypesResolver, + private val derivationStyleProvider: DerivationStyleProvider, private val seed: ByteArray? = null, ) : CardSessionRunnable { @@ -76,7 +78,7 @@ class CreateProductWalletTask( cardTypesResolver.isTangemTwins() -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") - else -> CreateWalletTangemWallet(seed) + else -> CreateWalletTangemWallet(seed, derivationStyleProvider) } commandProcessor.proceed(cardDto, session) { when (it) { @@ -133,6 +135,7 @@ private class CreateWalletTangemNote(private val cardTypesResolver: CardTypesRes private class CreateWalletTangemWallet( private val seed: ByteArray?, + private val derivationStyleProvider: DerivationStyleProvider, ) : ProductCommandProcessor { private var primaryCard: PrimaryCard? = null @@ -242,9 +245,9 @@ private class CreateWalletTangemWallet( val blockchainsForCurve = getBlockchains(response.cardId, card).filter { it.getSupportedCurves().contains(response.wallet.curve) } - val derivationPaths = blockchainsForCurve.mapNotNull { + val derivationPaths = blockchainsForCurve.mapNotNull { blockchain -> isBlockchainsForCurvesExist = true - it.derivationPath(card.derivationStyle) + blockchain.derivationPath(derivationStyleProvider.getDerivationStyle()) } if (derivationPaths.isNotEmpty()) { map[response.wallet.publicKey.toMapKey()] = derivationPaths 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 6774c4e61d..6a184f7f8e 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 @@ -14,6 +14,7 @@ import com.tangem.common.tlv.TlvDecoder import com.tangem.crypto.CryptoUtils import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.BlockchainNetwork +import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.TapWorkarounds.isExcluded import com.tangem.domain.common.TapWorkarounds.isNotSupportedInThatRelease import com.tangem.domain.common.TapWorkarounds.isStart2Coin @@ -21,6 +22,7 @@ import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.TwinsHelper import com.tangem.domain.common.extensions.getPrimaryCurve +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ProductType import com.tangem.domain.models.scan.ScanResponse @@ -208,31 +210,22 @@ private class ScanWalletProcessor( ) { val productType = ProductType.Wallet scope.launch { - val derivations = collectDerivations(card) + val scanResponse = ScanResponse( + card = card, + productType = productType, + walletData = session.environment.walletData, + primaryCard = primaryCard, + ) + val derivations = collectDerivations(card, scanResponse.derivationStyleProvider) if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { - callback( - CompletionResult.Success( - ScanResponse( - card = card, - productType = productType, - walletData = session.environment.walletData, - primaryCard = primaryCard, - ), - ), - ) + callback(CompletionResult.Success(scanResponse)) return@launch } DeriveMultipleWalletPublicKeysTask(derivations).run(session) { result -> when (result) { is CompletionResult.Success -> { - val response = ScanResponse( - card = card, - productType = productType, - walletData = session.environment.walletData, - derivedKeys = result.data.entries, - primaryCard = primaryCard, - ) + val response = scanResponse.copy(derivedKeys = result.data.entries) callback(CompletionResult.Success(response)) } is CompletionResult.Failure -> callback(CompletionResult.Failure(result.error)) @@ -241,7 +234,10 @@ private class ScanWalletProcessor( } } - private suspend fun getBlockchainsToDerive(card: CardDTO): List { + private suspend fun getBlockchainsToDerive( + card: CardDTO, + derivationStyleProvider: DerivationStyleProvider, + ): List { val userTokensRepository = userTokensRepository ?: return emptyList() val blockchainsToDerive = userTokensRepository.loadBlockchainsToDerive(card) .toMutableList() @@ -249,11 +245,11 @@ private class ScanWalletProcessor( mutableListOf( BlockchainNetwork( blockchain = Blockchain.Bitcoin, - card = card, + derivationStyleProvider = derivationStyleProvider, ), BlockchainNetwork( blockchain = Blockchain.Ethereum, - card = card, + derivationStyleProvider = derivationStyleProvider, ), ) } @@ -263,11 +259,11 @@ private class ScanWalletProcessor( listOf( BlockchainNetwork( blockchain = Blockchain.Ethereum, - card = card, + derivationStyleProvider = derivationStyleProvider, ), BlockchainNetwork( blockchain = Blockchain.EthereumTestnet, - card = card, + derivationStyleProvider = derivationStyleProvider, ), ), ) @@ -277,7 +273,7 @@ private class ScanWalletProcessor( additionalBlockchainsToDerive.map { BlockchainNetwork( blockchain = it, - card = card, + derivationStyleProvider = derivationStyleProvider, ) }, ) @@ -293,7 +289,7 @@ private class ScanWalletProcessor( ).map { BlockchainNetwork( blockchain = it, - card = card, + derivationStyleProvider = derivationStyleProvider, ) }, ) @@ -301,8 +297,11 @@ private class ScanWalletProcessor( return blockchainsToDerive.distinct() } - private suspend fun collectDerivations(card: CardDTO): Map> { - val blockchains = getBlockchainsToDerive(card) + private suspend fun collectDerivations( + card: CardDTO, + derivationStyleProvider: DerivationStyleProvider, + ): Map> { + val blockchains = getBlockchainsToDerive(card, derivationStyleProvider) val derivations = mutableMapOf>() blockchains.forEach { blockchain -> diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt index 07e8b8ccf6..96408e1315 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt @@ -1,9 +1,11 @@ package com.tangem.tap.domain.walletCurrencies.implementation -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.* import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.DerivationStyleProvider +import com.tangem.domain.common.extensions.derivationPath +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.wallets.legacy.WalletManagersRepository import com.tangem.domain.wallets.models.UserWallet @@ -61,7 +63,9 @@ internal class DefaultWalletCurrenciesManager( } val card = userWallet.scanResponse.card - val currenciesToAddWithMissingBlockchains = currenciesToAdd.addMissingBlockchainsIfNeeded(card) + val currenciesToAddWithMissingBlockchains = currenciesToAdd.addMissingBlockchainsIfNeeded( + userWallet.scanResponse.derivationStyleProvider, + ) listeners.forEach { it.willCurrenciesAdd(userWallet, currenciesToAddWithMissingBlockchains) } updateWalletStores( @@ -167,13 +171,15 @@ internal class DefaultWalletCurrenciesManager( return networks } - private fun List.addMissingBlockchainsIfNeeded(card: CardDTO): List { + private fun List.addMissingBlockchainsIfNeeded( + derivationStyleProvider: DerivationStyleProvider, + ): List { if (this.isEmpty()) return this val currencies = this.asSequence() return currencies .groupBy { currency -> - findBlockchainCurrency(currency, currencies, card.derivationStyle) + findBlockchainCurrency(currency, currencies, derivationStyleProvider.getDerivationStyle()) } .mapValues { (blockchainCurrency, blockchainCurrencies) -> findBlockchainTokens(blockchainCurrency, blockchainCurrencies) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt index a92ac13e11..40cf0892b5 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt @@ -7,8 +7,9 @@ import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.toNetworkId +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.domain.models.scan.ScanResponse @@ -114,7 +115,7 @@ class DefaultCustomTokenInteractor( val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter { it.getSupportedCurves().contains(curve) }.mapNotNull { - it.derivationPath(scanResponse.card.derivationStyle) + it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) } val customTokensCandidates = currencyList.filter { diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 297bf37f54..406963b699 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -11,14 +11,14 @@ import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.HDWalletError import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.* +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.features.customtoken.impl.domain.CustomTokenInteractor @@ -599,7 +599,7 @@ internal class AddCustomTokenViewModel @Inject constructor( if (blockchain == null) return null val derivationStyle = if (!isDerivationPathSelected()) { - reduxStateHolder.scanResponse?.card?.derivationStyle + reduxStateHolder.scanResponse?.derivationStyleProvider?.getDerivationStyle() } else { DerivationStyle.LEGACY } 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 c2276c9065..42a34e145d 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 @@ -1,18 +1,19 @@ package com.tangem.tap.features.details.redux.walletconnect import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.extensions.guard import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.DerivationStyleProvider +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver -import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.AppState @@ -357,9 +358,13 @@ class WalletConnectMiddleware { handleScanResponse(scanResponse = scanResponse, session = session, blockchain = blockchain) } - private fun getAvailableBlockchains(card: CardDTO, walletState: WalletState): List { + private fun getAvailableBlockchains( + derivationStyleProvider: DerivationStyleProvider, + walletState: WalletState, + ): List { return walletState.currencies.filter { - it.isBlockchain() && !it.isCustomCurrency(card.derivationStyle) && it.blockchain.isEvm() + it.isBlockchain() && + !it.isCustomCurrency(derivationStyleProvider.getDerivationStyle()) && it.blockchain.isEvm() }.map { it.blockchain } } @@ -390,7 +395,7 @@ class WalletConnectMiddleware { walletPublicKey = wallet.publicKey.seedKey, derivedPublicKey = derivedKey, derivationPath = wallet.publicKey.derivationPath, - derivationStyle = scanResponse.card.derivationStyle, + derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), blockchain = wallet.blockchain, ) @@ -403,7 +408,6 @@ class WalletConnectMiddleware { } private fun handleScanResponse(scanResponse: ScanResponse, session: WalletConnectSession, blockchain: Blockchain) { - val card = scanResponse.card if (!scanResponse.cardTypesResolver.isMultiwalletAllowed()) { store.dispatchOnMain(WalletConnectAction.UnsupportedCard) return @@ -415,7 +419,11 @@ class WalletConnectMiddleware { NewWcSessionData(session = updatedSession, scanResponse = scanResponse, blockchain = blockchain), ), ) - val blockchains = if (blockchain.isEvm()) getAvailableBlockchains(card, walletState) else emptyList() + val blockchains = if (blockchain.isEvm()) { + getAvailableBlockchains(scanResponse.derivationStyleProvider, walletState) + } else { + emptyList() + } store.dispatch( GlobalAction.ShowDialog( WalletConnectDialog.ApproveWcSession(session = updatedSession, networks = blockchains), @@ -433,8 +441,9 @@ class WalletConnectMiddleware { } else { blockchain } - val derivation = blockchainToMake.derivationPath(store.state.globalState.scanResponse?.card?.derivationStyle) - ?.rawPath + val derivation = blockchainToMake.derivationPath( + style = store.state.globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle(), + )?.rawPath val blockchainNetwork = BlockchainNetwork( blockchain = blockchainToMake, derivationPath = derivation, diff --git a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt index 135f597dc1..0446b1ca82 100644 --- a/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt +++ b/app/src/main/java/com/tangem/tap/features/details/redux/walletconnect/WalletConnectState.kt @@ -2,9 +2,9 @@ package com.tangem.tap.features.details.redux.walletconnect import com.squareup.moshi.JsonClass import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.TransactionData import com.tangem.blockchain.common.WalletManager +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.models.scan.ScanResponse import com.tangem.tap.common.redux.StateDialog diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt index 00a9d94af5..9a76813cab 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/otherCards/redux/OnboardingOtherCardsMiddleware.kt @@ -6,6 +6,7 @@ import com.tangem.core.analytics.Analytics import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.tap.* import com.tangem.tap.common.analytics.events.Onboarding import com.tangem.tap.common.postUi @@ -92,7 +93,7 @@ private fun handleOtherCardsAction(action: Action) { val blockchainNetwork = BlockchainNetwork( blockchain = primaryBlockchain, - card = updatedCard, + derivationStyleProvider = updatedResponse.derivationStyleProvider, ) .updateTokens( listOfNotNull(primaryToken), @@ -102,11 +103,11 @@ private fun handleOtherCardsAction(action: Action) { listOf( BlockchainNetwork( blockchain = Blockchain.Bitcoin, - card = updatedCard, + derivationStyleProvider = updatedResponse.derivationStyleProvider, ), BlockchainNetwork( blockchain = Blockchain.Ethereum, - card = updatedCard, + derivationStyleProvider = updatedResponse.derivationStyleProvider, ), ) } diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt index 2ce6725c37..d1a927c252 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/redux/OnboardingWalletMiddleware.kt @@ -12,6 +12,7 @@ import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.TapWorkarounds.canSkipBackup import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.userwallets.Artwork @@ -125,7 +126,10 @@ private fun handleWalletAction(action: Action) { } else { listOf(Blockchain.Bitcoin, Blockchain.Ethereum) }.map { blockchain -> - BlockchainNetwork(blockchain, result.data.card) + BlockchainNetwork( + blockchain = blockchain, + derivationStyleProvider = updatedResponse.derivationStyleProvider, + ) } scope.launch { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt index c7743263f4..940470bc09 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt @@ -2,18 +2,19 @@ package com.tangem.tap.features.tokens.impl.domain import androidx.paging.PagingData import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.extensions.derivationPath +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.supportsHdWallet import com.tangem.domain.models.scan.ScanResponse import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.* import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain import com.tangem.tap.common.redux.global.GlobalAction @@ -24,10 +25,6 @@ import com.tangem.tap.features.tokens.legacy.redux.TokenWithBlockchain import com.tangem.tap.features.tokens.legacy.redux.TokensMiddleware import com.tangem.tap.features.wallet.models.Currency import com.tangem.tap.proxy.AppStateHolder -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import timber.log.Timber @@ -51,11 +48,12 @@ internal class DefaultTokensListInteractor( override suspend fun saveChanges(tokens: List, blockchains: List) { val scanResponse = requireNotNull(reduxStateHolder.scanResponse) + val derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle() val currentTokens = store.state.tokensState.addedWallets - .toNonCustomTokensWithBlockchains(style = scanResponse.card.derivationStyle) + .toNonCustomTokensWithBlockchains(derivationStyle = derivationStyle) val currentBlockchains = store.state.tokensState.addedWallets - .toNonCustomBlockchains(derivationStyle = scanResponse.card.derivationStyle) + .toNonCustomBlockchains(derivationStyle = derivationStyle) val blockchainsToAdd = blockchains.filterNot(currentBlockchains::contains) val blockchainsToRemove = currentBlockchains.filterNot(blockchains::contains) @@ -73,18 +71,18 @@ internal class DefaultTokensListInteractor( remove( tokens = tokensToRemove, blockchains = blockchainsToRemove, - derivationStyle = scanResponse.card.derivationStyle, + derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), ) add(tokens = tokensToAdd, blockchains = blockchainsToAdd, scanResponse = scanResponse) } private fun List.toNonCustomTokensWithBlockchains( - style: DerivationStyle?, + derivationStyle: DerivationStyle?, ): List { return this.map(WalletDataModel::currency) .mapNotNull { currency -> - if (currency !is Currency.Token || currency.isCustomCurrency(style)) return@mapNotNull null + if (currency !is Currency.Token || currency.isCustomCurrency(derivationStyle)) return@mapNotNull null TokenWithBlockchain(token = currency.token, blockchain = currency.blockchain) } .distinct() @@ -123,7 +121,7 @@ internal class DefaultTokensListInteractor( val currenciesToAdd = convertToCurrencies( tokens = tokens, blockchains = blockchains, - derivationStyle = scanResponse.card.derivationStyle, + derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), ) // TODO("[REDACTED_TASK_KEY] use DerivationManager") @@ -184,7 +182,7 @@ internal class DefaultTokensListInteractor( .map(Currency::blockchain) .distinct() .filter { it.getSupportedCurves().contains(curve) } - .mapNotNull { it.derivationPath(scanResponse.card.derivationStyle) } + .mapNotNull { it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) } val customTokensCandidates = currencyList .filter { it.blockchain.getSupportedCurves().contains(curve) } diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt index 21f763e48a..c24f982fca 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensAction.kt @@ -1,7 +1,7 @@ package com.tangem.tap.features.tokens.legacy.redux import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.tap.domain.model.WalletDataModel import org.rekotlin.Action diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt index c62a8053d9..dca9ea4875 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt @@ -1,7 +1,7 @@ package com.tangem.tap.features.tokens.legacy.redux import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.CompletionResult import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.ByteArrayKey @@ -13,7 +13,8 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.DomainWrapped -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.extensions.derivationPath +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.common.util.supportsHdWallet import com.tangem.domain.features.addCustomToken.CustomCurrency @@ -21,7 +22,7 @@ import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.domainStore import com.tangem.operations.derivation.ExtendedPublicKeysMap -import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE +import com.tangem.tap.* import com.tangem.tap.common.analytics.events.ManageTokens import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchOnMain @@ -30,11 +31,6 @@ import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.features.wallet.models.Currency -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userWalletsListManager -import com.tangem.tap.walletCurrenciesManager import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.rekotlin.Middleware @@ -72,7 +68,7 @@ object TokensMiddleware { currencies = convertToCurrencies( blockchains = blockchainsToRemove, tokens = tokensToRemove, - derivationStyle = scanResponse.card.derivationStyle, + derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), ), ) @@ -87,7 +83,7 @@ object TokensMiddleware { val currencyList = convertToCurrencies( blockchains = blockchainsToAdd, tokens = tokensToAdd, - derivationStyle = scanResponse.card.derivationStyle, + derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle(), ) if (scanResponse.supportsHdWallet()) { @@ -175,7 +171,7 @@ object TokensMiddleware { val manageTokensCandidates = currencyList.map { it.blockchain }.distinct().filter { it.getSupportedCurves().contains(curve) }.mapNotNull { - it.derivationPath(scanResponse.card.derivationStyle) + it.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) } val customTokensCandidates = currencyList.filter { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensReducer.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensReducer.kt index e2589be5b7..8e33fdc8f4 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensReducer.kt @@ -1,7 +1,7 @@ package com.tangem.tap.features.tokens.legacy.redux import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.model.WalletDataModel import com.tangem.tap.features.wallet.models.Currency diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt index 4b82889e22..4af0733400 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt @@ -1,8 +1,9 @@ package com.tangem.tap.features.wallet.models -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.domain.common.BlockchainNetwork +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.features.addCustomToken.CustomCurrency 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 1c0c5027f2..f515410168 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 @@ -1,11 +1,7 @@ package com.tangem.tap.features.wallet.ui import android.os.Bundle -import android.view.Menu -import android.view.MenuInflater -import android.view.MenuItem -import android.view.View -import android.view.ViewGroup +import android.view.* import android.widget.TextView import androidx.activity.OnBackPressedCallback import androidx.annotation.ColorRes @@ -23,8 +19,8 @@ import com.tangem.common.doOnResult import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.NavigationAction -import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.withMainContext +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.feature.swap.api.SwapFeatureToggleManager import com.tangem.feature.swap.domain.SwapInteractor import com.tangem.sdk.extensions.dpToPx @@ -32,14 +28,7 @@ import com.tangem.tap.common.SnackbarHandler import com.tangem.tap.common.TestActions import com.tangem.tap.common.analytics.events.DetailsScreen import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.appendIfNotNull -import com.tangem.tap.common.extensions.beginDelayedTransition -import com.tangem.tap.common.extensions.fitChipsByGroupWidth -import com.tangem.tap.common.extensions.getColor -import com.tangem.tap.common.extensions.getString -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show -import com.tangem.tap.common.extensions.toQrCode +import com.tangem.tap.common.extensions.* import com.tangem.tap.common.recyclerView.SpaceItemDecoration import com.tangem.tap.common.redux.AppState import com.tangem.tap.common.utils.SafeStoreSubscriber @@ -57,15 +46,7 @@ import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter import com.tangem.tap.features.wallet.ui.adapters.WalletDetailWarningMessagesAdapter import com.tangem.tap.features.wallet.ui.images.load import com.tangem.tap.features.wallet.ui.test.TestWallet -import com.tangem.tap.features.wallet.ui.utils.assembleWarnings -import com.tangem.tap.features.wallet.ui.utils.getAvailableActions -import com.tangem.tap.features.wallet.ui.utils.getFormattedCryptoAmount -import com.tangem.tap.features.wallet.ui.utils.getFormattedFiatAmount -import com.tangem.tap.features.wallet.ui.utils.isAvailableToBuy -import com.tangem.tap.features.wallet.ui.utils.isAvailableToSell -import com.tangem.tap.features.wallet.ui.utils.isAvailableToSwap -import com.tangem.tap.features.wallet.ui.utils.mainButton -import com.tangem.tap.features.wallet.ui.utils.shouldShowMultipleAddress +import com.tangem.tap.features.wallet.ui.utils.* import com.tangem.tap.store import com.tangem.tap.userWalletsListManagerSafe import com.tangem.tap.walletCurrenciesManager @@ -336,10 +317,8 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt private fun handleCurrencyIcon(currency: Currency) = with(binding.lWalletDetails.lBalance) { ivCurrency.load( currency = currency, - derivationStyle = store.state.globalState - .scanResponse - ?.card - ?.derivationStyle, + derivationStyle = store.state.globalState.scanResponse + ?.derivationStyleProvider?.getDerivationStyle(), ) } diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt index e0a15c7e4b..3f511f7b65 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/adapters/WalletAdapter.kt @@ -7,7 +7,7 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import com.tangem.core.analytics.Analytics -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.tap.common.analytics.events.Portfolio import com.tangem.tap.common.extensions.getString import com.tangem.tap.common.extensions.hide @@ -77,10 +77,8 @@ class WalletAdapter : ListAdapter { + fun createDefaultCoinsForMultiCurrencyCard( + card: CardDTO, + derivationStyleProvider: DerivationStyleProvider, + ): List { var blockchains = if (demoConfig.isDemoCardId(card.cardId)) { demoConfig.demoBlockchains } else { @@ -23,25 +28,29 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { blockchains = blockchains.mapNotNull { it.getTestnetVersion() } } - return blockchains.mapNotNull { createCoin(it, card) } + return blockchains.mapNotNull { createCoin(it, derivationStyleProvider) } } fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency { - val card = scanResponse.card + val derivationStyleProvider = scanResponse.derivationStyleProvider val resolver = scanResponse.cardTypesResolver val blockchain = resolver.getBlockchain() - val coin = requireNotNull(createCoin(blockchain, card)) { + val coin = requireNotNull(createCoin(blockchain, derivationStyleProvider)) { "Coin for the single currency card cannot be null" } val primaryToken = resolver.getPrimaryToken()?.let { token -> - createToken(token, blockchain, card) + createToken(token, blockchain, derivationStyleProvider) } return primaryToken ?: coin } - private fun createToken(sdkToken: SdkToken, blockchain: Blockchain, card: CardDTO): CryptoCurrency.Token? { + private fun createToken( + sdkToken: SdkToken, + blockchain: Blockchain, + derivationStyleProvider: DerivationStyleProvider, + ): CryptoCurrency.Token? { if (blockchain != Blockchain.Unknown) { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null @@ -56,11 +65,14 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { decimals = sdkToken.decimals, isCustom = false, contractAddress = sdkToken.contractAddress, - derivationPath = getDerivationPath(blockchain, card), + derivationPath = getDerivationPath(blockchain, derivationStyleProvider), ) } - private fun createCoin(blockchain: Blockchain, card: CardDTO): CryptoCurrency.Coin? { + private fun createCoin( + blockchain: Blockchain, + derivationStyleProvider: DerivationStyleProvider, + ): CryptoCurrency.Coin? { if (blockchain == Blockchain.Unknown) { Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") return null @@ -73,7 +85,7 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { symbol = blockchain.currency, iconUrl = getCoinIconUrl(blockchain), decimals = blockchain.decimals(), - derivationPath = getDerivationPath(blockchain, card), + derivationPath = getDerivationPath(blockchain, derivationStyleProvider), ) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index c302d2246f..cbc158d2fc 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -2,10 +2,10 @@ package com.tangem.data.tokens.utils import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.IconsUtil -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.DerivationStyleProvider +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.tokens.models.CryptoCurrency.ID import com.tangem.domain.tokens.models.Network import com.tangem.blockchain.common.Token as SdkToken @@ -23,12 +23,8 @@ internal fun isCustomToken(tokenId: ID): Boolean { return tokenId.rawCurrencyId == null } -internal fun getDerivationPath(blockchain: Blockchain, card: CardDTO): String? { - return if (card.settings.isHDWalletAllowed) { - blockchain.derivationPath(card.derivationStyle)?.rawPath - } else { - null - } +internal fun getDerivationPath(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): String? { + return blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath } internal fun getBlockchain(networkId: Network.ID): Blockchain { diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt b/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt index 212bd764be..5cb15b3205 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt @@ -5,8 +5,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.calculateHashCode -import com.tangem.domain.common.TapWorkarounds.derivationStyle -import com.tangem.domain.models.scan.CardDTO +import com.tangem.domain.common.extensions.derivationPath @JsonClass(generateAdapter = true) data class BlockchainNetwork( @@ -15,13 +14,9 @@ data class BlockchainNetwork( val tokens: List, ) { - constructor(blockchain: Blockchain, card: CardDTO) : this( + constructor(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider) : this( blockchain = blockchain, - derivationPath = if (card.settings.isHDWalletAllowed) { - blockchain.derivationPath(card.derivationStyle)?.rawPath - } else { - null - }, + derivationPath = blockchain.derivationPath(derivationStyleProvider.getDerivationStyle())?.rawPath, tokens = emptyList(), ) diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/DerivationStyleProvider.kt b/domain/legacy/src/main/java/com/tangem/domain/common/DerivationStyleProvider.kt new file mode 100644 index 0000000000..570071b37a --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/common/DerivationStyleProvider.kt @@ -0,0 +1,26 @@ +package com.tangem.domain.common + +import com.tangem.blockchain.common.derivation.DerivationStyle +import com.tangem.domain.models.scan.CardDTO + +interface DerivationStyleProvider { + fun getDerivationStyle(): DerivationStyle? +} + +internal class TangemDerivationStyleProvider( + private val cardTypesResolver: CardTypesResolver, + private val card: CardDTO, +) : DerivationStyleProvider { + override fun getDerivationStyle(): DerivationStyle? { + return when { + !card.settings.isHDWalletAllowed -> null + firstBatchesOfWallet1(card) -> DerivationStyle.V1 + cardTypesResolver.isWallet2() -> DerivationStyle.V3 + else -> DerivationStyle.V2 + } + } + + private fun firstBatchesOfWallet1(card: CardDTO): Boolean { + return card.batchId == "AC01" || card.batchId == "AC02" || card.batchId == "CB95" + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index 3a04270558..8ff17001e9 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -1,7 +1,6 @@ package com.tangem.domain.common import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle import com.tangem.common.card.Card import com.tangem.common.card.FirmwareVersion import com.tangem.domain.models.scan.CardDTO @@ -32,14 +31,6 @@ object TapWorkarounds { val CardDTO.useOldStyleDerivation: Boolean get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95" - val CardDTO.derivationStyle: DerivationStyle? - get() = if (!settings.isHDWalletAllowed) { - null - } else if (useOldStyleDerivation) { - DerivationStyle.LEGACY - } else { - DerivationStyle.NEW - } val CardDTO.isExcluded: Boolean get() { val excludedBatch = excludedBatches.contains(batchId) diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index c54218c39a..70c16ba9d7 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -2,7 +2,9 @@ package com.tangem.domain.common.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.card.EllipticCurve +import com.tangem.crypto.hdWallet.DerivationPath import java.math.BigDecimal @Suppress("ComplexMethod") @@ -216,6 +218,16 @@ fun Blockchain.getPrimaryCurve(): EllipticCurve? { } } +fun Blockchain.derivationPath(style: DerivationStyle?): DerivationPath? { + if (style == null) return null + if (!getSupportedCurves().contains(EllipticCurve.Secp256k1) && + !getSupportedCurves().contains(EllipticCurve.Ed25519) + ) { + return null + } + return style.getConfig().derivations(this).values.first() +} + private const val NODL = "NODL" private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt b/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt index 8438a87f14..65d1cd6ea0 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt @@ -5,7 +5,9 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.toMapKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.CardTypesResolver +import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.TangemCardTypesResolver +import com.tangem.domain.common.TangemDerivationStyleProvider import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.models.scan.ScanResponse @@ -17,6 +19,12 @@ val ScanResponse.cardTypesResolver: CardTypesResolver walletData = walletData, ) +val ScanResponse.derivationStyleProvider: DerivationStyleProvider + get() = TangemDerivationStyleProvider( + cardTypesResolver, + card, + ) + fun ScanResponse.twinsIsTwinned(): Boolean = card.isTangemTwins && walletData != null && secondTwinPublicKey != null fun ScanResponse.supportsHdWallet(): Boolean = card.settings.isHDWalletAllowed fun ScanResponse.supportsBackup(): Boolean = card.settings.isBackupAllowed diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt index 6757215dda..78ff46be86 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/CustomCurrency.kt @@ -1,8 +1,8 @@ package com.tangem.domain.features.addCustomToken import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.form.BaseFieldDataConverter import com.tangem.domain.common.form.FieldId diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt index 30085691d3..3db713c231 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt @@ -5,49 +5,18 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.extensions.guard import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.domain.AddCustomTokenError -import com.tangem.domain.AddCustomTokenError.Warning.PotentialScamToken -import com.tangem.domain.AddCustomTokenError.Warning.TokenAlreadyAdded -import com.tangem.domain.AddCustomTokenError.Warning.UnsupportedSolanaToken +import com.tangem.domain.AddCustomTokenError.Warning.* import com.tangem.domain.DomainDialog import com.tangem.domain.DomainWrapped -import com.tangem.domain.common.TapWorkarounds.derivationStyle import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.toNetworkId -import com.tangem.domain.common.form.Field -import com.tangem.domain.common.form.Form -import com.tangem.domain.common.form.TokenContractAddressValidator -import com.tangem.domain.common.form.TokenDecimalsValidator -import com.tangem.domain.common.form.TokenNameValidator -import com.tangem.domain.common.form.TokenNetworkValidator -import com.tangem.domain.common.form.TokenSymbolValidator -import com.tangem.domain.features.addCustomToken.AddCustomTokenService -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.ContractAddress -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Decimals -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.DerivationPath -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Name -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Network -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Symbol -import com.tangem.domain.features.addCustomToken.TokenBlockchainField -import com.tangem.domain.features.addCustomToken.TokenDerivationPathField -import com.tangem.domain.features.addCustomToken.TokenField -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.FieldError -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.Init -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnAddCustomTokenClicked -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnCreate -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnDestroy -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenContractAddressChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDecimalsChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenDerivationPathChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNameChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenNetworkChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.OnTokenSymbolChanged -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.Screen -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.SetFoundTokenInfo -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.UpdateForm -import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.Warning +import com.tangem.domain.common.form.* +import com.tangem.domain.common.util.derivationStyleProvider +import com.tangem.domain.features.addCustomToken.* +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* +import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenAction.* import com.tangem.domain.features.addCustomToken.redux.AddCustomTokenState.Companion.createInitialScreenState import com.tangem.domain.redux.BaseStoreHub import com.tangem.domain.redux.DomainState @@ -590,7 +559,7 @@ private class AddCustomTokenReducer( ) state.copy( - cardDerivationStyle = card.derivationStyle, + cardDerivationStyle = globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle(), form = Form(AddCustomTokenState.createFormFields(card, CustomTokenType.Blockchain)), tangemTechServiceManager = tangemTechServiceManager, screenState = createInitialScreenState(card.settings.isHDWalletAllowed), diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index 3377646d25..f68eeb7b87 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -1,40 +1,19 @@ package com.tangem.domain.features.addCustomToken.redux import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.json.MoshiJsonConverter import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.domain.AddCustomTokenError import com.tangem.domain.DomainWrapped import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.isSupportedInApp import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.supportedTokens -import com.tangem.domain.common.form.CustomTokenValidator -import com.tangem.domain.common.form.DataField -import com.tangem.domain.common.form.FieldDataConverter -import com.tangem.domain.common.form.FieldId -import com.tangem.domain.common.form.FieldToJsonConverter -import com.tangem.domain.common.form.Form -import com.tangem.domain.common.form.StringIsEmptyValidator -import com.tangem.domain.common.form.StringIsNotEmptyValidator -import com.tangem.domain.common.form.TokenContractAddressValidator -import com.tangem.domain.common.form.TokenDecimalsValidator -import com.tangem.domain.common.form.TokenNameValidator -import com.tangem.domain.common.form.TokenNetworkValidator -import com.tangem.domain.common.form.TokenSymbolValidator -import com.tangem.domain.features.addCustomToken.AddCustomTokenService -import com.tangem.domain.features.addCustomToken.CustomCurrency -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.ContractAddress -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Decimals -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.DerivationPath -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Name -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Network -import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.Symbol -import com.tangem.domain.features.addCustomToken.TokenBlockchainField -import com.tangem.domain.features.addCustomToken.TokenDerivationPathField -import com.tangem.domain.features.addCustomToken.TokenField +import com.tangem.domain.common.form.* +import com.tangem.domain.features.addCustomToken.* +import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.redux.DomainState import com.tangem.domain.redux.state.StringActionStateConverter diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index 34ef60f9ed..bd0ea90ed4 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -8,7 +8,8 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.extensions.derivationPath +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.demo.DemoConfig import com.tangem.domain.tokens.models.CryptoCurrency @@ -56,7 +57,8 @@ class DefaultWalletManagersFacade( return getOrCreateWalletManager( userWallet = userWallet, blockchain = blockchain, - derivationPath = blockchain.derivationPath(userWallet.scanResponse.card.derivationStyle), + derivationPath = blockchain + .derivationPath(userWallet.scanResponse.derivationStyleProvider.getDerivationStyle()), ) ?.wallet ?.getExploreUrl() @@ -120,7 +122,7 @@ class DefaultWalletManagersFacade( extraTokens: Set, ): UpdateWalletManagerResult { val scanResponse = userWallet.scanResponse - val derivationPath = blockchain.derivationPath(scanResponse.card.derivationStyle) + val derivationPath = blockchain.derivationPath(scanResponse.derivationStyleProvider.getDerivationStyle()) if (derivationPath != null && !scanResponse.hasDerivation(blockchain, derivationPath.rawPath)) { Timber.e("Derivation missed for: $blockchain") diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 06ca0ab8c5..12cdb339a1 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -3,7 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels import androidx.lifecycle.* import androidx.paging.cachedIn import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.Provider import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess @@ -11,8 +11,9 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TxHistoryState import com.tangem.domain.card.* import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.TapWorkarounds.derivationStyle +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.cardTypesResolver +import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.demo.IsDemoCardUseCase import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyUseCase @@ -172,7 +173,7 @@ internal class WalletViewModel @Inject constructor( val wallet = getWallet(index) updateTxHistory( blockchain = getCardTypeResolver(index).getBlockchain(), - derivationStyle = wallet.scanResponse.card.derivationStyle, + derivationStyle = wallet.scanResponse.derivationStyleProvider.getDerivationStyle(), ) updateMarketPrice(userWalletId = wallet.walletId) updateNotifications(index) From a2e602f1f1f9c8cdd6a89334254518d03598d11e Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 15:03:22 +0300 Subject: [PATCH 26/52] Updated on 2026-08-14 --- .../tangem/data/tokens/di/TokensDataModule.kt | 12 ++- .../repository/DefaultQuotesRepository.kt | 81 +++++++++++++++++++ .../tokens/repository/MockQuotesRepository.kt | 29 ------- .../data/tokens/utils/QuotesConverter.kt | 18 +++++ 4 files changed, 109 insertions(+), 31 deletions(-) create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt delete mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index a5adf06ad8..7d62d72157 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -3,8 +3,9 @@ package com.tangem.data.tokens.di import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.tokens.repository.DefaultCurrenciesRepository import com.tangem.data.tokens.repository.DefaultNetworksRepository -import com.tangem.data.tokens.repository.MockQuotesRepository +import com.tangem.data.tokens.repository.DefaultQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.quote.QuotesStore import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.domain.tokens.repository.CurrenciesRepository @@ -36,7 +37,14 @@ internal object TokensDataModule { @Provides @Singleton - fun provideQuotesRepository(): QuotesRepository = MockQuotesRepository() + fun provideQuotesRepository( + tangemTechApi: TangemTechApi, + quotesStore: QuotesStore, + cacheRegistry: CacheRegistry, + dispatchers: CoroutineDispatcherProvider, + ): QuotesRepository { + return DefaultQuotesRepository(tangemTechApi, quotesStore, cacheRegistry, dispatchers) + } @Provides @Singleton diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt new file mode 100644 index 0000000000..ea7fdbd998 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -0,0 +1,81 @@ +package com.tangem.data.tokens.repository + +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.data.tokens.utils.QuotesConverter +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.quote.QuotesStore +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.domain.tokens.models.Quote +import com.tangem.domain.tokens.repository.QuotesRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import timber.log.Timber + +internal class DefaultQuotesRepository( + private val tangemTechApi: TangemTechApi, + private val quotesStore: QuotesStore, + private val cacheRegistry: CacheRegistry, + private val dispatchers: CoroutineDispatcherProvider, +) : QuotesRepository { + + private val quotesConverter = QuotesConverter() + + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { + return channelFlow { + launch(dispatchers.io) { + quotesStore.get(currenciesIds) + .map(quotesConverter::convertSet) + .collect(::send) + } + + launch(dispatchers.io) { + fetchExpiredQuotes(currenciesIds, refresh) + } + } + } + + private suspend fun fetchExpiredQuotes(currenciesIds: Set, refresh: Boolean) { + val expiredCurrenciesIds = filterExpiredCurrenciesIds(currenciesIds, refresh) + if (expiredCurrenciesIds.isEmpty()) return + + fetchQuotes(expiredCurrenciesIds) + } + + private suspend fun fetchQuotes(rawCurrenciesIds: Set) { + try { + val response = tangemTechApi.getQuotes( + currencyId = "usd", // TODO: [REDACTED_JIRA] + coinIds = rawCurrenciesIds.joinToString(separator = ","), + ) + + quotesStore.store(response) + } catch (e: Throwable) { + Timber.e(e, "Unable to fetch quotes for: $rawCurrenciesIds") + throw e + } + } + + private suspend fun filterExpiredCurrenciesIds( + currenciesIds: Set, + refresh: Boolean, + ): Set { + return currenciesIds.fold(hashSetOf()) { acc, currencyId -> + val rawCurrencyId = currencyId.rawCurrencyId + + if (rawCurrencyId != null && rawCurrencyId !in acc) { + cacheRegistry.invokeOnExpire( + key = getQuoteCacheKey(rawCurrencyId), + skipCache = refresh, + block = { acc.add(rawCurrencyId) }, + ) + } + + acc + } + } + + private fun getQuoteCacheKey(rawCurrencyId: String): String = "quote_$rawCurrencyId" +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt deleted file mode 100644 index 31e9c03dc5..0000000000 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/MockQuotesRepository.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.data.tokens.repository - -import com.tangem.domain.tokens.models.CryptoCurrency -import com.tangem.domain.tokens.models.Quote -import com.tangem.domain.tokens.repository.QuotesRepository -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.channelFlow -import java.math.BigDecimal -import kotlin.random.Random - -internal class MockQuotesRepository : QuotesRepository { - - override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { - return channelFlow { - val quotes = currenciesIds.mapNotNullTo(hashSetOf()) { id -> - Quote( - rawCurrencyId = id.rawCurrencyId ?: return@mapNotNullTo null, - fiatRate = BigDecimal.ZERO, - priceChange = BigDecimal.ZERO, - ) - } - - delay(Random.nextLong(from = 200, until = 2_000)) - - send(quotes) - } - } -} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt new file mode 100644 index 0000000000..b771150c67 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/QuotesConverter.kt @@ -0,0 +1,18 @@ +package com.tangem.data.tokens.utils + +import com.tangem.datasource.local.quote.model.StoredQuote +import com.tangem.domain.tokens.models.Quote +import com.tangem.utils.converter.Converter + +internal class QuotesConverter : Converter { + + override fun convert(value: StoredQuote): Quote { + val (rawCurrencyId, responseQuote) = value + + return Quote( + rawCurrencyId = rawCurrencyId, + fiatRate = responseQuote.price, + priceChange = responseQuote.priceChange.movePointLeft(2), + ) + } +} \ No newline at end of file From db0c6cc933133fb736473a308a542cc6357b8d16 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 17:45:54 +0300 Subject: [PATCH 27/52] Updated on 2026-08-14 --- .../domain/tokens/GetTokenListUseCase.kt | 9 +- .../tokens/model/CryptoCurrencyStatus.kt | 11 ++ .../CurrenciesStatusesOperations.kt | 91 ++++++++++---- .../operations/CurrencyStatusOperations.kt | 5 + .../TokenListFiatBalanceOperations.kt | 7 +- .../tokens/GetPrimaryCurrencyUseCaseTest.kt | 8 +- .../domain/tokens/GetTokenListUseCaseTest.kt | 113 ++++++++++++++---- .../domain/tokens/mock/MockTokenLists.kt | 11 +- .../domain/tokens/mock/MockTokensStates.kt | 9 ++ ...letSingleCurrencyLoadedBalanceConverter.kt | 70 +++++++---- ...ryptoCurrencyStatusToTokenItemConverter.kt | 1 + 11 files changed, 252 insertions(+), 83 deletions(-) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt index f27dadfec9..59125a081c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/GetTokenListUseCase.kt @@ -14,10 +14,7 @@ import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flatMapMerge -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.* class GetTokenListUseCase( internal val currenciesRepository: CurrenciesRepository, @@ -27,8 +24,8 @@ class GetTokenListUseCase( ) { @OptIn(ExperimentalCoroutinesApi::class) - operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = true): Flow> { - return getTokensStatuses(userWalletId, refresh).flatMapMerge flatMap@{ maybeTokens -> + operator fun invoke(userWalletId: UserWalletId, refresh: Boolean = false): Flow> { + return getTokensStatuses(userWalletId, refresh).flatMapMerge { maybeTokens -> maybeTokens.fold( ifLeft = { error -> flowOf(error.left()) diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt index 443ccfeca6..1f2db6777f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/model/CryptoCurrencyStatus.kt @@ -86,4 +86,15 @@ data class CryptoCurrencyStatus( override val priceChange: BigDecimal?, override val hasTransactionsInProgress: Boolean, ) : Status() + + /** + * Represents a state where the token is available, but there is no current quote available for it. + * + * @property amount The amount of the token. + * @property hasTransactionsInProgress Indicates if there are any transactions in progress related to the token. + */ + data class NoQuote( + override val amount: BigDecimal, + override val hasTransactionsInProgress: Boolean, + ) : Status() } \ No newline at end of file diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt index 555b0fab08..463205a41c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrenciesStatusesOperations.kt @@ -3,7 +3,8 @@ package com.tangem.domain.tokens.operations import arrow.core.* import arrow.core.raise.* import com.tangem.domain.tokens.GetTokenListUseCase -import com.tangem.domain.tokens.model.* +import com.tangem.domain.tokens.model.CryptoCurrencyStatus +import com.tangem.domain.tokens.model.NetworkStatus import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.models.Quote @@ -36,25 +37,41 @@ internal class CurrenciesStatusesOperations( @OptIn(ExperimentalCoroutinesApi::class) fun getCurrenciesStatusesFlow(): Flow>> { - return getMultiCurrencyWalletCurrencies().flatMapMerge flatMap@{ maybeCurrencies -> + return getMultiCurrencyWalletCurrencies().transformLatest { maybeCurrencies -> val nonEmptyCurrencies = maybeCurrencies.fold( ifLeft = { error -> - return@flatMap flowOf(error.left()) + emit(error.left()) + return@transformLatest }, - ifRight = { it.toNonEmptyListOrNull() }, - ) ?: return@flatMap flowOf(emptyList().right()) + ifRight = List::toNonEmptyListOrNull, + ) + + if (nonEmptyCurrencies == null) { + val emptyCurrenciesStatuses = emptyList() + + emit(emptyCurrenciesStatuses.right()) + return@transformLatest + } else if (!refresh) { + val maybeLoadingCurrenciesStatuses = createCurrenciesStatuses( + currencies = nonEmptyCurrencies, + maybeNetworkStatuses = null, + maybeQuotes = null, + ) + + emit(maybeLoadingCurrenciesStatuses) + } val (networksIds, currenciesIds) = getIds(nonEmptyCurrencies) - combine( + val currenciesFlow = combine( getQuotes(currenciesIds), getNetworksStatuses(networksIds), ) { maybeQuotes, maybeNetworksStatuses -> - either { - createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes.bind(), maybeNetworksStatuses.bind()) - } + createCurrenciesStatuses(nonEmptyCurrencies, maybeQuotes, maybeNetworksStatuses) } - } + + emitAll(currenciesFlow) + }.conflate() } suspend fun getCurrencyStatusFlow(currencyId: CryptoCurrency.ID): Flow> { @@ -76,14 +93,16 @@ internal class CurrenciesStatusesOperations( } private fun getCurrencyStatusFlow(currency: CryptoCurrency): Flow> { - val quoteFlow = getQuotes(nonEmptySetOf(currency.id)) + val (networksIds, currenciesIds) = getIds(nonEmptyListOf(currency)) + + val quoteFlow = getQuotes(currenciesIds) .map { maybeQuotes -> maybeQuotes.map { quotes -> quotes.singleOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } } } - val statusFlow = getNetworksStatuses(nonEmptySetOf(currency.networkId)) + val statusFlow = getNetworksStatuses(networksIds) .map { maybeStatuses -> maybeStatuses.map { statuses -> statuses.singleOrNull { it.networkId == currency.networkId } @@ -91,34 +110,58 @@ internal class CurrenciesStatusesOperations( } return combine(quoteFlow, statusFlow) { maybeQuote, maybeNetworkStatus -> - either { - createStatus(currency, maybeQuote.bind(), maybeNetworkStatus.bind()) - } + createStatus(currency, maybeQuote, maybeNetworkStatus) } } private fun createCurrenciesStatuses( currencies: NonEmptyList, - quotes: Set, - networkStatuses: Set, - ): List { - return currencies.map { currency -> - val quote = quotes.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } - val networkStatus = networkStatuses.firstOrNull { it.networkId == currency.networkId } + maybeQuotes: Either>?, + maybeNetworkStatuses: Either>?, + ): Either> = either { + var quotesRetrievingFailed = false - createStatus(currency, quote, networkStatus) + val networksStatuses = maybeNetworkStatuses?.bind()?.toNonEmptySetOrNull() + val quotes = recover({ maybeQuotes?.bind()?.toNonEmptySetOrNull() }) { + quotesRetrievingFailed = true + null + } + + currencies.map { currency -> + val quote = quotes?.firstOrNull { it.rawCurrencyId == currency.id.rawCurrencyId } + val networkStatus = networksStatuses?.firstOrNull { it.networkId == currency.networkId } + + createStatus(currency, quote, networkStatus, ignoreQuote = quotesRetrievingFailed) } } private fun createStatus( - token: CryptoCurrency, + currency: CryptoCurrency, + maybeQuote: Either, + maybeNetworkStatus: Either, + ): Either = either { + var quoteRetrievingFailed = false + + val networkStatus = maybeNetworkStatus.bind() + val quote = recover({ maybeQuote.bind() }) { + quoteRetrievingFailed = true + null + } + + createStatus(currency, quote, networkStatus, ignoreQuote = quoteRetrievingFailed) + } + + private fun createStatus( + currency: CryptoCurrency, quote: Quote?, networkStatus: NetworkStatus?, + ignoreQuote: Boolean, ): CryptoCurrencyStatus { val currencyStatusOperations = CurrencyStatusOperations( - currency = token, + currency = currency, quote = quote, networkStatus = networkStatus, + ignoreQuote = ignoreQuote, ) return currencyStatusOperations.createTokenStatus() diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt index 8653aae3e5..b37f324c1f 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/CurrencyStatusOperations.kt @@ -10,6 +10,7 @@ internal class CurrencyStatusOperations( private val currency: CryptoCurrency, private val quote: Quote?, private val networkStatus: NetworkStatus?, + private val ignoreQuote: Boolean, ) { fun createTokenStatus(): CryptoCurrencyStatus = CryptoCurrencyStatus(currency, createStatus()) @@ -28,6 +29,10 @@ internal class CurrencyStatusOperations( val amount = status.amounts[currency.id] ?: return CryptoCurrencyStatus.Unreachable return when { + ignoreQuote -> CryptoCurrencyStatus.NoQuote( + amount = amount, + hasTransactionsInProgress = status.hasTransactionsInProgress, + ) currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom( amount = amount, fiatAmount = calculateFiatAmountOrNull(amount, quote?.fiatRate), diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt index 830950ae1e..ec63a9fa0c 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/operations/TokenListFiatBalanceOperations.kt @@ -27,7 +27,7 @@ internal class TokenListFiatBalanceOperations( break } is CryptoCurrencyStatus.NoAccount -> { - fiatBalance = recalculateBalanceForNoAccountStatus(fiatBalance) + fiatBalance = recalculateBalanceWithoutQuote(fiatBalance) } is CryptoCurrencyStatus.Loaded -> { fiatBalance = recalculateBalance(status, fiatBalance) @@ -35,13 +35,16 @@ internal class TokenListFiatBalanceOperations( is CryptoCurrencyStatus.Custom -> { fiatBalance = recalculateBalance(status, fiatBalance) } + is CryptoCurrencyStatus.NoQuote -> { + fiatBalance = recalculateBalanceWithoutQuote(fiatBalance) + } } } return fiatBalance } - private fun recalculateBalanceForNoAccountStatus(currentBalance: TokenList.FiatBalance): TokenList.FiatBalance { + private fun recalculateBalanceWithoutQuote(currentBalance: TokenList.FiatBalance): TokenList.FiatBalance { return with(currentBalance) { (this as? TokenList.FiatBalance.Loaded)?.copy( isAllAmountsSummarized = false, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt index 707e9e90ef..3d5b25e5eb 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetPrimaryCurrencyUseCaseTest.kt @@ -59,9 +59,9 @@ internal class GetPrimaryCurrencyUseCaseTest { } @Test - fun `when quotes getting failed then error should be received`() = runTest { + fun `when quotes getting failed then currency with no quote status should be received`() = runTest { // Given - val expectedResult = CurrencyError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = MockTokensStates.noQuotesTokensStatuses.first().right() val useCase = getUseCase(quotes = flowOf(DataError.NetworkError.NoInternetConnection.left())) @@ -100,8 +100,8 @@ internal class GetPrimaryCurrencyUseCaseTest { } @Test - fun `when quotes flow is empty then error should be received`() = runTest { - val expectedResult = CurrencyError.UnableToCreateCurrency.left() + fun `when quotes flow is empty then no quote status should be received`() = runTest { + val expectedResult = MockTokensStates.noQuotesTokensStatuses.first().right() val useCase = getUseCase(quotes = flowOf()) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt index 00d7db54b3..8b9eebf8a8 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/GetTokenListUseCaseTest.kt @@ -10,6 +10,7 @@ import com.tangem.domain.tokens.mock.MockQuotes import com.tangem.domain.tokens.mock.MockTokenLists import com.tangem.domain.tokens.mock.MockTokens import com.tangem.domain.tokens.model.NetworkStatus +import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.models.Quote @@ -32,7 +33,10 @@ internal class GetTokenListUseCaseTest { @Test fun `when list ungrouped and unsorted then correct token list should be returned`() = runTest { // Given - val expectedResult = MockTokenLists.failedUngroupedTokenList.right() + val expectedResult = listOf( + MockTokenLists.loadingUngroupedTokenList.right(), + MockTokenLists.failedUngroupedTokenList.right(), + ) val useCase = getUseCase( isGrouped = flowOf(false.right()), @@ -40,7 +44,30 @@ internal class GetTokenListUseCaseTest { ) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() + + // Then + assertEquals(expectedResult, result) + } + + @Test + fun `when list refreshed then correct token list should be returned`() = runTest { + // Given + val expectedResult = listOf( + MockTokenLists.failedUngroupedTokenList.right(), + ) + + val useCase = getUseCase( + isGrouped = flowOf(false.right()), + isSortedByBalance = flowOf(false.right()), + ) + + // When + val result = useCase(userWalletId, refresh = true) + .take(count = 1) + .toList() // Then assertEquals(expectedResult, result) @@ -61,14 +88,22 @@ internal class GetTokenListUseCaseTest { } @Test - fun `when quotes getting failed then error should be received`() = runTest { + fun `when quotes getting failed then token list without quotes should be received`() = runTest { // Given - val expectedResult = TokenListError.DataError(DataError.NetworkError.NoInternetConnection).left() + val expectedResult = listOf( + MockTokenLists.loadingUngroupedTokenList.right(), + MockTokenLists.noQuotesUngroupedTokenList.right(), + ) - val useCase = getUseCase(quotes = flowOf(DataError.NetworkError.NoInternetConnection.left())) + val useCase = getUseCase( + quotes = flowOf(DataError.NetworkError.NoInternetConnection.left()), + statuses = flowOf(MockNetworks.verifiedNetworksStatuses.right()), + ) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) @@ -99,7 +134,7 @@ internal class GetTokenListUseCaseTest { val useCase = getUseCase(statuses = flowOf(DataError.NetworkError.NoInternetConnection.left())) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId, refresh = true).first() // Then assertEquals(expectedResult, result) @@ -138,6 +173,7 @@ internal class GetTokenListUseCaseTest { // Given val error = DataError.NetworkError.NoInternetConnection.left() val expectedResult = listOf( + MockTokenLists.loadingUngroupedTokenList.right(), MockTokenLists.failedUngroupedTokenList.right(), TokenListError.DataError(DataError.NetworkError.NoInternetConnection).left(), ) @@ -151,7 +187,7 @@ internal class GetTokenListUseCaseTest { // When val result = useCase(userWalletId) - .take(count = 2) + .take(count = 3) .toList() // Then @@ -160,12 +196,17 @@ internal class GetTokenListUseCaseTest { @Test fun `when list grouped then correct token list should be received`() = runTest { - val expectedResult = MockTokenLists.failedGroupedTokenList.right() + val expectedResult = listOf( + MockTokenLists.loadingGroupedTokenList.right(), + MockTokenLists.failedGroupedTokenList.right(), + ) val useCase = getUseCase(isGrouped = flowOf(true.right())) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) @@ -189,7 +230,10 @@ internal class GetTokenListUseCaseTest { @Test fun `when list is sorted and ungrouped then correct token list should be received`() = runTest { - val expectedResult = MockTokenLists.sortedUngroupedTokenList.right() + val expectedResult = listOf( + MockTokenLists.loadingUngroupedTokenList.copy(sortedBy = TokenList.SortType.BALANCE).right(), + MockTokenLists.sortedUngroupedTokenList.right(), + ) val useCase = getUseCase( statuses = flowOf(MockNetworks.verifiedNetworksStatuses.right()), @@ -198,7 +242,9 @@ internal class GetTokenListUseCaseTest { ) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) @@ -206,7 +252,10 @@ internal class GetTokenListUseCaseTest { @Test fun `when list is sorted and grouped then correct token list should be received`() = runTest { - val expectedResult = MockTokenLists.sortedGroupedTokenList.right() + val expectedResult = listOf( + MockTokenLists.loadingGroupedTokenList.copy(sortedBy = TokenList.SortType.BALANCE).right(), + MockTokenLists.sortedGroupedTokenList.right(), + ) val useCase = getUseCase( statuses = flowOf(MockNetworks.verifiedNetworksStatuses.right()), @@ -215,7 +264,9 @@ internal class GetTokenListUseCaseTest { ) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) @@ -236,7 +287,10 @@ internal class GetTokenListUseCaseTest { @Test fun `when networks is empty and list is grouped then ungrouped list should be received`() = runTest { - val expectedResult = TokenListError.UnableToSortTokenList(MockTokenLists.failedUngroupedTokenList).left() + val expectedResult = listOf( + TokenListError.UnableToSortTokenList(MockTokenLists.loadingUngroupedTokenList).left(), + TokenListError.UnableToSortTokenList(MockTokenLists.failedUngroupedTokenList).left(), + ) val useCase = getUseCase( networks = emptySet().right(), @@ -244,7 +298,9 @@ internal class GetTokenListUseCaseTest { ) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) @@ -265,12 +321,17 @@ internal class GetTokenListUseCaseTest { @Test fun `when networks statuses flow is empty then error should be received`() = runTest { - val expectedResult = TokenListError.EmptyTokens.left() + val expectedResult = listOf( + MockTokenLists.loadingUngroupedTokenList.right(), + TokenListError.EmptyTokens.left(), + ) val useCase = getUseCase(statuses = flowOf()) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) @@ -290,13 +351,21 @@ internal class GetTokenListUseCaseTest { } @Test - fun `when quotes flow is empty then error should be received`() = runTest { - val expectedResult = TokenListError.EmptyTokens.left() + fun `when quotes flow is empty then list without quotes should be received`() = runTest { + val expectedResult = listOf( + MockTokenLists.loadingUngroupedTokenList.right(), + MockTokenLists.noQuotesUngroupedTokenList.right(), + ) - val useCase = getUseCase(quotes = flowOf()) + val useCase = getUseCase( + statuses = flowOf(MockNetworks.verifiedNetworksStatuses.right()), + quotes = flowOf(), + ) // When - val result = useCase(userWalletId).first() + val result = useCase(userWalletId) + .take(count = 2) + .toList() // Then assertEquals(expectedResult, result) diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt index de1f837afd..cbfd65a56f 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokenLists.kt @@ -41,9 +41,14 @@ internal object MockTokenLists { sortedBy = TokenList.SortType.NONE, ) + val noQuotesUngroupedTokenList = failedUngroupedTokenList.copy( + totalFiatBalance = TokenList.FiatBalance.Loaded(amount = BigDecimal.ZERO, isAllAmountsSummarized = false), + currencies = MockTokensStates.noQuotesTokensStatuses, + ) + val loadingUngroupedTokenList = with(failedUngroupedTokenList) { copy( - currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }.toNonEmptyListOrNull()!!, + currencies = currencies.map { it.copy(value = CryptoCurrencyStatus.Loading) }, totalFiatBalance = TokenList.FiatBalance.Loading, ) } @@ -54,8 +59,7 @@ internal object MockTokenLists { groups = groups.map { group -> group.copy( currencies = group.currencies - .map { it.copy(value = CryptoCurrencyStatus.Loading) } - .toNonEmptyListOrNull()!!, + .map { it.copy(value = CryptoCurrencyStatus.Loading) }, ) }.toNonEmptyListOrNull()!!, ) @@ -95,7 +99,6 @@ internal object MockTokenLists { get() { val tokens = MockTokensStates.loadedTokensStates .sortedByDescending { it.value.fiatAmount } - .toNonEmptyListOrNull()!! return unsortedUngroupedTokenList.copy( currencies = tokens, diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt index 4dd2d81154..ee6b0a6a1e 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokensStates.kt @@ -87,4 +87,13 @@ internal object MockTokensStates { ), ) } + + val noQuotesTokensStatuses = loadedTokensStates.map { currency -> + currency.copy( + value = CryptoCurrencyStatus.NoQuote( + amount = currency.value.amount!!, + hasTransactionsInProgress = false, + ), + ) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index 143c38fdb4..6abcf3e185 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -43,23 +43,14 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( private fun getMarketPriceState(status: CryptoCurrencyStatus.Status, currencyName: String): MarketPriceBlockState { return when (status) { - is CryptoCurrencyStatus.Loaded -> MarketPriceBlockState.Content( + is CryptoCurrencyStatus.NoQuote, + is CryptoCurrencyStatus.Loaded, + -> MarketPriceBlockState.Content( currencyName = currencyName, - price = BigDecimalFormatter.formatFiatAmount( - fiatAmount = status.fiatRate, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, - ), + price = formatPrice(status), priceChangeConfig = PriceChangeConfig( - valueInPercent = BigDecimalFormatter.formatPercent( - percent = status.priceChange, - useAbsoluteValue = true, - ), - type = if (status.priceChange > BigDecimal.ZERO) { - PriceChangeConfig.Type.UP - } else { - PriceChangeConfig.Type.DOWN - }, + valueInPercent = formatPriceChange(status), + type = getPriceChangeType(status), ), ) is CryptoCurrencyStatus.Loading -> MarketPriceBlockState.Loading(currencyName) @@ -77,7 +68,9 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( ): WalletsListConfig { val selectedWallet = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex] val updatedWallet = when (status) { - is CryptoCurrencyStatus.Loaded -> { + is CryptoCurrencyStatus.NoQuote, + is CryptoCurrencyStatus.Loaded, + -> { WalletCardState.Content( id = selectedWallet.id, title = selectedWallet.title, @@ -88,11 +81,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( ), imageResId = selectedWallet.imageResId, onClick = selectedWallet.onClick, - balance = BigDecimalFormatter.formatFiatAmount( - fiatAmount = status.fiatAmount, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, - ), + balance = formatFiatAmount(status), ) } is CryptoCurrencyStatus.Loading -> { @@ -124,4 +113,43 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( .set(index = state.walletsListConfig.selectedWalletIndex, element = updatedWallet), ) } + + private fun getPriceChangeType(status: CryptoCurrencyStatus.Status): PriceChangeConfig.Type { + val priceChange = status.priceChange ?: return PriceChangeConfig.Type.DOWN + + return if (priceChange > BigDecimal.ZERO) { + PriceChangeConfig.Type.UP + } else { + PriceChangeConfig.Type.DOWN + } + } + + private fun formatPriceChange(status: CryptoCurrencyStatus.Status): String { + val priceChange = status.priceChange ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + + return BigDecimalFormatter.formatPercent( + percent = priceChange, + useAbsoluteValue = true, + ) + } + + private fun formatPrice(status: CryptoCurrencyStatus.Status): String { + val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + + return BigDecimalFormatter.formatFiatAmount( + fiatAmount = fiatRate, + fiatCurrencyCode = fiatCurrencyCode, + fiatCurrencySymbol = fiatCurrencySymbol, + ) + } + + private fun formatFiatAmount(status: CryptoCurrencyStatus.Status): String { + val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN + + return BigDecimalFormatter.formatFiatAmount( + fiatAmount = fiatAmount, + fiatCurrencyCode = fiatCurrencyCode, + fiatCurrencySymbol = fiatCurrencySymbol, + ) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index a3b84ee0b5..76a4c01582 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -33,6 +33,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter( is CryptoCurrencyStatus.Loading -> TokenItemState.Loading is CryptoCurrencyStatus.Loaded, is CryptoCurrencyStatus.Custom, + is CryptoCurrencyStatus.NoQuote, -> value.mapToTokenItemState() // TODO: Add other token item states, currently not designed is CryptoCurrencyStatus.MissedDerivation, From 606ae0918b648cac7bc37d8bd0539a0e5a8f4ff4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 15:18:02 +0800 Subject: [PATCH 28/52] Updated on 2026-08-14 --- .../buttons/HorizontalActionChips.kt | 6 +- .../presentation/common/WalletPreviewData.kt | 4 +- .../common/component/TokenItem.kt | 121 ++++++++++-------- .../common/state/TokenItemState.kt | 14 +- .../state/components/WalletTokensListState.kt | 15 ++- .../wallet/ui/components/WalletsList.kt | 6 +- .../components/common/WalletNotifications.kt | 1 + .../multicurrency/MultiCurrencyContent.kt | 8 +- .../MultiCurrencyOrganizeButton.kt | 6 +- .../SingleCurrencyControlButtons.kt | 4 +- .../SingleCurrencyMarketPriceBlock.kt | 7 +- ...ryptoCurrencyStatusToTokenItemConverter.kt | 2 +- .../wallet/utils/LoadingItemsProvider.kt | 12 +- 13 files changed, 129 insertions(+), 77 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt index c778c6c0a4..6b20a931f8 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/HorizontalActionChips.kt @@ -31,7 +31,11 @@ fun HorizontalActionChips( verticalAlignment = Alignment.CenterVertically, contentPadding = contentPadding, ) { - items(items = buttons, itemContent = { ActionButton(config = it) }) + items( + items = buttons, + key = { config -> "${config.text.hashCode()} ${config.iconResId}" }, + itemContent = { ActionButton(config = it) }, + ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index cfb5eeb72e..f7e10f8451 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -5,8 +5,8 @@ import com.tangem.core.ui.R import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.components.transactions.state.TransactionState -import com.tangem.core.ui.extensions.TextReference import com.tangem.core.ui.components.transactions.state.TxHistoryState +import com.tangem.core.ui.extensions.TextReference import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.common.state.TokenItemState import com.tangem.feature.wallet.presentation.common.state.TokenItemState.TokenOptionsState @@ -142,7 +142,7 @@ internal object WalletPreviewData { ) } - val loadingTokenItemState by lazy { TokenItemState.Loading } + val loadingTokenItemState by lazy { TokenItemState.Loading(id = "Loading#1") } private const val networksSize = 10 private const val tokensSize = 3 diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt index 5c4cb19fb7..8effab7dad 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt @@ -1,6 +1,9 @@ package com.tangem.feature.wallet.presentation.common.component import androidx.annotation.DrawableRes +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -16,11 +19,11 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstrainedLayoutReference import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.ConstraintLayoutScope @@ -124,7 +127,7 @@ internal fun UnreachableTokenItem(state: TokenItemState.Unreachable, modifier: M options = { ref -> Text( modifier = Modifier.constrainAsOptionsItem(scope = this, ref), - text = "Unreachable", // TODO (conform this text) + text = stringResource(id = R.string.common_unreachable), style = TangemTypography.body2, color = TangemTheme.colors.text.tertiary, ) @@ -143,7 +146,7 @@ private fun LoadingTokenItem(modifier: Modifier = Modifier) { vertical = TangemTheme.dimens.spacing4, ), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12), + horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing24), ) { CircleShimmer(modifier = Modifier.size(size = TangemTheme.dimens.size42)) Row( @@ -188,22 +191,17 @@ private fun LoadingTokenItem(modifier: Modifier = Modifier) { * Block for end part of token item * shows status is reachable, is drag, hidden or show balance */ +@OptIn(ExperimentalAnimationApi::class) @Composable private fun TokenOptionsBlock(state: TokenOptionsState, modifier: Modifier = Modifier) { - when (state) { - is TokenOptionsState.Visible -> { - TokenFiatPercentageBlock( - modifier = modifier, - fiatAmount = state.fiatAmount, - priceChange = state.priceChange, - ) - } - is TokenOptionsState.Hidden -> { - TokenFiatPercentageBlock( - modifier = modifier, - fiatAmount = DOTS, - priceChange = state.priceChange, - ) + AnimatedContent(targetState = state, label = "Update the options", modifier = modifier) { options -> + when (options) { + is TokenOptionsState.Visible -> { + TokenFiatPercentageBlock(fiatAmount = options.fiatAmount, priceChange = options.priceChange) + } + is TokenOptionsState.Hidden -> { + TokenFiatPercentageBlock(fiatAmount = DOTS, priceChange = options.priceChange) + } } } } @@ -243,7 +241,7 @@ private fun InternalTokenItem( TokenTitleAmountBlock( modifier = Modifier - .padding(horizontal = TangemTheme.dimens.spacing12) + .padding(horizontal = TangemTheme.dimens.spacing8) .constrainAs(tokenNameItem) { centerVerticallyTo(parent) start.linkTo(iconItem.end) @@ -298,17 +296,18 @@ private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Bo style = TangemTypography.subtitle2, color = TangemTheme.colors.text.primary1, ) - if (hasPending) { + + AnimatedVisibility(visible = hasPending, modifier = Modifier.align(Alignment.CenterVertically)) { Image( - modifier = Modifier.align(Alignment.CenterVertically), painter = painterResource(id = R.drawable.img_loader_15), contentDescription = null, ) } } - if (!amount.isNullOrBlank()) { + + AnimatedVisibility(visible = !amount.isNullOrBlank()) { Text( - text = amount, + text = requireNotNull(amount), style = TangemTypography.body2, color = TangemTheme.colors.text.tertiary, ) @@ -316,6 +315,7 @@ private fun TokenTitleAmountBlock(title: String, amount: String?, hasPending: Bo } } +@OptIn(ExperimentalAnimationApi::class) @Composable private fun TokenFiatPercentageBlock( fiatAmount: String, @@ -334,30 +334,39 @@ private fun TokenFiatPercentageBlock( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End, ) { - val iconChangeArrow: Int - val changeTextColor: Color - when (priceChange.type) { - PriceChangeConfig.Type.UP -> { - iconChangeArrow = R.drawable.img_arrow_up_8 - changeTextColor = TangemTheme.colors.text.accent - } - PriceChangeConfig.Type.DOWN -> { - iconChangeArrow = R.drawable.img_arrow_down_8 - changeTextColor = TangemTheme.colors.text.warning - } + AnimatedContent( + targetState = priceChange.type, + label = "Update the price change's arrow", + modifier = Modifier.align(Alignment.CenterVertically), + ) { + Image( + painter = painterResource( + id = when (priceChange.type) { + PriceChangeConfig.Type.UP -> R.drawable.img_arrow_up_8 + PriceChangeConfig.Type.DOWN -> R.drawable.img_arrow_down_8 + }, + ), + contentDescription = null, + ) } - Image( - modifier = Modifier.align(Alignment.CenterVertically), - painter = painterResource(id = iconChangeArrow), - contentDescription = null, - ) + SpacerW4() - Text( + + AnimatedContent( + targetState = priceChange.type, + label = "Update the price change's arrow", modifier = Modifier.align(Alignment.CenterVertically), - text = priceChange.valueInPercent, - style = TangemTypography.body2, - color = changeTextColor, - ) + ) { + Text( + modifier = Modifier.align(Alignment.CenterVertically), + text = priceChange.valueInPercent, + style = TangemTypography.body2, + color = when (priceChange.type) { + PriceChangeConfig.Type.UP -> TangemTheme.colors.text.accent + PriceChangeConfig.Type.DOWN -> TangemTheme.colors.text.warning + }, + ) + } } } } @@ -389,20 +398,20 @@ private fun TokenIcon( contentDescription = null, ) - if (networkIconRes != null) { - Box( + AnimatedVisibility( + visible = networkIconRes != null, + modifier = Modifier + .align(Alignment.TopEnd) + .size(TangemTheme.dimens.size18) + .background(color = Color.White, shape = CircleShape), + ) { + Image( modifier = Modifier - .align(Alignment.TopEnd) - .size(TangemTheme.dimens.size18) - .background(color = Color.White, shape = CircleShape), - contentAlignment = Alignment.Center, - ) { - Image( - modifier = Modifier.padding(all = 0.5.dp), - painter = painterResource(id = networkIconRes), - contentDescription = null, - ) - } + .padding(all = TangemTheme.dimens.spacing0_5) + .align(Alignment.Center), + painter = painterResource(id = requireNotNull(networkIconRes)), + contentDescription = null, + ) } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt index b1c8417e9e..b53c7458b8 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt @@ -8,12 +8,16 @@ import com.tangem.core.ui.components.marketprice.PriceChangeConfig @Immutable internal sealed interface TokenItemState { + /** Unique id */ + val id: String + /** Loading token state */ - object Loading : TokenItemState + data class Loading(override val id: String) : TokenItemState /** * Content token state * + * @property id unique id * @property tokenIconUrl token icon url * @property tokenIconResId token icon resource id * @property networkIconResId network icon resource id, may be null if it is a coin @@ -23,7 +27,7 @@ internal sealed interface TokenItemState { * @property tokenOptions state for token options */ data class Content( - val id: String, + override val id: String, val tokenIconUrl: String?, @DrawableRes val tokenIconResId: Int, @DrawableRes val networkIconResId: Int?, @@ -36,13 +40,15 @@ internal sealed interface TokenItemState { /** * Draggable token state * + * @property id unique id * @property tokenIconUrl token icon url * @property tokenIconResId token icon resource id * @property networkIconResId network icon resource id, may be null if it is a coin * @property name token name + * @property fiatAmount fiat amount of token */ data class Draggable( - val id: String, + override val id: String, val tokenIconUrl: String?, @DrawableRes val tokenIconResId: Int, @DrawableRes val networkIconResId: Int?, @@ -60,7 +66,7 @@ internal sealed interface TokenItemState { * @property name token name */ data class Unreachable( - val id: String, + override val id: String, val tokenIconUrl: String?, @DrawableRes val tokenIconResId: Int, @DrawableRes val networkIconResId: Int?, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index 98430d72a6..cc607f9f17 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -1,8 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.state.components +import com.tangem.core.ui.components.wallet.WalletLockedContentState import com.tangem.core.ui.extensions.TextReference import com.tangem.feature.wallet.impl.R -import com.tangem.core.ui.components.wallet.WalletLockedContentState import com.tangem.feature.wallet.presentation.common.state.TokenItemState import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -23,9 +23,8 @@ internal sealed class WalletTokensListState( /** Loading content state */ object Loading : WalletTokensListState( items = persistentListOf( - TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), - TokensListItemState.Token(state = TokenItemState.Loading), - TokensListItemState.Token(state = TokenItemState.Loading), + TokensListItemState.Token(state = TokenItemState.Loading(id = FIRST_LOADING_TOKEN_ID)), + TokensListItemState.Token(state = TokenItemState.Loading(id = SECOND_LOADING_TOKEN_ID)), ), onOrganizeTokensClick = null, ) @@ -46,7 +45,7 @@ internal sealed class WalletTokensListState( WalletTokensListState( items = persistentListOf( TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), - TokensListItemState.Token(state = TokenItemState.Loading), + TokensListItemState.Token(state = TokenItemState.Loading(id = LOCKED_TOKEN_ID)), ), onOrganizeTokensClick = null, ), @@ -69,4 +68,10 @@ internal sealed class WalletTokensListState( */ data class Token(val state: TokenItemState) : TokensListItemState } + + private companion object { + const val FIRST_LOADING_TOKEN_ID = "Loading#1" + const val SECOND_LOADING_TOKEN_ID = "Loading#2" + const val LOCKED_TOKEN_ID = "Locked#1" + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt index dcae40e426..09acf941f4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/WalletsList.kt @@ -45,7 +45,11 @@ internal fun WalletsList(config: WalletsListConfig, lazyListState: LazyListState horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing8), flingBehavior = rememberSnapFlingBehavior(lazyListState = lazyListState), ) { - items(items = config.wallets, key = { it.id.stringValue }) { state -> + items( + items = config.wallets, + key = { it.id.stringValue }, + contentType = { it::class.java }, + ) { state -> WalletCard( state = state, modifier = Modifier diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt index baa0144e1b..c6ef7f89ef 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/common/WalletNotifications.kt @@ -21,6 +21,7 @@ internal fun LazyListScope.notifications(configs: ImmutableList index }, + key = { _, item -> + when (item) { + is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> item.value.hashCode() + is WalletTokensListState.TokensListItemState.Token -> item.state.id + } + }, + contentType = { _, item -> item::class.java }, itemContent = { index, item -> MultiCurrencyContentItem( state = item, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt index a995ca2715..210bfd423c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyOrganizeButton.kt @@ -4,6 +4,8 @@ import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.ui.Modifier +private const val ORGANIZE_BUTTON_CONTENT_TYPE = "OrganizeTokensButton" + /** * Organize tokens button * @@ -14,5 +16,7 @@ import androidx.compose.ui.Modifier */ @OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.organizeButton(onClick: (() -> Unit)?, modifier: Modifier = Modifier) { - item { OrganizeTokensButton(onClick = onClick, modifier = modifier.animateItemPlacement()) } + item(key = ORGANIZE_BUTTON_CONTENT_TYPE, contentType = ORGANIZE_BUTTON_CONTENT_TYPE) { + OrganizeTokensButton(onClick = onClick, modifier = modifier.animateItemPlacement()) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt index f88ea6d07f..4843bd9dc4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyControlButtons.kt @@ -10,6 +10,8 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletMana import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList +private const val CONTROL_BUTTONS_CONTENT_TYPE = "ControlButtons" + /** * Single currency control buttons. Like, "Buy", "Sell", etc * @@ -20,7 +22,7 @@ import kotlinx.collections.immutable.toImmutableList */ @OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.controlButtons(configs: ImmutableList, modifier: Modifier = Modifier) { - item { + item(key = CONTROL_BUTTONS_CONTENT_TYPE, contentType = CONTROL_BUTTONS_CONTENT_TYPE) { HorizontalActionChips( buttons = configs.map(WalletManageButton::config).toImmutableList(), modifier = modifier.animateItemPlacement(), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt index b839d42baf..12d9b505b7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/singlecurrency/SingleCurrencyMarketPriceBlock.kt @@ -16,5 +16,10 @@ import com.tangem.core.ui.components.marketprice.MarketPriceBlockState */ @OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.marketPriceBlock(state: MarketPriceBlockState, modifier: Modifier = Modifier) { - item { MarketPriceBlock(state = state, modifier = modifier.animateItemPlacement()) } + item( + key = MarketPriceBlockState::class.java, + contentType = MarketPriceBlockState::class.java, + ) { + MarketPriceBlock(state = state, modifier = modifier.animateItemPlacement()) + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index 76a4c01582..2779e6473a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -30,7 +30,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter( override fun convert(value: CryptoCurrencyStatus): TokenItemState { return when (value.value) { - is CryptoCurrencyStatus.Loading -> TokenItemState.Loading + is CryptoCurrencyStatus.Loading -> TokenItemState.Loading(id = value.currency.id.value) is CryptoCurrencyStatus.Loaded, is CryptoCurrencyStatus.Custom, is CryptoCurrencyStatus.NoQuote, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt index 174c0b9f50..c7e545dec3 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/LoadingItemsProvider.kt @@ -8,8 +8,14 @@ import kotlinx.collections.immutable.toImmutableList internal object LoadingItemsProvider { fun getLoadingMultiCurrencyTokens(): ImmutableList { - return buildList(capacity = 5) { - add(WalletTokensListState.TokensListItemState.Token(state = TokenItemState.Loading)) - }.toImmutableList() + val items = mutableListOf() + repeat(times = 5) { + items.add( + WalletTokensListState.TokensListItemState.Token( + state = TokenItemState.Loading(id = "Loading#$it"), + ), + ) + } + return items.toImmutableList() } } \ No newline at end of file From c203e8cc5294d8d75cda830c3cc75afa1029b149 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 09:41:36 +0300 Subject: [PATCH 29/52] Updated on 2026-08-14 --- .../ui/OnboardingSeedPhraseStateHandler.kt | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt index 8a26aa71f0..05eaf95d55 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingSeedPhraseStateHandler.kt @@ -3,11 +3,13 @@ package com.tangem.tap.features.onboarding.products.wallet.ui import androidx.compose.runtime.collectAsState import com.tangem.feature.onboarding.api.OnboardingSeedPhrase import com.tangem.feature.onboarding.api.OnboardingSeedPhraseApi +import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseScreen import com.tangem.feature.onboarding.presentation.wallet2.viewmodel.SeedPhraseViewModel import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletStep +import com.tangem.wallet.R /** [REDACTED_AUTHOR] @@ -44,11 +46,27 @@ internal class OnboardingSeedPhraseStateHandler( walletFragment.binding.onboardingWalletContainer.hide() walletFragment.bindingSeedPhrase.onboardingSeedPhraseContainer.show() walletFragment.bindingSeedPhrase.onboardingSeedPhraseContainer.setContent { + val subScreen = viewModel.currentScreen.collectAsState().value + setMainScreenToolbarTitle(walletFragment, subScreen) + onboardingSeedPhraseApi.ScreenContent( uiState = viewModel.uiState, - subScreen = viewModel.currentScreen.collectAsState().value, + subScreen = subScreen, progress = viewModel.progress.collectAsState(0).value.toFloat() / onboardingWalletMaxProgress, ) } } + + private fun setMainScreenToolbarTitle(walletFragment: OnboardingWalletFragment, subScreen: SeedPhraseScreen) { + val titleResId = when (subScreen) { + SeedPhraseScreen.Intro, + SeedPhraseScreen.AboutSeedPhrase, + SeedPhraseScreen.YourSeedPhrase, + SeedPhraseScreen.CheckSeedPhrase, + -> R.string.onboarding_create_wallet_header + SeedPhraseScreen.ImportSeedPhrase -> R.string.onboarding_seed_intro_button_import + } + + walletFragment.binding.toolbar.title = walletFragment.getString(titleResId) + } } \ No newline at end of file From a62354d5fbb1740a65e374de505939ffd2b0eff6 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 17:19:17 +0800 Subject: [PATCH 30/52] Updated on 2026-08-14 --- core/res/src/main/res/values/strings.xml | 1 + .../com/tangem/core/ui/res/TangemDimens.kt | 2 + .../error/TokenListErrorConverter.kt | 1 - .../error/TokenListSortingErrorConverter.kt | 1 - .../state/components/WalletTokensListState.kt | 28 ++++++--- .../presentation/wallet/ui/WalletScreen.kt | 6 +- .../multicurrency/MultiCurrencyContent.kt | 62 ++++++++++++++++++- .../wallet/utils/TokenListErrorConverter.kt | 1 - .../utils/TokenListToContentItemsConverter.kt | 34 ++++++---- .../utils/TokenListToWalletStateConverter.kt | 4 +- .../WalletNotificationsListFactory.kt | 4 +- .../wallet/viewmodels/WalletViewModel.kt | 6 +- .../src/main/res/drawable/ic_empty_64.xml | 13 ++++ 13 files changed, 130 insertions(+), 33 deletions(-) create mode 100644 features/wallet/impl/src/main/res/drawable/ic_empty_64.xml diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index 6b09dce789..a04ba073c9 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -160,6 +160,7 @@ Tap the card Internal error: wallet manager not found You have updated biometrics, scan your card to enter + To begin tracking your crypto assets and transactions, add tokens. You have completed all of the lessons, and are now eligible to receive your 1INCH tokens Complete three lessons and receive %d 1INCH token to your wallet diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt index ddba0a20cd..1c60364fa2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemDimens.kt @@ -105,11 +105,13 @@ data class TangemDimens internal constructor( val spacing38: Dp = 38.dp, val spacing40: Dp = 40.dp, val spacing44: Dp = 44.dp, + val spacing48: Dp = 48.dp, val spacing50: Dp = 50.dp, val spacing52: Dp = 52.dp, val spacing54: Dp = 54.dp, val spacing56: Dp = 56.dp, val spacing92: Dp = 92.dp, + val spacing96: Dp = 96.dp, val spacing154: Dp = 154.dp, // endregion Spacing ) \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt index 9c11b12566..53f553015f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListErrorConverter.kt @@ -11,7 +11,6 @@ internal class TokenListErrorConverter( private val inProgressStateConverter: InProgressStateConverter, ) : Converter { - // TODO: [REDACTED_JIRA] override fun convert(value: TokenListError): OrganizeTokensState { return inProgressStateConverter.convertBack(currentState()) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt index fab77e7e34..e738cdada5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/error/TokenListSortingErrorConverter.kt @@ -11,7 +11,6 @@ internal class TokenListSortingErrorConverter( private val inProgressStateConverter: InProgressStateConverter, ) : Converter { - // TODO: [REDACTED_JIRA] override fun convert(value: TokenListSortingError): OrganizeTokensState { return inProgressStateConverter.convertBack(currentState()) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt index cc607f9f17..1209a2da37 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/components/WalletTokensListState.kt @@ -10,18 +10,26 @@ import kotlinx.collections.immutable.persistentListOf /** * Wallet tokens list state * - * @property items content items - * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked - * [REDACTED_AUTHOR] */ -internal sealed class WalletTokensListState( - open val items: ImmutableList, - open val onOrganizeTokensClick: (() -> Unit)?, -) { +internal sealed class WalletTokensListState { + + /** Empty token list state */ + object Empty : WalletTokensListState() + + /** + * Wallet content token list state + * + * @property items content items + * @property onOrganizeTokensClick lambda be invoked when organize tokens button is clicked + */ + sealed class ContentState( + open val items: ImmutableList, + open val onOrganizeTokensClick: (() -> Unit)?, + ) : WalletTokensListState() /** Loading content state */ - object Loading : WalletTokensListState( + object Loading : ContentState( items = persistentListOf( TokensListItemState.Token(state = TokenItemState.Loading(id = FIRST_LOADING_TOKEN_ID)), TokensListItemState.Token(state = TokenItemState.Loading(id = SECOND_LOADING_TOKEN_ID)), @@ -38,11 +46,11 @@ internal sealed class WalletTokensListState( data class Content( override val items: ImmutableList, override val onOrganizeTokensClick: (() -> Unit)?, - ) : WalletTokensListState(items, onOrganizeTokensClick) + ) : ContentState(items, onOrganizeTokensClick) /** Locked content state */ object Locked : - WalletTokensListState( + ContentState( items = persistentListOf( TokensListItemState.NetworkGroupTitle(value = TextReference.Res(id = R.string.main_tokens)), TokensListItemState.Token(state = TokenItemState.Loading(id = LOCKED_TOKEN_ID)), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt index db0cee15f1..4bc618e81d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/WalletScreen.kt @@ -22,6 +22,7 @@ import com.tangem.feature.wallet.presentation.common.WalletPreviewData import com.tangem.feature.wallet.presentation.wallet.state.WalletMultiCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletSingleCurrencyState import com.tangem.feature.wallet.presentation.wallet.state.WalletState +import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.ui.components.WalletsList import com.tangem.feature.wallet.presentation.wallet.ui.components.common.* import com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency.organizeButton @@ -106,7 +107,10 @@ private fun WalletContent(state: WalletState.ContentState) { contentItems(state = state, txHistoryItems = txHistoryItems, modifier = movableItemModifier) if (state is WalletMultiCurrencyState) { - organizeButton(onClick = state.tokensListState.onOrganizeTokensClick, modifier = itemModifier) + val tokensListState = state.tokensListState + if (tokensListState is WalletTokensListState.ContentState) { + organizeButton(onClick = tokensListState.onOrganizeTokensClick, modifier = itemModifier) + } } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt index bf7451e5ec..350cb95ecf 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/ui/components/multicurrency/MultiCurrencyContent.kt @@ -1,11 +1,26 @@ package com.tangem.feature.wallet.presentation.wallet.ui.components.multicurrency import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material3.Icon +import androidx.compose.material3.Text +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign import com.tangem.core.ui.decorations.roundedShapeItemDecoration +import com.tangem.core.ui.res.TangemTheme +import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState +import kotlinx.collections.immutable.ImmutableList + +private const val NON_CONTENT_TOKENS_LIST_KEY = "NON_CONTENT_TOKENS_LIST" /** * LazyList extension for [WalletTokensListState] @@ -15,10 +30,20 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletToke * [REDACTED_AUTHOR] */ -@OptIn(ExperimentalFoundationApi::class) internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifier: Modifier = Modifier) { + when (state) { + is WalletTokensListState.ContentState -> contentItems(items = state.items, modifier = modifier) + WalletTokensListState.Empty -> nonContentItem(modifier = modifier) + } +} + +@OptIn(ExperimentalFoundationApi::class) +private fun LazyListScope.contentItems( + items: ImmutableList, + modifier: Modifier = Modifier, +) { itemsIndexed( - items = state.items, + items = items, key = { _, item -> when (item) { is WalletTokensListState.TokensListItemState.NetworkGroupTitle -> item.value.hashCode() @@ -33,9 +58,40 @@ internal fun LazyListScope.tokensListItems(state: WalletTokensListState, modifie .animateItemPlacement() .roundedShapeItemDecoration( currentIndex = index, - lastIndex = state.items.lastIndex, + lastIndex = items.lastIndex, ), ) }, ) +} + +@OptIn(ExperimentalFoundationApi::class) +private fun LazyListScope.nonContentItem(modifier: Modifier = Modifier) { + item( + key = NON_CONTENT_TOKENS_LIST_KEY, + contentType = NON_CONTENT_TOKENS_LIST_KEY, + ) { + Column( + modifier = modifier + .animateItemPlacement() + .padding(top = TangemTheme.dimens.spacing96), + verticalArrangement = Arrangement.spacedBy(space = TangemTheme.dimens.spacing16), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Icon( + painter = painterResource(id = R.drawable.ic_empty_64), + contentDescription = null, + modifier = Modifier.size(size = TangemTheme.dimens.size64), + tint = TangemTheme.colors.icon.inactive, + ) + + Text( + text = stringResource(id = R.string.main_empty_tokens_list_message), + modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing48), + color = TangemTheme.colors.text.tertiary, + textAlign = TextAlign.Center, + style = TangemTheme.typography.caption, + ) + } + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt index 4d8b4cc1f4..ac32cdb876 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListErrorConverter.kt @@ -12,7 +12,6 @@ internal class TokenListErrorConverter( private val currentStateProvider: Provider, ) : Converter { - // TODO: [REDACTED_JIRA] override fun convert(value: TokenListError): WalletMultiCurrencyState.Content { return requireNotNull(currentStateProvider() as? WalletMultiCurrencyState.Content).copy( tokensListState = WalletTokensListState.Content(items = persistentListOf(), onOrganizeTokensClick = null), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index d20d628c57..5c8fb261e0 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -27,18 +27,28 @@ internal class TokenListToContentItemsConverter( ) override fun convert(value: TokenList): WalletTokensListState { - return WalletTokensListState.Content( - items = when (value) { - is TokenList.GroupedByNetwork -> value.mapToMultiCurrencyItems() - is TokenList.Ungrouped -> value.mapToMultiCurrencyItems() - is TokenList.NotInitialized -> getLoadingMultiCurrencyTokens() - }, - onOrganizeTokensClick = if (value.totalFiatBalance is TokenList.FiatBalance.Loaded) { - clickIntents::onOrganizeTokensClick - } else { - null - }, - ) + val isEmptyList = when (value) { + is TokenList.GroupedByNetwork -> value.groups.isEmpty() + is TokenList.NotInitialized -> true + is TokenList.Ungrouped -> value.currencies.isEmpty() + } + + return if (isEmptyList) { + WalletTokensListState.Empty + } else { + WalletTokensListState.Content( + items = when (value) { + is TokenList.GroupedByNetwork -> value.mapToMultiCurrencyItems() + is TokenList.Ungrouped -> value.mapToMultiCurrencyItems() + is TokenList.NotInitialized -> getLoadingMultiCurrencyTokens() + }, + onOrganizeTokensClick = if (value.totalFiatBalance is TokenList.FiatBalance.Loaded) { + clickIntents::onOrganizeTokensClick + } else { + null + }, + ) + } } private fun TokenList.GroupedByNetwork.mapToMultiCurrencyItems(): PersistentList { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index 885c12dd52..e4e37d8e7d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -63,7 +63,9 @@ internal class TokenListToWalletStateConverter( } private fun WalletState.getRefreshingStatus(): Boolean { - return if (this is WalletMultiCurrencyState.Content) { + return if (this is WalletMultiCurrencyState.Content && + this.tokensListState is WalletTokensListState.ContentState + ) { tokensListState.items.any { tokensListItemState -> tokensListItemState is WalletTokensListState.TokensListItemState.Token && tokensListItemState.state is TokenItemState.Loading diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt index 18bfbbbe34..11beecade5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletNotificationsListFactory.kt @@ -118,7 +118,9 @@ internal class WalletNotificationsListFactory( } return currentStateProvider().let { state -> - state is WalletMultiCurrencyState.Content && state.tokensListState.items.any(isUnreachableState) + state is WalletMultiCurrencyState.Content && + state.tokensListState is WalletTokensListState.ContentState && + state.tokensListState.items.any(isUnreachableState) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 12cdb339a1..f7d4a9fb1b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -352,10 +352,12 @@ internal class WalletViewModel @Inject constructor( // Check the special components return when (this) { is WalletMultiCurrencyState -> { - tokensListState is WalletTokensListState.Loading || - tokensListState.items + val hasLoadingTokens = tokensListState is WalletTokensListState.ContentState && + (tokensListState as WalletTokensListState.ContentState).items .filterIsInstance() .any { it.state is TokenItemState.Loading } + + tokensListState is WalletTokensListState.Loading || hasLoadingTokens } is WalletSingleCurrencyState -> { txHistoryState is TxHistoryState.Loading || marketPriceBlockState is MarketPriceBlockState.Loading diff --git a/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml b/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml new file mode 100644 index 0000000000..090f906247 --- /dev/null +++ b/features/wallet/impl/src/main/res/drawable/ic_empty_64.xml @@ -0,0 +1,13 @@ + + + + + + From 131ef0ae71a473dd674926447c3899209387e58b Mon Sep 17 00:00:00 2001 From: Tangem Date: Thu, 18 May 2023 19:48:52 +0800 Subject: [PATCH 31/52] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/TapErrors.kt | 4 +- .../AddCustomTokenFloatingButton.kt | 2 +- .../dialogs/ClipboardOrScanQrDialog.kt | 2 +- .../features/home/compose/StoriesScreen.kt | 2 +- .../home/compose/views/HomeButtons.kt | 9 +-- .../products/note/OnboardingNoteFragment.kt | 2 +- .../products/twins/ui/TwinsCardsFragment.kt | 2 +- .../wallet/ui/OnboardingWalletFragment.kt | 2 +- .../tap/features/shop/ui/ShopFragment.kt | 40 +++++------ .../impl/presentation/ui/TokensListScreen.kt | 2 +- .../impl/presentation/ui/TokensListToolbar.kt | 9 +-- .../viewmodels/TokensListViewModel.kt | 4 +- .../ui/components/WalletItem.kt | 2 +- .../main/res/layout/dialog_wallet_send.xml | 2 +- app/src/main/res/layout/fragment_shop.xml | 21 +++--- .../main/res/values-de/strings-blockchain.xml | 1 - core/res/src/main/res/values-de/strings.xml | 3 + .../main/res/values-fr/strings-blockchain.xml | 1 - core/res/src/main/res/values-fr/strings.xml | 3 + .../main/res/values-it/strings-blockchain.xml | 1 - core/res/src/main/res/values-it/strings.xml | 3 + .../main/res/values-ru/strings-blockchain.xml | 1 - core/res/src/main/res/values-ru/strings.xml | 68 +++++++++++++++---- .../res/values-zh-rTW/strings-blockchain.xml | 1 - .../src/main/res/values-zh-rTW/strings.xml | 39 ++++++++--- .../main/res/values/strings-blockchain.xml | 1 - core/res/src/main/res/values/strings.xml | 60 +++++++++++++--- .../presentation/ui/Learn2earnDialogs.kt | 51 +++++++------- .../feature/swap/ui/SwapSelectTokenScreen.kt | 12 +--- 29 files changed, 218 insertions(+), 132 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index b986f9f247..8ac459bf2b 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -26,10 +26,8 @@ sealed class TapError( val stateError: String, ) : TapError(R.string.common_custom_string, listOf("Unsupported state: $stateError")) - object ScanCardError : TapError(R.string.scan_card_error) object UnknownBlockchain : TapError(R.string.wallet_error_unsupported_blockchain_subtitle) object NoInternetConnection : TapError(R.string.wallet_notification_no_internet) - object BlockchainInternalError : TapError(R.string.send_error_blockchain_internal) object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance) data class AmountLowerExistentialDeposit( override val args: List, @@ -43,7 +41,7 @@ sealed class TapError( object DustChange : TapError(R.string.send_error_dust_change) sealed class WalletManager { - object CreationError : CustomError("Can't create wallet manager") + object CreationError : CustomError(customMessage = "Can't create wallet manager") class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount) class InternalError(message: String) : CustomError(message) object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt index 5011ca010c..558534f369 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/ui/components/AddCustomTokenFloatingButton.kt @@ -29,7 +29,7 @@ internal fun AddCustomTokenFloatingButton(model: AddCustomTokenFloatingButton, m .imePadding() .padding(horizontal = TangemTheme.dimens.spacing16) .fillMaxWidth(), - text = stringResource(id = R.string.common_add), + text = stringResource(id = R.string.custom_token_add_token), iconResId = R.drawable.ic_plus_24, enabled = model.isEnabled, onClick = model.onClick, diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt index a3f94a3cd9..ce79d75c14 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/dialogs/ClipboardOrScanQrDialog.kt @@ -12,7 +12,7 @@ import com.tangem.wallet.R object ClipboardOrScanQrDialog { fun create(wcUri: String, context: Context): AlertDialog { return AlertDialog.Builder(context).apply { - setTitle(context.getString(R.string.wallet_connect_title)) + setTitle(context.getString(R.string.common_select_action)) setMessage(context.getText(R.string.wallet_connect_clipboard_alert)) setPositiveButton(context.getText(R.string.wallet_connect_paste_from_clipboard)) { _, _ -> store.dispatch(WalletConnectAction.OpenSession(wcUri)) diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt index fa3e832436..ca6b84babc 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/StoriesScreen.kt @@ -188,7 +188,7 @@ fun StoriesScreen( contentDescription = null, ) Text( - text = stringResource(id = R.string.search_tokens_title), + text = stringResource(id = R.string.common_search_tokens), fontWeight = FontWeight.Medium, fontSize = 16.sp, textAlign = TextAlign.Center, diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt b/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt index 95049be464..ed7456d300 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/views/HomeButtons.kt @@ -1,11 +1,6 @@ package com.tangem.tap.features.home.compose.views -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.heightIn -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.material.Button import androidx.compose.material.ButtonDefaults import androidx.compose.material.CircularProgressIndicator @@ -51,7 +46,7 @@ fun HomeButtons( }, content = { Text( - text = stringResource(id = R.string.welcome_unlock_card), + text = stringResource(id = R.string.home_button_scan), fontWeight = FontWeight.Medium, fontSize = 16.sp, textAlign = TextAlign.Center, diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt index cf68e35486..6cdb83e4d7 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/note/OnboardingNoteFragment.kt @@ -151,7 +151,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment() { btnAlternativeAction.isVisible = false } - tvHeader.setText(R.string.onboarding_top_up_header) + tvHeader.setText(R.string.onboarding_topup_title) if (state.balanceNonCriticalError == null) { tvBody.setText(R.string.onboarding_top_up_body) } else { diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt index 2fa0a10909..5163bf4284 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/twins/ui/TwinsCardsFragment.kt @@ -350,7 +350,7 @@ class TwinsCardsFragment : BaseOnboardingFragment() { btnAlternativeAction.isVisible = false } - tvHeader.setText(R.string.onboarding_top_up_header) + tvHeader.setText(R.string.onboarding_topup_title) tvBody.setText(R.string.onboarding_top_up_body) btnRefreshBalanceWidget.changeState(state.walletBalance.state) diff --git a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt index 269449599e..5b6afc478f 100644 --- a/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/onboarding/products/wallet/ui/OnboardingWalletFragment.kt @@ -257,7 +257,7 @@ class OnboardingWalletFragment : prepareBackupView() tvHeader.text = getText(R.string.onboarding_title_scan_origin_card) tvBody.text = getString( - R.string.onboarding_subtitle_scan_origin_card, + R.string.onboarding_subtitle_scan_primary, ) with(layoutButtonsCommon) { diff --git a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt index 58b89b9901..bfb86a0f8a 100644 --- a/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/shop/ui/ShopFragment.kt @@ -15,7 +15,6 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.tap.common.GlobalLayoutStateHandler import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.extensions.getQuantityString -import com.tangem.tap.common.extensions.hide import com.tangem.tap.common.extensions.show import com.tangem.tap.features.BaseStoreFragment import com.tangem.tap.features.shop.domain.models.ProductState @@ -153,11 +152,12 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu animateProductSelection(state.selectedProduct) handlePriceState(state) handlePromoCodeState(state) - if (shopifyFeatureToggleManager.isDynamicSalesProductsEnabled) { - handleNotificationBlock(state) - } else { - handleOrderingDelayBlock(isVisible = state.isOrderingDelayBlockVisible) - } + // TODO: https://tangem.slack.com/archives/C01HARKDLQ0/p1691421861756069 + // if (shopifyFeatureToggleManager.isDynamicSalesProductsEnabled) { + // handleNotificationBlock(state) + // } else { + // handleOrderingDelayBlock(isVisible = state.isOrderingDelayBlockVisible) + // } handleButtonsState(state) } @@ -198,20 +198,20 @@ internal class ShopFragment : BaseStoreFragment(R.layout.fragment_shop), StoreSu pbPromoCode.show(state.promoCodeLoading) } - private fun handleOrderingDelayBlock(isVisible: Boolean) { - if (isVisible) binding.tvSoldOutDesc.show() else binding.tvSoldOutDesc.hide() - } - - private fun handleNotificationBlock(state: ShopState) { - if (isVisible) { - binding.tvSoldOutDesc.show() - getSelectedSalesProduct(state)?.notification?.let { notification -> - binding.tvSoldOutDesc.text = notification.description - } - } else { - binding.tvSoldOutDesc.hide() - } - } + // private fun handleOrderingDelayBlock(isVisible: Boolean) { + // if (isVisible) binding.tvSoldOutDesc.show() else binding.tvSoldOutDesc.hide() + // } + // + // private fun handleNotificationBlock(state: ShopState) { + // if (isVisible) { + // binding.tvSoldOutDesc.show() + // getSelectedSalesProduct(state)?.notification?.let { notification -> + // binding.tvSoldOutDesc.text = notification.description + // } + // } else { + // binding.tvSoldOutDesc.hide() + // } + // } private fun handleButtonsState(state: ShopState) = with(binding) { btnPayGooglePay.root.show(state.isGooglePayAvailable) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt index b35bcd93bc..174563c6be 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListScreen.kt @@ -234,7 +234,7 @@ private fun Preview_TokensListScreen_Read() { TokensListScreen( stateHolder = TokensListStateHolder.ReadContent( toolbarState = TokensListToolbarState.Title.Read( - titleResId = R.string.search_tokens_title, + titleResId = R.string.common_search_tokens, onBackButtonClick = {}, onSearchButtonClick = {}, ), diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListToolbar.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListToolbar.kt index 03ae578e8a..f5db5aa1a1 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListToolbar.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/ui/TokensListToolbar.kt @@ -11,12 +11,7 @@ import androidx.compose.material.Icon import androidx.compose.material.IconButton import androidx.compose.material.Text import androidx.compose.material.TopAppBar -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier @@ -186,7 +181,7 @@ private fun Preview_AddTokensToolbar_ReadAccess() { TangemTheme { TokensListToolbar( state = Title.Read( - titleResId = R.string.search_tokens_title, + titleResId = R.string.common_search_tokens, onBackButtonClick = {}, onSearchButtonClick = {}, ), diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index c9ab947187..a44d631c1c 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -102,14 +102,14 @@ internal class TokensListViewModel @Inject constructor( private fun getInitialToolbarState(): TokensListToolbarState { return if (args.isManageAccess) { TokensListToolbarState.Title.Manage( - titleResId = R.string.main_manage_tokens, + titleResId = R.string.add_tokens_title, onBackButtonClick = actionsHandler::onBackButtonClick, onSearchButtonClick = actionsHandler::onSearchButtonClick, onAddCustomTokenClick = actionsHandler::onAddCustomTokenClick, ) } else { TokensListToolbarState.Title.Read( - titleResId = R.string.search_tokens_title, + titleResId = R.string.common_search_tokens, onBackButtonClick = actionsHandler::onBackButtonClick, onSearchButtonClick = actionsHandler::onSearchButtonClick, ) diff --git a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt index 305cfa6545..62ef2c0468 100644 --- a/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt +++ b/app/src/main/java/com/tangem/tap/features/walletSelector/ui/components/WalletItem.kt @@ -251,7 +251,7 @@ private fun LoadedTokensInfo( SpacerH2() Text( text = pluralStringResource( - id = R.plurals.tokens_count, + id = R.plurals.token_count, count = tokensCount, tokensCount, ), diff --git a/app/src/main/res/layout/dialog_wallet_send.xml b/app/src/main/res/layout/dialog_wallet_send.xml index 2fa54ef3d3..d204d38de3 100644 --- a/app/src/main/res/layout/dialog_wallet_send.xml +++ b/app/src/main/res/layout/dialog_wallet_send.xml @@ -10,7 +10,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingBottom="16dp" - android:text="Select" + android:text="@string/wallet_choice_wallet_option_title" android:textSize="14sp" android:textStyle="bold" /> diff --git a/app/src/main/res/layout/fragment_shop.xml b/app/src/main/res/layout/fragment_shop.xml index 0a61bf5b4a..3d6e2f4020 100644 --- a/app/src/main/res/layout/fragment_shop.xml +++ b/app/src/main/res/layout/fragment_shop.xml @@ -264,16 +264,17 @@ - + + + + + + + + + + + Erhalt der Gebühr fehlgeschlagen Laden Sie %1$s+ %2$s auf um ein Konto zu erstellen - Interner Fehler der Blockchain Minimaler Betrag ist %s Restbestand zu klein Falsche Gebühr diff --git a/core/res/src/main/res/values-de/strings.xml b/core/res/src/main/res/values-de/strings.xml index 93537cff03..0542e0607f 100644 --- a/core/res/src/main/res/values-de/strings.xml +++ b/core/res/src/main/res/values-de/strings.xml @@ -10,6 +10,7 @@ Abbrechen Entfernen Erledigt + Fehler OK Änderungen speichern Absenden @@ -27,12 +28,14 @@ Signiert Details Nutzungsbedingungen + Karte scannen Tippen Sie um den Zugangscode zu ändern Tippen Sie um den Passcode zu ändern Legen Sie die Karte zum Scannen an Tippen um zu signieren Legen Sie die Karte an Der Betrag enthält nicht einige Ihrer Mittel + Ein wallet erstellen Betrag Adresse Die Adresse stimmt mit der Adresse Ihrer Brieftasche überein diff --git a/core/res/src/main/res/values-fr/strings-blockchain.xml b/core/res/src/main/res/values-fr/strings-blockchain.xml index 7a18c99c18..42c3c19b03 100644 --- a/core/res/src/main/res/values-fr/strings-blockchain.xml +++ b/core/res/src/main/res/values-fr/strings-blockchain.xml @@ -2,7 +2,6 @@ Échec de réception des commissions Pour créer un compte, téléchargez %1$s+ %2$s - Erreur interne de la blockchain Le montant minimal est de %s Le reste est trop petit Commission non valide diff --git a/core/res/src/main/res/values-fr/strings.xml b/core/res/src/main/res/values-fr/strings.xml index 529af080aa..3dcb556630 100644 --- a/core/res/src/main/res/values-fr/strings.xml +++ b/core/res/src/main/res/values-fr/strings.xml @@ -10,6 +10,7 @@ Annuler Supprimer Exécuté + Erreur OK Sauvegarder les modifications Envoyer @@ -27,12 +28,14 @@ Signé Référénces Conditions d\'utilisation + Scannez la carte Touchez, pour modifier le code d\'accès Touchez, pour modifier le mot de passe Posez pour scanner Touchez pour signer Posez la carte Le montant n\'inclut pas certains de vos fonds + Créer un portefeuille Somme Adresse L\'adresse est la même que celle de votre portefeuille diff --git a/core/res/src/main/res/values-it/strings-blockchain.xml b/core/res/src/main/res/values-it/strings-blockchain.xml index 92560ee28a..2d50632130 100644 --- a/core/res/src/main/res/values-it/strings-blockchain.xml +++ b/core/res/src/main/res/values-it/strings-blockchain.xml @@ -2,7 +2,6 @@ Impossibile ottenere la commissione Scarica %1$s+ %2$s per creare un account - Errore interno della blockchain L\'importo minimo è di %s L\'importo residuo è molto basso Commissione non valida diff --git a/core/res/src/main/res/values-it/strings.xml b/core/res/src/main/res/values-it/strings.xml index 3c294f6376..e9d1904611 100644 --- a/core/res/src/main/res/values-it/strings.xml +++ b/core/res/src/main/res/values-it/strings.xml @@ -10,6 +10,7 @@ Annulla Rimuovere Fatto + Errore OK Mantieni le modifiche Invia @@ -27,12 +28,14 @@ Firmato Requisiti Termini del servizio + Scansiona carta Avvicina per modificare il codice di accesso Avvicina per modificare la password Avvicina per scansionare Avvicina per firmare Avvicina la carta L\'importo non include alcuni dei tuoi fondi + Crea portafoglio Importo Indirizzo L\'indirizzo corrisponde all\'indirizzo del tuo portafoglio diff --git a/core/res/src/main/res/values-ru/strings-blockchain.xml b/core/res/src/main/res/values-ru/strings-blockchain.xml index 15d04919e5..0e716ae5d1 100644 --- a/core/res/src/main/res/values-ru/strings-blockchain.xml +++ b/core/res/src/main/res/values-ru/strings-blockchain.xml @@ -6,7 +6,6 @@ Из-за ограничений Kaspa в одну транзакцию может поместиться только %1$d UTXO. Это означает, что вы можете отправить только %2$s или меньше. Вам нужно уменьшить сумму. Пополните счет на %1$s+ %2$s, чтобы создать аккаунт Аккаунт получателя не активирован. Отправьте %s или более для активации аккаунта. - Внутренняя ошибка блокчейна Минимальная сумма: %s Сдача слишком мала Неверная комиссия diff --git a/core/res/src/main/res/values-ru/strings.xml b/core/res/src/main/res/values-ru/strings.xml index ed6016a6d2..ab73d49ede 100644 --- a/core/res/src/main/res/values-ru/strings.xml +++ b/core/res/src/main/res/values-ru/strings.xml @@ -1,13 +1,17 @@ Добавить токен + Валюты Отправляйте только %1$s (%2$s) в сети %3$s на этот адрес. Использование другой сети может привести к утрате средств. + Спасибо за ваш отзыв + Отправлено успешно Обратиться в поддержку Отправить отзыв Эта карта ранее пополнялась и подписывала транзакции. Выведите средства как можно быстрее, если вы получили эту карту из ненадежного источника. Если это ваша карта, то не о чем беспокоиться. Эта функция недоступна в демонстрационном режиме Приложение работает в демонстрационном режиме. Средства на всех счетах ненастоящие. Карта, которую вы отсканировали, является картой разработчика. Не принимайте её в качестве оплаты. + Не удалось отправить письмо Причина: %s Не могу отправить транзакцию Внимание! Валюты на разных сетях имеют разные адреса. Убедитесь, что адрес соответствует сети, в которой вы отправляете средства. @@ -36,14 +40,22 @@ Отключите эту опцию, если не хотите, чтобы эта карта использовалась для сброса кодов доступа на другие карты этого кошелька. Обратите внимание, сброс кода также не будет доступен на этой карте. Использовать эту карту для сброса кода доступа на других картах в этом кошельке + Отключить возможность сброса кода доступа на этой карте или других картах этого кошелька Восстановление кода доступа + Сбросить + Вы уверены, что хотите это сделать? Смена кода доступа Код доступа будет изменен только на данной карте Заводские настройки Тип безопасности + Выбранный способ защиты приложения Настройки карты Tangem Bot Чат + Оценить агента + Отправить логи + Пожалуйста, выберите действие + Пожалуйста, оцените работу агента Принять Добавить Применить @@ -56,6 +68,7 @@ Вы не предоставили доступ к камере, пожалуйста, измените настройки конфиденциальности. Отмена Закрыть + Продолжить Копировать Скопировать адрес Создать @@ -65,12 +78,17 @@ Готово Включить Включено + Ошибка Обменять Посмотреть историю транзакций Обозреватель + Сгенерировать адреса + Импортировать Нравится + Заблокирован Основная сеть Нет + Нет адреса Нет данных OK Основная карта @@ -79,7 +97,9 @@ Перезагрузить Сохранить изменения Искать + Поиск токенов Секретная фраза + Выберите действие Продать Отправить Сервер недоступен, повторите попытку позднее @@ -99,6 +119,7 @@ Да Адрес контракта скопирован! Доступные сети + Добавить токен Адрес контракта Адрес контракта некорректен Путь деривации некорректен @@ -138,7 +159,6 @@ Проверьте подключение с интернетом или переключитесь на другую сеть Условия использования К сожалению, текущая версия приложения не готова к работе с этой картой, проверьте наличие обновлений - Данное приложение не предназначено для работы с этой картой или требует обновления Вы использовали карту от другого кошелька. Приложите карту, связанную с этим кошельком. Вы получаете Вы отправляете @@ -153,10 +173,13 @@ Обращение в поддержку Tangem Не могу отправить транзакцию Купить + Сканировать Чтобы изменить код доступа, приложите карту как показано выше и не убирайте до окончания операции Чтобы изменить пароль, приложите карту как показано выше и не убирайте до окончания операции Чтобы создать кошелек, приложите карту как показано выше и не убирайте до окончания операции + Чтобы сбросить настройки до заводских, приложите карту как показано выше и не убирайте до окончания операции Приложите, чтобы отсканировать + Чтобы подписать транзакцию, приложите карту как показано выше и не убирайте до окончания операции Приложите, чтобы подписать Приложите карту Внутренняя ошибка: не удается найти менеджер кошельков @@ -174,10 +197,16 @@ Баланс В сумме учтены не все монеты 1INCH токены будут зачислены на адрес вашего кошелька в сети %s в течение 48 часов - По вашему промокоду не было покупки кошелька, а значит вы не можете получить бонус. Купите кошелек Tangem, отсканируйте его в приложении и получите бонус. Чтобы получить доступ ко всем сетям, вам необходимо отсканировать карту Отсканируйте карту Токены + + Вам надо сгенерировать адрес для %d новой сети, используя вашу карту + Вам надо сгенерировать адреса для %d новых сетей, используя вашу карту + Вам надо сгенерировать адреса для %d новых сетей, используя вашу карту + Вам надо сгенерировать адреса для %d новых сетей, используя вашу карту + + Некоторые адреса отсутствуют Вам необходимо установить единый код доступа для защиты всех ваших карт Защита Позже вы сможете установить индивидуальный код доступа для каждой карты @@ -219,7 +248,7 @@ Код доступа Подключиться Резервная копия - Читать про секретную фразу + Прочитать о секретной фразе Запишите эти 12 слов в порядке, указанном ниже, и сохраните их в надежном месте. Ваша секретная фраза Чтобы импортировать кошелек, введите секретную фразу в поле ниже @@ -237,7 +266,7 @@ Чтобы начать процесс резервного копирования, добавьте одну или две резервные карты. Вы можете добавить еще одну карту или завершить процесс резервного копирования Подготовьте резервную карту с номером %s - Подготовьте основную карту + Отсканируйте основную карту, чтобы начать процесс резервного копирования. Подготовьте основную карту с номером %s Поздравляем! Ваша платежная крипто карта теперь активирована! Ваша карта настроена и готова к использованию. @@ -255,7 +284,7 @@ Пополните кошелек более чем на %1$s %2$s, чтобы начать пользоваться картой Купить криптовалюту Показать адрес кошелька - Пополните свой кошелек + Активация кошелька Процесс связывания карт частично завершен. Вы не можете выйти из него сейчас. Если процесc создания кошелька каким-либо образом прервется, вам придется начинать сначала Вы можете сделать резервную копию своих ключей на одной или двух других пустых картах Wallet. @@ -275,12 +304,23 @@ Участвовать Не удалось загрузить информацию по реферальной программе. Пожалуйста, попробуйте позже. Не удалось загрузить информацию по реферальной программе. Код ошибки: %s. Пожалуйста, попробуйте позже. + Грядущие выплаты Ваши друзья купили + Меньше + Больше + Нет грядущих выплат + + за %d кошелек + за %d кошелька + за %d кошельков + за %d кошельков + + Получите ^^%1$s^^ на ваш адрес в сети %2$s %3$s ^^спустя 30 дней^^ за каждый кошелек, который купит ваш друг Получите на ваш адрес в сети %1$s%2$s за каждый кошелек, который купит ваш друг Вы Получит - при покупке карточки на сайте tangem.com + при покупке кошелька на сайте tangem.com %s скидку Ваш друг Персональный код скопирован! @@ -316,7 +356,6 @@ Сканировать Отсканируйте карту, чтобы изменить ее настройки. Изменения затронут только ту карту, которую вы отсканировали, и не повлияют на другие карты, привязанные к вашему кошельку. Приготовьте свою карту - Поиск валют Сумма Адрес Адрес совпадает с адресом кошелька @@ -342,7 +381,7 @@ У меня есть промо-код… Tangem Wallet Другие способы оплаты - Из-за высокого количества заказов, которые мы получаем, доставка может быть задержана на срок до 5 недель в зависимости от вашего местоположения + Сделать предзаказ Итого Сеть Solana взимает арендную плату в размере %1$s каждые 2 дня. Аккаунты, которые не могут позволить себе арендную плату, удаляются из сети. Пополните свой счет более чем на %2$s, чтобы не платить арендную плату. Держите свои криптосбережения в безопасности. Приватные ключи надежно хранятся на карте. @@ -414,12 +453,6 @@ Токен %1$s является основной валютой в сети %2$s и не может быть скрыт до тех пор, пока у вас в списке есть другие токены этой сети. Невозможно скрыть %s Нет цены - - %d токен - %d токена - %d токенов - %d токенов - контракт: %s У вас еще нет транзакций Не удалось загрузить историю транзакций.\nНажмите на кнопку перезагрузки, чтобы обновить информацию. @@ -437,6 +470,7 @@ Подготовка карты Tangem Twin Это действие необратимо. У вас не будет доступа к старому кошельку. + Приложите twin-карту с номером %s и не убирайте до окончания операции Добавить новый кошелек Вы уверены, что хотите удалить этот кошелек? %d выбрано @@ -456,6 +490,7 @@ Транзакция подтверждается… Подтвержденный баланс Действия + Что отправить? Вы хотите купить или продать криптовалюту? Запрос на подпись сообщения.\n\n%s Dapp %1$s, запрос на\nподпись транзакции с BNB.\n\n%2$s @@ -477,7 +512,10 @@ Произошла непредвиденная ошибка. Код ошибки: %d Попробуйте, пожалуйста, позже. Если проблема будет продолжать возникать - обратитесь в службу поддержки. Сообщение было успешно подписано и отправлено в Dapp Сеть %s не найдена. Пожалуйста, добавьте её и попробуйте заново. + Нет открытых сессий WalletConnect + Упс. Нет сессий. Вставить из буфера обмена + Сообщение для %1$s:\n%2$s Запрос на открытие сессии для\n%1$s\n\nСЕТЬ: %2$s\n\nURL: %3$s Операция не может быть завершена.\n\nВы уже установили сеанс WalletConnect с этими параметрами. Сканировать новый код @@ -485,6 +523,8 @@ Сеть не поддерживается. Пожалуйста, выберите другую сеть. Выберите сеть Dapp не предоставил необходимые данные для открытия сессии WalletConnect + Не удалось найти сессию для обработки запроса + Сессии WalletConnect Подключение к Dapps WalletConnect Транзакция успешно подписана и отправлена ​​в Dapp diff --git a/core/res/src/main/res/values-zh-rTW/strings-blockchain.xml b/core/res/src/main/res/values-zh-rTW/strings-blockchain.xml index 67f050ffa1..5aa30cef8f 100644 --- a/core/res/src/main/res/values-zh-rTW/strings-blockchain.xml +++ b/core/res/src/main/res/values-zh-rTW/strings-blockchain.xml @@ -6,7 +6,6 @@ 由於 Kaspa 的限制,只有%1$d UTXO 可以放入單次交易中。這意味著您只能發送%2$s或更少數量。您需要減少數量。 加載 %1$s+ %2$s 以創建帳戶 目標帳戶未激活。發送 %s 或更多以激活帳戶 - 區塊鏈內部錯誤 最小數量是 %s 更動太小 無效費用 diff --git a/core/res/src/main/res/values-zh-rTW/strings.xml b/core/res/src/main/res/values-zh-rTW/strings.xml index 711e9ca9c5..748251aefa 100644 --- a/core/res/src/main/res/values-zh-rTW/strings.xml +++ b/core/res/src/main/res/values-zh-rTW/strings.xml @@ -1,13 +1,17 @@ 添加自定義代幣 + 管理代幣 僅將 %1$s (%2$s) 從 %3$s 網絡發送到此地址。使用其他代幣和網絡可能會導致資金損失 + 謝謝您的反饋 + 成功送出 請求支持 發送反饋 此卡過去已經充值並簽署過交易。如果您從不受信任的來源收到此卡,請考慮立即提取所有資金。如果是您的卡,則無需擔心 此功能不在展示模式中提供 您正在展示模式。所有資產皆不是真的 您掃描的卡是開發卡。不要用它作為付款方式 + 發送電子郵件失敗 原因:%s 無法發送交易 請注意代幣在不同的網路有不同的地址。請再次檢查正確的地址 @@ -32,14 +36,22 @@ 如果您不希望使用此卡重置此錢包中其他卡上的訪問密碼,請禁用此選項。請注意,這也會阻止您重置此卡上的訪問密碼。 允許您使用此卡重置此錢包中其他卡上的訪問密碼 + 禁用重置此卡或此錢包中其他卡上的訪問密碼的功能 恢復訪問密碼 + 重置 + 您確定要這麼做嗎? 更改訪問密碼 訪問密碼將僅在此卡上更改 回復至原廠設置 安全模式 + 選定的應用程序保護方法 卡片設置 Tangem 機器人 支援 + 請評價 + 發送logs + 請選擇一個操作 + 請評價我們的服務 接受 添加 注意 @@ -50,6 +62,7 @@ 您尚未授予相機訪問權限,請更改您的隱私設置 刪除 關閉 + 繼續 複製 複製地址 創造 @@ -59,7 +72,9 @@ 完成 允許 啟用 + 錯誤 交易 + 導入 喜歡 OK @@ -67,6 +82,8 @@ 拒絕 保存設置 搜索 + 搜尋代幣 + 選擇 銷售 發送 伺服器不可用,請稍後在試 @@ -84,6 +101,7 @@ 已複製代幣地址 支持的網路 + 添加代幣 代幣地址 無效地址 衍生路徑錯誤 @@ -135,10 +153,13 @@ Tangem反饋 無法發送交易 訂購 + 掃描卡片 要更改訪問密碼,請完全按照上圖所示連接手機和卡片 要更改密碼,請完全按照上圖所示連接手機和卡 要創建錢包,請完全按照上圖所示連接手機和卡 + 要重置為出廠設置,請完全按照上圖所示連接手機和卡片 點擊掃描 + 要簽名,請完全按照上圖所示連接手機和卡 點擊簽名 點按卡片 內部錯誤:找不到錢包管理器 @@ -188,7 +209,7 @@ 這此情況,您必須要重新開始 您想要離開啟用程序嗎? 開始 - 您要添加的卡上已經創建了另一個錢包。你想重置它並將卡用於新錢包嗎 + 您要添加的卡上已經創建了另一個錢包。你想重置它並將卡用於新錢包嗎? PIN 碼 連接 創建備份 @@ -210,7 +231,7 @@ 要開始備份過程,最多可添加兩張備份卡。 您可以再添加一張卡或完成備份過程 準備編號為 %s 的備份卡 - 準備主卡 + 掃描主卡以啟動備份過程 準備編號為 %s 的主卡 恭喜! 您的第一張支付加密卡已啟用! 您的錢包卡已配置完畢,可以使用了 @@ -228,7 +249,7 @@ 要開始,只需為錢包充值超過 %1$s %2$s 購買加密貨幣 顯示錢包地址 - 充值你的錢包 + 啟動錢包 結對過程已部分完成。你現在不能退出 如果創建錢包的過程以任何方式中斷,您將得重新開始 您最多可以額外備份兩張空白的 Tangem冷錢包 @@ -247,7 +268,6 @@ 對於你的朋友在你的 %1$s 網絡地址%2$s上購買的每個錢包 得到 - 當在 tangem.com購買卡片 %s 折扣 你的朋友 個人促銷碼已複製! @@ -280,7 +300,6 @@ 掃描卡片 掃描卡片以更改其設置。這些更改只會影響您掃描過的卡,不會影響綁定到您錢包的其他卡。 準備好您的卡! - 搜尋代幣 數量 地址 地址與錢包地址相同 @@ -367,9 +386,6 @@ %1$s 代幣是 %2$s 網絡上的主要貨幣,只要列表中還有該網絡上的其他代幣,它就無法被隱藏。 無法隱藏 %s 無費用 - - %d 代幣 - 您還沒有任何交易 無法加載交易 進行中… @@ -383,6 +399,7 @@ 準備卡片 Tangem Twin 這個動作是不可逆的。您將無法訪問舊錢包 + 將您的 iPhone 靠近編號為 %s 的雙胞胎卡 添加新錢包 您確定要刪除此錢包? 已選擇 %d @@ -402,6 +419,7 @@ 交易進行中 檢視餘額 動作 + 選擇錢包選項 您想要購買或賣出交易貨幣? 請求籤署消息。%s Dapp %1$s,請求\n簽署 BNB 交易。\n%2$s @@ -423,7 +441,10 @@ 我們遇到了未知錯誤。錯誤代碼:%d。如果問題仍然存在-請隨時聯繫我們的支持人員 消息已成功簽名並發送至Dapp 沒有 %s 網路,請先加入後再試一次 + 沒有已連結的WalletConnect + Ooops, 沒有連接 從剪貼板貼上 + 給 %1$s 的消息:%2$s 請求開始會話\n%1$s\n\n網絡: %2$s\n\n網址:%3$s 無法完成執行,您已經使用此參數建立了 WalletConnect 連接 掃描新密碼 @@ -431,6 +452,8 @@ 不支持此網絡。請選擇其他網絡 選擇網路 Dapp 沒有提供必要的數據來建立 WalletConnect 連接 + 找不到請求的連接 + WalletConnect 連接 連結到Dapps WalletConnect 交易已成功簽署並發送至 Dapp diff --git a/core/res/src/main/res/values/strings-blockchain.xml b/core/res/src/main/res/values/strings-blockchain.xml index 4e305a3ce1..41209dd8f9 100644 --- a/core/res/src/main/res/values/strings-blockchain.xml +++ b/core/res/src/main/res/values/strings-blockchain.xml @@ -6,7 +6,6 @@ Due to Kaspa limitations only %1$d UTXOs can fit in a single transaction. This means you can only send %2$s or less. You need to reduce the amount. Load %1$s+ %2$s to create account Destination account is not active. Send %s or more to activate the account. - Blockchain internal error Minimum amount is %s Change is too small Invalid Fee diff --git a/core/res/src/main/res/values/strings.xml b/core/res/src/main/res/values/strings.xml index a04ba073c9..f1471f6745 100644 --- a/core/res/src/main/res/values/strings.xml +++ b/core/res/src/main/res/values/strings.xml @@ -1,13 +1,17 @@ Add custom token + Manage tokens Send only %1$s (%2$s) from %3$s network to this address. Using other tokens and networks may result in loss of funds. + Thank you for your feedback + Sent successfully Request support Send feedback This card has been already topped up and signed transactions in the past. Consider immediate withdrawal of all funds if you have received this card from an untrusted source. If it\'s your card, there is nothing to worry about. This feature is disabled in Demo mode You are currently running in Demo mode. All funds are not real. The card you scanned is a development card. Don\'t accept it as a payment. + Failed to send the email Reason: %s Can\'t send a transaction Note that tokens on different networks have different addresses. Double check that your address matches the network when you transfer funds. @@ -34,14 +38,22 @@ Disable this option if you don\'t want this card to be used to reset access codes on other cards in this wallet. Please note that this will also prevent you from resetting the access code on this card. Allows you to use this card to reset access code on other cards in this wallet + Disable the ability to reset the access code on this card or other cards in this wallet Access code recovery + Reset + Are you sure you want to do this? Change Access Code Access code will be changed on this card only Reset to Factory Settings Security Mode + Selected application protection method Card Settings Tangem Bot Support + Rate agent + Send logs + Please select an action + Please, rate the work of the agent Accept Add Apply @@ -54,6 +66,7 @@ You have not given access to your camera, please adjust your privacy settings Cancel Close + Continue Copy Copy address Create @@ -63,13 +76,18 @@ Done Enable Enabled + Error Exchange Explore transaction history Explorer + Generate addresses + Import Learn & Earn Like + Locked Main network No + No address No data OK Primary Card @@ -78,7 +96,9 @@ Reload Save changes Search + Search tokens Seed phrase + Select action Sell Send The server is not available, please try again later @@ -98,6 +118,7 @@ Yes Contract address copied! Available networks + Add token Contract address Contract address is invalid Derivation path is invalid @@ -137,7 +158,6 @@ Check your internet connection or switch to a different network Terms of Service Oops, the current version of the application is not ready to work with this card, please check for updates. - This application is not designed to work with this card or needs to be updated You have used a card from another wallet. Tap the card associated with this wallet You Receive You Send @@ -152,10 +172,13 @@ Tangem feedback Can\'t send a transaction Order + Scan card To change the access code tap the card as shown above and do not remove until the end of the operation To change the passcode tap the card as shown above and do not remove until the end of the operation To create the wallet tap the card as shown above and do not remove until the end of the operation + To reset to factory settings tap the card as shown above and do not remove until the end of the operation Tap to scan + To sign tap the card as shown above and do not remove until the end of the operation Tap to sign Tap the card Internal error: wallet manager not found @@ -172,10 +195,14 @@ Total balance The amount does not include some of your funds 1INCH tokens will be credited to your %s wallet address within 48 hours - There was no purchase of a wallet using your promo code, which means you cannot receive a bonus. Buy Tangem wallet, scan it in the app, and get the bonus. To access all the networks you need to scan the card Scan your card Tokens + + You need to generate address for %d new network using your card + You need to generate addresses for %d new networks using your card + + Some addresses are missing You have to set up a single access code to protect all your wallets Protect You can set up an individual access code on each card later @@ -235,7 +262,7 @@ To start the backup process add up to two backup cards. You can add one more card or finalize the backup process Prepare the backup card with number %s - Prepare the primary card + Scan the primary card to start the backup process. Prepare the primary card with number %s Congratulations! Your first payment crypto card has been activated! Your wallet card is configured and ready for use. @@ -253,7 +280,7 @@ To get started, simply top up the wallet with more than %1$s %2$s Buy crypto Show the wallet\'s address - Top up your wallet + Activate a wallet The twinning process is partly complete. You can\'t exit it now. If the process of creating the wallet gets interrupted in any way, you\'ll have to start over You can backup your keys up to two other blank Tangem Wallet cards. @@ -274,12 +301,21 @@ Participate Failed to load the information about the referral program. Please try again later. Failed to load the information about the referral program. Error code: %s. Please try again later. + Upcoming payments Your friends bought + Less + More + No upcoming payments + + for %d wallet + for %d wallets + + Will get ^^%1$s^^ for each wallet bought by your friend on your %2$s network address %3$s ^^30 days after^^ that Will get for each wallet bought by your friend on your %1$s network address%2$s You Will get a - when buying a card on tangem.com + when buying a wallet on tangem.com %s discount Your friend Personal code copied! @@ -313,7 +349,6 @@ Scan Card Scan the card to change its settings. The changes will impact only the card you\'ve scanned and will not affect other cards tied to your wallet. Get your card ready! - Search tokens Amount Address Address is the same as wallet address @@ -339,7 +374,7 @@ I have a promo code… Tangem Wallet Other payment methods - Due to the high volume of orders we are receiving shipping may be delayed up to 5 weeks depending on your location + Pre-order now Total Solana network charges a rent of %1$s every 2 days. Accounts that can\'t afford the rent are purged from the network. Deposit your account with more than %2$s to use it for free. Store your crypto assets secure while keeping private keys contained in your card @@ -409,10 +444,6 @@ The %1$s token is the main currency on the %2$s network and cannot be hidden as long as you have other tokens on this network in the list. Unable to hide %s No rate - - %d token - %d tokens - contract: %s You don\'t have any transactions yet Failed to load transaction history.\nClick on reload button to update the information. @@ -430,6 +461,7 @@ Preparing card Tangem Twin This action is irreversible. You will not have access to the old wallet. + Tap the twin card with number %s and do not remove until the end of the operation Add new wallet Are you sure you want to delete this wallet? %d selected @@ -449,6 +481,7 @@ Transaction is in progress… Verified Balance Actions + Choose wallet option Do you want to buy or sell crypto? Requesting to sign a message.\n\n%s Dapp %1$s, requesting to\nsign BNB transaction.\n\n%2$s @@ -470,7 +503,10 @@ We\'ve encountered unknown error. Error code: %d. If the problem persists — feel free to contact our support The message has been successfully signed and sent to the Dapp %s network not found. Please, add it first and try again. + No opened WalletConnect sessions + Ooops. No Sessions. Paste from clipboard + Message for %1$s:\n%2$s Request to start a session for\n%1$s\n\nNETWORK: %2$s\n\nURL: %3$s The operation couldn\'t be completed.\n\nYou have already established a WalletConnect session with this parameters. Scan new code @@ -478,6 +514,8 @@ This network is not supported. Please select another network. Select network Dapp didn\'t provide essential data to establish WalletConnect session + Failed to find session for request + WalletConnect Sessions Connect to Dapps WalletConnect The transaction has been successfully signed and sent to the Dapp diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnDialogs.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnDialogs.kt index 3c855863ac..58c4ac2cd1 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnDialogs.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/presentation/ui/Learn2earnDialogs.kt @@ -16,7 +16,8 @@ import com.tangem.feature.learn2earn.presentation.ui.state.MainScreenState internal fun Learn2earnDialogs(dialog: MainScreenState.Dialog?) { when (dialog) { is MainScreenState.Dialog.Claimed -> ClaimedDialog(dialog) - is MainScreenState.Dialog.PromoCodeNotRegistered -> PromoCodeNotRegisteredDialog(dialog) + // TODO: https://tangem.slack.com/archives/C01HARKDLQ0/p1691421861756069 + // is MainScreenState.Dialog.PromoCodeNotRegistered -> PromoCodeNotRegisteredDialog(dialog) is MainScreenState.Dialog.Error -> ErrorDialog(dialog) else -> Unit } @@ -41,30 +42,30 @@ private fun ClaimedDialog(dialog: MainScreenState.Dialog.Claimed) { ) } -@Composable -private fun PromoCodeNotRegisteredDialog(dialog: MainScreenState.Dialog.PromoCodeNotRegistered) { - AlertDialog( - title = { - Text(text = stringResource(id = R.string.common_error)) - }, - text = { - Text(text = stringResource(id = R.string.main_promotion_no_purchase)) - }, - dismissButton = { - TextButton( - text = stringResource(id = R.string.common_cancel), - onClick = dialog.onCancel, - ) - }, - confirmButton = { - TextButton( - text = stringResource(id = R.string.common_buy), - onClick = dialog.onOk, - ) - }, - onDismissRequest = dialog.onDismissRequest, - ) -} +// @Composable +// private fun PromoCodeNotRegisteredDialog(dialog: MainScreenState.Dialog.PromoCodeNotRegistered) { +// AlertDialog( +// title = { +// Text(text = stringResource(id = R.string.common_error)) +// }, +// text = { +// Text(text = stringResource(id = R.string.main_promotion_no_purchase)) +// }, +// dismissButton = { +// TextButton( +// text = stringResource(id = R.string.common_cancel), +// onClick = dialog.onCancel, +// ) +// }, +// confirmButton = { +// TextButton( +// text = stringResource(id = R.string.common_buy), +// onClick = dialog.onOk, +// ) +// }, +// onDismissRequest = dialog.onDismissRequest, +// ) +// } @Composable private fun ErrorDialog(dialog: MainScreenState.Dialog.Error) { diff --git a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt index b10aac1300..ad9b5d8d2c 100644 --- a/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt +++ b/features/swap/presentation/src/main/java/com/tangem/feature/swap/ui/SwapSelectTokenScreen.kt @@ -5,15 +5,7 @@ import androidx.annotation.StringRes import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.shape.CircleShape @@ -62,7 +54,7 @@ fun SwapSelectTokenScreen( ExpandableSearchView( title = stringResource(R.string.swapping_token_list_title), onBackClick = onBack, - placeholderSearchText = stringResource(id = R.string.search_tokens_title), + placeholderSearchText = stringResource(id = R.string.common_search_tokens), onSearchChange = state.onSearchEntered, onSearchDisplayClose = { state.onSearchEntered("") }, onFocusChange = onSearchFocusChange, From 0ec583301e566019d5ad096044057bd123064cd4 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 13:39:12 +0300 Subject: [PATCH 32/52] Updated on 2026-08-14 --- .../onboarding/domain/DefaultSeedPhraseInteractor.kt | 5 +++-- .../feature/onboarding/domain/SeedPhraseInteractor.kt | 3 ++- .../onboarding/presentation/wallet2/model/UiActions.kt | 8 ++++---- .../presentation/wallet2/viewmodel/SeedPhraseViewModel.kt | 8 +++++--- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt index a63b4bccea..a8748d1664 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt @@ -4,6 +4,7 @@ import com.tangem.common.core.TangemSdkError import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.bip39.MnemonicErrorResult import com.tangem.feature.onboarding.data.MnemonicRepository +import com.tangem.feature.onboarding.presentation.wallet2.model.SeedPhraseField import com.tangem.utils.extensions.isNotWhitespace import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf @@ -36,8 +37,8 @@ internal class DefaultSeedPhraseInteractor constructor( } } - override suspend fun isWordMatch(word: String): Boolean { - return repository.getWordsDictionary().contains(word) + override suspend fun isWordMatch(mnemonicComponents: List?, field: SeedPhraseField, word: String): Boolean { + return mnemonicComponents?.get(field.index) == word } override suspend fun validateMnemonicString(text: String): Result> { diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt index 2c0f319c19..d9420a9def 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/SeedPhraseInteractor.kt @@ -1,6 +1,7 @@ package com.tangem.feature.onboarding.domain import com.tangem.crypto.bip39.Mnemonic +import com.tangem.feature.onboarding.presentation.wallet2.model.SeedPhraseField import kotlinx.collections.immutable.ImmutableList /** @@ -9,7 +10,7 @@ import kotlinx.collections.immutable.ImmutableList interface SeedPhraseInteractor { suspend fun generateMnemonic(): Result suspend fun getMnemonicComponents(): Result> - suspend fun isWordMatch(word: String): Boolean + suspend fun isWordMatch(mnemonicComponents: List?, field: SeedPhraseField, word: String): Boolean suspend fun validateMnemonicString(text: String): Result> suspend fun getSuggestions(text: String, hasSelection: Boolean, cursorPosition: Int): ImmutableList suspend fun insertSuggestionWord(text: String, suggestion: String, cursorPosition: Int): InsertSuggestionResult diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/UiActions.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/UiActions.kt index 5a63039118..dec0e444b9 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/UiActions.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/model/UiActions.kt @@ -50,8 +50,8 @@ data class TextFieldUiAction( -enum class SeedPhraseField { - Second, - Seventh, - Eleventh, +enum class SeedPhraseField(val index: Int) { + Second(index = 1), + Seventh(index = 6), + Eleventh(index = 10), } \ No newline at end of file diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt index c7d9c234ba..69a68a2b7b 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt @@ -161,12 +161,15 @@ class SeedPhraseViewModel @Inject constructor( val fieldState = field.getState(uiState) if (fieldState.textFieldValue.text.isEmpty()) { - updateUi { uiBuilder.checkSeedPhrase.updateTextFieldError(uiState, field, hasError = false) } + updateUi { + val mediate = uiBuilder.checkSeedPhrase.updateTextFieldError(uiState, field, hasError = false) + uiBuilder.checkSeedPhrase.updateCreateWalletButton(mediate, enabled = false) + } return@launchSingle } createOrGetDebouncer(field.name).debounce(viewModelScope, context = dispatchers.io) { - val hasError = !interactor.isWordMatch(textFieldValue.text) + val hasError = !interactor.isWordMatch(generatedMnemonicComponents, field, textFieldValue.text) if (fieldState.isError != hasError) { updateUi { uiBuilder.checkSeedPhrase.updateTextFieldError(uiState, field, hasError) } } @@ -278,7 +281,6 @@ class SeedPhraseViewModel @Inject constructor( is CompletionResult.Success -> { isFinished = true } - is CompletionResult.Failure -> { // errors shows on the TangemSdk bottom sheet dialog } From dee402f968c803a8d97be4c3d7d24d8bf53a78a3 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 12:29:20 +0300 Subject: [PATCH 33/52] Updated on 2026-08-14 --- .../wallet2/viewmodel/SeedPhraseRouter.kt | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseRouter.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseRouter.kt index 9067f108cd..bfe8b2da3e 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseRouter.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseRouter.kt @@ -1,9 +1,6 @@ package com.tangem.feature.onboarding.presentation.wallet2.viewmodel import android.net.Uri -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -21,20 +18,17 @@ class SeedPhraseRouter( val currentScreen: StateFlow get() = _currentScreen - var currentScreenState by mutableStateOf(SeedPhraseScreen.Intro) - private set - fun navigateBack() { _currentScreen.value = when (_currentScreen.value) { - SeedPhraseScreen.Intro -> { + SeedPhraseScreen.CheckSeedPhrase -> SeedPhraseScreen.YourSeedPhrase + SeedPhraseScreen.Intro, + SeedPhraseScreen.AboutSeedPhrase, + SeedPhraseScreen.YourSeedPhrase, + SeedPhraseScreen.ImportSeedPhrase, + -> { onBack.invoke() return } - - SeedPhraseScreen.AboutSeedPhrase -> SeedPhraseScreen.Intro - SeedPhraseScreen.YourSeedPhrase -> SeedPhraseScreen.AboutSeedPhrase - SeedPhraseScreen.CheckSeedPhrase -> SeedPhraseScreen.YourSeedPhrase - SeedPhraseScreen.ImportSeedPhrase -> SeedPhraseScreen.AboutSeedPhrase } } From 133dd14ec9f7121ac8626e5cba770b4b76652921 Mon Sep 17 00:00:00 2001 From: Tangem Date: Fri, 11 Aug 2023 16:53:59 +0300 Subject: [PATCH 34/52] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../com/tangem/tap/domain/TangemSdkManager.kt | 24 ++++++------ .../tasks/product/CreateProductWalletTask.kt | 17 ++++++--- .../domain/tasks/product/CreateWalletsTask.kt | 9 ++++- .../domain/tasks/product/ScanProductTask.kt | 8 ++-- .../tap/domain/tokens/CurrenciesRepository.kt | 27 -------------- .../domain/DefaultCustomTokenInteractor.kt | 10 +++-- .../domain/DefaultTokensListInteractor.kt | 10 +++-- .../tokens/legacy/redux/TokensMiddleware.kt | 10 +++-- .../tangem/tap/proxy/DerivationManagerImpl.kt | 10 +++-- .../domain/common/configs/CardConfig.kt | 28 ++++++++++++++ .../common/configs/TangemWalletCardConfig.kt | 33 +++++++++++++++++ .../common/configs/Wallet2CardConfig.kt | 37 +++++++++++++++++++ .../domain/common/extensions/Blockchain.kt | 20 +++------- .../common/extensions/WalletManagerFactory.kt | 28 +++++++++++--- .../common/util/ScanResponseExtensions.kt | 24 ++++++++---- .../tangem/domain/features/BlockchainTests.kt | 1 - .../domain/DefaultSeedPhraseInteractor.kt | 1 + .../domain/OnboardingModuleError.kt | 1 + gradle.properties | 2 +- gradle/dependencies.toml | 4 +- plugins/configuration/build.gradle.kts | 4 +- .../configurations/DetektConfigurations.kt | 2 +- .../configurations/KotlinConfigurations.kt | 2 +- .../extension/BaseExtensionConfigurations.kt | 4 +- 25 files changed, 215 insertions(+), 103 deletions(-) delete mode 100644 app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt create mode 100644 domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt create mode 100644 domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt create mode 100644 domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index fad890b2a0..8c1c53b739 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit fad890b2a0b552be60d124949ca0a3a4d672dac1 +Subproject commit 8c1c53b73950698d4acfa4d925b8c14bf6f0da63 diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 0c0c30de42..51e0adb686 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -94,18 +94,20 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit scanResponse: ScanResponse, mnemonic: String, ): CompletionResult { - return when (val seedResult = DefaultMnemonic(mnemonic, tangemSdk.wordlist).generateSeed()) { - is CompletionResult.Success -> runTaskAsync( - CreateProductWalletTask( - cardTypesResolver = scanResponse.cardTypesResolver, - derivationStyleProvider = scanResponse.derivationStyleProvider, - ), - scanResponse.card.cardId, - Message(resources.getString(R.string.initial_message_create_wallet_body)), - ) - - is CompletionResult.Failure -> CompletionResult.Failure(seedResult.error) + val mnemonic = try { + DefaultMnemonic(mnemonic, tangemSdk.wordlist) + } catch (e: TangemSdkError.MnemonicException) { + return CompletionResult.Failure(e) } + return runTaskAsync( + CreateProductWalletTask( + scanResponse.cardTypesResolver, + derivationStyleProvider = scanResponse.derivationStyleProvider, + mnemonic, + ), + scanResponse.card.cardId, + Message(resources.getString(R.string.initial_message_create_wallet_body)), + ) } private fun sendScanResultsToAnalytics(result: CompletionResult) { diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index ede3d71091..9b638d235f 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -11,11 +11,13 @@ import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey import com.tangem.common.map +import com.tangem.crypto.bip39.Mnemonic import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.extensions.derivationPath +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.KeyWalletPublicKey import com.tangem.operations.CommandResponse @@ -58,7 +60,7 @@ private data class CreateWalletResponse( class CreateProductWalletTask( private val cardTypesResolver: CardTypesResolver, private val derivationStyleProvider: DerivationStyleProvider, - private val seed: ByteArray? = null, + private val mnemonic: Mnemonic? = null, ) : CardSessionRunnable { override val allowsRequestAccessCodeFromRepository: Boolean = false @@ -78,7 +80,7 @@ class CreateProductWalletTask( cardTypesResolver.isTangemTwins() -> throw UnsupportedOperationException("Use the TwinCardsManager to create a wallet") - else -> CreateWalletTangemWallet(seed, derivationStyleProvider) + else -> CreateWalletTangemWallet(mnemonic, derivationStyleProvider) } commandProcessor.proceed(cardDto, session) { when (it) { @@ -133,8 +135,11 @@ private class CreateWalletTangemNote(private val cardTypesResolver: CardTypesRes } } +/** + * Uses for multiWallet 1st and 2nd + */ private class CreateWalletTangemWallet( - private val seed: ByteArray?, + private val mnemonic: Mnemonic?, private val derivationStyleProvider: DerivationStyleProvider, ) : ProductCommandProcessor { @@ -145,8 +150,9 @@ private class CreateWalletTangemWallet( session: CardSession, callback: (result: CompletionResult) -> Unit, ) { + val config = CardConfig.createConfig(card) val walletsOnCard = card.wallets.map { it.curve }.toSet() - val curves = card.supportedCurves.intersect(CURVES_FOR_WALLETS).subtract(walletsOnCard).toList() + val curves = card.supportedCurves.intersect(config.mandatoryCurves.toSet()).subtract(walletsOnCard).toList() if (curves.isEmpty()) { val createWalletResponses = card.wallets.map { wallet -> @@ -155,8 +161,7 @@ private class CreateWalletTangemWallet( proceedWithCreatedWallets(card, createWalletResponses, session, callback) return } - - CreateWalletsTask(curves, seed).run(session) { result -> + CreateWalletsTask(curves, mnemonic).run(session) { result -> when (result) { is CompletionResult.Success -> { proceedWithCreatedWallets( diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt index 23f0986e34..2c75d8da36 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateWalletsTask.kt @@ -5,6 +5,8 @@ import com.tangem.common.card.EllipticCurve import com.tangem.common.core.CardSession import com.tangem.common.core.CardSessionRunnable import com.tangem.common.core.TangemSdkError +import com.tangem.crypto.bip39.Mnemonic +import com.tangem.crypto.hdWallet.masterkey.AnyMasterKeyFactory import com.tangem.operations.CommandResponse import com.tangem.operations.wallet.CreateWalletResponse import com.tangem.operations.wallet.CreateWalletTask @@ -18,7 +20,7 @@ class CreateWalletsResponse( class CreateWalletsTask( private val curves: List, - private val seed: ByteArray? = null, + private val mnemonic: Mnemonic? = null, ) : CardSessionRunnable { private val createdWalletsResponses = mutableListOf() @@ -38,7 +40,10 @@ class CreateWalletsTask( session: CardSession, callback: (result: CompletionResult) -> Unit, ) { - CreateWalletTask(curve, seed).run(session) { result -> + val extendedPrivateKey = mnemonic?.let { + AnyMasterKeyFactory(mnemonic = it, passphrase = "").makeMasterKey(curve) + } + CreateWalletTask(curve, extendedPrivateKey).run(session) { result -> when (result) { is CompletionResult.Success -> { createdWalletsResponses.add(result.data) 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 6a184f7f8e..d04cb9a62e 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 @@ -21,7 +21,7 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.TwinsHelper -import com.tangem.domain.common.extensions.getPrimaryCurve +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ProductType @@ -209,6 +209,7 @@ private class ScanWalletProcessor( callback: (result: CompletionResult) -> Unit, ) { val productType = ProductType.Wallet + val config = CardConfig.createConfig(card) scope.launch { val scanResponse = ScanResponse( card = card, @@ -216,7 +217,7 @@ private class ScanWalletProcessor( walletData = session.environment.walletData, primaryCard = primaryCard, ) - val derivations = collectDerivations(card, scanResponse.derivationStyleProvider) + val derivations = collectDerivations(card, config, scanResponse.derivationStyleProvider) if (derivations.isEmpty() || !card.settings.isHDWalletAllowed) { callback(CompletionResult.Success(scanResponse)) return@launch @@ -299,13 +300,14 @@ private class ScanWalletProcessor( private suspend fun collectDerivations( card: CardDTO, + config: CardConfig, derivationStyleProvider: DerivationStyleProvider, ): Map> { val blockchains = getBlockchainsToDerive(card, derivationStyleProvider) val derivations = mutableMapOf>() blockchains.forEach { blockchain -> - val curve = blockchain.blockchain.getPrimaryCurve() + val curve = config.primaryCurve(blockchain.blockchain) val wallet = card.wallets.firstOrNull { it.curve == curve } ?: return@forEach if (wallet.chainCode == null) return@forEach 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 deleted file mode 100644 index 22baf587ab..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tokens/CurrenciesRepository.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.tangem.tap.domain.tokens - -import com.tangem.blockchain.common.Blockchain -import com.tangem.common.card.FirmwareVersion -import com.tangem.domain.models.scan.CardDTO - -object CurrenciesRepository { - fun getBlockchains(cardFirmware: CardDTO.FirmwareVersion, isTestNet: Boolean = false): List { - val blockchains = if (cardFirmware < FirmwareVersion.MultiWalletAvailable) { - Blockchain.secp256k1Blockchains(isTestNet) - } else { - Blockchain.secp256k1Blockchains(isTestNet) + Blockchain.ed25519OnlyBlockchains(isTestNet) - } - return excludeUnsupportedBlockchains(blockchains) - } - - // Use this list to temporarily exclude a blockchain from the list of tokens. - private fun excludeUnsupportedBlockchains(blockchains: List): List { - return blockchains.toMutableList().apply { - removeAll( - listOf( -// Any blockchain - ), - ) - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt index 40cf0892b5..c5d75bbc2e 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt @@ -7,6 +7,7 @@ import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.util.derivationStyleProvider @@ -69,10 +70,11 @@ class DefaultCustomTokenInteractor( currencyList: List, onSuccess: suspend (ScanResponse) -> Unit, ) { - val derivationDataList = listOfNotNull( - getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList), - getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList), - ) + val config = CardConfig.createConfig(scanResponse.card) + val derivationDataList = currencyList.mapNotNull { + val curve = config.primaryCurve(it.blockchain) + curve?.let { getDerivations(curve, scanResponse, currencyList) } + } val derivations = derivationDataList.associate(TokensMiddleware.DerivationData::derivations) if (derivations.isEmpty()) { diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt index 940470bc09..f56ef520ca 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt @@ -9,6 +9,7 @@ import com.tangem.common.extensions.guard import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.supportsHdWallet @@ -134,10 +135,11 @@ internal class DefaultTokensListInteractor( } private suspend fun deriveMissingBlockchains(scanResponse: ScanResponse, currencies: List) { - val derivations = listOfNotNull( - getDerivations(EllipticCurve.Secp256k1, scanResponse, currencies), - getDerivations(EllipticCurve.Ed25519, scanResponse, currencies), - ).associate(transform = TokensMiddleware.DerivationData::derivations) + val config = CardConfig.createConfig(scanResponse.card) + val derivations = currencies.mapNotNull { + val curve = config.primaryCurve(it.blockchain) + curve?.let { getDerivations(curve, scanResponse, currencies) } + }.associate(transform = TokensMiddleware.DerivationData::derivations) if (derivations.isEmpty()) { submitAdd(scanResponse, currencies) diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt index dca9ea4875..9aff63b982 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt @@ -13,6 +13,7 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.DomainWrapped +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation @@ -119,10 +120,11 @@ object TokensMiddleware { currencyList: List, onSuccess: (ScanResponse) -> Unit, ) { - val derivationDataList = listOfNotNull( - getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList), - getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList), - ) + val config = CardConfig.createConfig(scanResponse.card) + val derivationDataList = currencyList.mapNotNull { + val curve = config.primaryCurve(it.blockchain) + curve?.let { getDerivations(curve, scanResponse, currencyList) } + } val derivations = derivationDataList.associate { it.derivations } if (derivations.isEmpty()) { onSuccess(scanResponse) diff --git a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt index 87b1dc7948..0966150ad0 100644 --- a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt @@ -10,6 +10,7 @@ import com.tangem.common.extensions.ByteArrayKey import com.tangem.common.extensions.toMapKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.BlockchainNetwork +import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.util.derivationStyleProvider @@ -94,10 +95,11 @@ class DerivationManagerImpl( onSuccess: (ScanResponse) -> Unit, onFailure: (Exception) -> Unit, ) { - val derivationDataList = listOfNotNull( - getDerivations(EllipticCurve.Secp256k1, scanResponse, currencyList), - getDerivations(EllipticCurve.Ed25519, scanResponse, currencyList), - ) + val config = CardConfig.createConfig(scanResponse.card) + val derivationDataList = currencyList.mapNotNull { + val curve = config.primaryCurve(it.blockchain) + curve?.let { getDerivations(curve, scanResponse, currencyList) } + } val derivations = derivationDataList.associate { it.derivations } if (derivations.isEmpty()) { onSuccess(scanResponse) diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt new file mode 100644 index 0000000000..1fd9e07d70 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt @@ -0,0 +1,28 @@ +package com.tangem.domain.common.configs + +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.EllipticCurve +import com.tangem.common.card.FirmwareVersion +import com.tangem.domain.models.scan.CardDTO + +sealed interface CardConfig { + + val mandatoryCurves: List + + fun primaryCurve(blockchain: Blockchain): EllipticCurve? + + companion object { + + fun createConfig(cardDTO: CardDTO): CardConfig { + if (cardDTO.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available) { + return Wallet2CardConfig + } + if (cardDTO.settings.isBackupAllowed && cardDTO.settings.isHDWalletAllowed && + cardDTO.firmwareVersion >= FirmwareVersion.MultiWalletAvailable + ) { + return TangemWalletCardConfig + } + error("This card is not supported by this configs") + } + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt new file mode 100644 index 0000000000..69c8c36b78 --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt @@ -0,0 +1,33 @@ +package com.tangem.domain.common.configs + +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.EllipticCurve +import timber.log.Timber + +object TangemWalletCardConfig : CardConfig { + override val mandatoryCurves: List + get() = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bip0340, + EllipticCurve.Bls12381G2Aug, + ) + + /** + * Old logic to determine primary curve for blockchain in TangemWallet + */ + override fun primaryCurve(blockchain: Blockchain): EllipticCurve? { + return when { + blockchain.getSupportedCurves().contains(EllipticCurve.Secp256k1) -> { + EllipticCurve.Secp256k1 + } + blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519) -> { + EllipticCurve.Ed25519 + } + else -> { + Timber.e("Unsupported blockchain, curve not found") + null + } + } + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt new file mode 100644 index 0000000000..58ecaef77c --- /dev/null +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/Wallet2CardConfig.kt @@ -0,0 +1,37 @@ +package com.tangem.domain.common.configs + +import com.tangem.blockchain.common.Blockchain +import com.tangem.common.card.EllipticCurve +import timber.log.Timber + +object Wallet2CardConfig : CardConfig { + override val mandatoryCurves: List + get() = listOf( + EllipticCurve.Secp256k1, + EllipticCurve.Ed25519, + EllipticCurve.Bip0340, + EllipticCurve.Bls12381G2Aug, + EllipticCurve.Ed25519Slip0010, + ) + + /** + * Logic to determine primary curve for blockchain in TangemWallet 2.0 + */ + override fun primaryCurve(blockchain: Blockchain): EllipticCurve? { + return when { + blockchain.getSupportedCurves().contains(EllipticCurve.Secp256k1) -> { + EllipticCurve.Secp256k1 + } + blockchain.getSupportedCurves().contains(EllipticCurve.Ed25519Slip0010) -> { + EllipticCurve.Ed25519Slip0010 + } + blockchain.getSupportedCurves().contains(EllipticCurve.Bls12381G2Aug) -> { + EllipticCurve.Bls12381G2Aug + } + else -> { + Timber.e("Unsupported blockchain, curve not found") + null + } + } + } +} \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 70c16ba9d7..2665166cf8 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -71,6 +71,8 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "aleph-zero/test" -> Blockchain.AlephZeroTestnet "octaspace" -> Blockchain.OctaSpace "octaspace/test" -> Blockchain.OctaSpaceTestnet + "chia" -> Blockchain.Chia + "chia/test" -> Blockchain.ChiaTestnet else -> null } } @@ -141,6 +143,8 @@ fun Blockchain.toNetworkId(): String { Blockchain.AlephZeroTestnet -> "aleph-zero/test" Blockchain.OctaSpace -> "octaspace" Blockchain.OctaSpaceTestnet -> "octaspace/test" + Blockchain.Chia -> "chia" + Blockchain.ChiaTestnet -> "chia/test" } } @@ -185,6 +189,8 @@ fun Blockchain.toCoinId(): String { Blockchain.Telos, Blockchain.TelosTestnet -> "telos" Blockchain.AlephZero, Blockchain.AlephZeroTestnet -> "aleph-zero" Blockchain.OctaSpace, Blockchain.OctaSpaceTestnet -> "octaspace" + Blockchain.Chia -> "chia" + Blockchain.ChiaTestnet -> "chia/test" } } @@ -204,20 +210,6 @@ fun Blockchain.minimalAmount(): BigDecimal { return 1.toBigDecimal().movePointLeft(decimals()) } -fun Blockchain.getPrimaryCurve(): EllipticCurve? { - return when { - getSupportedCurves().contains(EllipticCurve.Secp256k1) -> { - EllipticCurve.Secp256k1 - } - getSupportedCurves().contains(EllipticCurve.Ed25519) -> { - EllipticCurve.Ed25519 - } - else -> { - null - } - } -} - fun Blockchain.derivationPath(style: DerivationStyle?): DerivationPath? { if (style == null) return null if (!getSupportedCurves().contains(EllipticCurve.Secp256k1) && diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt index 86ce14a4f4..f050be813a 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt @@ -6,6 +6,8 @@ import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toMapKey import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation +import com.tangem.domain.common.configs.CardConfig +import com.tangem.domain.common.configs.Wallet2CardConfig import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse @@ -16,11 +18,16 @@ fun WalletManagerFactory.makeWalletManagerForApp( derivationParams: DerivationParams?, ): WalletManager? { val card = scanResponse.card + val cardConfig = CardConfig.createConfig(card) if (card.isTestCard && blockchain.getTestnetVersion() == null) return null val supportedCurves = blockchain.getSupportedCurves() val wallets = card.wallets.filter { wallet -> supportedCurves.contains(wallet.curve) } - val wallet = selectWallet(wallets) ?: return null + val wallet = selectWallet( + wallets = wallets, + cardConfig = cardConfig, + blockchain = blockchain, + ) ?: return null val environmentBlockchain = if (card.isTestCard) blockchain.getTestnetVersion()!! else blockchain @@ -85,10 +92,19 @@ fun WalletManagerFactory.makePrimaryWalletManager(scanResponse: ScanResponse): W ) } -private fun selectWallet(wallets: List): CardDTO.Wallet? { - return when (wallets.size) { - 0 -> null - 1 -> wallets[0] - else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0] +private fun selectWallet( + wallets: List, + cardConfig: CardConfig, + blockchain: Blockchain, +): CardDTO.Wallet? { + return if (cardConfig is Wallet2CardConfig) { + val primaryCurve = cardConfig.primaryCurve(blockchain) + wallets.firstOrNull { it.curve == primaryCurve } + } else { + when (wallets.size) { + 0 -> null + 1 -> wallets[0] + else -> wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: wallets[0] + } } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt b/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt index 65d1cd6ea0..e66ba82f40 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt @@ -10,6 +10,8 @@ import com.tangem.domain.common.TangemCardTypesResolver import com.tangem.domain.common.TangemDerivationStyleProvider import com.tangem.domain.common.TapWorkarounds.isTangemTwins import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.configs.CardConfig +import com.tangem.domain.common.configs.Wallet2CardConfig import com.tangem.domain.models.scan.ScanResponse val ScanResponse.cardTypesResolver: CardTypesResolver @@ -35,14 +37,22 @@ fun ScanResponse.hasDerivation(blockchain: Blockchain, rawDerivationPath: String private fun ScanResponse.hasDerivation(blockchain: Blockchain, derivationPath: DerivationPath): Boolean { val isTestnet = card.isTestCard || blockchain.isTestnet() - return when { - Blockchain.secp256k1Blockchains(isTestnet).contains(blockchain) -> { - hasDerivation(EllipticCurve.Secp256k1, derivationPath) + val config = CardConfig.createConfig(card) + return if (config is Wallet2CardConfig) { + // new logic for wallet2 + val primaryCurve = config.primaryCurve(blockchain) + primaryCurve?.let { hasDerivation(it, derivationPath) } ?: false + } else { + // leave logic for legacy wallets + when { + Blockchain.secp256k1Blockchains(isTestnet).contains(blockchain) -> { + hasDerivation(EllipticCurve.Secp256k1, derivationPath) + } + Blockchain.ed25519OnlyBlockchains(isTestnet).contains(blockchain) -> { + hasDerivation(EllipticCurve.Ed25519, derivationPath) + } + else -> false } - Blockchain.ed25519OnlyBlockchains(isTestnet).contains(blockchain) -> { - hasDerivation(EllipticCurve.Ed25519, derivationPath) - } - else -> false } } diff --git a/domain/legacy/src/test/java/com/tangem/domain/features/BlockchainTests.kt b/domain/legacy/src/test/java/com/tangem/domain/features/BlockchainTests.kt index 292d9e5e06..ff7740c1ba 100644 --- a/domain/legacy/src/test/java/com/tangem/domain/features/BlockchainTests.kt +++ b/domain/legacy/src/test/java/com/tangem/domain/features/BlockchainTests.kt @@ -13,7 +13,6 @@ class BlockchainTests { .toMutableList() .apply { remove(Blockchain.Unknown) - remove(Blockchain.Optimism) } .map { it to Blockchain.fromNetworkId(it.toNetworkId()) } .mapNotNull { if (it.second == null) it.first else null } diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt index a8748d1664..92ed7ae69c 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/DefaultSeedPhraseInteractor.kt @@ -112,4 +112,5 @@ private fun MnemonicErrorResult.mapToError(): SeedPhraseError = when (this) { MnemonicErrorResult.NormalizationFailed -> SeedPhraseError.NormalizationFailed MnemonicErrorResult.UnsupportedLanguage -> SeedPhraseError.UnsupportedLanguage is MnemonicErrorResult.InvalidWords -> SeedPhraseError.InvalidWords(this.words) + MnemonicErrorResult.InvalidMnemonic -> SeedPhraseError.InvalidMnemonic } \ No newline at end of file diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/OnboardingModuleError.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/OnboardingModuleError.kt index 60a22f0fec..e36bc935bd 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/OnboardingModuleError.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/domain/OnboardingModuleError.kt @@ -37,4 +37,5 @@ sealed class SeedPhraseError( object NormalizationFailed : SeedPhraseError(subCode = 5) object UnsupportedLanguage : SeedPhraseError(subCode = 6) data class InvalidWords(val words: Set) : SeedPhraseError(subCode = 7) + object InvalidMnemonic : SeedPhraseError(subCode = 8) } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 6f02666fb4..4131a81f9d 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. -org.gradle.jvmargs = -Xmx4096m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 +org.gradle.jvmargs = -Xmx4096m -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 5ad57c37ed..8a2db82671 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,9 +80,9 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-306" +tangemBlockchainSdk = "develop-312" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-283" +tangemCardSdk = "develop-288" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem diff --git a/plugins/configuration/build.gradle.kts b/plugins/configuration/build.gradle.kts index c2ab7a0a54..a8bc7da8a0 100644 --- a/plugins/configuration/build.gradle.kts +++ b/plugins/configuration/build.gradle.kts @@ -9,8 +9,8 @@ repositories { } configure { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } dependencies { diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/DetektConfigurations.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/DetektConfigurations.kt index 7f3919e6d8..44c658db99 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/DetektConfigurations.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/DetektConfigurations.kt @@ -47,6 +47,6 @@ private fun Project.configureDetektTask() { } } - jvmTarget = "11" + jvmTarget = "17" } } \ No newline at end of file diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/KotlinConfigurations.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/KotlinConfigurations.kt index 9d10cad98d..d0e39d6777 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/KotlinConfigurations.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/KotlinConfigurations.kt @@ -7,7 +7,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile internal fun Project.configureKotlinCompilerOptions() { project.tasks.withType { kotlinOptions { - jvmTarget = "11" + jvmTarget = "17" allWarningsAsErrors = false // this is required to produce a unique META-INF/*.kotlin_module files moduleName = project.path.removePrefix(":").replace(':', '-') diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt index c05a661a44..e148f4849c 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt @@ -12,8 +12,8 @@ internal fun BaseExtension.configureCompileSdk() { internal fun BaseExtension.configureCompilerOptions() { compileOptions { - sourceCompatibility = JavaVersion.VERSION_11 - targetCompatibility = JavaVersion.VERSION_11 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 } } From f422604467e9b86f2b69a312fbdc616406ec2efe Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 17:12:41 +0300 Subject: [PATCH 35/52] Updated on 2026-08-14 --- .../com/tangem/tap/common/extensions/Blockchain.kt | 1 + app/src/main/res/drawable/ic_chia_no_color.xml | 13 +++++++++++++ .../tangem/core/ui/extensions/BlockchainIcons.kt | 2 ++ core/ui/src/main/res/drawable/img_chia_22.xml | 13 +++++++++++++ 4 files changed, 29 insertions(+) create mode 100644 app/src/main/res/drawable/ic_chia_no_color.xml create mode 100644 core/ui/src/main/res/drawable/img_chia_22.xml diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt index 4d081cc2e6..8e50c099b3 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt @@ -46,6 +46,7 @@ fun Blockchain.getGreyedOutIconRes(): Int { Blockchain.Telos, Blockchain.TelosTestnet -> R.drawable.ic_telos_no_color Blockchain.AlephZero, Blockchain.AlephZeroTestnet -> R.drawable.ic_azero_no_color Blockchain.OctaSpace, Blockchain.OctaSpaceTestnet -> R.drawable.ic_octaspace_no_color + Blockchain.Chia, Blockchain.ChiaTestnet -> R.drawable.ic_chia_no_color else -> R.drawable.ic_tangem_logo } } diff --git a/app/src/main/res/drawable/ic_chia_no_color.xml b/app/src/main/res/drawable/ic_chia_no_color.xml new file mode 100644 index 0000000000..9119f89479 --- /dev/null +++ b/app/src/main/res/drawable/ic_chia_no_color.xml @@ -0,0 +1,13 @@ + + + + diff --git a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt index c5e8a5e327..9481be4e27 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/extensions/BlockchainIcons.kt @@ -43,6 +43,7 @@ fun getActiveIconRes(blockchainId: String): Int { "TELOS", "TELOS/test" -> R.drawable.img_telos_22 "aleph-zero", "aleph-zero/test" -> R.drawable.img_azero_22 "octaspace", "octaspace/test" -> R.drawable.img_octaspace_22 + "chia", "chia/test" -> R.drawable.img_chia_22 else -> R.drawable.ic_alert_24 } } @@ -89,6 +90,7 @@ fun getActiveIconResByCoinId(coinId: String, networkId: String): Int { "terra-2" -> R.drawable.img_terra2_22 "telos" -> R.drawable.img_telos_22 "octaspace" -> R.drawable.img_octaspace_22 + "chia" -> R.drawable.img_chia_22 else -> R.drawable.ic_alert_24 } } \ No newline at end of file diff --git a/core/ui/src/main/res/drawable/img_chia_22.xml b/core/ui/src/main/res/drawable/img_chia_22.xml new file mode 100644 index 0000000000..198cffa214 --- /dev/null +++ b/core/ui/src/main/res/drawable/img_chia_22.xml @@ -0,0 +1,13 @@ + + + + From eef7bac933591477d01be5b8e8a45d540e7a9949 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 19:02:42 +0800 Subject: [PATCH 36/52] Updated on 2026-08-14 --- .../ui/components/transactions/Transaction.kt | 11 ++- .../transactions/TransactionList.kt | 35 ++++++---- .../transactions/state/TransactionState.kt | 70 +++++++++++++------ .../transactions/state/TxHistoryState.kt | 10 ++- .../presentation/common/WalletPreviewData.kt | 2 + .../WalletTxHistoryItemFlowConverter.kt | 4 ++ .../utils/TokenListToContentItemsConverter.kt | 2 +- 7 files changed, 94 insertions(+), 40 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt index 1d05888f7f..9256ad0ccc 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/Transaction.kt @@ -23,6 +23,7 @@ import com.tangem.core.ui.components.CircleShimmer import com.tangem.core.ui.components.RectangleShimmer import com.tangem.core.ui.components.transactions.state.TransactionState import com.tangem.core.ui.res.TangemTheme +import java.util.UUID /** * Transaction component @@ -312,45 +313,53 @@ private fun Preview_TransactionItem_DarkTheme( private class TransactionItemStateProvider : CollectionPreviewParameterProvider( collection = listOf( TransactionState.Sending( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "-0.500913 BTC", timestamp = "8:41", ), TransactionState.Receiving( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "+0.500913 BTC", timestamp = "8:41", ), TransactionState.Approving( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "+0.500913 BTC", timestamp = "8:41", ), TransactionState.Swapping( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "+0.500913 BTC", timestamp = "8:41", ), TransactionState.Send( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "-0.500913 BTC", timestamp = "8:41", ), TransactionState.Receive( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "+0.500913 BTC", timestamp = "8:41", ), TransactionState.Approved( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "+0.500913 BTC", timestamp = "8:41", ), TransactionState.Swapped( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "+0.500913 BTC", timestamp = "8:41", ), - TransactionState.Loading, + TransactionState.Loading(txHash = UUID.randomUUID().toString()), ), ) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt index 9ea544233d..6be91c66ae 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/TransactionList.kt @@ -60,26 +60,31 @@ private fun LazyListScope.contentItems( ) { itemsIndexed( items = txHistoryItems, - key = { index, _ -> index }, - itemContent = { index, item -> - if (item == null) return@itemsIndexed - - TxHistoryListItem( - state = item, - modifier = modifier - .animateItemPlacement() - .roundedShapeItemDecoration( - currentIndex = index, - lastIndex = txHistoryItems.itemSnapshotList.lastIndex, - ), - ) + key = { _, item -> + when (item) { + is TxHistoryState.TxHistoryItemState.GroupTitle -> item.title + is TxHistoryState.TxHistoryItemState.Title -> item.onExploreClick.hashCode() + is TxHistoryState.TxHistoryItemState.Transaction -> item.state.txHash + } }, - ) + ) { index, item -> + if (item == null) return@itemsIndexed + + TxHistoryListItem( + state = item, + modifier = modifier + .animateItemPlacement() + .roundedShapeItemDecoration( + currentIndex = index, + lastIndex = txHistoryItems.itemSnapshotList.lastIndex, + ), + ) + } } @OptIn(ExperimentalFoundationApi::class) private fun LazyListScope.nonContentItem(state: EmptyTransactionsBlockState, modifier: Modifier = Modifier) { - item { + item(key = state::class.java, contentType = state::class.java) { EmptyTransactionBlock( state = state, modifier = modifier diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt index 225389c2f4..8c5f73c545 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TransactionState.kt @@ -7,33 +7,39 @@ package com.tangem.core.ui.components.transactions.state */ sealed interface TransactionState { + /** Transaction hash */ + val txHash: String + /** * Content state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ sealed class Content( + override val txHash: String, open val address: String, open val amount: String, open val timestamp: String, ) : TransactionState { fun copySealed( + txHash: String = this.txHash, address: String = this.address, amount: String = this.amount, timestamp: String = this.timestamp, ): Content { return when (this) { - is Approved -> copy(address, amount, timestamp) - is Receive -> copy(address, amount, timestamp) - is Send -> copy(address, amount, timestamp) - is Swapped -> copy(address, amount, timestamp) - is Approving -> copy(address, amount, timestamp) - is Receiving -> copy(address, amount, timestamp) - is Sending -> copy(address, amount, timestamp) - is Swapping -> copy(address, amount, timestamp) + is Approved -> copy(txHash, address, amount, timestamp) + is Receive -> copy(txHash, address, amount, timestamp) + is Send -> copy(txHash, address, amount, timestamp) + is Swapped -> copy(txHash, address, amount, timestamp) + is Approving -> copy(txHash, address, amount, timestamp) + is Receiving -> copy(txHash, address, amount, timestamp) + is Sending -> copy(txHash, address, amount, timestamp) + is Swapping -> copy(txHash, address, amount, timestamp) } } } @@ -41,133 +47,157 @@ sealed interface TransactionState { /** * Content state for processed transaction * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ sealed class ProcessedTransactionContent( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : Content(address, amount, timestamp) + ) : Content(txHash, address, amount, timestamp) /** * Content state for completed transaction * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ sealed class CompletedTransactionContent( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : Content(address, amount, timestamp) + ) : Content(txHash, address, amount, timestamp) /** * Processed sending transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Sending( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : ProcessedTransactionContent(address, amount, timestamp) + ) : ProcessedTransactionContent(txHash, address, amount, timestamp) /** * Processed receiving transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Receiving( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : ProcessedTransactionContent(address, amount, timestamp) + ) : ProcessedTransactionContent(txHash, address, amount, timestamp) /** * Processed approving transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Approving( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : ProcessedTransactionContent(address, amount, timestamp) + ) : ProcessedTransactionContent(txHash, address, amount, timestamp) /** * Processed swapping transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Swapping( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : ProcessedTransactionContent(address, amount, timestamp) + ) : ProcessedTransactionContent(txHash, address, amount, timestamp) /** * Completed sending transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Send( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : CompletedTransactionContent(address, amount, timestamp) + ) : CompletedTransactionContent(txHash, address, amount, timestamp) /** * Completed receiving transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Receive( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : CompletedTransactionContent(address, amount, timestamp) + ) : CompletedTransactionContent(txHash, address, amount, timestamp) /** * Completed approving transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Approved( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : CompletedTransactionContent(address, amount, timestamp) + ) : CompletedTransactionContent(txHash, address, amount, timestamp) /** * Completed swapping transaction state * + * @property txHash transaction hash * @property address address * @property amount amount * @property timestamp timestamp */ data class Swapped( + override val txHash: String, override val address: String, override val amount: String, override val timestamp: String, - ) : CompletedTransactionContent(address, amount, timestamp) + ) : CompletedTransactionContent(txHash, address, amount, timestamp) - /** Loading state */ - object Loading : TransactionState + /** + * Loading state + * + * @property txHash transaction hash + */ + data class Loading(override val txHash: String) : TransactionState } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt index 38db4fa180..85c907187a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/transactions/state/TxHistoryState.kt @@ -27,7 +27,7 @@ sealed interface TxHistoryState { PagingData.from( listOf( TxHistoryItemState.Title(onExploreClick = onExploreClick), - TxHistoryItemState.Transaction(state = TransactionState.Loading), + TxHistoryItemState.Transaction(state = TransactionState.Loading(txHash = LOADING_TX_HASH)), ), ), ), @@ -42,7 +42,7 @@ sealed interface TxHistoryState { items = flowOf( value = PagingData.from( data = buildList(capacity = itemsCount) { - add(TxHistoryItemState.Transaction(state = TransactionState.Loading)) + add(TxHistoryItemState.Transaction(state = TransactionState.Loading(txHash = LOADING_TX_HASH))) }, ), ), @@ -66,7 +66,7 @@ sealed interface TxHistoryState { PagingData.from( listOf( TxHistoryItemState.Title(onExploreClick = onExploreClick), - TxHistoryItemState.Transaction(state = TransactionState.Loading), + TxHistoryItemState.Transaction(state = TransactionState.Loading(txHash = LOADING_TX_HASH)), ), ), ), @@ -118,4 +118,8 @@ sealed interface TxHistoryState { */ data class Transaction(val state: TransactionState) : TxHistoryItemState } + + private companion object { + const val LOADING_TX_HASH = "LOADING_TX_HASH" + } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index f7e10f8451..03fbc388d2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -356,6 +356,7 @@ internal object WalletPreviewData { TxHistoryState.TxHistoryItemState.GroupTitle("Today"), TxHistoryState.TxHistoryItemState.Transaction( TransactionState.Sending( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "-0.500913 BTC", timestamp = "8:41", @@ -364,6 +365,7 @@ internal object WalletPreviewData { TxHistoryState.TxHistoryItemState.GroupTitle("Yesterday"), TxHistoryState.TxHistoryItemState.Transaction( TransactionState.Sending( + txHash = UUID.randomUUID().toString(), address = "33BddS...ga2B", amount = "-0.500913 BTC", timestamp = "8:41", diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt index 878f41f995..1c4af33fac 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/txhistory/WalletTxHistoryItemFlowConverter.kt @@ -97,11 +97,13 @@ internal class WalletTxHistoryItemFlowConverter( ): TransactionState { return when (item.status) { TxHistoryItem.TxStatus.Confirmed -> TransactionState.Receive( + txHash = item.txHash, address = direction.from.toBriefAddressFormat(), amount = item.amount.toCryptoCurrencyFormat(blockchain = blockchain), timestamp = item.getRawTimestamp(), ) TxHistoryItem.TxStatus.Unconfirmed -> TransactionState.Receiving( + txHash = item.txHash, address = direction.from.toBriefAddressFormat(), amount = item.amount.toCryptoCurrencyFormat(blockchain = blockchain), timestamp = item.getRawTimestamp(), @@ -116,11 +118,13 @@ internal class WalletTxHistoryItemFlowConverter( ): TransactionState { return when (item.status) { TxHistoryItem.TxStatus.Confirmed -> TransactionState.Send( + txHash = item.txHash, address = direction.to.toBriefAddressFormat(), amount = item.amount.toCryptoCurrencyFormat(blockchain = blockchain), timestamp = item.getRawTimestamp(), ) TxHistoryItem.TxStatus.Unconfirmed -> TransactionState.Sending( + txHash = item.txHash, address = direction.to.toBriefAddressFormat(), amount = item.amount.toCryptoCurrencyFormat(blockchain = blockchain), timestamp = item.getRawTimestamp(), diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index 5c8fb261e0..7fc1815c63 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -29,7 +29,7 @@ internal class TokenListToContentItemsConverter( override fun convert(value: TokenList): WalletTokensListState { val isEmptyList = when (value) { is TokenList.GroupedByNetwork -> value.groups.isEmpty() - is TokenList.NotInitialized -> true + is TokenList.NotInitialized -> false is TokenList.Ungrouped -> value.currencies.isEmpty() } From fc173d3a1f68a01c3e2a5c22a2f498c8e051e4a0 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 18:05:43 +0300 Subject: [PATCH 37/52] Updated on 2026-08-14 --- app/build.gradle.kts | 2 ++ .../tap/di/domain/AppCurrencyDomainModule.kt | 22 +++++++++++++ core/datasource/build.gradle.kts | 1 - data/app-currency/.gitignore | 1 + data/app-currency/build.gradle.kts | 33 +++++++++++++++++++ .../appcurrency/MockAppCurrencyRepository.kt | 23 +++++++++++++ .../appcurrency/di/AppCurrencyDataModule.kt | 20 +++++++++++ data/tokens/build.gradle.kts | 3 +- domain/app-currency/.gitignore | 1 + domain/app-currency/build.gradle.kts | 12 +++++++ domain/app-currency/models/.gitignore | 1 + domain/app-currency/models/build.gradle.kts | 4 +++ .../domain/appcurrency/model/AppCurrency.kt | 16 +++++++++ .../GetSelectedAppCurrencyUseCase.kt | 24 ++++++++++++++ .../error/SelectedAppCurrencyError.kt | 8 +++++ .../repository/AppCurrencyRepository.kt | 13 ++++++++ .../extension/BaseExtensionConfigurations.kt | 10 +++--- settings.gradle.kts | 3 ++ 18 files changed, 190 insertions(+), 7 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/di/domain/AppCurrencyDomainModule.kt create mode 100644 data/app-currency/.gitignore create mode 100644 data/app-currency/build.gradle.kts create mode 100644 data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt create mode 100644 data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt create mode 100644 domain/app-currency/.gitignore create mode 100644 domain/app-currency/build.gradle.kts create mode 100644 domain/app-currency/models/.gitignore create mode 100644 domain/app-currency/models/build.gradle.kts create mode 100644 domain/app-currency/models/src/main/kotlin/com/tangem/domain/appcurrency/model/AppCurrency.kt create mode 100644 domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt create mode 100644 domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/error/SelectedAppCurrencyError.kt create mode 100644 domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index de202bea18..473fbff374 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -32,6 +32,7 @@ dependencies { implementation(projects.domain.settings) implementation(projects.domain.tokens) implementation(projects.domain.txhistory) + implementation(projects.domain.appCurrency) implementation(project(":common")) implementation(project(":core:analytics")) @@ -51,6 +52,7 @@ dependencies { implementation(projects.data.settings) implementation(projects.data.tokens) implementation(projects.data.txhistory) + implementation(projects.data.appCurrency) /** Features */ implementation(project(":features:onboarding")) diff --git a/app/src/main/java/com/tangem/tap/di/domain/AppCurrencyDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/AppCurrencyDomainModule.kt new file mode 100644 index 0000000000..c69bca18d1 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/di/domain/AppCurrencyDomainModule.kt @@ -0,0 +1,22 @@ +package com.tangem.tap.di.domain + +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.android.components.ViewModelComponent +import dagger.hilt.android.scopes.ViewModelScoped + +@Module +@InstallIn(ViewModelComponent::class) +internal object AppCurrencyDomainModule { + + @Provides + @ViewModelScoped + fun provideGetSelectedAppCurrencyUseCase( + appCurrencyRepository: AppCurrencyRepository, + ): GetSelectedAppCurrencyUseCase { + return GetSelectedAppCurrencyUseCase(appCurrencyRepository) + } +} \ No newline at end of file diff --git a/core/datasource/build.gradle.kts b/core/datasource/build.gradle.kts index bbd2e57c0e..f304966f2a 100644 --- a/core/datasource/build.gradle.kts +++ b/core/datasource/build.gradle.kts @@ -11,7 +11,6 @@ dependencies { /** Project */ implementation(projects.core.utils) implementation(projects.libs.auth) - implementation(projects.domain.core) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) diff --git a/data/app-currency/.gitignore b/data/app-currency/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/data/app-currency/.gitignore @@ -0,0 +1 @@ +/build diff --git a/data/app-currency/build.gradle.kts b/data/app-currency/build.gradle.kts new file mode 100644 index 0000000000..7020b7c452 --- /dev/null +++ b/data/app-currency/build.gradle.kts @@ -0,0 +1,33 @@ +plugins { + alias(deps.plugins.android.library) + alias(deps.plugins.kotlin.android) + alias(deps.plugins.kotlin.kapt) + id("configuration") +} + +android { + namespace = "com.tangem.data.appcurrency" +} + +dependencies { + + /** Project - Domain */ + implementation(projects.domain.core) + implementation(projects.domain.appCurrency) + implementation(projects.domain.appCurrency.models) + + /** Project - Data */ + implementation(projects.core.datasource) + implementation(projects.data.common) + + /** Project - Utils */ + implementation(projects.core.utils) + + /** DI */ + implementation(deps.hilt.core) + kapt(deps.hilt.kapt) + + /** Other */ + implementation(deps.kotlin.coroutines) + implementation(deps.timber) +} \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt new file mode 100644 index 0000000000..d9c600872c --- /dev/null +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt @@ -0,0 +1,23 @@ +package com.tangem.data.appcurrency + +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +internal class MockAppCurrencyRepository : AppCurrencyRepository { + + private val mockAppCurrencies = listOf(AppCurrency(code = "USD", name = "US Dollar", symbol = "$")) + + override fun getSelectedAppCurrency(): Flow { + return flowOf(mockAppCurrencies.first()) + } + + override suspend fun getAvailableAppCurrencies(): List { + return mockAppCurrencies + } + + override suspend fun changeAppCurrency(appCurrency: AppCurrency) { + /* no-op */ + } +} \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt new file mode 100644 index 0000000000..d7f7ebb36c --- /dev/null +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt @@ -0,0 +1,20 @@ +package com.tangem.data.appcurrency.di + +import com.tangem.data.appcurrency.MockAppCurrencyRepository +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AppCurrencyDataModule { + + @Provides + @Singleton + fun provideAppCurrencyRepository(): AppCurrencyRepository { + return MockAppCurrencyRepository() + } +} \ No newline at end of file diff --git a/data/tokens/build.gradle.kts b/data/tokens/build.gradle.kts index c908cd02dd..a45be27508 100644 --- a/data/tokens/build.gradle.kts +++ b/data/tokens/build.gradle.kts @@ -13,10 +13,10 @@ dependencies { /** Project - Domain */ implementation(projects.domain.core) + implementation(projects.domain.demo) implementation(projects.domain.models) implementation(projects.domain.tokens) implementation(projects.domain.tokens.models) - implementation(projects.domain.demo) implementation(projects.domain.wallets.models) /** Project - Data */ @@ -37,7 +37,6 @@ dependencies { /** Other */ implementation(deps.kotlin.coroutines) - implementation(deps.arrow.core) implementation(deps.moshi.kotlin) implementation(deps.jodatime) implementation(deps.timber) diff --git a/domain/app-currency/.gitignore b/domain/app-currency/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/app-currency/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/app-currency/build.gradle.kts b/domain/app-currency/build.gradle.kts new file mode 100644 index 0000000000..45af2fd004 --- /dev/null +++ b/domain/app-currency/build.gradle.kts @@ -0,0 +1,12 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} + +dependencies { + + /** Project - Domain */ + implementation(projects.core.utils) + implementation(projects.domain.core) + implementation(projects.domain.appCurrency.models) +} \ No newline at end of file diff --git a/domain/app-currency/models/.gitignore b/domain/app-currency/models/.gitignore new file mode 100644 index 0000000000..796b96d1c4 --- /dev/null +++ b/domain/app-currency/models/.gitignore @@ -0,0 +1 @@ +/build diff --git a/domain/app-currency/models/build.gradle.kts b/domain/app-currency/models/build.gradle.kts new file mode 100644 index 0000000000..7ff7fb7522 --- /dev/null +++ b/domain/app-currency/models/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + alias(deps.plugins.kotlin.jvm) + id("configuration") +} \ No newline at end of file diff --git a/domain/app-currency/models/src/main/kotlin/com/tangem/domain/appcurrency/model/AppCurrency.kt b/domain/app-currency/models/src/main/kotlin/com/tangem/domain/appcurrency/model/AppCurrency.kt new file mode 100644 index 0000000000..aa8fcb62fc --- /dev/null +++ b/domain/app-currency/models/src/main/kotlin/com/tangem/domain/appcurrency/model/AppCurrency.kt @@ -0,0 +1,16 @@ +package com.tangem.domain.appcurrency.model + +data class AppCurrency( + val code: String, + val name: String, + val symbol: String, +) { + + companion object { + val Default = AppCurrency( + code = "USD", + name = "US Dollar", + symbol = "$", + ) + } +} \ No newline at end of file diff --git a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt new file mode 100644 index 0000000000..4bd3a52e99 --- /dev/null +++ b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/GetSelectedAppCurrencyUseCase.kt @@ -0,0 +1,24 @@ +package com.tangem.domain.appcurrency + +import arrow.core.Either +import arrow.core.left +import arrow.core.right +import com.tangem.domain.appcurrency.error.SelectedAppCurrencyError +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEmpty + +class GetSelectedAppCurrencyUseCase( + private val appCurrencyRepository: AppCurrencyRepository, +) { + + operator fun invoke(): Flow> { + return appCurrencyRepository.getSelectedAppCurrency() + .map> { it.right() } + .catch { emit(SelectedAppCurrencyError.DataError(it).left()) } + .onEmpty { emit(SelectedAppCurrencyError.NoAppCurrencySelected.left()) } + } +} \ No newline at end of file diff --git a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/error/SelectedAppCurrencyError.kt b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/error/SelectedAppCurrencyError.kt new file mode 100644 index 0000000000..4fc83d8671 --- /dev/null +++ b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/error/SelectedAppCurrencyError.kt @@ -0,0 +1,8 @@ +package com.tangem.domain.appcurrency.error + +sealed class SelectedAppCurrencyError { + + object NoAppCurrencySelected : SelectedAppCurrencyError() + + data class DataError(val cause: Throwable) : SelectedAppCurrencyError() +} \ No newline at end of file diff --git a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt new file mode 100644 index 0000000000..d2dafbc461 --- /dev/null +++ b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt @@ -0,0 +1,13 @@ +package com.tangem.domain.appcurrency.repository + +import com.tangem.domain.appcurrency.model.AppCurrency +import kotlinx.coroutines.flow.Flow + +interface AppCurrencyRepository { + + fun getSelectedAppCurrency(): Flow + + suspend fun getAvailableAppCurrencies(): List + + suspend fun changeAppCurrency(appCurrency: AppCurrency) +} \ No newline at end of file diff --git a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt index e148f4849c..3504fee112 100644 --- a/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt +++ b/plugins/configuration/src/main/kotlin/com/tangem/plugin/configuration/configurations/extension/BaseExtensionConfigurations.kt @@ -20,12 +20,14 @@ internal fun BaseExtension.configureCompilerOptions() { internal fun BaseExtension.configureCompose(project: Project) { val useCompose = with(project.path) { contains(":ui") || - contains(":onboarding") || // TODO: divide on api/impl after migrating all onboarding to module - contains(":presentation") || - contains(":app") || // TODO: [REDACTED_JIRA] - contains(":impl") + contains(Regex(pattern = ":onboarding\$")) || // TODO: divide on api/impl after migrating all onboarding to module + contains(Regex(pattern = ":presentation\$")) || + contains(Regex(pattern = ":app\$")) || // TODO: [REDACTED_JIRA] + contains(Regex(pattern = ":impl\$")) } + buildFeatures.compose = useCompose + if (useCompose) { composeOptions { kotlinCompilerExtensionVersion = project.findVersion(alias = "compose-compiler").requiredVersion diff --git a/settings.gradle.kts b/settings.gradle.kts index 8c859f1b92..33ccfc81d6 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -108,6 +108,8 @@ include(":domain:wallets") include(":domain:wallets:models") include(":domain:txhistory") include(":domain:txhistory:models") +include(":domain:app-currency") +include(":domain:app-currency:models") // endregion Domain modules // region Data modules @@ -117,4 +119,5 @@ include(":data:tokens") include(":data:source:preferences") include(":data:settings") include(":data:txhistory") +include(":data:app-currency") // endregion Data modules \ No newline at end of file From 982c8fdc669f123e1ba4eb25b20a15c771777976 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 12:03:17 +0400 Subject: [PATCH 38/52] Updated on 2026-08-14 --- .../com/tangem/tap/domain/TapWalletManager.kt | 26 ++++++++- .../data/WalletConnectRepositoryImpl.kt | 54 ++++++++++++------- .../domain/WalletConnectInteractor.kt | 9 ++++ .../domain/WalletConnectRepository.kt | 2 + 4 files changed, 72 insertions(+), 19 deletions(-) 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 23ee5bb963..6d23a8b78a 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapWalletManager.kt @@ -8,6 +8,7 @@ import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.core.analytics.Analytics import com.tangem.datasource.config.ConfigManager +import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.models.scan.ScanResponse @@ -20,6 +21,8 @@ import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.extensions.setContext import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.walletStores.WalletStoresError +import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor +import com.tangem.tap.domain.walletconnect2.domain.models.Account import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.disclaimer.createDisclaimer import com.tangem.tap.features.disclaimer.redux.DisclaimerAction @@ -92,7 +95,8 @@ class TapWalletManager( null } scope.launch { - store.state.daggerGraphState.walletConnectInteractor?.startListening( + val wcInteractor = store.state.daggerGraphState.walletConnectInteractor ?: return@launch + wcInteractor.startListening( userWalletId = userWallet.walletId.stringValue, cardId = cardId, ) @@ -106,6 +110,9 @@ class TapWalletManager( store.dispatchOnMain(WalletAction.LoadData.Success) store.state.globalState.topUpController?.loadDataSuccess() store.dispatchWithMain(WalletAction.Warnings.CheckHashesCount.VerifyOnlineIfNeeded) + + val wcInteractor = store.state.daggerGraphState.walletConnectInteractor + wcInteractor?.setUserChains(getAccountsForWc(wcInteractor)) } .doOnFailure { error -> val errorAction = when (error) { @@ -135,6 +142,23 @@ class TapWalletManager( } } + private fun getAccountsForWc(wcInteractor: WalletConnectInteractor): List { + return store.state.walletState.walletManagers + .mapNotNull { + val wallet = it.wallet + val chainId = wcInteractor.blockchainHelper.networkIdToChainIdOrNull( + wallet.blockchain.toNetworkId(), + ) + chainId?.let { + Account( + chainId, + wallet.address, + wallet.publicKey.derivationPath?.rawPath, + ) + } + } + } + fun updateConfigManager(data: ScanResponse) { val configManager = store.state.globalState.configManager diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt index 3304faa535..c0755f9bd9 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/data/WalletConnectRepositoryImpl.kt @@ -100,6 +100,28 @@ class WalletConnectRepositoryImpl @Inject constructor( Timber.d("sessionProposal: $sessionProposal") this@WalletConnectRepositoryImpl.sessionProposal = sessionProposal + val missingNetworks = findMissingNetworks( + namespaces = sessionProposal.requiredNamespaces, + userNamespaces = this@WalletConnectRepositoryImpl.userNamespaces ?: emptyMap(), + ) + + if (missingNetworks.isNotEmpty()) { + Timber.w("Not added blockchains: $missingNetworks") + scope.launch { + _events.emit( + WalletConnectEvents.SessionApprovalError( + WalletConnectError.ApprovalErrorMissingNetworks(missingNetworks.toList()), + ), + ) + } + return + } + + val optionalWithoutMissingNetworks = removeMissingNetworks( + namespaces = sessionProposal.optionalNamespaces, + userNamespaces = this@WalletConnectRepositoryImpl.userNamespaces ?: emptyMap(), + ) + scope.launch { _events.emit( WalletConnectEvents.SessionProposal( @@ -108,7 +130,7 @@ class WalletConnectRepositoryImpl @Inject constructor( sessionProposal.url, sessionProposal.icons, sessionProposal.requiredNamespaces.values.flatMap { it.chains ?: emptyList() }, - sessionProposal.optionalNamespaces.values.flatMap { it.chains ?: emptyList() }, + optionalWithoutMissingNetworks.toList(), ), ) } @@ -211,6 +233,10 @@ class WalletConnectRepositoryImpl @Inject constructor( } } + override fun setUserNamespaces(userNamespaces: Map>) { + this.userNamespaces = userNamespaces + } + override fun pair(uri: String) { Web3Wallet.pair(Wallet.Params.Pair(uri)) } @@ -220,23 +246,6 @@ class WalletConnectRepositoryImpl @Inject constructor( val sessionProposal: Wallet.Model.SessionProposal = requireNotNull(this.sessionProposal) - val missingNetworks = findMissingNetworks( - namespaces = sessionProposal.requiredNamespaces, - userNamespaces = userNamespaces, - ) - - if (missingNetworks.isNotEmpty()) { - Timber.e("Not added blockchains: $missingNetworks") - scope.launch { - _events.emit( - WalletConnectEvents.SessionApprovalError( - WalletConnectError.ApprovalErrorMissingNetworks(missingNetworks.toList()), - ), - ) - } - return - } - val userChains = userNamespaces.flatMap { namespace -> namespace.value.map { it.chainId to "${it.chainId}:${it.walletAddress}" } }.groupBy { pair -> pair.first } @@ -430,4 +439,13 @@ class WalletConnectRepositoryImpl @Inject constructor( val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } } return requiredChains.subtract(userChains.toSet()) } + + private fun removeMissingNetworks( + namespaces: Map, + userNamespaces: Map>, + ): Collection { + val wcProvidedChains = namespaces.values.flatMap { it.chains ?: emptyList() } + val userChains = userNamespaces.flatMap { it.value.map { account -> account.chainId } } + return wcProvidedChains.intersect(userChains.toSet()) + } } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt index 9f5ce79361..d95640dc2b 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectInteractor.kt @@ -44,6 +44,15 @@ class WalletConnectInteractor( } } + fun setUserChains(accounts: List) { + val userNamespaces: Map> = accounts + .groupBy { account -> + blockchainHelper.getNamespaceFromFullChainIdOrNull(account.chainId) + ?.let { NetworkNamespace(it) } + }.filterNotNull() + walletConnectRepository.setUserNamespaces(userNamespaces) + } + private suspend fun subscribeToEvents() { events .onEach { wcEvent -> diff --git a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectRepository.kt index 93e772b0d2..33d62fca22 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletconnect2/domain/WalletConnectRepository.kt @@ -11,6 +11,8 @@ interface WalletConnectRepository { fun init(projectId: String) + fun setUserNamespaces(userNamespaces: Map>) + fun updateSessions() fun pair(uri: String) From 9352e4d73eaaebbeb07c200e2520b1ca44b2e010 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 22:59:40 +0300 Subject: [PATCH 39/52] Updated on 2026-08-14 --- gradle/dependencies.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 8a2db82671..1058197b94 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-312" +tangemBlockchainSdk = "develop-314" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-288" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From bc0ecea749897a3c7b9fa6261b169337d8fec346 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 19:14:16 +0300 Subject: [PATCH 40/52] Updated on 2026-08-14 --- .../main/java/com/tangem/datasource/config/ConfigManagerImpl.kt | 1 + .../main/java/com/tangem/datasource/config/models/JsonModels.kt | 1 + 2 files changed, 2 insertions(+) diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt index 1557e84bf1..75b37e271f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/ConfigManagerImpl.kt @@ -96,6 +96,7 @@ internal class ConfigManagerImpl @Inject constructor() : ConfigManager { mainnetApiKey = configValues.tonCenterKeys.mainnet, testnetApiKey = configValues.tonCenterKeys.testnet, ), + chiaFireAcademyApiKey = configValues.chiaFireAcademyApiKey, ), appsFlyerDevKey = configValues.appsFlyer.appsFlyerDevKey, amplitudeApiKey = configValues.amplitudeApiKey, diff --git a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt index 2e195247a6..bcad6003db 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/config/models/JsonModels.kt @@ -38,6 +38,7 @@ class ConfigValueModel( val kaspaSecondaryApiUrl: String, val walletConnectProjectId: String, val tangemComAuthorization: String?, + val chiaFireAcademyApiKey: String?, ) data class AppsFlyer( From 794f1f0914e1566e2f7285204026cb20713a8e36 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 10:22:10 +0300 Subject: [PATCH 41/52] Updated on 2026-08-14 --- .../main/java/com/tangem/tap/common/extensions/Blockchain.kt | 2 +- .../impl/presentation/viewmodels/AddCustomTokenViewModel.kt | 5 ++--- .../tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt | 2 +- .../tap/network/exchangeServices/mercuryo/MercuryoService.kt | 2 +- .../java/com/tangem/domain/common/TangemCardTypesResolver.kt | 3 +++ .../src/main/java/com/tangem/domain/common/TapWorkarounds.kt | 2 +- .../java/com/tangem/domain/common/extensions/Blockchain.kt | 5 ++--- gradle/dependencies.toml | 2 +- 8 files changed, 12 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt index 8e50c099b3..88a8e16ed6 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Blockchain.kt @@ -16,7 +16,7 @@ fun Blockchain.getGreyedOutIconRes(): Int { Blockchain.Ethereum, Blockchain.EthereumTestnet -> R.drawable.ic_eth_no_color Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet -> R.drawable.ic_eth_no_color Blockchain.RSK -> R.drawable.ic_rsk_no_color - Blockchain.Cardano, Blockchain.CardanoShelley -> R.drawable.ic_cardano_no_color + Blockchain.Cardano -> R.drawable.ic_cardano_no_color Blockchain.Tezos -> R.drawable.ic_tezos_no_color Blockchain.XRP -> R.drawable.ic_xrp_no_color Blockchain.Stellar -> R.drawable.ic_stellar_no_color diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 406963b699..4aee8f0645 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -208,8 +208,7 @@ internal class AddCustomTokenViewModel @Inject constructor( val defaultNetwork = createNetworkSelectorItem(blockchain = Blockchain.Unknown) return listOf(defaultNetwork) + Blockchain.values() .filter { blockchain -> - reduxStateHolder.scanResponse?.card?.supportedBlockchains()?.contains(blockchain) == true && - blockchain != Blockchain.Cardano + reduxStateHolder.scanResponse?.card?.supportedBlockchains()?.contains(blockchain) == true } .sortedBy(Blockchain::fullName) .map(::createNetworkSelectorItem) @@ -273,7 +272,7 @@ internal class AddCustomTokenViewModel @Inject constructor( ), ) + Blockchain.values() .filter { blockchain -> - blockchain.isSupportedInApp() && !blockchain.isTestnet() && blockchain != Blockchain.Cardano + blockchain.isSupportedInApp() && !blockchain.isTestnet() } .sortedBy(Blockchain::fullName) .map(::createDerivationPathSelectorAdditionalItem) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt b/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt index c61cb57c3f..9531a726e3 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/ui/MultipleAddressUiHelper.kt @@ -12,7 +12,7 @@ object MultipleAddressUiHelper { Blockchain.BitcoinTestnet, Blockchain.Litecoin, Blockchain.BitcoinCash, - Blockchain.CardanoShelley, + Blockchain.Cardano, ) fun typeToId(type: AddressType, blockchain: Blockchain): Int { diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt index f7725ec89a..39d09fb0dc 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/mercuryo/MercuryoService.kt @@ -119,7 +119,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E return when (currencyName) { "BNB" -> Blockchain.BSC "ETH" -> Blockchain.Ethereum - "ADA" -> Blockchain.CardanoShelley + "ADA" -> Blockchain.Cardano else -> Blockchain.values().find { it.currency.lowercase() == currencyName.lowercase() } } } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index 757ce2bb3e..c3053f87b6 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -103,6 +103,9 @@ internal class TangemCardTypesResolver( "BINANCE/test" -> { Blockchain.BSCTestnet } + "CARDANO" -> { + Blockchain.Cardano + } else -> { Blockchain.fromId(blockchainName) } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index 8ff17001e9..bd892377ae 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -44,7 +44,7 @@ object TapWorkarounds { private val tangemNoteBatches = mapOf( "AB01" to Blockchain.Bitcoin, "AB02" to Blockchain.Ethereum, - "AB03" to Blockchain.CardanoShelley, + "AB03" to Blockchain.Cardano, "AB04" to Blockchain.Dogecoin, "AB05" to Blockchain.BSC, "AB06" to Blockchain.XRP, diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 2665166cf8..590f740d1a 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -32,7 +32,7 @@ fun Blockchain.Companion.fromNetworkId(networkId: String): Blockchain? { "bitcoin/test" -> Blockchain.BitcoinTestnet "bitcoin-cash" -> Blockchain.BitcoinCash "bitcoin-cash/test" -> Blockchain.BitcoinCashTestnet - "cardano" -> Blockchain.CardanoShelley + "cardano" -> Blockchain.Cardano "dogecoin" -> Blockchain.Dogecoin "ducatus" -> Blockchain.Ducatus "litecoin" -> Blockchain.Litecoin @@ -94,7 +94,6 @@ fun Blockchain.toNetworkId(): String { Blockchain.BitcoinCash -> "bitcoin-cash" Blockchain.BitcoinCashTestnet -> "bitcoin-cash/test" Blockchain.Cardano -> "cardano" - Blockchain.CardanoShelley -> "cardano" Blockchain.Dogecoin -> "dogecoin" Blockchain.Ducatus -> "ducatus" Blockchain.Ethereum -> "ethereum" @@ -157,7 +156,7 @@ fun Blockchain.toCoinId(): String { Blockchain.Ethereum, Blockchain.EthereumTestnet -> "ethereum" Blockchain.EthereumClassic, Blockchain.EthereumClassicTestnet -> "ethereum-classic" Blockchain.Stellar, Blockchain.StellarTestnet -> "stellar" - Blockchain.Cardano, Blockchain.CardanoShelley -> "cardano" + Blockchain.Cardano -> "cardano" Blockchain.Polygon, Blockchain.PolygonTestnet -> "matic-network" Blockchain.Arbitrum, Blockchain.ArbitrumTestnet -> "ethereum" Blockchain.Avalanche, Blockchain.AvalancheTestnet -> "avalanche-2" diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 8a2db82671..8bab2c5ac9 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-312" +tangemBlockchainSdk = "develop-313" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-288" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From de890421cbd42451a510f6a9fe2a299071f79ce7 Mon Sep 17 00:00:00 2001 From: Tangem Date: Mon, 14 Aug 2023 08:14:56 +0300 Subject: [PATCH 42/52] Updated on 2026-08-14 --- .../com/tangem/tap/proxy/di/ProxyModule.kt | 6 ++-- .../common/locale/DefaultLocaleProvider.kt | 28 +++++++++++++++++++ .../data/common/locale/LocaleProvider.kt | 13 +++++++++ .../common/locale/di/LocaleProviderModule.kt | 20 +++++++++++++ features/learn2earn/impl/build.gradle.kts | 1 + .../domain/DefaultLearn2earnInteractor.kt | 7 +++-- .../learn2earn/domain/WebViewUriBuilder.kt | 23 ++++----------- .../api/Learn2earnDependencyProvider.kt | 5 ++-- .../domain/di/Learn2earnDomainModule.kt | 5 +++- features/onboarding/build.gradle.kts | 1 + .../wallet2/viewmodel/SeedPhraseViewModel.kt | 15 +++++++--- tangem-android-tools | 2 +- 12 files changed, 94 insertions(+), 32 deletions(-) create mode 100644 data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt create mode 100644 data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt create mode 100644 data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt diff --git a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt index fcd78bd469..19474ce5cc 100644 --- a/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt +++ b/app/src/main/java/com/tangem/tap/proxy/di/ProxyModule.kt @@ -1,6 +1,6 @@ package com.tangem.tap.proxy.di -import androidx.compose.ui.text.intl.Locale +import com.tangem.common.Provider import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.domain.card.repository.CardSdkConfigRepository import com.tangem.domain.common.CardTypesResolver @@ -74,9 +74,7 @@ class ProxyModule { } } - override fun getLocaleProvider(): () -> String = { Locale.current.language } - - override fun getWebViewAuthCredentialsProvider(): () -> String? = { + override fun getWebViewAuthCredentialsProvider(): Provider = Provider { appStateHolder.mainStore?.state?.globalState?.configManager?.config?.tangemComAuthorization } } diff --git a/data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt b/data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt new file mode 100644 index 0000000000..55cdf424ec --- /dev/null +++ b/data/common/src/main/kotlin/com/tangem/data/common/locale/DefaultLocaleProvider.kt @@ -0,0 +1,28 @@ +package com.tangem.data.common.locale + +import java.util.Locale + +/** +[REDACTED_AUTHOR] + */ +internal class DefaultLocaleProvider : LocaleProvider { + + override fun getLocale(): Locale { + return Locale.getDefault() + } + + override fun getWebUriLocaleLanguage(): String { + val language = getLocale().language + return if (LOCALE_LANG_RU.equals(language, true) || LOCALE_LANG_BY.equals(language, true)) { + LOCALE_LANG_RU + } else { + LOCALE_LANG_EN + } + } + + companion object { + const val LOCALE_LANG_RU = "ru" + const val LOCALE_LANG_BY = "by" + const val LOCALE_LANG_EN = "en" + } +} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt b/data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt new file mode 100644 index 0000000000..5f52c75915 --- /dev/null +++ b/data/common/src/main/kotlin/com/tangem/data/common/locale/LocaleProvider.kt @@ -0,0 +1,13 @@ +package com.tangem.data.common.locale + +import java.util.Locale + +/** +[REDACTED_AUTHOR] + */ +interface LocaleProvider { + + fun getLocale(): Locale + + fun getWebUriLocaleLanguage(): String +} \ No newline at end of file diff --git a/data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt b/data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt new file mode 100644 index 0000000000..656c6ed529 --- /dev/null +++ b/data/common/src/main/kotlin/com/tangem/data/common/locale/di/LocaleProviderModule.kt @@ -0,0 +1,20 @@ +package com.tangem.data.common.locale.di + +import com.tangem.data.common.locale.DefaultLocaleProvider +import com.tangem.data.common.locale.LocaleProvider +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object LocaleProviderModule { + + @Provides + @Singleton + fun provideCacheRegistry(): LocaleProvider { + return DefaultLocaleProvider() + } +} \ No newline at end of file diff --git a/features/learn2earn/impl/build.gradle.kts b/features/learn2earn/impl/build.gradle.kts index d85bd7dfba..b9fb7f48a6 100644 --- a/features/learn2earn/impl/build.gradle.kts +++ b/features/learn2earn/impl/build.gradle.kts @@ -19,6 +19,7 @@ dependencies { implementation(project(":core:ui")) implementation(project(":core:res")) implementation(project(":data:source:preferences")) + implementation(project(":data:common")) implementation(project(":libs:auth")) implementation(project(":libs:crypto")) diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt index e7988b5050..59aa4de658 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/DefaultLearn2earnInteractor.kt @@ -1,7 +1,9 @@ package com.tangem.feature.learn2earn.domain import android.net.Uri +import com.tangem.common.Provider import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.data.common.locale.LocaleProvider import com.tangem.datasource.api.promotion.models.PromotionInfoResponse import com.tangem.datasource.demo.DemoModeDatasource import com.tangem.feature.learn2earn.analytics.AnalyticsParam @@ -35,6 +37,7 @@ internal class DefaultLearn2earnInteractor( private val repository: Learn2earnRepository, private val userWalletManager: UserWalletManager, private val derivationManager: DerivationManager, + private val localeProvider: LocaleProvider, private val analytics: AnalyticsEventHandler, private val demoModeDatasource: DemoModeDatasource, private val dependencyProvider: Learn2earnDependencyProvider, @@ -57,8 +60,8 @@ internal class DefaultLearn2earnInteractor( private val webViewUriBuilder: WebViewUriBuilder by lazy { WebViewUriBuilder( authCredentialsProvider = dependencyProvider.getWebViewAuthCredentialsProvider(), - localeLanguageProvider = dependencyProvider.getLocaleProvider(), - promoCodeProvider = { promoUserData.promoCode }, + localeProvider = localeProvider, + promoCodeProvider = Provider { promoUserData.promoCode }, ) } diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt index ac763f5a52..22b06daeb9 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/WebViewUriBuilder.kt @@ -1,15 +1,17 @@ package com.tangem.feature.learn2earn.domain import android.net.Uri +import com.tangem.common.Provider +import com.tangem.data.common.locale.LocaleProvider import com.tangem.feature.learn2earn.impl.BuildConfig /** [REDACTED_AUTHOR] */ internal class WebViewUriBuilder( - private val authCredentialsProvider: () -> String?, - private val localeLanguageProvider: () -> String, - private val promoCodeProvider: () -> String?, + private val authCredentialsProvider: Provider, + private val localeProvider: LocaleProvider, + private val promoCodeProvider: Provider, ) { fun buildUriForNewUser(learningIsFinished: Boolean): Uri { @@ -46,20 +48,11 @@ internal class WebViewUriBuilder( } else { authority(BASE_URL) } - appendPath(getLocaleLanguage(localeLanguageProvider.invoke())) + appendPath(localeProvider.getWebUriLocaleLanguage()) appendPath(PATH_PROMOTION) appendQueryParameter(QUERY_FINISHED, learningIsFinished.toString()) } - // TODO: locale: This can be used by another feature. Move it to the appropriate location - private fun getLocaleLanguage(language: String): String { - return if (LOCALE_LANG_RU.equals(language, true) || LOCALE_LANG_BY.equals(language, true)) { - LOCALE_LANG_RU - } else { - LOCALE_LANG_EN - } - } - private companion object { const val SCHEME = "https" @@ -71,9 +64,5 @@ internal class WebViewUriBuilder( const val QUERY_FINISHED = "finished" const val DEV_BASE_URL = "devweb.tangem.com" - - const val LOCALE_LANG_RU = "ru" - const val LOCALE_LANG_BY = "by" - const val LOCALE_LANG_EN = "en" } } \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnDependencyProvider.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnDependencyProvider.kt index 60a23a88fb..946d065952 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnDependencyProvider.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/api/Learn2earnDependencyProvider.kt @@ -1,5 +1,6 @@ package com.tangem.feature.learn2earn.domain.api +import com.tangem.common.Provider import com.tangem.domain.common.CardTypesResolver import kotlinx.coroutines.flow.Flow @@ -12,7 +13,5 @@ interface Learn2earnDependencyProvider { fun getCardTypeResolverFlow(): Flow - fun getLocaleProvider(): () -> String - - fun getWebViewAuthCredentialsProvider(): () -> String? + fun getWebViewAuthCredentialsProvider(): Provider } \ No newline at end of file diff --git a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt index ae96ed3668..559deecff7 100644 --- a/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt +++ b/features/learn2earn/impl/src/main/java/com/tangem/feature/learn2earn/domain/di/Learn2earnDomainModule.kt @@ -3,6 +3,7 @@ package com.tangem.feature.learn2earn.domain.di import android.content.Context import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.featuretoggle.manager.FeatureTogglesManager +import com.tangem.data.common.locale.LocaleProvider import com.tangem.datasource.demo.DemoModeDatasource import com.tangem.feature.learn2earn.data.api.Learn2earnRepository import com.tangem.feature.learn2earn.data.toggles.DefaultLearn2earnFeatureToggleManager @@ -45,8 +46,9 @@ internal class Learn2earnDomainModule { repository: Learn2earnRepository, dependencyProvider: Learn2earnDependencyProvider, userWalletManager: UserWalletManager, - analyticsEventHandler: AnalyticsEventHandler, derivationManager: DerivationManager, + localeProvider: LocaleProvider, + analyticsEventHandler: AnalyticsEventHandler, demoModeDatasource: DemoModeDatasource, dispatchers: AppCoroutineDispatcherProvider, ): Learn2earnInteractor { @@ -55,6 +57,7 @@ internal class Learn2earnDomainModule { repository = repository, userWalletManager = userWalletManager, derivationManager = derivationManager, + localeProvider = localeProvider, analytics = analyticsEventHandler, demoModeDatasource = demoModeDatasource, dependencyProvider = dependencyProvider, diff --git a/features/onboarding/build.gradle.kts b/features/onboarding/build.gradle.kts index cdd735f59c..8318108615 100644 --- a/features/onboarding/build.gradle.kts +++ b/features/onboarding/build.gradle.kts @@ -18,6 +18,7 @@ dependencies { implementation(project(":core:utils")) implementation(project(":core:ui")) implementation(project(":core:res")) + implementation(project(":data:common")) /** Tangem libraries */ implementation(deps.tangem.card.core) diff --git a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt index 69a68a2b7b..204ae54069 100644 --- a/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt +++ b/features/onboarding/src/main/java/com/tangem/feature/onboarding/presentation/wallet2/viewmodel/SeedPhraseViewModel.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.tangem.common.CompletionResult import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.data.common.locale.LocaleProvider import com.tangem.feature.onboarding.data.model.CreateWalletResponse import com.tangem.feature.onboarding.domain.SeedPhraseError import com.tangem.feature.onboarding.domain.SeedPhraseInteractor @@ -35,6 +36,7 @@ import javax.inject.Inject @HiltViewModel class SeedPhraseViewModel @Inject constructor( private val interactor: SeedPhraseInteractor, + private val localeProvider: LocaleProvider, private val dispatchers: CoroutineDispatcherProvider, private val analyticsEventHandler: AnalyticsEventHandler, ) : ViewModel() { @@ -303,7 +305,14 @@ class SeedPhraseViewModel @Inject constructor( private fun buttonReadMoreAboutSeedPhraseClick() { analyticsEventHandler.send(SeedPhraseEvents.ButtonReadMore) - router.openUri(URI_ABOUT_SEED_PHRASE) + val webUri = Uri.Builder() + .scheme("https") + .authority("tangem.com") + .appendPath(localeProvider.getWebUriLocaleLanguage()) + .appendPath("blog/post/seed-phrase-a-risky-solution") + .build() + + router.openUri(webUri) } private fun buttonGenerateSeedPhraseClick() { @@ -327,7 +336,7 @@ class SeedPhraseViewModel @Inject constructor( } } - private suspend fun generateMnemonicGridList(mnemonicComponents: List): ImmutableList { + private fun generateMnemonicGridList(mnemonicComponents: List): ImmutableList { val size = mnemonicComponents.size val splitIndex = if (size.isEven()) size / 2 else size / 2 + 1 val leftColumn = mnemonicComponents.subList(0, splitIndex) @@ -418,8 +427,6 @@ class SeedPhraseViewModel @Inject constructor( // endregion Utils companion object { - private val URI_ABOUT_SEED_PHRASE = - Uri.parse("https://tangem.com/ru/blog/post/seed-phrase-a-risky-solution/") private const val MNEMONIC_DEBOUNCER = "MnemonicDebouncer" private const val MNEMONIC_DEBOUNCE_DELAY = 700L private const val DELAY_GENERATE_SEED_PHRASE = 300L diff --git a/tangem-android-tools b/tangem-android-tools index 03186c35c1..0e4934995b 160000 --- a/tangem-android-tools +++ b/tangem-android-tools @@ -1 +1 @@ -Subproject commit 03186c35c13d8693d9a4c7dcb6e760fa58984e67 +Subproject commit 0e4934995b8e7c69862ef80362e86bd166bd1288 From 40b616868d163f91949b83262844c6e4613c4f7f Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 13:24:38 +0300 Subject: [PATCH 43/52] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + .../java/com/tangem/tap/TapApplication.kt | 5 ++ .../common/redux/global/GlobalMiddleware.kt | 46 ++++++++---- .../middlewares/AppCurrencyMiddleware.kt | 73 ++++++++++++++----- .../redux/middlewares/WalletMiddleware.kt | 2 + .../tap/proxy/redux/DaggerGraphState.kt | 2 + .../datasource/di/AppCurrencyDataModule.kt | 20 +++++ .../MockSelectedAppCurrencyStore.kt | 26 +++++++ .../appcurrency/SelectedAppCurrencyStore.kt | 11 +++ .../com/tangem/utils/coroutines}/JobHolder.kt | 6 +- .../appcurrency/MockAppCurrencyRepository.kt | 2 +- .../tangem/data/tokens/di/TokensDataModule.kt | 10 ++- .../repository/DefaultQuotesRepository.kt | 28 +++++-- .../repository/AppCurrencyRepository.kt | 2 +- domain/tokens/build.gradle.kts | 6 ++ features/wallet/impl/build.gradle.kts | 2 + .../router/DefaultWalletRouter.kt | 2 + .../WalletLoadedTokensListConverter.kt | 5 +- ...letSingleCurrencyLoadedBalanceConverter.kt | 21 +++--- .../state/factory/WalletStateFactory.kt | 6 +- ...ryptoCurrencyStatusToTokenItemConverter.kt | 8 +- .../utils/FiatBalanceToWalletCardConverter.kt | 8 +- .../utils/TokenListToContentItemsConverter.kt | 8 +- .../utils/TokenListToWalletStateConverter.kt | 10 +-- .../wallet/viewmodels/WalletViewModel.kt | 26 ++++++- 25 files changed, 258 insertions(+), 78 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/MockSelectedAppCurrencyStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt rename {features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels => core/utils/src/main/java/com/tangem/utils/coroutines}/JobHolder.kt (63%) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 473fbff374..38e1d9c644 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -33,6 +33,7 @@ dependencies { implementation(projects.domain.tokens) implementation(projects.domain.txhistory) implementation(projects.domain.appCurrency) + implementation(projects.domain.appCurrency.models) implementation(project(":common")) implementation(project(":core:analytics")) diff --git a/app/src/main/java/com/tangem/tap/TapApplication.kt b/app/src/main/java/com/tangem/tap/TapApplication.kt index cb21e52c1d..28cd0f57de 100644 --- a/app/src/main/java/com/tangem/tap/TapApplication.kt +++ b/app/src/main/java/com/tangem/tap/TapApplication.kt @@ -23,6 +23,7 @@ import com.tangem.datasource.config.FeaturesLocalLoader import com.tangem.datasource.config.models.Config import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.domain.DomainLayer +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.common.LogConfig import com.tangem.domain.wallets.legacy.WalletManagersRepository @@ -161,6 +162,9 @@ class TapApplication : Application(), ImageLoaderFactory { @Inject lateinit var blockchainExceptionHandler: BlockchainExceptionHandler + @Inject + lateinit var appCurrencyRepository: AppCurrencyRepository + override fun onCreate() { super.onCreate() @@ -179,6 +183,7 @@ class TapApplication : Application(), ImageLoaderFactory { walletConnectSessionsRepository = walletConnectSessionsRepository, tokenDetailsFeatureToggles = tokenDetailsFeatureToggles, scanCardProcessor = scanCardProcessor, + appCurrencyRepository = appCurrencyRepository, ), ), ) diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt index 46835f3076..287b5d35f1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt @@ -8,10 +8,12 @@ import com.tangem.domain.common.LogConfig import com.tangem.domain.common.extensions.withMainContext import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse +import com.tangem.tap.* import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.dispatchDebugErrorNotification import com.tangem.tap.common.extensions.dispatchDialogShow import com.tangem.tap.common.extensions.dispatchOnMain +import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager @@ -24,17 +26,13 @@ import com.tangem.tap.network.exchangeServices.ExchangeService import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoEnvironment import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService import com.tangem.tap.network.exchangeServices.moonpay.MoonPayService -import com.tangem.tap.preferencesStorage -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.tap.tangemSdkManager -import com.tangem.tap.userTokensRepository -import com.tangem.tap.walletCurrenciesManager +import com.tangem.tap.proxy.redux.DaggerGraphState +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch import org.rekotlin.Action import org.rekotlin.DispatchFunction import org.rekotlin.Middleware -import java.util.* +import java.util.Locale object GlobalMiddleware { val handler = globalMiddlewareHandler @@ -68,13 +66,11 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } } is GlobalAction.RestoreAppCurrency -> { - store.dispatch( - GlobalAction.RestoreAppCurrency.Success( - preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() - ?.run { FiatCurrency(code, name, symbol) } - ?: FiatCurrency.Default, - ), - ) + if (store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles).isRedesignedScreenEnabled) { + restoreAppCurrencyNew() + } else { + restoreAppCurrencyLegacy() + } } is GlobalAction.HideWarningMessage -> { store.state.globalState.warningManager?.let { @@ -193,6 +189,28 @@ private fun handleAction(action: Action, appState: () -> AppState?, dispatch: Di } } +private fun restoreAppCurrencyLegacy() { + store.dispatch( + GlobalAction.RestoreAppCurrency.Success( + preferencesStorage.fiatCurrenciesPrefStorage.getAppCurrency() + ?.run { FiatCurrency(code, name, symbol) } + ?: FiatCurrency.Default, + ), + ) +} + +private fun restoreAppCurrencyNew() { + scope.launch { + val currency = store.state.daggerGraphState.get(DaggerGraphState::appCurrencyRepository) + .getSelectedAppCurrency() + .firstOrNull() + ?.run { FiatCurrency(code, name, symbol) } + ?: FiatCurrency.Default + + store.dispatchWithMain(GlobalAction.RestoreAppCurrency.Success(currency)) + } +} + private fun makeSellExchangeService(config: Config): ExchangeService { return MoonPayService( apiKey = config.moonPayApiKey, diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt index bc5e960649..402f1aaef9 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/AppCurrencyMiddleware.kt @@ -3,12 +3,14 @@ package com.tangem.tap.features.wallet.redux.middlewares import com.tangem.common.extensions.guard import com.tangem.core.analytics.Analytics import com.tangem.data.source.preferences.model.DataSourceCurrency -import com.tangem.data.source.preferences.model.DataSourceFiatCurrency import com.tangem.data.source.preferences.storage.FiatCurrenciesPrefStorage +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import com.tangem.features.wallet.featuretoggles.WalletFeatureToggles import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.MainScreen import com.tangem.tap.common.entities.FiatCurrency import com.tangem.tap.common.extensions.dispatchDialogShow +import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapWalletManager import com.tangem.tap.features.details.redux.DetailsAction @@ -19,6 +21,8 @@ import com.tangem.tap.features.walletSelector.redux.WalletSelectorAction import com.tangem.tap.scope import com.tangem.tap.store import com.tangem.tap.userWalletsListManager +import com.tangem.utils.coroutines.JobHolder +import com.tangem.utils.coroutines.saveIn import kotlinx.coroutines.launch import timber.log.Timber @@ -26,8 +30,12 @@ class AppCurrencyMiddleware( private val walletRepository: WalletRepository, private val tapWalletManager: TapWalletManager, private val fiatCurrenciesPrefStorage: FiatCurrenciesPrefStorage, + private val featureToggles: WalletFeatureToggles, + private val appCurrencyRepository: AppCurrencyRepository, private val appCurrencyProvider: () -> FiatCurrency, ) { + private val showSelectorJobHolder = JobHolder() + fun handle(action: WalletAction.AppCurrencyAction) { when (action) { is WalletAction.AppCurrencyAction.ChooseAppCurrency -> showSelector() @@ -36,6 +44,52 @@ class AppCurrencyMiddleware( } private fun showSelector() { + if (featureToggles.isRedesignedScreenEnabled) { + showSelectorNew() + } else { + showSelectorLegacy() + } + } + + private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) { + Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(action.fiatCurrency))) + + scope.launch { + appCurrencyRepository.changeAppCurrency(action.fiatCurrency.code) + + store.dispatchWithMain(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) + store.dispatchWithMain(DetailsAction.ChangeAppCurrency(action.fiatCurrency)) + store.dispatchWithMain(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency)) + + val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { + Timber.e("Unable to select currency, no user wallet selected") + return@launch + } + + tapWalletManager.loadData(selectedUserWallet, refresh = true) + } + } + + private fun showSelectorNew() { + scope.launch { + val currencies = appCurrencyRepository.getAvailableAppCurrencies() + + store.dispatchDialogShow( + WalletDialog.CurrencySelectionDialog( + currenciesList = currencies.map { appCurrency -> + FiatCurrency( + code = appCurrency.code, + name = appCurrency.name, + symbol = appCurrency.symbol, + ) + }, + currentAppCurrency = appCurrencyProvider.invoke(), + ), + ) + }.saveIn(showSelectorJobHolder) + } + + private fun showSelectorLegacy() { val storedFiatCurrencies = fiatCurrenciesPrefStorage.restore() if (storedFiatCurrencies.isNotEmpty()) { store.dispatchDialogShow( @@ -65,23 +119,6 @@ class AppCurrencyMiddleware( } } - private fun selectCurrency(action: WalletAction.AppCurrencyAction.SelectAppCurrency) { - Analytics.send(MainScreen.MainCurrencyChanged(AnalyticsParam.CurrencyType.FiatCurrency(action.fiatCurrency))) - fiatCurrenciesPrefStorage.saveAppCurrency( - with(action.fiatCurrency) { DataSourceFiatCurrency(code, name, symbol) }, - ) - store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrency)) - store.dispatch(DetailsAction.ChangeAppCurrency(action.fiatCurrency)) - store.dispatch(WalletSelectorAction.ChangeAppCurrency(action.fiatCurrency)) - val selectedUserWallet = userWalletsListManager.selectedUserWalletSync.guard { - Timber.e("Unable to select currency, no user wallet selected") - return - } - scope.launch { - tapWalletManager.loadData(selectedUserWallet, refresh = true) - } - } - private fun List.mapToUiModel(): List { return this.map { FiatCurrency( diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt index e7d5ff135d..c84ac7fa2d 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/WalletMiddleware.kt @@ -55,6 +55,8 @@ class WalletMiddleware { walletRepository = store.state.featureRepositoryProvider.walletRepository, tapWalletManager = store.state.globalState.tapWalletManager, fiatCurrenciesPrefStorage = preferencesStorage.fiatCurrenciesPrefStorage, + appCurrencyRepository = store.state.daggerGraphState.get(DaggerGraphState::appCurrencyRepository), + featureToggles = store.state.daggerGraphState.get(DaggerGraphState::walletFeatureToggles), appCurrencyProvider = { store.state.globalState.appCurrency }, ) } diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index 228d3f014b..b9be4b9f93 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -2,6 +2,7 @@ package com.tangem.tap.proxy.redux import com.tangem.datasource.asset.AssetReader import com.tangem.datasource.connection.NetworkConnectionManager +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.ScanCardUseCase import com.tangem.domain.card.repository.CardSdkConfigRepository @@ -31,6 +32,7 @@ data class DaggerGraphState( val tokenDetailsRouter: TokenDetailsRouter? = null, val scanCardProcessor: ScanCardProcessor? = null, val cardSdkConfigRepository: CardSdkConfigRepository? = null, + val appCurrencyRepository: AppCurrencyRepository? = null, ) : StateType { inline fun get(getDependency: DaggerGraphState.() -> T?): T { diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt new file mode 100644 index 0000000000..724634e0a3 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt @@ -0,0 +1,20 @@ +package com.tangem.datasource.di + +import com.tangem.datasource.local.appcurrency.MockSelectedAppCurrencyStore +import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import dagger.Module +import dagger.Provides +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import javax.inject.Singleton + +@Module +@InstallIn(SingletonComponent::class) +internal object AppCurrencyDataModule { + + @Provides + @Singleton + fun provideSelectedAppCurrencyStore(): SelectedAppCurrencyStore { + return MockSelectedAppCurrencyStore() + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/MockSelectedAppCurrencyStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/MockSelectedAppCurrencyStore.kt new file mode 100644 index 0000000000..58f48aacc8 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/MockSelectedAppCurrencyStore.kt @@ -0,0 +1,26 @@ +package com.tangem.datasource.local.appcurrency + +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf + +// TODO: Will be implemented in [REDACTED_TASK_KEY] task +internal class MockSelectedAppCurrencyStore : SelectedAppCurrencyStore { + + override fun get(): Flow { + return flowOf( + CurrenciesResponse.Currency( + id = "usd", + code = "USD", + name = "US Dollar", + unit = "$", + type = "fiat", + rateBTC = "", + ), + ) + } + + override suspend fun store(item: CurrenciesResponse.Currency) { + /* no-op */ + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt new file mode 100644 index 0000000000..e1fbad8329 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/SelectedAppCurrencyStore.kt @@ -0,0 +1,11 @@ +package com.tangem.datasource.local.appcurrency + +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import kotlinx.coroutines.flow.Flow + +interface SelectedAppCurrencyStore { + + fun get(): Flow + + suspend fun store(item: CurrenciesResponse.Currency) +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt b/core/utils/src/main/java/com/tangem/utils/coroutines/JobHolder.kt similarity index 63% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt rename to core/utils/src/main/java/com/tangem/utils/coroutines/JobHolder.kt index 6414445119..3c0ca6bd8d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/JobHolder.kt +++ b/core/utils/src/main/java/com/tangem/utils/coroutines/JobHolder.kt @@ -1,4 +1,4 @@ -package com.tangem.feature.wallet.presentation.wallet.viewmodels +package com.tangem.utils.coroutines import kotlinx.coroutines.Job @@ -7,7 +7,7 @@ import kotlinx.coroutines.Job * [REDACTED_AUTHOR] */ -internal class JobHolder { +class JobHolder { private var job: Job? = null @@ -18,4 +18,4 @@ internal class JobHolder { } } -internal fun Job.saveIn(jobHolder: JobHolder) = jobHolder.update(job = this) \ No newline at end of file +fun Job.saveIn(jobHolder: JobHolder) = jobHolder.update(job = this) \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt index d9c600872c..e3e518afdc 100644 --- a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt @@ -17,7 +17,7 @@ internal class MockAppCurrencyRepository : AppCurrencyRepository { return mockAppCurrencies } - override suspend fun changeAppCurrency(appCurrency: AppCurrency) { + override suspend fun changeAppCurrency(currencyCode: String) { /* no-op */ } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt index 7d62d72157..c1229cde5b 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/di/TokensDataModule.kt @@ -5,6 +5,7 @@ import com.tangem.data.tokens.repository.DefaultCurrenciesRepository import com.tangem.data.tokens.repository.DefaultNetworksRepository import com.tangem.data.tokens.repository.DefaultQuotesRepository import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore import com.tangem.datasource.local.quote.QuotesStore import com.tangem.datasource.local.token.UserTokensStore import com.tangem.datasource.local.userwallet.UserWalletsStore @@ -40,10 +41,17 @@ internal object TokensDataModule { fun provideQuotesRepository( tangemTechApi: TangemTechApi, quotesStore: QuotesStore, + selectedAppCurrencyStore: SelectedAppCurrencyStore, cacheRegistry: CacheRegistry, dispatchers: CoroutineDispatcherProvider, ): QuotesRepository { - return DefaultQuotesRepository(tangemTechApi, quotesStore, cacheRegistry, dispatchers) + return DefaultQuotesRepository( + tangemTechApi, + quotesStore, + selectedAppCurrencyStore, + cacheRegistry, + dispatchers, + ) } @Provides diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt index ea7fdbd998..25fb880a50 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/repository/DefaultQuotesRepository.kt @@ -3,6 +3,7 @@ package com.tangem.data.tokens.repository import com.tangem.data.common.cache.CacheRegistry import com.tangem.data.tokens.utils.QuotesConverter import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore import com.tangem.datasource.local.quote.QuotesStore import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Quote @@ -10,6 +11,7 @@ import com.tangem.domain.tokens.repository.QuotesRepository import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import timber.log.Timber @@ -17,12 +19,15 @@ import timber.log.Timber internal class DefaultQuotesRepository( private val tangemTechApi: TangemTechApi, private val quotesStore: QuotesStore, + private val selectedAppCurrencyStore: SelectedAppCurrencyStore, private val cacheRegistry: CacheRegistry, private val dispatchers: CoroutineDispatcherProvider, ) : QuotesRepository { private val quotesConverter = QuotesConverter() + private var quotesFetchedForAppCurrency: String? = null + override fun getQuotes(currenciesIds: Set, refresh: Boolean): Flow> { return channelFlow { launch(dispatchers.io) { @@ -32,22 +37,33 @@ internal class DefaultQuotesRepository( } launch(dispatchers.io) { - fetchExpiredQuotes(currenciesIds, refresh) + selectedAppCurrencyStore.get().collectLatest { appCurrency -> + fetchExpiredQuotes(currenciesIds, appCurrency.id, refresh) + } } } } - private suspend fun fetchExpiredQuotes(currenciesIds: Set, refresh: Boolean) { - val expiredCurrenciesIds = filterExpiredCurrenciesIds(currenciesIds, refresh) + private suspend fun fetchExpiredQuotes( + currenciesIds: Set, + appCurrencyId: String, + refresh: Boolean, + ) { + val expiredCurrenciesIds = filterExpiredCurrenciesIds( + currenciesIds = currenciesIds, + refresh = refresh || quotesFetchedForAppCurrency != appCurrencyId, + ) if (expiredCurrenciesIds.isEmpty()) return - fetchQuotes(expiredCurrenciesIds) + quotesFetchedForAppCurrency = appCurrencyId + + fetchQuotes(expiredCurrenciesIds, appCurrencyId) } - private suspend fun fetchQuotes(rawCurrenciesIds: Set) { + private suspend fun fetchQuotes(rawCurrenciesIds: Set, appCurrencyId: String) { try { val response = tangemTechApi.getQuotes( - currencyId = "usd", // TODO: [REDACTED_JIRA] + currencyId = appCurrencyId, coinIds = rawCurrenciesIds.joinToString(separator = ","), ) diff --git a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt index d2dafbc461..3ba529f8ae 100644 --- a/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt +++ b/domain/app-currency/src/main/kotlin/com/tangem/domain/appcurrency/repository/AppCurrencyRepository.kt @@ -9,5 +9,5 @@ interface AppCurrencyRepository { suspend fun getAvailableAppCurrencies(): List - suspend fun changeAppCurrency(appCurrency: AppCurrency) + suspend fun changeAppCurrency(currencyCode: String) } \ No newline at end of file diff --git a/domain/tokens/build.gradle.kts b/domain/tokens/build.gradle.kts index 4099f104eb..e2b240e5bc 100644 --- a/domain/tokens/build.gradle.kts +++ b/domain/tokens/build.gradle.kts @@ -4,11 +4,17 @@ plugins { } dependencies { + + /** Project - Domain */ implementation(projects.domain.core) implementation(projects.domain.tokens.models) implementation(projects.domain.wallets.models) + implementation(projects.domain.appCurrency.models) + + /** Project - Other */ implementation(projects.core.utils) + /** Tests */ testImplementation(deps.test.junit) testImplementation(deps.test.coroutine) } \ No newline at end of file diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index d517ab1def..ed9a84d4f4 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -56,6 +56,8 @@ dependencies { implementation(projects.domain.txhistory.models) implementation(projects.domain.wallets) implementation(projects.domain.wallets.models) + implementation(projects.domain.appCurrency) + implementation(projects.domain.appCurrency.models) /** Feature Apis */ implementation(projects.features.wallet.api) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index de6989f860..397a39c2ae 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -83,6 +83,8 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation } override fun openDetailsScreen() { + // FIXME: Prepare details screen (e.g. dispatch action: `DetailsAction.PrepareScreen`) + // [REDACTED_JIRA] navigationStateHolder.navigate(action = NavigationAction.NavigateTo(AppScreen.Details)) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt index 6b4f617a1b..de50af78d2 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletLoadedTokensListConverter.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import arrow.core.Either import com.tangem.common.Provider +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.model.TokenList @@ -25,6 +26,7 @@ import com.tangem.utils.converter.Converter */ internal class WalletLoadedTokensListConverter( private val currentStateProvider: Provider, + appCurrencyProvider: Provider, cardTypeResolverProvider: Provider, isLockedWalletProvider: Provider, clickIntents: WalletClickIntents, @@ -34,9 +36,8 @@ internal class WalletLoadedTokensListConverter( currentStateProvider = currentStateProvider, cardTypeResolverProvider = cardTypeResolverProvider, isLockedWalletProvider = isLockedWalletProvider, + appCurrencyProvider = appCurrencyProvider, isWalletContentHidden = false, // TODO: [REDACTED_JIRA] - fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA] - fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA] clickIntents = clickIntents, ) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt index 6abcf3e185..c6d3e7fae9 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletSingleCurrencyLoadedBalanceConverter.kt @@ -5,6 +5,7 @@ import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.model.CryptoCurrencyStatus @@ -20,8 +21,7 @@ import java.math.BigDecimal internal class WalletSingleCurrencyLoadedBalanceConverter( private val currentStateProvider: Provider, private val cardTypeResolverProvider: Provider, - private val fiatCurrencyCode: String, - private val fiatCurrencySymbol: String, + private val appCurrencyProvider: Provider, ) : Converter, WalletSingleCurrencyState.Content> { override fun convert(value: Either): WalletSingleCurrencyState.Content { @@ -47,7 +47,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( is CryptoCurrencyStatus.Loaded, -> MarketPriceBlockState.Content( currencyName = currencyName, - price = formatPrice(status), + price = formatPrice(status, appCurrencyProvider()), priceChangeConfig = PriceChangeConfig( valueInPercent = formatPriceChange(status), type = getPriceChangeType(status), @@ -67,6 +67,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( state: WalletSingleCurrencyState, ): WalletsListConfig { val selectedWallet = state.walletsListConfig.wallets[state.walletsListConfig.selectedWalletIndex] + val updatedWallet = when (status) { is CryptoCurrencyStatus.NoQuote, is CryptoCurrencyStatus.Loaded, @@ -81,7 +82,7 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( ), imageResId = selectedWallet.imageResId, onClick = selectedWallet.onClick, - balance = formatFiatAmount(status), + balance = formatFiatAmount(status, appCurrencyProvider()), ) } is CryptoCurrencyStatus.Loading -> { @@ -133,23 +134,23 @@ internal class WalletSingleCurrencyLoadedBalanceConverter( ) } - private fun formatPrice(status: CryptoCurrencyStatus.Status): String { + private fun formatPrice(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String { val fiatRate = status.fiatRate ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN return BigDecimalFormatter.formatFiatAmount( fiatAmount = fiatRate, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, ) } - private fun formatFiatAmount(status: CryptoCurrencyStatus.Status): String { + private fun formatFiatAmount(status: CryptoCurrencyStatus.Status, appCurrency: AppCurrency): String { val fiatAmount = status.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN return BigDecimalFormatter.formatFiatAmount( fiatAmount = fiatAmount, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, + fiatCurrencyCode = appCurrency.code, + fiatCurrencySymbol = appCurrency.symbol, ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt index 80436dcaa8..9e274774b5 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/state/factory/WalletStateFactory.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.wallet.state.factory import androidx.paging.PagingData import arrow.core.Either import com.tangem.common.Provider +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.error.CurrencyError import com.tangem.domain.tokens.error.TokenListError @@ -37,6 +38,7 @@ internal class WalletStateFactory( private val currentStateProvider: Provider, private val currentCardTypeResolverProvider: Provider, private val isLockedWalletProvider: Provider, + private val appCurrencyProvider: Provider, private val clickIntents: WalletClickIntents, ) { @@ -47,6 +49,7 @@ internal class WalletStateFactory( currentStateProvider = currentStateProvider, cardTypeResolverProvider = currentCardTypeResolverProvider, isLockedWalletProvider = isLockedWalletProvider, + appCurrencyProvider = appCurrencyProvider, clickIntents = clickIntents, ) } @@ -70,8 +73,7 @@ internal class WalletStateFactory( WalletSingleCurrencyLoadedBalanceConverter( currentStateProvider = currentStateProvider, cardTypeResolverProvider = currentCardTypeResolverProvider, - fiatCurrencyCode = "USD", // TODO: [REDACTED_JIRA] - fiatCurrencySymbol = "$", // TODO: [REDACTED_JIRA] + appCurrencyProvider = appCurrencyProvider, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index 2779e6473a..1790a50da4 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -1,8 +1,10 @@ package com.tangem.feature.wallet.presentation.wallet.utils import androidx.annotation.DrawableRes +import com.tangem.common.Provider import com.tangem.core.ui.components.marketprice.PriceChangeConfig import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.feature.wallet.impl.R @@ -11,9 +13,8 @@ import com.tangem.utils.converter.Converter import java.math.BigDecimal internal class CryptoCurrencyStatusToTokenItemConverter( + private val appCurrencyProvider: Provider, private val isWalletContentHidden: Boolean, - private val fiatCurrencyCode: String, - private val fiatCurrencySymbol: String, ) : Converter { private val CryptoCurrencyStatus.networkIconResId: Int? @@ -71,8 +72,9 @@ internal class CryptoCurrencyStatusToTokenItemConverter( private fun CryptoCurrencyStatus.getFormattedFiatAmount(): String { val fiatAmount = value.fiatAmount ?: return UNKNOWN_AMOUNT_SIGN + val appCurrency = appCurrencyProvider() - return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol) + return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol) } private fun CryptoCurrencyStatus.mapToUnreachableTokenItemState() = TokenItemState.Unreachable( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt index c8b1d0b3b3..8d48f9b5ec 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/FiatBalanceToWalletCardConverter.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.common.Provider import com.tangem.core.ui.utils.BigDecimalFormatter.formatFiatAmount +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.wallet.domain.WalletAdditionalInfoFactory @@ -11,10 +12,9 @@ import com.tangem.utils.converter.Converter internal class FiatBalanceToWalletCardConverter( private val currentState: WalletCardState, private val cardTypeResolverProvider: Provider, + private val appCurrencyProvider: Provider, private val isLockedState: Boolean, private val isWalletContentHidden: Boolean, - private val fiatCurrencyCode: String, - private val fiatCurrencySymbol: String, ) : Converter { override fun convert(value: TokenList.FiatBalance): WalletCardState { @@ -33,13 +33,15 @@ internal class FiatBalanceToWalletCardConverter( if (isWalletContentHidden) { WalletCardState.HiddenContent(id, title, additionalInfo, imageResId, onClick) } else { + val appCurrency = appCurrencyProvider() + WalletCardState.Content( id = id, title = title, additionalInfo = additionalInfo, imageResId = imageResId, onClick = onClick, - balance = formatFiatAmount(value.amount, fiatCurrencyCode, fiatCurrencySymbol), + balance = formatFiatAmount(value.amount, appCurrency.code, appCurrency.symbol), ) } } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index 7fc1815c63..8bef92141f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -1,6 +1,8 @@ package com.tangem.feature.wallet.presentation.wallet.utils +import com.tangem.common.Provider import com.tangem.core.ui.extensions.TextReference +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.model.NetworkGroup import com.tangem.domain.tokens.model.TokenList @@ -14,16 +16,14 @@ import kotlinx.collections.immutable.mutate import kotlinx.collections.immutable.persistentListOf internal class TokenListToContentItemsConverter( + appCurrencyProvider: Provider, isWalletContentHidden: Boolean, - fiatCurrencyCode: String, - fiatCurrencySymbol: String, private val clickIntents: WalletClickIntents, ) : Converter { private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter( isWalletContentHidden = isWalletContentHidden, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, + appCurrencyProvider = appCurrencyProvider, ) override fun convert(value: TokenList): WalletTokensListState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt index e4e37d8e7d..fb21502fe6 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToWalletStateConverter.kt @@ -1,6 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.utils import com.tangem.common.Provider +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.common.state.TokenItemState @@ -18,16 +19,14 @@ internal class TokenListToWalletStateConverter( private val currentStateProvider: Provider, private val cardTypeResolverProvider: Provider, private val isLockedWalletProvider: Provider, + private val appCurrencyProvider: Provider, private val isWalletContentHidden: Boolean, - private val fiatCurrencyCode: String, - private val fiatCurrencySymbol: String, clickIntents: WalletClickIntents, ) : Converter { private val tokenListToContentConverter = TokenListToContentItemsConverter( isWalletContentHidden = isWalletContentHidden, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, + appCurrencyProvider = appCurrencyProvider, clickIntents = clickIntents, ) @@ -51,9 +50,8 @@ internal class TokenListToWalletStateConverter( currentState = selectedWalletCard, isLockedState = isLockedWalletProvider(), cardTypeResolverProvider = cardTypeResolverProvider, + appCurrencyProvider = appCurrencyProvider, isWalletContentHidden = isWalletContentHidden, - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, ) return walletsListConfig.copy( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index f7d4a9fb1b..3745757446 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -2,6 +2,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels import androidx.lifecycle.* import androidx.paging.cachedIn +import arrow.core.getOrElse import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.Provider @@ -9,6 +10,8 @@ import com.tangem.common.doOnFailure import com.tangem.common.doOnSuccess import com.tangem.core.ui.components.marketprice.MarketPriceBlockState import com.tangem.core.ui.components.transactions.state.TxHistoryState +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.* import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.extensions.derivationPath @@ -37,11 +40,10 @@ import com.tangem.feature.wallet.presentation.wallet.state.components.WalletCard import com.tangem.feature.wallet.presentation.wallet.state.components.WalletTokensListState import com.tangem.feature.wallet.presentation.wallet.state.factory.WalletStateFactory import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import com.tangem.utils.coroutines.JobHolder +import com.tangem.utils.coroutines.saveIn import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import javax.inject.Inject import kotlin.properties.Delegates @@ -71,12 +73,15 @@ internal class WalletViewModel @Inject constructor( private val txHistoryItemsUseCase: GetTxHistoryItemsUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase, private val unlockWalletsUseCase: UnlockWalletsUseCase, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val dispatchers: CoroutineDispatcherProvider, ) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents { /** Feature router */ var router: InnerWalletRouter by Delegates.notNull() + private val selectedAppCurrencyFlow: StateFlow = createSelectedAppCurrencyFlow() + private val notificationsListFactory = WalletNotificationsListFactory( currentStateProvider = Provider { uiState }, wasCardScannedCallback = getCardWasScannedUseCase::invoke, @@ -95,6 +100,7 @@ internal class WalletViewModel @Inject constructor( isLockedWalletProvider = Provider { wallets[requireNotNull(uiState as? WalletState.ContentState).walletsListConfig.selectedWalletIndex].isLocked }, + appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), clickIntents = this, ) @@ -432,4 +438,16 @@ internal class WalletViewModel @Inject constructor( override fun onBottomSheetDismiss() { uiState = stateFactory.getStateWithClosedBottomSheet() } + + private fun createSelectedAppCurrencyFlow(): StateFlow { + return getSelectedAppCurrencyUseCase() + .map { maybeAppCurrency -> + maybeAppCurrency.getOrElse { AppCurrency.Default } + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) + } } \ No newline at end of file From 2a0e8f78a6612c2e1b9ac25529af83efc16ac25e Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 15:59:41 +0500 Subject: [PATCH 44/52] Updated on 2026-08-14 --- app/build.gradle.kts | 1 + .../converters/CryptoCurrencyConverter.kt | 43 +++++++++++++++ .../middlewares/MultiWalletMiddleware.kt | 12 ++++- .../tokens/utils/CardCurrenciesFactory.kt | 53 ++----------------- .../tokens/utils/CryptoCurrencyFactory.kt | 53 +++++++++++++++++++ .../tokens/utils/ResponseCurrenciesFactory.kt | 2 + .../data/tokens/utils/TokensOperations.kt | 11 ++++ .../domain/tokens/models/CryptoCurrency.kt | 26 ++++++++- .../tangem/domain/tokens/mock/MockTokens.kt | 14 +++++ .../navigation/TokenDetailsRouter.kt | 4 ++ features/tokendetails/impl/build.gradle.kts | 1 + .../viewmodels/TokenDetailsViewModel.kt | 27 +++++++++- features/wallet/impl/build.gradle.kts | 1 + .../presentation/common/WalletPreviewData.kt | 2 + .../common/component/TokenItem.kt | 7 ++- .../common/state/TokenItemState.kt | 1 + .../router/DefaultWalletRouter.kt | 15 +++++- .../presentation/router/InnerWalletRouter.kt | 4 ++ ...ryptoCurrencyStatusToTokenItemConverter.kt | 3 ++ .../utils/TokenListToContentItemsConverter.kt | 1 + .../wallet/viewmodels/WalletClickIntents.kt | 4 ++ .../wallet/viewmodels/WalletViewModel.kt | 5 ++ 22 files changed, 236 insertions(+), 54 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt create mode 100644 data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 38e1d9c644..5e5e70656a 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -31,6 +31,7 @@ dependencies { implementation(project(":domain:wallets:models")) implementation(projects.domain.settings) implementation(projects.domain.tokens) + implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory) implementation(projects.domain.appCurrency) implementation(projects.domain.appCurrency.models) diff --git a/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt b/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt new file mode 100644 index 0000000000..1cf1515b01 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/wallet/converters/CryptoCurrencyConverter.kt @@ -0,0 +1,43 @@ +package com.tangem.tap.features.wallet.converters + +import com.tangem.data.tokens.utils.CryptoCurrencyFactory +import com.tangem.domain.common.util.derivationStyleProvider +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.tap.features.wallet.models.Currency +import com.tangem.tap.store +import com.tangem.utils.converter.Converter + +class CryptoCurrencyConverter : Converter { + + private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() } + + override fun convert(value: Currency): CryptoCurrency { + return when (value) { + is Currency.Blockchain -> requireNotNull( + cryptoCurrencyFactory.createCoin( + blockchain = value.blockchain, + derivationStyleProvider = requireNotNull( + store.state.globalState + .userWalletsListManager + ?.selectedUserWalletSync + ?.scanResponse + ?.derivationStyleProvider, + ), + ), + ) + is Currency.Token -> requireNotNull( + cryptoCurrencyFactory.createToken( + sdkToken = value.token, + blockchain = value.blockchain, + derivationStyleProvider = requireNotNull( + store.state.globalState + .userWalletsListManager + ?.selectedUserWalletSync + ?.scanResponse + ?.derivationStyleProvider, + ), + ), + ) + } + } +} \ No newline at end of file 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 3f080d645a..91fa88858c 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 @@ -1,5 +1,6 @@ package com.tangem.tap.features.wallet.redux.middlewares +import androidx.core.os.bundleOf import com.tangem.common.doOnSuccess import com.tangem.common.extensions.guard import com.tangem.common.flatMap @@ -7,6 +8,7 @@ import com.tangem.core.analytics.Analytics import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.wallets.models.UserWallet +import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import com.tangem.tap.common.analytics.events.AnalyticsParam import com.tangem.tap.common.analytics.events.Token.ButtonRemoveToken import com.tangem.tap.common.extensions.addContext @@ -15,6 +17,7 @@ import com.tangem.tap.common.extensions.dispatchErrorNotification import com.tangem.tap.common.extensions.dispatchWithMain import com.tangem.tap.common.redux.global.GlobalAction import com.tangem.tap.domain.TapError +import com.tangem.tap.features.wallet.converters.CryptoCurrencyConverter import com.tangem.tap.features.wallet.redux.WalletAction import com.tangem.tap.features.wallet.redux.WalletState import com.tangem.tap.features.wallet.redux.models.WalletDialog @@ -28,12 +31,19 @@ import kotlinx.coroutines.launch import timber.log.Timber class MultiWalletMiddleware { + + private val cryptoCurrencyConverter by lazy { CryptoCurrencyConverter() } + @Suppress("LongMethod", "ComplexMethod") fun handle(action: WalletAction.MultiWallet, walletState: WalletState?) { when (action) { is WalletAction.MultiWallet.SelectWallet -> { if (action.currency != null) { - store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletDetails)) + val bundle = bundleOf( + // TODO: [REDACTED_JIRA] + TokenDetailsRouter.SELECTED_CURRENCY_KEY to cryptoCurrencyConverter.convert(action.currency), + ) + store.dispatch(NavigationAction.NavigateTo(screen = AppScreen.WalletDetails, bundle = bundle)) } } is WalletAction.MultiWallet.TryToRemoveWallet -> { diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt index a98f9b0d8e..269c65e9f7 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CardCurrenciesFactory.kt @@ -9,11 +9,11 @@ import com.tangem.domain.demo.DemoConfig import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.tokens.models.CryptoCurrency -import timber.log.Timber -import com.tangem.blockchain.common.Token as SdkToken internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { + private val cryptoCurrencyFactory by lazy { CryptoCurrencyFactory() } + fun createDefaultCoinsForMultiCurrencyCard( card: CardDTO, derivationStyleProvider: DerivationStyleProvider, @@ -28,7 +28,7 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { blockchains = blockchains.mapNotNull { it.getTestnetVersion() } } - return blockchains.mapNotNull { createCoin(it, derivationStyleProvider) } + return blockchains.mapNotNull { cryptoCurrencyFactory.createCoin(it, derivationStyleProvider) } } fun createPrimaryCurrencyForSingleCurrencyCard(scanResponse: ScanResponse): CryptoCurrency { @@ -36,56 +36,13 @@ internal class CardCurrenciesFactory(private val demoConfig: DemoConfig) { val resolver = scanResponse.cardTypesResolver val blockchain = resolver.getBlockchain() - val coin = requireNotNull(createCoin(blockchain, derivationStyleProvider)) { + val coin = requireNotNull(cryptoCurrencyFactory.createCoin(blockchain, derivationStyleProvider)) { "Coin for the single currency card cannot be null" } val primaryToken = resolver.getPrimaryToken()?.let { token -> - createToken(token, blockchain, derivationStyleProvider) + cryptoCurrencyFactory.createToken(token, blockchain, derivationStyleProvider) } return primaryToken ?: coin } - - private fun createToken( - sdkToken: SdkToken, - blockchain: Blockchain, - derivationStyleProvider: DerivationStyleProvider, - ): CryptoCurrency.Token? { - if (blockchain != Blockchain.Unknown) { - Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") - return null - } - - return CryptoCurrency.Token( - id = getTokenId(blockchain, sdkToken), - networkId = getNetworkId(blockchain), - name = sdkToken.name, - symbol = sdkToken.symbol, - iconUrl = getTokenIconUrl(blockchain, sdkToken), - decimals = sdkToken.decimals, - isCustom = false, - contractAddress = sdkToken.contractAddress, - derivationPath = getDerivationPath(blockchain, derivationStyleProvider), - ) - } - - private fun createCoin( - blockchain: Blockchain, - derivationStyleProvider: DerivationStyleProvider, - ): CryptoCurrency.Coin? { - if (blockchain == Blockchain.Unknown) { - Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") - return null - } - - return CryptoCurrency.Coin( - id = getCoinId(blockchain), - networkId = getNetworkId(blockchain), - name = blockchain.fullName, - symbol = blockchain.currency, - iconUrl = getCoinIconUrl(blockchain), - decimals = blockchain.decimals(), - derivationPath = getDerivationPath(blockchain, derivationStyleProvider), - ) - } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt new file mode 100644 index 0000000000..f4e0148b34 --- /dev/null +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/CryptoCurrencyFactory.kt @@ -0,0 +1,53 @@ +package com.tangem.data.tokens.utils + +import com.tangem.blockchain.common.Blockchain +import com.tangem.blockchain.common.Token as SdkToken +import com.tangem.domain.common.DerivationStyleProvider +import com.tangem.domain.tokens.models.CryptoCurrency +import timber.log.Timber + +class CryptoCurrencyFactory { + + fun createToken( + sdkToken: SdkToken, + blockchain: Blockchain, + derivationStyleProvider: DerivationStyleProvider, + ): CryptoCurrency.Token? { + if (blockchain == Blockchain.Unknown) { + Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") + return null + } + + val id = getTokenId(blockchain, sdkToken) + return CryptoCurrency.Token( + id = id, + networkId = getNetworkId(blockchain), + name = sdkToken.name, + symbol = sdkToken.symbol, + iconUrl = getTokenIconUrl(blockchain, sdkToken), + decimals = sdkToken.decimals, + isCustom = isCustomToken(id), + contractAddress = sdkToken.contractAddress, + derivationPath = getDerivationPath(blockchain, derivationStyleProvider), + blockchainName = blockchain.fullName, + standardType = getTokenStandardType(blockchain, sdkToken), + ) + } + + fun createCoin(blockchain: Blockchain, derivationStyleProvider: DerivationStyleProvider): CryptoCurrency.Coin? { + if (blockchain == Blockchain.Unknown) { + Timber.e("Unable to map the SDK token to the domain token with Unknown blockchain") + return null + } + + return CryptoCurrency.Coin( + id = getCoinId(blockchain), + networkId = getNetworkId(blockchain), + name = blockchain.fullName, + symbol = blockchain.currency, + iconUrl = getCoinIconUrl(blockchain), + decimals = blockchain.decimals(), + derivationPath = getDerivationPath(blockchain, derivationStyleProvider), + ) + } +} \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt index 89d6bfec27..b79f32f515 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/ResponseCurrenciesFactory.kt @@ -84,6 +84,8 @@ internal class ResponseCurrenciesFactory(private val demoConfig: DemoConfig) { iconUrl = getTokenIconUrl(blockchain, sdkToken), contractAddress = sdkToken.contractAddress, isCustom = isCustomToken(id), + blockchainName = blockchain.fullName, + standardType = getTokenStandardType(blockchain, sdkToken), ) } } \ No newline at end of file diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index cbc158d2fc..b047d19e32 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -6,6 +6,7 @@ import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.CryptoCurrency.ID import com.tangem.domain.tokens.models.Network import com.tangem.blockchain.common.Token as SdkToken @@ -45,6 +46,16 @@ internal fun getTokenId(blockchain: Blockchain, token: SdkToken): ID { return getTokenOrCoinId(blockchain, token) } +internal fun getTokenStandardType(blockchain: Blockchain, token: SdkToken): CryptoCurrency.StandardType { + return when (blockchain) { + Blockchain.Ethereum, Blockchain.EthereumTestnet -> CryptoCurrency.StandardType.ERC20 + Blockchain.BSC, Blockchain.BSCTestnet -> CryptoCurrency.StandardType.BEP20 + Blockchain.Binance, Blockchain.BinanceTestnet -> CryptoCurrency.StandardType.BEP2 + Blockchain.Tron, Blockchain.TronTestnet -> CryptoCurrency.StandardType.TRC20 + else -> CryptoCurrency.StandardType.Unspecified(token.name) + } +} + internal fun getTokenIconUrl(blockchain: Blockchain, token: SdkToken): String? { val tokenId = token.id diff --git a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt index d31577a3a9..83d1d65eb0 100644 --- a/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt +++ b/domain/tokens/models/src/main/java/com/tangem/domain/tokens/models/CryptoCurrency.kt @@ -1,5 +1,7 @@ package com.tangem.domain.tokens.models +import java.io.Serializable + /** * Represents a generic cryptocurrency. * @@ -12,7 +14,7 @@ package com.tangem.domain.tokens.models * @property derivationPath Optional path used for key derivation. `null` if the wallet does not support the * [HD Wallet](https://coinsutra.com/hd-wallets-deterministic-wallet/) feature. */ -sealed class CryptoCurrency { +sealed class CryptoCurrency : Serializable { abstract val id: ID abstract val networkId: Network.ID @@ -56,6 +58,8 @@ sealed class CryptoCurrency { override val derivationPath: String?, val contractAddress: String, val isCustom: Boolean, + val blockchainName: String, // TODO: Move this field to proper entity + val standardType: StandardType, // TODO: Move this field to proper entity ) : CryptoCurrency() { init { @@ -130,6 +134,26 @@ sealed class CryptoCurrency { } } + sealed class StandardType { + abstract val name: String + + object ERC20 : StandardType() { + override val name: String = "ERC20" + } + object TRC20 : StandardType() { + override val name: String = "TRC20" + } + object BEP20 : StandardType() { + override val name: String = "BEP20" + } + object BEP2 : StandardType() { + override val name: String = "BEP2" + } + class Unspecified(val tokenName: String) : StandardType() { + override val name: String = tokenName + } + } + protected fun checkProperties() { require(name.isNotBlank()) { "Crypto currency name must not be blank" } require(symbol.isNotBlank()) { "Crypto currency symbol must not be blank" } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt index 7e22b04347..6e3749a46f 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/mock/MockTokens.kt @@ -26,6 +26,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val token3 get() = CryptoCurrency.Token( @@ -38,6 +40,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val token4 get() = CryptoCurrency.Coin( @@ -60,6 +64,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val token6 get() = CryptoCurrency.Token( @@ -72,6 +78,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val token7 get() = CryptoCurrency.Coin( @@ -94,6 +102,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val token9 get() = CryptoCurrency.Token( @@ -106,6 +116,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val token10 get() = CryptoCurrency.Token( @@ -118,6 +130,8 @@ internal object MockTokens { iconUrl = null, contractAddress = "address", derivationPath = null, + blockchainName = "Ethereum", + standardType = CryptoCurrency.StandardType.ERC20, ) val tokens = listOf(token1, token2, token3, token4, token5, token6, token7, token8, token9, token10) diff --git a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt index a361d75438..81dbc9eb69 100644 --- a/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt +++ b/features/tokendetails/api/src/main/kotlin/com/tangem/features/tokendetails/navigation/TokenDetailsRouter.kt @@ -5,4 +5,8 @@ import androidx.fragment.app.Fragment interface TokenDetailsRouter { fun getEntryFragment(): Fragment + + companion object { + const val SELECTED_CURRENCY_KEY = "selected_currency" + } } \ No newline at end of file diff --git a/features/tokendetails/impl/build.gradle.kts b/features/tokendetails/impl/build.gradle.kts index e37da473bc..a7c6440862 100644 --- a/features/tokendetails/impl/build.gradle.kts +++ b/features/tokendetails/impl/build.gradle.kts @@ -38,6 +38,7 @@ dependencies { implementation(projects.core.ui) implementation(projects.core.navigation) + implementation(projects.domain.tokens.models) implementation(projects.domain.txhistory) implementation(projects.domain.txhistory.models) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt index 4a75d46fb5..5dfeaf2b82 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/viewmodels/TokenDetailsViewModel.kt @@ -3,10 +3,15 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.viewmodels import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter import com.tangem.feature.tokendetails.presentation.tokendetails.TokenDetailsPreviewData +import com.tangem.feature.tokendetails.presentation.tokendetails.state.TokenInfoBlockState +import com.tangem.features.tokendetails.impl.R +import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -16,9 +21,14 @@ import kotlin.properties.Delegates private const val LOADING_DELAY = 4_000L @HiltViewModel -internal class TokenDetailsViewModel @Inject constructor() : ViewModel() { +internal class TokenDetailsViewModel @Inject constructor( + savedStateHandle: SavedStateHandle, +) : ViewModel() { - var router: InnerTokenDetailsRouter by Delegates.notNull() + private val cryptoCurrency: CryptoCurrency = savedStateHandle[TokenDetailsRouter.SELECTED_CURRENCY_KEY] + ?: error("no expected parameter CryptoCurrency found") + + var router by Delegates.notNull() var uiState by mutableStateOf(getInitialState()) private set @@ -38,6 +48,19 @@ internal class TokenDetailsViewModel @Inject constructor() : ViewModel() { topAppBarConfig = TokenDetailsPreviewData.tokenDetailsTopAppBarConfig.copy( onBackClick = ::onBackClick, ), + tokenInfoBlockState = TokenInfoBlockState( + name = cryptoCurrency.name, + iconUrl = requireNotNull(cryptoCurrency.iconUrl), + currency = when (cryptoCurrency) { + is CryptoCurrency.Coin -> TokenInfoBlockState.Currency.Native + is CryptoCurrency.Token -> TokenInfoBlockState.Currency.Token( + networkName = cryptoCurrency.standardType.name, + blockchainName = cryptoCurrency.blockchainName, + // TODO: [REDACTED_JIRA] + networkIcon = R.drawable.img_eth_22, + ) + }, + ), ) private fun onBackClick() { diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index ed9a84d4f4..cc22ff78e4 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -61,4 +61,5 @@ dependencies { /** Feature Apis */ implementation(projects.features.wallet.api) + implementation(projects.features.tokendetails.api) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt index 03fbc388d2..d0bb5cc8e7 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/WalletPreviewData.kt @@ -100,6 +100,7 @@ internal object WalletPreviewData { type = PriceChangeConfig.Type.UP, ), ), + onClick = {}, ) } @@ -118,6 +119,7 @@ internal object WalletPreviewData { type = PriceChangeConfig.Type.UP, ), ), + onClick = {}, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt index 8effab7dad..0e9751d451 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/component/TokenItem.kt @@ -61,6 +61,7 @@ internal fun TokenItem(state: TokenItemState, modifier: Modifier = Modifier) { private fun ContentTokenItem(content: TokenItemState.Content, modifier: Modifier = Modifier) { InternalTokenItem( modifier = modifier, + onClick = content.onClick, name = content.name, tokenIconUrl = content.tokenIconUrl, tokenIconResId = content.tokenIconResId, @@ -217,8 +218,12 @@ private fun InternalTokenItem( hasPending: Boolean, options: @Composable ConstraintLayoutScope.(ref: ConstrainedLayoutReference) -> Unit, modifier: Modifier = Modifier, + onClick: (() -> Unit)? = null, ) { - BaseSurface(modifier) { + BaseSurface( + modifier = modifier, + onClick = onClick, + ) { ConstraintLayout( modifier = Modifier .fillMaxWidth() diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt index b53c7458b8..bb2883ed5f 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/common/state/TokenItemState.kt @@ -35,6 +35,7 @@ internal sealed interface TokenItemState { val amount: String, val hasPending: Boolean, val tokenOptions: TokenOptionsState, + val onClick: () -> Unit, ) : TokenItemState /** diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index 397a39c2ae..aa8edc6a9c 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -4,6 +4,7 @@ import androidx.compose.foundation.layout.systemBarsPadding import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner +import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.hilt.navigation.compose.hiltViewModel @@ -16,12 +17,14 @@ import androidx.navigation.navArgument import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.core.navigation.NavigationStateHolder +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.feature.wallet.presentation.WalletFragment import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensScreen import com.tangem.feature.wallet.presentation.organizetokens.OrganizeTokensViewModel import com.tangem.feature.wallet.presentation.wallet.ui.WalletScreen import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletViewModel +import com.tangem.features.tokendetails.navigation.TokenDetailsRouter import kotlin.properties.Delegates /** Default implementation of wallet feature router */ @@ -42,7 +45,7 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation ) { composable(WalletRoute.Wallet.route) { val viewModel = hiltViewModel().apply { router = this@DefaultWalletRouter } - LocalLifecycleOwner.current.lifecycle.addObserver(observer = viewModel) + LocalLifecycleOwner.current.lifecycle.addObserver(viewModel) WalletScreen(state = viewModel.uiState) } @@ -96,6 +99,16 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation navigationStateHolder.navigate(action = NavigationAction.OpenUrl(url)) } + override fun openTokenDetails(currency: CryptoCurrency) { + navigationStateHolder.navigate( + action = NavigationAction.NavigateTo( + screen = AppScreen.WalletDetails, + // TODO: [REDACTED_JIRA] + bundle = bundleOf(TokenDetailsRouter.SELECTED_CURRENCY_KEY to currency), + ), + ) + } + private companion object { const val BACKSTACK_ENTRY_COUNT_TO_CLOSE_WALLET_SCREEN = 2 } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt index 5fab6367de..4a0d24bbaa 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/InnerWalletRouter.kt @@ -3,6 +3,7 @@ package com.tangem.feature.wallet.presentation.router import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable import androidx.fragment.app.FragmentManager +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.wallets.models.UserWalletId import com.tangem.features.wallet.navigation.WalletRouter @@ -40,4 +41,7 @@ internal interface InnerWalletRouter : WalletRouter { /** Open transaction history website by [url] */ fun openTxHistoryWebsite(url: String) + + /** Open token details screen */ + fun openTokenDetails(currency: CryptoCurrency) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt index 1790a50da4..4c9c49ed14 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/CryptoCurrencyStatusToTokenItemConverter.kt @@ -9,12 +9,14 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.feature.wallet.impl.R import com.tangem.feature.wallet.presentation.common.state.TokenItemState +import com.tangem.feature.wallet.presentation.wallet.viewmodels.WalletClickIntents import com.tangem.utils.converter.Converter import java.math.BigDecimal internal class CryptoCurrencyStatusToTokenItemConverter( private val appCurrencyProvider: Provider, private val isWalletContentHidden: Boolean, + private val clickIntents: WalletClickIntents, ) : Converter { private val CryptoCurrencyStatus.networkIconResId: Int? @@ -61,6 +63,7 @@ internal class CryptoCurrencyStatusToTokenItemConverter( priceChange = getPriceChangeConfig(), ) }, + onClick = { clickIntents.onTokenClick(currency) }, ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt index 8bef92141f..ca50986a37 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/utils/TokenListToContentItemsConverter.kt @@ -24,6 +24,7 @@ internal class TokenListToContentItemsConverter( private val tokenStatusConverter = CryptoCurrencyStatusToTokenItemConverter( isWalletContentHidden = isWalletContentHidden, appCurrencyProvider = appCurrencyProvider, + clickIntents = clickIntents, ) override fun convert(value: TokenList): WalletTokensListState { diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt index e38b9bd0f3..2dcc2ddc06 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletClickIntents.kt @@ -1,5 +1,7 @@ package com.tangem.feature.wallet.presentation.wallet.viewmodels +import com.tangem.domain.tokens.models.CryptoCurrency + internal interface WalletClickIntents { fun onBackClick() @@ -37,4 +39,6 @@ internal interface WalletClickIntents { fun onUnlockWalletNotificationClick() fun onBottomSheetDismiss() + + fun onTokenClick(currency: CryptoCurrency) } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 3745757446..866dfadf62 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -22,6 +22,7 @@ import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase import com.tangem.domain.tokens.GetPrimaryCurrencyUseCase import com.tangem.domain.tokens.GetTokenListUseCase import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.domain.tokens.models.Network import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase @@ -439,6 +440,10 @@ internal class WalletViewModel @Inject constructor( uiState = stateFactory.getStateWithClosedBottomSheet() } + override fun onTokenClick(currency: CryptoCurrency) { + router.openTokenDetails(currency = currency) + } + private fun createSelectedAppCurrencyFlow(): StateFlow { return getSelectedAppCurrencyUseCase() .map { maybeAppCurrency -> From 7aa44c4da7039a20ef2c207eed6c94440a6e9223 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 14:08:30 +0300 Subject: [PATCH 45/52] Updated on 2026-08-14 --- .../datasource/di/AppCurrencyDataModule.kt | 31 +++++- .../AvailableAppCurrenciesStore.kt | 12 ++ .../DefaultAvailableAppCurrenciesStore.kt | 22 ++++ .../DefaultSelectedAppCurrencyStore.kt | 10 ++ .../local/datastore/FileDataStore.kt | 33 ++++-- .../local/datastore/RuntimeDataStore.kt | 8 ++ .../datastore/SharedPreferencesDataStore.kt | 49 +++++--- .../local/datastore/core/DataStore.kt | 4 + .../core/KeylessDataStoreDecorator.kt | 28 +++++ .../core/StringKeyDataStoreDecorator.kt | 22 ++-- .../local/datastore/model/WriteTrigger.kt | 3 - .../local/datastore/utils/Trigger.kt | 35 ++++++ data/app-currency/build.gradle.kts | 1 + .../DefaultAppCurrencyRepository.kt | 105 ++++++++++++++++++ .../appcurrency/MockAppCurrencyRepository.kt | 23 ---- .../appcurrency/di/AppCurrencyDataModule.kt | 23 +++- .../appcurrency/utils/AppCurrencyConverter.kt | 16 +++ 17 files changed, 364 insertions(+), 61 deletions(-) create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/AvailableAppCurrenciesStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultAvailableAppCurrenciesStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/KeylessDataStoreDecorator.kt delete mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/model/WriteTrigger.kt create mode 100644 core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt create mode 100644 data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt delete mode 100644 data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt create mode 100644 data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/utils/AppCurrencyConverter.kt diff --git a/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt b/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt index 724634e0a3..b6d8832f86 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/di/AppCurrencyDataModule.kt @@ -1,10 +1,18 @@ package com.tangem.datasource.di -import com.tangem.datasource.local.appcurrency.MockSelectedAppCurrencyStore +import android.content.Context +import com.squareup.moshi.Moshi +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import com.tangem.datasource.local.appcurrency.implementation.DefaultAvailableAppCurrenciesStore +import com.tangem.datasource.local.appcurrency.implementation.DefaultSelectedAppCurrencyStore +import com.tangem.datasource.local.datastore.RuntimeDataStore +import com.tangem.datasource.local.datastore.SharedPreferencesDataStore import dagger.Module import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import javax.inject.Singleton @@ -14,7 +22,24 @@ internal object AppCurrencyDataModule { @Provides @Singleton - fun provideSelectedAppCurrencyStore(): SelectedAppCurrencyStore { - return MockSelectedAppCurrencyStore() + fun provideAvailableAppCurrenciesStore(): AvailableAppCurrenciesStore { + return DefaultAvailableAppCurrenciesStore( + dataStore = RuntimeDataStore(), + ) + } + + @Provides + @Singleton + fun provideSelectedAppCurrencyStore( + @ApplicationContext context: Context, + @NetworkMoshi moshi: Moshi, + ): SelectedAppCurrencyStore { + return DefaultSelectedAppCurrencyStore( + dataStore = SharedPreferencesDataStore( + preferencesName = "selected_app_currency", + context = context, + adapter = moshi.adapter(CurrenciesResponse.Currency::class.java), + ), + ) } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/AvailableAppCurrenciesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/AvailableAppCurrenciesStore.kt new file mode 100644 index 0000000000..be9b7281cf --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/AvailableAppCurrenciesStore.kt @@ -0,0 +1,12 @@ +package com.tangem.datasource.local.appcurrency + +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse + +interface AvailableAppCurrenciesStore { + + suspend fun getAllSyncOrNull(): List? + + suspend fun getSyncOrNull(key: String): CurrenciesResponse.Currency? + + suspend fun store(response: CurrenciesResponse) +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultAvailableAppCurrenciesStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultAvailableAppCurrenciesStore.kt new file mode 100644 index 0000000000..ed9d257ec8 --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultAvailableAppCurrenciesStore.kt @@ -0,0 +1,22 @@ +package com.tangem.datasource.local.appcurrency.implementation + +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore +import com.tangem.datasource.local.datastore.core.StringKeyDataStore +import com.tangem.datasource.local.datastore.core.StringKeyDataStoreDecorator + +internal class DefaultAvailableAppCurrenciesStore( + private val dataStore: StringKeyDataStore, +) : AvailableAppCurrenciesStore, + StringKeyDataStoreDecorator(dataStore) { + + override fun provideStringKey(key: String): String { + return key + } + + override suspend fun store(response: CurrenciesResponse) { + val currencies = response.currencies.associateBy(CurrenciesResponse.Currency::code) + + dataStore.store(currencies) + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt new file mode 100644 index 0000000000..c79bf3045b --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/appcurrency/implementation/DefaultSelectedAppCurrencyStore.kt @@ -0,0 +1,10 @@ +package com.tangem.datasource.local.appcurrency.implementation + +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import com.tangem.datasource.local.datastore.core.KeylessDataStoreDecorator +import com.tangem.datasource.local.datastore.core.StringKeyDataStore + +internal class DefaultSelectedAppCurrencyStore( + dataStore: StringKeyDataStore, +) : SelectedAppCurrencyStore, KeylessDataStoreDecorator(dataStore) \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt index c69c6d5c64..cc65a7ff21 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/FileDataStore.kt @@ -3,38 +3,49 @@ package com.tangem.datasource.local.datastore import com.squareup.moshi.JsonAdapter import com.tangem.datasource.files.FileReader import com.tangem.datasource.local.datastore.core.StringKeyDataStore -import com.tangem.datasource.local.datastore.model.WriteTrigger -import kotlinx.coroutines.channels.BufferOverflow +import com.tangem.datasource.local.datastore.utils.Trigger import kotlinx.coroutines.flow.* import timber.log.Timber +@Deprecated("Use shared preferences data store instead") internal class FileDataStore( private val fileReader: FileReader, private val adapter: JsonAdapter, ) : StringKeyDataStore { - private val writeTrigger = MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) + private val writeTrigger = Trigger() override fun get(key: String): Flow { return writeTrigger - .onEmpty { emit(WriteTrigger) } .map { getInternal(key) } .filterNotNull() + .distinctUntilChanged() + } + + override fun getAll(): Flow> { + val e = NotImplementedError("`getAll()` function not implemented for `FileDataStore`") + Timber.e(e) + + throw e } override suspend fun getSyncOrNull(key: String): Value? { return getInternal(key) } + override suspend fun getAllSyncOrNull(): List { + val e = NotImplementedError("`getAllSyncOrNull()` function not implemented for `FileDataStore`") + Timber.e(e) + + throw e + } + override suspend fun store(key: String, item: Value) { try { val json = adapter.toJson(item) fileReader.rewriteFile(json, key) - writeTrigger.tryEmit(WriteTrigger) + writeTrigger.trigger() } catch (e: Throwable) { Timber.e(e, "Unable to write file: $key") } @@ -48,10 +59,14 @@ internal class FileDataStore( override suspend fun remove(key: String) { fileReader.removeFile(key) + writeTrigger.trigger() } override suspend fun clear() { - // TODO: Implement if needed + val e = NotImplementedError("`clear()` function not implemented for `FileDataStore`") + Timber.e(e) + + throw e } private fun getInternal(fileName: String): Value? { diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt index d1c4eff1eb..2d4149648f 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/RuntimeDataStore.kt @@ -13,10 +13,18 @@ internal class RuntimeDataStore : StringKeyDataStore { .filterNotNull() } + override fun getAll(): Flow> { + return store.map { value -> value.values.toList() } + } + override suspend fun getSyncOrNull(key: String): Data? { return store.value[key] } + override suspend fun getAllSyncOrNull(): List { + return store.value.values.toList() + } + override suspend fun store(key: String, item: Data) { store.update { value -> value[key] = item diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt index e8952a5a82..cd2ed5c463 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/SharedPreferencesDataStore.kt @@ -2,12 +2,15 @@ package com.tangem.datasource.local.datastore import android.content.Context import android.content.Context.MODE_PRIVATE +import android.content.SharedPreferences import androidx.core.content.edit import com.squareup.moshi.JsonAdapter import com.tangem.datasource.local.datastore.core.StringKeyDataStore -import com.tangem.datasource.local.datastore.model.WriteTrigger -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.* +import com.tangem.datasource.local.datastore.utils.Trigger +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map import timber.log.Timber internal class SharedPreferencesDataStore( @@ -16,32 +19,39 @@ internal class SharedPreferencesDataStore( private val adapter: JsonAdapter, ) : StringKeyDataStore { - private val sharedPreferences by lazy { + private val sharedPreferences: SharedPreferences by lazy { context.getSharedPreferences(preferencesName, MODE_PRIVATE) } - private val writeTrigger = MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) + private val writeTrigger = Trigger() override fun get(key: String): Flow { return writeTrigger - .onEmpty { emit(WriteTrigger) } .map { getInternal(key) } .filterNotNull() + .distinctUntilChanged() + } + + override fun getAll(): Flow> { + return writeTrigger + .map { getAllInternal() } + .distinctUntilChanged() } override suspend fun getSyncOrNull(key: String): Value? { return getInternal(key) } + override suspend fun getAllSyncOrNull(): List { + return getAllInternal() + } + override suspend fun store(key: String, item: Value) { try { val json = adapter.toJson(item) sharedPreferences.edit { putString(key, json) } - writeTrigger.emit(WriteTrigger) + writeTrigger.trigger() } catch (e: Throwable) { Timber.e(e, "Unable to edit preferences: $key") } @@ -54,13 +64,13 @@ internal class SharedPreferencesDataStore( } override suspend fun remove(key: String) { - sharedPreferences.edit { - remove(key) - } + sharedPreferences.edit { remove(key) } + writeTrigger.trigger() } override suspend fun clear() { sharedPreferences.edit { clear() } + writeTrigger.trigger() } private fun getInternal(key: String): Value? { @@ -73,4 +83,17 @@ internal class SharedPreferencesDataStore( null } } + + private fun getAllInternal(): List { + return sharedPreferences.all.mapNotNull { (key, value) -> + try { + val json = value as? String ?: return@mapNotNull null + + adapter.fromJson(json) + } catch (e: Throwable) { + Timber.e(e, "Unable to convert value from JSON: $key") + null + } + } + } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt index 7374c56980..4ac5668e44 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/DataStore.kt @@ -6,8 +6,12 @@ internal interface DataStore { fun get(key: Key): Flow + fun getAll(): Flow> + suspend fun getSyncOrNull(key: Key): Value? + suspend fun getAllSyncOrNull(): List + suspend fun store(key: Key, item: Value) suspend fun store(items: Map) diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/KeylessDataStoreDecorator.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/KeylessDataStoreDecorator.kt new file mode 100644 index 0000000000..3d567967cf --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/KeylessDataStoreDecorator.kt @@ -0,0 +1,28 @@ +package com.tangem.datasource.local.datastore.core + +import kotlinx.coroutines.flow.Flow + +internal abstract class KeylessDataStoreDecorator( + wrappedDataStore: StringKeyDataStore, +) : StringKeyDataStoreDecorator(wrappedDataStore) { + + override fun provideStringKey(key: Unit): String { + return STRING_KEY + } + + fun get(): Flow { + return get(Unit) + } + + suspend fun getSyncOrNull(): Value? { + return getSyncOrNull(Unit) + } + + suspend fun store(item: Value) { + store(Unit, item) + } + + private companion object { + const val STRING_KEY = "key" + } +} \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt index 92daa1a731..e6afee1d93 100644 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/core/StringKeyDataStoreDecorator.kt @@ -3,34 +3,42 @@ package com.tangem.datasource.local.datastore.core import kotlinx.coroutines.flow.Flow internal abstract class StringKeyDataStoreDecorator( - private val dataStore: StringKeyDataStore, + private val wrappedDataStore: StringKeyDataStore, ) : DataStore { abstract fun provideStringKey(key: Key): String override fun get(key: Key): Flow { - return dataStore.get(provideStringKey(key)) + return wrappedDataStore.get(provideStringKey(key)) + } + + override fun getAll(): Flow> { + return wrappedDataStore.getAll() + } + + override suspend fun getAllSyncOrNull(): List { + return wrappedDataStore.getAllSyncOrNull() } override suspend fun getSyncOrNull(key: Key): Value? { - return dataStore.getSyncOrNull(provideStringKey(key)) + return wrappedDataStore.getSyncOrNull(provideStringKey(key)) } override suspend fun store(key: Key, item: Value) { - dataStore.store(provideStringKey(key), item) + wrappedDataStore.store(provideStringKey(key), item) } override suspend fun store(items: Map) { - dataStore.store( + wrappedDataStore.store( items = items.mapKeys { (key, _) -> provideStringKey(key) }, ) } override suspend fun remove(key: Key) { - dataStore.remove(provideStringKey(key)) + wrappedDataStore.remove(provideStringKey(key)) } override suspend fun clear() { - dataStore.clear() + wrappedDataStore.clear() } } \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/model/WriteTrigger.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/model/WriteTrigger.kt deleted file mode 100644 index aaab7f83df..0000000000 --- a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/model/WriteTrigger.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.tangem.datasource.local.datastore.model - -internal typealias WriteTrigger = Unit \ No newline at end of file diff --git a/core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt new file mode 100644 index 0000000000..d343129a6f --- /dev/null +++ b/core/datasource/src/main/java/com/tangem/datasource/local/datastore/utils/Trigger.kt @@ -0,0 +1,35 @@ +package com.tangem.datasource.local.datastore.utils + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.MutableStateFlow + +/** + * Represents a trigger mechanism to emit values on-demand. + * + * This class provides a mechanism to trigger emissions via the [trigger] method. + * + * @property triggerFlow The internal flow that gets toggled to trigger emissions. + */ +internal class Trigger( + private val triggerFlow: MutableStateFlow = MutableStateFlow(value = false), +) : Flow { + + /** + * Collects values emitted by this flow. + * + * Overrides the default collection mechanism to emit a [Unit] value whenever [triggerFlow] changes. + * + * @param collector The collector responsible for handling emitted values. + */ + override suspend fun collect(collector: FlowCollector): Nothing { + triggerFlow.collect { collector.emit(Unit) } + } + + /** + * Triggers an emission. + */ + fun trigger() { + triggerFlow.value = !triggerFlow.value + } +} \ No newline at end of file diff --git a/data/app-currency/build.gradle.kts b/data/app-currency/build.gradle.kts index 7020b7c452..69613e2e85 100644 --- a/data/app-currency/build.gradle.kts +++ b/data/app-currency/build.gradle.kts @@ -30,4 +30,5 @@ dependencies { /** Other */ implementation(deps.kotlin.coroutines) implementation(deps.timber) + implementation(deps.jodatime) } \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt new file mode 100644 index 0000000000..8a780ba151 --- /dev/null +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/DefaultAppCurrencyRepository.kt @@ -0,0 +1,105 @@ +package com.tangem.data.appcurrency + +import com.tangem.data.appcurrency.utils.AppCurrencyConverter +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore +import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEmpty +import kotlinx.coroutines.withContext +import org.joda.time.Duration +import timber.log.Timber + +internal class DefaultAppCurrencyRepository( + private val tangemTechApi: TangemTechApi, + private val availableAppCurrenciesStore: AvailableAppCurrenciesStore, + private val selectedAppCurrencyStore: SelectedAppCurrencyStore, + private val cacheRegistry: CacheRegistry, + private val dispatchers: CoroutineDispatcherProvider, +) : AppCurrencyRepository { + + private val appCurrencyConverter = AppCurrencyConverter() + + override fun getSelectedAppCurrency(): Flow { + return selectedAppCurrencyStore.get() + .onEmpty { fetchDefaultAppCurrency() } + .map(appCurrencyConverter::convert) + .flowOn(dispatchers.io) + } + + override suspend fun getAvailableAppCurrencies(): List { + return withContext(dispatchers.io) { + fetchAvailableCurrenciesIfExpired() + + val currencies = availableAppCurrenciesStore.getAllSyncOrNull() + ?.map(appCurrencyConverter::convert) + + requireNotNull(currencies) { + "No available currencies stored" + } + } + } + + override suspend fun changeAppCurrency(currencyCode: String) { + withContext(dispatchers.io) { + val currency = requireNotNull(availableAppCurrenciesStore.getSyncOrNull(currencyCode)) { + "Unable to find app currency with provided code: $currencyCode" + } + + selectedAppCurrencyStore.store(currency) + } + } + + private suspend fun fetchDefaultAppCurrency() { + fetchAvailableCurrenciesIfExpired() + + changeAppCurrency(DEFAULT_CURRENCY_CODE) + } + + private suspend fun fetchAvailableCurrenciesIfExpired() { + cacheRegistry.invokeOnExpire( + key = AVAILABLE_CURRENCIES_CACHE_KEY, + skipCache = false, + expireIn = Duration.standardMinutes(AVAILABLE_CURRENCIES_CACHE_KEY_EXPIRE_MINUTES), + block = { fetchAvailableCurrencies() }, + ) + } + + private suspend fun fetchAvailableCurrencies() { + try { + val response = tangemTechApi.getCurrencyList() + + availableAppCurrenciesStore.store(response) + } catch (e: Throwable) { + Timber.e(e, "Unable to fetch available currencies") + + availableAppCurrenciesStore.store(getDefaultCurrenciesResponse()) + } + } + + private fun getDefaultCurrenciesResponse(): CurrenciesResponse = CurrenciesResponse( + currencies = listOf( + CurrenciesResponse.Currency( + id = DEFAULT_CURRENCY_CODE.lowercase(), + code = DEFAULT_CURRENCY_CODE, + name = "US Dollar", + unit = "$", + type = "fiat", + rateBTC = "", + ), + ), + ) + + private companion object { + const val AVAILABLE_CURRENCIES_CACHE_KEY = "available_currencies" + const val AVAILABLE_CURRENCIES_CACHE_KEY_EXPIRE_MINUTES = 15L + const val DEFAULT_CURRENCY_CODE = "USD" + } +} \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt deleted file mode 100644 index e3e518afdc..0000000000 --- a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/MockAppCurrencyRepository.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.tangem.data.appcurrency - -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.appcurrency.repository.AppCurrencyRepository -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -internal class MockAppCurrencyRepository : AppCurrencyRepository { - - private val mockAppCurrencies = listOf(AppCurrency(code = "USD", name = "US Dollar", symbol = "$")) - - override fun getSelectedAppCurrency(): Flow { - return flowOf(mockAppCurrencies.first()) - } - - override suspend fun getAvailableAppCurrencies(): List { - return mockAppCurrencies - } - - override suspend fun changeAppCurrency(currencyCode: String) { - /* no-op */ - } -} \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt index d7f7ebb36c..cd80c7c52b 100644 --- a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/di/AppCurrencyDataModule.kt @@ -1,7 +1,12 @@ package com.tangem.data.appcurrency.di -import com.tangem.data.appcurrency.MockAppCurrencyRepository +import com.tangem.data.appcurrency.DefaultAppCurrencyRepository +import com.tangem.data.common.cache.CacheRegistry +import com.tangem.datasource.api.tangemTech.TangemTechApi +import com.tangem.datasource.local.appcurrency.AvailableAppCurrenciesStore +import com.tangem.datasource.local.appcurrency.SelectedAppCurrencyStore import com.tangem.domain.appcurrency.repository.AppCurrencyRepository +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides import dagger.hilt.InstallIn @@ -14,7 +19,19 @@ internal object AppCurrencyDataModule { @Provides @Singleton - fun provideAppCurrencyRepository(): AppCurrencyRepository { - return MockAppCurrencyRepository() + fun provideAppCurrencyRepository( + tangemTechApi: TangemTechApi, + availableAppCurrenciesStore: AvailableAppCurrenciesStore, + selectedAppCurrencyStore: SelectedAppCurrencyStore, + cacheRegistry: CacheRegistry, + dispatchers: CoroutineDispatcherProvider, + ): AppCurrencyRepository { + return DefaultAppCurrencyRepository( + tangemTechApi, + availableAppCurrenciesStore, + selectedAppCurrencyStore, + cacheRegistry, + dispatchers, + ) } } \ No newline at end of file diff --git a/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/utils/AppCurrencyConverter.kt b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/utils/AppCurrencyConverter.kt new file mode 100644 index 0000000000..6b8a6409e8 --- /dev/null +++ b/data/app-currency/src/main/kotlin/com/tangem/data/appcurrency/utils/AppCurrencyConverter.kt @@ -0,0 +1,16 @@ +package com.tangem.data.appcurrency.utils + +import com.tangem.datasource.api.tangemTech.models.CurrenciesResponse +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.utils.converter.Converter + +internal class AppCurrencyConverter : Converter { + + override fun convert(value: CurrenciesResponse.Currency): AppCurrency { + return AppCurrency( + code = value.code, + name = value.name, + symbol = value.unit, + ) + } +} \ No newline at end of file From 8cb232cd363624eb1026718229b539a6ee3b5362 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 17:25:50 +0300 Subject: [PATCH 46/52] Updated on 2026-08-14 --- .../tokens/ApplyTokenListSortingUseCase.kt | 13 +- .../ApplyTokenListSortingUseCaseTest.kt | 12 +- features/wallet/impl/build.gradle.kts | 1 + .../organizetokens/OrganizeTokensIntents.kt | 11 - .../organizetokens/OrganizeTokensScreen.kt | 3 + .../OrganizeTokensStateHolder.kt | 38 +--- .../organizetokens/OrganizeTokensViewModel.kt | 212 ++++++++++-------- .../utils/CryptoCurrenciesIdsResolver.kt | 32 +++ .../CryptoCurrencyToDraggableItemConverter.kt | 33 ++- .../router/DefaultWalletRouter.kt | 10 +- gradle/dependencies.toml | 1 + 11 files changed, 204 insertions(+), 162 deletions(-) create mode 100644 features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt diff --git a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt index 2094f6fdfb..e57b268e68 100644 --- a/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt +++ b/domain/tokens/src/main/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCase.kt @@ -9,7 +9,6 @@ import arrow.core.toNonEmptyListOrNull import arrow.core.toNonEmptySetOrNull import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.models.CryptoCurrency -import com.tangem.domain.tokens.models.Network import com.tangem.domain.tokens.repository.CurrenciesRepository import com.tangem.domain.wallets.models.UserWalletId import com.tangem.utils.coroutines.CoroutineDispatcherProvider @@ -23,7 +22,7 @@ class ApplyTokenListSortingUseCase( suspend operator fun invoke( userWalletId: UserWalletId, - sortedTokensIds: List>, + sortedTokensIds: List, isGroupedByNetwork: Boolean, isSortedByBalance: Boolean, ): Either { @@ -40,7 +39,7 @@ class ApplyTokenListSortingUseCase( } private suspend fun Raise.sortTokens( - sortedTokensIds: List>, + sortedTokensIds: List, unsortedTokens: List, ): List = withContext(dispatchers.default) { val nonEmptySortedTokensIds = ensureNotNull(sortedTokensIds.toNonEmptySetOrNull()) { @@ -49,13 +48,13 @@ class ApplyTokenListSortingUseCase( val sortedTokens = sortedMapOf() - unsortedTokens.distinct().forEach { token -> - val index = nonEmptySortedTokensIds.indexOfFirst { (networkId, tokenId) -> - networkId == token.networkId && tokenId == token.id + unsortedTokens.distinct().forEach { currency -> + val index = nonEmptySortedTokensIds.indexOfFirst { currencyId -> + currencyId == currency.id } if (index >= 0) { - sortedTokens[index] = token + sortedTokens[index] = currency } else { raise(TokenListSortingError.UnableToSortTokenList) } diff --git a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt index 77d37c2c2b..d5f180d4be 100644 --- a/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt +++ b/domain/tokens/src/test/kotlin/com/tangem/domain/tokens/ApplyTokenListSortingUseCaseTest.kt @@ -54,7 +54,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokensIds = MockTokens.tokens.map { it.networkId to it.id }, + sortedTokensIds = MockTokens.tokens.map { it.id }, isGroupedByNetwork = false, isSortedByBalance = false, ) @@ -76,7 +76,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }, + sortedTokensIds = expectedTokens.map { it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -100,7 +100,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }, + sortedTokensIds = expectedTokens.map { it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -124,7 +124,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }, + sortedTokensIds = expectedTokens.map { it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -148,7 +148,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When useCase( userWalletId = userWalletId, - sortedTokensIds = expectedTokens.map { it.networkId to it.id }, + sortedTokensIds = expectedTokens.map { it.id }, isGroupedByNetwork = expectedIsGrouped, isSortedByBalance = expectedIsSorted, ) @@ -170,7 +170,7 @@ internal class ApplyTokenListSortingUseCaseTest { // When val result = useCase( userWalletId = userWalletId, - sortedTokensIds = getSortedTokens().drop(n = 3).map { it.networkId to it.id }, + sortedTokensIds = getSortedTokens().drop(n = 3).map { it.id }, isGroupedByNetwork = false, isSortedByBalance = false, ) diff --git a/features/wallet/impl/build.gradle.kts b/features/wallet/impl/build.gradle.kts index cc22ff78e4..48a2eca7c6 100644 --- a/features/wallet/impl/build.gradle.kts +++ b/features/wallet/impl/build.gradle.kts @@ -9,6 +9,7 @@ plugins { dependencies { /** AndroidX */ implementation(deps.androidx.activity.compose) + implementation(deps.lifecycle.compose) implementation(deps.material) /** Compose */ diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt index 250b29d900..84ce246245 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensIntents.kt @@ -1,8 +1,5 @@ package com.tangem.feature.wallet.presentation.organizetokens -import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem -import org.burnoutcrew.reorderable.ItemPosition - internal interface OrganizeTokensIntents { fun onBackClick() @@ -14,12 +11,4 @@ internal interface OrganizeTokensIntents { fun onApplyClick() fun onCancelClick() - - fun onItemDragged(from: ItemPosition, to: ItemPosition) - - fun canDragItemOver(dragOver: ItemPosition, dragging: ItemPosition): Boolean - - fun onItemDraggingStart(item: DraggableItem) - - fun onItemDraggingEnd() } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt index ce0aabb8b1..53616f4e93 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensScreen.kt @@ -1,5 +1,6 @@ package com.tangem.feature.wallet.presentation.organizetokens +import androidx.activity.compose.BackHandler import androidx.compose.animation.core.* import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -38,6 +39,8 @@ import org.burnoutcrew.reorderable.* @Composable internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier = Modifier) { + BackHandler(onBack = state.onBackClick) + val tokensListState = rememberLazyListState() Scaffold( diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt index 33161ae553..14fecec78d 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensStateHolder.kt @@ -1,12 +1,12 @@ package com.tangem.feature.wallet.presentation.organizetokens import com.tangem.common.Provider +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.error.TokenListError import com.tangem.domain.tokens.error.TokenListSortingError import com.tangem.domain.tokens.model.TokenList import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState -import com.tangem.feature.wallet.presentation.organizetokens.utils.common.updateSorting import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.InProgressStateConverter import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.TokenListToStateConverter import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.error.TokenListErrorConverter @@ -17,22 +17,17 @@ import com.tangem.feature.wallet.presentation.organizetokens.utils.converter.ite import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.* -@Suppress("unused", "MemberVisibilityCanBePrivate") // TODO: Will be used in next MR internal class OrganizeTokensStateHolder( private val intents: OrganizeTokensIntents, - private val fiatCurrencyCode: String, - private val fiatCurrencySymbol: String, + private val appCurrencyProvider: Provider, private val onSubscription: () -> Unit, - scope: CoroutineScope, + stateFlowScope: CoroutineScope, ) { private val stateFlowInternal: MutableStateFlow = MutableStateFlow(getInitialState()) private val tokenListConverter by lazy { - val tokensConverter = CryptoCurrencyToDraggableItemConverter( - fiatCurrencyCode = fiatCurrencyCode, - fiatCurrencySymbol = fiatCurrencySymbol, - ) + val tokensConverter = CryptoCurrencyToDraggableItemConverter(appCurrencyProvider) val itemsConverter = TokenListToListStateConverter( tokensConverter = tokensConverter, groupsConverter = NetworkGroupToDraggableItemsConverter(tokensConverter), @@ -56,17 +51,13 @@ internal class OrganizeTokensStateHolder( val stateFlow: StateFlow = stateFlowInternal .onSubscription { onSubscription() } .stateIn( - scope = scope, + scope = stateFlowScope, started = SharingStarted.WhileSubscribed(), initialValue = getInitialState(), ) - var tokenList: TokenList? = null - private set - fun updateStateWithTokenList(tokenList: TokenList) { updateState { tokenListConverter.convert(tokenList) } - this.tokenList = tokenList } fun updateStateToDisplayProgress() { @@ -77,16 +68,6 @@ internal class OrganizeTokensStateHolder( updateState { inProgressStateConverter.convertBack(value = this) } } - fun updateStateWithManualSorting(itemsState: OrganizeTokensListState) { - updateState { - copy( - header = header.copy(isSortedByBalance = false), - itemsState = itemsState, - ) - } - tokenList = tokenList?.updateSorting(isSortedByBalance = false) - } - fun updateStateWithError(error: TokenListError) { updateState { tokenListErrorConverter.convert(error) } } @@ -107,11 +88,12 @@ internal class OrganizeTokensStateHolder( onApplyClick = intents::onApplyClick, onCancelClick = intents::onCancelClick, ), + // TODO: Will be added in next MR dndConfig = OrganizeTokensState.DragAndDropConfig( - onItemDragged = intents::onItemDragged, - onDragStart = intents::onItemDraggingStart, - onItemDragEnd = intents::onItemDraggingEnd, - canDragItemOver = intents::canDragItemOver, + onItemDragged = { _, _ -> }, + onDragStart = { }, + onItemDragEnd = { }, + canDragItemOver = { _, _ -> false }, ), ) } diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt index b3fa65555b..0313bf1e7a 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/OrganizeTokensViewModel.kt @@ -1,133 +1,147 @@ package com.tangem.feature.wallet.presentation.organizetokens -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.setValue import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import arrow.core.getOrElse +import com.tangem.common.Provider +import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase +import com.tangem.domain.appcurrency.model.AppCurrency +import com.tangem.domain.tokens.ApplyTokenListSortingUseCase +import com.tangem.domain.tokens.GetTokenListUseCase +import com.tangem.domain.tokens.ToggleTokenListGroupingUseCase +import com.tangem.domain.tokens.ToggleTokenListSortingUseCase +import com.tangem.domain.tokens.model.TokenList import com.tangem.domain.wallets.models.UserWalletId -import com.tangem.feature.wallet.presentation.common.WalletPreviewData -import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensState -import com.tangem.feature.wallet.presentation.organizetokens.utils.common.* +import com.tangem.feature.wallet.presentation.organizetokens.utils.CryptoCurrenciesIdsResolver import com.tangem.feature.wallet.presentation.router.InnerWalletRouter import com.tangem.feature.wallet.presentation.router.WalletRoute import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.collections.immutable.toPersistentList import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch -import org.burnoutcrew.reorderable.ItemPosition +import kotlinx.coroutines.withContext import javax.inject.Inject -import kotlin.properties.Delegates -// FIXME: Implemented with preview data @HiltViewModel -internal class OrganizeTokensViewModel @Inject constructor(savedStateHandle: SavedStateHandle) : ViewModel() { +internal class OrganizeTokensViewModel @Inject constructor( + private val getTokenListUseCase: GetTokenListUseCase, + private val toggleTokenListGroupingUseCase: ToggleTokenListGroupingUseCase, + private val toggleTokenListSortingUseCase: ToggleTokenListSortingUseCase, + private val applyTokenListSortingUseCase: ApplyTokenListSortingUseCase, + private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, + savedStateHandle: SavedStateHandle, +) : ViewModel(), OrganizeTokensIntents { - @Volatile - private var movingItem: DraggableItem? = null + lateinit var router: InnerWalletRouter - var router: InnerWalletRouter by Delegates.notNull() - val userWalletId: UserWalletId by lazy { + private val selectedAppCurrencyFlow = createSelectedAppCurrencyFlow() + + private val stateHolder = OrganizeTokensStateHolder( + stateFlowScope = viewModelScope, + intents = this, + appCurrencyProvider = Provider(selectedAppCurrencyFlow::value), + onSubscription = { + bootstrapTokenList() + }, + ) + + private val userWalletId: UserWalletId by lazy { val userWalletIdValue: String = checkNotNull(savedStateHandle[WalletRoute.userWalletIdKey]) UserWalletId(userWalletIdValue) } - var uiState: OrganizeTokensState by mutableStateOf(getInitialState()) - private set + private var tokenList: TokenList? = null - private fun getInitialState(): OrganizeTokensState = WalletPreviewData.organizeTokensState.copy( - itemsState = OrganizeTokensListState.Ungrouped( - items = WalletPreviewData.draggableTokens, - ), - dndConfig = OrganizeTokensState.DragAndDropConfig( - onItemDragged = this::moveItem, - canDragItemOver = this::checkCanMoveItemOver, - onItemDragEnd = this::endMoving, - onDragStart = this::startMoving, - ), - header = OrganizeTokensState.HeaderConfig( - onSortClick = { /* no-op */ }, - onGroupClick = this::toggleTokensByNetworkGrouping, - ), - ) + val uiState: StateFlow = stateHolder.stateFlow - private fun toggleTokensByNetworkGrouping() { - val newListState = when (val itemsState = uiState.itemsState) { - is OrganizeTokensListState.GroupedByNetwork -> OrganizeTokensListState.Ungrouped( - items = itemsState.items.filterIsInstance().toPersistentList(), + override fun onBackClick() { + router.popBackStack() + } + + override fun onSortClick() { + viewModelScope.launch(Dispatchers.Default) { + val list = tokenList ?: return@launch + + toggleTokenListSortingUseCase(list).fold( + ifLeft = stateHolder::updateStateWithError, + ifRight = { + stateHolder.updateStateWithTokenList(it) + tokenList = it + }, ) - is OrganizeTokensListState.Ungrouped -> OrganizeTokensListState.GroupedByNetwork( - items = WalletPreviewData.draggableItems, + } + } + + override fun onGroupClick() { + viewModelScope.launch(Dispatchers.Default) { + val list = tokenList ?: return@launch + + toggleTokenListGroupingUseCase(list).fold( + ifLeft = stateHolder::updateStateWithError, + ifRight = { + stateHolder.updateStateWithTokenList(it) + tokenList = it + }, ) - is OrganizeTokensListState.Empty -> itemsState - } - - uiState = uiState.copy(itemsState = newListState) - } - - private fun checkCanMoveItemOver(moveOverItemPosition: ItemPosition, movedItemPosition: ItemPosition): Boolean { - val items = (uiState.itemsState as? OrganizeTokensListState.GroupedByNetwork) - ?.items - ?: return true // If ungrouped then item can be moved anywhere - - val (moveOverItem, movedItem) = items.findItemsToMove(moveOverItemPosition.key, movedItemPosition.key) - - if (moveOverItem == null || movedItem == null) { - return false - } - - return when (movedItem) { - is DraggableItem.GroupHeader -> checkCanMoveHeaderOver(moveOverItemPosition, moveOverItem, items.lastIndex) - is DraggableItem.Token -> checkCanMoveTokenOver(movedItem, moveOverItem) - is DraggableItem.GroupPlaceholder -> false } } - private fun startMoving(movingItem: DraggableItem) = viewModelScope.launch(Dispatchers.Default) { - if (this@OrganizeTokensViewModel.movingItem != null) return@launch - this@OrganizeTokensViewModel.movingItem = movingItem + override fun onApplyClick() { + viewModelScope.launch(Dispatchers.Default) { + stateHolder.updateStateToDisplayProgress() - val updatedItemsState = uiState.itemsState.updateItems { items -> - when (movingItem) { - is DraggableItem.GroupHeader -> items.collapseGroup(movingItem) - is DraggableItem.Token -> when (uiState.itemsState) { - is OrganizeTokensListState.GroupedByNetwork -> items.divideGroups(movingItem) - is OrganizeTokensListState.Ungrouped -> items.divideItems(movingItem) - is OrganizeTokensListState.Empty -> uiState.itemsState.items - } - is DraggableItem.GroupPlaceholder -> items + val listState = uiState.value.itemsState + val resolver = CryptoCurrenciesIdsResolver() + + val result = applyTokenListSortingUseCase( + userWalletId = userWalletId, + sortedTokensIds = resolver.resolve(listState, tokenList), + isGroupedByNetwork = listState is OrganizeTokensListState.GroupedByNetwork, + isSortedByBalance = uiState.value.header.isSortedByBalance, + ) + + result.fold( + ifLeft = stateHolder::updateStateWithError, + ifRight = { + stateHolder.updateStateToHideProgress() + withContext(Dispatchers.Main) { router.popBackStack() } + }, + ) + } + } + + override fun onCancelClick() { + router.popBackStack() + } + + private fun bootstrapTokenList() { + viewModelScope.launch(Dispatchers.Default) { + val maybeTokenList = getTokenListUseCase(userWalletId) + .first { it.getOrNull()?.totalFiatBalance is TokenList.FiatBalance.Loaded } + + maybeTokenList.fold( + ifLeft = stateHolder::updateStateWithError, + ifRight = { + stateHolder.updateStateWithTokenList(it) + tokenList = it + }, + ) + } + } + + private fun createSelectedAppCurrencyFlow(): StateFlow { + return getSelectedAppCurrencyUseCase() + .map { maybeAppCurrency -> + maybeAppCurrency.getOrElse { AppCurrency.Default } } - } - - uiState = uiState.copy(itemsState = updatedItemsState) - } - - private fun endMoving() = viewModelScope.launch(Dispatchers.Default) { - if (movingItem == null) return@launch - - val updatedItemsState = uiState.itemsState.updateItems { items -> - when (movingItem) { - is DraggableItem.GroupHeader -> items.expandGroups() - is DraggableItem.Token -> items.uniteItems() - is DraggableItem.GroupPlaceholder, - null, - -> items - } - } - - uiState = uiState.copy(itemsState = updatedItemsState) - movingItem = null - } - - private fun moveItem(from: ItemPosition, to: ItemPosition) = viewModelScope.launch(Dispatchers.Default) { - uiState = uiState.copy( - itemsState = uiState.itemsState.updateItems { - it.moveItem(from.index, to.index) - }, - ) + .stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = AppCurrency.Default, + ) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt new file mode 100644 index 0000000000..71690752fb --- /dev/null +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/CryptoCurrenciesIdsResolver.kt @@ -0,0 +1,32 @@ +package com.tangem.feature.wallet.presentation.organizetokens.utils + +import com.tangem.domain.tokens.model.TokenList +import com.tangem.domain.tokens.models.CryptoCurrency +import com.tangem.feature.wallet.presentation.organizetokens.model.DraggableItem +import com.tangem.feature.wallet.presentation.organizetokens.model.OrganizeTokensListState + +internal class CryptoCurrenciesIdsResolver { + + fun resolve(listState: OrganizeTokensListState, tokenList: TokenList?): List { + val draggableTokens = when (listState) { + is OrganizeTokensListState.Empty -> return emptyList() + is OrganizeTokensListState.GroupedByNetwork -> listState.items.filterIsInstance() + is OrganizeTokensListState.Ungrouped -> listState.items + } + val currenciesStatuses = when (tokenList) { + is TokenList.GroupedByNetwork -> tokenList.groups.flatMap { it.currencies } + is TokenList.Ungrouped -> tokenList.currencies + is TokenList.NotInitialized, + null, + -> return emptyList() + } + + return draggableTokens.mapNotNull { draggableToken -> + val currencyStatus = currenciesStatuses.firstOrNull { + it.currency.id.value == draggableToken.id + } + + currencyStatus?.currency?.id + } + } +} \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt index d92843a1f6..5284855142 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/organizetokens/utils/converter/items/CryptoCurrencyToDraggableItemConverter.kt @@ -1,7 +1,9 @@ package com.tangem.feature.wallet.presentation.organizetokens.utils.converter.items import androidx.annotation.DrawableRes +import com.tangem.common.Provider import com.tangem.core.ui.utils.BigDecimalFormatter +import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.tokens.model.CryptoCurrencyStatus import com.tangem.domain.tokens.models.CryptoCurrency import com.tangem.feature.wallet.impl.R @@ -12,8 +14,7 @@ import com.tangem.feature.wallet.presentation.organizetokens.utils.common.getTok import com.tangem.utils.converter.Converter internal class CryptoCurrencyToDraggableItemConverter( - private val fiatCurrencyCode: String, - private val fiatCurrencySymbol: String, + private val appCurrencyProvider: Provider, ) : Converter { private val CryptoCurrency.networkIconResId: Int? @@ -29,13 +30,29 @@ internal class CryptoCurrencyToDraggableItemConverter( } override fun convert(value: CryptoCurrencyStatus): DraggableItem.Token { + return createDraggableToken(value, appCurrencyProvider()) + } + + override fun convertList(input: Collection): List { + val appCurrency = appCurrencyProvider() + + return input.map { createDraggableToken(it, appCurrency) } + } + + private fun createDraggableToken( + currencyStatus: CryptoCurrencyStatus, + appCurrency: AppCurrency, + ): DraggableItem.Token { return DraggableItem.Token( - tokenItemState = createToTokenItemState(value), - groupId = getGroupHeaderId(value.currency.networkId), + tokenItemState = createTokenItemState(currencyStatus, appCurrency), + groupId = getGroupHeaderId(currencyStatus.currency.networkId), ) } - private fun createToTokenItemState(currencyStatus: CryptoCurrencyStatus): TokenItemState.Draggable { + private fun createTokenItemState( + currencyStatus: CryptoCurrencyStatus, + appCurrency: AppCurrency, + ): TokenItemState.Draggable { val currency = currencyStatus.currency return TokenItemState.Draggable( @@ -44,13 +61,13 @@ internal class CryptoCurrencyToDraggableItemConverter( tokenIconResId = currency.tokenIconResId, networkIconResId = currency.networkIconResId, name = currency.name, - fiatAmount = getFormattedFiatAmount(currencyStatus), + fiatAmount = getFormattedFiatAmount(currencyStatus, appCurrency), ) } - private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus): String { + private fun getFormattedFiatAmount(currency: CryptoCurrencyStatus, appCurrency: AppCurrency): String { val fiatAmount = currency.value.fiatAmount ?: return BigDecimalFormatter.EMPTY_BALANCE_SIGN - return BigDecimalFormatter.formatFiatAmount(fiatAmount, fiatCurrencyCode, fiatCurrencySymbol) + return BigDecimalFormatter.formatFiatAmount(fiatAmount, appCurrency.code, appCurrency.symbol) } } \ No newline at end of file diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt index aa8edc6a9c..84ca2ae72b 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/router/DefaultWalletRouter.kt @@ -1,13 +1,15 @@ package com.tangem.feature.wallet.presentation.router -import androidx.compose.foundation.layout.systemBarsPadding +import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.core.os.bundleOf import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.NavHostController import androidx.navigation.NavType import androidx.navigation.compose.NavHost @@ -59,9 +61,11 @@ internal class DefaultWalletRouter(private val navigationStateHolder: Navigation router = this@DefaultWalletRouter } + val uiState by viewModel.uiState.collectAsStateWithLifecycle() + OrganizeTokensScreen( - modifier = Modifier.systemBarsPadding(), - state = viewModel.uiState, + modifier = Modifier.statusBarsPadding(), + state = uiState, ) } } diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 1058197b94..ffcb1326b2 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -132,6 +132,7 @@ androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.r 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" } +lifecycle-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" } # region AndroidX # region Compose From 89cb0e3cdb8b6c4e05e1c5a39e594b894ec23f2c Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 17:44:04 +0300 Subject: [PATCH 47/52] Updated on 2026-08-14 --- .../buttons/actions/ActionButtonConfig.kt | 3 ++ .../ui/components/buttons/actions/Actions.kt | 19 +++++++++-- .../organizetokens/OrganizeTokensScreen.kt | 32 +++++++++++++------ 3 files changed, 43 insertions(+), 11 deletions(-) diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt index ae19be321b..6b9347623a 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/ActionButtonConfig.kt @@ -10,6 +10,8 @@ import com.tangem.core.ui.extensions.TextReference * @property iconResId icon resource id * @property onClick lambda be invoked when action component is clicked * @property enabled enabled + * @property dimContent determines whether the button content will be dimmed. This property will be ignored if [enabled] + * is `false`. * [REDACTED_AUTHOR] */ @@ -18,4 +20,5 @@ data class ActionButtonConfig( @DrawableRes val iconResId: Int, val onClick: () -> Unit, val enabled: Boolean = true, + val dimContent: Boolean = false, ) \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt index af38dad59f..65422a2f7b 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/buttons/actions/Actions.kt @@ -99,14 +99,22 @@ private fun Button( painter = painterResource(id = config.iconResId), contentDescription = null, modifier = Modifier.size(size = TangemTheme.dimens.size20), - tint = if (config.enabled) TangemTheme.colors.icon.primary1 else TangemTheme.colors.icon.informative, + tint = when { + !config.enabled -> TangemTheme.colors.icon.informative + config.dimContent -> TangemTheme.colors.icon.secondary + else -> TangemTheme.colors.icon.primary1 + }, ) SpacerW8() Text( text = config.text.resolveReference(), - color = if (config.enabled) TangemTheme.colors.text.primary1 else TangemTheme.colors.text.disabled, + color = when { + !config.enabled -> TangemTheme.colors.text.disabled + config.dimContent -> TangemTheme.colors.text.secondary + else -> TangemTheme.colors.text.primary1 + }, overflow = TextOverflow.Ellipsis, maxLines = 1, style = TangemTheme.typography.button, @@ -154,6 +162,13 @@ private class ActionStateProvider : CollectionPreviewParameterProvider TokenList( - modifier = Modifier.padding(paddingValues), + modifier = Modifier + .padding(paddingValues) + .fillMaxSize(), listState = tokensListState, state = state.itemsState, - dragConfig = state.dndConfig, + dndConfig = state.dndConfig, ) }, floatingActionButtonPosition = FabPosition.Center, @@ -68,15 +70,15 @@ internal fun OrganizeTokensScreen(state: OrganizeTokensState, modifier: Modifier private fun TokenList( listState: LazyListState, state: OrganizeTokensListState, - dragConfig: OrganizeTokensState.DragAndDropConfig, + dndConfig: OrganizeTokensState.DragAndDropConfig, modifier: Modifier = Modifier, ) { Box(modifier = modifier) { val reorderableListState = rememberReorderableLazyListState( - onMove = dragConfig.onItemDragged, + onMove = dndConfig.onItemDragged, listState = listState, - canDragOver = dragConfig.canDragItemOver, - onDragEnd = { _, _ -> dragConfig.onItemDragEnd() }, + canDragOver = dndConfig.canDragItemOver, + onDragEnd = { _, _ -> dndConfig.onItemDragEnd() }, ) val items = state.items @@ -84,7 +86,8 @@ private fun TokenList( modifier = Modifier .reorderable(reorderableListState) .align(Alignment.TopCenter) - .padding(horizontal = TangemTheme.dimens.spacing16), + .padding(horizontal = TangemTheme.dimens.spacing16) + .fillMaxSize(), state = reorderableListState.listState, contentPadding = PaddingValues( top = TangemTheme.dimens.spacing12, @@ -97,7 +100,7 @@ private fun TokenList( ) { index, item -> val onDragStart = remember(item) { - { dragConfig.onDragStart(item) } + { dndConfig.onDragStart(item) } } DraggableItem( @@ -217,14 +220,23 @@ private fun TopBar( config = ActionButtonConfig( text = TextReference.Res(id = R.string.organize_tokens_sort_by_balance), iconResId = R.drawable.ic_sort_24, + enabled = config.isEnabled, onClick = config.onSortClick, + dimContent = !config.isSortedByBalance, ), modifier = Modifier.weight(1f), color = TangemTheme.colors.background.primary, ) RoundedActionButton( config = ActionButtonConfig( - text = TextReference.Res(id = R.string.organize_tokens_group), + text = TextReference.Res( + id = if (config.isGrouped) { + R.string.organize_tokens_ungroup + } else { + R.string.organize_tokens_group + }, + ), + enabled = config.isEnabled, iconResId = R.drawable.ic_group_24, onClick = config.onGroupClick, ), @@ -253,6 +265,8 @@ private fun Actions(config: OrganizeTokensState.ActionsConfig, modifier: Modifie modifier = Modifier.weight(1f), text = stringResource(id = R.string.common_apply), onClick = config.onApplyClick, + showProgress = config.showApplyProgress, + enabled = config.canApply, ) } } From 2e09b2e3ca51d49f44c7c5d23e42304c05384bec Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 16:25:49 +0300 Subject: [PATCH 48/52] Updated on 2026-08-14 --- app/src/main/assets/tangem-app-config | 2 +- .../network/exchangeServices/CurrencyExchangeManager.kt | 6 +++++- .../com/tangem/domain/common/TangemCardTypesResolver.kt | 3 ++- .../main/java/com/tangem/domain/common/TapWorkarounds.kt | 3 +++ .../java/com/tangem/domain/common/configs/CardConfig.kt | 4 ++-- .../{TangemWalletCardConfig.kt => GenericCardConfig.kt} | 2 +- .../java/com/tangem/domain/common/extensions/CardSdk.kt | 9 ++++++++- .../tangem/domain/common/util/ScanResponseExtensions.kt | 2 +- gradle/dependencies.toml | 4 ++-- 9 files changed, 25 insertions(+), 10 deletions(-) rename domain/legacy/src/main/java/com/tangem/domain/common/configs/{TangemWalletCardConfig.kt => GenericCardConfig.kt} (95%) diff --git a/app/src/main/assets/tangem-app-config b/app/src/main/assets/tangem-app-config index b791bd4cf6..8c1c53b739 160000 --- a/app/src/main/assets/tangem-app-config +++ b/app/src/main/assets/tangem-app-config @@ -1 +1 @@ -Subproject commit b791bd4cf6c5eca9778f89e87cd62b72d24f5ce9 +Subproject commit 8c1c53b73950698d4acfa4d925b8c14bf6f0da63 diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index ae3e244978..db3fb6f2f8 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -6,6 +6,7 @@ import com.tangem.blockchain.common.Amount import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token +import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.blockchain.extensions.Result import com.tangem.domain.models.scan.CardDTO import com.tangem.tap.common.extensions.safeUpdate @@ -96,7 +97,10 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa amountToSend, destinationAddress, ) as? Result.Success ?: return - val fee = feeResult.data.minimum + val fee = when (val feeForTx = feeResult.data) { + is TransactionFee.Choosable -> feeForTx.minimum + is TransactionFee.Single -> feeForTx.normal + } val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO if (coinValue < fee.amount.value) return diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index c3053f87b6..f35b18309f 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -8,6 +8,7 @@ import com.tangem.common.card.WalletData import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.TapWorkarounds.isWallet2 import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ProductType import com.tangem.operations.attestation.Attestation @@ -30,7 +31,7 @@ internal class TangemCardTypesResolver( } override fun isWallet2(): Boolean { - return card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && card.settings.isKeysImportAllowed + return card.isWallet2 } override fun isTangemTwins(): Boolean = productType == ProductType.Twins diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index bd892377ae..cf4a3cc6e5 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -28,6 +28,9 @@ object TapWorkarounds { val CardDTO.canSkipBackup: Boolean get() = this.firmwareVersion < backupRequiredFirmwareVersion + val CardDTO.isWallet2: Boolean + get() = this.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && this.settings.isKeysImportAllowed + val CardDTO.useOldStyleDerivation: Boolean get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95" diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt index 1fd9e07d70..cb34af8d75 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/CardConfig.kt @@ -20,9 +20,9 @@ sealed interface CardConfig { if (cardDTO.settings.isBackupAllowed && cardDTO.settings.isHDWalletAllowed && cardDTO.firmwareVersion >= FirmwareVersion.MultiWalletAvailable ) { - return TangemWalletCardConfig + return GenericCardConfig } - error("This card is not supported by this configs") + return GenericCardConfig } } } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt similarity index 95% rename from domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt rename to domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt index 69c8c36b78..3a48e2d8ab 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/configs/TangemWalletCardConfig.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/configs/GenericCardConfig.kt @@ -4,7 +4,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.EllipticCurve import timber.log.Timber -object TangemWalletCardConfig : CardConfig { +object GenericCardConfig : CardConfig { override val mandatoryCurves: List get() = listOf( EllipticCurve.Secp256k1, diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt index 77e154859f..9fc609a7b2 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion import com.tangem.domain.common.TapWorkarounds.isTestCard +import com.tangem.domain.common.TapWorkarounds.isWallet2 import com.tangem.domain.models.scan.CardDTO /** @@ -17,8 +18,14 @@ fun CardDTO.supportedBlockchains(): List { Blockchain.fromCurve(EllipticCurve.Secp256k1) } else { wallets.flatMap { Blockchain.fromCurve(it.curve) }.distinct() + }.toMutableList() + // disabled Cardano for wallet 2 for now, should be enabled after key processed + // ([REDACTED_JIRA]) + if (this.isWallet2) { + supportedBlockchains.apply { + remove(Blockchain.Cardano) + } } - return supportedBlockchains .filter { isTestCard == it.isTestnet() } .filter { it.isSupportedInApp() } diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt b/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt index e66ba82f40..61ec0cc72e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/util/ScanResponseExtensions.kt @@ -48,7 +48,7 @@ private fun ScanResponse.hasDerivation(blockchain: Blockchain, derivationPath: D Blockchain.secp256k1Blockchains(isTestnet).contains(blockchain) -> { hasDerivation(EllipticCurve.Secp256k1, derivationPath) } - Blockchain.ed25519OnlyBlockchains(isTestnet).contains(blockchain) -> { + Blockchain.ed25519Blockchains(isTestnet).contains(blockchain) -> { hasDerivation(EllipticCurve.Ed25519, derivationPath) } else -> false diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index ffcb1326b2..90781cb08b 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,9 +80,9 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-314" +tangemBlockchainSdk = "develop-316" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds -tangemCardSdk = "develop-288" +tangemCardSdk = "develop-289" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds # endregion Tangem From a67518573f3417d0db308df12c5113131edd47ce Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 17:08:46 +0300 Subject: [PATCH 49/52] Updated on 2026-08-14 --- .../impl/data/DefaultCustomTokenRepository.kt | 6 ++-- .../viewmodels/AddCustomTokenViewModel.kt | 19 +++++++++--- .../impl/data/TangemApiTokensPagingSource.kt | 4 ++- .../viewmodels/TokensListViewModel.kt | 9 +++++- .../domain/common/TangemCardTypesResolver.kt | 3 +- .../tangem/domain/common/TapWorkarounds.kt | 3 -- .../domain/common/extensions/CardSdk.kt | 16 +++++----- .../addCustomToken/redux/AddCustomTokenHub.kt | 29 ++++++++++++++----- .../redux/AddCustomTokenState.kt | 23 ++++++++++----- 9 files changed, 78 insertions(+), 34 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt index 119872604d..b949793a7d 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/data/DefaultCustomTokenRepository.kt @@ -4,6 +4,7 @@ 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.domain.common.util.cardTypesResolver import com.tangem.tap.features.customtoken.impl.data.converters.FoundTokenConverter import com.tangem.tap.features.customtoken.impl.domain.CustomTokenRepository import com.tangem.tap.features.customtoken.impl.domain.models.FoundToken @@ -27,8 +28,9 @@ class DefaultCustomTokenRepository( ) : CustomTokenRepository { override suspend fun findToken(address: String, networkId: String?): FoundToken { - val supportedTokenNetworkIds = requireNotNull(reduxStateHolder.scanResponse?.card) - .supportedBlockchains() + val scanResponse = requireNotNull(reduxStateHolder.scanResponse) + val supportedTokenNetworkIds = requireNotNull(scanResponse.card) + .supportedBlockchains(scanResponse.cardTypesResolver) .filter(Blockchain::canHandleTokens) .map(Blockchain::toNetworkId) diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt index 4aee8f0645..3b5e96c9e9 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/presentation/viewmodels/AddCustomTokenViewModel.kt @@ -18,6 +18,7 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.crypto.hdWallet.HDWalletError import com.tangem.domain.AddCustomTokenError import com.tangem.domain.common.extensions.* +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.features.addCustomToken.CustomCurrency import com.tangem.tap.domain.model.WalletDataModel @@ -206,9 +207,11 @@ internal class AddCustomTokenViewModel @Inject constructor( private fun getNetworkSelectorItems(): List { val defaultNetwork = createNetworkSelectorItem(blockchain = Blockchain.Unknown) + val scanResponse = reduxStateHolder.scanResponse return listOf(defaultNetwork) + Blockchain.values() .filter { blockchain -> - reduxStateHolder.scanResponse?.card?.supportedBlockchains()?.contains(blockchain) == true + scanResponse?.card?.supportedBlockchains(scanResponse.cardTypesResolver) + ?.contains(blockchain) == true } .sortedBy(Blockchain::fullName) .map(::createNetworkSelectorItem) @@ -404,7 +407,11 @@ internal class AddCustomTokenViewModel @Inject constructor( val isSupportedToken = if (!isNetworkSelected()) { true } else { - reduxStateHolder.scanResponse?.card?.canHandleToken(networkSelectorValue) ?: false + val scanResponse = reduxStateHolder.scanResponse + scanResponse?.card?.canHandleToken( + blockchain = networkSelectorValue, + cardTypesResolver = scanResponse.cardTypesResolver, + ) ?: false } return buildSet { @@ -438,8 +445,12 @@ internal class AddCustomTokenViewModel @Inject constructor( address = uiState.form.contractAddressInputField.value, blockchain = networkSelectorValue, ) - val isSupportedToken = reduxStateHolder.scanResponse?.card - ?.canHandleToken(networkSelectorValue) + val scanResponse = reduxStateHolder.scanResponse + val isSupportedToken = scanResponse?.card + ?.canHandleToken( + blockchain = networkSelectorValue, + cardTypesResolver = scanResponse.cardTypesResolver, + ) ?: false uiState.copySealed( diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt index 2cae031963..fb04538ee2 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/data/TangemApiTokensPagingSource.kt @@ -6,6 +6,7 @@ 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.domain.common.util.cardTypesResolver import com.tangem.tap.features.tokens.impl.data.converters.CoinsResponseConverter import com.tangem.tap.features.tokens.impl.domain.models.Token import com.tangem.tap.proxy.AppStateHolder @@ -38,7 +39,8 @@ internal class TangemApiTokensPagingSource( val page = params.key ?: 0 return runCatching(dispatchers.io) { - val supportedBlockchains = reduxStateHolder.scanResponse?.card?.supportedBlockchains() + val scanResponse = reduxStateHolder.scanResponse + val supportedBlockchains = scanResponse?.card?.supportedBlockchains(scanResponse.cardTypesResolver) ?: Blockchain.values().toList() api.getCoins( diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt index a44d631c1c..acc0ef2830 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/presentation/viewmodels/TokensListViewModel.kt @@ -16,6 +16,7 @@ import com.tangem.core.ui.extensions.getActiveIconRes import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation import com.tangem.domain.common.extensions.canHandleToken import com.tangem.domain.common.extensions.fromNetworkId +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.tap.common.extensions.fullNameWithoutTestnet import com.tangem.tap.common.extensions.getGreyedOutIconRes import com.tangem.tap.common.extensions.getNetworkName @@ -349,8 +350,14 @@ internal class TokensListViewModel @Inject constructor( toggledNetwork.changeToggleState() } } else { + val scanResponse = reduxStateHolder.scanResponse val isUnsupportedToken = - !(reduxStateHolder.scanResponse?.card?.canHandleToken(token.blockchain) ?: false) + !( + scanResponse?.card?.canHandleToken( + blockchain = token.blockchain, + cardTypesResolver = scanResponse.cardTypesResolver, + ) ?: false + ) if (isUnsupportedToken) { router.openUnsupportedSoltanaNetworkAlert() diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt index f35b18309f..c3053f87b6 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TangemCardTypesResolver.kt @@ -8,7 +8,6 @@ import com.tangem.common.card.WalletData import com.tangem.domain.common.TapWorkarounds.getTangemNoteBlockchain import com.tangem.domain.common.TapWorkarounds.isStart2Coin import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.TapWorkarounds.isWallet2 import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.ProductType import com.tangem.operations.attestation.Attestation @@ -31,7 +30,7 @@ internal class TangemCardTypesResolver( } override fun isWallet2(): Boolean { - return card.isWallet2 + return card.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && card.settings.isKeysImportAllowed } override fun isTangemTwins(): Boolean = productType == ProductType.Twins diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt index cf4a3cc6e5..bd892377ae 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/TapWorkarounds.kt @@ -28,9 +28,6 @@ object TapWorkarounds { val CardDTO.canSkipBackup: Boolean get() = this.firmwareVersion < backupRequiredFirmwareVersion - val CardDTO.isWallet2: Boolean - get() = this.firmwareVersion >= FirmwareVersion.Ed25519Slip0010Available && this.settings.isKeysImportAllowed - val CardDTO.useOldStyleDerivation: Boolean get() = batchId == "AC01" || batchId == "AC02" || batchId == "CB95" diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt index 9fc609a7b2..1efeec3cf8 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/CardSdk.kt @@ -3,8 +3,8 @@ package com.tangem.domain.common.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.common.card.EllipticCurve import com.tangem.common.card.FirmwareVersion +import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.TapWorkarounds.isWallet2 import com.tangem.domain.models.scan.CardDTO /** @@ -13,7 +13,7 @@ import com.tangem.domain.models.scan.CardDTO val FirmwareVersion.Companion.SolanaTokensAvailable get() = FirmwareVersion(4, 52) -fun CardDTO.supportedBlockchains(): List { +fun CardDTO.supportedBlockchains(cardTypesResolver: CardTypesResolver): List { val supportedBlockchains = if (firmwareVersion < FirmwareVersion.MultiWalletAvailable) { Blockchain.fromCurve(EllipticCurve.Secp256k1) } else { @@ -21,7 +21,7 @@ fun CardDTO.supportedBlockchains(): List { }.toMutableList() // disabled Cardano for wallet 2 for now, should be enabled after key processed // ([REDACTED_JIRA]) - if (this.isWallet2) { + if (cardTypesResolver.isWallet2()) { supportedBlockchains.apply { remove(Blockchain.Cardano) } @@ -31,8 +31,10 @@ fun CardDTO.supportedBlockchains(): List { .filter { it.isSupportedInApp() } } -fun CardDTO.supportedTokens(): List { - val tokensSupportedByBlockchain = supportedBlockchains().filter { it.canHandleTokens() }.toMutableList() +fun CardDTO.supportedTokens(cardTypesResolver: CardTypesResolver): List { + val tokensSupportedByBlockchain = supportedBlockchains(cardTypesResolver) + .filter { it.canHandleTokens() } + .toMutableList() val tokensSupportedByCard = when { firmwareVersion >= FirmwareVersion.SolanaTokensAvailable -> tokensSupportedByBlockchain else -> { @@ -46,6 +48,6 @@ fun CardDTO.supportedTokens(): List { return tokensSupportedByCard.filter { isTestCard == it.isTestnet() } } -fun CardDTO.canHandleToken(blockchain: Blockchain): Boolean { - return this.supportedTokens().contains(blockchain) +fun CardDTO.canHandleToken(blockchain: Blockchain, cardTypesResolver: CardTypesResolver): Boolean { + return this.supportedTokens(cardTypesResolver).contains(blockchain) } \ No newline at end of file diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt index 3db713c231..aa12f86b00 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenHub.kt @@ -13,6 +13,7 @@ import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.form.* +import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.features.addCustomToken.* import com.tangem.domain.features.addCustomToken.CustomTokenFieldId.* @@ -435,7 +436,13 @@ internal class AddCustomTokenHub : BaseStoreHub("AddCustomT private fun tokenIsSupported(blockchain: Blockchain): Boolean = when (blockchain) { Blockchain.Unknown -> true - else -> globalState.scanResponse?.card?.canHandleToken(blockchain) ?: false + else -> { + val scanResponse = globalState.scanResponse + scanResponse?.card?.canHandleToken( + blockchain = blockchain, + cardTypesResolver = scanResponse.cardTypesResolver, + ) ?: false + } } @Throws @@ -547,8 +554,9 @@ private class AddCustomTokenReducer( state.copy(onTokenAddCallback = action.callback) } is OnCreate -> { - val card = requireNotNull(globalState.scanResponse?.card) - val supportedTokenNetworkIds = card.supportedBlockchains() + val scanResponse = requireNotNull(globalState.scanResponse) + val card = globalState.scanResponse.card + val supportedTokenNetworkIds = card.supportedBlockchains(scanResponse.cardTypesResolver) .filter(Blockchain::canHandleTokens) .map(Blockchain::toNetworkId) @@ -559,15 +567,22 @@ private class AddCustomTokenReducer( ) state.copy( - cardDerivationStyle = globalState.scanResponse?.derivationStyleProvider?.getDerivationStyle(), - form = Form(AddCustomTokenState.createFormFields(card, CustomTokenType.Blockchain)), + cardDerivationStyle = globalState.scanResponse.derivationStyleProvider.getDerivationStyle(), + form = Form( + AddCustomTokenState.createFormFields( + cardTypesResolver = globalState.scanResponse.cardTypesResolver, + card = card, + type = CustomTokenType.Blockchain, + ), + ), tangemTechServiceManager = tangemTechServiceManager, screenState = createInitialScreenState(card.settings.isHDWalletAllowed), ) } is OnDestroy -> { - val card = requireNotNull(globalState.scanResponse?.card) - state.reset(card) + val scanResponse = requireNotNull(globalState.scanResponse) + val card = scanResponse.card + state.reset(scanResponse.cardTypesResolver, card) } is UpdateForm -> { updateFormState(action.state) diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index f68eeb7b87..aaa7693875 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -6,6 +6,7 @@ import com.tangem.common.json.MoshiJsonConverter import com.tangem.datasource.api.tangemTech.models.CoinsResponse import com.tangem.domain.AddCustomTokenError import com.tangem.domain.DomainWrapped +import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.TapWorkarounds.isTestCard import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.isSupportedInApp @@ -113,12 +114,12 @@ data class AddCustomTokenState( null } - fun reset(card: CardDTO): AddCustomTokenState { + fun reset(cardTypesResolver: CardTypesResolver, card: CardDTO): AddCustomTokenState { return this.copy( appSavedCurrencies = null, onTokenAddCallback = null, cardDerivationStyle = null, - form = Form(createFormFields(card, CustomTokenType.Blockchain)), + form = Form(createFormFields(cardTypesResolver, card, CustomTokenType.Blockchain)), formErrors = emptyMap(), foundToken = null, warnings = emptySet(), @@ -165,10 +166,14 @@ data class AddCustomTokenState( }.derivationPath(derivationStyleToUse) } - internal fun createFormFields(card: CardDTO, type: CustomTokenType): List> { + internal fun createFormFields( + cardTypesResolver: CardTypesResolver, + card: CardDTO, + type: CustomTokenType, + ): List> { return listOf( TokenField(ContractAddress), - TokenBlockchainField(Network, getNetworksList(card, type)), + TokenBlockchainField(Network, getNetworksList(cardTypesResolver, card, type)), TokenField(Name), TokenField(Symbol), TokenField(Decimals), @@ -180,7 +185,11 @@ data class AddCustomTokenState( * Serves to determine the networks (blockchains & tokens) that can be selected by Form.Networks. * Blockchain.Unknown - is the default selection */ - private fun getNetworksList(card: CardDTO, type: CustomTokenType): List { + private fun getNetworksList( + cardTypesResolver: CardTypesResolver, + card: CardDTO, + type: CustomTokenType, + ): List { val evmBlockchains = Blockchain.values() .filter { it.isEvm() } .filter { card.isTestCard == it.isTestnet() } @@ -195,8 +204,8 @@ data class AddCustomTokenState( ) val supportedByCard = when (type) { - CustomTokenType.Blockchain -> card.supportedBlockchains() - CustomTokenType.Token -> card.supportedTokens() + CustomTokenType.Blockchain -> card.supportedBlockchains(cardTypesResolver) + CustomTokenType.Token -> card.supportedTokens(cardTypesResolver) } val typedNetworksList = (evmBlockchains + additionalBlockchains) .filter { supportedByCard.contains(it) } From c48f149b459bceb632856e18212a91bb23ee2fa2 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 16:10:29 +0300 Subject: [PATCH 50/52] Updated on 2026-08-14 --- .../com/tangem/tap/features/home/compose/content/Content.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt b/app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt index d119f5b863..2c430b48db 100644 --- a/app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt +++ b/app/src/main/java/com/tangem/tap/features/home/compose/content/Content.kt @@ -176,6 +176,7 @@ private fun StoriesTitleText(text: String, isDarkBackground: Boolean) { .padding(start = 40.dp, end = 40.dp), text = text, fontSize = 32.sp, + lineHeight = 38.sp, fontWeight = FontWeight.SemiBold, color = if (isDarkBackground) Color.White else Color(0xFF090E13), textAlign = TextAlign.Center, @@ -198,6 +199,7 @@ private fun StoriesSubtitleText(subtitleText: AnnotatedString) { fontWeight = FontWeight.Normal, text = subtitleText, fontSize = 20.sp, + lineHeight = 26.sp, color = color, textAlign = TextAlign.Center, ) From 084c6570dce587bd234e094cd069e222af7586d8 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 19:00:10 +0300 Subject: [PATCH 51/52] Updated on 2026-08-14 --- .../java/com/tangem/tap/domain/tokens/UserTokensRepository.kt | 3 ++- .../implementation/DefaultWalletManagersRepository.kt | 1 + .../tap/network/exchangeServices/CurrencyExchangeManager.kt | 1 + .../tangem/domain/common/extensions/WalletManagerFactory.kt | 1 + .../tangem/domain/walletmanager/utils/WalletManagerFactory.kt | 1 + gradle/dependencies.toml | 2 +- 6 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt index c3ed94f70c..20101d8deb 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt @@ -1,7 +1,7 @@ package com.tangem.tap.domain.tokens import android.content.Context -import com.tangem.blockchain.common.DerivationStyle +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.core.TangemSdkError import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.TangemTechService @@ -9,6 +9,7 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.files.AndroidFileReader import com.tangem.domain.common.BlockchainNetwork +import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.tap.domain.tokens.converters.CurrencyConverter diff --git a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt index 2fbd1e0624..006bca6820 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletStores/repository/implementation/DefaultWalletManagersRepository.kt @@ -1,6 +1,7 @@ package com.tangem.tap.domain.walletStores.repository.implementation import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.CompletionResult import com.tangem.common.catching import com.tangem.common.doOnSuccess diff --git a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt index db3fb6f2f8..d3294b1ed2 100644 --- a/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt +++ b/app/src/main/java/com/tangem/tap/network/exchangeServices/CurrencyExchangeManager.kt @@ -97,6 +97,7 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa amountToSend, destinationAddress, ) as? Result.Success ?: return + val fee = when (val feeForTx = feeResult.data) { is TransactionFee.Choosable -> feeForTx.minimum is TransactionFee.Single -> feeForTx.normal diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt index f050be813a..f65ca67b3e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/WalletManagerFactory.kt @@ -1,6 +1,7 @@ package com.tangem.domain.common.extensions import com.tangem.blockchain.common.* +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.card.EllipticCurve import com.tangem.common.extensions.hexToBytes import com.tangem.common.extensions.toMapKey diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt index f588b47702..770721f3f7 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/utils/WalletManagerFactory.kt @@ -2,6 +2,7 @@ package com.tangem.domain.walletmanager.utils import com.tangem.blockchain.common.* import com.tangem.blockchain.common.WalletManagerFactory +import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager import com.tangem.domain.common.TapWorkarounds.useOldStyleDerivation diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 90781cb08b..4c98623249 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-316" +tangemBlockchainSdk = "develop-317" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-289" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds From b163b2c6f03d1740b6c13996a537f8ed140b5289 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 15 Aug 2023 19:32:42 +0300 Subject: [PATCH 52/52] Updated on 2026-08-14 --- .../domain/tasks/product/CreateProductWalletTask.kt | 1 - .../tap/domain/tokens/UserTokensRepository.kt | 1 - .../DefaultWalletCurrenciesManager.kt | 1 - .../impl/domain/DefaultCustomTokenInteractor.kt | 1 - .../redux/walletconnect/WalletConnectMiddleware.kt | 1 - .../impl/domain/DefaultTokensListInteractor.kt | 1 - .../tokens/legacy/redux/TokensMiddleware.kt | 1 - .../tangem/tap/features/wallet/models/Currency.kt | 1 - .../com/tangem/tap/proxy/DerivationManagerImpl.kt | 1 - .../tangem/data/tokens/utils/TokensOperations.kt | 1 - .../com/tangem/domain/common/BlockchainNetwork.kt | 1 - .../tangem/domain/common/extensions/Blockchain.kt | 13 ------------- .../addCustomToken/redux/AddCustomTokenState.kt | 1 - .../walletmanager/DefaultWalletManagersFacade.kt | 1 - .../wallet/viewmodels/WalletViewModel.kt | 1 - gradle/dependencies.toml | 2 +- 16 files changed, 1 insertion(+), 28 deletions(-) diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt index 9b638d235f..78d1ce464f 100644 --- a/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt +++ b/app/src/main/java/com/tangem/tap/domain/tasks/product/CreateProductWalletTask.kt @@ -16,7 +16,6 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.DerivationStyleProvider import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.configs.CardConfig import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.models.scan.KeyWalletPublicKey diff --git a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt index 20101d8deb..1304588781 100644 --- a/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt +++ b/app/src/main/java/com/tangem/tap/domain/tokens/UserTokensRepository.kt @@ -9,7 +9,6 @@ import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.datasource.connection.NetworkConnectionManager import com.tangem.datasource.files.AndroidFileReader import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.userwallets.UserWalletIdBuilder import com.tangem.tap.domain.tokens.converters.CurrencyConverter diff --git a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt index 96408e1315..db1f9347dc 100644 --- a/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/walletCurrencies/implementation/DefaultWalletCurrenciesManager.kt @@ -4,7 +4,6 @@ import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.common.* import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.models.scan.CardDTO import com.tangem.domain.wallets.legacy.WalletManagersRepository diff --git a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt index c5d75bbc2e..1f38373d86 100644 --- a/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/customtoken/impl/domain/DefaultCustomTokenInteractor.kt @@ -8,7 +8,6 @@ import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation 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 42a34e145d..a436d55d8e 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 @@ -8,7 +8,6 @@ import com.tangem.core.navigation.AppScreen import com.tangem.core.navigation.NavigationAction import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.common.extensions.withMainContext diff --git a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt index f56ef520ca..c61c1a2f7e 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/impl/domain/DefaultTokensListInteractor.kt @@ -10,7 +10,6 @@ import com.tangem.common.extensions.toMapKey import com.tangem.common.flatMap import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.supportsHdWallet import com.tangem.domain.models.scan.ScanResponse diff --git a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt index 9aff63b982..67bbf74043 100644 --- a/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/tokens/legacy/redux/TokensMiddleware.kt @@ -14,7 +14,6 @@ import com.tangem.core.navigation.NavigationAction import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.DomainWrapped import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.common.util.supportsHdWallet diff --git a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt index 4af0733400..9262a61187 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/models/Currency.kt @@ -3,7 +3,6 @@ package com.tangem.tap.features.wallet.models import com.tangem.blockchain.common.derivation.DerivationStyle import com.tangem.datasource.api.tangemTech.models.UserTokensResponse import com.tangem.domain.common.BlockchainNetwork -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.features.addCustomToken.CustomCurrency diff --git a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt index 0966150ad0..894467225e 100644 --- a/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt +++ b/app/src/main/java/com/tangem/tap/proxy/DerivationManagerImpl.kt @@ -11,7 +11,6 @@ import com.tangem.common.extensions.toMapKey import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.domain.common.BlockchainNetwork import com.tangem.domain.common.configs.CardConfig -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.fromNetworkId import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation diff --git a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt index b047d19e32..2298a28fb2 100644 --- a/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt +++ b/data/tokens/src/main/kotlin/com/tangem/data/tokens/utils/TokensOperations.kt @@ -3,7 +3,6 @@ package com.tangem.data.tokens.utils import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.IconsUtil import com.tangem.domain.common.DerivationStyleProvider -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.toCoinId import com.tangem.domain.common.extensions.toNetworkId import com.tangem.domain.tokens.models.CryptoCurrency diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt b/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt index 5cb15b3205..8a81005383 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/BlockchainNetwork.kt @@ -5,7 +5,6 @@ import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token import com.tangem.blockchain.common.WalletManager import com.tangem.common.extensions.calculateHashCode -import com.tangem.domain.common.extensions.derivationPath @JsonClass(generateAdapter = true) data class BlockchainNetwork( diff --git a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt index 1910997a9f..9085357e7e 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/common/extensions/Blockchain.kt @@ -2,9 +2,6 @@ package com.tangem.domain.common.extensions import com.tangem.blockchain.common.Blockchain import com.tangem.blockchain.common.Token -import com.tangem.blockchain.common.derivation.DerivationStyle -import com.tangem.common.card.EllipticCurve -import com.tangem.crypto.hdWallet.DerivationPath import java.math.BigDecimal @Suppress("ComplexMethod") @@ -209,16 +206,6 @@ fun Blockchain.minimalAmount(): BigDecimal { return 1.toBigDecimal().movePointLeft(decimals()) } -fun Blockchain.derivationPath(style: DerivationStyle?): DerivationPath? { - if (style == null) return null - if (!getSupportedCurves().contains(EllipticCurve.Secp256k1) && - !getSupportedCurves().contains(EllipticCurve.Ed25519) - ) { - return null - } - return style.getConfig().derivations(this).values.first() -} - private const val NODL = "NODL" private const val NODL_AMOUNT_TO_CREATE_ACCOUNT = 1.5 diff --git a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt index aaa7693875..6d1b3fd2b0 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/features/addCustomToken/redux/AddCustomTokenState.kt @@ -8,7 +8,6 @@ import com.tangem.domain.AddCustomTokenError import com.tangem.domain.DomainWrapped import com.tangem.domain.common.CardTypesResolver import com.tangem.domain.common.TapWorkarounds.isTestCard -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.extensions.isSupportedInApp import com.tangem.domain.common.extensions.supportedBlockchains import com.tangem.domain.common.extensions.supportedTokens diff --git a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt index bd0ea90ed4..e119f09792 100644 --- a/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt +++ b/domain/legacy/src/main/java/com/tangem/domain/walletmanager/DefaultWalletManagersFacade.kt @@ -8,7 +8,6 @@ import com.tangem.crypto.hdWallet.DerivationPath import com.tangem.datasource.config.ConfigManager import com.tangem.datasource.local.userwallet.UserWalletsStore import com.tangem.datasource.local.walletmanager.WalletManagersStore -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.common.util.hasDerivation import com.tangem.domain.demo.DemoConfig diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt index 866dfadf62..f81cd1f969 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/viewmodels/WalletViewModel.kt @@ -14,7 +14,6 @@ import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency import com.tangem.domain.card.* import com.tangem.domain.common.CardTypesResolver -import com.tangem.domain.common.extensions.derivationPath import com.tangem.domain.common.util.cardTypesResolver import com.tangem.domain.common.util.derivationStyleProvider import com.tangem.domain.demo.IsDemoCardUseCase diff --git a/gradle/dependencies.toml b/gradle/dependencies.toml index 4c98623249..0025b49241 100644 --- a/gradle/dependencies.toml +++ b/gradle/dependencies.toml @@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0" # endregion Other libraries # region Tangem -tangemBlockchainSdk = "develop-317" +tangemBlockchainSdk = "develop-318" #tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds tangemCardSdk = "develop-289" #tangemCardSdk = "0.0.1" # Keep it! - used for local builds