Updated on 2026-08-14

This commit is contained in:
Tangem 2025-02-13 19:01:17 +03:00
parent 6f973aed83
commit 9812f9f253
15 changed files with 184 additions and 67 deletions

View file

@ -5,6 +5,7 @@ import arrow.core.getOrElse
import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.common.CompletionResult
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.CompletionCallback
@ -173,10 +174,10 @@ class VisaCardActivationTask @AssistedInject constructor(
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
return if (card.wallets.any { it.curve == VisaUtilities.mandatoryCurve }) {
return if (card.wallets.any { it.curve == EllipticCurve.Secp256k1 }) {
createOTP()
} else {
val createWalletTask = CreateWalletTask(VisaUtilities.mandatoryCurve)
val createWalletTask = CreateWalletTask(EllipticCurve.Secp256k1)
val timedResult = RealtimeMonotonicTimeSource.measureTimedValue {
suspendCancellableCoroutine { continuation ->
@ -241,13 +242,13 @@ class VisaCardActivationTask @AssistedInject constructor(
val card =
session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
val wallet =
card.wallets.firstOrNull { it.curve == VisaUtilities.mandatoryCurve }
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 }
?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
val derivedPublicKey = wallet.derivedKeys[VisaUtilities.visaDefaultDerivationPath]
?: return CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message))
val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnVisaCurve(derivedPublicKey.publicKey)
val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(derivedPublicKey.publicKey)
.getOrElse { return CompletionResult.Failure(TangemSdkError.Underlying(it.message)) }
.value

View file

@ -4,6 +4,7 @@ import arrow.core.getOrElse
import com.tangem.common.CompletionResult
import com.tangem.common.card.Card
import com.tangem.common.card.CardWallet
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.CardSessionRunnable
import com.tangem.common.core.CompletionCallback
@ -81,7 +82,7 @@ class VisaCustomerWalletApproveTask(
return
}
val wallet = card.wallets.firstOrNull { it.curve == VisaUtilities.mandatoryCurve } ?: run {
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run {
callback(CompletionResult.Failure(TangemSdkError.Underlying(VisaActivationError.MissingWallet.message)))
return
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.domain.visa
import arrow.core.getOrElse
import com.tangem.common.CompletionResult
import com.tangem.common.card.CardWallet
import com.tangem.common.card.EllipticCurve
import com.tangem.common.core.CardSession
import com.tangem.common.core.TangemSdkError
import com.tangem.common.extensions.hexToBytes
@ -56,7 +57,7 @@ internal class VisaCardScanHandler @Inject constructor(
session = session,
)
val wallet = card.wallets.firstOrNull { it.curve == VisaUtilities.mandatoryCurve } ?: run {
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run {
val activationInput =
VisaActivationInput(card.cardId, card.cardPublicKey.toHexString(), card.isAccessCodeSet)
val activationStatus = VisaCardActivationStatus.NotStartedActivation(activationInput)
@ -110,7 +111,7 @@ internal class VisaCardScanHandler @Inject constructor(
val card = session.environment.card ?: return CompletionResult.Failure(TangemSdkError.MissingPreflightRead())
val wallet = card.wallets.firstOrNull { it.curve == VisaUtilities.mandatoryCurve } ?: run {
val wallet = card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: run {
Timber.e("Failed to find extended public key while handling wallet authorization")
return CompletionResult.Failure(
TangemSdkError.Underlying(VisaCardScanHandlerError.FailedToFindDerivedWalletKey.errorDescription),
@ -124,7 +125,7 @@ internal class VisaCardScanHandler @Inject constructor(
)
}
val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnVisaCurve(extendedPublicKey.publicKey)
val walletAddress = VisaWalletPublicKeyUtility.generateAddressOnSecp256k1(extendedPublicKey.publicKey)
.getOrElse {
return CompletionResult.Failure(
TangemSdkError.Underlying("Cannot generate address on Visa curve"),

View file

@ -6,6 +6,7 @@ import androidx.paging.PagingData
import arrow.fx.coroutines.parZip
import com.tangem.blockchain.common.address.Address
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.card.EllipticCurve
import com.tangem.common.extensions.hexToBytes
import com.tangem.common.extensions.toHexString
import com.tangem.data.common.cache.CacheRegistry
@ -195,7 +196,7 @@ internal class DefaultVisaRepository @Inject constructor(
if (VisaConstants.IS_DEMO_MODE_ENABLED) return getDemoPublicKey()
val cardWallet = userWallet.scanResponse.card.wallets.firstOrNull {
it.curve == VisaUtilities.mandatoryCurve
it.curve == EllipticCurve.Secp256k1
}
requireNotNull(cardWallet) { "Visa card wallet not found" }

View file

@ -2,7 +2,6 @@ package com.tangem.domain.common.visa
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.derivation.DerivationStyle
import com.tangem.common.card.EllipticCurve
import com.tangem.common.card.FirmwareVersion
import com.tangem.domain.models.scan.CardDTO
@ -10,8 +9,6 @@ private const val VISA_BATCH_START = "AE"
object VisaUtilities {
val mandatoryCurve = EllipticCurve.Secp256k1
const val tokenId = "tether"
val visaBlockchain = Blockchain.Polygon

View file

@ -5,6 +5,7 @@ import arrow.core.raise.catch
import arrow.core.raise.either
import com.tangem.blockchain.common.address.Address
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.card.EllipticCurve
import com.tangem.crypto.hdWallet.bip32.ExtendedPublicKey
import com.tangem.domain.models.scan.CardDTO
@ -19,7 +20,7 @@ object VisaWalletPublicKeyUtility {
}
fun findKeyWithoutDerivation(targetAddress: String, card: CardDTO): Either<Error, ByteArray> = either {
val wallet = findWalletOnVisaCurve(card).bind()
val wallet = findWalletOnSecp256k1(card).bind()
validatePublicKey(
targetAddress = targetAddress,
@ -29,13 +30,13 @@ object VisaWalletPublicKeyUtility {
wallet.publicKey
}
fun generateAddressOnVisaCurve(walletPublicKey: ByteArray): Either<Error, Address> = either {
fun generateAddressOnSecp256k1(walletPublicKey: ByteArray): Either<Error, Address> = either {
val addresses = catch(
block = {
VisaUtilities.visaBlockchain.makeAddresses(
walletPublicKey = walletPublicKey,
pairPublicKey = null,
curve = VisaUtilities.mandatoryCurve,
curve = EllipticCurve.Secp256k1,
)
},
catch = { raise(Error.FailedToCreateAddress) },
@ -44,12 +45,12 @@ object VisaWalletPublicKeyUtility {
addresses.firstOrNull { it.type == AddressType.Default } ?: raise(Error.FailedToCreateAddress)
}
private fun findWalletOnVisaCurve(card: CardDTO): Either<Error, CardDTO.Wallet> = either {
card.wallets.firstOrNull { it.curve == VisaUtilities.mandatoryCurve } ?: raise(Error.MissingWalletOnTargetCurve)
private fun findWalletOnSecp256k1(card: CardDTO): Either<Error, CardDTO.Wallet> = either {
card.wallets.firstOrNull { it.curve == EllipticCurve.Secp256k1 } ?: raise(Error.MissingWalletOnTargetCurve)
}
private fun validatePublicKey(targetAddress: String, publicKey: ByteArray): Either<Error, Unit> = either {
val address = generateAddressOnVisaCurve(publicKey).bind()
val address = generateAddressOnSecp256k1(publicKey).bind()
if (address.value != targetAddress) {
raise(Error.AddressNotMatched)

View file

@ -80,6 +80,10 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
}
init {
componentScope.launch {
model.onDone.collect { params.onDone() }
}
// sets title and stepper value
childStack.observe(lifecycle) { stack ->
val currentRoute = stack.active.configuration
@ -138,20 +142,14 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
onEvent = { event -> model.navigateFromChooseWallet(route, event) },
),
)
OnboardingVisaRoute.InProgress -> OnboardingVisaInProgressComponent(
is OnboardingVisaRoute.InProgress -> OnboardingVisaInProgressComponent(
appComponentContext = factoryContext,
config = OnboardingVisaInProgressComponent.Config(
scanResponse = model.currentScanResponse.value,
),
params = OnboardingVisaInProgressComponent.Params(
childParams = childParams,
onDone = {
model.stackNavigation.push(
OnboardingVisaRoute.PinCode(
activationOrderInfo = TODO(), // TODO [REDACTED_TASK_KEY] [Visa] In progress screen after pin code
),
)
},
onDone = { model.navigateFromInProgress(it) },
),
)
is OnboardingVisaRoute.OtherWalletApproveOption -> OnboardingVisaOtherWalletComponent(
@ -163,11 +161,7 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
params = OnboardingVisaOtherWalletComponent.Params(
childParams = childParams,
onDone = {
model.stackNavigation.push(
OnboardingVisaRoute.PinCode(
activationOrderInfo = TODO(),
),
)
model.stackNavigation.push(OnboardingVisaRoute.PinCode(activationOrderInfo = it))
},
),
)
@ -179,7 +173,11 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
),
params = OnboardingVisaPinCodeComponent.Params(
childParams = childParams,
onDone = { params.onDone() },
onDone = {
model.stackNavigation.push(
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.PinCode),
)
},
),
)
is OnboardingVisaRoute.TangemWalletApproveOption -> OnboardingVisaApproveComponent(
@ -191,7 +189,11 @@ internal class DefaultOnboardingVisaComponent @AssistedInject constructor(
),
params = OnboardingVisaApproveComponent.Params(
childParams = childParams,
onDone = { model.stackNavigation.push(OnboardingVisaRoute.InProgress) },
onDone = {
model.stackNavigation.push(
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.Approve),
)
},
),
)
}

View file

@ -11,6 +11,7 @@ import com.tangem.domain.models.scan.ScanResponse
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.model.OnboardingVisaInProgressModel
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.ui.OnboardingVisaInProgress
import com.tangem.features.onboarding.v2.visa.impl.route.OnboardingVisaRoute
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
@ -24,7 +25,7 @@ internal class OnboardingVisaInProgressComponent(
init {
model.onDone
.onEach { params.onDone() }
.onEach { params.onDone(it) }
.launchIn(componentScope)
}
@ -43,6 +44,12 @@ internal class OnboardingVisaInProgressComponent(
data class Params(
val childParams: DefaultOnboardingVisaComponent.ChildParams,
val onDone: () -> Unit,
)
val onDone: (DoneEvent) -> Unit,
) {
sealed class DoneEvent {
data class NavigateTo(val route: OnboardingVisaRoute) : DoneEvent()
data object Activated : DoneEvent()
}
}
}

View file

@ -5,47 +5,116 @@ import com.tangem.common.extensions.toHexString
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.domain.visa.model.VisaCardId
import com.tangem.datasource.local.visa.VisaAuthTokenStorage
import com.tangem.datasource.local.visa.VisaOTPStorage
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.*
import com.tangem.domain.visa.repository.VisaActivationRepository
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent
import com.tangem.domain.visa.repository.VisaAuthRepository
import com.tangem.domain.wallets.builder.UserWalletBuilder
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent.Config
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent.Params
import com.tangem.features.onboarding.v2.visa.impl.route.OnboardingVisaRoute
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ComponentScoped
internal class OnboardingVisaInProgressModel @Inject constructor(
paramsContainer: ParamsContainer,
visaActivationRepositoryFactory: VisaActivationRepository.Factory,
override val dispatchers: CoroutineDispatcherProvider,
private val visaAuthRepository: VisaAuthRepository,
private val visaAuthTokenStorage: VisaAuthTokenStorage,
private val otpStorage: VisaOTPStorage,
private val generateWalletNameUseCase: GenerateWalletNameUseCase,
private val userWalletsListManager: UserWalletsListManager,
) : Model() {
private val params = paramsContainer.require<OnboardingVisaInProgressComponent.Config>()
private val params = paramsContainer.require<Config>()
private val visaActivationRepository = visaActivationRepositoryFactory.create(
VisaCardId(
cardId = params.scanResponse.card.cardId,
cardPublicKey = params.scanResponse.card.cardPublicKey.toHexString(),
),
)
val onDone = MutableSharedFlow<Unit>()
val onDone = MutableSharedFlow<Params.DoneEvent>()
init {
modelScope.launch {
while (true) {
val result = runCatching {
visaActivationRepository.getActivationRemoteStateLongPoll()
}.getOrNull()
}.getOrNull() ?: continue
if (result is VisaActivationRemoteState.WaitingPinCode) {
onDone.emit(Unit)
break
when (result) {
is VisaActivationRemoteState.CardWalletSignatureRequired,
VisaActivationRemoteState.BlockedForActivation,
-> {
// TODO show alert inconsistent state
return@launch
}
is VisaActivationRemoteState.CustomerWalletSignatureRequired,
VisaActivationRemoteState.PaymentAccountDeploying,
VisaActivationRemoteState.WaitingForActivationFinishing,
-> {
}
is VisaActivationRemoteState.WaitingPinCode -> {
navigateToPinCode(result.activationOrderInfo)
return@launch
}
VisaActivationRemoteState.Activated -> {
finishActivation()
return@launch
}
}
delay(timeMillis = 1000)
}
}
}
private suspend fun navigateToPinCode(activationOrderInfo: VisaActivationOrderInfo) {
onDone.emit(Params.DoneEvent.NavigateTo(OnboardingVisaRoute.PinCode(activationOrderInfo)))
}
private suspend fun finishActivation() {
val authTokens = visaAuthTokenStorage.get(params.scanResponse.card.cardId)
?: error("Auth tokens are not found. This should not happen.")
val newTokens = runCatching {
visaAuthRepository.refreshAccessTokens(authTokens.refreshToken)
}.getOrElse {
// TODO show alert
return
}
val userWallet = createUserWallet(params.scanResponse, newTokens)
userWalletsListManager.save(userWallet)
visaAuthTokenStorage.remove(params.scanResponse.card.cardId)
otpStorage.removeOTP(params.scanResponse.card.cardId)
onDone.emit(Params.DoneEvent.Activated)
}
private suspend fun createUserWallet(scanResponse: ScanResponse, authTokens: VisaAuthTokens): UserWallet =
withContext(dispatchers.io) {
val newActivationStatus = VisaCardActivationStatus.Activated(visaAuthTokens = authTokens)
requireNotNull(
value = UserWalletBuilder(
scanResponse.copy(visaCardActivationStatus = newActivationStatus),
generateWalletNameUseCase,
).build(),
lazyMessage = { "User wallet not created" },
)
}
}

View file

@ -10,6 +10,7 @@ import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.models.scan.ScanResponse
import com.tangem.domain.visa.model.VisaActivationOrderInfo
import com.tangem.features.onboarding.v2.visa.impl.DefaultOnboardingVisaComponent
import com.tangem.features.onboarding.v2.visa.impl.child.otherwallet.model.OnboardingVisaOtherWalletModel
import com.tangem.features.onboarding.v2.visa.impl.child.otherwallet.ui.OnboardingVisaOtherWallet
@ -27,7 +28,7 @@ internal class OnboardingVisaOtherWalletComponent(
init {
model.onDone
.onEach { params.onDone() }
.onEach { params.onDone(it) }
.launchIn(componentScope)
}
@ -50,6 +51,6 @@ internal class OnboardingVisaOtherWalletComponent(
data class Params(
val childParams: DefaultOnboardingVisaComponent.ChildParams,
val onDone: () -> Unit,
val onDone: (VisaActivationOrderInfo) -> Unit,
)
}

View file

@ -7,6 +7,7 @@ import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.navigation.share.ShareManager
import com.tangem.core.navigation.url.UrlOpener
import com.tangem.domain.visa.model.VisaActivationOrderInfo
import com.tangem.domain.visa.model.VisaActivationRemoteState
import com.tangem.domain.visa.model.VisaCardId
import com.tangem.domain.visa.repository.VisaActivationRepository
@ -40,7 +41,7 @@ internal class OnboardingVisaOtherWalletModel @Inject constructor(
private val _uiState = MutableStateFlow(getInitialState())
val uiState = _uiState.asStateFlow()
val onDone = MutableSharedFlow<Unit>()
val onDone = MutableSharedFlow<VisaActivationOrderInfo>()
init {
modelScope.launch {
@ -50,7 +51,7 @@ internal class OnboardingVisaOtherWalletModel @Inject constructor(
}.getOrNull()
if (result is VisaActivationRemoteState.WaitingPinCode) {
onDone.emit(Unit)
onDone.emit(result.activationOrderInfo)
break
}

View file

@ -1,9 +1,11 @@
package com.tangem.features.onboarding.v2.visa.impl.model
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.ExperimentalDecomposeApi
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.push
import com.arkivanov.decompose.router.stack.pushNew
import com.tangem.core.decompose.di.ComponentScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
@ -16,13 +18,16 @@ import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.onboarding.v2.visa.api.OnboardingVisaComponent
import com.tangem.features.onboarding.v2.visa.impl.OnboardingVisaInnerNavigationState
import com.tangem.features.onboarding.v2.visa.impl.child.choosewallet.OnboardingVisaChooseWalletComponent
import com.tangem.features.onboarding.v2.visa.impl.child.inprogress.OnboardingVisaInProgressComponent
import com.tangem.features.onboarding.v2.visa.impl.common.ActivationReadyEvent
import com.tangem.features.onboarding.v2.visa.impl.common.PreparationDataForApprove
import com.tangem.features.onboarding.v2.visa.impl.route.OnboardingVisaRoute
import com.tangem.features.onboarding.v2.visa.impl.route.stepNum
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import javax.inject.Inject
@Stable
@ -49,6 +54,8 @@ internal class OnboardingVisaModel @Inject constructor(
val innerNavigationState = _innerNavigationState.asStateFlow()
val onDone = MutableSharedFlow<Unit>()
fun updateStepForNewRoute(route: OnboardingVisaRoute) {
_innerNavigationState.value = innerNavigationState.value.copy(
stackSize = route.stepNum(),
@ -77,6 +84,18 @@ internal class OnboardingVisaModel @Inject constructor(
)
}
@OptIn(ExperimentalDecomposeApi::class)
fun navigateFromInProgress(event: OnboardingVisaInProgressComponent.Params.DoneEvent) {
when (event) {
OnboardingVisaInProgressComponent.Params.DoneEvent.Activated -> {
modelScope.launch { onDone.emit(Unit) }
}
is OnboardingVisaInProgressComponent.Params.DoneEvent.NavigateTo -> {
stackNavigation.pushNew(event.route)
}
}
}
fun navigateFromChooseWallet(
route: OnboardingVisaRoute.ChooseWallet,
event: OnboardingVisaChooseWalletComponent.Params.Event,
@ -115,7 +134,7 @@ internal class OnboardingVisaModel @Inject constructor(
is OnboardingVisaRoute.WelcomeBack,
is OnboardingVisaRoute.Welcome,
is OnboardingVisaRoute.ChooseWallet,
OnboardingVisaRoute.InProgress,
is OnboardingVisaRoute.InProgress,
is OnboardingVisaRoute.PinCode,
-> {
}
@ -123,22 +142,27 @@ internal class OnboardingVisaModel @Inject constructor(
}
private fun initializeRoute(): OnboardingVisaRoute {
val activationStatus = params.scanResponse.visaCardActivationStatus
return when (activationStatus) {
return when (val activationStatus = params.scanResponse.visaCardActivationStatus) {
is VisaCardActivationStatus.ActivationStarted -> {
when (val remoteState = activationStatus.remoteState) {
is VisaActivationRemoteState.CardWalletSignatureRequired -> OnboardingVisaRoute.WelcomeBack(
activationInput = activationStatus.activationInput,
dataToSignByCardWalletRequest = remoteState.request,
)
is VisaActivationRemoteState.CustomerWalletSignatureRequired ->
is VisaActivationRemoteState.CardWalletSignatureRequired -> {
OnboardingVisaRoute.WelcomeBack(
activationInput = activationStatus.activationInput,
dataToSignByCardWalletRequest = remoteState.request,
)
}
is VisaActivationRemoteState.CustomerWalletSignatureRequired -> {
remoteState.getRoute(activationStatus)
VisaActivationRemoteState.PaymentAccountDeploying -> OnboardingVisaRoute.InProgress // TODO
VisaActivationRemoteState.WaitingForActivationFinishing -> OnboardingVisaRoute.InProgress // TODO
is VisaActivationRemoteState.WaitingPinCode -> OnboardingVisaRoute.PinCode(
activationOrderInfo = remoteState.activationOrderInfo,
)
}
VisaActivationRemoteState.PaymentAccountDeploying -> {
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.Approve)
}
VisaActivationRemoteState.WaitingForActivationFinishing -> {
OnboardingVisaRoute.InProgress(from = OnboardingVisaRoute.InProgress.From.PinCode)
}
is VisaActivationRemoteState.WaitingPinCode -> {
OnboardingVisaRoute.PinCode(activationOrderInfo = remoteState.activationOrderInfo)
}
VisaActivationRemoteState.Activated,
VisaActivationRemoteState.BlockedForActivation,
-> error("Activation status is not correct for onboarding flow")

View file

@ -39,7 +39,13 @@ internal sealed class OnboardingVisaRoute {
) : OnboardingVisaRoute()
@Serializable
data object InProgress : OnboardingVisaRoute()
data class InProgress(
val from: From,
) : OnboardingVisaRoute() {
enum class From {
Approve, PinCode
}
}
@Serializable
data class PinCode(val activationOrderInfo: VisaActivationOrderInfo) : OnboardingVisaRoute()

View file

@ -9,8 +9,13 @@ internal fun OnboardingVisaRoute.stepNum() = when (this) {
is OnboardingVisaRoute.ChooseWallet -> 3
is OnboardingVisaRoute.OtherWalletApproveOption -> 4
is OnboardingVisaRoute.TangemWalletApproveOption -> 4
OnboardingVisaRoute.InProgress -> 5
is OnboardingVisaRoute.PinCode -> 6
is OnboardingVisaRoute.InProgress -> {
when (this.from) {
OnboardingVisaRoute.InProgress.From.Approve -> 5
OnboardingVisaRoute.InProgress.From.PinCode -> 7
}
}
}
internal const val ONBOARDING_VISA_STEPS_COUNT = 7
internal const val ONBOARDING_VISA_STEPS_COUNT = 8

View file

@ -6,7 +6,7 @@ import com.tangem.core.ui.extensions.TextReference
internal fun OnboardingVisaRoute.screenTitle(): TextReference = when (this) {
OnboardingVisaRoute.AccessCode -> TextReference.Str("Access code")
is OnboardingVisaRoute.ChooseWallet -> TextReference.Str("Account activation")
OnboardingVisaRoute.InProgress -> TextReference.Str("In progress")
is OnboardingVisaRoute.InProgress -> TextReference.Str("In progress")
is OnboardingVisaRoute.OtherWalletApproveOption -> TextReference.Str("Wallet connection")
is OnboardingVisaRoute.PinCode -> TextReference.Str("PIN code")
is OnboardingVisaRoute.TangemWalletApproveOption -> TextReference.Str("Wallet connection")