From 65d2e84c6ec88e2a032373bf62d0fe1e419fd4b5 Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 14 Jul 2026 10:03:41 +0200 Subject: [PATCH] Updated on 2026-08-14 --- .../block/model/ContactsBlockModel.kt | 7 +++--- .../block/model/ContactsBlockModelTest.kt | 21 ++++++++++------ .../destination/model/SendDestinationModel.kt | 17 ++++++------- .../model/SendDestinationModelTest.kt | 25 +++++++++---------- 4 files changed, 37 insertions(+), 33 deletions(-) diff --git a/features/address-book/impl/src/main/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModel.kt b/features/address-book/impl/src/main/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModel.kt index f5c36a478b..8b4a3c1704 100644 --- a/features/address-book/impl/src/main/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModel.kt +++ b/features/address-book/impl/src/main/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModel.kt @@ -3,7 +3,7 @@ package com.tangem.features.addressbook.block.model 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.addressbook.usecase.GetContactsUseCase +import com.tangem.domain.addressbook.interactor.GetVerifiedContactsInteractor import com.tangem.domain.addressbook.usecase.SyncAddressBooksUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase import com.tangem.features.addressbook.AddressBookContactsBlockComponent @@ -28,7 +28,7 @@ internal class ContactsBlockModel @Inject constructor( private val stateController: ContactsBlockStateController, private val analyticsSender: AddressBookAnalyticsSender, private val syncAddressBooksUseCase: SyncAddressBooksUseCase, - getContactsUseCase: GetContactsUseCase, + getVerifiedContactsInteractor: GetVerifiedContactsInteractor, getWalletsUseCase: GetWalletsUseCase, ) : Model() { @@ -41,7 +41,8 @@ internal class ContactsBlockModel @Inject constructor( combine( params.queryFlow.flatMapLatest { query -> - getContactsUseCase(query = query, userWalletId = null) + getVerifiedContactsInteractor.getVerifiedContacts(query = query, userWalletId = null) + .map { verified -> verified.map { it.contact } } }, getWalletsUseCase.invokeAsMap(isOnlyMultiCurrency = false, filterLocked = true), ) { contacts, wallets -> contacts to wallets.values.toList() } diff --git a/features/address-book/impl/src/test/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModelTest.kt b/features/address-book/impl/src/test/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModelTest.kt index 1f7e03f325..ab3bf01ef1 100644 --- a/features/address-book/impl/src/test/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModelTest.kt +++ b/features/address-book/impl/src/test/kotlin/com/tangem/features/addressbook/block/model/ContactsBlockModelTest.kt @@ -2,8 +2,8 @@ package com.tangem.features.addressbook.block.model import com.google.common.truth.Truth.assertThat import com.tangem.core.decompose.model.MutableParamsContainer +import com.tangem.domain.addressbook.interactor.GetVerifiedContactsInteractor import com.tangem.domain.addressbook.model.* -import com.tangem.domain.addressbook.usecase.GetContactsUseCase import com.tangem.domain.addressbook.usecase.SyncAddressBooksUseCase import com.tangem.domain.models.account.CryptoPortfolioIcon import com.tangem.domain.models.network.Network @@ -36,7 +36,7 @@ import org.junit.jupiter.api.TestInstance @TestInstance(TestInstance.Lifecycle.PER_CLASS) internal class ContactsBlockModelTest { - private val getContactsUseCase: GetContactsUseCase = mockk() + private val getVerifiedContactsInteractor: GetVerifiedContactsInteractor = mockk() private val getWalletsUseCase: GetWalletsUseCase = mockk() private val syncAddressBooksUseCase: SyncAddressBooksUseCase = mockk(relaxed = true) private val analyticsSender: AddressBookAnalyticsSender = mockk(relaxed = true) @@ -46,7 +46,7 @@ internal class ContactsBlockModelTest { @BeforeEach fun resetMocks() { - clearMocks(getContactsUseCase, getWalletsUseCase, analyticsSender) + clearMocks(getVerifiedContactsInteractor, getWalletsUseCase, analyticsSender) every { getWalletsUseCase.invokeAsMap(isOnlyMultiCurrency = false, filterLocked = true) } returns flowOf(linkedMapOf()) } @@ -60,8 +60,8 @@ internal class ContactsBlockModelTest { @Test fun `GIVEN matching contacts WHEN block populated THEN SendFlowWidgetShown sent once`() = runTest { // Arrange - every { getContactsUseCase(query = any(), userWalletId = null) } returns - flowOf(listOf(contact(id = "1"), contact(id = "2"))) + every { getVerifiedContactsInteractor.getVerifiedContacts(query = any(), userWalletId = null) } returns + flowOf(listOf(verified(id = "1"), verified(id = "2"))) // Act createModel(testScope = this) @@ -74,7 +74,8 @@ internal class ContactsBlockModelTest { @Test fun `GIVEN no matching contacts WHEN block empty THEN SendFlowWidgetShown not sent`() = runTest { // Arrange - every { getContactsUseCase(query = any(), userWalletId = null) } returns flowOf(emptyList()) + every { getVerifiedContactsInteractor.getVerifiedContacts(query = any(), userWalletId = null) } returns + flowOf(emptyList()) // Act val model = createModel(testScope = this) @@ -89,7 +90,8 @@ internal class ContactsBlockModelTest { fun `GIVEN populated block WHEN contact tapped THEN ContactSelectedInSend sent AND click propagated`() = runTest { // Arrange var clicked: MatchedContact? = null - every { getContactsUseCase(query = any(), userWalletId = null) } returns flowOf(listOf(contact(id = "42"))) + every { getVerifiedContactsInteractor.getVerifiedContacts(query = any(), userWalletId = null) } returns + flowOf(listOf(verified(id = "42"))) val model = createModel(testScope = this, onContactClick = { clicked = it }) advanceUntilIdle() @@ -101,6 +103,9 @@ internal class ContactsBlockModelTest { assertThat(clicked?.contactId).isEqualTo("42") } + private fun verified(id: String): VerifiedContact = + VerifiedContact(contact = contact(id = id), invalidEntries = emptyList()) + private fun contact(id: String): Contact = Contact( id = ContactId(id), walletId = UserWalletId("a"), @@ -137,7 +142,7 @@ internal class ContactsBlockModelTest { stateController = ContactsBlockStateController(), analyticsSender = analyticsSender, syncAddressBooksUseCase = syncAddressBooksUseCase, - getContactsUseCase = getContactsUseCase, + getVerifiedContactsInteractor = getVerifiedContactsInteractor, getWalletsUseCase = getWalletsUseCase, ).also { model = it } } diff --git a/features/send/impl/src/main/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModel.kt b/features/send/impl/src/main/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModel.kt index 74f6520ad9..d8946bc0ce 100644 --- a/features/send/impl/src/main/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModel.kt +++ b/features/send/impl/src/main/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModel.kt @@ -16,8 +16,8 @@ import com.tangem.core.decompose.navigation.Router import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier import com.tangem.domain.account.status.usecase.GetBackupProblematicWalletForAddressUseCase import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase +import com.tangem.domain.addressbook.interactor.GetVerifiedContactsInteractor import com.tangem.domain.addressbook.model.Contact -import com.tangem.domain.addressbook.usecase.GetContactsUseCase import com.tangem.domain.addressbook.usecase.SyncAddressBooksUseCase import com.tangem.domain.feedback.SendBackupProblemEmailUseCase import com.tangem.domain.models.account.AccountStatus @@ -38,11 +38,7 @@ import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.features.addressbook.AddressBookFeatureToggles -import com.tangem.features.addressbook.AddressBookSendAnalytics -import com.tangem.features.addressbook.ContactSelectionListener -import com.tangem.features.addressbook.MatchedContact -import com.tangem.features.addressbook.SelectedContact +import com.tangem.features.addressbook.* import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents.SendScreenSource import com.tangem.features.send.api.entity.PredefinedValues @@ -92,7 +88,7 @@ internal class SendDestinationModel @Inject constructor( private val addressBookSendAnalytics: AddressBookSendAnalytics, private val syncAddressBooksUseCase: SyncAddressBooksUseCase, private val addressBookFeatureToggles: AddressBookFeatureToggles, - getContactsUseCase: GetContactsUseCase, + getVerifiedContactsInteractor: GetVerifiedContactsInteractor, contactSelectionListener: ContactSelectionListener, ) : Model(), SendDestinationClickIntents { private val params: SendDestinationComponentParams = paramsContainer.require() @@ -104,8 +100,11 @@ internal class SendDestinationModel @Inject constructor( private val cryptoCurrency = params.cryptoCurrency private val userWalletId = params.userWalletId - private val contacts: StateFlow> = getContactsUseCase(query = "", userWalletId = null) - .stateIn(modelScope, SharingStarted.Eagerly, emptyList()) + private val contacts: StateFlow> = + getVerifiedContactsInteractor.getVerifiedContacts(query = "", userWalletId = null) + .map { verified -> verified.map { it.contact } } + .flowOn(dispatchers.default) + .stateIn(modelScope, SharingStarted.Eagerly, emptyList()) val addressSelectorNavigation = SlotNavigation() val addressQuery: StateFlow = uiState diff --git a/features/send/impl/src/test/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModelTest.kt b/features/send/impl/src/test/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModelTest.kt index 8c81544ec3..358585e456 100644 --- a/features/send/impl/src/test/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModelTest.kt +++ b/features/send/impl/src/test/java/com/tangem/features/send/subcomponents/destination/model/SendDestinationModelTest.kt @@ -14,8 +14,8 @@ import com.tangem.core.ui.extensions.stringReference import com.tangem.domain.account.status.supplier.MultiAccountStatusListSupplier import com.tangem.domain.account.status.usecase.GetBackupProblematicWalletForAddressUseCase import com.tangem.domain.account.status.usecase.IsAccountsModeEnabledUseCase +import com.tangem.domain.addressbook.interactor.GetVerifiedContactsInteractor import com.tangem.domain.addressbook.model.* -import com.tangem.domain.addressbook.usecase.GetContactsUseCase import com.tangem.domain.addressbook.usecase.SyncAddressBooksUseCase import com.tangem.domain.feedback.SendBackupProblemEmailUseCase import com.tangem.domain.models.currency.CryptoCurrency @@ -34,11 +34,7 @@ import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase import com.tangem.domain.wallets.usecase.GetWalletsUseCase -import com.tangem.features.addressbook.AddressBookFeatureToggles -import com.tangem.features.addressbook.AddressBookSendAnalytics -import com.tangem.features.addressbook.ContactSelectionListener -import com.tangem.features.addressbook.MatchedContact -import com.tangem.features.addressbook.SelectedContact +import com.tangem.features.addressbook.* import com.tangem.features.send.api.analytics.CommonSendAnalyticEvents import com.tangem.features.send.api.entity.PredefinedValues import com.tangem.features.send.api.subcomponents.destination.DestinationRoute @@ -94,7 +90,7 @@ internal class SendDestinationModelTest { mockk(relaxed = true) private val sendDestinationAlertFactory: SendDestinationAlertFactory = mockk(relaxed = true) private val sendBackupProblemEmailUseCase: SendBackupProblemEmailUseCase = mockk(relaxed = true) - private val getContactsUseCase: GetContactsUseCase = mockk(relaxed = true) + private val getVerifiedContactsInteractor: GetVerifiedContactsInteractor = mockk(relaxed = true) private val syncAddressBooksUseCase: SyncAddressBooksUseCase = mockk(relaxed = true) private val addressBookFeatureToggles: AddressBookFeatureToggles = mockk(relaxed = true) private val contactSelectionListener: ContactSelectionListener = mockk(relaxed = true) @@ -115,7 +111,7 @@ internal class SendDestinationModelTest { every { listenToQrScanningUseCase(any()) } returns emptyFlow().right() coEvery { validateWalletMemoUseCase(any(), any(), any()) } returns Unit.right() coEvery { isMemoRequiredUseCase(any(), any()) } returns false - every { getContactsUseCase(any(), any()) } returns flowOf(emptyList()) + every { getVerifiedContactsInteractor.getVerifiedContacts(any(), any()) } returns flowOf(emptyList()) every { contactSelectionListener.resultFlow } returns MutableSharedFlow() coEvery { getBackupProblematicWalletForAddressUseCase(any()) } returns null every { cryptoCurrency.network.rawId } returns networkRawId @@ -393,8 +389,8 @@ internal class SendDestinationModelTest { coEvery { validateWalletAddressUseCase(any(), any(), any(), any>(), any()) } returns AddressValidation.Success.Valid.right() - every { getContactsUseCase(any(), any()) } returns - flowOf(listOf(buildContact(name = model.savedName, address = model.savedAddress))) + every { getVerifiedContactsInteractor.getVerifiedContacts(any(), any()) } returns + flowOf(listOf(verified(buildContact(name = model.savedName, address = model.savedAddress)))) val sut = buildModel() advanceUntilIdle() @@ -479,8 +475,8 @@ internal class SendDestinationModelTest { coEvery { validateWalletAddressUseCase(any(), any(), any(), any>(), any()) } returns AddressValidation.Success.Valid.right() - every { getContactsUseCase(any(), any()) } returns - flowOf(model.savedAddresses.map { buildContact(address = it) }) + every { getVerifiedContactsInteractor.getVerifiedContacts(any(), any()) } returns + flowOf(model.savedAddresses.map { verified(buildContact(address = it)) }) val sut = buildBlockModel(isAddContactAvailable = model.isAddContactAvailable) advanceUntilIdle() @@ -607,11 +603,14 @@ internal class SendDestinationModelTest { addressBookSendAnalytics = addressBookSendAnalytics, syncAddressBooksUseCase = syncAddressBooksUseCase, addressBookFeatureToggles = addressBookFeatureToggles, - getContactsUseCase = getContactsUseCase, + getVerifiedContactsInteractor = getVerifiedContactsInteractor, contactSelectionListener = contactSelectionListener, ) } + private fun verified(contact: Contact): VerifiedContact = + VerifiedContact(contact = contact, invalidEntries = emptyList()) + private fun buildContact(name: String = "Alice", address: String = "0xAddr"): Contact = Contact( id = ContactId("c1"), walletId = testUserWalletId,