Updated on 2026-08-14

This commit is contained in:
Tangem 2024-07-29 15:32:22 +03:00
parent 66b2a35bc6
commit 5ef6a4167d
15 changed files with 59 additions and 41 deletions

View file

@ -32,26 +32,27 @@ import timber.log.Timber
*/
object OnboardingHelper {
suspend fun isOnboardingCase(response: ScanResponse): Boolean {
val onboardingManager = store.state.globalState.onboardingState.onboardingManager
val onboardingManager =
store.state.globalState.onboardingState.onboardingManager ?: OnboardingManager(response)
val cardId = response.card.cardId
return when {
response.cardTypesResolver.isTangemTwins() -> {
if (!response.twinsIsTwinned()) {
true
} else {
onboardingManager?.isActivationInProgress(cardId) ?: false
onboardingManager.isActivationInProgress(cardId) ?: false
}
}
response.cardTypesResolver.isWallet2() || response.cardTypesResolver.isShibaWallet() -> {
val emptyWallets = response.card.wallets.isEmpty()
val activationInProgress = onboardingManager?.isActivationInProgress(cardId)
val activationInProgress = onboardingManager.isActivationInProgress(cardId)
val isNoBackup = response.card.backupStatus == CardDTO.BackupStatus.NoBackup &&
!DemoHelper.isDemoCard(response)
emptyWallets || activationInProgress == true || isNoBackup
emptyWallets || activationInProgress || isNoBackup
}
response.card.wallets.isNotEmpty() -> onboardingManager?.isActivationInProgress(cardId) ?: false
response.card.wallets.isNotEmpty() -> onboardingManager.isActivationInProgress(cardId) ?: false
else -> true
}
}

View file

@ -11,12 +11,14 @@ import coil.load
import com.tangem.blockchain.common.Blockchain
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.ShareElement
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.common.analytics.events.Onboarding
import com.tangem.tap.common.extensions.getDrawableCompat
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.toggleWidget.RefreshBalanceWidget
import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteAction
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
@ -134,7 +136,7 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
}
private fun setupTopUpWalletState(state: OnboardingNoteState) = with(mainBinding.onboardingActionContainer) {
if (state.isBuyAllowed) {
if (availableForBuy(state.scanResponse, state.walletBalance)) {
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
btnMainAction.icon = null
btnMainAction.setOnClickListener {
@ -217,6 +219,11 @@ class OnboardingNoteFragment : BaseOnboardingFragment<OnboardingNoteState>() {
}
}
private fun availableForBuy(scanResponse: ScanResponse?, walletBalance: OnboardingWalletBalance): Boolean {
scanResponse ?: return false
return store.state.globalState.exchangeManager.availableForBuy(scanResponse, walletBalance.currency)
}
override fun handleOnBackPressed() {
store.dispatch(OnboardingNoteAction.OnBackPressed)
}

View file

@ -13,7 +13,7 @@ private fun internalReduce(action: Action, appState: AppState): OnboardingNoteSt
when (action) {
is GlobalAction.Onboarding.Start -> {
state = OnboardingNoteState()
state = OnboardingNoteState(scanResponse = action.scanResponse)
}
is OnboardingNoteAction.SetArtworkUrl -> {
state = state.copy(cardArtworkUrl = action.artworkUrl)

View file

@ -1,11 +1,10 @@
package com.tangem.tap.features.onboarding.products.note.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.domain.TapError
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.store
import org.rekotlin.StateType
import kotlin.properties.ReadOnlyProperty
/**
[REDACTED_AUTHOR]
@ -20,14 +19,11 @@ data class OnboardingNoteState(
val currentStep: OnboardingNoteStep = OnboardingNoteStep.None,
val steps: List<OnboardingNoteStep> = OnboardingNoteStep.values().toList(),
val showConfetti: Boolean = false,
val scanResponse: ScanResponse? = null,
) : StateType {
val progress: Int
get() = steps.indexOf(currentStep)
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
}
}
enum class OnboardingNoteStep {

View file

@ -6,9 +6,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.store
import org.rekotlin.StateType
import kotlin.properties.ReadOnlyProperty
/**
[REDACTED_AUTHOR]
@ -56,10 +54,6 @@ data class TwinCardsState(
val twinningInProgress: Boolean
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { _, _ ->
store.state.globalState.exchangeManager.availableForBuy(walletBalance.currency)
}
}
enum class CreateTwinWalletMode { CreateWallet, RecreateWallet }

View file

@ -27,6 +27,7 @@ import com.tangem.tap.common.transitions.InternalNoteLayoutTransition
import com.tangem.tap.domain.twins.TwinsCardWidget
import com.tangem.tap.features.addBackPressHandler
import com.tangem.tap.features.onboarding.OnboardingMenuProvider
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.features.onboarding.products.BaseOnboardingFragment
import com.tangem.tap.features.onboarding.products.twins.redux.CreateTwinWalletMode
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsAction
@ -65,12 +66,7 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
}
override fun loadToolbarMenu(): MenuProvider = OnboardingMenuProvider(
scanResponseProvider = Provider {
store.state.twinCardsState.welcomeOnlyScanResponse
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: store.state.detailsState.scanResponse
?: error("ScanResponse must be not null")
},
scanResponseProvider = Provider { getActualScanResponse() },
)
@Suppress("MagicNumber")
@ -342,7 +338,7 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
else -> {}
}
if (state.isBuyAllowed) {
if (availableForBuy(getActualScanResponse(), state.walletBalance)) {
btnMainAction.setText(R.string.onboarding_top_up_button_but_crypto)
btnMainAction.setOnClickListener {
store.dispatch(TwinCardsAction.TopUp)
@ -424,6 +420,18 @@ internal class OnboardingTwinsFragment : BaseOnboardingFragment<TwinCardsState>(
}
}
private fun availableForBuy(scanResponse: ScanResponse?, walletBalance: OnboardingWalletBalance): Boolean {
scanResponse ?: return false
return store.state.globalState.exchangeManager.availableForBuy(scanResponse, walletBalance.currency)
}
private fun getActualScanResponse(): ScanResponse {
return store.state.twinCardsState.welcomeOnlyScanResponse
?: store.state.globalState.onboardingState.onboardingManager?.scanResponse
?: store.state.detailsState.scanResponse
?: error("ScanResponse must be not null")
}
override fun handleOnBackPressed() {
store.dispatch(
TwinCardsAction.OnBackPressed { should, popAction ->

View file

@ -1,5 +1,6 @@
package com.tangem.tap.network.exchangeServices
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.domain.model.Currency
import com.tangem.tap.network.exchangeServices.mercuryo.MercuryoService
@ -32,7 +33,8 @@ internal class BuyExchangeService(
override fun isSellAllowed(): Boolean = currentService.isSellAllowed()
override fun availableForBuy(currency: Currency): Boolean = currentService.availableForBuy(currency)
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean =
currentService.availableForBuy(scanResponse, currency)
override fun availableForSell(currency: Currency): Boolean = currentService.availableForSell(currency)

View file

@ -2,6 +2,7 @@ package com.tangem.tap.network.exchangeServices
import com.tangem.domain.common.TapWorkarounds.isStart2Coin
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.tap.domain.model.Currency
import com.tangem.tap.features.demo.isDemoCard
@ -38,8 +39,8 @@ class CardExchangeRules(
}
}
override fun availableForBuy(currency: Currency): Boolean {
val card = cardProvider() ?: return false
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean {
val card = scanResponse.card
return when {
card.isDemoCard() -> true

View file

@ -8,6 +8,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.transaction.TransactionFee
import com.tangem.blockchain.extensions.Result
import com.tangem.domain.models.scan.CardDTO
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.common.extensions.inject
import com.tangem.tap.common.extensions.safeUpdate
@ -38,8 +39,9 @@ class CurrencyExchangeManager(
override fun isBuyAllowed(): Boolean = primaryRules.isBuyAllowed() && buyService.isBuyAllowed()
override fun isSellAllowed(): Boolean = primaryRules.isSellAllowed() && sellService.isSellAllowed()
override fun availableForBuy(currency: Currency): Boolean {
return primaryRules.availableForBuy(currency) && buyService.availableForBuy(currency)
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean {
return primaryRules.availableForBuy(scanResponse, currency) &&
buyService.availableForBuy(scanResponse, currency)
}
override fun availableForSell(currency: Currency): Boolean {

View file

@ -1,13 +1,15 @@
package com.tangem.tap.network.exchangeServices
import com.tangem.domain.exchange.RampStateManager
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
class DefaultRampManager(private val exchangeService: ExchangeService?) : RampStateManager {
private val cryptoCurrencyConverter = CryptoCurrencyConverter()
override fun availableForBuy(cryptoCurrency: CryptoCurrency): Boolean {
override fun availableForBuy(scanResponse: ScanResponse, cryptoCurrency: CryptoCurrency): Boolean {
return exchangeService?.availableForBuy(
scanResponse,
currency = cryptoCurrencyConverter.convertBack(cryptoCurrency),
) ?: false
}

View file

@ -1,5 +1,6 @@
package com.tangem.tap.network.exchangeServices
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.common.feature.Feature
import com.tangem.tap.domain.model.Currency
@ -7,7 +8,7 @@ import com.tangem.tap.domain.model.Currency
interface Exchanger {
fun isBuyAllowed(): Boolean
fun isSellAllowed(): Boolean
fun availableForBuy(currency: Currency): Boolean
fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean
fun availableForSell(currency: Currency): Boolean
}
@ -20,7 +21,7 @@ interface ExchangeService : Feature, Exchanger, ExchangeUrlBuilder {
override suspend fun update() {}
override fun isBuyAllowed(): Boolean = false
override fun isSellAllowed(): Boolean = false
override fun availableForBuy(currency: Currency): Boolean = false
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean = false
override fun availableForSell(currency: Currency): Boolean = false
override fun getUrl(
action: CurrencyExchangeManager.Action,
@ -45,7 +46,7 @@ interface ExchangeRules : Feature, Exchanger {
override fun featureIsSwitchedOn(): Boolean = false
override fun isBuyAllowed(): Boolean = false
override fun isSellAllowed(): Boolean = false
override fun availableForBuy(currency: Currency): Boolean = false
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean = false
override fun availableForSell(currency: Currency): Boolean = false
}
}

View file

@ -6,6 +6,7 @@ import com.tangem.common.extensions.calculateSha512
import com.tangem.common.extensions.toHexString
import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.domain.model.Currency
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
@ -28,7 +29,7 @@ internal class MercuryoService(private val environment: MercuryoEnvironment) : E
override fun isSellAllowed(): Boolean = false
override fun availableForBuy(currency: Currency): Boolean {
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean {
if (!isBuyAllowed()) return false
val mercuryoNetwork = currency.blockchain.mercuryoNetwork()

View file

@ -7,6 +7,7 @@ import com.tangem.common.services.Result
import com.tangem.common.services.performRequest
import com.tangem.datasource.api.common.createRetrofitInstance
import com.tangem.domain.common.extensions.withIOContext
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
import com.tangem.tap.domain.model.Currency
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
@ -83,7 +84,7 @@ class MoonPayService(
return status?.responseUserStatus?.isSellAllowed ?: false
}
override fun availableForBuy(currency: Currency): Boolean = false
override fun availableForBuy(scanResponse: ScanResponse, currency: Currency): Boolean = false
override fun availableForSell(currency: Currency): Boolean {
if (!isSellAllowed()) return false

View file

@ -1,5 +1,6 @@
package com.tangem.domain.exchange
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.tokens.model.CryptoCurrency
/**
@ -7,7 +8,7 @@ import com.tangem.domain.tokens.model.CryptoCurrency
*/
interface RampStateManager {
fun availableForBuy(cryptoCurrency: CryptoCurrency): Boolean
fun availableForBuy(scanResponse: ScanResponse, cryptoCurrency: CryptoCurrency): Boolean
fun availableForSell(cryptoCurrency: CryptoCurrency): Boolean
}

View file

@ -106,7 +106,7 @@ class GetCryptoCurrencyActionsUseCase(
return listOf(TokenActionsState.ActionState.HideToken(ScenarioUnavailabilityReason.None))
}
if (cryptoCurrencyStatus.value is CryptoCurrencyStatus.Unreachable) {
return getActionsForUnreachableCurrency(cryptoCurrencyStatus, needAssociateAsset)
return getActionsForUnreachableCurrency(userWallet, cryptoCurrencyStatus, needAssociateAsset)
}
val activeList = mutableListOf<TokenActionsState.ActionState>()
@ -168,7 +168,7 @@ class GetCryptoCurrencyActionsUseCase(
}
// buy
if (rampManager.availableForBuy(cryptoCurrency)) {
if (rampManager.availableForBuy(userWallet.scanResponse, cryptoCurrency)) {
activeList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
} else {
disabledList.add(
@ -222,6 +222,7 @@ class GetCryptoCurrencyActionsUseCase(
}
private fun getActionsForUnreachableCurrency(
userWallet: UserWallet,
cryptoCurrencyStatus: CryptoCurrencyStatus,
needAssociateAsset: Boolean,
): List<TokenActionsState.ActionState> {
@ -230,7 +231,7 @@ class GetCryptoCurrencyActionsUseCase(
if (isAddressAvailable(cryptoCurrencyStatus.value.networkAddress)) {
actionsList.add(TokenActionsState.ActionState.CopyAddress(ScenarioUnavailabilityReason.None))
}
if (rampManager.availableForBuy(cryptoCurrencyStatus.currency)) {
if (rampManager.availableForBuy(userWallet.scanResponse, cryptoCurrencyStatus.currency)) {
actionsList.add(TokenActionsState.ActionState.Buy(ScenarioUnavailabilityReason.None))
} else {
actionsList.add(