Updated on 2026-08-14
This commit is contained in:
parent
6f973aed83
commit
9812f9f253
15 changed files with 184 additions and 67 deletions
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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" },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue