Updated on 2026-08-14
This commit is contained in:
commit
dcda504df6
40 changed files with 1442 additions and 182 deletions
|
|
@ -284,7 +284,7 @@ class GetAccountCurrencyStatusUseCaseTest {
|
|||
coEvery { supplier(supplierParams) } returns flowOf(accountStatusList)
|
||||
|
||||
// Act
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = null)
|
||||
val actual = useCase(userWalletId = userWalletId, currencyId = currency.id, network = currency.network)
|
||||
.let(::getEmittedValues)
|
||||
|
||||
// Assert
|
||||
|
|
|
|||
|
|
@ -30,6 +30,11 @@ interface NetworksRepository {
|
|||
*/
|
||||
suspend fun getNetworkAddresses(userWalletId: UserWalletId, network: Network.RawID): List<CryptoCurrencyAddress>
|
||||
|
||||
/**
|
||||
* Returns the default address for the given [network] in the selected [userWalletId]
|
||||
*/
|
||||
suspend fun getDefaultAddress(userWalletId: UserWalletId, network: Network): String?
|
||||
|
||||
/** Checks if there are cached statuses for given [userWalletId] */
|
||||
suspend fun hasCachedStatuses(userWalletId: UserWalletId): Boolean
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ import com.tangem.domain.promo.models.StoryContent
|
|||
import com.tangem.domain.promo.models.StoryContentIds
|
||||
import com.tangem.domain.settings.repositories.SettingsRepository
|
||||
import com.tangem.domain.settings.usercountry.models.needApplyFCARestrictions
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
|
@ -39,12 +40,14 @@ class GetStoryContentUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
@OptIn(FlowPreview::class)
|
||||
private fun isFCAAllowed(id: String): Flow<Boolean> {
|
||||
return if (id == StoryContentIds.STORY_FIRST_TIME_SWAP.id) {
|
||||
settingsRepository.getUserCountryCode()
|
||||
.filterNotNull()
|
||||
.timeout(5.seconds)
|
||||
.timeout(3.seconds)
|
||||
.map { !it.needApplyFCARestrictions() }
|
||||
.catch { emit(true) }
|
||||
} else {
|
||||
flowOf(true)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ dependencies {
|
|||
api(projects.domain.models)
|
||||
implementation(projects.domain.account)
|
||||
implementation(projects.domain.common)
|
||||
implementation(projects.domain.networks)
|
||||
implementation(projects.domain.qrScanning.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ sealed interface ClassifiedQrContent {
|
|||
val matchingCurrencies: List<CryptoCurrency>,
|
||||
) : ClassifiedQrContent
|
||||
|
||||
data class PaymentUriWarning(
|
||||
val paymentUri: PaymentUri,
|
||||
val unsupportedParams: Map<String, String>,
|
||||
) : ClassifiedQrContent
|
||||
|
||||
sealed interface Error : ClassifiedQrContent {
|
||||
|
||||
/** QR code not recognized by any parser */
|
||||
|
|
|
|||
|
|
@ -39,6 +39,13 @@ sealed class QrSendTarget {
|
|||
)
|
||||
}
|
||||
|
||||
data object AddressSameAsWallet : QrSendTarget()
|
||||
|
||||
data class Warning(
|
||||
val target: QrSendTarget,
|
||||
val unsupportedParams: Map<String, String>,
|
||||
) : QrSendTarget()
|
||||
|
||||
data class WalletConnect(val uri: String) : QrSendTarget()
|
||||
|
||||
data class Error(val error: ClassifiedQrContent.Error) : QrSendTarget()
|
||||
|
|
|
|||
|
|
@ -5,17 +5,23 @@ import com.tangem.domain.models.account.Account
|
|||
import com.tangem.domain.models.account.AccountId
|
||||
import com.tangem.domain.models.account.AccountName
|
||||
import com.tangem.domain.models.currency.CryptoCurrency
|
||||
import com.tangem.domain.models.network.Network
|
||||
import com.tangem.domain.models.wallet.UserWalletId
|
||||
import com.tangem.domain.common.wallets.UserWalletsListRepository
|
||||
import com.tangem.domain.networks.repository.NetworksRepository
|
||||
import com.tangem.domain.qrscanning.models.ClassifiedQrContent
|
||||
import com.tangem.domain.qrscanning.repository.QrScanningEventsRepository
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import java.math.BigDecimal
|
||||
import com.tangem.domain.qrscanning.models.QrSendTarget
|
||||
import kotlinx.coroutines.awaitAll
|
||||
|
||||
class ResolveQrSendTargetsUseCase(
|
||||
private val multiAccountListSupplier: MultiAccountListSupplier,
|
||||
private val qrScanningEventsRepository: QrScanningEventsRepository,
|
||||
private val userWalletsListRepository: UserWalletsListRepository,
|
||||
private val networksRepository: NetworksRepository,
|
||||
) {
|
||||
|
||||
suspend operator fun invoke(qrCode: String): QrSendTarget {
|
||||
|
|
@ -49,7 +55,7 @@ class ResolveQrSendTargetsUseCase(
|
|||
return resolve(classified, portfolioIndex)
|
||||
}
|
||||
|
||||
private fun resolve(classified: ClassifiedQrContent, portfolioIndex: PortfolioIndex): QrSendTarget {
|
||||
private suspend fun resolve(classified: ClassifiedQrContent, portfolioIndex: PortfolioIndex): QrSendTarget {
|
||||
return when (classified) {
|
||||
is ClassifiedQrContent.WalletConnect -> QrSendTarget.WalletConnect(classified.uri)
|
||||
is ClassifiedQrContent.Error -> QrSendTarget.Error(classified)
|
||||
|
|
@ -67,17 +73,29 @@ class ResolveQrSendTargetsUseCase(
|
|||
matchingCurrencies = classified.matchingCurrencies,
|
||||
portfolioIndex = portfolioIndex,
|
||||
)
|
||||
is ClassifiedQrContent.PaymentUriWarning -> {
|
||||
val inner = resolve(classified.paymentUri, portfolioIndex)
|
||||
QrSendTarget.Warning(
|
||||
target = inner,
|
||||
unsupportedParams = classified.unsupportedParams,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun resolveAddressTarget(
|
||||
private suspend fun resolveAddressTarget(
|
||||
address: String,
|
||||
amount: BigDecimal?,
|
||||
memo: String?,
|
||||
matchingCurrencies: List<CryptoCurrency>,
|
||||
portfolioIndex: PortfolioIndex,
|
||||
): QrSendTarget {
|
||||
val walletGroups = buildWalletGroups(matchingCurrencies, portfolioIndex)
|
||||
val ownAddressNetworks = findOwnAddressNetworks(address, matchingCurrencies, portfolioIndex)
|
||||
val walletGroups = buildWalletGroups(matchingCurrencies, portfolioIndex, ownAddressNetworks)
|
||||
|
||||
if (walletGroups.isEmpty()) {
|
||||
return QrSendTarget.AddressSameAsWallet
|
||||
}
|
||||
|
||||
val singleGroup = walletGroups.singleOrNull()
|
||||
val singleCurrency = singleGroup?.accounts?.singleOrNull()?.currencies?.singleOrNull()
|
||||
|
|
@ -100,9 +118,33 @@ class ResolveQrSendTargetsUseCase(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun findOwnAddressNetworks(
|
||||
address: String,
|
||||
matchingCurrencies: List<CryptoCurrency>,
|
||||
portfolioIndex: PortfolioIndex,
|
||||
): Map<UserWalletId, List<Network.ID>> = coroutineScope {
|
||||
matchingCurrencies.distinctBy { it.id }
|
||||
.flatMap { currency ->
|
||||
portfolioIndex.currencyLocations[currency.id].orEmpty().map {
|
||||
it.accountId.userWalletId to currency.network
|
||||
}
|
||||
}
|
||||
.distinct()
|
||||
.map { (walletId, network) ->
|
||||
async {
|
||||
val ownAddress = networksRepository.getDefaultAddress(walletId, network)
|
||||
if (ownAddress == address) walletId to network else null
|
||||
}
|
||||
}
|
||||
.awaitAll()
|
||||
.filterNotNull()
|
||||
.groupBy(keySelector = { it.first }, valueTransform = { it.second.id })
|
||||
}
|
||||
|
||||
private fun buildWalletGroups(
|
||||
matchingCurrencies: List<CryptoCurrency>,
|
||||
portfolioIndex: PortfolioIndex,
|
||||
ownAddressNetworks: Map<UserWalletId, List<Network.ID>>,
|
||||
): List<QrSendTarget.Multiple.WalletGroup> {
|
||||
val walletMap = linkedMapOf<UserWalletId, WalletInfo>()
|
||||
val uniqueCurrencies = matchingCurrencies.distinctBy { it.id }
|
||||
|
|
@ -110,13 +152,18 @@ class ResolveQrSendTargetsUseCase(
|
|||
for (currency in uniqueCurrencies) {
|
||||
val locations = portfolioIndex.currencyLocations[currency.id].orEmpty()
|
||||
for (location in locations) {
|
||||
val walletInfo = walletMap.getOrPut(location.accountId.userWalletId) {
|
||||
WalletInfo(location.walletName, linkedMapOf())
|
||||
val walletId = location.accountId.userWalletId
|
||||
val isOwnAddress = ownAddressNetworks[walletId]?.contains(currency.network.id) == true
|
||||
|
||||
if (!isOwnAddress) {
|
||||
val walletInfo = walletMap.getOrPut(walletId) {
|
||||
WalletInfo(location.walletName, linkedMapOf())
|
||||
}
|
||||
val accountInfo = walletInfo.accounts.getOrPut(location.accountId) {
|
||||
AccountInfo(location.accountName, mutableListOf())
|
||||
}
|
||||
accountInfo.currencies.add(currency)
|
||||
}
|
||||
val accountInfo = walletInfo.accounts.getOrPut(location.accountId) {
|
||||
AccountInfo(location.accountName, mutableListOf())
|
||||
}
|
||||
accountInfo.currencies.add(currency)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue