Updated on 2026-08-14

This commit is contained in:
Tangem 2026-06-25 03:20:02 -07:00
parent e5c441a9f0
commit acb79b2490
17 changed files with 491 additions and 15 deletions

View file

@ -154,7 +154,7 @@ object PreferencesKeys {
val TANGEM_PAY_ACTIVE_WITHDRAW_ORDERS_KEY by lazy {
stringPreferencesKey(name = "tangemPayActiveWithdrawOrdersKey")
}
val TANGEM_PAY_ELIGIBILITY_KEY by lazy { stringSetPreferencesKey(name = "tangemPayEligibilityList") }
val TANGEM_PAY_ELIGIBILITY_KEY by lazy { stringSetPreferencesKey(name = "tangemPayEligibilityListV2") }
fun getShouldShowNotificationKey(key: String) = booleanPreferencesKey("showShowNotificationUM_$key")
// endregion

View file

@ -34,6 +34,7 @@ dependencies {
/** Project - Domain */
implementation(projects.domain.visa)
implementation(projects.domain.virtualAccount)
implementation(projects.domain.card)
implementation(projects.domain.wallets)
implementation(projects.domain.legacy)

View file

@ -1,17 +1,17 @@
package com.tangem.data.pay
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.pay.TangemPayEligibilityType
import com.tangem.domain.models.pay.isTangemPayType
import com.tangem.utils.coroutines.AppCoroutineScope
import com.tangem.domain.models.wallet.UserWallet
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.models.wallet.isTangemPayCompatible
import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.model.TangemPayEntryPoint
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.hot.sdk.model.HotWalletId
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.sync.Mutex
@ -85,7 +85,7 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
val wallets = userWalletsListRepository.userWallets.value ?: return emptyList()
val candidates = wallets.filter { wallet ->
wallet.isMultiCurrency && !wallet.isLocked && wallet.isCompatible() &&
wallet.isMultiCurrency && !wallet.isLocked && wallet.isTangemPayCompatible &&
!onboardingRepository.isTangemPayDeactivated(wallet.walletId)
}
@ -98,11 +98,6 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
return candidates
}
private fun UserWallet.isCompatible(): Boolean = when (this) {
is UserWallet.Cold -> scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
is UserWallet.Hot -> hotWalletId.authType != HotWalletId.AuthType.NoPassword
}
private suspend fun List<UserWallet>.addPaeraCustomersData(): List<UserWalletData> {
if (isEmpty()) return emptyList()
@ -139,7 +134,7 @@ internal class DefaultTangemPayEligibilityManager @Inject constructor(
onboardingRepository.checkCustomerEligibility()
}
return if (entryPoint == null) {
eligibility.isNotEmpty()
eligibility.any { it.isTangemPayType }
} else {
eligibility.any { it == entryPoint.toEligibilityType() }
}

View file

@ -18,8 +18,13 @@ import com.tangem.datasource.utils.mapWithStringKeyTypes
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusFetcher
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusProducer
import com.tangem.domain.virtualaccount.flow.VirtualAccountStatusSupplier
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.virtualaccount.repository.VirtualAccountActivationRepository
import com.tangem.domain.virtualaccount.usecase.ActivateVirtualAccountUseCase
import com.tangem.domain.virtualaccount.usecase.GetVirtualAccountEligibilityUseCase
import com.tangem.domain.virtualaccount.usecase.GetVirtualAccountSuitableWalletsUseCase
import com.tangem.security.DeviceSecurityInfoProvider
import com.tangem.utils.coroutines.AppCoroutineScope
import dagger.Binds
import dagger.Module
@ -94,5 +99,26 @@ internal interface VirtualAccountDataModule {
): ActivateVirtualAccountUseCase {
return ActivateVirtualAccountUseCase(repository = repository)
}
@Provides
@Singleton
fun provideGetVirtualAccountSuitableWalletsUseCase(
userWalletsListRepository: UserWalletsListRepository,
): GetVirtualAccountSuitableWalletsUseCase {
return GetVirtualAccountSuitableWalletsUseCase(userWalletsListRepository = userWalletsListRepository)
}
@Provides
fun provideGetVirtualAccountEligibilityUseCase(
getVirtualAccountSuitableWalletsUseCase: GetVirtualAccountSuitableWalletsUseCase,
onboardingRepository: OnboardingRepository,
deviceSecurityInfoProvider: DeviceSecurityInfoProvider,
): GetVirtualAccountEligibilityUseCase {
return GetVirtualAccountEligibilityUseCase(
getVirtualAccountSuitableWalletsUseCase = getVirtualAccountSuitableWalletsUseCase,
onboardingRepository = onboardingRepository,
deviceSecurityInfoProvider = deviceSecurityInfoProvider,
)
}
}
}

View file

@ -4,14 +4,42 @@ enum class TangemPayEligibilityType {
BANNER,
DETAILS,
DEEPLINK,
BANNER_VIRTUAL_ACCOUNT,
DETAILS_VIRTUAL_ACCOUNT,
DEEPLINK_VIRTUAL_ACCOUNT,
UNKNOWN,
;
companion object {
fun fromString(value: String): TangemPayEligibilityType = when (value.lowercase()) {
"banner" -> BANNER
"details" -> DETAILS
fun fromString(value: String): TangemPayEligibilityType = when (value.uppercase()) {
"BANNER" -> BANNER
"DETAILS" -> DETAILS
"DEEPLINK" -> DEEPLINK
"BANNER_VIRTUAL_ACCOUNT" -> BANNER_VIRTUAL_ACCOUNT
"DETAILS_VIRTUAL_ACCOUNT" -> DETAILS_VIRTUAL_ACCOUNT
"DEEPLINK_VIRTUAL_ACCOUNT" -> DEEPLINK_VIRTUAL_ACCOUNT
else -> UNKNOWN
}
}
}
val TangemPayEligibilityType.isVirtualAccountType: Boolean
get() = this in VIRTUAL_ACCOUNT_TYPES
val TangemPayEligibilityType.isTangemPayType: Boolean
get() = this in TANGEM_PAY_TYPES
private val VIRTUAL_ACCOUNT_TYPES = setOf(
TangemPayEligibilityType.BANNER_VIRTUAL_ACCOUNT,
TangemPayEligibilityType.DETAILS_VIRTUAL_ACCOUNT,
TangemPayEligibilityType.DEEPLINK_VIRTUAL_ACCOUNT,
)
private val TANGEM_PAY_TYPES = setOf(
TangemPayEligibilityType.BANNER,
TangemPayEligibilityType.DETAILS,
TangemPayEligibilityType.DEEPLINK,
)

View file

@ -1,5 +1,6 @@
package com.tangem.domain.models.wallet
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.models.MobileWallet
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
@ -119,3 +120,9 @@ val UserWallet.isLocked
inline val UserWallet.isHotWallet get() = this is UserWallet.Hot
inline val UserWallet.isColdWallet get() = this is UserWallet.Cold
val UserWallet.isTangemPayCompatible: Boolean
get() = when (this) {
is UserWallet.Cold -> scanResponse.card.firmwareVersion >= FirmwareVersion.HDWalletAvailable
is UserWallet.Hot -> hotWalletId.authType != HotWalletId.AuthType.NoPassword
}

View file

@ -10,4 +10,24 @@ android {
}
dependencies {
/** Project - Domain */
api(projects.domain.models)
api(projects.domain.virtualAccount.models)
implementation(projects.domain.common)
implementation(projects.domain.visa)
/** Project - Core */
implementation(projects.core.security)
/** Coroutines */
implementation(deps.kotlin.coroutines)
/** Tests */
testImplementation(deps.test.junit5)
testImplementation(deps.test.mockk)
testImplementation(deps.test.coroutine)
testImplementation(deps.test.truth)
testImplementation(projects.test.core)
testImplementation(projects.common.test)
testImplementation(projects.domain.card)
}

View file

@ -10,4 +10,5 @@ android {
}
dependencies {
api(projects.domain.models)
}

View file

@ -0,0 +1,12 @@
package com.tangem.domain.virtualaccount.model
import com.tangem.domain.models.wallet.UserWallet
sealed interface VirtualAccountEligibility {
data class Available(
val wallets: List<UserWallet>,
) : VirtualAccountEligibility
data object NotAvailable : VirtualAccountEligibility
}

View file

@ -0,0 +1,7 @@
package com.tangem.domain.virtualaccount.model
enum class VirtualAccountEntryPoint {
BANNER,
DETAILS,
DEEPLINK,
}

View file

@ -0,0 +1,69 @@
package com.tangem.domain.virtualaccount.usecase
import com.tangem.domain.models.pay.TangemPayEligibilityType
import com.tangem.domain.models.pay.isVirtualAccountType
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.virtualaccount.model.VirtualAccountEligibility
import com.tangem.domain.virtualaccount.model.VirtualAccountEntryPoint
import com.tangem.security.DeviceSecurityInfoProvider
import com.tangem.security.isSecurityExposed
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.coroutineScope
class GetVirtualAccountEligibilityUseCase(
private val getVirtualAccountSuitableWalletsUseCase: GetVirtualAccountSuitableWalletsUseCase,
private val onboardingRepository: OnboardingRepository,
private val deviceSecurityInfoProvider: DeviceSecurityInfoProvider,
) {
suspend operator fun invoke(entryPoint: VirtualAccountEntryPoint?): VirtualAccountEligibility {
if (deviceSecurityInfoProvider.isSecurityExposed()) {
return VirtualAccountEligibility.NotAvailable
}
val suitableWallets = getVirtualAccountSuitableWalletsUseCase()
if (suitableWallets.isEmpty()) {
return VirtualAccountEligibility.NotAvailable
}
val isEligible = checkEligibility(entryPoint)
if (isEligible) {
return VirtualAccountEligibility.Available(suitableWallets)
}
val eligibleWallets = coroutineScope {
suitableWallets
.map { wallet ->
async {
val isExistingCustomer = onboardingRepository.hasTangemPayInWallet(wallet.walletId).getOrNull()
wallet.takeIf { isExistingCustomer == true }
}
}
.awaitAll()
.filterNotNull()
}
return if (eligibleWallets.isEmpty()) {
VirtualAccountEligibility.NotAvailable
} else {
VirtualAccountEligibility.Available(eligibleWallets)
}
}
private suspend fun checkEligibility(entryPoint: VirtualAccountEntryPoint?): Boolean {
val eligibility = onboardingRepository.getCustomerEligibility().ifEmpty {
onboardingRepository.checkCustomerEligibility()
}
return if (entryPoint == null) {
eligibility.any { it.isVirtualAccountType }
} else {
eligibility.contains(entryPoint.toEligibilityType())
}
}
private fun VirtualAccountEntryPoint.toEligibilityType(): TangemPayEligibilityType = when (this) {
VirtualAccountEntryPoint.BANNER -> TangemPayEligibilityType.BANNER_VIRTUAL_ACCOUNT
VirtualAccountEntryPoint.DETAILS -> TangemPayEligibilityType.DETAILS_VIRTUAL_ACCOUNT
VirtualAccountEntryPoint.DEEPLINK -> TangemPayEligibilityType.DEEPLINK_VIRTUAL_ACCOUNT
}
}

View file

@ -0,0 +1,17 @@
package com.tangem.domain.virtualaccount.usecase
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.models.wallet.isTangemPayCompatible
class GetVirtualAccountSuitableWalletsUseCase(
private val userWalletsListRepository: UserWalletsListRepository,
) {
operator fun invoke(): List<UserWallet> {
return userWalletsListRepository.userWallets.value
.orEmpty()
.filter { it.isMultiCurrency && !it.isLocked && it.isTangemPayCompatible }
}
}

View file

@ -0,0 +1,174 @@
package com.tangem.domain.virtualaccount.usecase
import arrow.core.left
import arrow.core.right
import com.google.common.truth.Truth.assertThat
import com.tangem.domain.models.pay.TangemPayEligibilityType
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.pay.repository.OnboardingRepository
import com.tangem.domain.virtualaccount.model.VirtualAccountEligibility
import com.tangem.domain.virtualaccount.model.VirtualAccountEntryPoint
import com.tangem.domain.visa.error.VisaApiError
import com.tangem.security.DeviceSecurityInfoProvider
import io.mockk.clearMocks
import io.mockk.coEvery
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.test.runTest
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInstance
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
internal class GetVirtualAccountEligibilityUseCaseTest {
private val getVirtualAccountSuitableWalletsUseCase: GetVirtualAccountSuitableWalletsUseCase = mockk()
private val onboardingRepository: OnboardingRepository = mockk()
private val deviceSecurityInfoProvider: DeviceSecurityInfoProvider = mockk()
private val useCase = GetVirtualAccountEligibilityUseCase(
getVirtualAccountSuitableWalletsUseCase = getVirtualAccountSuitableWalletsUseCase,
onboardingRepository = onboardingRepository,
deviceSecurityInfoProvider = deviceSecurityInfoProvider,
)
@BeforeEach
fun setup() {
clearMocks(getVirtualAccountSuitableWalletsUseCase, onboardingRepository, deviceSecurityInfoProvider)
every { deviceSecurityInfoProvider.isRooted } returns false
every { deviceSecurityInfoProvider.isBootloaderUnlocked } returns false
every { deviceSecurityInfoProvider.isXposed } returns false
}
@Test
fun `GIVEN device is rooted WHEN invoke THEN returns NotAvailable`() = runTest {
// GIVEN
every { deviceSecurityInfoProvider.isRooted } returns true
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.NotAvailable)
}
@Test
fun `GIVEN no suitable wallets WHEN invoke THEN returns NotAvailable`() = runTest {
// GIVEN
every { getVirtualAccountSuitableWalletsUseCase() } returns emptyList()
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.NotAvailable)
}
@Test
fun `GIVEN entry point eligibility passes WHEN invoke THEN returns Available with all suitable wallets`() = runTest {
// GIVEN
val wallets = listOf(mockWallet(), mockWallet())
every { getVirtualAccountSuitableWalletsUseCase() } returns wallets
coEvery {
onboardingRepository.getCustomerEligibility()
} returns listOf(TangemPayEligibilityType.BANNER_VIRTUAL_ACCOUNT)
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.Available(wallets))
}
@Test
fun `GIVEN null entry point AND any VA eligibility present WHEN invoke THEN returns Available`() = runTest {
// GIVEN
val wallets = listOf(mockWallet())
every { getVirtualAccountSuitableWalletsUseCase() } returns wallets
coEvery {
onboardingRepository.getCustomerEligibility()
} returns listOf(TangemPayEligibilityType.DEEPLINK_VIRTUAL_ACCOUNT)
// WHEN
val result = useCase(entryPoint = null)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.Available(wallets))
}
@Test
fun `GIVEN cached eligibility empty WHEN invoke THEN falls back to fetched eligibility`() = runTest {
// GIVEN
val wallets = listOf(mockWallet())
every { getVirtualAccountSuitableWalletsUseCase() } returns wallets
coEvery { onboardingRepository.getCustomerEligibility() } returns emptyList()
coEvery {
onboardingRepository.checkCustomerEligibility()
} returns listOf(TangemPayEligibilityType.BANNER_VIRTUAL_ACCOUNT)
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.Available(wallets))
}
@Test
fun `GIVEN eligibility fails AND wallet is existing customer WHEN invoke THEN returns Available with wallet`() =
runTest {
// GIVEN
val wallet = mockWallet()
every { getVirtualAccountSuitableWalletsUseCase() } returns listOf(wallet)
coEvery { onboardingRepository.getCustomerEligibility() } returns listOf(TangemPayEligibilityType.BANNER)
coEvery { onboardingRepository.hasTangemPayInWallet(wallet.walletId) } returns true.right()
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.Available(listOf(wallet)))
}
@Test
fun `GIVEN eligibility fails AND wallet is not a customer WHEN invoke THEN returns NotAvailable`() = runTest {
// GIVEN
val wallet = mockWallet()
every { getVirtualAccountSuitableWalletsUseCase() } returns listOf(wallet)
coEvery { onboardingRepository.getCustomerEligibility() } returns listOf(TangemPayEligibilityType.BANNER)
coEvery {
onboardingRepository.hasTangemPayInWallet(wallet.walletId)
} returns VisaApiError.NotPaeraCustomer.left()
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.NotAvailable)
}
@Test
fun `GIVEN eligibility fails AND only some wallets are customers WHEN invoke THEN returns Available with customers`() =
runTest {
// GIVEN
val customerWallet = mockWallet()
val nonCustomerWallet = mockWallet()
every {
getVirtualAccountSuitableWalletsUseCase()
} returns listOf(customerWallet, nonCustomerWallet)
coEvery { onboardingRepository.getCustomerEligibility() } returns listOf(TangemPayEligibilityType.BANNER)
coEvery { onboardingRepository.hasTangemPayInWallet(customerWallet.walletId) } returns true.right()
coEvery { onboardingRepository.hasTangemPayInWallet(nonCustomerWallet.walletId) } returns false.right()
// WHEN
val result = useCase(VirtualAccountEntryPoint.BANNER)
// THEN
assertThat(result).isEqualTo(VirtualAccountEligibility.Available(listOf(customerWallet)))
}
private fun mockWallet(): UserWallet {
val id = mockk<UserWalletId>()
return mockk { every { walletId } returns id }
}
}

View file

@ -0,0 +1,52 @@
package com.tangem.domain.virtualaccount.usecase
import com.google.common.truth.Truth.assertThat
import com.tangem.common.test.domain.card.MockScanResponseFactory
import com.tangem.common.test.domain.wallet.MockUserWalletFactory
import com.tangem.domain.card.configs.GenericCardConfig
import com.tangem.domain.card.configs.Wallet2CardConfig
import com.tangem.domain.common.wallets.UserWalletsListRepository
import com.tangem.domain.models.wallet.UserWallet
import io.mockk.every
import io.mockk.mockk
import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.jupiter.api.Test
internal class GetVirtualAccountSuitableWalletsUseCaseTest {
private val userWalletsListRepository: UserWalletsListRepository = mockk()
private val useCase = GetVirtualAccountSuitableWalletsUseCase(userWalletsListRepository = userWalletsListRepository)
@Test
fun `GIVEN compatible, single-currency and outdated wallets WHEN invoke THEN returns only the compatible one`() {
// GIVEN
val compatible = MockUserWalletFactory.create(
MockScanResponseFactory.create(cardConfig = Wallet2CardConfig, derivedKeys = emptyMap()),
)
val singleCurrency = MockUserWalletFactory.createSingleWalletWithToken()
val outdatedFirmware = MockUserWalletFactory.create(
MockScanResponseFactory.create(cardConfig = GenericCardConfig(maxWalletCount = 2), derivedKeys = emptyMap()),
)
every { userWalletsListRepository.userWallets } returns
MutableStateFlow(listOf(compatible, singleCurrency, outdatedFirmware))
// WHEN
val result = useCase()
// THEN
assertThat(result).containsExactly(compatible)
}
@Test
fun `GIVEN no wallets WHEN invoke THEN returns empty list`() {
// GIVEN
every { userWalletsListRepository.userWallets } returns MutableStateFlow(null)
// WHEN
val result = useCase()
// THEN
assertThat(result).isEmpty()
}
}

View file

@ -50,6 +50,7 @@ dependencies {
implementation(projects.domain.legacy)
implementation(projects.domain.settings)
implementation(projects.domain.visa)
implementation(projects.domain.virtualAccount)
/* SDK */
// TODO: For TangemError model, should be removed after card domain scanning refactoring

View file

@ -22,6 +22,9 @@ import com.tangem.domain.pay.TangemPayEligibilityManager
import com.tangem.domain.pay.model.TangemPayEntryPoint
import com.tangem.domain.tangempay.GetTangemPayCustomerIdUseCase
import com.tangem.domain.tangempay.TangemPayAnalyticsEvents
import com.tangem.domain.virtualaccount.model.VirtualAccountEligibility
import com.tangem.domain.virtualaccount.model.VirtualAccountEntryPoint
import com.tangem.domain.virtualaccount.usecase.GetVirtualAccountEligibilityUseCase
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
import com.tangem.domain.wallets.usecase.GenerateBuyTangemCardLinkUseCase
import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase
@ -67,6 +70,7 @@ internal class DetailsModel @Inject constructor(
private val generateBuyTangemCardLinkUseCase: GenerateBuyTangemCardLinkUseCase,
private val analyticsEventHandler: AnalyticsEventHandler,
private val tangemPayEligibilityManager: TangemPayEligibilityManager,
private val getVirtualAccountEligibilityUseCase: GetVirtualAccountEligibilityUseCase,
) : Model() {
private val params: DetailsComponent.Params = paramsContainer.require()
@ -99,6 +103,7 @@ internal class DetailsModel @Inject constructor(
)
addTangemPayItemIfEligible()
addVirtualAccountItemIfEligible()
state = MutableStateFlow(
value = DetailsUM(
@ -285,5 +290,32 @@ internal class DetailsModel @Inject constructor(
}
}
private fun addVirtualAccountItemIfEligible() {
modelScope.launch {
val eligibility = getVirtualAccountEligibilityUseCase(VirtualAccountEntryPoint.DETAILS)
if (eligibility is VirtualAccountEligibility.Available) {
items.update { items ->
itemsBuilder.addVirtualAccountItem(
items = items,
onClick = ::onVirtualAccountItemClicked,
)
}
}
}
}
private fun onVirtualAccountItemClicked() {
modelScope.launch {
when (val eligibility = getVirtualAccountEligibilityUseCase(VirtualAccountEntryPoint.DETAILS)) {
is VirtualAccountEligibility.Available -> router.push(
AppRoute.VirtualAccountOnboarding(
AppRoute.VirtualAccountOnboarding.Mode.FromDetailsScreen(eligibility.wallets.first().walletId),
),
)
VirtualAccountEligibility.NotAvailable -> items.update { itemsBuilder.removeVirtualAccountItem(it) }
}
}
}
private fun getAppVersion(): String = "${appInfoProvider.appVersion} (${appInfoProvider.appVersionCode})"
}

View file

@ -16,6 +16,7 @@ import kotlinx.collections.immutable.toPersistentList
import javax.inject.Inject
private const val TANGEM_PAY_ITEM_ID = "get_tangem_pay"
private const val VIRTUAL_ACCOUNT_ITEM_ID = "get_virtual_account"
@ModelScoped
internal class ItemsBuilder @Inject constructor(
@ -81,6 +82,30 @@ internal class ItemsBuilder @Inject constructor(
}.toImmutableList()
}
fun addVirtualAccountItem(items: ImmutableList<DetailsItemUM>, onClick: () -> Unit): ImmutableList<DetailsItemUM> {
return items.map { block ->
if (block.id == "shop" && block is DetailsItemUM.Basic) {
val newItems = block
.items
.toMutableList()
.apply { add(getVirtualAccountItem(onClick = onClick)) }
block.copy(items = newItems.toImmutableList())
} else {
block
}
}.toImmutableList()
}
fun removeVirtualAccountItem(items: ImmutableList<DetailsItemUM>): ImmutableList<DetailsItemUM> {
return items.map { block ->
if (block is DetailsItemUM.Basic && block.items.any { it.id == VIRTUAL_ACCOUNT_ITEM_ID }) {
block.copy(items = block.items.filter { it.id != VIRTUAL_ACCOUNT_ITEM_ID }.toImmutableList())
} else {
block
}
}.toImmutableList()
}
private fun buildWalletConnectBlock(isWalletConnectAvailable: Boolean, userWalletId: UserWalletId): DetailsItemUM? {
return if (isWalletConnectAvailable) {
DetailsItemUM.WalletConnect(
@ -194,4 +219,13 @@ internal class ItemsBuilder @Inject constructor(
onClick = onClick,
),
)
private fun getVirtualAccountItem(onClick: () -> Unit): DetailsItemUM.Basic.Item = DetailsItemUM.Basic.Item(
id = VIRTUAL_ACCOUNT_ITEM_ID,
block = BlockUM(
text = resourceReference(R.string.virtual_account_title),
iconRes = R.drawable.ic_tangem_pay_24,
onClick = onClick,
),
)
}