Updated on 2026-08-14
This commit is contained in:
commit
d9d74474bb
13 changed files with 124 additions and 111 deletions
|
|
@ -5,6 +5,7 @@ import android.content.Intent
|
|||
import com.google.android.gms.wallet.PaymentData
|
||||
import com.shopify.buy3.Storefront
|
||||
import com.tangem.tap.common.analytics.AnalyticsHandler
|
||||
import com.tangem.tap.common.extensions.filterNotNull
|
||||
import com.tangem.tap.common.shop.data.ProductType
|
||||
import com.tangem.tap.common.shop.data.TangemProduct
|
||||
import com.tangem.tap.common.shop.data.TotalSum
|
||||
|
|
@ -21,7 +22,6 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
|
|||
|
||||
private val shopifyService = ShopifyService(application, shopifyShop)
|
||||
|
||||
private lateinit var product: Storefront.Product
|
||||
|
||||
private val checkouts = mutableMapOf<ProductType, Storefront.Checkout>()
|
||||
private val variants = mutableMapOf<ProductType, Storefront.ProductVariant>()
|
||||
|
|
@ -31,22 +31,19 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
|
|||
suspend fun getProducts(): Result<List<TangemProduct>> {
|
||||
val result = shopifyService.getProducts()
|
||||
|
||||
result.onSuccess {
|
||||
product = it.first {
|
||||
val variantsSku = it.variants.edges.map { it.node.sku }
|
||||
variantsSku.contains(ProductType.WALLET_2_CARDS.sku) && variantsSku.contains(
|
||||
ProductType.WALLET_3_CARDS.sku
|
||||
)
|
||||
}
|
||||
product.variants.edges.map { it.node }
|
||||
.forEach { variant ->
|
||||
if (variant.sku == ProductType.WALLET_2_CARDS.sku) {
|
||||
variants[ProductType.WALLET_2_CARDS] = variant
|
||||
} else if (variant.sku == ProductType.WALLET_3_CARDS.sku) {
|
||||
variants[ProductType.WALLET_3_CARDS] = variant
|
||||
}
|
||||
}
|
||||
return result.mapCatching { product ->
|
||||
val availableVariants = product
|
||||
.flatMap { it.variants.edges.map { it.node } }
|
||||
.filter { SKUS_TO_DISPLAY.contains(it.sku) }
|
||||
.associateBy { ProductType.fromSku(it.sku) }
|
||||
.filterNotNull()
|
||||
variants.putAll(availableVariants)
|
||||
|
||||
if (variants.size < SKUS_TO_DISPLAY.size) {
|
||||
return Result.failure(Exception(
|
||||
"Shopify: products are missing, " +
|
||||
"\nproducts available: ${variants.keys.map { it.sku }}"))
|
||||
}
|
||||
|
||||
val twoCardsProduct = TangemProduct(
|
||||
type = ProductType.WALLET_2_CARDS,
|
||||
|
|
@ -65,7 +62,6 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
|
|||
createCheckouts()
|
||||
return Result.success(listOf(twoCardsProduct, threeCardsProduct))
|
||||
}
|
||||
return Result.failure(result.exceptionOrNull()!!)
|
||||
}
|
||||
|
||||
private suspend fun createCheckouts() {
|
||||
|
|
@ -214,6 +210,7 @@ class TangemShopService(application: Application, shopifyShop: ShopifyShop) {
|
|||
companion object {
|
||||
const val TANGEM_WALLET_2_CARDS_SKU = "TG115x2"
|
||||
const val TANGEM_WALLET_3_CARDS_SKU = "TG115x3"
|
||||
val SKUS_TO_DISPLAY = listOf(TANGEM_WALLET_2_CARDS_SKU, TANGEM_WALLET_3_CARDS_SKU)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,5 +4,15 @@ import com.tangem.tap.common.shop.TangemShopService
|
|||
|
||||
enum class ProductType(val sku: String) {
|
||||
WALLET_2_CARDS(TangemShopService.TANGEM_WALLET_2_CARDS_SKU),
|
||||
WALLET_3_CARDS(TangemShopService.TANGEM_WALLET_3_CARDS_SKU)
|
||||
WALLET_3_CARDS(TangemShopService.TANGEM_WALLET_3_CARDS_SKU);
|
||||
|
||||
companion object {
|
||||
fun fromSku(sku: String): ProductType? {
|
||||
return when (sku) {
|
||||
WALLET_2_CARDS.sku -> WALLET_2_CARDS
|
||||
WALLET_3_CARDS.sku -> WALLET_3_CARDS
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,7 @@ import kotlinx.coroutines.withContext
|
|||
|
||||
class TapWalletManager {
|
||||
val walletManagerFactory: WalletManagerFactory
|
||||
by lazy { WalletManagerFactory(blockchainSdkConfig) }
|
||||
by lazy { WalletManagerFactory(blockchainSdkConfig) }
|
||||
|
||||
val rates: RatesRepository = RatesRepository()
|
||||
|
||||
|
|
@ -34,10 +34,10 @@ class TapWalletManager {
|
|||
store.state.globalState.configManager?.config?.blockchainSdkConfig ?: BlockchainSdkConfig()
|
||||
}
|
||||
|
||||
private val walletManagersThrottler = ThrottlerWithValues<BlockchainNetwork, Result<Wallet>>(10000)
|
||||
private val walletManagersThrottler =
|
||||
ThrottlerWithValues<BlockchainNetwork, Result<Wallet>>(10000)
|
||||
|
||||
suspend fun loadWalletData(walletManager: WalletManager) {
|
||||
val blockchain = walletManager.wallet.blockchain
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(walletManager)
|
||||
val result = if (walletManagersThrottler.isStillThrottled(blockchainNetwork)) {
|
||||
walletManagersThrottler.geValue(blockchainNetwork)!!
|
||||
|
|
@ -54,17 +54,21 @@ class TapWalletManager {
|
|||
is Result.Failure -> {
|
||||
when (result.error) {
|
||||
is TapError.WalletManager.NoAccountError -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.NoAccount(
|
||||
walletManager.wallet,
|
||||
blockchainNetwork,
|
||||
(result.error as TapError.WalletManager.NoAccountError).customMessage
|
||||
))
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet.NoAccount(
|
||||
walletManager.wallet,
|
||||
blockchainNetwork,
|
||||
(result.error as TapError.WalletManager.NoAccountError).customMessage
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> {
|
||||
dispatchOnMain(WalletAction.LoadWallet.Failure(
|
||||
walletManager.wallet,
|
||||
result.error.localizedMessage
|
||||
))
|
||||
dispatchOnMain(
|
||||
WalletAction.LoadWallet.Failure(
|
||||
walletManager.wallet,
|
||||
result.error.localizedMessage
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -109,6 +113,37 @@ class TapWalletManager {
|
|||
return
|
||||
}
|
||||
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data)
|
||||
} else {
|
||||
loadSingleWalletData(data)
|
||||
}
|
||||
|
||||
dispatchOnMain(WalletAction.LoadWallet())
|
||||
dispatchOnMain(WalletAction.LoadFiatRate())
|
||||
}
|
||||
|
||||
private suspend fun loadMultiWalletData(
|
||||
scanResponse: ScanResponse
|
||||
) {
|
||||
val savedCurrencies = currenciesRepository.loadSavedCurrencies(
|
||||
scanResponse.card.cardId, scanResponse.card.settings.isHDWalletAllowed
|
||||
)
|
||||
if (savedCurrencies.isEmpty()) return
|
||||
|
||||
val walletManagers =
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies)
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers),
|
||||
)
|
||||
savedCurrencies.map {
|
||||
if (it.tokens.isNotEmpty()) {
|
||||
dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadSingleWalletData(data: ScanResponse) {
|
||||
val blockchain = data.getBlockchain()
|
||||
val primaryWalletManager = walletManagerFactory.makePrimaryWalletManager(data)
|
||||
|
||||
|
|
@ -120,87 +155,12 @@ class TapWalletManager {
|
|||
primaryWalletManager.addToken(primaryToken)
|
||||
dispatchOnMain(WalletAction.MultiWallet.SetPrimaryToken(primaryToken))
|
||||
}
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data, blockchain, primaryWalletManager)
|
||||
} else {
|
||||
dispatchOnMain(WalletAction.MultiWallet.AddBlockchains(
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddBlockchains(
|
||||
listOf(BlockchainNetwork.fromWalletManager(primaryWalletManager)),
|
||||
listOf(primaryWalletManager)
|
||||
))
|
||||
}
|
||||
} else {
|
||||
if (data.card.isMultiwalletAllowed) {
|
||||
loadMultiWalletData(data, blockchain, null)
|
||||
}
|
||||
}
|
||||
dispatchOnMain(WalletAction.LoadWallet())
|
||||
dispatchOnMain(WalletAction.LoadFiatRate())
|
||||
}
|
||||
|
||||
private suspend fun loadMultiWalletData(
|
||||
scanResponse: ScanResponse,
|
||||
primaryBlockchain: Blockchain?,
|
||||
primaryWalletManager: WalletManager?
|
||||
) {
|
||||
val primaryTokens = primaryWalletManager?.cardTokens?.toList() ?: emptyList()
|
||||
val savedCurrencies = currenciesRepository.loadSavedCurrencies(
|
||||
scanResponse.card.cardId, scanResponse.card.settings.isHDWalletAllowed
|
||||
)
|
||||
|
||||
if (savedCurrencies.isEmpty()) {
|
||||
if (primaryBlockchain != null && primaryWalletManager != null) {
|
||||
val blockchainNetwork = BlockchainNetwork.fromWalletManager(primaryWalletManager)
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.SaveCurrencies(listOf(blockchainNetwork)),
|
||||
WalletAction.MultiWallet.AddBlockchains(
|
||||
listOf(blockchainNetwork),
|
||||
listOf(primaryWalletManager)
|
||||
),
|
||||
WalletAction.MultiWallet.AddTokens(primaryTokens.toList(), blockchainNetwork)
|
||||
)
|
||||
|
||||
} else {
|
||||
val blockchainNetworks = listOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, scanResponse.card),
|
||||
BlockchainNetwork(Blockchain.Ethereum, scanResponse.card)
|
||||
)
|
||||
|
||||
val walletManagers = walletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse,
|
||||
blockchainNetworks
|
||||
)
|
||||
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks),
|
||||
WalletAction.MultiWallet.AddBlockchains(blockchainNetworks, walletManagers),
|
||||
)
|
||||
}
|
||||
// dispatchOnMain(
|
||||
// WalletAction.MultiWallet.FindBlockchainsInUse,
|
||||
// WalletAction.MultiWallet.FindTokensInUse,
|
||||
// )
|
||||
} else {
|
||||
val walletManagers = if (
|
||||
primaryTokens.isNotEmpty() &&
|
||||
primaryWalletManager != null &&
|
||||
primaryBlockchain != null && primaryBlockchain != Blockchain.Unknown
|
||||
) {
|
||||
val blockchainsWithoutPrimary = savedCurrencies.filterNot { it.blockchain == primaryBlockchain }
|
||||
walletManagerFactory.makeWalletManagersForApp(
|
||||
scanResponse,
|
||||
blockchainsWithoutPrimary
|
||||
).plus(primaryWalletManager)
|
||||
} else {
|
||||
walletManagerFactory.makeWalletManagersForApp(scanResponse, savedCurrencies)
|
||||
}
|
||||
dispatchOnMain(
|
||||
WalletAction.MultiWallet.AddBlockchains(savedCurrencies, walletManagers),
|
||||
)
|
||||
savedCurrencies.map {
|
||||
if (it.tokens.isNotEmpty()) {
|
||||
dispatchOnMain(WalletAction.MultiWallet.AddTokens(it.tokens, it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.onboarding.products.otherCards.redux
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.tap.DELAY_SDK_DIALOG_CLOSE
|
||||
|
|
@ -10,6 +11,8 @@ import com.tangem.tap.common.redux.global.GlobalAction
|
|||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.tap.tangemSdkManager
|
||||
|
|
@ -73,6 +76,21 @@ private fun handleOtherCardsAction(action: Action, dispatch: DispatchFunction) {
|
|||
onboardingManager.scanResponse = updatedResponse
|
||||
onboardingManager.activationStarted(updatedResponse.card.cardId)
|
||||
|
||||
val primaryBlockchain = updatedResponse.getBlockchain()
|
||||
val blockchainNetworks = if (primaryBlockchain != Blockchain.Unknown) {
|
||||
val primaryToken = updatedResponse.getPrimaryToken()
|
||||
val blockchainNetwork = BlockchainNetwork(primaryBlockchain, updatedResponse.card).updateTokens(
|
||||
listOfNotNull(primaryToken))
|
||||
listOf(blockchainNetwork)
|
||||
} else {
|
||||
listOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, updatedResponse.card),
|
||||
BlockchainNetwork(Blockchain.Ethereum, updatedResponse.card)
|
||||
)
|
||||
}
|
||||
|
||||
store.dispatch(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks))
|
||||
|
||||
delay(DELAY_SDK_DIALOG_CLOSE)
|
||||
store.dispatch(OnboardingOtherCardsAction.SetStepOfScreen(OnboardingOtherCardsStep.Done))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.tap.features.onboarding.products.wallet.redux
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.common.card.Card
|
||||
import com.tangem.domain.common.ScanResponse
|
||||
|
|
@ -12,9 +13,11 @@ import com.tangem.tap.common.redux.global.GlobalAction
|
|||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.domain.extensions.hasWallets
|
||||
import com.tangem.tap.domain.tokens.models.BlockchainNetwork
|
||||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.redux.HomeAction
|
||||
import com.tangem.tap.features.wallet.redux.Artwork
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
|
|
@ -84,6 +87,11 @@ private fun handleWalletAction(action: Action) {
|
|||
primaryCard = result.data.primaryCard
|
||||
)
|
||||
onboardingManager.scanResponse = updatedResponse
|
||||
val blockchainNetworks = listOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, result.data.card),
|
||||
BlockchainNetwork(Blockchain.Ethereum, result.data.card)
|
||||
)
|
||||
store.dispatchOnMain(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks))
|
||||
onboardingManager.activationStarted(updatedResponse.card.cardId)
|
||||
store.dispatch(OnboardingWalletAction.ProceedBackup)
|
||||
}
|
||||
|
|
@ -260,6 +268,11 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
|
|||
backupService.proceedBackup { result ->
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
val blockchainNetworks = listOf(
|
||||
BlockchainNetwork(Blockchain.Bitcoin, result.data),
|
||||
BlockchainNetwork(Blockchain.Ethereum, result.data)
|
||||
)
|
||||
store.dispatchOnMain(WalletAction.MultiWallet.SaveCurrencies(blockchainNetworks))
|
||||
if (backupService.currentState == BackupService.State.Finished) {
|
||||
store.dispatchOnMain(BackupAction.FinishBackup)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,15 +1,20 @@
|
|||
package com.tangem.tap.features.shop.redux
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.common.shop.GooglePayService
|
||||
import com.tangem.tap.common.shop.data.ProductType
|
||||
import com.tangem.tap.common.shop.data.TangemProduct
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class ShopAction : Action {
|
||||
|
||||
object LoadProducts : ShopAction() {
|
||||
data class Success(val products: List<TangemProduct>) : ShopAction()
|
||||
object Failure : ShopAction(), NotificationAction {
|
||||
override val messageResource = R.string.common_server_unavailable
|
||||
}
|
||||
}
|
||||
|
||||
data class ApplyPromoCode(val promoCode: String) : ShopAction() {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import com.tangem.tap.store
|
|||
import kotlinx.coroutines.launch
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import timber.log.Timber
|
||||
|
||||
class ShopMiddleware {
|
||||
|
||||
|
|
@ -88,10 +89,13 @@ private fun handle(action: Action) {
|
|||
}
|
||||
ShopAction.LoadProducts -> {
|
||||
scope.launch {
|
||||
val result = shopService.getProducts()
|
||||
result.onSuccess {
|
||||
store.dispatchOnMain(ShopAction.LoadProducts.Success(it))
|
||||
}
|
||||
shopService.getProducts().fold(
|
||||
onSuccess = { store.dispatchOnMain(ShopAction.LoadProducts.Success(it)) },
|
||||
onFailure = {
|
||||
Timber.e(it)
|
||||
store.dispatchOnMain(ShopAction.LoadProducts.Failure)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
is ShopAction.CheckIfGooglePayAvailable -> {
|
||||
|
|
|
|||
|
|
@ -65,5 +65,6 @@ private fun internalReduce(action: Action, state: ShopState): ShopState {
|
|||
}
|
||||
ShopAction.FinishSuccessfulOrder -> state
|
||||
ShopAction.ResetState -> ShopState()
|
||||
ShopAction.LoadProducts.Failure -> state
|
||||
}
|
||||
}
|
||||
|
|
@ -47,6 +47,7 @@
|
|||
<string name="common_custom">Custom</string>
|
||||
<string name="common_notice">Notice</string>
|
||||
<string name="common_attention">Attention</string>
|
||||
<string name="common_server_unavailable">The server is not available, please try again later</string>
|
||||
|
||||
<string name="token_details_hide_alert_title">Hide %s</string>
|
||||
<string name="token_details_hide_alert_hide">Hide</string>
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
|
||||
|
||||
<string name="common_custom">Custom</string>
|
||||
<string name="common_server_unavailable">The server is not available, please try again later</string>
|
||||
<string name="common_notice">Notice</string>
|
||||
<string name="common_attention">Attention</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
<string name="alert_funds_restoration_message">If you chose the wrong network during your crypto transfer from the exchange, this guide will help you recover your funds</string>
|
||||
|
||||
<string name="common_custom">Custom</string>
|
||||
<string name="common_server_unavailable">The server is not available, please try again later</string>
|
||||
<string name="common_notice">Notice</string>
|
||||
<string name="common_attention">Attention</string>
|
||||
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
<string name="common_custom">Пользовательский</string>
|
||||
<string name="common_notice">Уведомление</string>
|
||||
<string name="common_attention">Внимание</string>
|
||||
<string name="common_server_unavailable">Сервер недоступен, повторите попытку позднее</string>
|
||||
|
||||
<string name="main_page_balance">Баланс</string>
|
||||
<string name="main_processing_full_amount">В сумме учтены не все монеты</string>
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
<string name="common_custom">Custom</string>
|
||||
<string name="common_notice">Notice</string>
|
||||
<string name="common_attention">Attention</string>
|
||||
<string name="common_server_unavailable">The server is not available, please try again later</string>
|
||||
|
||||
<string name="main_page_balance">Total balance</string>
|
||||
<string name="main_processing_full_amount">The amount does not include some of your funds</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue