From 7d461390e073ae82f2e32a35c317960d0f777e40 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 3 Mar 2026 10:39:48 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../tangem/tap/di/domain/NFTDomainModule.kt | 5 - .../data/wallets/DefaultWalletsRepository.kt | 88 ++-- .../data/wallets/di/WalletsDataModule.kt | 8 - .../wallets/DefaultWalletsRepositoryTest.kt | 440 ++++++++---------- .../domain/nft/GetNFTCollectionsUseCase.kt | 47 +- features/nft/impl/detekt-baseline-debug.xml | 11 - .../collections/model/NFTCollectionsModel.kt | 64 +-- .../nft/common/DefaultNFTComponent.kt | 31 +- .../staking/impl/detekt-baseline-debug.xml | 16 - .../impl/presentation/model/StakingModel.kt | 126 +++-- .../impl/detekt-baseline-debug.xml | 15 - .../DefaultTokenDetailsDeepLinkHandler.kt | 29 +- .../model/ExpressTransactionsModel.kt | 24 +- .../tokendetails/model/TokenDetailsModel.kt | 81 ++-- .../impl/detekt-baseline-debug.xml | 5 - .../impl/DefaultWalletSettingsComponent.kt | 8 +- .../utils/AccountItemsDelegate.kt | 4 +- .../implementors/MultiWalletContentLoader.kt | 2 +- ...criberV2.kt => WalletNFTListSubscriber.kt} | 6 +- 19 files changed, 387 insertions(+), 623 deletions(-) delete mode 100644 features/wallet-settings/impl/detekt-baseline-debug.xml rename features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/{WalletNFTListSubscriberV2.kt => WalletNFTListSubscriber.kt} (94%) diff --git a/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt b/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt index cf1a7707c6..4cbe877c01 100644 --- a/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt +++ b/app/src/main/java/com/tangem/tap/di/domain/NFTDomainModule.kt @@ -1,6 +1,5 @@ package com.tangem.tap.di.domain -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.supplier.SingleAccountListSupplier import com.tangem.domain.networks.single.SingleNetworkStatusSupplier import com.tangem.domain.nft.* @@ -25,15 +24,11 @@ internal object NFTDomainModule { @Provides @Singleton fun providesGetNFTCollectionsUseCase( - currenciesRepository: CurrenciesRepository, nftRepository: NFTRepository, singleAccountListSupplier: SingleAccountListSupplier, - accountsFeatureToggles: AccountsFeatureToggles, ): GetNFTCollectionsUseCase = GetNFTCollectionsUseCase( - currenciesRepository = currenciesRepository, nftRepository = nftRepository, singleAccountListSupplier = singleAccountListSupplier, - accountsFeatureToggles = accountsFeatureToggles, ) @Provides diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt index 83af408c47..8d90aedc75 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/DefaultWalletsRepository.kt @@ -5,17 +5,14 @@ import arrow.core.left import arrow.core.right import com.tangem.data.common.wallet.WalletServerBinder import com.tangem.data.wallets.converters.UserWalletRemoteInfoConverter -import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.ApiResponseError.HttpException import com.tangem.datasource.api.common.response.fold import com.tangem.datasource.api.common.response.getOrThrow import com.tangem.datasource.api.common.response.isNetworkError import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.datasource.api.tangemTech.converters.WalletIdBodyConverter import com.tangem.datasource.api.tangemTech.models.* import com.tangem.datasource.api.tangemTech.models.SeedPhraseNotificationDTO.Status -import com.tangem.datasource.local.appsflyer.AppsFlyerStore import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys @@ -24,7 +21,6 @@ import com.tangem.datasource.local.preferences.utils.getObjectMap import com.tangem.datasource.local.preferences.utils.getSyncOrDefault import com.tangem.datasource.local.preferences.utils.getSyncOrNull import com.tangem.datasource.local.preferences.utils.store -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.common.wallets.getSyncOrNull import com.tangem.domain.common.wallets.getSyncStrict @@ -52,10 +48,7 @@ internal class DefaultWalletsRepository( private val userWalletsListRepository: UserWalletsListRepository, private val seedPhraseNotificationVisibilityStore: RuntimeStateStore, private val dispatchers: CoroutineDispatcherProvider, - private val authProvider: AuthProvider, private val walletServerBinder: WalletServerBinder, - private val appsFlyerStore: AppsFlyerStore, - private val accountsFeatureToggles: AccountsFeatureToggles, private val moshi: com.squareup.moshi.Moshi, ) : WalletsRepository { @@ -368,59 +361,36 @@ internal class DefaultWalletsRepository( override suspend fun associateWallets(applicationId: String, wallets: List) = withContext(dispatchers.io) { - if (accountsFeatureToggles.isFeatureEnabled) { - val associateApplicationIdWithWallets: suspend () -> ApiResponse = { - tangemTechApi.associateApplicationIdWithWalletsV2( - applicationId = applicationId, - body = AssociateApplicationIdWithWalletsBody( - walletIds = wallets.map { it.walletId.stringValue }.distinct(), - ), - ) - } - - val apiResponse = associateApplicationIdWithWallets() - - if (apiResponse is ApiResponse.Success) return@withContext - - if (apiResponse is ApiResponse.Error && - apiResponse.cause.isNetworkError(HttpException.Code.BAD_REQUEST) - ) { - val errorBody = (apiResponse.cause as? HttpException)?.errorBody - ?: error("Bad Request must have error body") - - val adapter = moshi.adapter(AssociateAppWithWalletsErrorResponse::class.java) - val errorResponse = adapter.fromJson(errorBody) - ?: error("Cannot parse error body: $errorBody") - - errorResponse.missingWalletIds - .map { - async { createWallet(userWalletId = UserWalletId(it)) } - } - .awaitAll() - - associateApplicationIdWithWallets().getOrThrow() - } - } else { - val conversionData = appsFlyerStore.get() - val publicKeys = authProvider.getCardsPublicKeys() - val walletsBody = wallets.map { userWallet -> - WalletIdBodyConverter.convert( - userWallet = userWallet, - conversionData = conversionData, - publicKeys = if (userWallet is UserWallet.Cold) { - publicKeys.filterKeys { - userWallet.cardsInWallet.contains(it) - } - } else { - emptyMap() - }, - ) - } - - tangemTechApi.associateApplicationIdWithWallets( + val associateApplicationIdWithWallets: suspend () -> ApiResponse = { + tangemTechApi.associateApplicationIdWithWalletsV2( applicationId = applicationId, - body = walletsBody, - ).getOrThrow() + body = AssociateApplicationIdWithWalletsBody( + walletIds = wallets.map { it.walletId.stringValue }.distinct(), + ), + ) + } + + val apiResponse = associateApplicationIdWithWallets() + + if (apiResponse is ApiResponse.Success) return@withContext + + if (apiResponse is ApiResponse.Error && + apiResponse.cause.isNetworkError(HttpException.Code.BAD_REQUEST) + ) { + val errorBody = (apiResponse.cause as? HttpException)?.errorBody + ?: error("Bad Request must have error body") + + val adapter = moshi.adapter(AssociateAppWithWalletsErrorResponse::class.java) + val errorResponse = adapter.fromJson(errorBody) + ?: error("Cannot parse error body: $errorBody") + + errorResponse.missingWalletIds + .map { + async { createWallet(userWalletId = UserWalletId(it)) } + } + .awaitAll() + + associateApplicationIdWithWallets().getOrThrow() } } diff --git a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt index c580d60ae1..c7561aab8b 100644 --- a/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt +++ b/data/wallets/src/main/java/com/tangem/data/wallets/di/WalletsDataModule.kt @@ -9,13 +9,11 @@ import com.tangem.data.wallets.cold.DefaultColdMapDerivationsRepository import com.tangem.data.wallets.derivations.DefaultDerivationsRepository import com.tangem.data.wallets.hot.DefaultHotMapDerivationsRepository import com.tangem.data.wallets.hot.DefaultHotWalletAccessCodeAttemptsRepository -import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.di.NetworkMoshi import com.tangem.datasource.local.appsflyer.AppsFlyerStore import com.tangem.datasource.local.datastore.RuntimeStateStore import com.tangem.datasource.local.preferences.AppPreferencesStore -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.common.wallets.UserWalletsListRepository import com.tangem.domain.wallets.derivations.ColdMapDerivationsRepository import com.tangem.domain.wallets.derivations.DerivationsRepository @@ -43,10 +41,7 @@ internal object WalletsDataModule { tangemTechApi: TangemTechApi, userWalletsListRepository: UserWalletsListRepository, dispatchers: CoroutineDispatcherProvider, - authProvider: AuthProvider, walletServerBinder: WalletServerBinder, - appsFlyerStore: AppsFlyerStore, - accountsFeatureToggles: AccountsFeatureToggles, @NetworkMoshi moshi: Moshi, ): WalletsRepository { return DefaultWalletsRepository( @@ -55,10 +50,7 @@ internal object WalletsDataModule { userWalletsListRepository = userWalletsListRepository, seedPhraseNotificationVisibilityStore = RuntimeStateStore(defaultValue = emptyMap()), dispatchers = dispatchers, - authProvider = authProvider, walletServerBinder = walletServerBinder, - appsFlyerStore = appsFlyerStore, - accountsFeatureToggles = accountsFeatureToggles, moshi = moshi, ) } diff --git a/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt b/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt index 71182a19c0..74d60d237c 100644 --- a/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt +++ b/data/wallets/src/test/java/com/tangem/data/wallets/DefaultWalletsRepositoryTest.kt @@ -5,149 +5,149 @@ import androidx.datastore.preferences.core.Preferences import com.google.common.truth.Truth.assertThat import com.squareup.moshi.Moshi import com.tangem.data.common.wallet.WalletServerBinder -import com.tangem.datasource.api.common.AuthProvider import com.tangem.datasource.api.common.response.ApiResponse import com.tangem.datasource.api.common.response.ApiResponseError.HttpException import com.tangem.datasource.api.tangemTech.TangemTechApi import com.tangem.datasource.api.tangemTech.models.PromocodeActivationBody import com.tangem.datasource.api.tangemTech.models.PromocodeActivationResponse import com.tangem.datasource.api.tangemTech.models.WalletResponse -import com.tangem.datasource.local.appsflyer.AppsFlyerStore import com.tangem.datasource.local.preferences.AppPreferencesStore import com.tangem.datasource.local.preferences.PreferencesKeys -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.wallets.models.errors.ActivatePromoCodeError -import com.tangem.utils.coroutines.CoroutineDispatcherProvider import com.tangem.utils.coroutines.TestingCoroutineDispatcherProvider -import io.mockk.coEvery -import io.mockk.coVerify -import io.mockk.every -import io.mockk.mockk +import io.mockk.* import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test +import org.junit.jupiter.api.AfterEach +import org.junit.jupiter.api.Nested +import org.junit.jupiter.api.Test +import org.junit.jupiter.api.TestInstance +/** + * Tests for [DefaultWalletsRepository] + */ +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class DefaultWalletsRepositoryTest { - private lateinit var repository: DefaultWalletsRepository - private val preferencesDataStore = mockk>(relaxed = true) + + private val preferencesDataStore: DataStore = mockk(relaxed = true) + private val tangemTechApi: TangemTechApi = mockk() + private val walletServerBinder: WalletServerBinder = mockk() + private val appPreferenceStore = AppPreferencesStore( moshi = Moshi.Builder().build(), dispatchers = TestingCoroutineDispatcherProvider(), preferencesDataStore = preferencesDataStore, ) - private lateinit var tangemTechApi: TangemTechApi - private lateinit var dispatchers: CoroutineDispatcherProvider - private lateinit var walletServerBinder: WalletServerBinder - private lateinit var appsFlyerStore: AppsFlyerStore + + private val repository = DefaultWalletsRepository( + appPreferencesStore = appPreferenceStore, + tangemTechApi = tangemTechApi, + userWalletsListRepository = mockk(), + seedPhraseNotificationVisibilityStore = mockk(), + dispatchers = TestingCoroutineDispatcherProvider(), + walletServerBinder = walletServerBinder, + moshi = mockk(), + ) private val testWalletId = UserWalletId("1234567890abcdef") - @Before - fun setup() { - tangemTechApi = mockk() - walletServerBinder = mockk() - appsFlyerStore = mockk() - dispatchers = TestingCoroutineDispatcherProvider() - repository = DefaultWalletsRepository( - appPreferencesStore = appPreferenceStore, - tangemTechApi = tangemTechApi, - userWalletsListRepository = mockk(), - seedPhraseNotificationVisibilityStore = mockk(), - dispatchers = dispatchers, - authProvider = mockk(), - walletServerBinder = walletServerBinder, - appsFlyerStore = appsFlyerStore, - accountsFeatureToggles = mockk(), - moshi = mockk(), - ) + @AfterEach + fun tearDown() { + clearMocks(tangemTechApi, preferencesDataStore) } - @Test - fun `GIVEN local storage has value WHEN isNotificationsEnabled THEN should return local value`() = runTest { - // GIVEN - val expectedPreferences = """{"${testWalletId.stringValue}":true}""" - val preferences = mockk() - coEvery { preferencesDataStore.data } returns flowOf(preferences) - coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns expectedPreferences + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class IsNotificationsEnabled { - // WHEN - val result = repository.isNotificationsEnabled(testWalletId) + @Test + fun `should return local value when local storage has value`() = runTest { + // Arrange + val expectedPreferences = """{"${testWalletId.stringValue}":true}""" + val preferences = mockk() + coEvery { preferencesDataStore.data } returns flowOf(preferences) + coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns expectedPreferences - // THEN - assertThat(result).isTrue() + // Act + val result = repository.isNotificationsEnabled(testWalletId) + + // Assert + assertThat(result).isTrue() + } + + @Test + fun `should return false when local storage is empty`() = runTest { + // Arrange + val preferences = mockk() + coEvery { preferencesDataStore.data } returns flowOf(preferences) + coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns "{}" + + // Act + val result = repository.isNotificationsEnabled(testWalletId) + + // Assert + assertThat(result).isFalse() + } } - @Test - fun `GIVEN local storage is empty WHEN isNotificationsEnabled THEN should return false`() = runTest { - // GIVEN - val preferences = mockk() - coEvery { preferencesDataStore.data } returns flowOf(preferences) - coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns "{}" + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class SetNotificationsEnabled { - // WHEN - val result = repository.isNotificationsEnabled(testWalletId) + @Test + fun `should update local storage when enabled status`() = runTest { + // Arrange + val preferences = mockk() + coEvery { preferencesDataStore.data } returns flowOf(preferences) + coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns "{}" + coEvery { preferencesDataStore.updateData(any()) } returns mockk() - // THEN - assertThat(result).isFalse() + // Act + repository.setNotificationsEnabled(testWalletId, isEnabled = true) + + // Assert + coVerify(exactly = 1) { preferencesDataStore.updateData(any()) } + } + + @Test + fun `should update local storage when disabled status`() = runTest { + // Arrange + val preferences = mockk() + coEvery { preferencesDataStore.data } returns flowOf(preferences) + coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns "{}" + coEvery { preferencesDataStore.updateData(any()) } returns mockk() + + // Act + repository.setNotificationsEnabled(testWalletId, isEnabled = false) + + // Assert + coVerify(exactly = 1) { preferencesDataStore.updateData(any()) } + } } - @Test - fun `GIVEN enabled status WHEN setNotificationsEnabled THEN should update local storage`() = runTest { - // GIVEN - val preferences = mockk() - coEvery { preferencesDataStore.data } returns flowOf(preferences) - coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns "{}" - coEvery { preferencesDataStore.updateData(any()) } returns mockk() + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class GetWalletsInfo { - // WHEN - repository.setNotificationsEnabled(testWalletId, isEnabled = true) - - // THEN - coVerify(exactly = 1) { preferencesDataStore.updateData(any()) } - } - - @Test - fun `GIVEN disabled status WHEN setNotificationsEnabled THEN should update local storage`() = runTest { - // GIVEN - val preferences = mockk() - coEvery { preferencesDataStore.data } returns flowOf(preferences) - coEvery { preferences[PreferencesKeys.NOTIFICATIONS_ENABLED_STATES_KEY] } returns "{}" - coEvery { preferencesDataStore.updateData(any()) } returns mockk() - - // WHEN - repository.setNotificationsEnabled(testWalletId, isEnabled = false) - - // THEN - coVerify(exactly = 1) { preferencesDataStore.updateData(any()) } - } - - @Test - fun `GIVEN API returns wallets WHEN getWalletsInfo THEN should return converted wallets and update cache if requested`() = - runTest { - // GIVEN + @Test + fun `should return converted wallets and update cache when updateCache is true`() = runTest { + // Arrange val applicationId = "test_app_id" val wallet1Id = "1234567890abcdef" val wallet2Id = "fedcba0987654321" val walletResponses = listOf( - WalletResponse( - id = wallet1Id, - notifyStatus = true, - ), - WalletResponse( - id = wallet2Id, - notifyStatus = false, - ), + WalletResponse(id = wallet1Id, notifyStatus = true), + WalletResponse(id = wallet2Id, notifyStatus = false), ) coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses) coEvery { preferencesDataStore.updateData(any()) } returns mockk() - // WHEN + // Act val result = repository.getWalletsInfo(applicationId, updateCache = true) - // THEN + // Assert assertThat(result).hasSize(2) assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id) assertThat(result[0].isNotificationsEnabled).isTrue() @@ -158,24 +158,20 @@ class DefaultWalletsRepositoryTest { coVerify(exactly = 2) { preferencesDataStore.updateData(any()) } } - @Test - fun `GIVEN API returns wallets WHEN getWalletsInfo with updateCache false THEN should return converted wallets without updating cache`() = - runTest { - // GIVEN + @Test + fun `should return converted wallets without updating cache when updateCache is false`() = runTest { + // Arrange val applicationId = "test_app_id" val wallet1Id = "1234567890abcdef" val walletResponses = listOf( - WalletResponse( - id = wallet1Id, - notifyStatus = true, - ), + WalletResponse(id = wallet1Id, notifyStatus = true), ) coEvery { tangemTechApi.getWallets(applicationId) } returns ApiResponse.Success(walletResponses) - // WHEN + // Act val result = repository.getWalletsInfo(applicationId, updateCache = false) - // THEN + // Assert assertThat(result).hasSize(1) assertThat(result[0].walletId.stringValue).isEqualTo(wallet1Id) assertThat(result[0].isNotificationsEnabled).isTrue() @@ -183,152 +179,126 @@ class DefaultWalletsRepositoryTest { coVerify(exactly = 1) { tangemTechApi.getWallets(applicationId) } coVerify(exactly = 0) { preferencesDataStore.updateData(any()) } } + } - @Test - fun `GIVEN user wallets and application ID WHEN associateWallets THEN should convert and send to API`() = runTest { - // GIVEN - val applicationId = "test_app_id" - val wallet1Id = "1234567890abcdef" - val wallet2Id = "fedcba0987654321" - val card1PublicKey = "card1_public_key" - val card2PublicKey = "card2_public_key" + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class AssociateWallets { - val userWallets = listOf( - mockk { - every { cardsInWallet } returns setOf(card1PublicKey) - every { walletId } returns UserWalletId(wallet1Id) - every { name } returns "Wallet 1" - }, - mockk { - every { cardsInWallet } returns setOf(card2PublicKey) - every { walletId } returns UserWalletId(wallet2Id) - every { name } returns "Wallet 2" - }, - ) + @Test + fun `should convert and send to API V2`() = runTest { + // Arrange + val applicationId = "test_app_id" + val wallet1Id = "1234567890abcdef" + val wallet2Id = "fedcba0987654321" - val publicKeys = mapOf( - card1PublicKey to "public_key_1", - card2PublicKey to "public_key_2", - ) - - val authProvider = mockk { - coEvery { getCardsPublicKeys() } returns publicKeys - } - - val accountsFeatureToggles = mockk { - every { isFeatureEnabled } returns false - } - - repository = DefaultWalletsRepository( - appPreferencesStore = appPreferenceStore, - tangemTechApi = tangemTechApi, - userWalletsListRepository = mockk(), - seedPhraseNotificationVisibilityStore = mockk(), - dispatchers = dispatchers, - authProvider = authProvider, - walletServerBinder = walletServerBinder, - appsFlyerStore = appsFlyerStore, - accountsFeatureToggles = accountsFeatureToggles, - moshi = mockk(), - ) - - coEvery { appsFlyerStore.get() } returns null - - coEvery { - tangemTechApi.associateApplicationIdWithWallets( - eq(applicationId), - any(), - ) - } returns ApiResponse.Success(Unit) - - // WHEN - repository.associateWallets(applicationId, userWallets) - - // THEN - coVerify(exactly = 1) { - tangemTechApi.associateApplicationIdWithWallets( - applicationId = eq(applicationId), - body = match { body -> - body.size == 2 && - body.any { - it.walletId == wallet1Id && - it.cards!!.any { card -> card.cardPublicKey == "public_key_1" } && - it.name == "Wallet 1" - } && - body.any { - it.walletId == wallet2Id && - it.cards!!.any { card -> card.cardPublicKey == "public_key_2" } && - it.name == "Wallet 2" - } + val userWallets = listOf( + mockk { + every { walletId } returns UserWalletId(wallet1Id) + }, + mockk { + every { walletId } returns UserWalletId(wallet2Id) }, ) + + coEvery { + tangemTechApi.associateApplicationIdWithWalletsV2(eq(applicationId), any()) + } returns ApiResponse.Success(Unit) + + // Act + repository.associateWallets(applicationId, userWallets) + + // Assert + coVerify(exactly = 1) { + tangemTechApi.associateApplicationIdWithWalletsV2( + applicationId = eq(applicationId), + body = match { body -> + body.walletIds.size == 2 && + body.walletIds.contains(wallet1Id) && + body.walletIds.contains(wallet2Id) + }, + ) + } } } - @Test - fun `GIVEN valid data WHEN activatePromoCode THEN returns Right with status and calls API`() = runTest { - // GIVEN - val walletId = UserWalletId("1234567890abcdef") - val promoCode = "PROMO123" - val address = "bc1qexampleaddress" - coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Success( - PromocodeActivationResponse(status = "activated"), - ) + @Nested + @TestInstance(TestInstance.Lifecycle.PER_CLASS) + inner class ActivatePromoCode { - // WHEN - val result = repository.activatePromoCode( - userWalletId = walletId, - promoCode = promoCode, - bitcoinAddress = address - ) - - // THEN - var right: String? = null - var left: ActivatePromoCodeError? = null - result.fold({ left = it }, { right = it }) - assertThat(left).isNull() - assertThat(right).isEqualTo("activated") - - coVerify(exactly = 1) { - tangemTechApi.activatePromoCode( - match { it is PromocodeActivationBody && it.promoCode == promoCode && it.address == address }, + @Test + fun `should return Right with status when API returns success`() = runTest { + // Arrange + val walletId = UserWalletId("1234567890abcdef") + val promoCode = "PROMO123" + val address = "bc1qexampleaddress" + coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Success( + PromocodeActivationResponse(status = "activated"), ) - } - } - @Test - fun `GIVEN NOT_FOUND error WHEN activatePromoCode THEN returns Left InvalidPromoCode`() = runTest { - // GIVEN - val walletId = UserWalletId("1234567890abcdef") - coEvery { tangemTechApi.activatePromoCode(any()) } returns - ApiResponse.Error( + // Act + val result = repository.activatePromoCode( + userWalletId = walletId, + promoCode = promoCode, + bitcoinAddress = address, + ) + + // Assert + var right: String? = null + var left: ActivatePromoCodeError? = null + result.fold({ left = it }, { right = it }) + assertThat(left).isNull() + assertThat(right).isEqualTo("activated") + + coVerify(exactly = 1) { + tangemTechApi.activatePromoCode( + match { it.promoCode == promoCode && it.address == address }, + ) + } + } + + @Test + fun `should return Left InvalidPromoCode when API returns NOT_FOUND`() = runTest { + // Arrange + val walletId = UserWalletId("1234567890abcdef") + @Suppress("UNCHECKED_CAST") + coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Error( HttpException(code = HttpException.Code.NOT_FOUND, message = null, errorBody = null), ) as ApiResponse - // WHEN - val result = repository.activatePromoCode(userWalletId = walletId, promoCode = "PROMO", bitcoinAddress = "addr") + // Act + val result = repository.activatePromoCode( + userWalletId = walletId, + promoCode = "PROMO", + bitcoinAddress = "addr", + ) - // THEN - var error: ActivatePromoCodeError? = null - result.fold({ error = it }, { }) - assertThat(error).isEqualTo(ActivatePromoCodeError.InvalidPromoCode) - } + // Assert + var error: ActivatePromoCodeError? = null + result.fold({ error = it }, { }) + assertThat(error).isEqualTo(ActivatePromoCodeError.InvalidPromoCode) + } - @Test - fun `GIVEN CONFLICT error WHEN activatePromoCode THEN returns Left PromocodeAlreadyUsed`() = runTest { - // GIVEN - val walletId = UserWalletId("1234567890abcdef") - coEvery { tangemTechApi.activatePromoCode(any()) } returns - ApiResponse.Error( + @Test + fun `should return Left PromocodeAlreadyUsed when API returns CONFLICT`() = runTest { + // Arrange + val walletId = UserWalletId("1234567890abcdef") + @Suppress("UNCHECKED_CAST") + coEvery { tangemTechApi.activatePromoCode(any()) } returns ApiResponse.Error( HttpException(code = HttpException.Code.CONFLICT, message = null, errorBody = null), ) as ApiResponse - // WHEN - val result = repository.activatePromoCode(userWalletId = walletId, promoCode = "PROMO", bitcoinAddress = "addr") + // Act + val result = repository.activatePromoCode( + userWalletId = walletId, + promoCode = "PROMO", + bitcoinAddress = "addr", + ) - // THEN - var error: ActivatePromoCodeError? = null - result.fold({ error = it }, { }) - assertThat(error).isEqualTo(ActivatePromoCodeError.PromocodeAlreadyUsed) + // Assert + var error: ActivatePromoCodeError? = null + result.fold({ error = it }, { }) + assertThat(error).isEqualTo(ActivatePromoCodeError.PromocodeAlreadyUsed) + } } } \ No newline at end of file diff --git a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCollectionsUseCase.kt b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCollectionsUseCase.kt index 95b2fd53cb..f8432faa75 100644 --- a/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCollectionsUseCase.kt +++ b/domain/nft/src/main/kotlin/com/tangem/domain/nft/GetNFTCollectionsUseCase.kt @@ -1,6 +1,5 @@ package com.tangem.domain.nft -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.supplier.SingleAccountListSupplier import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency @@ -8,52 +7,42 @@ import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.nft.models.NFTCollections import com.tangem.domain.nft.models.WalletNFTCollections import com.tangem.domain.nft.repository.NFTRepository -import com.tangem.domain.tokens.repository.CurrenciesRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* class GetNFTCollectionsUseCase( - private val currenciesRepository: CurrenciesRepository, private val nftRepository: NFTRepository, private val singleAccountListSupplier: SingleAccountListSupplier, - private val accountsFeatureToggles: AccountsFeatureToggles, ) { - @Deprecated("Use invokeForAccounts instead") @OptIn(ExperimentalCoroutinesApi::class) - operator fun invoke(userWalletId: UserWalletId): Flow> = - if (accountsFeatureToggles.isFeatureEnabled) { - invokeForAccounts(userWalletId).map { it.flattenCollections } - } else { - currenciesRepository - .getWalletCurrenciesUpdates(userWalletId) - .flatMapLatest { - nftCollections(userWalletId, it) - } - } - - @OptIn(ExperimentalCoroutinesApi::class) - fun invokeForAccounts(userWalletId: UserWalletId): Flow { - fun Account.flowOfNFTCollections(): Flow>>? { - val currencies = (this as? Account.CryptoPortfolio)?.cryptoCurrencies.orEmpty() - if (currencies.isEmpty()) return null - return nftCollections(userWalletId = userWalletId, cryptoCurrencies = currencies.toList()) - .map { nfts -> this to nfts } - } - + operator fun invoke(userWalletId: UserWalletId): Flow { return singleAccountListSupplier(userWalletId) - .mapLatest { statusList -> statusList.accounts.mapNotNull { it.flowOfNFTCollections() } } - .flatMapLatest { flows -> combine(flows) { WalletNFTCollections(it.toMap()) } } + .mapLatest { statusList -> statusList.accounts.mapNotNull(::flowOfNFTCollections) } + .flatMapLatest { flows -> + combine(flows) { WalletNFTCollections(it.toMap()) } + } } - private fun nftCollections( + private fun flowOfNFTCollections(account: Account): Flow>>? { + val currencies = (account as? Account.CryptoPortfolio)?.cryptoCurrencies.orEmpty() + + if (currencies.isEmpty()) return null + + return getNftCollections(userWalletId = account.userWalletId, cryptoCurrencies = currencies.toList()) + .map { nfts -> account to nfts } + } + + private fun getNftCollections( userWalletId: UserWalletId, cryptoCurrencies: List, ): Flow> { val networks = cryptoCurrencies - .map { cryptoCurrency -> cryptoCurrency.network } + .map(CryptoCurrency::network) .distinct() + if (networks.isEmpty()) return flowOf(emptyList()) + return nftRepository.observeCollections(userWalletId, networks) } } \ No newline at end of file diff --git a/features/nft/impl/detekt-baseline-debug.xml b/features/nft/impl/detekt-baseline-debug.xml index 97fee15b2b..cadeff498d 100644 --- a/features/nft/impl/detekt-baseline-debug.xml +++ b/features/nft/impl/detekt-baseline-debug.xml @@ -5,34 +5,23 @@ BooleanPropertyNaming:NFTAssetUM.kt$NFTAssetUM$val showAllTraitsButton: Boolean BooleanPropertyNaming:NFTAssetUM.kt$NFTAssetUM.BlockItem$val showInfoButton: Boolean BooleanPropertyNaming:NFTAssetUM.kt$NFTAssetUM.Rarity.Content$val showDivider: Boolean - BooleanPropertyNaming:NFTCollectionsModel.kt$NFTCollectionsModel$val assetsFulfillQuery = if (query.isEmpty()) { true } else { when (val assets = it.assets) { is NFTCollection.Assets.Empty, is NFTCollection.Assets.Failed, is NFTCollection.Assets.Loading, -> false is NFTCollection.Assets.Value -> { assets.items.any { asset -> asset.name?.lowercase()?.contains(query.lowercase()) == true } } } } - BooleanPropertyNaming:NFTCollectionsModel.kt$NFTCollectionsModel$val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true BooleanPropertyNaming:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$val custom = derivationPath is Network.DerivationPath.Custom MultilineLambdaItParameter:ChangeCollectionExpandedStateTransformer.kt$ChangeCollectionExpandedStateTransformer${ val collectionId = collection.collectionIdProvider() if (it.id == collectionId && it is NFTCollectionUM) { if (!it.isExpanded) { onFirstExpanded() } it.copy(isExpanded = !it.isExpanded) } else { it } } MultilineLambdaItParameter:NFTCollectionsContent.kt${ key(it.id) { NFTCollectionWarning( modifier = Modifier .padding(top = TangemTheme.dimens.spacing16), state = it, ) } } - MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ ChangeCollectionExpandedStateTransformer( collection = collection, collectionIdProvider = collectionIdProvider, onFirstExpanded = { onFirstExpanded(collection) }, ).transform(it) } - MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ UpdateDataStateTransformer( nftCollections = listOf(), isAccountMode = isAccountMode, walletNFTCollections = nftCollections.copy(collections = filteredNFTs), onReceiveClick = { params.onReceiveClick() }, onRetryClick = ::onRefresh, onExpandCollectionClick = ::onExpandCollectionClick, onRetryAssetsClick = ::onRetryAssetsClick, onAssetClick = { asset, collection -> params.onAssetClick(asset, collection) }, initialSearchBarFactory = ::getInitialSearchBar, collectionIdProvider = collectionIdProvider, ).transform(it) } - MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ UpdateDataStateTransformer( nftCollections = nftCollections.filter(query), onReceiveClick = { params.onReceiveClick() }, onRetryClick = ::onRefresh, onExpandCollectionClick = ::onExpandCollectionClick, onRetryAssetsClick = ::onRetryAssetsClick, onAssetClick = { asset, collection -> params.onAssetClick(asset, collection) }, initialSearchBarFactory = ::getInitialSearchBar, collectionIdProvider = collectionIdProvider, ).transform(it) } - MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ it.copy( content = when (val content = it.content) { is NFTCollections.Content.Collections -> content.copy( collections = content.collections.orEmpty().filter { val assetsFulfillQuery = if (query.isEmpty()) { true } else { when (val assets = it.assets) { is NFTCollection.Assets.Empty, is NFTCollection.Assets.Failed, is NFTCollection.Assets.Loading, -> false is NFTCollection.Assets.Value -> { assets.items.any { asset -> asset.name?.lowercase()?.contains(query.lowercase()) == true } } } } val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true collectionFulfillQuery || assetsFulfillQuery }, ) is NFTCollections.Content.Error -> it.content }, ) } - MultilineLambdaItParameter:NFTCollectionsModel.kt$NFTCollectionsModel${ val assetsFulfillQuery = if (query.isEmpty()) { true } else { when (val assets = it.assets) { is NFTCollection.Assets.Empty, is NFTCollection.Assets.Failed, is NFTCollection.Assets.Loading, -> false is NFTCollection.Assets.Value -> { assets.items.any { asset -> asset.name?.lowercase()?.contains(query.lowercase()) == true } } } } val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true collectionFulfillQuery || assetsFulfillQuery } MultilineLambdaItParameter:NFTDetailsUMFactory.kt$NFTDetailsUMFactory${ NFTAssetUM.BlockItem( title = stringReference(it.name), value = it.value, showInfoButton = false, ) } MultilineLambdaItParameter:NFTDetailsUMFactory.kt$NFTDetailsUMFactory${ NFTAssetUM.Media.Content( url = it, ) } MultilineLambdaItParameter:NFTReceiveModel.kt$NFTReceiveModel${ ShowReceiveBottomSheetTransformer( network = network, networkAddress = value.address, onDismissBottomSheet = ::onReceiveBottomSheetDismiss, onCopyClick = { text -> onCopyClick(text, network) }, onShareClick = { text -> onShareClick(text, network) }, ).transform(it) } MultilineLambdaItParameter:NFTReceiveModel.kt$NFTReceiveModel${ UpdateDataStateTransformer( networks = filteredNetworks, onNetworkClick = ::onNetworkClick, ).transform(it) } MultilineLambdaItParameter:NFTReceiveModel.kt$NFTReceiveModel${ it.copy( bottomSheetConfig = it.bottomSheetConfig?.copy(isShown = false), ) } MultilineLambdaItParameter:UpdateDataStateTransformer.kt$UpdateDataStateTransformer${ NFTCollectionUM( id = it.collectionIdProvider(), networkIconId = getActiveIconRes(it.network.rawId), name = it.name.orEmpty(), description = TextReference.PluralRes( R.plurals.nft_collections_count, it.count, wrappedList(it.count), ), logoUrl = it.logoUrl, assets = it.transformAssets(), onExpandClick = { onExpandCollectionClick(it) }, isExpanded = it.isExpanded(state), ) } - NoNameShadowing:NFTCollectionsModel.kt$NFTCollectionsModel${ val assetsFulfillQuery = if (query.isEmpty()) { true } else { when (val assets = it.assets) { is NFTCollection.Assets.Empty, is NFTCollection.Assets.Failed, is NFTCollection.Assets.Loading, -> false is NFTCollection.Assets.Value -> { assets.items.any { asset -> asset.name?.lowercase()?.contains(query.lowercase()) == true } } } } val collectionFulfillQuery = query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true collectionFulfillQuery || assetsFulfillQuery } NullableBooleanCheck:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$(state.content as? NFTCollectionsUM.Content) ?.collections ?.filterIsInstance<NFTCollectionUM>() ?.firstOrNull { it.id == this.collectionIdProvider() } ?.isExpanded ?: false NullableToStringCall:NFTCollectionsContent.kt$${item2?.id} - NullableToStringCall:NFTCollectionsModel.kt$NFTCollectionsModel$${network.derivationPath.value} PropertyUsedBeforeDeclaration:NFTDetailsModel.kt$NFTDetailsModel$_state ReusedModifierInstance:NFTCollectionsContent.kt$Box( modifier = modifier .fillMaxSize() .padding(bottom = bottomPadding), ) { Text( modifier = Modifier .align(Alignment.Center), text = stringResourceSafe(id = R.string.nft_empty_search), style = TangemTheme.typography.body2, color = TangemTheme.colors.text.tertiary, textAlign = TextAlign.Center, ) } ReusedModifierInstance:NFTCollectionsLoading.kt$Card( modifier = modifier .fillMaxWidth() .padding( top = TangemTheme.dimens.spacing16, ), shape = RoundedCornerShape(TangemTheme.dimens.radius16), colors = CardDefaults.cardColors( containerColor = TangemTheme.colors.background.primary, contentColor = TangemTheme.colors.text.primary1, disabledContainerColor = TangemTheme.colors.background.primary, disabledContentColor = TangemTheme.colors.text.primary1, ), ) { Column { repeat(SHIMMER_ITEMS_COUNT) { CollectionPlaceholder() } } } ReusedModifierInstance:NFTDetailsAsset.kt$Column( modifier = modifier .verticalScroll(scrollState) .padding( start = TangemTheme.dimens.spacing16, top = TangemTheme.dimens.spacing16, end = TangemTheme.dimens.spacing16, bottom = bottomPadding, ) .fillMaxSize(), ) { NFTDetailsLogo( state = state.media, modifier = Modifier .aspectRatio(1f), ) NFTDetailsInfoGroup( modifier = Modifier .padding(top = TangemTheme.dimens.spacing12), state = state.topInfo, onReadMoreClick = onReadMoreClick, ) NFTDetailsBlocksGroup( modifier = Modifier .padding(top = TangemTheme.dimens.spacing12), items = state.traits, title = resourceReference(R.string.nft_details_traits), action = if (state.showAllTraitsButton) { { NFTBlocksGroupAction( text = resourceReference(R.string.common_see_all), startIcon = { }, onClick = onSeeAllTraitsClick, ) } } else { null }, ) NFTDetailsBlocksGroup( modifier = Modifier .padding(top = TangemTheme.dimens.spacing12), items = state.baseInfoItems, title = resourceReference(R.string.nft_details_base_information), action = { NFTBlocksGroupAction( text = resourceReference(R.string.common_explore), startIcon = { NFTBlocksGroupActionIcon(iconRes = R.drawable.ic_compass_24) }, onClick = onExploreClick, ) }, ) } ReusedModifierInstance:NFTDetailsInfoGroup.kt$Column( modifier = modifier .padding( start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, ), ) { TextShimmer( modifier = Modifier .width(TangemTheme.dimens.size158), style = TangemTheme.typography.head, textSizeHeight = true, ) TextShimmer( modifier = Modifier .padding(top = TangemTheme.dimens.spacing4) .width(TangemTheme.dimens.size90), style = TangemTheme.typography.caption2, textSizeHeight = true, ) } ReusedModifierInstance:NFTDetailsInfoGroup.kt$Column( modifier = modifier .padding( start = TangemTheme.dimens.spacing12, end = TangemTheme.dimens.spacing12, ), verticalArrangement = Arrangement.SpaceAround, ) { Text( text = state.cryptoPrice.resolveReference(), style = TangemTheme.typography.head, color = TangemTheme.colors.text.primary1, ) Text( modifier = Modifier .padding(top = TangemTheme.dimens.spacing4) .flicker(state.isFlickering), text = state.fiatPrice.resolveReference(), style = TangemTheme.typography.caption2, color = TangemTheme.colors.text.tertiary, ) } - UnsafeCallOnNullableType:DefaultNFTComponent.kt$DefaultNFTComponent$portfolioFetcher!! - UseEmptyCounterpart:NFTCollectionsModel.kt$NFTCollectionsModel$listOf() UseEmptyCounterpart:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$listOf() UseOrEmpty:UpdateDataStateTransformer.kt$UpdateDataStateTransformer$walletNFTCollections.collections.values.firstOrNull() ?: listOf() diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt index 20f1fc3414..2f6f77b6c7 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/collections/model/NFTCollectionsModel.kt @@ -7,7 +7,6 @@ import com.tangem.core.ui.components.containers.pullToRefresh.PullToRefreshConfi import com.tangem.core.ui.components.fields.InputManager import com.tangem.core.ui.components.fields.entity.SearchBarUM import com.tangem.core.ui.extensions.resourceReference -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.nft.FetchNFTCollectionAssetsUseCase import com.tangem.domain.nft.GetNFTCollectionsUseCase @@ -33,7 +32,6 @@ internal class NFTCollectionsModel @Inject constructor( private val getNFTCollectionsUseCase: GetNFTCollectionsUseCase, private val fetchNFTCollectionAssetsUseCase: FetchNFTCollectionAssetsUseCase, private val refreshAllNFTUseCase: RefreshAllNFTUseCase, - private val accountsFeatureToggles: AccountsFeatureToggles, private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase, paramsContainer: ParamsContainer, ) : Model() { @@ -66,51 +64,21 @@ internal class NFTCollectionsModel @Inject constructor( } init { - if (accountsFeatureToggles.isFeatureEnabled) { - subscribeToNFTCollectionsNew() - } else { - subscribeToNFTCollections() - } + subscribeToNFTCollections() } private fun subscribeToNFTCollections() { combine( flow = getNFTCollectionsUseCase(params.userWalletId), flow2 = searchManager.query.distinctUntilChanged(), - ) { nftCollections, query -> - _state.update { - UpdateDataStateTransformer( - nftCollections = nftCollections.filter(query), - onReceiveClick = { - params.onReceiveClick() - }, - onRetryClick = ::onRefresh, - onExpandCollectionClick = ::onExpandCollectionClick, - onRetryAssetsClick = ::onRetryAssetsClick, - onAssetClick = { asset, collection -> - params.onAssetClick(asset, collection) - }, - initialSearchBarFactory = ::getInitialSearchBar, - collectionIdProvider = collectionIdProvider, - ).transform(it) - } - } - .onStart { onRefresh() } - .launchIn(modelScope) - } - - private fun subscribeToNFTCollectionsNew() { - combine( - flow = getNFTCollectionsUseCase.invokeForAccounts(params.userWalletId), - flow2 = searchManager.query.distinctUntilChanged(), flow3 = isAccountsModeEnabledUseCase(), ) { nftCollections, query, isAccountMode -> val filteredNFTs = nftCollections.collections .mapValues { (_, nfts) -> nfts.filter(query) } - _state.update { + _state.update { stateUM -> UpdateDataStateTransformer( - nftCollections = listOf(), + nftCollections = emptyList(), isAccountMode = isAccountMode, walletNFTCollections = nftCollections.copy(collections = filteredNFTs), onReceiveClick = { @@ -124,22 +92,22 @@ internal class NFTCollectionsModel @Inject constructor( }, initialSearchBarFactory = ::getInitialSearchBar, collectionIdProvider = collectionIdProvider, - ).transform(it) + ).transform(stateUM) } } .onStart { onRefresh() } .launchIn(modelScope) } - private fun List.filter(query: String): List = map { - it.copy( - content = when (val content = it.content) { + private fun List.filter(query: String): List = map { collections -> + collections.copy( + content = when (val content = collections.content) { is NFTCollections.Content.Collections -> content.copy( - collections = content.collections.orEmpty().filter { - val assetsFulfillQuery = if (query.isEmpty()) { + collections = content.collections.orEmpty().filter { collection: NFTCollection -> + val isAssetsFulfillQuery = if (query.isEmpty()) { true } else { - when (val assets = it.assets) { + when (val assets = collection.assets) { is NFTCollection.Assets.Empty, is NFTCollection.Assets.Failed, is NFTCollection.Assets.Loading, @@ -152,13 +120,13 @@ internal class NFTCollectionsModel @Inject constructor( } } - val collectionFulfillQuery = - query.isEmpty() || it.name?.lowercase()?.contains(query.lowercase()) == true + val isCollectionFulfillQuery = + query.isEmpty() || collection.name?.lowercase()?.contains(query.lowercase()) == true - collectionFulfillQuery || assetsFulfillQuery + isCollectionFulfillQuery || isAssetsFulfillQuery }, ) - is NFTCollections.Content.Error -> it.content + is NFTCollections.Content.Error -> collections.content }, ) } @@ -198,12 +166,12 @@ internal class NFTCollectionsModel @Inject constructor( } private fun onExpandCollectionClick(collection: NFTCollection) { - _state.update { + _state.update { stateUM -> ChangeCollectionExpandedStateTransformer( collection = collection, collectionIdProvider = collectionIdProvider, onFirstExpanded = { onFirstExpanded(collection) }, - ).transform(it) + ).transform(stateUM) } } diff --git a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/DefaultNFTComponent.kt b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/DefaultNFTComponent.kt index 6c9581913e..0cbb637c1c 100644 --- a/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/DefaultNFTComponent.kt +++ b/features/nft/impl/src/main/kotlin/com/tangem/features/nft/common/DefaultNFTComponent.kt @@ -20,7 +20,6 @@ import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.navigation.inner.InnerRouter import com.tangem.core.ui.decompose.ComposableBottomSheetComponent import com.tangem.core.ui.decompose.ComposableContentComponent -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.models.PortfolioId import com.tangem.features.account.PortfolioFetcher import com.tangem.features.account.PortfolioSelectorComponent @@ -53,7 +52,6 @@ internal class DefaultNFTComponent @AssistedInject constructor( private val portfolioSelectorComponentFactory: PortfolioSelectorComponent.Factory, private val portfolioSelectorController: PortfolioSelectorController, portfolioFetcherFactory: PortfolioFetcher.Factory, - private val accountsFeatureToggles: AccountsFeatureToggles, ) : NFTComponent, AppComponentContext by appComponentContext { private val stackNavigation = StackNavigation() @@ -66,14 +64,10 @@ internal class DefaultNFTComponent @AssistedInject constructor( private val initialRoute: NFTRoute = NFTRoute.Collections(params.userWalletId) private val currentRoute = MutableStateFlow(initialRoute) private val onReceiveClickJob = JobHolder() - private val portfolioFetcher: PortfolioFetcher? = if (accountsFeatureToggles.isFeatureEnabled) { - portfolioFetcherFactory.create( - mode = PortfolioFetcher.Mode.Wallet(params.userWalletId), - scope = componentScope, - ) - } else { - null - } + private val portfolioFetcher: PortfolioFetcher = portfolioFetcherFactory.create( + mode = PortfolioFetcher.Mode.Wallet(params.userWalletId), + scope = componentScope, + ) private val bottomSheetNavigation: SlotNavigation = SlotNavigation() private val portfolioSelectorCallback = object : PortfolioSelectorComponent.BottomSheetCallback { override val onDismiss: () -> Unit = { bottomSheetNavigation.dismiss() } @@ -83,7 +77,7 @@ internal class DefaultNFTComponent @AssistedInject constructor( source = bottomSheetNavigation, serializer = Unit.serializer(), handleBackButton = false, - childFactory = { configuration, context -> bottomSheetChild(context) }, + childFactory = { _, context -> bottomSheetChild(context) }, ) private val childStack = childStack( @@ -146,17 +140,7 @@ internal class DefaultNFTComponent @AssistedInject constructor( params = NFTCollectionsComponent.Params( userWalletId = route.userWalletId, onBackClick = ::onChildBack, - onReceiveClick = { - if (accountsFeatureToggles.isFeatureEnabled) { - onReceiveClick(route) - } else { - innerRouter.push( - NFTRoute.Receive( - portfolioId = PortfolioId(route.userWalletId), - ), - ) - } - }, + onReceiveClick = { onReceiveClick(route) }, onAssetClick = { asset, collection -> innerRouter.push( NFTRoute.Details( @@ -170,7 +154,6 @@ internal class DefaultNFTComponent @AssistedInject constructor( ) private fun onReceiveClick(route: NFTRoute.Collections) = componentScope.launch { - val portfolioFetcher = requireNotNull(portfolioFetcher) portfolioSelectorController.selectAccount(null) portfolioFetcher.updateMode(mode = PortfolioFetcher.Mode.Wallet(route.userWalletId)) val portfolioData = portfolioFetcher.data.first() @@ -245,7 +228,7 @@ internal class DefaultNFTComponent @AssistedInject constructor( portfolioSelectorComponentFactory.create( context = childByContext(componentContext), params = PortfolioSelectorComponent.Params( - portfolioFetcher = portfolioFetcher!!, + portfolioFetcher = portfolioFetcher, controller = portfolioSelectorController, bsCallback = portfolioSelectorCallback, ), diff --git a/features/staking/impl/detekt-baseline-debug.xml b/features/staking/impl/detekt-baseline-debug.xml index 113f53de5d..8cd2dca55b 100644 --- a/features/staking/impl/detekt-baseline-debug.xml +++ b/features/staking/impl/detekt-baseline-debug.xml @@ -4,7 +4,6 @@ BooleanPropertyNaming:AddStakingNotificationsTransformer.kt$AddStakingNotificationsTransformer$val showNotification = sendingAmount + feeAmount > balance BooleanPropertyNaming:AmountCurrencyChangeStateTransformer.kt$AmountCurrencyChangeStateTransformer$private val value: Boolean - BooleanPropertyNaming:StakingModel.kt$StakingModel$val noBalanceState = balanceState == null BooleanPropertyNaming:StakingUiState.kt$StakingStates.InitialInfoState.Data$val showBanner: Boolean BooleanPropertyNaming:StakingUiState.kt$StakingUiState$val showColdWalletInteractionIcon: Boolean CastNullableToNonNullableType:SetApprovalBottomSheetInProgressTransformer.kt$SetApprovalBottomSheetInProgressTransformer$as @@ -14,28 +13,13 @@ MultilineLambdaItParameter:StakingFeeBlock.kt${ if (it == FeeState.Error) { Text( text = DASH_SIGN, color = TangemTheme.colors.text.primary1, style = TangemTheme.typography.body1, ) } } MultilineLambdaItParameter:StakingFeeBlock.kt${ if (it == FeeState.Loading) { RectangleShimmer( radius = TangemTheme.dimens.radius3, modifier = Modifier.size( height = TangemTheme.dimens.size24, width = TangemTheme.dimens.size90, ), ) } } MultilineLambdaItParameter:StakingInfoNotificationsFactory.kt$StakingInfoNotificationsFactory${ it.type == BalanceType.PREPARING || it.type == BalanceType.STAKED || it.type == BalanceType.LOCKED } - MultilineLambdaItParameter:StakingModel.kt$StakingModel${ EnterAmountBoundary( amount = it, fiatRate = status.value.fiatRate.orZero(), ) } - MultilineLambdaItParameter:StakingModel.kt$StakingModel${ Timber.e(it) false } - MultilineLambdaItParameter:StakingModel.kt$StakingModel${ isBalanceHiddenFlow.value = it.isBalanceHidden stateController.update( transformer = HideBalanceStateTransformer( isBalanceHidden = it.isBalanceHidden, cryptoCurrencyStatus = cryptoCurrencyStatus, appCurrency = appCurrency, ), ) } - MultilineLambdaItParameter:StakingModel.kt$StakingModel${ stateController.update( SetFeeToTonInitializeBottomSheetTransformer( appCurrencyProvider = Provider { appCurrency }, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, fee = it.normal, isFeeApproximate = false, ), ) } MultilineLambdaItParameter:StakingStateController.kt$StakingStateController${ it.copy( showColdWalletInteractionIcon = userWallet is UserWallet.Cold, ) } - NullCheckOnMutableProperty:StakingModel.kt$StakingModel$if (feeCryptoCurrencyStatus != null && fee != null) { getBalanceNotEnoughForFeeWarningUseCase( fee = fee, userWalletId = userWalletId, tokenStatus = cryptoCurrencyStatus, coinStatus = feeCryptoCurrencyStatus ?: cryptoCurrencyStatus, ).getOrNull() } else { null } NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$networkId NullableToStringCall:DefaultStakingDeepLinkHandler.kt$DefaultStakingDeepLinkHandler$$tokenId PropertyUsedBeforeDeclaration:StakingFeeBlock.kt$FeeBlockPreviewProvider$contentState - PropertyUsedBeforeDeclaration:StakingModel.kt$StakingModel$isAmountSubtractAvailable PropertyUsedBeforeDeclaration:StakingStateController.kt$StakingStateController$uiState - SuspendFunSwallowedCancellation:StakingModel.kt$StakingModel$runCatching UnnecessaryEventHandlerParameter:StakingInitialInfoContent.kt$onClick: (BalanceState) -> Unit - UnnecessaryLet:StakingModel.kt$StakingModel$let(::add) UnnecessaryLet:StakingTosText.kt$let { onTextClick(PRIVACY_POLICY_URL) } UnnecessaryLet:StakingTosText.kt$let { onTextClick(TERMS_OF_USE_URL) } - UnsafeCallOnNullableType:StakingModel.kt$StakingModel$tonAccountInitializeTransaction!! - VarCouldBeVal:StakingModel.kt$StakingModel$private var actionsJobHolder: JobHolder = JobHolder() - VarCouldBeVal:StakingModel.kt$StakingModel$private var approvalJobHolder: JobHolder = JobHolder() - VarCouldBeVal:StakingModel.kt$StakingModel$private var feeJobHolder: JobHolder = JobHolder() - VarCouldBeVal:StakingModel.kt$StakingModel$private var sendTransactionJobHolder = JobHolder() - VarCouldBeVal:StakingModel.kt$StakingModel$private var stakingStateRouter: StakingStateRouter = StakingStateRouter( appRouter = appRouter, stateController = stateController, analyticsEventsHandler = analyticsEventHandler, ) - VarCouldBeVal:StakingModel.kt$StakingModel$private var stepChangesJobHolder = JobHolder() diff --git a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt index 2ef0911b14..9a5c3044f1 100644 --- a/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt +++ b/features/staking/impl/src/main/java/com/tangem/features/staking/impl/presentation/model/StakingModel.kt @@ -7,7 +7,6 @@ import com.tangem.blockchain.common.transaction.Fee import com.tangem.blockchain.common.transaction.TransactionFee import com.tangem.common.getValidatorsCount import com.tangem.common.routing.AppRouter -import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer import com.tangem.common.ui.amountScreen.converters.AmountReduceByTransformer.ReduceByData import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.amountScreen.models.EnterAmountBoundary @@ -28,7 +27,6 @@ import com.tangem.core.ui.format.bigdecimal.format import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.VibratorHapticManager import com.tangem.core.ui.message.DialogMessage -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -113,7 +111,6 @@ internal class StakingModel @Inject constructor( private val stateController: StakingStateController, override val dispatchers: CoroutineDispatcherProvider, private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, - private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase, private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, @@ -149,7 +146,6 @@ internal class StakingModel @Inject constructor( @DelayedWork private val coroutineScope: CoroutineScope, private val innerRouter: InnerStakingRouter, private val messageSender: UiMessageSender, - private val accountsFeatureToggles: AccountsFeatureToggles, appRouter: AppRouter, ) : Model(), StakingClickIntents { @@ -158,7 +154,7 @@ internal class StakingModel @Inject constructor( private val params = paramsContainer.require() - private var stakingStateRouter: StakingStateRouter = StakingStateRouter( + private val stakingStateRouter: StakingStateRouter = StakingStateRouter( appRouter = appRouter, stateController = stateController, analyticsEventsHandler = analyticsEventHandler, @@ -237,6 +233,7 @@ internal class StakingModel @Inject constructor( ) } + @Suppress("PropertyUsedBeforeDeclaration") private val transactionSender: StakingTransactionSender by lazy(LazyThreadSafetyMode.NONE) { stakingOperationsFactory.createTransactionSender( cryptoCurrencyStatus = cryptoCurrencyStatus, @@ -298,17 +295,17 @@ internal class StakingModel @Inject constructor( override fun onNextClick(balanceState: BalanceState?) { modelScope.launch { val isInitialInfoStep = value.currentStep == StakingStep.InitialInfo - val noBalanceState = balanceState == null + val isBalanceAbsent = balanceState == null val hasNoYieldBalanceData = cryptoCurrencyStatus.value.stakingBalance !is StakingBalance.Data.StakeKit when { - isInitialInfoStep && noBalanceState && integration.areAllTargetsFull && hasNoYieldBalanceData -> { + isInitialInfoStep && isBalanceAbsent && integration.areAllTargetsFull && hasNoYieldBalanceData -> { stakingEventFactory.createStakingValidatorsUnavailableAlert() return@launch } - isInitialInfoStep && noBalanceState -> { + isInitialInfoStep && isBalanceAbsent -> { val list = buildList { - SetConfirmationStateInitTransformer( + val setConfirmationStateInitTransformer = SetConfirmationStateInitTransformer( isEnter = true, isExplicitExit = false, balanceState = null, @@ -316,13 +313,17 @@ internal class StakingModel @Inject constructor( stakingApproval = stakingApproval, stakingAllowance = stakingAllowance, integration = integration, - ).let(::add) + ) + + add(setConfirmationStateInitTransformer) + if (integration.isPartialAmountDisabled) { - ValidatorSelectChangeTransformer( + val validatorSelectChangeTransformer = ValidatorSelectChangeTransformer( selectedTarget = integration.preferredTargets.firstOrNull(), integration = integration, - ).let(::add) - SetAmountDataTransformer( + ) + + val setAmountDataTransformer = SetAmountDataTransformer( clickIntents = this@StakingModel, cryptoCurrencyStatusProvider = Provider { cryptoCurrencyStatus }, userWalletProvider = Provider { userWallet }, @@ -330,15 +331,25 @@ internal class StakingModel @Inject constructor( isBalanceHidden = isBalanceHiddenFlow.value, isAccountsModeEnabled = isAccountsModeEnabled, account = account, - ).let(::add) - AmountMaxValueStateTransformer( + ) + + val amountMaxValueStateTransformer = AmountMaxValueStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, minimumTransactionAmount = minimumTransactionAmount, actionType = uiState.value.actionType, integration = integration, - ).let(::add) + ) + + addAll( + listOf( + validatorSelectChangeTransformer, + setAmountDataTransformer, + amountMaxValueStateTransformer, + ), + ) } } + stateController.updateAll(*list.toTypedArray()) } } @@ -900,7 +911,7 @@ internal class StakingModel @Inject constructor( AmountReduceByStateTransformer( cryptoCurrencyStatus = cryptoCurrencyStatus, minimumTransactionAmount = minimumTransactionAmount, - value = AmountReduceByTransformer.ReduceByData( + value = ReduceByData( reduceAmountBy = reduceAmountBy, reduceAmountByDiff = reduceAmountByDiff, ), @@ -953,7 +964,7 @@ internal class StakingModel @Inject constructor( task = PeriodicTask( delay = ALLOWANCE_UPDATE_DELAY, task = { - runCatching { + runSuspendCatching { getAllowanceUseCase( userWalletId = userWalletId, cryptoCurrency = cryptoCurrencyStatus.currency, @@ -1073,17 +1084,20 @@ internal class StakingModel @Inject constructor( network = cryptoCurrencyStatus.currency.network, memo = null, ) - tonAccountInitializeTransaction = transaction.getOrElse { + + val initialTransaction = transaction.getOrElse { stateController.update( SetFeeErrorToTonInitializeBottomSheetTransformer(), ) return@launch } + tonAccountInitializeTransaction = initialTransaction + val transactionFee = getFeeUseCase( userWallet = userWallet, network = cryptoCurrencyStatus.currency.network, - transactionData = tonAccountInitializeTransaction!!, + transactionData = initialTransaction, ) transactionFee.fold( @@ -1092,12 +1106,12 @@ internal class StakingModel @Inject constructor( SetFeeErrorToTonInitializeBottomSheetTransformer(), ) }, - ifRight = { + ifRight = { fee -> stateController.update( SetFeeToTonInitializeBottomSheetTransformer( appCurrencyProvider = Provider { appCurrency }, feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, - fee = it.normal, + fee = fee.normal, isFeeApproximate = false, ), ) @@ -1190,41 +1204,20 @@ internal class StakingModel @Inject constructor( } private fun subscribeOnCurrencyStatusUpdates() { - if (accountsFeatureToggles.isFeatureEnabled) { - getAccountCurrencyStatusUseCase( - userWalletId = params.userWalletId, - currency = params.cryptoCurrency, - ).conflate().distinctUntilChanged() - .filter { - value.currentStep == StakingStep.InitialInfo || isTopHeatupCase() - }.onEach { (maybeAccount, maybeStatus) -> - isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync() - account = maybeAccount - onDataLoaded(maybeStatus) - }.flowOn(dispatchers.main) - .launchIn(modelScope) - } else { - getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( - userWalletId = userWalletId, - currencyId = cryptoCurrencyId, - isSingleWalletWithTokens = false, - ).conflate().distinctUntilChanged() - .filter { - value.currentStep == StakingStep.InitialInfo || isTopHeatupCase() - } - .onEach { maybeStatus -> - maybeStatus.fold( - ifRight = { onDataLoaded(it) }, - ifLeft = { error -> - stakingEventFactory.createGenericErrorAlert(error.toString()) - stateController.update( - SetConfirmationStateResetAssentTransformer(cryptoCurrencyStatus = cryptoCurrencyStatus), - ) - }, - ) - }.flowOn(dispatchers.main) - .launchIn(modelScope) - } + getAccountCurrencyStatusUseCase( + userWalletId = params.userWalletId, + currency = params.cryptoCurrency, + ) + .conflate() + .distinctUntilChanged() + .filter { value.currentStep == StakingStep.InitialInfo || isTopHeatupCase() } + .onEach { (maybeAccount, maybeStatus) -> + isAccountsModeEnabled = isAccountsModeEnabledUseCase.invokeSync() + account = maybeAccount + onDataLoaded(maybeStatus) + } + .flowOn(dispatchers.main) + .launchIn(modelScope) } private suspend fun onDataLoaded(status: CryptoCurrencyStatus) { @@ -1241,12 +1234,11 @@ internal class StakingModel @Inject constructor( ) } - feeCryptoCurrencyStatus = - getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull() - minimumTransactionAmount = - getMinimumTransactionAmountSyncUseCase(userWalletId, status).getOrNull()?.let { + feeCryptoCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull() + minimumTransactionAmount = getMinimumTransactionAmountSyncUseCase(userWalletId, status).getOrNull() + ?.let { amount -> EnterAmountBoundary( - amount = it, + amount = amount, fiatRate = status.value.fiatRate.orZero(), ) } @@ -1265,11 +1257,11 @@ internal class StakingModel @Inject constructor( getBalanceHidingSettingsUseCase() .conflate() .distinctUntilChanged() - .onEach { - isBalanceHiddenFlow.value = it.isBalanceHidden + .onEach { settings -> + isBalanceHiddenFlow.value = settings.isBalanceHidden stateController.update( transformer = HideBalanceStateTransformer( - isBalanceHidden = it.isBalanceHidden, + isBalanceHidden = settings.isBalanceHidden, cryptoCurrencyStatus = cryptoCurrencyStatus, appCurrency = appCurrency, ), @@ -1403,8 +1395,8 @@ internal class StakingModel @Inject constructor( val isAccountInitializedNewValue = checkAccountInitializedUseCase.invoke( userWalletId = userWalletId, network = cryptoCurrencyStatus.currency.network, - ).getOrElse { - Timber.e(it) + ).getOrElse { throwable -> + Timber.e(throwable) false } diff --git a/features/tokendetails/impl/detekt-baseline-debug.xml b/features/tokendetails/impl/detekt-baseline-debug.xml index 71ab124fb2..23e783a751 100644 --- a/features/tokendetails/impl/detekt-baseline-debug.xml +++ b/features/tokendetails/impl/detekt-baseline-debug.xml @@ -13,17 +13,8 @@ BooleanPropertyNaming:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$val showProviderLink = getShowProviderLink(notification, statusModel) BooleanPropertyNaming:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$val showProviderLink = getShowProviderLink(notification, transaction.status) BooleanPropertyNaming:TokenDetailsTopAppBar.kt$var showDropdownMenu by rememberSaveable { mutableStateOf(false) } - MultilineLambdaItParameter:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler${ val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true) val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true val isDefaultDerivation = it.network.derivationPath is Network.DerivationPath.Card val isCustomDerivation = derivationPath?.equals(it.network.derivationPath.value) == true val isCorrectDerivation = isDefaultDerivation || isCustomDerivation isNetwork && isCurrency && isCorrectDerivation } MultilineLambdaItParameter:ExpressStatusFactory.kt$ExpressStatusFactory${ when (it) { is ExpressTransactionStateUM.OnrampUM -> it.activeStatus.isHidden else -> false } } MultilineLambdaItParameter:OnrampStatusFactory.kt$OnrampStatusFactory${ Timber.e("Couldn't update onramp status. $it") onrampTx } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ Timber.e(it.cause?.localizedMessage.orEmpty()) "" } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ analyticsEventsHandler.send( TokenReceiveAnalyticsEvent.ButtonShareAddress(cryptoCurrency.symbol), ) shareManager.shareText(text = it) } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ analyticsEventsHandler.send(TokenReceiveAnalyticsEvent.ButtonCopyAddress(cryptoCurrency.symbol)) clipboardManager.setText(text = it, isSensitive = true) } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ analyticsExceptionHandler.sendException( event = ExceptionAnalyticsEvent( exception = it, params = mapOf( "blockchainId" to cryptoCurrency.network.id.rawId.value, "networkId" to cryptoCurrency.network.backendId, ), ), ) Timber.e( /* t = */ it, /* message = */ "Unable to get wallet manager for user wallet %s and network %s", /* ...args = */ userWalletId, cryptoCurrency.network, ) false } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ internalUiState.value = stateFactory.getStateWithErrorDialog(stringReference(it)) Timber.e(it) } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ internalUiState.value = stateFactory.getStateWithUpdatedHidden( isBalanceHidden = it.isBalanceHidden, ) } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ sendButtonsEvents(it.states) internalUiState.value = stateFactory.getManageButtonsState(actions = it.states) } - MultilineLambdaItParameter:TokenDetailsModel.kt$TokenDetailsModel${ val updatedState = stateFactory.getStateWithNotifications(it) notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications) internalUiState.value = updatedState } MultilineLambdaItParameter:TokenDetailsScreen.kt${ Notification( modifier = itemModifier.animateItem(), config = it.config, iconTint = when (it) { is TokenDetailsNotification.Informational -> TangemTheme.colors.icon.accent is TokenDetailsNotification.UsedOutdatedData -> TangemTheme.colors.text.attention else -> null }, ) } MultilineLambdaItParameter:TokenDetailsTopAppBar.kt${ TangemDropdownItem( item = it, dismissParent = { showDropdownMenu = false }, ) } MultilineLambdaItParameter:TokenStakingBlock.kt${ when (it) { is StakingBlockUM.TemporaryUnavailable -> StakingTemporaryUnavailableBlock() is StakingBlockUM.Loading -> StakingLoading() is StakingBlockUM.Staked -> StakingBalanceBlock( state = it, isBalanceHidden = isBalanceHidden, ) is StakingBlockUM.StakeAvailable -> StakingAvailableContent( state = it, ) } } @@ -31,21 +22,15 @@ NamedArguments:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$createStateInfo( transaction, toCryptoCurrency, fromCryptoCurrency, toFiatAmount, fromFiatAmount, ) NestedScopeFunctions:TokenDetailsBalanceSelectStateConverter.kt$TokenDetailsBalanceSelectStateConverter$let { cryptoCurrencyStatus.value.fiatRate?.multiply(it) } NullableBooleanCheck:TokenDetailsSwapTransactionsStateConverter.kt$TokenDetailsSwapTransactionsStateConverter$transaction.status?.hasLongTime ?: false - NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$networkId - NullableToStringCall:DefaultTokenDetailsDeepLinkHandler.kt$DefaultTokenDetailsDeepLinkHandler$$tokenId NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingCryptoAmount NullableToStringCall:TokenDetailsStakingInfoConverter.kt$TokenDetailsStakingInfoConverter$$stakingEntryInfo PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$network PropertyUsedBeforeDeclaration:ExpressStatusBottomSheetStateProvider.kt$ExpressStatusBottomSheetStateProvider$token - PropertyUsedBeforeDeclaration:TokenDetailsModel.kt$TokenDetailsModel$uiState SuspendFunSwallowedCancellation:ExchangeStatusFactory.kt$ExchangeStatusFactory$runCatching - SuspendFunSwallowedCancellation:TokenDetailsModel.kt$TokenDetailsModel$runCatching - UnnecessaryLet:TokenDetailsModel.kt$TokenDetailsModel$let { internalUiState.value = stateFactory.getStateWithErrorDialog(message) } UnnecessaryLet:TokenDetailsSkeletonStateConverter.kt$TokenDetailsSkeletonStateConverter$let(::add) UnnecessaryLet:TokenDetailsStateFactory.kt$TokenDetailsStateFactory$let(::add) UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent$mapOf() UseEmptyCounterpart:TokenDetailsAnalyticsEvent.kt$TokenDetailsAnalyticsEvent.Notice$mapOf() UseOrEmpty:ExchangeStatusFactory.kt$ExchangeStatusFactory$savedTransactions ?.flatMap { setOf(it.fromCryptoCurrency.id, it.toCryptoCurrency.id) } ?.toSet() ?.getQuotesOrEmpty() ?: emptySet() - VarCouldBeVal:TokenDetailsModel.kt$TokenDetailsModel$private var expressTxStatusTaskScheduler = SingleTaskScheduler<PersistentList<ExpressTransactionStateUM>>() diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt index 4992503c8a..64c0be0735 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/deeplink/DefaultTokenDetailsDeepLinkHandler.kt @@ -9,7 +9,6 @@ import com.tangem.common.routing.deeplink.DeeplinkConst.TRANSACTION_ID_KEY import com.tangem.common.routing.deeplink.DeeplinkConst.TYPE_KEY import com.tangem.common.routing.deeplink.DeeplinkConst.WALLET_ID_KEY import com.tangem.core.analytics.api.AnalyticsEventHandler -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.network.Network @@ -18,7 +17,10 @@ import com.tangem.domain.models.wallet.UserWalletId import com.tangem.domain.models.wallet.isLocked import com.tangem.domain.models.wallet.isMultiCurrency import com.tangem.domain.notifications.models.NotificationType -import com.tangem.domain.tokens.* +import com.tangem.domain.tokens.FetchCurrencyStatusUseCase +import com.tangem.domain.tokens.GetCryptoCurrencyUseCase +import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesProducer +import com.tangem.domain.tokens.MultiWalletCryptoCurrenciesSupplier import com.tangem.domain.tokens.wallet.WalletBalanceFetcher import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.domain.wallets.usecase.SelectWalletUseCase @@ -40,7 +42,6 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor( @Assisted private val isFromOnNewIntent: Boolean, private val appRouter: AppRouter, private val selectWalletUseCase: SelectWalletUseCase, - private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase, private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val tokenDetailsDeepLinkActionTrigger: TokenDetailsDeepLinkActionTrigger, @@ -48,7 +49,6 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor( private val analyticsEventHandler: AnalyticsEventHandler, private val getUserWalletUseCase: GetUserWalletUseCase, private val walletBalanceFetcher: WalletBalanceFetcher, - private val accountsFeatureToggles: AccountsFeatureToggles, private val tangemPayFeatureToggles: TangemPayFeatureToggles, private val multiWalletCryptoCurrenciesSupplier: MultiWalletCryptoCurrenciesSupplier, ) : TokenDetailsDeepLinkHandler { @@ -142,12 +142,12 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor( if (userWallet.isMultiCurrency) { val derivationPath = queryParams[DERIVATION_PATH_KEY] - getCryptoCurrencies(userWalletId = userWallet.walletId)?.firstOrNull { - val isNetwork = it.network.backendId.equals(networkId, ignoreCase = true) - val isCurrency = it.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true + getCryptoCurrencies(userWalletId = userWallet.walletId)?.firstOrNull { currency -> + val isNetwork = currency.network.backendId.equals(networkId, ignoreCase = true) + val isCurrency = currency.id.rawCurrencyId?.value?.equals(tokenId, ignoreCase = true) == true - val isDefaultDerivation = it.network.derivationPath is Network.DerivationPath.Card - val isCustomDerivation = derivationPath?.equals(it.network.derivationPath.value) == true + val isDefaultDerivation = currency.network.derivationPath is Network.DerivationPath.Card + val isCustomDerivation = derivationPath?.equals(currency.network.derivationPath.value) == true val isCorrectDerivation = isDefaultDerivation || isCustomDerivation isNetwork && isCurrency && isCorrectDerivation } @@ -156,14 +156,9 @@ internal class DefaultTokenDetailsDeepLinkHandler @AssistedInject constructor( } private suspend fun getCryptoCurrencies(userWalletId: UserWalletId): List? { - return if (accountsFeatureToggles.isFeatureEnabled) { - multiWalletCryptoCurrenciesSupplier.getSyncOrNull( - params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId), - ) - ?.toList() - } else { - getCryptoCurrenciesUseCase(userWalletId = userWalletId).getOrNull() - } + return multiWalletCryptoCurrenciesSupplier.getSyncOrNull( + params = MultiWalletCryptoCurrenciesProducer.Params(userWalletId = userWalletId), + )?.toList() } @AssistedFactory diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt index 5e7a0d7096..bd24cb6cc5 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/ExpressTransactionsModel.kt @@ -2,24 +2,20 @@ package com.tangem.feature.tokendetails.presentation.tokendetails.model import androidx.compose.runtime.Stable import arrow.core.getOrElse -import arrow.core.right import com.tangem.common.ui.expressStatus.ExpressStatusBottomSheetConfig import com.tangem.common.ui.expressStatus.state.ExpressTransactionStateUM import com.tangem.common.ui.expressStatus.state.ExpressTransactionsBlockState import com.tangem.core.decompose.di.ModelScoped import com.tangem.core.decompose.model.Model import com.tangem.core.decompose.model.ParamsContainer -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.card.common.util.cardTypesResolver import com.tangem.domain.models.account.Account import com.tangem.domain.models.currency.CryptoCurrency import com.tangem.domain.models.currency.CryptoCurrencyStatus import com.tangem.domain.models.wallet.UserWallet import com.tangem.domain.models.wallet.UserWalletId -import com.tangem.domain.tokens.GetSingleCryptoCurrencyStatusUseCase import com.tangem.domain.wallets.usecase.GetUserWalletUseCase import com.tangem.feature.tokendetails.presentation.router.InnerTokenDetailsRouter import com.tangem.feature.tokendetails.presentation.tokendetails.state.factory.ExpressStateFactory @@ -43,10 +39,8 @@ internal class ExpressTransactionsModel @Inject constructor( paramsContainer: ParamsContainer, expressStatusFactory: ExpressStatusFactory.Factory, getUserWalletUseCase: GetUserWalletUseCase, - private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val router: InnerTokenDetailsRouter, - private val accountsFeatureToggles: AccountsFeatureToggles, private val getAccountCryptoCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, private val expressTransactionsEventListener: ExpressTransactionsEventListener, ) : Model(), ExpressTransactionsClickIntents { @@ -163,22 +157,10 @@ internal class ExpressTransactionsModel @Inject constructor( } private fun subscribeOnCurrencyStatusUpdates() { - if (accountsFeatureToggles.isFeatureEnabled) { - getAccountCryptoCurrencyStatusUseCase(userWalletId, cryptoCurrency) - .onEach { account = it.account } - .map { it.status.right() } - } else { - getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - isSingleWalletWithTokens = userWallet is UserWallet.Cold && - userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), - ) - } + getAccountCryptoCurrencyStatusUseCase(userWalletId, cryptoCurrency) + .onEach { account = it.account } .distinctUntilChanged() - .onEach { maybeCurrencyStatus -> - maybeCurrencyStatus.onRight { status -> cryptoCurrencyStatus = status } - } + .onEach { cryptoCurrencyStatus = it.status } .flowOn(dispatchers.main) .launchIn(modelScope) .saveIn(marketPriceJobHolder) diff --git a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt index 17c4f1ad60..2f8b9f76ed 100644 --- a/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt +++ b/features/tokendetails/impl/src/main/kotlin/com/tangem/feature/tokendetails/presentation/tokendetails/model/TokenDetailsModel.kt @@ -33,7 +33,6 @@ import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.haptic.TangemHapticEffect import com.tangem.core.ui.haptic.VibratorHapticManager import com.tangem.core.ui.message.SnackbarMessage -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.status.usecase.GetAccountCurrencyStatusUseCase import com.tangem.domain.account.status.usecase.ManageCryptoCurrenciesUseCase import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase @@ -105,12 +104,11 @@ import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject -@Suppress("LongParameterList", "LargeClass", "TooManyFunctions") +@Suppress("LongParameterList", "LargeClass", "TooManyFunctions", "PropertyUsedBeforeDeclaration") @Stable @ModelScoped internal class TokenDetailsModel @Inject constructor( override val dispatchers: CoroutineDispatcherProvider, - private val getSingleCryptoCurrencyStatusUseCase: GetSingleCryptoCurrencyStatusUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val fetchCurrencyStatusUseCase: FetchCurrencyStatusUseCase, private val getExploreUrlUseCase: GetExploreUrlUseCase, @@ -148,7 +146,6 @@ internal class TokenDetailsModel @Inject constructor( private val saveViewedYieldSupplyWarningUseCase: SaveViewedYieldSupplyWarningUseCase, private val saveViewedTokenReceiveWarningUseCase: SaveViewedTokenReceiveWarningUseCase, private val needShowYieldSupplyDepositedWarningUseCase: NeedShowYieldSupplyDepositedWarningUseCase, - private val accountsFeatureToggles: AccountsFeatureToggles, private val getAccountCryptoCurrencyStatusUseCase: GetAccountCurrencyStatusUseCase, private val manageCryptoCurrenciesUseCase: ManageCryptoCurrenciesUseCase, private val yieldSupplyGetRewardsBalanceUseCase: YieldSupplyGetRewardsBalanceUseCase, @@ -177,7 +174,7 @@ internal class TokenDetailsModel @Inject constructor( private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null private var account: Account.CryptoPortfolio? = null private var isBalanceLoadedEventSent = false - private var expressTxStatusTaskScheduler = SingleTaskScheduler>() + private val expressTxStatusTaskScheduler = SingleTaskScheduler>() /** Transaction id to check for status */ private val waitForFirstExpressStatusEmmit = MutableStateFlow(false) @@ -265,21 +262,13 @@ internal class TokenDetailsModel @Inject constructor( private fun initButtons() { // we need also init buttons before start all loading to avoid buttons blocking modelScope.launch { - val currentCryptoCurrencyStatus = if (accountsFeatureToggles.isFeatureEnabled) { - getAccountCryptoCurrencyStatusUseCase.invokeSync( - userWalletId = userWalletId, - currency = cryptoCurrency, - ) - .onSome { account = it.account } - .getOrNull() - ?.status - } else { - getSingleCryptoCurrencyStatusUseCase.invokeMultiWalletSync( - userWalletId = userWalletId, - cryptoCurrencyId = cryptoCurrency.id, - isSingleWalletWithTokens = false, - ).getOrNull() - } + val currentCryptoCurrencyStatus = getAccountCryptoCurrencyStatusUseCase.invokeSync( + userWalletId = userWalletId, + currency = cryptoCurrency, + ) + .onSome { account = it.account } + .getOrNull() + ?.status currentCryptoCurrencyStatus?.let { status -> cryptoCurrencyStatus = status @@ -296,9 +285,9 @@ internal class TokenDetailsModel @Inject constructor( private fun handleBalanceHiding() { getBalanceHidingSettingsUseCase() - .onEach { + .onEach { settings -> internalUiState.value = stateFactory.getStateWithUpdatedHidden( - isBalanceHidden = it.isBalanceHidden, + isBalanceHidden = settings.isBalanceHidden, ) } .launchIn(modelScope) @@ -311,9 +300,9 @@ internal class TokenDetailsModel @Inject constructor( ) .conflate() .distinctUntilChanged() - .onEach { - sendButtonsEvents(it.states) - internalUiState.value = stateFactory.getManageButtonsState(actions = it.states) + .onEach { state -> + sendButtonsEvents(state.states) + internalUiState.value = stateFactory.getManageButtonsState(actions = state.states) } .flowOn(dispatchers.main) .launchIn(modelScope) @@ -345,8 +334,8 @@ internal class TokenDetailsModel @Inject constructor( userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), ) .distinctUntilChanged() - .onEach { - val updatedState = stateFactory.getStateWithNotifications(it) + .onEach { warnings -> + val updatedState = stateFactory.getStateWithNotifications(warnings) notificationsAnalyticsSender.send(internalUiState.value, updatedState.notifications) internalUiState.value = updatedState } @@ -356,18 +345,9 @@ internal class TokenDetailsModel @Inject constructor( } private fun subscribeOnCurrencyStatusUpdates() { - if (accountsFeatureToggles.isFeatureEnabled) { - getAccountCryptoCurrencyStatusUseCase(userWalletId, cryptoCurrency) - .onEach { account = it.account } - .map { it.status.right() } - } else { - getSingleCryptoCurrencyStatusUseCase.invokeMultiWallet( - userWalletId = userWalletId, - currencyId = cryptoCurrency.id, - isSingleWalletWithTokens = userWallet is UserWallet.Cold && - userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(), - ) - } + getAccountCryptoCurrencyStatusUseCase(userWalletId, cryptoCurrency) + .onEach { account = it.account } + .map { it.status.right() } .distinctUntilChanged() .onEach { maybeCurrencyStatus -> internalUiState.value = stateFactory.getCurrencyLoadedBalanceState(maybeCurrencyStatus) @@ -402,7 +382,7 @@ internal class TokenDetailsModel @Inject constructor( isDelayFirst = false, delay = EXPRESS_STATUS_UPDATE_DELAY, task = { - runCatching { + runSuspendCatching { expressStatusFactory.getUpdatedExpressStatuses(internalUiState.value.expressTxs) } }, @@ -508,10 +488,10 @@ internal class TokenDetailsModel @Inject constructor( userWalletId = userWalletId, network = cryptoCurrency.network, ) - .mapLeft { + .mapLeft { throwable -> analyticsExceptionHandler.sendException( event = ExceptionAnalyticsEvent( - exception = it, + exception = throwable, params = mapOf( "blockchainId" to cryptoCurrency.network.id.rawId.value, "networkId" to cryptoCurrency.network.backendId, @@ -520,7 +500,7 @@ internal class TokenDetailsModel @Inject constructor( ) Timber.e( - /* t = */ it, + /* t = */ throwable, /* message = */ "Unable to get wallet manager for user wallet %s and network %s", /* ...args = */ userWalletId, cryptoCurrency.network, @@ -674,8 +654,8 @@ internal class TokenDetailsModel @Inject constructor( userWalletId, cryptoCurrency.network, ).fold( - ifLeft = { - Timber.e(it.cause?.localizedMessage.orEmpty()) + ifLeft = { throwable -> + Timber.e(throwable.cause?.localizedMessage.orEmpty()) "" }, ifRight = { it }, @@ -976,9 +956,9 @@ internal class TokenDetailsModel @Inject constructor( } } } - message?.let { - internalUiState.value = stateFactory.getStateWithErrorDialog(stringReference(it)) - Timber.e(it) + if (message != null) { + internalUiState.value = stateFactory.getStateWithErrorDialog(stringReference(message)) + Timber.e(message) } }, ifRight = { @@ -1019,7 +999,10 @@ internal class TokenDetailsModel @Inject constructor( is SendTransactionError.UnknownError -> error.ex?.localizedMessage }?.let { stringReference(it) } } - message?.let { internalUiState.value = stateFactory.getStateWithErrorDialog(message) } + + if (message != null) { + internalUiState.value = stateFactory.getStateWithErrorDialog(message) + } }, ifRight = { internalUiState.value = stateFactory.getStateWithRemovedRequiredTrustlineNotification() }, ) diff --git a/features/wallet-settings/impl/detekt-baseline-debug.xml b/features/wallet-settings/impl/detekt-baseline-debug.xml deleted file mode 100644 index ecf2e0cce8..0000000000 --- a/features/wallet-settings/impl/detekt-baseline-debug.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt index 3b7692fe1f..34f48f98ba 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/component/impl/DefaultWalletSettingsComponent.kt @@ -22,7 +22,6 @@ import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.EventMessageAction import com.tangem.core.ui.utils.requestPermission import com.tangem.datasource.local.accounts.AccountTokenMigrationStore -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.feature.walletsettings.component.NetworksAvailableForNotificationsComponent import com.tangem.feature.walletsettings.component.RenameWalletComponent import com.tangem.feature.walletsettings.component.WalletSettingsComponent @@ -49,7 +48,6 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor( private val renameWalletComponentFactory: RenameWalletComponent.Factory, private val networksAvailableForNotificationsComponent: NetworksAvailableForNotificationsComponent.Factory, private val accountTokenMigrationStore: AccountTokenMigrationStore, - private val accountsFeatureToggles: AccountsFeatureToggles, ) : WalletSettingsComponent, AppComponentContext by context { private val model: WalletSettingsModel = getOrCreateModel(params) @@ -74,11 +72,7 @@ internal class DefaultWalletSettingsComponent @AssistedInject constructor( init { lifecycle.subscribe( - onResume = { - if (accountsFeatureToggles.isFeatureEnabled) { - showMigrationAlertIfNeeded() - } - }, + onResume = { showMigrationAlertIfNeeded() }, onPause = { accountMigrationJobHolder.cancel() }, ) } diff --git a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt index b6b47ea825..56be825aca 100644 --- a/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt +++ b/features/wallet-settings/impl/src/main/kotlin/com/tangem/feature/walletsettings/utils/AccountItemsDelegate.kt @@ -12,7 +12,6 @@ import com.tangem.core.ui.extensions.resourceReference import com.tangem.core.ui.extensions.wrappedList import com.tangem.core.ui.message.DialogMessage import com.tangem.core.ui.message.EventMessageAction -import com.tangem.domain.account.featuretoggle.AccountsFeatureToggles import com.tangem.domain.account.models.AccountList import com.tangem.domain.account.models.AccountStatusList import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier @@ -46,13 +45,12 @@ internal class AccountItemsDelegate @Inject constructor( private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase, private val accountListSortingSaver: AccountListSortingSaver, - private val accountsFeatureToggles: AccountsFeatureToggles, private val analyticsEventHandler: AnalyticsEventHandler, ) { private val userWalletId = paramsContainer.require().userWalletId - fun isAccountsSupported(wallet: UserWallet) = accountsFeatureToggles.isFeatureEnabled && wallet.isAccountsSupported + fun isAccountsSupported(wallet: UserWallet) = wallet.isAccountsSupported fun loadAccount(wallet: UserWallet): Flow> { if (!isAccountsSupported(wallet)) return flowOf(emptyList()) diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt index 8abda2b315..69217500ac 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/loaders/implementors/MultiWalletContentLoader.kt @@ -11,7 +11,7 @@ import dagger.assisted.AssistedInject internal class MultiWalletContentLoader @AssistedInject constructor( @Assisted private val userWallet: UserWallet, private val accountListSubscriberFactory: AccountListSubscriber.Factory, - private val walletNFTListSubscriberFactory: WalletNFTListSubscriberV2.Factory, + private val walletNFTListSubscriberFactory: WalletNFTListSubscriber.Factory, private val checkWalletWithFundsSubscriberFactory: CheckWalletWithFundsSubscriber.Factory, private val multiWalletWarningsSubscriberFactory: MultiWalletWarningsSubscriber.Factory, private val walletNotificationsSubscriberFactory: WalletNotificationsSubscriber.Factory, diff --git a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNFTListSubscriberV2.kt b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNFTListSubscriber.kt similarity index 94% rename from features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNFTListSubscriberV2.kt rename to features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNFTListSubscriber.kt index 88ccbc686f..500b3bcd98 100644 --- a/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNFTListSubscriberV2.kt +++ b/features/wallet/impl/src/main/java/com/tangem/feature/wallet/presentation/wallet/subscribers/WalletNFTListSubscriber.kt @@ -15,7 +15,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* -internal class WalletNFTListSubscriberV2 @AssistedInject constructor( +internal class WalletNFTListSubscriber @AssistedInject constructor( @Assisted override val userWallet: UserWallet, override val singleAccountStatusListSupplier: SingleAccountStatusListSupplier, private val walletsRepository: WalletsRepository, @@ -35,7 +35,7 @@ internal class WalletNFTListSubscriberV2 @AssistedInject constructor( // if NFT is enabled for this wallet and there are currencies, // then start observing changes from store and apply transformer if need if (nftEnabled && currencies.isNotEmpty()) { - getNFTCollectionsUseCase.invokeForAccounts(userWallet.walletId) + getNFTCollectionsUseCase(userWallet.walletId) .shareIn( scope = coroutineScope, started = SharingStarted.WhileSubscribed(), @@ -61,6 +61,6 @@ internal class WalletNFTListSubscriberV2 @AssistedInject constructor( @AssistedFactory interface Factory { - fun create(userWallet: UserWallet): WalletNFTListSubscriberV2 + fun create(userWallet: UserWallet): WalletNFTListSubscriber } } \ No newline at end of file